【问题标题】:Understanding how dynamic linking works on UNIX了解动态链接在 UNIX 上的工作原理
【发布时间】:2011-05-05 16:43:56
【问题描述】:

考虑我们有以下情况:

  • 名为program 的程序动态依赖于libfoo.so
  • libfoo.so 这不依赖于任何东西(嗯,它依赖于 libstdc++ 和其他东西,但我想我们可以省略它)

program 完美运行。

突然,libfoo 代码发生了变化,现在某些函数在内部使用了 func_bar() 另一个库 libbar.so 提供的函数。

libfoo.so 被重新编译,现在依赖于libbar.soprogram 保持不变,它仍然只依赖于libfoo.so

现在当我执行program 时,它抱怨他找不到func_bar()

这是我的问题:

  • libfoo.so 接口没有改变,只有它的实现。为什么program 必须明确libbar.so 链接?
  • 依赖树不是递归的吗?我会认为由于libfoo.so 依赖于libbar.solibbar.so 会自动添加到program 的依赖列表中,无需重新编译。但是,ldd program 表明情况并非如此。

每次库的依赖关系发生变化时,必须重新编译(重新链接)依赖于某个库的每个二进制文件,这似乎很奇怪。我有什么解决方案可以防止这种情况发生?

【问题讨论】:

  • 你能发布一个重现问题的最小配置吗? vitaut 在他的帖子之后的 cmets 似乎描述了相同的过程,并没有遇到相同的问题。也许如果你把简单的步骤,我们可以帮助回答这个问题?

标签: c++ linux g++ shared-libraries dynamic-linking


【解决方案1】:

当您没有将libfoo.solibbar 关联时,就会出现问题。当你编译一个可执行文件时,默认情况下链接器不会让你留下未定义的引用。但是,当您编译共享库时,它 - 并且希望它们在链接时得到满足。这样libfoo 可以使用program 本身导出的函数- 当您尝试运行它时,动态链接器期望func_bar()program 提供。问题说明如下:

foo.c 是独立的)

export LD_RUN_PATH=`pwd`
gcc -Wall -shared foo.c -o libfoo.so
gcc -Wall -L. p.c -lfoo -o p

此时,./p 运行正常,正如您所期望的那样。然后我们创建libbar.so 并修改foo.c 以使用它:

gcc -Wall -shared bar.c -o libbar.so
gcc -Wall -shared foo.c -o libfoo.so

此时,./p 给出了您描述的错误。如果我们检查ldd libfoo.so,我们注意到它确实依赖于libbar.so - 这是错误。要更正错误,我们必须正确链接libfoo.so

gcc -Wall -L. -lbar -shared foo.c -o libfoo.so

此时,./p 再次正确运行,ldd libfoo.so 显示对libbar.so 的依赖。

【讨论】:

  • 你是对的:我没有链接到libbar.so。现在就像一个魅力!非常感谢!
【解决方案2】:

在 Fedora 上,动态链接由 ld-linux.so.2 执行。 动态链接器使用 /etc/ld.so.cache 和 /etc/ld.so.preload 来查找库文件。

运行 ldconfig 告诉系统 libfoo 应该在哪里寻找 libbar。

ldconfig 查找 /lib、/usr/lib 和 /etc/ld.so.conf 中列出的任何目录。 您可以通过 ldd 检查程序使用了哪些库。

更多详细信息可在每个命令的手册页上找到。

这是一个使用共享库的应用程序示例。
程序.cc

#include "foo.h"
#include <iostream>

int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; ++i) {
        std::cout << func_foo(argv[i]) << std::endl;
    }
}

foo.h

#ifndef FOO_H
#define FOO_H
#include <string>
std::string func_foo(std::string const &);
#endif

foo.cc

#include "foo.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__;
}

bar.h

#ifndef BAR_H
#define BAR_H
#include <string>
std::string func_bar();
#endif

bar.cc

#include "bar.h"

std::string func_bar()
{
    return __func__;
}

使用 libfoo.so 作为共享库进行构建。
g++ -Wall -Wextra -fPIC -shared foo.cc -o libfoo.so
g++ -lfoo -L./ -Wall -Wextra 程序.cc foo.h -o 程序
ldd 程序
...
libfoo.so => 未找到

更新 /etc/ld.so.cache
sudo ldconfig /home/tobias/projects/stubs/so/

ldd 显示动态链接器找到了 libfoo.so
ldd 程序
...
libfoo.so => /home/tobias/projects/stubs/so/libfoo.so (0x00007f0bb9f15000)

