7

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

/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”。

为什么?

*更新#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 .....so 而不是 libre2.so.0。(libjson .....so 没有 SONAME 条目)。

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

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

4

4 回答 4

12

我将首先回答您问题的第二部分,即为什么重命名 libre2.so.0 没有达到您的预期。

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

readelf -d libre2.so.0 | grep SONAME

重命名文件没关系。上述命令的结果仍然会为您提供相同的 soname,因此程序仍然无法找到“libre2.so.0”的原因。

至于您问题的原始部分,这一切都取决于库是否具有RPATHRUNPATH内置于它们和/或您的LD_LIBRARY_PATH环境变量的内容是什么。这些是运行时链接器 (ld.so) 将用来查找共享库的东西。尝试

man ld.so

了解更多信息。

由于您自己构建了库,因此您将知道它们是否在最终链接阶段使用了-rpathor-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/”?即readelfSONAME 的命令是否返回

lib/libjson_linux-gcc-4.4.6_libmt.so

而不仅仅是

libjson_linux-gcc-4.4.6_libmt.so

另外,通过运行检查程序在 soname 方面需要什么

readelf -d my_progam | grep NEEDED

由于您将它传递给 gcc 的方式,可能存在“lib/”前缀。如果是这样,那么如果您使用@enobayram 给出的gcc 命令,那么它将公平竞争,即它也将无法找到libjson。

首先要确定的不是它为什么没有找到 libre2.so.0,而是它如何设法找到 libjson。如果您尝试从不同的目录运行您的可执行文件,它仍然可以工作还是现在对于 libjson 也会失败?或者,如果您将 libre2.so.0 复制到可执行文件旁边,这会改变什么吗?

编辑

Fedora 论坛上的一篇帖子表明 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 Free Software Foundation, Inc.

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

因此,您评论中的理论是正确的。如果-soname在构建库时明确指定 no,则共享对象中不存在 SONAME,并且可执行文件中的 NEEDED 字段仅具有提供给链接器的文件名,在您的情况下,该文件名包含前导“lib/”。

于 2012-04-17T22:15:42.833 回答
3

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

替代解决方案:

  • 将 .so 文件放在与可执行文件相同的目录中
  • 运行LD_LIBRARY_PATH=lib ./program
  • 将 .so 文件安装到库路径中的某个位置,例如/usr/local/lib,在运行程序之前。
  • 静态链接
于 2012-04-17T21:34:23.603 回答
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

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

于 2015-04-21T06:43:21.483 回答
0

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

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

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

于 2012-04-17T21:34:44.293 回答