【问题标题】:How can a shared library (.so) call a function that is implemented in its loading program?共享库 (.so) 如何调用在其加载程序中实现的函数?
【发布时间】:2013-06-09 11:42:56
【问题描述】:

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

【问题讨论】:

    标签: c linux gcc shared-libraries


    【解决方案1】:

    以下可用于加载动态库并从加载调用中调用它(以防有人在寻找如何加载和调用 .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,它应该会为您提供一些描述问题的有意义的消息。

    但是,其他海报为您的问题提供了更合适的答案。

    【讨论】:

    • 是的,这将从另一个文件(例如您的主文件所在的位置)调用您的 .so 库中的一个函数。这是你的意思吗?
    • 由于其他人提供了比我更好的解决方案,我不确定是否有什么要补充的。如果您坚持我可以删除它(如果有人正在寻找加载动态库,它仍然很有用)。
    • 这篇文章实际上并没有回答 OP 提出的问题。
    • @Dr.JohnnyMohawk cmets 已经很明显了。
    • 当然。只需按照 StackOverflow 提供的说明进行操作 - 相对于 vis,请解释您的反对意见。
    【解决方案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();
    }
    

    【讨论】:

    • 我宁愿使用if (callback) { callback(); return 0; } else { return -1; } 来指示错误并避免调用NULL(这将是致命的)。
    • 对,去掉了第一行。
    【解决方案3】:

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

    选项 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
    

    【讨论】:

    • 当你像这样编译libprog.so时会发现exported_callback是一个缺失的符号。
    • ps。回答我自己的问题:可以禁用“未定义的符号”错误,它会起作用,例如在与-undefined dynamic_lookup 的铿锵声中。在带有 gcc 的 linux 上,这就像帖子中描述的那样神奇地工作,但我不知道为什么。
    • 这比接受的答案要好得多!我希望它会在顶部。
    【解决方案4】:
    1. 将主函数的原型放在 .h 文件中,并将其包含在主库代码和动态库代码中。

    2. 使用 GCC,只需使用 -rdynamic 标志编译您的主程序。

    3. 加载后,您的库将能够从主程序调用该函数。

    进一步的解释是,一旦编译,您的动态库中将包含一个未定义符号,用于主代码中的函数。让您的主应用程序加载库后,该符号将由主程序的符号表解析。我已经多次使用上述模式,它就像一个魅力。

    【讨论】:

    • 只是好奇,如果另一个共享库也有这个符号会怎样?
    • 主应用程序的符号将取代/覆盖库的符号,除非库的函数是使用 static 关键字定义的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-13
    • 1970-01-01
    • 2015-06-21
    • 1970-01-01
    • 2021-11-19
    • 1970-01-01
    • 2023-03-26
    相关资源
    最近更新 更多