【问题标题】:Is there a way to set the elf NEEDED field at link time?有没有办法在链接时设置精灵 NEEDED 字段?
【发布时间】:2017-12-27 11:45:17
【问题描述】:

给定一个可执行文件:

>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 --add-needed libstdc++.so.6.0.22 someprog >objdump -x someprog | grep c++ 需要 libstdc++.so.6 需要 libstdc++.so.6.0.22

(我还没有为 --replace-needed 找到一个可以工作的命令行)

这两个对我来说都像是黑客攻击。 有没有办法在编译或链接时使用适当的 -Wl 标志到 gcc 来实现相同的目标?

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

对于一个常规库来说,仅仅链接到特定版本就足够了 libstdc++ 它不是(或者更确切地说,我怀疑 -stdlib 会覆盖我提供的后续完全限定名称)。

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

匹配

我不喜欢静态链接,因为我实际上想安装许多共享相同 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 建议搜索顺序应该是:

如果库依赖项不包含斜杠,则搜索它 按以下顺序:

   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

两者似乎都被实际使用所压倒,但实际上并非如此。 需要的是寻找符号链接:

>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 的另一个问题的互动,其中建议不需要链接。 如果您没有指定完整的语义版本,那么它们显然是是必需的。

【问题讨论】:

  • 前/后情况似乎没有区别,所以不清楚你想要什么。
  • 这是我的错字(现已修复),我的意思是 6.0.22。不是这样。6
  • 现在它又有意义了,希望可以删除反对票。

标签: c++ gcc ld libstdc++


【解决方案1】:

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

您应该静态链接整个 libstdc++(这可能需要库的静态 PIC 变体)而不导出任何符号(可能使用链接器版本脚本),或者仅静态链接新符号。

第二种方法似乎是目前最好的选择:它允许您使用大多数新的语言功能,但您仍然与系统的其余部分有很大程度的互操作性(特别是如果您使用--with-default-libstdcxx-abi=gcc4-compatible 配置 GCC ),并且您不必安装任何其他共享对象进行部署。这就是Developer Toolset software collection 提供更新版本 GCC 的方式(我也相信SUSE Linux Toolchain Module)。如果 C++ 对象跨越共享对象边界(包括异常处理)传递,则完全静态链接会导致问题,而这种选择性静态链接可以避免许多此类问题。

【讨论】:

  • 我想你可能误解了问题的背景。可执行文件是由比系统版本更高版本的 gcc 构建的(6.3 与 4.9,因此令人惊讶的是 soname 只被赋予了不同的“补丁级别”。符号 GLIBCXX 版本更加不同)使用 .so.6.0.22 和 仅与该版本相关联。问题是在运行时可执行文件要求 .so.6 并获取系统 .so.6 而不是它需要的 .so.6.0.22。或者您是指将 .so.6 和 .so.6.0.22 都列为 NEEDED 的 patchelf 命令?这可能有点道理。
  • 如果已经构建了可执行文件,唯一的解决方案是将较新版本的 libstdc++ 放在库搜索路径中,以便程序为旧代码和新代码都拾取它。我在建议避免这种情况的方法。
  • 较新版本的 libstdc++ 已经在 RPATH 上。不幸的是,系统路径 /usr/lib64(包含 libstdc++.so.6 作为指向 libstdc++.so.6.0.19 的链接)胜过 RPATH。
  • DT_RUNPATH 路径元素在/usr/lib64 之前被提前搜索。如果新的 libstdc++ 没有被选中,那肯定是有另一个原因。或许您应该从您实际尝试解决的问题开始一个新问题?
  • 这实际上是针对一个老问题的下一部分(希望是最后一部分) - stackoverflow.com/questions/25979778/…
【解决方案2】:

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

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 的符号链接

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

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

【讨论】:

  • GCC 会为您安装该符号链接。如果您将libstdc++.so.6.0.22 文件放在其他位置并且不将符号链接也放在那里,那么您将得到您想要的!问题只是用户错误,解决方法是正确安装库,而不是重命名库或编辑 NEEDED 标签等复杂、容易出错的黑客攻击。
  • 如果您安装完整的 gcc 堆栈,当然可以。但是我们不想仅仅因为应用程序在开发中使用它就在生产服务器上安装 gcc。那将是疯狂的。我们只需要 C++ 运行时。
  • 我不是建议在生产服务器上安装整个 GCC,只是在您从安装树中复制的库旁边的符号链接。我没有说还要安装编译器二进制文件、头文件、文档等,甚至其他运行时库。但是当 GCC 安装 libstdc++ 时,它也会创建必要的符号链接。将图书馆搬到别处时也应该这样做。
  • 很公平,但是如果您没有设置 RPATH,而要求您实际需要的确切库版本,则单独的链接将无济于事。如果您链接到您自己构建的库(例如 foobar.1.0.2),您可以使用您不使用或不需要链接的完整版本(例如 foobar.1)。所以我认为这个问题仍然是合理的。在这种情况下,与创建安装符号链接的 RPM 的解决方法相关的问题 - 请参阅 stackoverflow.com/questions/45213145/…
猜你喜欢
  • 2020-04-10
  • 1970-01-01
  • 2023-04-10
  • 2020-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-08
  • 1970-01-01
相关资源
最近更新 更多