Forcing or preventing use of a particular minor version of libstdc++
Asked Answered
T

1

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.

Throw answered 22/9, 2014 at 17:25 Comment(8)
Have you considered using -rpath (linker option) to specify a nonstandard location to search in first? ie: -Wl,-rpath=/opt/lib64Chaunce
just out of curiosity, what are the subtle differences you are observing?Hieronymus
@Hieronymus I was curious about that too. I would venture to guess it may be actual bugs in the OPs code than libstdc++. Just that the bugs manifest themselves with a different library.Chaunce
I haven't tried to debug the specifics. I have another custom app which was compiled using the original libstdc++ and I observed it reporting errors when it picked up the later version of the library on LD_LIBRARY_PATH rather than the version it was compiled with. It could be down to bug incompatability or undefined behaving.Throw
I haven't tried -rpath however I don't think it would solve the problem of existing programs using the new version by mistake. From what I read it just seems like a way of adding a default search path directly to my program. It might make things worse for it in fact if care isn't taken to allow for it being installed to a non-standard location.Throw
I would try to use g++ only for compilation and use ld manually giving it the -l you want to link with and see if that prevents it from taking another version of the lib.Theomania
My build process already has separate compilation and linking stages, though we use gcc as the driver for the final link as well. I don't see how calling the linker directly would help here. Besides, that won't help prevent existing applications from taking the new version of the library by mistake.Throw
This question (unix.stackexchange.com/questions/109477/…) might suggest using a custom ld.so.cache as an alternative option. I'm not sure if its any better or worse than my solution however.Throw
T
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.

Tegular answered 22/7, 2016 at 10:51 Comment(8)
There is still a potential problem is if users update LD_LIBRARY_PATH so that it does scan my install path. At the moment I actively encourage that as I'm not using $ORIGIN. I will test this solution as it ought to improve on my hack.Throw
If users set LD_LIBRARY_PATH to find a different library that causes problems then surely they get to keep both pieces.Tegular
It can be quite subtle though.Throw
Put them in a directory adjacent to the binary called do_not_use_this_you_idiots and use '-Wl,-rpath,$ORIGIN/do_not_use_this_you_idiots' ? :) Nobody's going to say "I used LD_LIBRARY_PATH=/foo/bar/do_not_use_this_you_idiots and now I get weird problems"Tegular
Good idea. I like your thinking but I also like trying to follow the lsb rules so I think I will opt for something along the lines of /opt/<vendor>/private/lib64.Throw
There is one problem that RPATH alone does not solve - see my related question #45237894Throw
Just to say the RPATH $ORIGIN tip has been very helpful in making my applications more user friendly (no need to set LD_LIBRARY_PATH any more), though I haven't solved the original problem to my satisfaction yet.Throw
Actually RPATH does solve that problem provided you install the proper symbolic links along with your library. See my answer to that question.Throw

© 2022 - 2024 — McMap. All rights reserved.