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

我刚刚提供了一个在$ORIGIN中使用ld的-rpath选项的基本示例(请参阅第二个响应中的工作版本)。 我试图创build一个例子,其中main.run链接到foo.so ,链接到bar.so ,所有使用rpath$ORIGIN

运行时文件结构是:

  • 项目/
    • LIB /
      • DIR /
        • 子/
          • bar.so
        • foo.so
    • 跑/
      • main.run (未能build立)

我正在build设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 

哪些build立好。 ldd lib/dir/foo.so甚至可以findbar.so

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

我正在build设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不recursion链接。 (在make.sh中取消注释行,在下面的项目中进行testing)。

然而,使用正常的foo.so我得到这个错误时,build设main.run

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

所以我的问题是:

  1. foo.so中的$ORIGIN是否parsing为project/lib/dir (其中foo.so是)还是project/run (其中main.run (链接它的可执行文件))?
    ldd似乎表明它是project/lib/dir ,这似乎是最好的方法(尽pipe我试图假设两者)。
  2. 如何获得这些链接(同时保留可重定位性) – 最好不使用-rpath-link

你可以在这里下载这个项目。 这很简单,我可以做到。 4个简短的来源和脚本。
解压后,只需在project/运行./make.sh

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

Solutions Collecting From Web of "ld:在共享库中使用-rpath,$ ORIGIN(recursion)"

那么,我有一些工作。 但我真的不明白为什么它的作品。 这感觉像在我的ld中的错误。

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

它也似乎在搜索名为“lib / dir / sub / bar.so”的-rpath-link路径,而不仅仅是“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 -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来处理这个bug。 对于main.run,或者你可以用通常的方式(libxxx.so)来重命名内容。

从ld-linux(8)手册页:

$ ORIGIN和rpath

ld.so理解rpath规范(DT_RPATH或DT_RUNPATH)中的字符串$ ORIGIN(或者等同于$ {ORIGIN})以表示包含应用程序可执行文件的目录。 因此,位于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 

首先,$ sign的扩展可能会导致问题。 我从源代码构建Python,我这样做:

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

在运行之前。 这工作正常,它发现第一级依赖。 处理这种类型的宏扩展问题时要小心单引号和双引号。

其次,如果在二进制文件或库上运行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头文件。 我发现你的问题,同时googling rpath recursive尝试和解决我的问题制作一个发行版独立包。

稍后添加 rpath标题仅更改搜索库的FIRST路径。 如果没有找到,那么装载机继续在正常的地方搜索。 ldd只列出了作为搜索结果找到的库的实际路径。 当我将这些库复制到rpath目录,然后一切正常。 基本上没有找到所有的依赖和复制它们的方法,只是ldd -v path/to/python和一些输出的解析。

我一直在研究这个,最好我可以告诉你需要使用-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指定的路径中扩展$ORIGIN ,或者从子依赖的RPATH中检索路径。 在上面的例子中, mylib2依赖于mylib1 ; 当链接一个somebinaryld尝试使用mylib1中的literal / unexpanded字符串$ORIGIN/../lib/嵌入来查找mylib1。 ld.so会在运行时,但不是ld

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

检查我的make脚本的修改版本 。 基本上,应该使用没有$ORIGIN的附加-rpath-link ,因为ld完全不理解$ORIGIN

至于你的问题。

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

而且, 这是我对整个链接的理解 。 我希望它有帮助。