【问题标题】:ld: Using -rpath,$ORIGIN inside a shared library (recursive)ld:在共享库中使用 -rpath,$ORIGIN(递归)
【发布时间】:2011-09-13 11:53:03
【问题描述】:

我刚刚做了一个使用 ld 的 -rpath 选项和 $ORIGIN here 的基本示例(有关工作版本,请参阅第二个响应)。我正在尝试创建一个示例,其中main.run 链接到foo.so,而后者又链接到bar.so,全部使用rpath$ORIGIN

运行时文件结构为:

  • 项目/
    • lib/
      • 目录/
        • 子/
          • bar.so
        • foo.so
    • 运行/
      • ma​​in.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)

所以我的问题是:

  1. foo.so 中的$ORIGIN 是否解析为project/lib/dirfoo.so 所在的位置)或project/runmain.run(链接它的可执行文件)所在的位置)?
    ldd 似乎表明它是project/lib/dir,这似乎是最好的方法(尽管我尝试同时假设两者)。
  2. 如何让这些链接(同时保持可重定位性) - 最好不使用 -rpath-link

您可以下载项目here。这就像我能做到的一样简单。 4 个短资源和一个脚本。
解压后,在project/内运行./make.sh即可。

注意:我使用的是-l:。这不应该改变任何东西,除了库被命名为foo.so 而不是libfoo.so,并且与-l:foo.so 而不是-lfoo 连接。

【问题讨论】:

  • 试试-Wl,-rpath,'$ORIGIN/../lib/dir' -Wl,-rpath,'$ORIGIN/sub'。 (也许 foo.so rpath 没有被合并到最终 exe 的 rpath 中)
  • 实际上,我应该澄清一下 - 构建失败(在制作 main.run 时),所以没有最终的 exe。我会解决的。
  • 抱歉,运气不好。将我能想到的每条路径都添加到两者中。
  • 另请注意:如果foo.so-rpath 是绝对的,而不是$ORIGIN,则它不会出错。
  • 哦,好吧:-)。如果您在这里没有得到任何有用的答案,请尝试在 binutils@sourceware.org 邮件列表中提问。

标签: linux linker shared rpath


【解决方案1】:

嗯,我有一些工作。但我真的不明白为什么它会起作用。这对我来说就像 ld 中的一个错误。

我为 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 时的行为非常不同。

foo.so 上运行readelf -a。在您的构建中,它显示了对“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 语法。出于好奇,你是怎么想到的?

假设它是一个受支持的功能,这看起来有点像一个错误(-l:bar.so 创建对 lib/dir/sub/bar.so 的依赖,而不仅仅是 bar.so)。您可以通过将 rpath-link 设置为 '.' 来处理此错误。对于 main.run,或者您可以以通常的方式重命名 (libxxx.so)。

【讨论】:

  • 我确实尝试过,但没有运气。这就是问题#1的内容。 ld 的文档没有提到 $ORIGIN 所以我不确定在哪里可以找到官方的东西。
  • 该死的,这在这里不起作用。我试过strace(ty),打开的命令只有./bar.so$ORIGIN/usr//lib/bar.so....至于-l:,我记得在搜索如何不要使用lib 前缀,也不要使用完整的文件名——但我现在找不到链接。我会继续寻找。
  • 我的readelf -a foo.so 在我的原始版本中没有显示lib/dir/sub/bar.so,只有bar.so。也许我们的系统不同。 (Core2 duo, Ubuntu 64 10.04, ld = 2.20.51-system.20100908, gcc 4.4.5(Ubuntu/Linaro 4.4.4-14ubuntu5)) - 这都是默认的,我没碰过。
  • 如果你的 readelf 显示 "bar.so" 作为依赖,那么 "-Wl,-rpath-link,lib/dir/sub" 应该允许 main.run 链接。如果在构建 main.run 时将 lib/dir/sub 包含在 rpath-link 中,strace 会显示什么?
  • 在船上有点晚了——我遇到了和 OP 一样的问题——但我想补充一点,man ld 确实很好地记录了-l:bar.so 语法。在@ 987654344@ :- If namespec is of the form :filename, ld will search the library path for a file called filename, otherwise it will search the library path for a file called libnamespec.a.
【解决方案2】:

来自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

【讨论】:

  • 请注意(现在,至少在某些实现上)第一点是不正确的。如果一个程序(main)和一个共享对象(foo)都包含带有$ORIGIN标记的RPATH,并且共享对象foo加载了另一个共享对象bar,那么在这个搜索@的过程中987654332@,首先将使用foo的RPATH,$ORIGINfoo的目录,只有这样,如果失败,main的RPATH将与$ORIGIN一起使用@987654338 @的目录。请参阅my answer to similar question
【解决方案3】:

首先,$ 符号扩展存在可能导致问题的问题。我正在从源代码构建 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 标头仅更改搜索库的第一个路径。如果在那里找不到它们,则加载程序继续在正常位置进行搜索。 ldd 仅列出作为搜索结果找到的库的实际路径。当我将这些库复制到rpath 目录时,一切正常。基本上没有找到所有依赖项并复制它们的简洁方法,只有ldd -v path/to/python 和对该输出的一些解析。

【讨论】:

  • 这种转义策略非常有效。在一个类似但不相关的构建中,我无法让$ORIGIN 文本在configure 脚本和make 中存活。问题解决了。
  • 为什么不改用$$ORIGIN
  • 我从事过一个类似的项目,我确认单引号双引号 '$$ORIGIN' 似乎是在 autoconf 中生存所必需的。最终我使用了:LDFLAGS="-Wl,-rpath,'\$\$ORIGIN'" ./configure
【解决方案4】:

检查my modified version of your make script。基本上,应该使用不带$ORIGIN 的附加-rpath-link,因为ld 根本不理解$ORIGIN

至于你的问题。

  1. $ORIGIN 仅在运行时有效,它是 w.r.t.每个图书馆。所以不同的共享库有不同的$ORIGIN
  2. 恐怕最好的方法是添加rpath-link,这不会影响您的可移植性,因为它们是相对的,并且不会存在于最终的可执行文件中,正如我在我的@版本中所示987654330@

另外,this is my own understanding of the whole linking stuff。希望对你有帮助。

【讨论】:

    【解决方案5】:

    我也一直在研究这个问题,并且尽我所能告诉您需要将-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 不会在使用 -rpath-link 指定的路径或从子依赖的 RPATH 检索的路径中扩展 $ORIGIN。在上面的例子中,mylib2 依赖于mylib1;在链接 somebinary 时,ld 尝试使用嵌入在 libmylib2.so 中的文字/未扩展字符串 $ORIGIN/../lib/ 查找 mylib1ld.so 会在运行时,但不会 ld

    它也不会使用-L 指定的路径来查找子依赖库(y|ies)。

    【讨论】:

      【解决方案6】:

      据我了解,这是 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 日志...

      【讨论】:

        猜你喜欢
        • 2023-03-14
        • 2014-05-25
        • 2018-10-29
        • 2016-08-20
        • 1970-01-01
        • 1970-01-01
        • 2021-12-13
        • 1970-01-01
        • 2014-11-11
        相关资源
        最近更新 更多