我对共享库的二级依赖有一个问题。假设我们有如下的依赖树:
libA.so
├─libB.so
└─libC.so
└─libD.so
也就是说,共享库a依赖于共享库B和C,而共享库C依赖于共享库d。libC
的一个很好的例子是GSL共享库,它依赖于CBLAS。
做一个自给自足的易于使用的不同版本的安装包,避免问题共享库,我包库libB
libC
和libD
libA
,解释,例如,在Drepper的文章,"如何写共享库"(2011),我添加链接标志-Wl,-rpath,$ORIGIN --enable-new-dtags
设置RUN_PATH
libA.so
,以便动态加载程序可以找到libA
的依赖性在相应的目录,无需设置LD_LIBRARY_PATH
(缺点)。
问题是在libA
中设置RUN_PATH
对次要依赖项(如libD.so
)没有帮助。打开动态加载器调试消息(通过设置LD_DEBUG
)显示加载器只尝试在标准库位置查找libD.so
,而不是在libA
的位置(与查找libB
或libC
相反)。
有办法克服这个问题吗?
实际上,如果我有源代码或正确编译的静态库,我可以静态地链接库C和D。但是有没有更好的办法呢?
解决方案:
如Employed Russian
所述,解决方案是设置RPATH
而不是RUNPATH
。对于最新版本的GCC,应该使用以下链接标志:
-Wl,--disable-new-dtags,-rpath,$ORIGIN
RPATH
查找传递依赖项;也就是说,RPATH
中的路径将被考虑为动态加载的所有内容,甚至是依赖项的依赖项。
相比之下,ld
动态链接器不搜索RUNPATH
位置的传递依赖关系(不像RPATH
)。
参见:
- 维基百科:https://en.wikipedia.org/wiki/Rpath The_role_of_GNU_ld 问题:如何在GCC/LD中设置RPATH和RUNPATH ?
通过设置DT_RUNPATH
,您告诉加载器每个二进制文件都与其所有依赖项相链接。
但是对于libC.so
来说不是真的——它(显然)没有自己的DT_RUNPATH
。
我可以静态链接库C和D,…但是有没有更好的办法呢?
是:链接libC.so
与正确的DT_RUNPATH
(如果libC.so
和libD.so
在同一目录下,那么-Wl,-rpath,$ORIGIN
也将适用于libC.so
)。
更新:
问题是我可能没有
libC
的源文件或正确编译的目标文件
在这种情况下,你应该使用RPATH
而不是RUNPATH
。与后者不同,前者适用于对象本身以及该对象的所有依赖项。
换句话说,在这种情况下使用--enable-new-dtags
是错误的——您需要的是相反的。
在这种情况下,没有解决方案(除了静态链接);正确吗?
另一个解决方案(除了使用RPATH
)是在环境中设置LD_LIBRARY_PATH
。
更新2:
RPATH
与RUNPATH
的差异
差异在ld.so手册页中解释:
If a shared object dependency does not contain a slash, then it
is searched for in the following order:
o Using the directories specified in the DT_RPATH dynamic
section attribute of the binary if present and DT_RUNPATH
attribute does not exist. Use of DT_RPATH is deprecated.
...
o Using the directories specified in the DT_RUNPATH dynamic
section attribute of the binary if present. Such directories
are searched only to find those objects required by DT_NEEDED
(direct dependencies) entries and do not apply to those
objects' children, which must themselves have their own
DT_RUNPATH entries. This is unlike DT_RPATH, which is applied
to searches for all children in the dependency tree.