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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24598047/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 11:12:22  来源:igfitidea点击:

Why does ld need -rpath-link when linking an executable against a so that needs another so?

cgccshared-librariesldrpath

提问by Troels Folke

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

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

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

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

otherwise ld will complain.

否则ld会抱怨。

Why is it, that ld MUST be able to locate liba.sowhen 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 ./testonly lists libb.soas needed, so I guess the dynamic linker must discover the libb.so -> liba.sodependency on its own, and make it's own search for liba.so.

为什么 ld 必须能够liba.so在链接时定位test?因为对我来说, ld 似乎除了确认liba.so' 的存在之外没有做太多其他事情。例如,readelf --dynamic ./testlibb.so根据需要运行列表,所以我猜动态链接器必须libb.so -> liba.so自己发现依赖关系,并自行搜索liba.so.

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

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

采纳答案by anton_rh

Why is it, that ld MUST be able to locate liba.sowhen 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 ./testonly lists libb.soas needed, so I guess the dynamic linker must discover the libb.so -> liba.sodependency on its own, and make it's own search for liba.so.

为什么 ld 必须能够liba.so在链接时定位test?因为对我来说, ld 似乎除了确认liba.so' 的存在之外没有做太多其他事情。例如,readelf --dynamic ./testlibb.so根据需要运行列表,所以我猜动态链接器必须libb.so -> liba.so自己发现依赖关系,并自行搜索liba.so.

Well if I understand linking process correctly, ldactually does not need to locate even libb.so. It could just ignore all unresolved references in testhoping that dynamic linker would resolve them when loading libb.soat runtime. But if ldwere doing in this way, many "undefined reference" errors would not be detected at link time, instead they would be found when trying to load testin runtime. So ldjust does additional checking that all symbols not found in testitself can be really found in shared libraries that testdepend on. So if testprogram has "undefined reference" error (some variable or function not found in testitself 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实际上甚至不需要定位libb.so. 它可以忽略所有未解析的引用,test希望动态链接器libb.so在运行时加载时解析它们。但是如果ld这样做,在链接时将不会检测到许多“未定义引用”错误,而是在尝试test在运行时加载时发现它们。所以ld只是做了额外的检查,所有在test自身中找不到的符号都可以在test依赖的共享库中真正找到。因此,如果test程序有“未定义的引用”错误(某些变量或函数test本身没有找到,也没有在libb.so),这在链接时变得明显,而不仅仅是在运行时。因此,这种行为只是额外的健全性检查。

But ldgoes even further. When you link test, ldalso checks that all unresolved references in libb.soare found in the shared libraries that libb.sodepends on (in our case libb.sodepends on liba.so, so it requires liba.soto be located at link time). Well, actually ldhas already done this checking, when it was linking libb.so. Why does it do this checking second time... Maybe developers of ldfound 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.sowas later reworked and some of the function was removed from it).

ld更进一步。当您 link 时testld还会检查在依赖libb.so的共享库中是否找到了所有未解析的引用libb.so(在我们的情况下libb.so取决于liba.so,因此它需要liba.so在链接时定位)。好吧,实际上ld在链接libb.so. 为什么要第二次进行这种检查...也许ld 的开发人员发现,当您尝试将程序链接到可能在链接时加载的过时库时,这种双重检查对于检测损坏的依赖项很有用,但现在它可以' 不会被加载,因为它所依赖的库已更新(例如,liba.so后来进行了重新设计,并从中删除了一些功能)。

UPD

UPD

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

只是做了很少的实验。看来我的假设“实际上 ld 在链接时已经完成了这项检查libb.so是错误的。

Let us suppose the liba.chas the following content:

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

int liba_func(int i)
{
    return i + 1;
}

and libb.chas the next:

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);
}

and test.c

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;
}

When linking libb.so:

链接时libb.so

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

linker doesn't generate any error messages that liba_nonexistent_funccannot 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 arwhich doesn't resolve symbols of the generated library too.

链接器不会生成任何liba_nonexistent_func无法解析的错误消息,而只是默默地生成损坏的共享库libb.so。行为与您libb.a使用ar制作静态库 ( ) 相同,它也不会解析生成库的符号。

But when you try to link test:

但是当您尝试链接时test

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

you get the error:

你得到错误:

libb.so: undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status

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

如果ld不递归扫描所有共享库,则无法检测到此类错误。因此,问题的答案似乎与我上面所说的相同:ld需要-rpath-link以确保稍后可以通过动态加载加载链接的可执行文件。只是一个健全的检查。

UPD2

UPD2

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

尽早检查未解析的引用(链接时libb.so)是有意义的,但ld由于某些原因并没有这样做。这可能是为了允许为共享库创建循环依赖项。

