了解dynamic链接如何在UNIX上工作

考虑我们有以下情况:

  • 一个名为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
  • 不是依赖树recursion? 我会认为,因为libfoo.so依赖于libbar.solibbar.so会自动添加到program的依赖列表中, 而无需重新编译。 但是, ldd program表明情况并非如此。

似乎很奇怪,每次依赖库改变时,都必须重新编译(重新链接)依赖于某个库的每个二进制文件 。 我有什么解决scheme来防止这种情况?

libfoo.so没有链接到libbar时,问题就出现了。 当你编译一个可执行文件时,默认情况下链接器不会让你留下未定义的引用。 然而,当你编译一个共享库的时候,它 – 并且期望它们在链接时被满足。 这样libfoo就可以使用program本身导出的函数 – 当你试图运行它时,动态链接器需要program提供func_bar() 。 问题如下所示:

foo.c是selfcontained)

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

在这一点上,./ ./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的依赖关系。

在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的库。

每个命令的手册页上都提供了更多详细信息。

以下是使用共享库的应用程序示例。
Program.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 program.cc foo.h -o program
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程序

/ lib / lib /
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是一种重新链接。 奇怪或不连接器需要知道它链接的库的依赖关系。 还有很多其他的方法来实现这个,但这是选择。

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

LD_DEBUG =文件程序

另外检查“如何编写共享(ELF)库” (pdf)(不管您是否使用glibc)

你的程序不需要链接libbar.so。

我认为这个问题是由于没有将libbar.solibfoo.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只与foolibfoo.so )链接,而foo只与barlibbar.so )链接。

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

这不应该是这样,除非有关bar_func 符号的东西改变。 使用“nm”命令获取程序和共享对象中的符号转储 – 查看是否存在不匹配以及原因。