我刚刚提供了一个使用ld的-rpath
选项和$ORIGIN
here的基本示例(请参阅第二个响应,了解工作版本)。我正在尝试创建一个示例,其中main.run
链接到foo.so
,后者又链接到bar.so
,全部使用rpath
和$ORIGIN
。
运行时文件结构是:
- 项目/
- LIB /
- DIR /
- 子/
- 的 bar.so
- 的 foo.so
- 运行/
- main.run (无法构建)
我正在建立foo.so使用:
g++ -c -o obj/foo.o src/foo.cpp -fPIC
g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$ORIGIN/sub' -Llib/dir/sub -l:bar.so
哪个构建正常。 ldd lib/dir/foo.so
甚至可以找到bar.so
。
但是,当我尝试将main.run
与foo.so
关联时,foo.so
无法找到bar.so。
我正在建设main.so使用:
g++ -c -o obj/main.o src/main.cpp
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib/dir' -Llib/dir -l:foo.so
如果使用不以递归方式链接的foo.so
的其他版本,则此方法正常。 (取消注释make.sh中的行,在下面的项目中进行测试)。
但是,使用正常foo.so
我在构建main.run
时遇到此错误:
/ usr / bin / ld:警告:bar.so,lib / dir / foo.so需要,找不到(尝试使用-rpath或-rpath-link)
所以我的问题是:
$ORIGIN
是否解析为project/lib/dir
(其中foo.so
是)或project/run
(其中main.run
(链接它的可执行文件)是) ?project/lib/dir
,这似乎是最好的方式(虽然我试过假设两者)。-rpath-link
。您可以下载项目here。它就像我能做到的一样简单。 4个简短的来源和一个剧本
提取后,只需从./make.sh
内运行project/
。
注意:我正在使用-l:
。这不应该更改任何内容,除了库名称为foo.so
而不是libfoo.so
,并且-l:foo.so
而不是-lfoo
。
答案 0 :(得分:7)
嗯,我有一些工作。但我真的不明白为什么它有效。这对我来说就像是一个错误。
我为main.run编译运行strace -f -o /var/tmp/strace.out -- g++ ...
。静态链接器实际上是尝试打开其文字名称看起来像“$ ORIGIN / lib / dir / sub / bar.so”的文件,其中包括20-30个其他内容。 (换句话说,它正在寻找一个名为$ORIGIN
的实际目录。严重的。)
它似乎也在-rpath-link路径中搜索名称“lib / dir / sub / bar.so”,而不仅仅是“bar.so”。我不知道为什么。
无论如何,这是为我工作的main.run的链接:
g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib/dir' -Wl,-rpath-link,. -Llib/dir -l:foo.so
它与您的相同,但插入了-Wl,-rpath-link,.
。
[附录]
好的,我想我知道发生了什么。首先,静态链接器(GNU ld)根本不会在它链接的库中使用$ ORIGIN。
其次,使用-lbar
与-l:bar.so
时的行为非常不同。
在readelf -a
上运行foo.so
。在您的构建中,它显示依赖于“lib / dir / sub / bar.so”。这就是将rpath-link设置为“。”的原因。修复main.run的构建;它会导致静态链接器搜索“。”对于它找到的“lib / dir / sub / bar.so”。
如果你将bar.so重命名为libbar.so,并将foo.so链接到使用-lbar
而不是-l:bar.so
,则相同的readelf会显示foo.so现在依赖于“libbar.so” (没有路径组件)。使用那个foo.so,你可以使用-Wl,-rpath-link,lib/dir/sub
获得main.run链接,正如你所知,如果你知道静态链接器根本不符合$ ORIGIN。
顺便说一下,我没有在GNU ld手册中的任何地方看到-l:bar.so
语法。出于好奇,你是怎么想出来的?
假设它是一个支持的功能,这看起来有点像一个bug(-l:bar.so创建一个依赖于lib / dir / sub / bar.so而不仅仅是bar.so)。您可以通过将rpath-link设置为'。'来处理此错误。对于main.run,或者你可以用通常的方式重命名东西(libxxx.so)。
答案 1 :(得分:6)
来自ld-linux(8)联机帮助页:
$ ORIGIN和rpath
ld.so理解字符串$ ORIGIN(或等价$ {ORIGIN}) rpath规范(DT_RPATH或DT_RUNPATH)表示目录 包含应用程序可执行文因此,位于的应用程序 somedir / app可以使用gcc -Wl,-rpath,' $ ORIGIN /../ lib'进行编译。所以 它在somedir / lib中找到了一个关联的共享库,无论如何 其中somedir位于目录层次结构中。这有利于 创造"交钥匙"不需要的应用程序 安装到特殊目录中,但可以解压缩到 任何目录,仍然可以找到自己的共享库。
因此,在回答您的第一个问题时,$ORIGIN
只有一个值:project/run
。
因此,第二个问题的答案应该是使用以下命令链接foo.so
:
g++ -shared -o lib/dir/foo.so obj/foo.o -Wl,-soname,foo.so -Wl,-rpath,'$ORIGIN/../lib/dir/sub' -Llib/dir/sub -l:bar.so
答案 2 :(得分:5)
首先,$ sign扩展存在问题,可能会导致问题。我正在从源代码构建Python,我这样做:
export LDFLAGS='-Wl,-rpath,\$${ORIGIN}/../lib -Wl,-rpath,\$${ORIGIN}/../usr/lib -Wl,--enable-new-dtags'
在运行make
之前。这很好,它找到了第一级依赖项。处理此类宏扩展问题时,请注意单引号和双引号。
其次,如果在二进制文件或库上运行objdump -x
,则可以看到它实际包含的RPATH头。当我运行objdump -x path/to/python |grep RPATH
时,它会告诉我这个。 RPATH $ {ORIGIN} /../ lib:$ {ORIGIN} /../ usr / lib`
我建议您检查二进制文件以查看RPATH标头中的实际内容。不幸的是,我不认为这会解决你的问题。这就是我在运行ldd path/to/python
时看到的内容:
libpython2.7.so.1.0 => /data1/python27/bin/../lib/libpython2.7.so.1.0 (0x00002ad369d4f000)
libpthread.so.0 => /lib/libpthread.so.0 (0x00002ad36a12f000)
libdl.so.2 => /lib/libdl.so.2 (0x00002ad36a34d000)
libutil.so.1 => /lib/libutil.so.1 (0x00002ad36a551000)
libm.so.6 => /lib/libm.so.6 (0x00002ad36a754000)
libc.so.6 => /lib/libc.so.6 (0x00002ad36a9d8000)
/lib64/ld-linux-x86-64.so.2 (0x00002ad369b2d000)
正如您所看到的,第一级依赖项由rpath
正确处理,但第二级依赖项(即libpython的依赖项)将恢复为系统库。是的,libpython在其二进制文件中具有完全相同的RPATH头。我在谷歌搜索rpath recursive
时发现了您的问题,试图解决我制作发行版独立包的问题。
稍后添加
rpath
标头仅更改搜索库的FIRST路径。如果在那里找不到它们,则加载器继续在正常位置搜索。 ldd
仅列出作为搜索结果找到的库的实际路径。当我将这些库复制到rpath
目录时,一切正常。基本上没有找到所有依赖关系并复制它们的简洁方法,只需ldd -v path/to/python
并对该输出进行一些解析。
答案 3 :(得分:1)
我一直在研究这个问题,而且我可以告诉你需要使用-rpath-link
来使用任何使用ORIGIN扩展的路径。例如:
CC -shared (other flags) -R'$ORIGIN/../lib/' -o /buildpath/lib/libmylib1.so
CC -shared (other flags) -R'$ORIGIN/../lib/' -lmylib1 -o /buildpath/lib/libmylib2.so
# This fails to link 'somebinary'
CC (various flags) -R'$ORIGIN/../lib/' -lmylib2 -o /buildpath/bin/somebinary
# This works correctly
CC (various flags) -R'$ORIGIN/../lib/' -Wl,-rpath-link,/buildpath/lib/mylib1 -lmylib2 -o /buildpath/bin/somebinary
# The text above the carets to the right is a typo: ------------------^^^^^^
# I'm fairly sure it should read like this (though it has been awhile since I wrote this):
# (...) -Wl,-rpath-link,/buildpath/lib -lmylib1 (...)
ld
不会在使用$ORIGIN
指定的路径中展开-rpath-link
,也不会从子依赖项的RPATH中检索路径。在上面的示例中,mylib2
取决于mylib1
;在关联somebinary
时,ld
尝试使用libmylib2.so中嵌入的文字/未扩展字符串mylib1
来查找$ORIGIN/../lib/
。 ld.so
会在运行时,但不会ld
。
它也不会使用-L
指定的路径来查找子依赖库(y | ies)。
答案 4 :(得分:1)
检查my modified version of your make script。基本上,应该使用不-rpath-link
的其他$ORIGIN
,因为ld
根本不了解$ORIGIN
。
关于你的问题。
$ORIGIN
仅在运行时运行,而且它是w.r.t.每个图书馆。因此,不同的共享库具有不同的$ORIGIN
。rpath-link
,这不会影响您的可移植性,因为它们是相对的,并且不会存在于最终的可执行文件中,如我已在我的make.sh
此外,this is my own understanding of the whole linking stuff。我希望它有所帮助。
答案 5 :(得分:0)
据我了解,这是ld(即binutils)中的问题,以及它如何解决“次要依赖项”
AFAIK从binutils >= 2.30
开始。依赖项中的rpath将添加到搜索中。
即ld ... main.exe
找到foo.so
,然后在foo.so
中读取RPATH,从而找到bar.so
这是我的堆栈溢出问题: Binutils Secondary Dependency Change
这里是我对各种发行版(在Docker容器内部)的调查,以测试各种binutils版本 https://github.com/Mizux/SecondaryDependency
注意:看看travis-CI日志...