共享库(.so)如何调用其加载器代码中实现的函数?
- 2024-11-08 09:04:00
- admin 原创
- 30
问题描述:
我有一个自己实现的共享库,并希望 .so 调用在主程序中实现的加载该库的函数。
假设我有一个 main.c (可执行文件),其中包含:
void inmain_function(void*);
dlopen("libmy.so");
在 my.c (libmy.so 的代码)中我想要调用inmain_function
:
inmain_function(NULL);
inmain_function
无论事实inmain_function
是否在主程序中定义,共享库如何调用。
注意:我想从 my.c 调用 main.c 中的符号,而不是反之亦然,这是常见的用法。
解决方案 1:
您有两个选项可供选择:
选项 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!
");
}
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):
#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;
}
因此,首先构建库(此步骤没有区别):
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
解决方案 2:
您需要在 .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();
}
解决方案 3:
将您的主函数的原型放在.h 文件中,并将其包含在主库和动态库代码中。
使用 GCC,只需使用标志编译您的主程序
-rdynamic
。一旦加载,您的库将能够从主程序调用该函数。
进一步解释一下,一旦编译完成,您的动态库中就会有一个未定义的符号,该符号对应于主代码中的函数。当您的主应用程序加载该库时,该符号将由主程序的符号表解析。我已经多次使用上述模式,效果非常好。
解决方案 4:
以下内容可用于加载动态库并从加载调用中调用它(以防有人在寻找如何加载和调用 .so 库中的函数后来到此处):
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
,它会给您一些描述问题的有意义的消息。
然而,其他海报已经为您的问题提供了更合适的答案。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件