c - 为什么 ld 在将可执行文件与 a 链接时需要 -rpath-link 以便需要另一个 so ?

标签 c gcc shared-libraries ld rpath

我只是在这里很好奇。我创建了一个共享对象:

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 linking test? Because to me it doesn't seem like ld is doing much else than confirming liba.so's existence. For instance, running readelf --dynamic ./test only lists libb.so as needed, so I guess the dynamic linker must discover the libb.so -> liba.so dependency on its own, and make it's own search for liba.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.solibb.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.solibb.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/

相关文章:

google-apps-script - 找到一种方法来获取使用的库版本?

指向 char 数组的 char 指针

GCC 常量函数输入的编译时检查

gcc - 使用外部工具链构建根 - 创建根文件系统

c - 在 osx (.so) 中构建共享库

shared-libraries - 从 cargo dylib 命名中删除哈希扩展

linux - 为什么共享库要用GOT(Global Offset Table)来实现?

c - 带有 TCC 的 GNU Autotools

c - 指向函数返回的指针

c - vim/ubuntu 中的双引号?