在 libfoo.so 中添加对 libbar.so 的调用
新的 foo.cc

#include "foo.h"
#include "bar.h"

std::string func_foo(std::string const &arg)
{
    return arg + "|" + __func__ + "|" + func_bar();
}

构建 libbar.so 并重新构建 libfoo.so
g++ -Wall -Wextra -fPIC -shared bar.cc -o libbar.so
g++ -Wall -Wextra -fPIC -shared libbar.so foo.cc -o libfoo.so
ldd libfoo.so
...
libbar.so => 未找到

ldd 程序
...
libfoo.so => /home/tobias/projects/stubs/so/libfoo.so (0x00007f49236c7000)
libbar.so => 未找到
这表明动态链接器仍然找到 libfoo.so 而不是 libbar.so
再次更新 /etc/ld.so.cache 并重新检查。
sudo ldconfig /home/tobias/projects/stubs/so/
ldd libfoo.so
...
libbar.so => /home/tobias/projects/stubs/so/libbar.so (0x00007f935e0bd000)

ldd 程序
...
libfoo.so => /home/tobias/projects/stubs/so/libfoo.so (0x00007f2be4f11000)
libbar.so => /home/tobias/projects/stubs/so/libbar.so (0x00007f2be4d0e000)

libfoo.so 和 libbar.so 都找到了。

注意最后一步对应用程序没有影响。 如果你真的很严格,运行 ldconfig 是一种重新链接。 链接器是否奇怪需要知道它链接的库的依赖关系。 有很多其他方法可以实现这一点,但选择了这个。

【讨论】:

    【解决方案3】:

    您没有提供任何系统信息,您使用的是 glibc 吗?如果是,此命令的输出是什么:

    LD_DEBUG=文件程序

    同时检查"How to write shared (ELF) libraries"(pdf)(你是否使用glibc)

    【讨论】:

      【解决方案4】:

      您的程序不必与 libbar.so 链接。

      我认为问题是由于在构建后者时未能将libbar.so 指定为libfoo.so 的依赖项。 我不确定您使用的是什么构建系统,但在 CMake 中可以按如下方式完成:

      add_library(bar SHARED bar.c)
      
      add_library(foo SHARED foo.c)
      target_link_libraries(foo bar)
      
      add_executable(program program.c)
      target_link_libraries(program foo)
      

      如您所见,program 仅与 foo (libfoo.so) 链接,foo 仅与 bar (libbar.so) 链接。

      或者可能是找不到libbar.so。尝试在LD_LIBRARY_PATH 环境变量中指定其目录的路径。

      【讨论】:

      • 问题是 CMake 在幕后做了什么,它可能足够聪明,可以为程序构建正确的链接命令
      • @Peer Stitzinger:好问题。我考虑了这一点并做了一些实验(构建programfoo 没有指向bar 的链接,然后构建foobar,但没有program)并且它起作用了。因此,在这种情况下,cmake 方面的魔法似乎不太可能,尽管它确实很聪明 =)。
      • 这真的回答了这个问题吗?当然,将libbarlibfoo 链接并thenprogram 链接将起作用,独立于构建系统。但是,如果programlibfoo 链接,然后libfoo 更新为与libbar 链接,则情况可能并非如此。如果您的 CMake 脚本,很明显 programlibfoo 重新链接,如果 libfoo 更改为与 libbar 链接而没有任何魔法。
      • 尝试以下测试:编写 2 个 CMake 脚本,一个通过与 libfoo 链接构建 program,另一个构建 libfoo。按照 OP 的要求进行操作:构建 libfoo,然后构建 program 并运行测试。然后,将libfoo 更改为与libbar 链接,重新构建libfoo 但不要重新构建program。试试看它是否仍然有效。
      • @André Caron:很抱歉,如果我的回答不清楚,但我几乎完全按照您在上一篇文章中的描述进行了操作。
      【解决方案5】:

      除非关于 bar_func symbol 的某些内容发生了变化,否则情况不应如此。使用“nm”命令获取程序和共享对象中的符号转储 - 查看是否存在不匹配以及原因。

      【讨论】:

      • 我读到的问题是bar_func()的变化是以前没用过,现在用了。
      猜你喜欢
      • 1970-01-01
      • 2019-08-26
      • 2011-11-11
      • 2016-02-20
      • 2012-12-11
      • 2011-02-18
      • 2018-02-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多