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

2019-03-04 14:50发布

问题:

Given a executable such that:

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

I want to change the requirement to the full version (including the minor version and patch level):

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

I know of two ways to do this:

  1. create a dummy library as per this question (Forcing or preventing use of a particular minor version of libstdc++)
  2. use 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

(I haven't figured out a working command line for --replace-needed)

Both of these feel like hacks to me. Is there a way to achieve the same at compile or link time using appropriate -Wl flags to gcc?

Ideally I want to avoid using -nostdlib as that requires I specific not just libstd++ but libc and everything else for which I do want standard versions.

For a regular library just linking to the specific version is enough for libstdc++ it is not (or rather I suspect -stdlib overrides subsequent fully qualified names I provide).

Background: I have executables which require a later version of libstdc++ than is installed on the system. Unfortunately the installed version could be the same major version and if so ld will happily use the system version as it matches the soname libstdc++.so.6

I prefer not to statically link as I actually want to install many small programs sharing the same C++ runtime and this would bloat the installation considerably.


Some information about my (the) library search path is available here:

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");

It is clear in my case that /usr/lib64 is being searched before the RPATH of the executable which is:

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

man ld.so suggests the search order should be:

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.

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

Both seem to be trumped by actual usage but in fact this is not the case. the needed is looking for symbolic 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

This is an interaction with another question I had install shared imported library with necessary links where the suggestion was that the links are not required. They clearly are required if you do not specify the full semantic version.

回答1:

This will not work because libstdc++.so.6.0.22 will export the same symbols as the system libstdc++, and you will end up with a wild mix between the two libraries (assuming you indeed change the soname of the newer libstdc++ version).

You should either link the whole of libstdc++ statically (this may require a static PIC variant of the library) and not export any symbols (probably with a linker version script), or link only the new symbols statically.

The second approach seems to be the best option at present: It allows you to use most new language features, but you have still a large degree of interoperability with the rest of the system (especially if you configure GCC with --with-default-libstdcxx-abi=gcc4-compatible), and you don't have to install any additional shared objects for deployment. This is how the Developer Toolset software collection provides newer versions of GCC (and I believe the SUSE Linux Toolchain Module, too). Full static linking causes problems if C++ objects are passed across shared object boundaries (which includes exception handling), and this kind of selective static linking avoids many of these these problems.



回答2:

I think I have answered my problem though not the question I actually asked.

RPATH is searched before LD_LIBRARY_PATH. The reason /usr/lib64/libstdc++.so.6 is being picked up rather than libstdc++.so.6.0.22 is that there is no symbolic link from /where/i/installed/libstdc++.so.6 to /where/i/installed/libstdc++.so.6.0.22

So the rule is follow the standards for your platform (where they are sensible). In this case: Whenever you install a shared library install the expected links as well.

I think the actual question I asked is still interesting technically so I will still accept a better answer to that if anyone has one (even years later).