Как общая библиотека (.so) может вызвать функцию, реализованную в программе-загрузчике?

У меня есть общая библиотека, которую я реализую, и я хочу, чтобы .so вызывал функцию в основной программе, которая загружает библиотеку.

Скажем, у меня есть main.c(исполняемый файл), который содержит:

void inmain_function(void*);
dlopen("libmy.so");

В my.c(код для libmy.so) я хочу вызвать inmain_function:

inmain_function(NULL);

Как вызов общей библиотеки inmain_function, независимо от того, что факт inmain_function определен в основной программе.

Примечание. Я хочу вызвать символ в main.c из my.c, а не наоборот, что является общим использованием.

Ответы

Ответ 1

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

Вот так:

void in_main_func () {
// this is the function that need to be called from a .so
}

void (*register_function)(void(*)());
void *handle = dlopen("libmylib.so");

register_function = dlsym(handle, "register_function");

register_function(in_main_func);

register_function должен хранить указатель функции в переменной .so, где другая функция в .so может найти ее.

Ваш mylib.c должен будет выглядеть примерно так:

void (*callback)() = NULL;

void register_function( void (*in_main_func)())
{
    callback = in_main_func();
}

void function_needing_callback() 
{
     callback();
}

Ответ 2

У вас есть два варианта, из которых вы можете выбрать:

Вариант 1: экспортировать все символы из исполняемого файла. Это простой вариант, просто при создании исполняемого файла добавьте флаг -Wl,--export-dynamic. Это сделает все функции доступными для вызовов библиотеки.

Вариант 2: создать файл экспортных файлов со списком функций и использовать -Wl,--dynamic-list=exported.txt. Это требует некоторого обслуживания, но более точно.

Чтобы продемонстрировать: простая исполняемая и динамически загружаемая библиотека.

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

void exported_callback() /*< Function we want to export */
{
    printf("Hello from callback!\n");
}

viud unexported_callback() /*< Function we don't want to export */
{
    printf("Hello from unexported callback!\n");
}

typedef void (*lib_func)();

int call_library()
{
   void     *handle  = NULL;
   lib_func  func    = NULL;
   handle = dlopen("./libprog.so", RTLD_NOW | RTLD_GLOBAL);
   if (handle == NULL)
   {
       fprintf(stderr, "Unable to open lib: %s\n", dlerror());
       return -1;
   }
   func = dlsym(handle, "library_function");

   if (func == NULL) {
       fprintf(stderr, "Unable to get symbol\n");
      return -1;
   }

   func();
   return 0;
}

int main(int argc, const char *argv[])
{
    printf("Hello from main!\n");
    call_library();
    return 0;
}

Библиотечный код (lib.c):

#include <stdio.h>
int exported_callback();

int library_function()
{
    printf("Hello from library!\n");
    exported_callback();
    /* unexported_callback(); */ /*< This one will not be exported in the second case */
    return 0;
}

Итак, сначала создайте библиотеку (этот шаг не отличается):

 gcc -shared -fPIC lib.c -o libprog.so

Теперь создайте исполняемый файл со всеми экспортированными символами:

 gcc -Wl,--export-dynamic main.c -o prog.exe -ldl

Пример выполнения:

 $ ./prog.exe
 Hello from main!
 Hello from library!
 Hello from callback!

Выводимые символы:

 $ objdump -e prog.exe -T | grep callback
 00000000004009f4 g    DF .text  0000000000000015  Base        exported_callback
 0000000000400a09 g    DF .text  0000000000000015  Base        unexported_callback

Теперь с экспортированным списком (exported.txt):

{
    extern "C"
    {
       exported_callback;
    };
};

Сборка и проверка видимых символов:

$ gcc -Wl,--dynamic-list=./exported.txt main.c -o prog.exe -ldl
$ objdump -e prog.exe -T | grep callback
0000000000400774 g    DF .text  0000000000000015  Base        exported_callback

Ответ 3

  • Поместите свой прототип основной функции в файл .h и включите его как в основной, так и в динамический библиотечный код.

  • С GCC просто скомпилируйте свою основную программу с помощью флага -rdynamic.

  • После загрузки ваша библиотека сможет вызвать функцию из основной программы.

Еще одно объяснение заключается в том, что после компиляции ваша динамическая библиотека будет иметь в ней символ undefined для функции, которая находится в основном коде. После того, как ваше основное приложение загрузит библиотеку, символ будет разрешен основной таблицей символов программы. Я использовал эту схему неоднократно и работает как шарм.

Ответ 4

Для загрузки динамической библиотеки в ваш код можно использовать следующее: если кто-то пришел сюда, посмотрев, как это сделать:

void* func_handle = dlopen ("my.so", RTLD_LAZY); /* open a handle to your library */

void (*ptr)() = dlsym (func_handle, "my_function"); /* get the address of the function you want to call */

ptr(); /* call it */

dlclose (func_handle); /* close the handle */

Не забудьте поставить #include <dlfcn.h> и связать с опцией –ldl.

Вы также можете добавить некоторую логику, которая проверяет, возвращается ли NULL. Если это так, вы можете вызвать dlerror, и он должен дать вам несколько содержательных сообщений, описывающих проблему.

Другие плакаты, однако, предоставили более подходящие ответы для вашей проблемы.