有没有办法在 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
我知道有两种方法可以做到这一点:
- 根据这个问题创建一个虚拟库 (Forcing or preventing use of a particular minor version of libstdc++)
- 使用 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 避免了许多这些问题。
我想我已经回答了我的问题,尽管不是我实际提出的问题。
RPATH
在 LD_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。
我认为我问的实际问题在技术上仍然很有趣,所以如果有人有更好的答案(甚至多年后),我仍然会接受更好的答案。
给定一个可执行文件:
>objdump -x someprog | grep c++
NEEDED libstdc++.so.6
我想将需求更改为完整版(包括次要版本和补丁级别):
>objdump -x someprog | grep c++
NEEDED libstdc++.so.6.0.22
我知道有两种方法可以做到这一点:
- 根据这个问题创建一个虚拟库 (Forcing or preventing use of a particular minor version of libstdc++)
- 使用 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 避免了许多这些问题。
我想我已经回答了我的问题,尽管不是我实际提出的问题。
RPATH
在 LD_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
因此规则是遵循您平台的标准(在它们合理的地方)。在这种情况下: 每当您安装共享库时,也会安装预期的 links。
我认为我问的实际问题在技术上仍然很有趣,所以如果有人有更好的答案(甚至多年后),我仍然会接受更好的答案。