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

2

4

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 specify 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.

Silicify answered 21/7, 2017 at 12:12 Comment(3)
There does not seem to be a difference between the before/after case, so it's unclear what you want.Bibliotaph
That was a typo (now fixed) on my part I meant so.6.0.22. not so.6Silicify
Now that it makes sense again hopefully the downvotes will be removed.Silicify
S
1

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).

Silicify answered 21/7, 2017 at 17:32 Comment(4)
GCC installs that symlink for you. If you put the libstdc++.so.6.0.22 file somewhere else and don't also put the symlink there then you get what you asked for! The problem was simply user error, and the fix is to install the library properly, instead of complex, error-prone hacks like renaming the library or editing the NEEDED tags.Ibson
If you install the complete gcc stack yes of course. But we don't want to be installing gcc on a production server simply because an application used it in development. That would be madness. We only want the C++ runtime.Silicify
I'm not suggesting installing the whole of GCC on production servers, just the symink that is right next to the library you copied out of the install tree. I didn't say anything about also installing the compiler binaries, headers, documentation etc. or even the other runtime libraries. But when GCC installs libstdc++ it also creates the necessary symlinks. You should do so too when moving the library elsewhere.Ibson
Fair enough but the links alone will not help you if you haven't set the RPATH whereas asking for the exact library version you actually need will. If you link against a library you built yourself (e.g. foobar.1.0.2) you can use the full version you don't use or need the links (e.g. foobar.1). So I think the question is still legitimate. In this case there was a related issue to workaround with creating an RPM that installs the symlinks - see #45213645 .Silicify
B
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.

Bibliotaph answered 21/7, 2017 at 13:13 Comment(9)
I think may you misunderstand the background of the question. The executable is built by a later version of gcc than the system one (6.3 vs 4.9 so its surprising the soname is only given a different 'patchlevel'. The symbolic GLIBCXX version is much more different) using .so.6.0.22 and only linked with that version. The problem is at runtime the executable asks for .so.6 and gets the system .so.6 rather than the .so.6.0.22 that it needs. Or are you referring to the patchelf command that leaves both .so.6 and .so.6.0.22 listed as NEEDED? That might make some sense.Silicify
If the executable has already been built, the only solution is to put the newer version of libstdc++ on the library search path, so that it is picked up by the program for both old and new code. I was suggesting ways to avoid that.Bibliotaph
The newer version of libstdc++ is already on the RPATH. Unfortunately the system path /usr/lib64 (containing libstdc++.so.6 as a link to libstdc++.so.6.0.19) trumps the RPATH.Silicify
DT_RUNPATH path elements are searched early, before /usr/lib64. If the new libstdc++ is not picked up, there has to be another reason. Perhaps you should start a new question with the problem you are actually trying to solve?Bibliotaph
This is actually the next (hopefully final) installment towards an old question - #25980278Silicify
If the goal is to use C++11 and later on older systems, you should really investigate the Developer Toolset/Toolchain Module approach, which avoids this problem.Bibliotaph
I'm not clear what you mean by "the Developer Toolset/Toolchain Module approach" here.Silicify
You are correct that RPATH is searched early. However with NEEDED set to .so.6 it is searching for a symbolic link from .so.6 to .so.6.0.22 which I have not created in the installation. If I set the NEEDED field to the full semantic version .so.6.0.22. I do not need to. So creating the required symbolic links could be considered option three.Silicify
@BruceAdams, I expanded a bit on the DTS/Toolchain Module aspect. Basically, it avoids the need for shipping a new libstdc++ at deployment.Bibliotaph
S
1

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).

Silicify answered 21/7, 2017 at 17:32 Comment(4)
GCC installs that symlink for you. If you put the libstdc++.so.6.0.22 file somewhere else and don't also put the symlink there then you get what you asked for! The problem was simply user error, and the fix is to install the library properly, instead of complex, error-prone hacks like renaming the library or editing the NEEDED tags.Ibson
If you install the complete gcc stack yes of course. But we don't want to be installing gcc on a production server simply because an application used it in development. That would be madness. We only want the C++ runtime.Silicify
I'm not suggesting installing the whole of GCC on production servers, just the symink that is right next to the library you copied out of the install tree. I didn't say anything about also installing the compiler binaries, headers, documentation etc. or even the other runtime libraries. But when GCC installs libstdc++ it also creates the necessary symlinks. You should do so too when moving the library elsewhere.Ibson
Fair enough but the links alone will not help you if you haven't set the RPATH whereas asking for the exact library version you actually need will. If you link against a library you built yourself (e.g. foobar.1.0.2) you can use the full version you don't use or need the links (e.g. foobar.1). So I think the question is still legitimate. In this case there was a related issue to workaround with creating an RPM that installs the symlinks - see #45213645 .Silicify

© 2022 - 2024 — McMap. All rights reserved.