当将可执行文件与a链接时,为什么ld需要-rpath-link而又需要一个so? [英] Why does ld need -rpath-link when linking an executable against a so that needs another so?

查看:210
本文介绍了当将可执行文件与a链接时,为什么ld需要-rpath-link而又需要一个so?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很好奇.我创建了一个共享对象:

I'm just curious here. I have created a shared object:

gcc -o liba.so -fPIC -shared liba.c

还有一个共享对象,它与前一个对象链接:

And one more shared object, that links against the former one:

gcc -o libb.so -fPIC -shared libb.c liba.so

现在,当创建链接到 libb.so 的可执行文件时,我将必须指定-rpath-link到ld,以便在发现时可以找到 liba.so libb.so 依赖于它:

Now, when creating an executable that links against libb.so, I will have to specify -rpath-link to ld so it can find liba.so when discovering that libb.so depends on it:

gcc -o test -Wl,-rpath-link,./ test.c libb.so

否则ld会抱怨.

为什么,链接 test 时ld必须能够找到 liba.so ?因为对我来说,除了确认 liba.so 的存在之外,ld似乎并没有做其他事情.例如,运行 readelf --dynamic ./test 仅根据需要列出 libb.so ,所以我猜动态链接器必须发现 libb.so->;liba.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.

我在x86-64 GNU/Linux平台上,并且 test 中的main()例程调用 libb.so 中的一个函数,该函数依次调用 liba.so 中的一个函数.

I'm on an x86-64 GNU/Linux platform, and the main()-routine in test calls a function in libb.so that in turn calls a function in liba.so.

推荐答案

为什么,链接 test 时ld必须能够找到 liba.so ?因为对我来说,除了确认 liba.so 的存在之外,ld似乎并没有做其他事情.例如,运行 readelf --dynamic ./test 仅根据需要列出 libb.so ,所以我猜动态链接器必须发现 libb.so->;liba.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 以此方式进行操作,则在链接时将不会检测到许多未定义的引用"错误,而是在尝试在运行时加载 test 时会发现它们.因此, ld 只是进行额外的检查,以确保在 test 所依赖的共享库中确实可以找到所有未在 test 中找到的符号.因此,如果 test 程序出现未定义的引用"错误(在 test 本身中没有找到某些变量或函数,而在 libb.so 中均未找到)在链接时变得很明显,而不仅仅是在运行时.因此,这种行为只是额外的健全性检查.

Well if I understand linking process correctly, ld actually does not need to locate even libb.so. It could just ignore all unresolved references in test hoping that dynamic linker would resolve them when loading libb.so at runtime. But if ld were doing in this way, many "undefined reference" errors would not be detected at link time, instead they would be found when trying to load test in runtime. So ld just does additional checking that all symbols not found in test itself can be really found in shared libraries that test depend on. So if test program has "undefined reference" error (some variable or function not found in test itself and neither in libb.so), this becomes obvious at link time, not just at runtime. Thus such behavior is just an additional sanity check.

但是 ld 走得更远.链接 test 时, ld 还会检查 libb.so 中所有未解析的引用是否在 libb.so的共享库中找到取决于(在我们的例子中, libb.so 取决于 liba.so ,因此它需要 liba.so 位于链接时间).好吧,实际上 ld 在链接 libb.so 时已经进行了此检查.为什么第二次执行此检查...也许 ld 的开发人员在尝试将程序链接到可能在其加载时已过期的过时的库中进行链接时,可能会发现这种双重检查对于检测损坏的依赖项很有用.已链接,但现在无法加载,因为它所依赖的库已更新(例如,后来对 liba.so 进行了重新设计,并从其中删除了一些功能).

But ld goes even further. When you link test, ld also checks that all unresolved references in libb.so are found in the shared libraries that libb.so depends on (in our case libb.so depends on liba.so, so it requires liba.so to be located at link time). Well, actually ld has already done this checking, when it was linking libb.so. Why does it do this checking second time... Maybe developers of ld found this double checking useful to detect broken dependencies when you try to link your program against outdated library that could be loaded in the times when it was linked, but now it can't be loaded because the libraries it depends on are updated (for example, liba.so was later reworked and some of the function was removed from it).

UPD

只做了很少的实验.看来我的假设实际上ld在链接 libb.so " 时实际上已经进行了此检查.

Just did few experiments. It seems my assumption "actually ld has already done this checking, when it was linking libb.so" is wrong.

让我们假设 liba.c 具有以下内容:

Let us suppose the liba.c has the following content:

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 )相同,该静态库也无法解析生成的库的符号.

linker doesn't generate any error messages that liba_nonexistent_func cannot be resolved, instead it just silently generate broken shared library libb.so. The behavior is the same as you would make a static library (libb.a) with ar which doesn't resolve symbols of the generated library too.

但是当您尝试链接 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 以确保可以稍后加载链接的可执行文件.通过动态加载.只是一个健全的检查.

Detecting such error would not be possible if ld didn't scan recursively all the shared libraries. So it seems that the answer to the question is the same as I told above: ld needs -rpath-link in order to make sure that the linked executable can be loaded later by dynamic loaded. Just a sanity check.

UPD2

尽早检查未解析的引用(链接 libb.so 时)是有道理的,但是出于某些原因, ld 却没有这样做.可能是为了允许对共享库进行循环依赖.

It would make sense to check for unresolved references as early as possible (when linking libb.so), but ld for some reasons doesn't do this. It's probably for allowing to make cyclic dependencies for shared libraries.

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 (最好永远不要做这样的事情).这样可以成功编译并运行:

So liba.so uses libb.so and libb.so uses liba.so (better never do such a thing). This successfully compiles and works:

$ 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 的链接将无法进行.

If ld checked for unresolved symbols during the linking of a shared library, the linking of liba.so would not be possible.

请注意,我使用的是 -rpath 键而不是 -rpath-link .区别在于, -rpath-link 在链接时仅用于检查最终可执行文件中的所有符号都可以解析,而 -rpath 实际上嵌入您指定的路径作为进入ELF的参数:

Note that I used -rpath key instead of -rpath-link. The difference is that -rpath-link is used at linking time only for checking that all symbols in the final executable can be resolved, whereas -rpath actually embeds the path you specify as parameter into the ELF:

$ readelf -d test | grep RPATH
 0x0000000f (RPATH)                      Library rpath: [./]

因此,如果共享库( liba.so libb.so )位于您当前的工作位置,则现在可以运行 test 目录( ./).如果仅使用 -rpath-link ,则 test ELF中将没有此类条目,并且您必须将共享库的路径添加到/etc/ld.so.conf 文件或 LD_LIBRARY_PATH 环境变量.

So it's now possible to run test if the shared libraries (liba.so and libb.so) are located at your current working directory (./). If you just used -rpath-link there would be no such entry in test ELF, and you would have to add the path to the shared libraries to the /etc/ld.so.conf file or to the LD_LIBRARY_PATH environment variable.

UPD3

实际上可以在链接共享库时检查未解析的符号,必须使用-no-undefined 选项:

It is actually possible to check for unresolved symbols during linking shared library, --no-undefined option must be used for doing that:

$ 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

我还找到了一篇很好的文章,阐明了链接依赖于其他共享库的共享库的许多方面:通过示例更好地理解Linux次要依赖项的解决方法.

Also I found a good article that clarifies many aspects of linking shared libraries that depend on other shared libraries: Better understanding Linux secondary dependencies solving with examples.

这篇关于当将可执行文件与a链接时,为什么ld需要-rpath-link而又需要一个so?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