Linux上的共享库依赖性较弱

我想让我的可执行文件“可选地依赖于”其他共享对象。 因此,如果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.在应用程序连接阶段如何处理这些符号? 如果我明确链接到my_lib.so,那么应用程序将严格依赖于它,因此无法在没有my_lib.so的情况下启动。 如果没有, ld会抱怨未定义的符号。
2.如何强制dlopen()使所有my_lib.so符号可用于我的应用程序?

Upd:我意识到,显式链接与共享库,而不标记为DT_NEEDED会做的伎俩。 但是我不知道如何让ld做到这一点。

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

虽然我赞赏apmasell面向新代码的面向对象方法 ,但现有代码库并不总是可行的。 幸运的是,有一种机制允许加载模块导出自己的功能:

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

如果你的连接器不支持这个,你可以编写你自己的init函数并用dlsym()加载它。 所以对于每一个模块,你都会有一个叫做void init_module(void *handle);的函数void init_module(void *handle); (其中handle是来自dlopen的句柄)导出任何自己需要的符号。

这是可能的,但可能比你愿意投资更多的工作。 去做吧:

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

  2. 你将不得不修补动态加载器来理解你的符号。

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

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

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