How can a shared library (.so) call a function that is implemented in its loading program?
我有一个实现的共享库,并且希望.so调用在加载该库的主程序中实现的函数。
假设我有main.c(可执行文件),其中包含:
1 2 | void inmain_function(void*); dlopen("libmy.so"); |
在my.c(libmy.so的代码)中,我想调用
1 | inmain_function(NULL); |
无论事实
注意:我想从my.c调用main.c中的符号,反之亦然,这是常见用法。
您有两种选择,可以选择:
选项1:从可执行文件中导出所有符号。
这是简单的选项,仅在构建可执行文件时,添加标志
选项2:使用功能列表创建导出符号文件,然后使用
演示:简单的可执行文件和动态加载的库。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <stdio.h> #include <dlfcn.h> void exported_callback() /*< Function we want to export */ { printf("Hello from callback!\ "); } void unexported_callback() /*< Function we don't want to export */ { printf("Hello from unexported callback!\ "); } 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\ ", dlerror()); return -1; } func = dlsym(handle,"library_function"); if (func == NULL) { fprintf(stderr,"Unable to get symbol\ "); return -1; } func(); return 0; } int main(int argc, const char *argv[]) { printf("Hello from main!\ "); call_library(); return 0; } |
库代码(lib.c):
1 2 3 4 5 6 7 8 9 10 11 | #include <stdio.h> int exported_callback(); int library_function() { printf("Hello from library!\ "); exported_callback(); /* unexported_callback(); */ /*< This one will not be exported in the second case */ return 0; } |
因此,首先构建库(此步骤没有什么不同):
1 | gcc -shared -fPIC lib.c -o libprog.so |
现在,用导出的所有符号构建可执行文件:
1 | gcc -Wl,--export-dynamic main.c -o prog.exe -ldl |
运行示例:
1 2 3 4 | $ ./prog.exe Hello from main! Hello from library! Hello from callback! |
导出的符号:
1 2 3 | $ objdump -e prog.exe -T | grep callback 00000000004009f4 g DF .text 0000000000000015 Base exported_callback 0000000000400a09 g DF .text 0000000000000015 Base unexported_callback |
现在带有导出的列表(
1 2 3 4 5 6 | { extern"C" { exported_callback; }; }; |
构建并检查可见符号:
1 2 3 | $ 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 |
您需要在.so中创建一个注册函数,以便可执行文件可以提供指向.so的函数指针,以供以后使用。
像这样:
1 2 3 4 5 6 7 8 9 10 | 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需要看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 | void (*callback)() = NULL; void register_function( void (*in_main_func)()) { callback = in_main_func(); } void function_needing_callback() { callback(); } |
将主函数的原型放在.h文件中,并将其包含在主库代码和动态库代码中。
使用GCC,只需使用
加载后,您的库将能够从主程序中调用该函数。
进一步的解释是,编译后,动态库中的主代码中的函数将具有未定义的符号。在您的主应用程序加载库后,该符号将由主程序的符号表解析。我已经多次使用上述模式,并且它就像一种魅力。
以下代码可用于在代码中加载动态库(以防万一有人看了看怎么做后来到这里):
1 2 3 4 5 6 7 | 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 */ |
不要忘记放置
您可能还想添加一些逻辑,以检查是否返回了
但是,其他海报为您的问题提供了更合适的答案。