Как общая библиотека (.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
, и он должен дать вам несколько содержательных сообщений, описывающих проблему.
Другие плакаты, однако, предоставили более подходящие ответы для вашей проблемы.