如何在构建依赖共享库的程序后更改共享库的文件名?

26
我有一个程序依赖于一个共享库,该程序希望在目录结构的深处找到该库。我想把这个共享库移到更好的位置。在OS X上,可以使用install_name_tool来完成此操作。我找不到Linux的等效工具。
用于参考的readelf -d myprogram命令输出以下类似内容:
Dynamic section at offset 0x1e9ed4 contains 30 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [this/is/terrible/library.so]
 0x00000001 (NEEDED)                     Shared library: [libGL.so.1]
 0x00000001 (NEEDED)                     Shared library: [libGLU.so.1]
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so.6]
(continues in an uninteresting fashion)

(同时根据请求,ldd myprogram:)

    linux-gate.so.1 =>  (0x0056a000)
    this/is/terrible/library.so => not found
    libGL.so.1 => /usr/lib/mesa/libGL.so.1 (0x0017d000)
    libGLU.so.1 => /usr/lib/libGLU.so.1 (0x00a9c000)
    libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00710000)
   (etc, etc)
我希望将"this/is/terrible/library.so"更正为"shared/library.so"。需要注意的是,如果程序保留在其“构建”位置,即实际存在相对路径this/is/terrible/library.so的位置,则ldd能够找到它,这是您所期望的。
我知道RPATH,但这不是我要寻找的,我不需要全局更改搜索路径。

那么你的意思是路径“this/is/terrible/”没有设置为rpath?它是库的SONAME的一部分吗?我不认为Linux上有install_name。 - Dmitry Yudakov
是的,它将成为SONAME的一部分,由链接器根据最初链接的路径引入。 - ZorbaTHut
你能否也发布 ldd ./yourprogram 的输出? - Dmitry Yudakov
+1,我有一个几乎相同的问题已经困扰了我好几个月。感谢你促使我向一些可能能够帮助我的人请教。 - Tim Post
这只是意味着您的程序构建方式有问题。您不应该使用相对(或绝对)库路径,而应该将库与路径一起添加到可由ld找到的目录中(或使用-L /path/to/lib gcc选项)。我不确定您是否有能力修改程序的构建过程。如果不能...好运。 - Stan
4个回答

31

我们可以使用patchelf

patchelf --replace-needed liboriginal.so.1 libreplacement.so.1 my-program

我们也可以删除一个依赖项:

patchelf --remove-needed libfoo.so.1 my-program

添加依赖项:

patchelf --add-needed libfoo.so.1 my-program

或更改搜索库的路径(rpath):

patchelf --set-rpath /path/to/lib:/other/path my-program

1
“patchelf” 经常会破坏我的二进制文件,有时候一开始并不容易察觉,但后来可能会导致段错误或者 ldconfig 创建垃圾命名的符号链接等问题。所以请注意。 - Ruslan

11

发布一个初步的、可怕的、hacky的解决方案。

库依赖项存储在称为.depends块的ELF块中。该块的格式是一个大的标识符/字符串指针对数组,其中字符串指针指向二进制文件中某个位置处的标准C空终止字符串。

你知道这样会导致什么问题了吗?

是的,只要你需要的新路径不比旧路径更长,你就可以直接进入二进制文件并进行简单的字符串替换。确保不添加或删除字节,否则整个二进制文件将被破坏。如果您希望更安全地执行此操作,您可以实际遍历ELF结构以确保您拥有正确的位置-现在我只是检查源字符串是否出现了一次。

ELF确实包括一个校验和,但显然没有任何加载器实际验证它,因此忽略它是“安全”的,尽管有点混乱。

“真正的解决方案”将是一个允许对ELF结构进行低级通用操作的实用程序。据我所知,除了少数专门情况(主要是RPATH)之外,没有这样的实用程序存在。我不敢自称知道编写这样一个实用程序有多困难。

我绝对希望有更好的解决方案,但到目前为止,这似乎有效。


1
当我想到要对ELF块进行十六进制编辑时,我感到很不舒服,但我想这应该是可行的。如果应用程序安装后不会被移动,您可以将"shared/library.so"和"this/is/terrible/library.so"建立符号链接。然而,我建议使用正确的路径重新构建,并向您的程序用户发布补丁/更新,作为更加用户友好的解决方案。 - bta
“重建”的真正问题在于,我不想用运行时的奇怪要求来污染我的构建系统布局,同样地,我也不想用构建时的奇怪要求来污染我的运行时布局。这根本不是兼容性问题,只是确保我和我的用户都感到满意。” - ZorbaTHut
我不明白用一个额外的参数来重建你的应用程序会如何污染你的构建环境。但我猜你应该比我更清楚 :-) - Stan
考虑提及 patchelf 以更改 ELF 二进制文件的 RPATH。 - ony
是的,它可以工作,这就是我在http://stackoverflow.com/questions/14611140/forcing-linking-with-a-different-soname-than-this-of-library中所做的。 - philippe lhardy

6

HT - 这可能会有所帮助。

HT 是一个可执行文件的文件编辑器/查看器/分析器。其目标是将调试器的低级功能和 IDE 的易用性相结合。我们计划实现所有(十六进制)编辑功能,并支持最重要的文件格式。

我没有找到比 ZorbaTHut 的解决方案更不同的东西,但也许可以使用不同长度的名称并仍保持二进制文件有效。

gelf - 这也可能很有用。

GElf 是一个通用的、与 ELF 类无关的 API,用于操作 ELF 目标文件。GElf 提供了一个单一、通用的接口,用于处理 32 位和 64 位 ELF 格式目标文件。


不错。对我来说,HT并不实用,因为我需要一个CLI解决方案。gelf看起来完全可行——假设它能正常工作——我只需要围绕它编写实际的代码即可。到目前为止,这是最好的答案。 - ZorbaTHut
patchelf(目前)没有这个功能。它只允许您完全删除依赖项或设置RPATH(这与更改一个共享库的名称不同)。 - hraban
2
现在是可以的。请查看我的回答。 - Bernardo Ramos

-3

您可以使用LD_LIBRARY_PATH更改共享库的搜索路径。如果您的程序依赖于特定的相对路径,就像您的示例所显示的那样,您仍然需要具有该目录结构。换句话说,您可以将lib从/home/user/dev/project/this/is/terrible/library.so移动到/usr/local/lib/this/is/terrible/library.so,但不能移动到/usr/local/lib/library.so

如果您可以重新构建程序,则可以更改其用于lib的相对路径。

Linux中有关共享库的更多信息,请参见http://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html


在我的情况下没有帮助。我正在使用已经构建好的程序,然后尝试将其部署到其他人的系统上。首先,我不想在他们的系统上搞乱LD_LIBRARY_PATH。其次,正如你所提到的,那并不能真正消除这个/is/terrible/前缀,只能让我在它前面添加另一个前缀。我的程序仅依赖于该目录结构,因为那是它寻找DLL的地方 - 这正是我试图改变的 :) - ZorbaTHut

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