Linux 共享库 (.so) 如何调用在其加载程序中实现的函数?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17081131/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can a shared library (.so) call a function that is implemented in its loading program?
提问by 0x90
I have a shared library that I implemented and want the .so to call a function that's implemented in the main program which loads the library.
我有一个我实现的共享库,并希望 .so 调用在加载库的主程序中实现的函数。
Let's say I have main.c (executable) which contains:
假设我有 main.c(可执行文件),其中包含:
void inmain_function(void*);
dlopen("libmy.so");
In the my.c (the code for the libmy.so) I want to call inmain_function
:
在 my.c(libmy.so 的代码)中,我想调用inmain_function
:
inmain_function(NULL);
How can the shared library call inmain_function
regardless the fact inmain_function
is defined in the main program.
inmain_function
无论inmain_function
主程序中定义的事实如何,共享库如何调用。
Note: I want to call a symbol in main.c from my.c not vice versa which is the common usage.
注意:我想从 my.c 调用 main.c 中的符号,反之亦然,这是常见的用法。
采纳答案by user746527
You'll need make a register function in your .so so that the executable can give a function pointer to your .so for it's later use.
您需要在 .so 中创建一个 register 函数,以便可执行文件可以为您的 .so 提供一个函数指针,以供以后使用。
Like this:
像这样:
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);
the register_function needs to store the function pointer in a variable in the .so where the other function in the .so can find it.
register_function 需要将函数指针存储在 .so 中的一个变量中,而 .so 中的另一个函数可以找到它。
Your mylib.c would the need to look something like this:
你的 mylib.c 需要看起来像这样:
void (*callback)() = NULL;
void register_function( void (*in_main_func)())
{
callback = in_main_func();
}
void function_needing_callback()
{
callback();
}
回答by Nobilis
The following can be used to load a dynamic library in your code (in case somebody came here after looking at how to do that):
以下内容可用于在您的代码中加载动态库(以防有人在查看如何执行此操作后来到这里):
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 */
Don't forget to put #include <dlfcn.h>
and link with the –ldl
option.
不要忘记放置#include <dlfcn.h>
并链接–ldl
选项。
You might also want to add some logic that checks if NULL
is returned. If it is the case you can call dlerror
and it should give you some meaningful messages describing the problem.
您可能还想添加一些检查是否NULL
返回的逻辑。如果是这种情况,您可以致电dlerror
,它应该会给您一些描述问题的有意义的消息。
Other posters have however provided more suitable answers for your problem.
然而,其他海报为您的问题提供了更合适的答案。
回答by Valeri Atamaniouk
You have two options, from which you can choose:
您有两个选项,您可以从中选择:
Option 1: export all symbols from your executable.
This is simple option, just when building executable, add a flag -Wl,--export-dynamic
. This would make all functions available to library calls.
选项 1:从可执行文件中导出所有符号。这是一个简单的选项,只是在构建可执行文件时,添加一个标志-Wl,--export-dynamic
。这将使所有函数都可用于库调用。
Option 2: create an export symbol file with list of functions, and use -Wl,--dynamic-list=exported.txt
. This requires some maintenance, but more accurate.
选项 2:创建一个带有函数列表的导出符号文件,并使用-Wl,--dynamic-list=exported.txt
. 这需要一些维护,但更准确。
To demonstrate: simple executable and dynamically loaded library.
演示:简单的可执行文件和动态加载的库。
#include <stdio.h>
#include <dlfcn.h>
void exported_callback() /*< Function we want to export */
{
printf("Hello from callback!\n");
}
void 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;
}
Library code (lib.c):
库代码(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;
}
So, first build the library (this step doesn't differ):
所以,首先构建库(这一步没有区别):
gcc -shared -fPIC lib.c -o libprog.so
Now build executable with all symbols exported:
现在使用导出的所有符号构建可执行文件:
gcc -Wl,--export-dynamic main.c -o prog.exe -ldl
Run example:
运行示例:
$ ./prog.exe
Hello from main!
Hello from library!
Hello from callback!
Symbols exported:
导出的符号:
$ objdump -e prog.exe -T | grep callback
00000000004009f4 g DF .text 0000000000000015 Base exported_callback
0000000000400a09 g DF .text 0000000000000015 Base unexported_callback
Now with the exported list (exported.txt
):
现在使用导出的列表 ( exported.txt
):
{
extern "C"
{
exported_callback;
};
};
Build & check visible symbols:
构建和检查可见符号:
$ 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
回答by mshildt
Put your main function's prototype in a .h file and include it in both your main and dynamic library code.
With GCC, simply compile your main program with the
-rdynamic
flag.Once loaded, your library will be able to call the function from the main program.
将主函数的原型放在 .h 文件中,并将其包含在主库代码和动态库代码中。
使用 GCC,只需使用
-rdynamic
标志编译您的主程序。加载后,您的库将能够从主程序调用该函数。
A little further explanation is that once compiled, your dynamic library will have an undefined symbol in it for the function that is in the main code. Upon having your main app load the library, the symbol will be resolved by the main program's symbol table. I've used the above pattern numerous times and it works like a charm.
进一步的解释是,一旦编译,您的动态库中将有一个未定义的符号,用于主代码中的函数。在您的主应用程序加载库后,符号将由主程序的符号表解析。我已经多次使用上述模式,它就像一个魅力。