【问题标题】:Add dynamic dependency to shared object in C++在 C++ 中为共享对象添加动态依赖项
【发布时间】:2016-02-04 22:03:16
【问题描述】:

我想创建动态链接到共享对象 A 的共享对象 B。我正在使用以下命令编译共享对象 B:

g++ -fPIC -shared -L/path/to/directory -lA -o libB.so B.cpp

据我了解,-lA 告诉链接器 libB.so 应该动态链接到 /path/to/directory/libA.so。但是,当我在最终产品上执行 ldd 时,未列出依赖项(并且由于缺少这些依赖项,加载 libB.so 失败)。

ldd libB.so

linux-vdso.so.1 =>  (0x00007ffd4233e000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f35072fe000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f35070e7000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f3506de1000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3506a1c000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3507807000)

我对 -l 应该做什么有误吗?我假设以上是来自 C++ 的最小动态依赖集。

有什么需要寻找的吗?例如,链接器在找不到文件或其他内容时是否会简单地忽略-l 请求(并且我必须调试我的路径而不是我已经拥有的)?

我是否必须在我的 C++ 代码中添加一些东西来表明依赖关系(例如“外部”函数或其他东西)?

更新:

我已确定 ld 报告的动态依赖项集确实依赖于我的 C++ 代码,似乎依赖于任何 -L 或 @ 987654332@ 我提供的标志。链接器会自动猜测我的libB.so 应该依赖哪些共享对象,但它的假设还不够。

例如,我知道我需要 B 来加载 A,因为我调用了一些最终调用 libA.so 中的代码的代码。如何将这些信息提供给链接器?

说明:

我所说的“共享对象 A”是一个复杂的东西,它可能会动态加载一些代码。我想包含足够的依赖项,以便在尝试动态加载此代码时不会因“缺少符号”而失败。这就是为什么我想在 B 中强制依赖,因为链接器可能不会静态地在依赖树中找到它们。

另外,我使用的是 g++ 4.8.4 (Ubuntu 14.04)。这是相关的,因为 g++ 从 4.6 版开始隐式应用 -Wl,--as-needed

【问题讨论】:

    标签: linker g++


    【解决方案1】:

    ldd 的输出中可能未列出依赖项,因为在 libA.so 中找不到 B.cpp 中使用的符号。这可能是因为 C++ 符号名称重整:如果 C 编译器已编译 libA.so,它可能会将函数 void foo() 的符号存储在漂亮的名称 foo 下,而 C++ 编译器会将其重整为类似 @987654327 的名称@。您可以使用以下命令进行检查:

    $ # Replace "SomeSharedObject" with the actual name of symbol exported by libA.so.
    $ strings libA.so | grep SomeSharedObject
    $ strings libB.so | grep SomeSharedObject
    

    为避免这种情况,可以将符号foo 的声明放入extern "C" {} 子句中。然后编译器不会破坏这个名字,链接器可能会在libA.so中找到这个名字。

    -l 选项可在实验室环境中正常工作:

    $ cat bar.cpp 
    extern void foo();
    
    void bar()
    {
        foo();
    }
    $ cat baz.cpp 
    extern void bar();
    
    void baz()
    {
        bar();
    }
    $ # Link against libssl.so (OpenSSL).
    $ # Obviously libssl.so is unnecessary in libbar.so.
    $ g++ -fPIC -shared bar.cpp -o libbar.so -lssl 
    $ ldd libbar.so 
        statically linked
    $ # Link against libbar.so in the current directory
    $ g++ -fPIC -shared baz.cpp -o libbaz.so -L`pwd` -lbar
    $ ldd libbaz.so 
        linux-vdso.so.1 =>  (0x00007fff7cfe2000)
        libbar.so => not found
    

    这里libbar.so 依赖于函数foo。但是在任何库中都没有找到它,包括libssl.so。所以ldd 将共享对象libbar.so 报告为“静态链接”。在生成 libbar.so 时未找到的所有符号,将在创建依赖于 libbar.so 的最终可执行文件时进行搜索。

    反过来,libbaz.so 依赖于libbar.so,因为函数void bar() 是在我们通过-l 选项指定的上述共享对象中找到的。如果我们省略-L 选项,链接器将报告类似-lbar: not found 的错误。如果我们同时省略-L-llibbaz.so 将不会像libbar.so 那样依赖任何共享对象。

    【讨论】:

    • 我正在针对 C++ 编译 C++。特别是,我链接的库 (A) 需要 -std=c++11,尽管我的库 (B) 对于任何版本的标准都足够通用。但是任何地方都没有纯 C 代码。
    • 实际上,“按您的预期工作”示例说明了令我惊讶的行为。你明确告诉libbar.so 它应该依赖于libssl.so,它会忽略你的建议。 (我也创建了一个这样的简单示例,只是为了确定。)考虑到-l 忽略冗余库并在库不足时引发编译错误,我怎么会处于可以编译库 B 的情况,但是由于缺少我知道在 A 中的符号,它无法加载? (“A”是一个庞大的项目,其中 .so 文件取决于其他 .so 文件。我想链接 足够 个文件。)
    • @JimPivarski “由于缺少我知道在 A 中的符号而无法加载”——我的猜测是 A 或 B 中的符号名称存在拼写错误,或者 A 实际上没有导出B 要求的符号。你试过在libA.so上运行strings吗?
    【解决方案2】:

    您正在编译为一个奇怪的对象名称。创建可执行文件时会发生链接。 那么你必须提到你的各种对象和库。

    【讨论】:

    • 它是如何回答问题的?
    • 我在示例中故意使用了常规名称。你觉得哪个名字很奇怪? (我只是在问,以防我因为这样的事情而错过了重点。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-02
    • 2013-09-18
    • 2016-02-18
    • 2019-11-24
    • 2017-04-08
    • 2013-05-20
    • 1970-01-01
    相关资源
    最近更新 更多