运行时 C++ 链接器缺少库(SONAME 行为)
我制作了一个程序,它使用两个共享库(我编译过),并且放置如下:
I've made a program that uses two shared libraries (which I compiled) and are placed like this:
/home_directory_where_I_compile_and_run_everything
-->/lib/libjson_linux-gcc-4.4.6_libmt.so
-->/lib/libre2.so.0
当我编译我的程序时,我将这些库的相对位置传递给链接器,如下所示:
When I compile my program I pass the relative location of those libraries to the linker, like this:
g++ ...... stuff ........ my_program.cc lib/libjson_linux-gcc-4.4.6_libmt.so lib/libre2.so.0
它编译得很好,但是在运行程序时它找不到 libre2.so,如果我用 ldd 检查它,会发生以下情况:
And it compiles fine, however when running the program it fails to find libre2.so, and if I inspect it with ldd, here's what happens:
....
lib/libjson_linux-gcc-4.4.6_libmt.so (0x00007f62906bc000)
libre2.so.0 => not found
....
显然,它确实承认 libjson 上的路径是相对的,但它不会在 libre2.so.0 上这样做(它会修剪所有路径并留下 libre2.so.0)
Apparently, it does acknowledge that the path on libjson is relative, but it doesn't do that on libre2.so.0 (it trims all the path and just leaves libre2.so.0)
谁能告诉我为什么会这样?
Can someone tell me why this happens?
另外,有没有办法通过 g++ 参数来修改它?
Also, is there a way to modify this via a g++ argument?
最好的.
* 更新 * 哇,看看这个!我已将 libre2.so.0 的名称更改为 stuff.so,然后尝试编译基本相同,如下所示:
* UPDATE * Whoa check this out! I've changed the name of libre2.so.0 to stuff.so, and then tried to compile essentially the same like this:
g++ ...... stuff ........ my_program.cc lib/libjson_linux-gcc-4.4.6_libmt.so lib/stuff.so
无论如何它都失败了;它不仅失败了,而且失败了,因为它找不到libre2.so.0".
And it fails anyways; not only it does fail, it fails because it can't find "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] 就好了.
Now, if I could just make that [libre2.so.0] to be [lib/libre2.so.0] instead it would be fine.
* 更新 #3 *
正如@troubadour 发现的那样:
As @troubadour found out:
当可执行文件与具有 DT_SONAME 字段的共享对象链接时,当可执行文件运行时,动态链接器将尝试加载由 DT_SONAME 字段指定的共享对象,而不是使用链接器提供的文件名.
When an executable is linked with a shared object which has a DT_SONAME field, then when the executable is run the dynamic linker will attempt to load the shared object specified by the DT_SONAME field rather than the using the file name given to the linker.
这就是为什么它适用于 libjson.....so 而不是 libre2.so.0.(libjson .....so 没有 SONAME 条目).
That's why it works with libjson.....so and not with libre2.so.0. (libjson.....so does not have an entry for SONAME).
我终于找到了我正在寻找的确切问题:
And I finally found the exact question for what I'm looking for:
有什么方法可以告诉 gcc 链接器忽略共享库文件中的 SONAME 条目,而是链接到特定的文件路径?
推荐答案
我将首先回答你问题的第二部分,即为什么重命名 libre2.so.0 没有达到你的预期.
I'll answer the second part of your question first i.e. why renaming libre2.so.0 didn't do what you expected.
当您运行可执行文件时,传递给链接器的文件名无关紧要(除非您在构建库时未能提供 -soname
- 请参阅下面的第二个编辑).依赖关系取自所谓的soname".如果你在你的库上运行 readelf
命令,你可以确定它的 soname,例如.
The file name that you pass to the linker is irrelevant when you run the executable (unless you fail to supply -soname
when building the library - see second edit below). The dependency is taken from what is called the "soname". If you run the readelf
command on your library you can determine its soname eg.
readelf -d libre2.so.0 | grep SONAME
重命名文件没关系.上述命令的结果仍然会为您提供相同的 soname,因此程序仍然无法找到libre2.so.0".
It doesn't matter if you rename the file. The result of the above command will still give you the same soname, hence the reason the program still failed to find "libre2.so.0".
至于您问题的原始部分,这完全取决于库是否内置了 RPATH
或 RUNPATH
和/或您的 LD_LIBRARY_PATH
环境变量是.这些是运行时链接器 (ld.so) 将用来查找共享库的东西.试试
As to the original part of your question it all hinges on whether the libraries have an RPATH
or RUNPATH
built in to them and/or what the content of your LD_LIBRARY_PATH
environment variable is. These are the things the run-time linker (ld.so) will use to find the shared libraries. Try
man ld.so
了解更多信息.
由于您自己构建了这些库,您将知道它们在最终链接阶段是否使用了 -rpath
或 -runpath
选项.或者,再次使用 readelf
,例如.
Since you built the libraries yourself you will know whether or not they used the -rpath
or -runpath
options at the final linking stage. Alternatively, use readelf
again eg.
readelf -d libre2.so.0 | grep RPATH
readelf -d libre2.so.0 | grep RUNPATH
我怀疑以上两个命令不会返回任何内容.
I suspect the above two commands will return nothing.
我的猜测是您的 LD_LIBRARY_PATH
中有当前目录,这将允许运行时链接器找到 lib/libjson_linux-gcc-4.4.6_libmt.so 而不是 libre2.所以.0.我注意到您已回复我对您的问题的评论说您的 LD_LIBRARY_PATH
是空的.这很奇怪.
My guess was going to be that you have the current directory in your LD_LIBRARY_PATH
which would allow the run-time linker to find lib/libjson_linux-gcc-4.4.6_libmt.so but not libre2.so.0. I notice that you've replied to my comment on your question to say that your LD_LIBRARY_PATH
is empty. That's odd.
也许您在 libjson 的 soname 上以某种方式获得了前缀lib/"?即 SONAME 的 readelf
命令是否返回
Perhaps you've somehow got the prefix "lib/" on the soname for libjson? i.e. did the readelf
command for the SONAME return
lib/libjson_linux-gcc-4.4.6_libmt.so
而不仅仅是
libjson_linux-gcc-4.4.6_libmt.so
另外,通过运行检查程序在 soname 方面需要什么
Also, check what the program needs in terms of soname by running
readelf -d my_progam | grep NEEDED
也许lib/"前缀在那里是因为你将它传递给 gcc 的方式.如果是这样,那么如果您使用@enobayram 给出的 gcc 命令,那么它将公平竞争,即它也将无法找到 libjson.
Perhaps the "lib/" prefix is in there because of the way you passed it to gcc. If so then if you use the gcc command given by @enobayram then it will level the playing field i.e. it will fail to find libjson too.
首先要确定的不是它为什么不 找到 libre2.so.0,而是它是如何 设法找到 libjson.如果您尝试从不同的目录运行您的可执行文件,它仍然可以工作还是现在对于 libjson 也会失败?或者,如果您将 libre2.so.0 复制到可执行文件旁边,这会改变什么吗?
The first thing to establish is not why it is not finding libre2.so.0 but how it is managing to find libjson. If you try running your executable from a different directory does it still work or does it now fail for libjson too? Alternatively, if you copy libre2.so.0 to be beside your executable does that change anything?
编辑
在 Fedora 论坛上发帖 建议使用 Fedora 版本的 ld.当前目录也是内置搜索路径.虽然我无法验证这一点,但它可以解释为什么你要选择任何库,因为你的案例中没有 ld.so 使用的所有其他东西.
A posting on the Fedora Forum suggest that the Fedora version of ld.so has the current directory as a built-in search path. I haven't been able to verify this though but it would explain why you are picking up any libraries at all given that all the other things ld.so uses are absent on your case.
编辑 2
来自我系统上的 ld 手册页
From the ld man page on my system
-soname=名称
创建 ELF 共享对象时,设置内部 DT_SONAME 字段到指定的名称.当可执行文件与共享链接时具有 DT_SONAME 字段的对象,然后在运行可执行文件时动态链接器将尝试加载由指定的共享对象DT_SONAME 字段而不是使用给定的文件名链接器.
When creating an ELF shared object, set the internal DT_SONAME field to the specified name. When an executable is linked with a shared object which has a DT_SONAME field, then when the executable is run the dynamic linker will attempt to load the shared object specified by the DT_SONAME field rather than the using the file name given to the linker.
版权 (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 免费软件基金会公司
Copyright (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免费文档许可证".
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.3 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back- Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".
所以,您评论中的理论是正确的.如果在构建库时未显式指定 -soname
则共享对象中不存在 SONAME 并且可执行文件中的 NEEDED 字段仅具有提供给链接器的文件名,在您的情况下,该文件名包含前导lib/".
So, the theory in your comment is correct. If no -soname
is explicitly specified when the library is built then no SONAME exists in the shared object and the NEEDED field in the executable simply has the file name given to the linker which, in your case, contained the leading "lib/".
相关文章