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

时间:2011-06-12 18:06:22

标签: linux linker shared rpath

我刚刚提供了一个使用ld的-rpath选项和$ORIGIN here的基本示例(请参阅第二个响应,了解工作版本)。我正在尝试创建一个示例,其中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

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

但是,当我尝试将main.runfoo.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/dir(其中foo.so是)或project/run(其中main.run(链接它的可执行文件)是) ?
    ldd似乎表明它是project/lib/dir,这似乎是最好的方式(虽然我试过假设两者)。
  2. 如何连接这些链接(同时保留可重定位性) - 最好不要使用-rpath-link
  3. 您可以下载项目here。它就像我能做到的一样简单。 4个简短的来源和一个剧本 提取后,只需从./make.sh内运行project/

    注意:我正在使用-l:。这不应该更改任何内容,除了库名称为foo.so而不是libfoo.so,并且-l:foo.so而不是-lfoo

6 个答案:

答案 0 :(得分:7)

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

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

readelf -a上运行foo.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,你可以使用-Wl,-rpath-link,lib/dir/sub获得main.run链接,正如你所知,如果你知道静态链接器根本不符合$ ORIGIN。

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

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

答案 1 :(得分:6)

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

答案 2 :(得分:5)

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

答案 3 :(得分: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;在关联somebinary时,ld尝试使用libmylib2.so中嵌入的文字/未扩展字符串mylib1来查找$ORIGIN/../lib/ld.so会在运行时,但不会ld

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

答案 4 :(得分:1)

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

关于你的问题。

  1. $ORIGIN仅在运行时运行,而且它是w.r.t.每个图书馆。因此,不同的共享库具有不同的$ORIGIN
  2. 我担心最好的方法是添加rpath-link,这不会影响您的可移植性,因为它们是相对的,并且不会存在于最终的可执行文件中,如我已在我的make.sh
  3. 版本中展示过

    此外,this is my own understanding of the whole linking stuff。我希望它有所帮助。

答案 5 :(得分:0)

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