运行时C ++链接器缺少库(SONAME行为)

时间:2012-04-17 20:58:01

标签: c++ linker g++ shared

我已经制作了一个程序,它使用了两个共享库(我编译过)并且放置如下:

/home_directory_where_I_compile_and_run_everything
-->/lib/libjson_linux-gcc-4.4.6_libmt.so
-->/lib/libre2.so.0

当我编译程序时,我将这些库的相对位置传递给链接器,如下所示:

g++ ...... stuff ........ my_program.cc lib/libjson_linux-gcc-4.4.6_libmt.so lib/libre2.so.0

它编译得很好,但是在运行程序时它无法找到libre2.so,如果我用ldd检查它,那么会发生什么:

....
lib/libjson_linux-gcc-4.4.6_libmt.so (0x00007f62906bc000)
libre2.so.0 => not found
....

显然,它确实承认libjson上的路径是相对的,但它在libre2.so.0上没有这样做(它修剪了所有路径,只留下了libre2.so.0)

有人可以告诉我为什么会这样吗?

另外,有没有办法通过g ++参数修改它?

最佳。

*更新* 哇哇呀呀! 我已将libre2.so.0的名称更改为stuff.so,然后尝试编译基本相同的内容:

g++ ...... stuff ........ my_program.cc lib/libjson_linux-gcc-4.4.6_libmt.so lib/stuff.so

反正它失败了;不仅它失败了,它失败了,因为它找不到“libre2.so.0”。

Whyyy?

*更新#2 *

readelf -d the_program.o

的输出
0x0000000000000001 (NEEDED)             Shared library: [lib/libjson_linux-gcc-4.4.6_libmt.so]
0x0000000000000001 (NEEDED)             Shared library: [libre2.so.0]

现在,如果我可以将[libre2.so.0]改为[lib / libre2.so.0],那就没关系了。

*更新#3 *

正如@troubadour发现的那样:

  

当可执行文件与具有DT_SONAME字段的共享对象链接时,则在运行可执行文件时,动态链接器将尝试加载由DT_SONAME字段指定的共享对象,而不是使用为链接器指定的文件名

这就是为什么它适用于libjson .....所以而不是libre2.so.0。 (libjson .....所以没有SONAME的条目)。

我终于找到了我正在寻找的确切问题:

有没有办法告诉gcc链接器忽略共享库文件中的SONAME条目,而是链接到特定的文件路径?

4 个答案:

答案 0 :(得分:11)

我将首先回答你问题的第二部分,即为什么重命名libre2.so.0不符合预期。

运行可执行文件时传递给链接器的文件名无关紧要(除非在构建库时未能提供-soname - 请参阅下面的第二个编辑)。依赖性来自所谓的“soname”。如果在库上运行readelf命令,则可以确定其声纳名称,例如

readelf -d libre2.so.0 | grep SONAME

重命名文件无关紧要。上面命令的结果仍然会给你相同的soname,因此程序仍然无法找到“libre2.so.0”。

至于问题的原始部分,这一切都取决于图书馆是否内置了RPATHRUNPATH和/或LD_LIBRARY_PATH环境变量的内容是。这些是运行时链接程序(ld.so)将用于查找共享库的内容。尝试

man ld.so

了解更多信息。

由于您自己构建了库,因此您将知道它们是否在最终链接阶段使用了-rpath-runpath选项。或者,再次使用readelf,例如

readelf -d libre2.so.0 | grep RPATH
readelf -d libre2.so.0 | grep RUNPATH

我怀疑上述两个命令都不会返回任何内容。

我的猜测是你的LD_LIBRARY_PATH中有当前目录,它允许运行时链接器找到lib / libjson_linux-gcc-4.4.6_libmt.so而不是libre2.so.0 。我注意到您已回复我对您的问题的评论,说您的LD_LIBRARY_PATH是空的。那很奇怪。

也许你已经以某种方式在libjson的soname上获得了前缀“lib /”?即,SONAME的readelf命令返回

lib/libjson_linux-gcc-4.4.6_libmt.so

而不仅仅是

libjson_linux-gcc-4.4.6_libmt.so

此外,通过运行

检查程序在soname方面的需求
readelf -d my_progam | grep NEEDED

也许“lib /”前缀在那里,因为你将它传递给gcc的方式。如果是这样,那么如果你使用@enobayram给出的gcc命令,那么它将对游戏区域进行调整,即它也无法找到libjson。

要建立的第一件事不是为什么找到libre2.so.0,而是 如何找到libjson。如果你尝试从不同的目录运行你的可执行文件它仍然可以工作,或者它现在也失败了libjson?或者,如果您将libre2.so.0复制到可执行文件旁边,那会改变什么吗?

修改

A posting on the Fedora Forum表明Fedora版本的ld.so将当前目录作为内置搜索路径。虽然我无法对此进行验证,但它可以解释为什么你会选择任何库,因为你的案例中没有ld.so使用的所有其他东西。

修改2

从我系统上的ld手册页

  

-soname =名称

     

创建ELF共享对象时,请设置内部DT_SONAME字段   到指定的名称。当可执行文件与共享链接时   具有DT_SONAME字段的对象,然后运行可执行文件   动态链接器将尝试加载由指定的共享对象   DT_SONAME字段而不是使用给出的文件名   接头

     

版权所有(c)1991,1992,1993,1994,1995,1996,1997,1998,1999,   2000,2001,2002,2003,2004,2005,2006,2007,2008,2009免费   Software Foundation,Inc。

     

允许复制,分发和/或修改本文档   根据GNU自由文档许可证1.3版或   自由软件基金会发布的任何更新版本;没有   不变章节,没有封面文字,没有封底   文本。许可证的副本包含在标题为“GNU”的部分中   免费文档许可证“。

所以,你评论中的理论是正确的。如果在构建库时没有显式指定-soname,则共享对象中不存在SONAME,并且可执行文件中的NEEDED字段只有链接器的文件名,在您的情况下,包含前导“lib” /".

答案 1 :(得分:3)

当您链接到共享库(.so)时,当您执行程序时,这些库必须存在于库加载路径中。您在链接时提供的位置不会在可执行文件中“记住”。

替代解决方案:

  • 将.so文件放在与可执行文件相同的目录中
  • 使用LD_LIBRARY_PATH=lib ./program
  • 运行
  • 在运行程序之前,将.so文件安装到库路径中的某个位置,例如/usr/local/lib
  • 静态链接

答案 2 :(得分:1)

您需要在两种情况下告诉共享库的位置。

一个是在连接时间 有几种方法:

  • 将库复制到某个标准目录,例如/usr/local/lib,然后安装它 通过ldconfig。这样,就不需要其他选项/操作。
  • 使用-L告诉gcc在哪里找到lib

一个是在运行时间 还有几种方法:

  • 喜欢链接时间,通过ldconfig安装,然后不需要其他操作。
  • 使用LD_LIBRARY_PATH

      

    LD_LIBRARY_PATH =。:$ LD_LIBRARY_PATH ./a.out

  • 使用rpath将搜索路径写入可执行文件

      

    g ++ main.cpp -lxxx -L / a / b / c -Wl,-rpath,/ a / b / c

仅在链接阶段指定共享库位置,例如通过-L,并不意味着当您运行程序时,系统的加载程序将找到所需的库。

答案 3 :(得分:0)

正确的编译器命令行应该是:

g++ ...... stuff ........ my_program.cc -Llib -ljson_linux-gcc-4.4.6_libmt -lre2

您应该将共享对象放在标准目录(例如/ usr / lib)中,或者应该将它们的路径添加到LD_LIBRARY_PATH以便能够运行可执行文件。