Как загруженная библиотечная функция вызовет символ в основном приложении?

Когда загруженная разделяемая библиотека открывается через функцию dlopen(), есть ли способ вызвать функции в основной программе?

Приведенные ниже ответы хорошо отвечают на вопрос, но я должен спросить - не могли бы вы объяснить более широкий контекст этого требования? Когда я обнаружил, что это необходимо, это либо для создания модели расширяемости / плагина, либо потому, что моя программа не очень хорошо продумана.

reuben 25.12.2008 09:18

его также можно использовать для управления инверсией, не так ли? Определите поток приложения в библиотеке, пока фактическая реализация находится в основном приложении.

hhafez 25.12.2008 14:19

Подумайте о модуле Perl XS. Он должен использовать функции Perl низкого уровня (скажем, newSViv () для создания SV из целого числа); удобно, если модуль использует функцию newSViv () из Perl, а не встраивает свою собственную копию в общий объект модуля. Также для кода нужна стандартная библиотека C.

Jonathan Leffler 25.12.2008 22:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
12
3
4 910
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Да, если вы предоставите своей библиотеке указатель на эту функцию, я уверен, что библиотека сможет запускать / выполнять функцию в основной программе.

Вот пример, не скомпилировал, так что будьте осторожны;)

/* in main app */

/* define your function */

int do_it( char arg1, char arg2);

int do_it( char arg1, char arg2){
  /* do it! */
  return 1;
}

/* some where else in main app (init maybe?) provide the pointer */
 LIB_set_do_it(&do_it);
/** END MAIN CODE ***/

/* in LIBRARY */

int (*LIB_do_it_ptr)(char, char) = NULL;

void LIB_set_do_it( int (*do_it_ptr)(char, char) ){
    LIB_do_it_ptr = do_it_ptr;
}

int LIB_do_it(){
  char arg1, arg2;

  /* do something to the args 
  ...
  ... */

  return LIB_do_it_ptr( arg1, arg2);
}

do_it_ptr принимает указатель на функцию, которая ожидает 3 аргумента типа char; вы назначаете указатели на функции для функций, которые принимают только 2 аргумента типа char. Объявление extern для doit () вряд ли понадобится. Do_it_ptr не требуется; вы можете просто передать do_it по имени, где вы сейчас передаете do_it_ptr. Так далее!

Jonathan Leffler 25.12.2008 09:18

это правильно :) на самом деле вы также можете избавиться от LIB_get_it () и просто определить новый LIB_do_it (int (* do_it_ptr) (char, char, char)) {return do_it_ptr (arg1, arg2, arg3)}

hhafez 25.12.2008 14:17
Ответ принят как подходящий

Код dlo.c (библиотека):

#include <stdio.h>

// function is defined in main program
void callb(void);

void test(void) {
    printf("here, in lib\n");
    callb();
}

Скомпилировать с

gcc -shared -olibdlo.so dlo.c

Вот код основной программы (скопирован из man-страницы dlopen и скорректирован):

#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>

void callb(void) {
    printf("here, i'm back\n");
}

int
main(int argc, char **argv)
{
    void *handle;
    void (*test)(void);
    char *error;

    handle = dlopen("libdlo.so", RTLD_LAZY);
    if (!handle) {
        fprintf(stderr, "%s\n", dlerror());
        exit(EXIT_FAILURE);
    }

    dlerror();    /* Clear any existing error */

    *(void **) (&test) = dlsym(handle, "test");

    if ((error = dlerror()) != NULL)  {
        fprintf(stderr, "%s\n", error);
        exit(EXIT_FAILURE);
    }

    (*test)();
    dlclose(handle);
    exit(EXIT_SUCCESS);
}

Строить с

gcc -ldl -rdynamic main.c

Выход:

[js@HOST2 dlopen]$ LD_LIBRARY_PATH=. ./a.out
here, in lib
here, i'm back
[js@HOST2 dlopen]$

Опция -rdynamic помещает все символы в динамическую таблицу символов (которая отображается в память), а не только имена используемых символов. Об этом читайте дальше здесь. Конечно, вы также можете предоставить указатели на функции (или структуру указателей на функции), которые определяют интерфейс между библиотекой и вашей основной программой. На самом деле я бы выбрал именно этот метод. Я слышал от других людей, что сделать -rdynamic в Windows не так просто, и это также сделало бы более чистую связь между библиотекой и основной программой (у вас есть точный контроль над тем, что можно вызывать, а что нет), но для этого также требуется больше домашнего хозяйства.

Отличный ответ. Кто-нибудь заработал 10к реп :-)

Norman Ramsey 25.12.2008 06:35

Это интересный способ кастинга. Разве не более нормально преобразовать возвращаемое значение dlsym () в указатель на функцию, а не делать вид, что указатель, который вы назначаете, имеет тот же тип, что и функция, возвращаемая dlsym ()?

Jonathan Leffler 25.12.2008 09:14

C не говорит, что произойдет, если вы приведете значение void * к указателю на функцию. к сожалению, я не нашел абзаца, в котором говорилось бы, что это неопределенное поведение, но в справочной странице говорится об этом.

Johannes Schaub - litb 25.12.2008 09:41

вы найдете более подробную информацию здесь: opengroup.org/onlinepubs/009695399/functions/dlsym.html. В любом случае, C++ явно запрещает приведение из void * или указателей объектов к типу указателя на функцию или обратно.

Johannes Schaub - litb 25.12.2008 09:42

поэтому приведение материала к void **, как это сделал я, а затем разыменование по-прежнему является неопределенным поведением, но XSI-совместимые системы будут его поддерживать, а C++ явно не запрещает (делает его плохо сформированным) (вместо этого он говорит, что это неопределенное поведение ).

Johannes Schaub - litb 25.12.2008 09:45

@litb - я хотел бы добавить комментарий длиной более 300 символов об использовании union для «решения» проблемы. Могу я отредактировать ваш ответ, чтобы добавить эту информацию?

Jonathan Leffler 25.12.2008 22:36

Джонатан, я думаю, вы хотите использовать объединение и поместить в них указатель функции и указатель void. затем назначьте указатель void и прочитайте член указателя функции. он, конечно, будет компилироваться, как и приведение к void **, и оба являются неопределенным поведением в C.

Johannes Schaub - litb 26.12.2008 00:32

@litb: Да, вы поняли. Единственное отличие состоит в том, что объединение игнорирует предупреждения, которых нет при приведении типов.

Jonathan Leffler 26.12.2008 10:35

Функция dlopen(), как обсуждается @litb, в основном предоставляется в системах, использующих объектные файлы формата ELF. Это довольно мощный инструмент, который позволяет вам контролировать, могут ли символы, на которые ссылается загруженная библиотека, выполняться из основной программы, и, как правило, позволяет им удовлетворяться. Не все системы загрузки разделяемых библиотек настолько гибки - имейте в виду, если дело дойдет до переноса кода.

Механизм обратного вызова, описанный @hhafez, теперь работает, когда изгибы в этом коде устранены.

Другие вопросы по теме