C语言 共享库(.so)如何调用在其加载器代码中实现的函数?

vjrehmav  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(188)

我有一个我实现的共享库,并希望.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中的符号,而不是相反,这是常见的用法。

62lalag4

62lalag41#

您有两个选项,您可以从中选择:

选项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");
}

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;
}

字符串
库代码(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

vqlkdk9b

vqlkdk9b2#

你需要在你的.so中创建一个register函数,这样可执行文件就可以给予一个指向你的.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();
}

f5emj3cl

f5emj3cl3#

1.将main函数的原型放在.h文件中,并将其包含在main和动态库代码中。
1.使用GCC,只需使用-rdynamic标志编译主程序。
1.一旦加载,您的库将能够从主程序调用该函数。
进一步的解释是,一旦编译完成,你的动态库中会有一个未定义的符号,用于主代码中的函数。当你的主程序加载动态库时,这个符号将被主程序的符号表解析。我已经使用过上面的模式很多次了,它就像一个魅力。

zqry0prt

zqry0prt4#

以下代码可用于加载动态库并从加载调用中调用它(如果有人在寻找如何加载和调用.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,它应该会给您给予一些有意义的消息来描述问题。
然而,其他海报为您的问题提供了更合适的答案。

相关问题