Linux ld:在共享库中使用 -rpath,$ORIGIN(递归)

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6323603/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:28:43  来源:igfitidea点击:

ld: Using -rpath,$ORIGIN inside a shared library (recursive)

linuxlinkersharedrpath

提问by Simon

I just made a basic example of using ld's -rpathoption with $ORIGINhere(see 2nd response for a working version). I'm trying to create an example where main.runlinks to foo.so, which in turn links to bar.so, all using rpathand $ORIGIN.

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

The run-time file-structure is:

运行时文件结构是:

  • project/
    • lib/
      • dir/
        • sub/
          • bar.so
        • foo.so
    • run/
      • main.run(failing to build)
  • 项目/
    • 库/
      • 目录/
        • 子/
          • 酒吧.so
        • foo.so
    • 跑/
      • main.run(无法构建)

I'm building foo.so using:

我正在构建 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

Which builds fine. ldd lib/dir/foo.socan even find bar.so.

哪个构建良好。ldd lib/dir/foo.so甚至可以找到bar.so

However, when I try to link main.runto foo.so, foo.socan't find bar.so.

但是,当我尝试链接main.run到 时foo.sofoo.so找不到 bar.so。

I'm building main.so using:

我正在构建 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

This works fine if another version of foo.sois used that doesn't recursively link. (Uncomment lines in make.sh, in project below, to test).

如果foo.so使用不递归链接的另一个版本,这可以正常工作。(在下面的项目中取消注释 make.sh 中的行以进行测试)。

However, using the normal foo.soI'm getting this error when building main.run:

但是,使用正常foo.so构建时我收到此错误main.run

/usr/bin/ld: warning: bar.so, needed by lib/dir/foo.so, not found (try using -rpath or -rpath-link)

/usr/bin/ld: 警告:bar.so,需要 lib/dir/foo.so,未找到(尝试使用 -rpath 或 -rpath-link)

So my questions are:

所以我的问题是:

  1. Does $ORIGINwithin foo.so resolve to project/lib/dir(where foo.sois) or project/run(where main.run(the executable linking it) is)?
    ldd would seem to indicate that it's project/lib/dir, which would seem to be the best way (although I tried assuming both).
  2. How do I get these to link (while preserving relocatability) - preferably without using -rpath-link.
  1. $ORIGIN在 foo.so 中是否解析为project/lib/dir(where foo.sois) 或project/run(where main.run(链接它的可执行文件) is)?
    ldd 似乎表明它是project/lib/dir,这似乎是最好的方法(尽管我尝试假设两者)。
  2. 我如何让这些链接(同时保持可重定位性) - 最好不使用-rpath-link.

You can download the project here. It's as simple as I can make it. 4 short sources and a script.
After extracting, just run ./make.shfrom within project/.

您可以在此处下载该项目。这很简单,我可以做到。4 个简短的源代码和一个脚本。
解压后,直接./make.sh从里面运行project/

Note:I'm using -l:. This shouldn't change anything except that the libraries are named like foo.soinstead of libfoo.so, and lunk with -l:foo.soinstead of -lfoo.

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

回答by Nemo

Well, I have something working. But I do not really understand why it works. This feels like a bug in ld to me.

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

I ran strace -f -o /var/tmp/strace.out -- g++ ...for the main.run compilation. The static linker is actually trying to open files whose literal name looks like "$ORIGIN/lib/dir/sub/bar.so", among 20-30 other things. (In other words, it is looking for an actual directory named $ORIGIN. Seriously.)

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

It also appears to be searching the -rpath-link path for the name "lib/dir/sub/bar.so", not just "bar.so". I have no clue why.

它似乎也在搜索名称“lib/dir/sub/bar.so”的 -rpath-link 路径,而不仅仅是“bar.so”。我不知道为什么。

Anyway, this is the link for main.run that is working for me:

无论如何,这是对我有用的 main.run 链接:

g++ -o run/main.run obj/main.o -Wl,-rpath,'$ORIGIN/../lib/dir' -Wl,-rpath-link,. -Llib/dir -l:foo.so

