我只是在这里很好奇。我创建了一个共享对象:
gcc -o liba.so -fPIC -shared liba.c
还有一个共享对象,它与前一个链接:
gcc -o libb.so -fPIC -shared libb.c liba.so
现在,当创建一个链接到
libb.so
的可执行文件时,我必须指定 -rpath-link 到 ld 以便它可以找到 liba.so
当发现libb.so
取决于它:gcc -o test -Wl,-rpath-link,./ test.c libb.so
否则ld会提示。
为什么,那个 ld 必须能够定位
liba.so
链接时test
?因为对我来说,除了确认 liba.so
ld 似乎没有做太多其他的事情的存在。例如,运行 readelf --dynamic ./test
仅列出 libb.so
根据需要,所以我猜动态链接器必须发现 libb.so -> liba.so
依赖自己,并自行搜索 liba.so
.我在 x86-64 GNU/Linux 平台上,main() 例程在
test
调用 libb.so
中的函数依次调用 liba.so
中的函数.
最佳答案
Why is it, that ld MUST be able to locate
liba.so
when linkingtest
? Because to me it doesn't seem like ld is doing much else than confirmingliba.so
's existence. For instance, runningreadelf --dynamic ./test
only listslibb.so
as needed, so I guess the dynamic linker must discover thelibb.so -> liba.so
dependency on its own, and make it's own search forliba.so
.
好吧,如果我正确理解链接过程,ld 实际上甚至不需要定位
libb.so
.它可以忽略 test
中所有未解析的引用希望动态链接器在加载 libb.so
时能够解决它们在运行时。但是如果 ld 这样做,许多“ undefined reference ”错误将不会在链接时检测到,而是会在尝试加载 test
时发现。在运行时。 So ld 只是额外检查所有在 test
中找不到的符号本身确实可以在 test
的共享库中找到。取决于。所以如果 test
程序有“ undefined reference ”错误(在 test
本身和 libb.so
中都没有找到某些变量或函数),这在链接时变得明显,而不仅仅是在运行时。因此,这种行为只是额外的健全性检查。但 ld 更进一步。当您链接时
test
, ld 还会检查 libb.so
中所有未解析的引用在共享库中找到 libb.so
取决于(在我们的例子中 libb.so
取决于 liba.so
,所以它需要在链接时定位 liba.so
)。好吧,实际上 ld 在链接 libb.so
时已经做了这个检查。 .为什么要第二次进行这种检查...也许 ld 的开发人员发现,当您尝试将程序链接到可能在链接时加载的过时库时,这种双重检查对于检测损坏的依赖项很有用,但现在它可以' 不会被加载,因为它所依赖的库被更新(例如,liba.so
后来被重新设计并且其中的一些函数被删除了)。UPD
只是做了很少的实验。看来我的假设“实际上 ld 已经完成了这项检查,当它链接
libb.so
时”是错误的。让我们假设
liba.c
有以下内容:int liba_func(int i)
{
return i + 1;
}
和
libb.c
有下一个:int liba_func(int i);
int liba_nonexistent_func(int i);
int libb_func(int i)
{
return liba_func(i + 1) + liba_nonexistent_func(i + 2);
}
和
test.c
#include <stdio.h>
int libb_func(int i);
int main(int argc, char *argv[])
{
fprintf(stdout, "%d\n", libb_func(argc));
return 0;
}
链接时
libb.so
:gcc -o libb.so -fPIC -shared libb.c liba.so
链接器不会生成任何错误消息
liba_nonexistent_func
无法解决,它只是默默地生成损坏的共享库 libb.so
.行为与您使用 ar 制作静态库 ( libb.a
) 相同,它也不会解析生成的库的符号。但是当您尝试链接时
test
:gcc -o test -Wl,-rpath-link=./ test.c libb.so
你得到错误:
libb.so: undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status
如果 ld 不递归扫描所有共享库,则无法检测到此类错误。因此,问题的答案似乎与我上面所说的相同: ld 需要 -rpath-link 以确保稍后可以通过动态加载加载链接的可执行文件。只是一个健全的检查。
UPD2
尽早检查未解析的引用是有意义的(当链接
libb.so
时),但 ld 由于某些原因并没有这样做。这可能是为了允许为共享库创建循环依赖项。liba.c
可以有以下实现:int libb_func(int i);
int liba_func(int i)
{
int (*func_ptr)(int) = libb_func;
return i + (int)func_ptr;
}
所以
liba.so
用途 libb.so
和 libb.so
用途 liba.so
(最好永远不要做这样的事情)。这成功编译并运行:$ gcc -o liba.so -fPIC -shared liba.c
$ gcc -o libb.so -fPIC -shared libb.c liba.so
$ gcc -o test test.c -Wl,-rpath=./ libb.so
$ ./test
-1217026998
虽然 readelf 说
liba.so
不需要libb.so
:$ readelf -d liba.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [libc.so.6]
$ readelf -d libb.so | grep NEEDED
0x00000001 (NEEDED) Shared library: [liba.so]
0x00000001 (NEEDED) Shared library: [libc.so.6]
如果 ld 在链接共享库期间检查未解析的符号,则链接
liba.so
不可能。请注意,我使用了 -rpath 键而不是 -rpath-link .不同之处在于 -rpath-link 在链接时仅用于检查最终可执行文件中的所有符号是否都可以解析,而 -rpath 实际上将您指定为参数的路径嵌入到 ELF 中:
$ readelf -d test | grep RPATH
0x0000000f (RPATH) Library rpath: [./]
所以现在可以运行
test
如果共享库( liba.so
和 libb.so
)位于您当前的工作目录( ./
)。如果您只是使用了 -rpath-link,那么 test
中将没有这样的条目ELF,您必须将共享库的路径添加到 /etc/ld.so.conf
文件或到 LD_LIBRARY_PATH
环境变量。UPD3
实际上可以在链接共享库期间检查未解析的符号,
--no-undefined
选项必须用于这样做:$ gcc -Wl,--no-undefined -o libb.so -fPIC -shared libb.c liba.so
/tmp/cc1D6uiS.o: In function `libb_func':
libb.c:(.text+0x2d): undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status
我还发现了一篇很好的文章,它阐明了链接依赖于其他共享库的共享库的许多方面:
Better understanding Linux secondary dependencies solving with examples .
关于c - 为什么 ld 在将可执行文件与 a 链接时需要 -rpath-link 以便需要另一个 so ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24598047/