Linux上对共享库的依赖性较弱

时间:2012-08-15 12:29:06

标签: c linux ld dlopen

我想让我的可执行文件“可选地依赖”其他共享对象。因此,如果DSO不存在,它将能够在没有某些符号的情况下运行。

我可以使用dlopen/dlsym调用实现此目的,但我必须手动加载每个符号并为它们添加包装器,如下所示:

void *my_lib = dlopen("my_lib.so", RTLD_LAZY);  
if (!my_lib)  {
    // ok, I promise not to touch my_lib symbols
} else {
    my_foo_ptr = dlsym(my_lib, "my_foo");
    my_bar_ptr = dlsym(my_lib, "my_bar");
}

... my_foo(...) {
    assert(my_foo_ptr);
    return (*my_foo_ptr)(...);
}

... my_bar(...) {
    assert(my_foo_ptr);
    return (*my_bar_ptr)(...);
}

这是一个愚蠢的代码,它直接依赖于“my_lib.so”ABI,这意味着每次库更新时我都必须更新它。

我正在寻找一些方法让ld.so为我做这件事。所以理想的是:

void *my_lib = dlopen("my_lib.so", /* bring me all my symbols */);  
if (!my_lib)  {
    // ok, I promise not to touch my_lib symbols
} else {
    // ok, I can directly call symbols from my_lib.so
    my_foo();
    my_bar();
}

但这有两个问题:
1.在app链接阶段如何处理这些符号?如果我显式链接到my_lib.so,应用程序将严格依赖于它,因此无法在没有my_lib.so的情况下启动。如果没有,ld会抱怨未定义的符号 2.如何强制dlopen()使我的应用程序可以使用所有my_lib.so符号?

Upd:我意识到明确链接共享库而不将其标记为DT_NEEDED就可以了。但我不知道如何让ld做到这一点。

3 个答案:

答案 0 :(得分:3)

编写一个了解程序需求的模块可能更为明智,该模块可以最大限度地减少程序与此库之间的交互,然后将该代码链接到库中。想象一下像音乐播放器这样的东西:而不是为每种音频格式做这种舞蹈,创建一个简单的界面,然后为每种音频格式创建一个单独编译的模块,并让每个模块链接到适当的支持库。这样做的好处是,您可以确保模块都具有相同的符号,并且处理它们更简单:加载模块时,使用函数指针创建结构,然后在调用模块时,只检查结构是否为空并且调用函数指针(通过宏可能是明智的)。这也意味着如果需要,您可以轻松添加此功能的不同版本。

答案 1 :(得分:1)

虽然我对新代码感谢apmasell's object oriented approach,但现有代码库并不总是可行。幸运的是,有一种机制允许加载的模块导出自己的函数:

 -Wl,-init,<function name> and -Wl,-fini,<function name>

如果您的链接器不支持此功能,您可以编写自己的init函数并使用dlsym()加载它。因此,对于每个模块,您将拥有一个名为void init_module(void *handle);的函数(其中handle是来自dlopen的句柄),它会导出任何自己需要的符号。

答案 2 :(得分:0)

这是可能的,但可能比你愿意投入更多的工作。要做到这一点:

  1. 您需要创建GCC的补丁版本,特别是binutils,它允许您创建一个“可选”符号列表(即动态加载程序丢失时不会抱怨的符号) )。

  2. 您必须修补动态加载程序才能理解符号。

  3. 更简单的方法可能是使用DSL工具(=脚本或简单的C程序)可以读取并用于为您生成锅炉板C代码。

    DSL将定义所有符号(和参数),工具会使用上面的代码模板将其转换为.h和.c文件。

    my_lib.so更改时(或需要更多符号时),您只需编辑(小)DSL并重新运行脚本。