It is identical to yours but with -Wl,-rpath-link,.inserted.

它与您的相同,但已-Wl,-rpath-link,.插入。

[addendum]

[附录]

OK I think I see what is going on. First, the static linker (GNU ld) simply does not honor $ORIGIN in the libraries it links against.

好的,我想我明白发生了什么。首先,静态链接器 (GNU ld) 根本不支持它链接的库中的 $ORIGIN。

Second, the behavior when you use -lbarversus -l:bar.sois very different.

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

Run readelf -aon foo.so. In your build, it shows a dependency on "lib/dir/sub/bar.so". This is why setting the rpath-link to "." fixes the build of main.run; it causes the static linker to search "." for "lib/dir/sub/bar.so", which it finds.

运行readelf -afoo.so。在您的构建中,它显示了对“lib/dir/sub/bar.so”的依赖。这就是将 rpath-link 设置为“.”的原因。修复了 main.run 的构建;它会导致静态链接器搜索“。” 对于它找到的“lib/dir/sub/bar.so”。

If you rename bar.so to libbar.so, and link foo.so to use -lbarinstead of -l:bar.so, the same readelf shows that foo.so now depends on "libbar.so" (with no path component). With that foo.so, you can get the main.run link to work using -Wl,-rpath-link,lib/dir/sub, as you would expect if you knew that the static linker simply does not honor $ORIGIN.

如果将 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,您会期望的。

By the way, I do not see the -l:bar.sosyntax documented anywhere in the GNU ld manual. Out of curiosity, how did you come up with it?

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

Assuming it is a supported feature, this looks a bit like a bug (-l:bar.so creating a dependency on lib/dir/sub/bar.so instead of just bar.so). You can either deal with this bug by setting rpath-link to '.' for main.run, or you can rename stuff in the usual way (libxxx.so).

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

回答by Michael Dillon

First, there are issues with $ sign expansion that might be causing problems. I'm building Python from source and I do this:

首先,$ 符号扩展存在可能导致问题的问题。我正在从源代码构建 Python,我这样做:

export LDFLAGS='-Wl,-rpath,$${ORIGIN}/../lib -Wl,-rpath,$${ORIGIN}/../usr/lib -Wl,--enable-new-dtags'

before running make. That works fine and it finds 1st level dependencies. Be careful with single and double quotes when dealing with this type of macro expansion issue.

跑步前make。这工作正常,它找到了第一级依赖项。处理这种类型的宏扩展问题时要小心使用单引号和双引号。

Secondly, if you run objdump -xon a binary or a library, you can see the RPATH header that it actually contains. When I run objdump -x path/to/python |grep RPATHit shows me this.RPATH ${ORIGIN}/../lib:${ORIGIN}/../usr/lib`

其次,如果您objdump -x在二进制文件或库上运行,您可以看到它实际包含的 RPATH 标头。当我运行时,objdump -x path/to/python |grep RPATH它向我显示了这一点。RPATH ${ORIGIN}/../lib:${ORIGIN}/../usr/lib`

I suggest that you check your binaries to see what is actually in the RPATH header. Unfortunately, I don't think that this will solve your problem. This is what I see when I run ldd path/to/python:

我建议您检查二进制文件以查看 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)

As you can see, the first level dependency is correctly handled by rpathbut the second level dependencies, i.e. the dependencies of libpython, revert to system libraries. And yes, libpython has the exact same RPATH header in its binary. I found your question while googling rpath recursiveto try and resolve my problem of making a distro independent package.

如您所见,第一级依赖被正确处理,rpath但第二级依赖,即 libpython 的依赖,恢复到系统库。是的,libpython 在其二进制文件中具有完全相同的 RPATH 标头。我在谷歌搜索rpath recursive以尝试解决我制作发行版独立包的问题时发现了您的问题。

Added laterThe rpathheaders only change the FIRST path searched for libraries. If they are not found there, then the loader continues to search in the normal places. lddonly lists the actual path of the library that was found as a result of the search. When I copied these libraries to the rpathdirectory, then everything worked. Basically there is no tidy way to find all the dependencies and copy them, just ldd -v path/to/pythonand some parsing of that output.

