5

我想让我的可执行文件“可选地依赖”其他共享对象。因此,如果没有 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做到这一点。

4

3 回答 3

3

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

于 2012-08-15T14:12:46.210 回答
1

虽然我很欣赏apmasell 面向对象的新代码方法,但它并不总是适用于现有的代码库。幸运的是,有一种机制允许加载的模块导出自己的函数:

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

如果您的链接器不支持这一点,您可以编写自己的 init 函数并使用dlsym(). 因此,对于每个模块,您将有一个名为void init_module(void *handle);(dlopen 的句柄在哪里)之类的函数handle,它可以导出任何自己需要的符号。

于 2015-01-11T10:18:17.013 回答
0

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

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

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

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

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

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

于 2012-08-15T12:41:02.640 回答