Tro*_*lke 37 c gcc rpath shared-libraries ld
我只是好奇.我创建了一个共享对象:
gcc -o liba.so -fPIC -shared liba.c
Run Code Online (Sandbox Code Playgroud)
还有一个共享对象,它与前者相关联:
gcc -o libb.so -fPIC -shared libb.c liba.so
Run Code Online (Sandbox Code Playgroud)
现在,在创建链接的可执行文件时libb.so,我必须指定-rpath-link to ld,以便在发现liba.so时可以找到它libb.so依赖于它:
gcc -o test -Wl,-rpath-link,./ test.c libb.so
Run Code Online (Sandbox Code Playgroud)
否则ld会抱怨.
为什么,ld必须能够liba.so在链接时找到test?因为对我而言,似乎ld在确认liba.so存在方面做的事情并不多.例如,根据需要readelf --dynamic ./test只运行列表libb.so,所以我想动态链接器必须发现libb.so -> liba.so它自己的依赖关系,并使它自己搜索liba.so.
我在x86-64 GNU/Linux平台上,而main() - 例程在test调用函数libb.so时依次调用函数liba.so.
ant*_*_rh 28
为什么,ld必须能够
liba.so在链接时找到test?因为对我而言,似乎ld在确认liba.so存在方面做的事情并不多.例如,根据需要readelf --dynamic ./test只运行列表libb.so,所以我想动态链接器必须发现libb.so -> liba.so它自己的依赖关系,并使它自己搜索liba.so.
好吧,如果我理解正确的链接过程,ld实际上不需要找到甚至libb.so.它可以忽略所有未解析的引用,test希望动态链接器libb.so在运行时加载时解析它们.但是如果ld以这种方式进行,则在链接时不会检测到许多"未定义的引用"错误,而是在尝试test在运行时加载时会发现它们.所以ld只是做了额外的检查,test在所test依赖的共享库中真正找到了自身找不到的所有符号.因此,如果test程序有"未定义的引用"错误(某些变量或函数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;
}
Run Code Online (Sandbox Code Playgroud)
并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);
}
Run Code Online (Sandbox Code Playgroud)
和 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;
}
Run Code Online (Sandbox Code Playgroud)
链接时libb.so:
gcc -o libb.so -fPIC -shared libb.c liba.so
Run Code Online (Sandbox Code Playgroud)
链接器不会生成任何liba_nonexistent_func无法解析的错误消息,而只是静默生成损坏的共享库libb.so.该行为libb.a与使用ar生成静态库()的行为相同,该库也不解析生成的库的符号.
但是当你尝试链接时test:
gcc -o test -Wl,-rpath-link=./ test.c libb.so
Run Code Online (Sandbox Code Playgroud)
你得到错误:
libb.so: undefined reference to `liba_nonexistent_func'
collect2: ld returned 1 exit status
Run Code Online (Sandbox Code Playgroud)
如果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;
}
Run Code Online (Sandbox Code Playgroud)
所以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
Run Code Online (Sandbox Code Playgroud)
虽然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]
Run Code Online (Sandbox Code Playgroud)
如果ld在链接共享库期间检查了未解析的符号,则liba.so无法进行链接.
请注意,我使用了-rpath密钥而不是-rpath-link.不同之处在于-rpath-link仅在链接时用于检查最终可执行文件中的所有符号是否可以解析,而-rpath实际上将您指定的路径嵌入到ELF中:
$ readelf -d test | grep RPATH
0x0000000f (RPATH) Library rpath: [./]
Run Code Online (Sandbox Code Playgroud)
因此,test如果共享库(liba.so和libb.so)位于当前工作目录(./),则现在可以运行.如果您刚刚使用-rpath-link,那么testELF中就没有这样的条目,您必须将共享库的路径添加到/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
Run Code Online (Sandbox Code Playgroud)
此外,我发现了一篇很好的文章,阐明了链接依赖于其他共享库的共享库的许多方面: 通过示例更好地理解Linux二级依赖项.
系统,通过ld.so.conf,ld.so.conf.d和系统环境LD_LIBRARY_PATH等提供了系统范围的库搜索路径,pkg-config当您针对标准库构建时,这些路径由安装的库通过信息等补充.当库位于定义的搜索路径中时,将自动遵循标准库搜索路径,从而允许找到所有必需的库.
您自己创建的自定义共享库没有标准的运行时库搜索路径.您可以-L/path/to/lib在编译和链接期间通过指定指定库的搜索路径.对于非标准位置的库,可以在编译时将库搜索路径选择性地放在可执行文件(ELF头)的头中,以便可执行文件可以找到所需的库.
rpath提供了一种在ELF头中嵌入自定义运行时库搜索路径的方法,以便可以找到自定义库,而无需在每次使用时指定搜索路径.这适用于依赖库的库.正如您所发现的,不仅命令行上指定库的顺序很重要,还必须为要链接的每个依赖库提供运行时库搜索路径或rpath信息,以便标题包含运行所需的所有库的位置.
来自评论的Addemdum
我的问题主要是为什么ld必须"自动尝试找到共享库"(liba.so)并"将其包含在链接中".
这就是方式ld.从man ld"-rpath选项也用于查找链接中明确包含的共享对象所需的共享对象 ...如果在链接ELF可执行文件时未使用-rpath,则环境变量"LD_RUN_PATH"的内容将为如果被定义则使用." 你的情况liba是不是地处LD_RUN_PATH所以ld将需要有一种方式定位liba您的可执行文件的编译过程中,无论是与rpath(如上所述)或通过提供给它一个明确的搜索路径.
其次,"将其包含在链接中"的确意味着什么.对我来说,它似乎意味着:"确认它的存在"(liba.so's),因为libb.so的ELF头文件没有被修改(它们已经有一个针对liba.so的NEEDED标签),而exec的头文件只声明了libb.所以需要.为什么ld关心如何找到liba.so,它是否可以将任务留给运行时链接器?
不,回到语义ld.为了产生"良好的链接",ld必须能够找到所有依赖的库.ld否则无法保证良好的联系.运行时链接程序必须查找并加载,而不仅仅是查找程序所需的共享库.ld不能保证会发生,除非ld它自己可以在程序链接时找到所有需要的共享库.
我猜您需要知道何时使用-rpathoption和-rpath-linkoption。首先,我引用man ld指定的内容:
- -rpath和-rpath-link之间的区别在于,由-rpath选项指定的目录包含在可执行文件中并在运行时使用,而-rpath-link选项仅在链接时有效。只有使用--with-sysroot选项配置的本机链接程序和交叉链接程序才支持以这种方式搜索-rpath。
您必须区分链接时间和运行时。根据您接受的anton_rh的回答,在编译和链接共享库或静态库时,不会启用检查未定义符号的功能,而在编译和链接可执行文件时,则启用ENABLED的功能。(但是,请注意,有些文件是共享库以及可执行文件,例如ld.so。。键入man ld.so以进行探索,并且我不知道在编译这些“双重”文件时是否启用了未定义符号检查功能”)。
So -rpath-link用于链接时检查,并-rpath用于链接时和运行时,因为它rpath已嵌入到ELF标头中。但是,请注意,如果同时指定了两个-rpath-link选项,则该-rpath选项在链接时将覆盖选项。
但是,为什么-rpath-option还有-rpath选择呢?我认为它们用于消除“超链接”。请参阅此示例,更好地了解Linux二级依赖性。,只需使用ctrl + F导航到与“超链接”相关的内容即可。您应该关注为什么“ overlinking”不好,并且由于我们采用了避免“ overlinking”的方法,因此存在ld选项-rpath-link并且-rpath是合理的:我们在命令中故意省略了一些库来进行编译和链接,以避免“ overlinking”,并且由于省略,因此ld需要-rpath-link或-rpath找到这些省略的库。
| 归档时间: |
|
| 查看次数: |
30553 次 |
| 最近记录: |