后来添加rpath头只改变第一路径搜索库。如果在那里找不到它们,那么加载程序将继续在正常位置搜索。ldd仅列出作为搜索结果找到的库的实际路径。当我将这些库复制到rpath目录时,一切正常。基本上没有一种整洁的方法可以找到所有依赖项并复制它们,只是ldd -v path/to/python对该输出进行一些解析。

回答by Brian Vandenberg

I've been looking into this as well, and as best I can tell you need to use -rpath-linkfor any path that would use ORIGIN expansion. For example:

我也一直在研究这个,并且尽我所能告诉你需要使用-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 (...)

ldwill not expand $ORIGINwithin paths specified using -rpath-linkor paths it retrieves from a sub-dependency's RPATH. In the above example, mylib2depends on mylib1; while linking somebinary, ldattempts to find mylib1using the literal/unexpanded string $ORIGIN/../lib/embedded in libmylib2.so. ld.sowould at runtime, but not ld.

ld不会$ORIGIN在使用指定的路径-rpath-link或从子依赖项的 RPATH 检索的路径内扩展。在上面的例子中,mylib2取决于mylib1;在链接时somebinaryld尝试mylib1使用$ORIGIN/../lib/嵌入在 libmylib2.so 中的文字/未扩展字符串进行查找。 ld.so会在运行时,但不会ld

It also won't use paths specified with -Lto find the sub-dependency librar(y|ies).

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

回答by Rob Stewart

From the ld-linux(8)manpage:

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

$ORIGIN and rpath

ld.so understands the string $ORIGIN (or equivalently ${ORIGIN}) in an rpath specification (DT_RPATH or DT_RUNPATH) to mean the directory containing the application executable. Thus, an application located in somedir/app could be compiled with gcc -Wl,-rpath,'$ORIGIN/../lib' so that it finds an associated shared library in somedir/lib no matter where somedir is located in the directory hierarchy. This facilitates the creation of "turn-key" applications that do not need to be installed into special directories, but can instead be unpacked into any directory and still find their own shared libraries.

$ORIGIN 和 rpath

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

Thus, in answer to your first question, there's only one value for $ORIGIN: project/run.

因此,在回答您的第一个问题时,$ORIGIN:仅有一个值project/run

Therefore, the answer to your second question should be to use the following command to link foo.so:

因此,您的第二个问题的答案应该是使用以下命令来链接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

回答by zym1010

Check my modified version of your make script. Basically, an additional -rpath-linkwithout $ORIGINshould be used, since lddoesn't understand $ORIGINat all.

检查我的 make 脚本的修改版本。基本上,应该使用一个额外的-rpath-linkwithout $ORIGIN,因为ld根本不明白$ORIGIN

As for your questions.

至于你的问题。

  1. $ORIGINonly works during runtime, and it's w.r.t. each library. So different shared libraries have different $ORIGIN.
  2. I'm afraid the best way is to add rpath-link, and this won't affect your portability, since they are relative, and won't exist in the final executable, as I have shown in my version of make.sh
  1. $ORIGIN仅在运行时有效,并且每个库都适用。所以不同的共享库有不同的$ORIGIN.
  2. 恐怕最好的方法是添加rpath-link,这不会影响您的可移植性,因为它们是相对的,并且不会存在于最终的可执行文件中,正如我在我的版本中所示make.sh

Also, this is my own understanding of the whole linking stuff. I hope it helps.

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

回答by Mizux

From my understanding this is an issue in ld (i.e. binutils) and how it resolve "Secondary Dependencies"

根据我的理解,这是 ld(即 binutils)中的一个问题,以及它如何解决“二级依赖”

AFAIK started from binutils >= 2.30. rpath in dependencies are added to the search. i.e. ld ... main.exefind foo.so, then read the RPATH in foo.sothus find bar.so

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

Here my stack overflow question: Binutils Secondary Dependency Change

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

And here my investigation on various distro (inside docker container) to test various binutils version https://github.com/Mizux/SecondaryDependency

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

note: take a look a the travis-CI log...

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