Forcing or preventing use of a particular minor version of libstdc++

9

In order to make use of C++11 and c++14 features I have an application compiled using a newer version of gcc (4.9.1) and thus an newer version of libstdc++. The application consists of many small programs so I am linking with libstdc++ as a shared library rather than a static one (i.e. I don't wish to use -static-libstdc++)

I wish to ship the new version of libstdc++ with the application under /opt//lib64 (note: that this is specifically allowed under an exception to the GPL)

The new version of libstdc++.so differs with the version on the target platform only by minor version. libstdc++ is designed to be forward compatible so that existing programs can use the new version of the library. However, I have observed subtle differences in behaviour (i.e. bugs) when some programs use the new version rather than the older one. I wish to prevent this.

I also find that ld will try to link my application with the system version of libstdc++ unless I put /opt//lib64 earlier on the LD_LIBRARY_PATH. Supposedly you can force linking against a specific version using -l:<library>.<version>, however, this doesn't seem to work. I suspect that it would for a user created library but not for a language runtime library like libstd++ because gcc itself generates the linker script. Also on one of my target platforms (RHEL5) it is not even understood by gcc/ld. I presume this is possible via using -nostdlib and linking in everything required (e.g. -lgcc) in my build system rather than leaving it to gcc which I would prefer. So far I haven't tried this.

A simple way workaround for this is to ensure LD_LIBRARY_PATH contains /opt//lib64 when I run my application and not otherwise or equally I could use LD_PRELOAD with the correct library version. The problem with this if someone decides ignore my advice and runs

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/<vendor>/lib64 

it could lead to subtle and hard to diagnose problems. So I have been looking for a better way.

I wondered if there was some way I could rename libstdc++ as lib_stdc++ and link against that soname instead. Renaming libstdc++ is not enough as you need to alter the soname in the file as given by readelf. i.e

0x000000000000000e (SONAME)             Library soname: [libstdc++.so.6]

If you do this on a normal program linked by gcc even using -l:stdc++.so.6.0.20 say you will notice this gives the major version and not the specific minor version. i.e.

readelf -d <myapp>
0x0000000e (SONAME)                     Library soname: [libstdc++.so.6]

rather than:

0x0000000e (SONAME)                     Library soname: [libstdc++.so.6.0.20]

So I have instead created a dummy shared library with the soname I want to depend on in order to add a dependency as follows:

gcc dummy.o -Wl,-soname,lib<vendor>_stdc++.so.6.0.20 -nostdlib -shared -o lib<vender>_dummycpp.so

(where dummy.o is an empty object file made from an empty source file to stop -nostdlib leading to complaints)

then:

gcc <myapp> -l<vendor>_dummycpp

as desired I now get:

readelf -d <myapp>

0x0000000000000001 (NEEDED)             Shared library: [lib<vendor>_stdc++.so.6.0.20]

instead of

0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]

The dummycpp library contains all the symbols in libstdc++ because it is libstd++ so there is no need for gcc to link with the soname libstdc++ at all. I appear to have solved the issue completely.

It strikes me as a slightly hacky solution so the questions I want to ask here are:

  • Is this a good idea?

  • If not why not?

  • Is there a better/more correct way?

Note: that if you package the libstdc++ with a different name (e.g. lib_stdc++.so.6.0.20) in an RPM you will get a missing dependency on it and need to install using --nodeps. This is because RPM scans for link time dependencies. The workaround for this is to install the dummy library as well. RPM will then pick up the soname from the dummy library and not claim it is missing.

c++
linux
gcc
c++11
libstdc++
asked on Stack Overflow Sep 22, 2014 by Bruce Adams • edited Mar 30, 2016 by Bruce Adams

1 Answer

2

The libstdc++ FAQ entry How do I insure that the dynamically linked library will be found links to the manual section Finding Dynamic or Shared Libraries which explains how to use RPATH instead.

My preferred method is to use an RPATH of $ORIGIN which means that searching for dynamic library dependencies starts in the same directory as the binary (see ld.so(8)). So if you link with '-Wl,-rpath,$ORIGIN' (note the quotes to prevent $ORIGIN being expanded by the shell) then you can install shared libraries in the same directory as your installed binary and they will be found when your binary is run. Or use '-Wl,-rpath,$ORIGIN/../lib' if you'd rather have separate bin and lib directories under some installation prefix.

With the library installed alongside the binary in some custom path that ldconfig doesn't scan, and no LD_LIBRARY_PATH messing up the environment, the newer libstdc++ will never be found by applications which are not supposed to use that version.

Make sure you also install the libstdc++.so.6 symlink pointing to the libstdc++.so.6.0.20 file so that the DT_NEEDED for libstdc++.so.6 can find the file.

answered on Stack Overflow Jul 22, 2016 by Jonathan Wakely • edited Jul 23, 2017 by Jonathan Wakely

User contributions licensed under CC BY-SA 3.0