I need to deploy a C++ application built on Ubuntu 12.10 with GCC 4.7’s libstdc++ to systems running Ubuntu 10.04, which comes with a considerably older version of libstdc++.
Currently, I’m compiling with
-static-libstdc++ -static-libgcc, as suggested by this blog post: Linking libstdc++ statically. The author warns against using any dynamically-loaded C++ code when compiling libstdc++ statically, which is something I haven’t yet checked. Still, everything seems to be going smoothly so far: I can make use of C++11 features on Ubuntu 10.04, which is what I was after.
I note that this article is from 2005, and perhaps much has changed since then. Is its advice still current? Are there any lurking issues I should be aware of?
That blog post is pretty inaccurate.
As far as I know C++ ABI changes have been introduced with every major release of GCC (i.e. those with different first or second version number components).
Not true. The only C++ ABI changes introduced since GCC 3.4 have been backward-compatible, meaning the C++ ABI has been stable for nearly nine years.
To make matters worse, most major Linux distributions use GCC snapshots and/or patch their GCC versions, making it virtually impossible to know exactly what GCC versions you might be dealing with when you distribute binaries.
The differences between distributions’ patched versions of GCC are minor, and not ABI changing, e.g. Fedora’s 4.6.3 20120306 (Red Hat 4.6.3-2) is ABI compatible with the upstream FSF 4.6.x releases and almost certainly with any 4.6.x from any other distro.
On GNU/Linux GCC’s runtime libraries use ELF symbol versioning so it’s easy to check the symbol versions needed by objects and libraries, and if you have a
libstdc++.so that provides those symbols it will work, it doesn’t matter if it’s a slightly different patched version from another version of your distro.
but no C++ code (or any code using the C++ runtime support) may be linked dynamically if this is to work.
This is not true either.
That said, statically linking to
libstdc++.a is one option for you.
The reason it might not work if you dynamically load a library (using
dlopen) is that libstdc++ symbols it depends on might not have been needed by your application when you (statically) linked it, so those symbols will not be present in your executable. That can be solved by dynamically-linking the shared library to
libstdc++.so (which is the right thing to do anyway if it depends on it.) ELF symbol interposition means symbols that are present in your executable will be used by the shared library, but others not present in your executable will be found in whichever
libstdc++.so it links to. If your application doesn’t use
dlopen you don’t need to care about that.
Another option (and the one I prefer) is to deploy the newer
libstdc++.so alongside your application and ensure it is found before the default system
libstdc++.so, which can be done by forcing the dynamic linker to look in the right place, either using
$LD_LIBRARY_PATH environment variable at run-time, or by setting an
RPATH in the executable at link-time. I prefer to use
RPATH as it doesn’t rely on the environment being set correctly for the application to work. If you link your application with
'-Wl,-rpath,$ORIGIN' (note the single quotes to prevent the shell trying to expand
$ORIGIN) then the executable will have an
$ORIGIN which tells the dynamic linker to look for shared libraries in the same directory as the executable itself. If you put the newer
libstdc++.so in the same directory as the executable it will be found at run-time, problem solved. (Another option is to put the executable in
/some/path/bin/ and the newer libstdc++.so in
/some/path/lib/ and link with
'-Wl,-rpath,$ORIGIN/../lib' or any other fixed location relative to the executable, and set the RPATH relative to
One addition to Jonathan Wakely’s excellent answer, why dlopen() is problematic:
Due to the new exception handling pool in GCC 5 (see PR 64535 and PR 65434), if you dlopen and dlclose a library that is statically linked to libstdc++, you will get a memory leak (of the pool object) each time. So if there’s any chance that you’ll ever use dlopen, it seems like a really bad idea to statically link libstdc++. Note that this is a real leak as opposed to the benign one mentioned in PR 65434.
You might also need to make sure that you don’t depend on the dynamic glibc. Run
ldd on your resulting executable and note any dynamic dependencies (libc/libm/libpthread are usal suspects).
Additional exercise would be building a bunch of involved C++11 examples using this methodology and actually trying the resulting binaries on a real 10.04 system. In most cases, unless you do something weird with dynamic loading, you’ll know right away whether the program works or it crashes.