liba.ccan have the following implementation:

liba.c可以有以下实现:

int libb_func(int i);

int liba_func(int i)
{
    int (*func_ptr)(int) = libb_func;
    return i + (int)func_ptr;
}

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

所以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

Though readelfsays that liba.sodoesn't need libb.so:

虽然readelfliba.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]

If ldchecked for unresolved symbols during the linking of a shared library, the linking of liba.sowould not be possible.

如果ld在链接共享库期间检查未解析的符号,则liba.so无法链接。

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

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

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

So it's now possible to run testif the shared libraries (liba.soand libb.so) are located at your current working directory (./). If you just used -rpath-linkthere would be no such entry in testELF, and you would have to add the path to the shared libraries to the /etc/ld.so.conffile or to the LD_LIBRARY_PATHenvironment variable.

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

UPD3

UPD3

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

实际上可以在链接共享库期间检查未解析的符号,--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

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.

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

回答by Han XIAO

I guess you need to know when to use -rpathoption and -rpath-linkoption. First I quote what man ldspecified :

我想您需要知道何时使用-rpath选项和-rpath-link选项。首先我引用man ld指定的内容:

  1. The difference between -rpath and -rpath-link is that directories specified by -rpath options are included in the executable and used at runtime, whereas the -rpath-link option is only effective at link time. Searching -rpath in this way is only supported by native linkers and cross linkers which have been configured with the --with-sysroot option.
  1. -rpath 和 -rpath-link 之间的区别在于 -rpath 选项指定的目录包含在可执行文件中并在运行时使用,而 -rpath-link 选项仅在链接时有效。以这种方式搜索 -rpath 仅支持使用 --with-sysroot 选项配置的本机链接器和交叉链接器。

You must distinguish between link-time and runtime. According to your accepted anton_rh's answer, checking for undefined symbols is not enabled when compiling and linking shared libraries or static libraries, but ENABLED when compiling and linking executables. (However, please note that there exist some files which are shared library as well as executables, for example, ld.so. Type man ld.soto explore this, and I don't know whether or not checking for undefined symbols is enabled when compiling these files of "dual" kinds).

您必须区分链接时和运行时。根据您接受的 anton_rh 的回答,在编译和链接共享库或静态库时未启用检查未定义符号,但在编译和链接可执行文件时启用检查。(但是,请注意,存在一些共享库和可执行文件的文件,例如,ld.so。键入man ld.so来探索这一点,我不知道在编译这些“双重” 种)。

So -rpath-linkis used in link-time checking, and -rpathis used for link-time and runtime because rpathis embedded into ELF headers. But you should be careful that -rpath-linkoption will override -rpathoption during link-time if both of them are specified.

So-rpath-link用于链接时间检查,并且-rpath用于链接时间和运行时,因为rpath它嵌入到 ELF 标头中。但是你应该小心,如果这两个选项都被指定,-rpath-link选项将-rpath在链接时覆盖选项。

But still, why -rpath-optionand -rpathoption? I think they are used for eliminating "overlinking". See this Better understanding Linux secondary dependencies solving with examples., simply use ctrl + Fto navigate to contents related to "overlinking". You should focus on why "overlinking" is bad, and because of the method we adopt to avoid "overlinking", the existence of ldoptions -rpath-linkand -rpathis reasonable: we deliberately omit some libraries in the commands for compiling and linking to avoid "overlinking", and because of omitting, ldneed -rpath-linkor -rpathto locate these omitted libraries.

但是,为什么-rpath-option-rpath选择?我认为它们用于消除“过度链接”。请参阅此通过示例更好地理解 Linux 次要依赖项解决方案。,仅用于ctrl + F导航到与“过度链接”相关的内容。你应该关注为什么“过度链接”是不好的,因为我们采取的避免“过度链接”的方法,ld选项的存在-rpath-link-rpath合理:我们在编译和链接的命令中故意省略了一些库,以避免“过度链接”,并且由于省略,ld需要-rpath-link-rpath定位这些省略的库。

回答by David C. Rankin

You system, through ld.so.conf, ld.so.conf.d, and the system environment, LD_LIBRARY_PATH, etc.., provides the system-widelibrary search paths which are supplemented by installed libraries through pkg-configinformation and the like when you build against standard libraries. When a library resides in a defined search path, the standard library search paths are followed automatically allowing all required libraries to be found.

您的系统,通过ld.so.confld.so.conf.d和系统环境,LD_LIBRARY_PATH等等,提供系统范围的库搜索路径,pkg-config当您针对标准库进行构建时,这些路径由已安装的库通过信息等补充。当库位于定义的搜索路径中时,将自动遵循标准库搜索路径,从而可以找到所有需要的库。

