考虑我们有以下情况:
program
,它依赖于libfoo.so
libfoo.so
依赖于任何东西(嗯,这取决于libstdc++
和东西,但我想我们可以省略) program
运行完美。
突然之间, libfoo
代码发生了变化,现在一些函数在内部使用func_bar()
函数,该函数由另一个库libbar.so
。
libfoo.so
被重新编译,现在取决于libbar.so
。 program
保持不变,它依然只依赖于libfoo.so
。
现在当我执行program
它抱怨说他找不到func_bar()
。
这是我的问题:
libfoo.so
接口没有改变,只有它的实现。 为什么program
必须明确地链接到libbar.so
? libfoo.so
依赖于libbar.so
, libbar.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.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
环境变量中指定其目录的路径。
这不应该是这样,除非有关bar_func 符号的东西改变。 使用“nm”命令获取程序和共享对象中的符号转储 – 查看是否存在不匹配以及原因。