我可以更改已经编译的二进制文件中的'rpath'吗?

136

我有一个旧的可执行文件,虽然它现在还没有被淘汰,但它依赖于一些已从我的环境中删除的库。不过我在某个地方有一些存根库,它仍能正常工作。我想将该可执行文件指向这些存根库。是的,我可以设置LD_LIBRARY_PATH,但这个可执行文件会被许多脚本和用户调用,所以我希望在一个地方修复它。

我没有这个可执行文件的源代码,也很难得到。我在考虑 - 我能否使用一个ELF感知编辑器编辑这个文件,并添加一个简单的PATH到rpath,让它使用新的库?这可能吗,或者一旦创建了一个ELF二进制文件,就必须将其固定在某个位置,不能移动吗?


5
将它包装成一个脚本,设置LD_LIBRARY_PATH并调用二进制文件。将这个脚本放在调用者的PATH路径中。 - wildplasser
1
LD_LIBRARY_PATH被子进程继承。你可能不希望这样。 - Will
3
@will 是的,我已经说过我不想这样做了。 :) - Rich Homolka
4个回答

230

有一个比 chrpath 更通用的工具,叫做patchelf。它最初是为 Nix 和 NixOS(打包系统和 GNU/Linux 发行版)制作软件包而创建的。

如果二进制文件中没有 rpath(此处称为 rdsamp),则 chrpath 将失败:

chrpath -r '$ORIGIN/../lib64' rdsamp 
rdsamp: no rpath or runpath tag found.

另一方面,

patchelf --set-rpath '$ORIGIN/../lib64' rdsamp

成功得很好。


12
尤其是,patchelf 能够为不包含 rpath 的二进制文件添加 rpath,而 chrpath 似乎只能修改已有的项目。 - maxschlepzig
6
作为一般性的说明,值得理解“rpath”和“runpath”之间微妙的区别。基本上,一个可以被覆盖LD_LIBRARY_PATH,而另一个则不能。详情请参见http://blog.tremily.us/posts/rpath/。 - Stuart Berg
7
令人烦恼的是,无论是“chrpath”还是“patchelf”,它们的术语都很粗略。例如,上面显示的“patchelf”命令将更改“runpath”,但如果没有提供“--force-rpath”选项,则不会更改“rpath”。 - Stuart Berg
14
是的,但通常区别并不重要。patchelf 的 CHANGELOG 中的这个条目解释了它:“'--set-rpath'、'--shrink-rpath'、和'--print-rpath' 现在优先选择 DT_RUNPATH 而不是过时的 DT_RPATH。在更新时,如果两者都存在,则两者都会更新。如果只有 DT_RPATH 存在,则除非指定了 --force-rpath,否则它将被转换为 DT_RUNPATH。如果两者都不存在,则除非指定了 --force-rpath,否则将添加 DT_RUNPATH,在这种情况下将添加 DT_RPATH。”为了保持兼容性,该选项名称可能没有更改。 - user7610
2
迄今为止最好的答案,这应该成为被接受的答案! - Kenneth Hoste
显示剩余6条评论

92

有一个名为chrpath的工具可以实现此功能 - 它可能已经包含在您的发行版软件包中。


14
提醒Mac用户,使用install_name_tool工具可以通过-rpath标志来实现此操作。 - Kevin Tonon
17
如果你遇到错误信息:<binary>: no rpath or runpath tag found.,那么你无法使用 chrpath 来解决它,但是在这种情况下,你可以使用 patchelfpatchelf --set-rpath /path/to/libaries <binary>。请注意,翻译文本意思保持一致且通俗易懂,没有包含任何解释性内容,并且返回的结果只包含翻译内容。 - phyatt
如果可能的话,我更喜欢使用chrpath,因为虽然它更通用,但patchelf存在一些长期存在的错误,会大幅增加库/可执行文件的大小。 - taranaki
@taranaki:你说的是哪个版本的patchelf? - hagello
3
chrpath 存在一个严重的限制:它只能将 RPATH 替换为长度相等或更短的 RPATH(来自 rpath 0.16 版的 man 手册)。 - hagello
@hagello 我不记得了,也许现在已经修复了。 - taranaki

47

正如@user7610所说,正确的方法是使用patchelf工具。

但是,我认为我可以给出更全面的答案,涵盖所有需要执行的命令。

有关此主题的综合文章,请单击这里

首先,许多开发人员谈论RPATH,但实际上他们指的是RUNPATH。这是两个不同的可选动态部分,加载程序处理它们的方式非常不同。您可以在我之前提到的链接中阅读有关它们之间差异的更多信息。

现在,只需记住:

  • 如果设置了RUNPATH,则忽略RPATH
  • RPATH已过时,应避免使用
  • RUNPATH优先,因为它可以被LD_LIBRARY_PATH覆盖

查看当前R[UN]PATH

readelf -d <path-to-elf> | egrep "RPATH|RUNPATH"

清除 R[UN]PATH

patchelf --remove-rpath <path-to-elf>

注:

  • 该命令会同时移除RPATHRUNPATH

向R[UN]PATH添加值

patchelf [--force-rpath] --set-rpath "<desired-rpath>" <path-to-elf>

注意:

  • <desired-path> 是以冒号分隔的目录列表,例如: /my/libs:/my/other/libs
  • 如果您指定了--force-rpath,则设置RPATH,否则设置RUNPATH

1
-Wl,-R,<desired-rpath> -Wl,--enable-new-dtags 设置了 DT_RUNPATH,这是大多数人应该使用的。RUNPATH 可以被 LD_LIBRARY_PATH 覆盖,因此人们不应该使用 --force-rpath - jww
@jww 我发现我没有关于RPATH弃用的注释,所以我刚刚添加了一个。谢谢! - Daniel Trugman
请注意,示例 <desired-path> 中使用了冒号;它应该是一个逗号(即:/my/libs,/my/other/libs)。 - Alan De Smet
@AlanDeSmet,我不知道逗号怎么样,但是冒号对我有效。 - Daniel Trugman
2
RPATH可能已经被弃用,但使用RUNPATH可能会导致“意外”的边角情况。例如,请参见https://www.qt.io/blog/2011/10/28/rpath-and-runpath。 - Rob
1
"...使用冒号(而不是逗号)分隔..."。 - CristiFati

0

这对我有用,将XORIGIN替换为$ORIGIN。

chrpath -r '\$\ORIGIN/../lib64' httpd


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接