There is no standard run-time library search pathfor custom shared libraries you create yourself. You specify the search path to your libraries through the -L/path/to/libdesignation during compile and link. For libraries in non-standard locations, the library search path can be optionally placed in the header of your executable (ELF header) at compile-time so that your executable can find the needed libraries.

您自己创建的自定义共享库没有标准的运行时库搜索路径。您可以-L/path/to/lib在编译和链接期间通过名称指定库的搜索路径。对于非标准位置的库,可以在编译时将库搜索路径选择性地放置在可执行文件的头文件(ELF 头文件)中,以便您的可执行文件可以找到所需的库。

rpathprovides a way of embedding your custom run-time library search path in the ELF header so that your custom libraries can be found as well without having to specify the search path each time it is used. This applies to libraries that depend on libraries as well. As you have found, not only is the order you specify the libraries on the command line important, you also must provide the run-time library search path, or rpath, information for each dependent library you are linking against as well so that the header contains the location of alllibraries needed to run.

rpath提供了一种在 ELF 标头中嵌入自定义运行时库搜索路径的方法,以便您也可以找到自定义库,而无需在每次使用时指定搜索路径。这也适用于依赖库的库。正如您所发现的,不仅您在命令行上指定库的顺序很重要,您还必须提供运行时库搜索路径或 rpath,以及您链接的每个依赖库的信息,以便标题包含运行所需的所有库的位置。

Addemdum from Comments

来自评论的附录

My question is primarily why ld must "automatically try to locate the shared library" (liba.so) and "include it in the link".

我的问题主要是为什么 ld 必须“自动尝试定位共享库”(liba.so)并“将其包含在链接中”。

That is simply the way ldworks. From man ld"The -rpath option is also used when locating shared objects which are needed by shared objectsexplicitly included in the link ... If -rpath is not used when linking an ELF executable, the contents of the environment variable "LD_RUN_PATH" will be used if it is defined." In your case libaisn't located in the LD_RUN_PATHso ldwill need a way locating libaduring the compile of your executable, either with rpath(described above) or by providing an explicit search path to it.

这就是ld工作的方式。从man ld“在定位显式包含在链接中的共享对象所需的共享对象时,也使用 -rpath 选项......如果在链接 ELF 可执行文件时未使用 -rpath,则环境变量“LD_RUN_PATH”的内容将是如果已定义,则使用。” 在您的情况下,liba它不位于,LD_RUN_PATH因此ld需要liba在编译可执行文件期间找到一种方法,使用rpath(如上所述)或通过提供显式搜索路径来定位它。

Secondarily what "include it in the link" really means. To me it seems that it just means: "confirm it's existence" (liba.so's), since libb.so's ELF headers are not modified (they already had a NEEDED tag against liba.so), and the exec's headers only declare libb.so as NEEDED. Why does ld care about finding liba.so, can it not just leave the task to the run-time linker?

其次,“将其包含在链接中”的真正含义是什么。对我来说,它似乎只是意味着:“确认它的存在”(liba.so 的),因为 libb.so 的 ELF 标头没有被修改(它们已经有一个针对 liba.so 的 NEEDED 标签),并且 exec 的标头只声明了 libb。所以需要。为什么ld会在意寻找liba.so,难道不能只把任务留给运行时链接器吗?

No, back to the semantics of ld. In order to produce a "good link", ldmust be able to locate alldependent libraries. ldcannot insure a good link otherwise. The runtime linker must find and load, not just to findthe shared libraries needed by a program. ldcannot guarantee that will happen unless lditself can locate all needed shared librariesat the time the progam is linked.

不,回到ld. 为了产生“良好的链接”ld必须能够定位所有依赖库。ld否则无法确保良好的链接。运行时链接器必须查找和加载,而不仅仅是查找程序所需的共享库。ld不能保证会发生这种情况,除非ld它自己可以在链接程序时找到所有需要的共享库

回答by Mark Nunberg

You are not actually telling ld (when linking libbagainst liba) wherelibais - only that it's a dependency. A quick ldd libb.sowill show you that it cannot find liba.

您实际上并没有告诉 ld (链接libbliba在哪里liba- 只是它是一个依赖项。快速ldd libb.so会告诉你它找不到liba

Since presumably these libraries are not in your linker search path, you will get a linker error when you link the executable. Keep in mind that when you link liba itself, the function in libb is stillunresolved, but ld's default behavior is not to care about unresolved symbols in DSOs until you link the final executable.

由于这些库可能不在您的链接器搜索路径中,因此在链接可执行文件时会出现链接器错误。请记住,当您链接 liba 本身时,libb 中的函数仍然未解析,但ld的默认行为是在您链接最终可执行文件之前不关心 DSO 中未解析的符号。