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

Sim*_*mon 29 linux linker shared rpath

我只是做了使用ld的一个基本的例子-rpath选项与$ORIGIN 这里(见一个工作版本第二反应).我试图创造一个例子main.run链接foo.so,进而链接bar.so,全部采用rpath$ORIGIN.

运行时文件结构是:

  • 项目/
    • LIB /
      • DIR /
        • 子/
          • bar.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
Run Code Online (Sandbox Code Playgroud)

哪个建好了.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
Run Code Online (Sandbox Code Playgroud)

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

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

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

所以我的问题是:

  1. 是否$ORIGIN内foo.so解析project/lib/dir(这里foo.so是)或project/run(其中main.run(可执行文件链接吧))?
    ldd似乎表明它是project/lib/dir,这似乎是最好的方式(虽然我尝试假设两者).
  2. 如何将这些链接(同时保留可重新定位性) - 最好不要使用-rpath-link.

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

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

Nem*_*emo 8

好吧,我有一些工作.但我真的不明白为什么它有效.这对我来说就像是一个错误.

我跑strace -f -o /var/tmp/strace.out -- g++ ...了main.run编译.静态链接器实际上是尝试打开其文字名称看起来像"$ 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
Run Code Online (Sandbox Code Playgroud)

它与您的相同,但-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手册中任何地方记录的语法.出于好奇,你是怎么想出来的?

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


小智 6

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
Run Code Online (Sandbox Code Playgroud)

  • 请注意(现在,至少在某些实现上)第一点是不正确的。如果程序(`main`)和共享对象(`foo`)都包含带有`$ORIGIN`标记的RPATH,并且共享对象`foo`加载另一个共享对象`bar`,那么在这个搜索过程中`bar`,首先会使用 `foo` 的 RPATH,而 `$ORIGIN` 是 `foo` 的目录,只有这样,如果失败,`main` 的 RPATH 才会与 `$ORIGIN` 一起使用是 `main` 的目录。请参阅[我对类似问题的回答](/sf/answers/3685298151/)。 (3认同)

Mic*_*lon 5

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

export LDFLAGS='-Wl,-rpath,\$${ORIGIN}/../lib -Wl,-rpath,\$${ORIGIN}/../usr/lib -Wl,--enable-new-dtags'
Run Code Online (Sandbox Code Playgroud)

在跑步之前make.这很好,它找到了第一级依赖项.处理此类宏扩展问题时,请注意单引号和双引号.

其次,如果您运行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:

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)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,第一级依赖项是由rpath第二级依赖项正确处理的,但是第二级依赖项(即libpython的依赖项)还原为系统库.是的,libpython在其二进制文件中具有完全相同的RPATH头.我在google搜索时发现了你的问题rpath recursive,试图解决我制作发行版独立包的问题.

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