30

我只是在这里做了一个使用 ld-rpath选项的基本示例(有关工作版本,请参阅第二个响应)。我正在尝试创建一个示例,其中链接到,然后链接到,全部使用and 。$ORIGIN main.runfoo.sobar.sorpath$ORIGIN

运行时文件结构是:

  • 项目/
    • 库/
      • 目录/
        • 子/
          • 吧.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.sofoo.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/dir(where foo.sois) 或project/run(where main.run(the executable linking it) is)?
    ldd 似乎表明它是project/lib/dir,这似乎是最好的方法(尽管我尝试假设两者)。
  2. 我如何让这些链接(同时保留可重定位性) - 最好不使用-rpath-link.

您可以在此处下载该项目。这就像我能做到的一样简单。4 个短源和一个脚本。
提取后,只需./make.sh从内部运行project/

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

4

6 回答 6

8

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

我跑strace -f -o /var/tmp/strace.out -- g++ ...了 main.run 编译。静态链接器实际上是在尝试打开文字名称类似于“$ORIGIN/lib/dir/sub/bar.so”的文件,以及其他 20-30 个文件。(换句话说,它正在寻找一个名为$ORIGIN. Seriously 的实际目录。)

它似乎也在 -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。

其次,使用-lbarvs时的行为-l:bar.so非常不同。

继续运行readelf -afoo.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,您可以使用 使 main.run 链接正常工作-Wl,-rpath-link,lib/dir/sub,如果您知道静态链接器根本不支持 $ORIGIN,您会期望。

顺便说一句,我没有看到-l:bar.soGNU ld 手册中任何地方记录的语法。出于好奇,你是怎么想到的?

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

于 2011-06-12T18:55:54.763 回答
7

ld-linux(8)联机帮助页:

$ORIGIN 和 rpath

ld.so 将 rpath 规范(DT_RPATH 或 DT_RUNPATH)中的字符串 $ORIGIN(或等效的 ${ORIGIN})理解为表示包含应用程序可执行文件的目录。因此,可以使用 gcc -Wl,-rpath,'$ORIGIN/../lib' 编译位于 somedir/app 中的应用程序,这样无论 somedir 在目录中的什么位置,它都能在 somedir/lib 中找到关联的共享库等级制度。这有助于创建不需要安装到特殊目录中的“交钥匙”应用程序,而是可以将其解压缩到任何目录中,并且仍然可以找到它们自己的共享库。

$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
于 2014-07-14T16:10:37.697 回答
5

首先,$ 符号扩展存在可能导致问题的问题。我正在从源代码构建 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对该输出进行一些解析。

于 2011-06-14T18:38:27.330 回答
2

检查我修改后的 make 脚本版本。基本上,应该使用额外的-rpath-linkwithout ,因为根本不理解。$ORIGINld$ORIGIN

至于你的问题。

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

另外,这是我自己对整个链接内容的理解。我希望它有所帮助。

于 2016-08-30T19:16:15.463 回答
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; 链接时somebinaryld尝试使用嵌入在 libmylib2.so 中 mylib1的文字/未扩展字符串进行查找。会在运行时,但不是.$ORIGIN/../lib/ld.sold

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

于 2012-07-03T19:44:10.800 回答
1

据我了解,这是 ld(即 binutils)中的一个问题以及它如何解决“次要依赖项”

AFAIK 从binutils >= 2.30. 将依赖项中的 rpath 添加到搜索中。即ld ... main.exefind foo.so,然后读取 RPATHfoo.so从而找到bar.so

这是我的堆栈溢出问题: Binutils Secondary Dependency Change

在这里我对各种发行版(在 docker 容器内)的调查以测试各种 binutils 版本 https://github.com/Mizux/SecondaryDependency

注意:看看 travis-CI 日志...

于 2018-12-13T13:34:22.113 回答