有没有办法在 link 时间设置 elf NEEDED 字段?

Is there a way to set the elf NEEDED field at link time?

给定一个可执行文件:

>objdump -x someprog | grep c++
NEEDED               libstdc++.so.6

我想将需求更改为完整版(包括次要版本和补丁级别):

>objdump -x someprog | grep c++
NEEDED               libstdc++.so.6.0.22

我知道有两种方法可以做到这一点:

  1. 根据这个问题创建一个虚拟库 (Forcing or preventing use of a particular minor version of libstdc++)
  2. 使用 patchelf
    >patchelf --add-needed libstdc++.so.6.0.22 someprog
    >objdump -x someprog | grep c++
    NEEDED               libstdc++.so.6
    NEEDED               libstdc++.so.6.0.22

(我还没有想出 --replace-needed 的工作命令行)

这两个对我来说都像是 hack。 有没有一种方法可以在编译时或 link 时对 gcc 使用适当的 -Wl 标志来实现相同的目的?

理想情况下,我想避免使用 -nostdlib,因为这不仅要求我指定 libstd++,还指定 libc 以及我确实需要标准版本的所有其他内容。

对于常规库,只需 linking 到特定版本就足够了 libstdc++,但事实并非如此(或者更确切地说,我怀疑 -stdlib 会覆盖我提供的后续完全限定名称)。

背景:我的可执行文件需要比系统上安装的更高版本的 libstdc++。不幸的是,安装的版本可能是相同的主要版本,如果是这样,ld 将愉快地使用系统版本,因为它匹配 soname libstdc++.so.6

我不想静态地 link 因为我实际上想安装许多共享相同 C++ 运行时的小程序,这会使安装变得相当臃肿。


有关我(的)图书馆搜索路径的一些信息可在此处获得:

ld --verbose | grep SEARCH_DIR SEARCH_DIR("/usr/x86_64-redhat-linux/lib64"); SEARCH_DIR("/usr/lib64"); SEARCH_DIR("/usr/local/lib64"); SEARCH_DIR("/lib64"); SEARCH_DIR("/usr/x86_64-redhat-linux/lib"); SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");

很明显,在我的例子中,/usr/lib64 是在可执行文件的 RPATH 之前被搜索的,它是:

>objdump -x /opt/foo/bin/bar | grep PATH
RPATH                $ORIGIN/../lib64/private:$ORIGIN/../lib64:$ORIGIN/

man ld.so 建议搜索顺序应为:

If a library dependency does not contain a slash, then it is searched for in the following order:

   o  (ELF only) Using the directories specified in the DT_RPATH dynamic section attribute of the binary if present and DT_RUNPATH attribute does not exist.  Use of DT_RPATH is deprecated.

   o  Using the environment variable LD_LIBRARY_PATH.  Except if the executable is a set-user-ID/set-group-ID binary, in which case it is ignored.

   o  (ELF only) Using the directories specified in the DT_RUNPATH dynamic section attribute of the binary if present.

   o  From the cache file /etc/ld.so.cache, which contains a compiled list of candidate libraries previously found in the augmented library path.  If, however, the binary was linked with the  -z  node‐
      flib linker option, libraries in the default library paths are skipped.  Libraries installed in hardware capability directories (see below) are preferred to other libraries.

   o  In the default path /lib, and then /usr/lib.  If the binary was linked with the -z nodeflib linker option, this step is skipped.

同样https://software.intel.com/sites/default/files/m/a/1/e/dsohowto.pdf

两者似乎都被实际使用打败了,但实际上并非如此。 需要的是寻找符号 links:

>LD_LIBRARY_PATH= LD_DEBUG=libs ldd /opt/foo/bin/bar
 21720:     find library=libstdc++.so.6 [0]; searching
 21720:      search path=/opt/foo/bin/../lib64/private:/opt/foo/bin/../lib64:/opt/foo/bin                (RPATH from file /opt/foo/bin/bar)
 21720:       trying file=/opt/foo/bin/../lib64/private/libstdc++.so.6

这是与我 install shared imported library with necessary links 提出的另一个问题的互动,其中建议 link 不是必需的。 如果您不指定完整的语义版本,它们显然 是必需的。

这将不起作用,因为 libstdc++.so.6.0.22 将导出与系统 libstdc++ 相同的符号,并且您最终将在两个库之间疯狂混合(假设您确实更改了较新的 libstdc++ 版本的 soname ).

您应该 link 整个 libstdc++ 静态(这可能需要库的静态 PIC 变体)并且不导出任何符号(可能使用 linker 版本脚本),或者link 只有新符号是静态的。

第二种方法似乎是目前最好的选择:它允许你使用大多数新的语言特性,但你仍然与系统的其余部分有很大程度的互操作性(特别是如果你配置 GCC --with-default-libstdcxx-abi=gcc4-compatible),并且您不必为部署安装任何额外的共享对象。 Developer Toolset software collection provides newer versions of GCC (and I believe the SUSE Linux Toolchain Module也是这样)。如果 C++ 对象跨共享对象边界(包括异常处理)传递,完全静态 linking 会导致问题,而这种选择性静态 linking 避免了许多这些问题。

我想我已经回答了我的问题,尽管不是我实际提出的问题。

RPATHLD_LIBRARY_PATH 之前搜索。 选择 /usr/lib64/libstdc++.so.6 而不是 libstdc++.so.6.0.22 的原因是从 /where/i/installed/libstdc++.so.6/where/i/installed/libstdc++.so.6.0.22

没有符号 link

因此规则是遵循您平台的标准(在它们合理的地方)。在这种情况下: 每当您安装共享库时,也会安装预期的 links。

我认为我问的实际问题在技术上仍然很有趣,所以如果有人有更好的答案(甚至多年后),我仍然会接受更好的答案。