We have a given set of legacy C++ libraries compiled on an old release system with the very old compiler gcc 3.2 using libstdc++.so.5.0.0. An up-to-date CentOS7 will run an executable compiled on the old OS using the compatibility library libstdc++.so.5.0.7, while the current C++ version with a different library ABI is libstdc++.so.6.
Our new version of the executable is built on the new OS and cannot be built on the old platform any more. But currently recompiling the legacy libs on CentOS7 is also not an option: the numerical results will be slightly different. So I would like to use them as they are with their libstdc++.so.5, while the rest of my binary uses c++ version 6.
this post suggest using a wrapper c-interface library which is linked with the static version libstdc++.a.5.
this post mainly suggests the same.
This method seems possible in our case since the interface is only C-style and does not use C++ objects. And the C-ABI seems to work as a kind of barrier between the two versions of the c++ lib.
However, I have yet to find someone providing the static C++.5 libs. Also I think I have once heard talk about using two shared lib versions of libstdc++ in parallel, in one application. So is there also a solution keeping the shared C++.5 libs? I have some weak memory that they also used a C-library as border. Would it also work with dlopen()?
My current situation is like this:
Compiled on old OS with old gcc-3.2, libstdc++.so.5, 32-bit mode:
- libA1.so using C++
- libA2.so calling libB.so, both use C++, libA2 explicitely linked against libstdc++ with linker option -lstdc++, libB not but probably it gets its symbols from libA
- libwrapper.so only using C not C++, linked with/using libA1 and libA2.
Compiled on new OS with new gcc-4.8.5:
- executable (32-bit) calling libwrapper.so by using dlopen() system call, so indirectly calling C++ libs libA1.so and libA2.
- executable also uses a shared C++ lib libC.so (but interface is C style)
- executable also uses a shared C++ lib libB.so (but interface is C style), which is also needed by libA1 and libA2.
So the executable must use the new libstdc++.so.6 for libC, but will indirectly call libA1, libA2, libB which must use the old libstdc++.so.5.
Do I have to limit the symbols exported by the wrapper lib exactly to my API, not exposing any c++ symbols like in pobedims comment in 1, using a version-script flag for the linker ?
Or maybe I just link the legacy lib on the old OS not with -lstdc++, but with the full name /usr/lib/libstdc++.so.5 ? And libB will continue to get the correct symbols from libA2 ?
My executable links/depends on ...
- libB.so (C++)
- libC.so (C++) which needs libstdc++.6
- libwrapper.so (with C interface), which is loaded using the dynamic load dlopen(), and which itself is linked to the "worker" libA1 and libA2 (which also present a C interface):
- libA1.so using C++.5
- libA2.so using C++.5
- libB.so using C++.5
The executable is linked with -rdynamic, maybe that is a problem? How can I be sure that no symbols in libA1, A2, B are resolved using symbols from the executable with same name?
Edit later / Resolution:
Note1: Trying on the old platform to link using archive version libstdc++.a.5 leads to an error which is only fixed in later version of gcc >= 3.4:
Error: ld: libwrapper.so: undefined versioned symbol name _ZSt10time_put_w@@GLIBCPP_3.2
ld: failed to set dynamic section sizes: Bad value
so this is not an option.
Note2: LibB was a problem since it is expected by the working libs and by the executable itself. If the executable uses the new version of libB it introduces libstdc++.so.6, of which the symbols are passed to the worker libs, which crash upon start. I have changed the version of libB used to the one compiled on the old system, simply by exchanging it on the runtime system. This is possible because of the simple C interface being binary compatible. Thus my executable (via libB) and the worker libs libA1 and libA2 all use the same libstdc++.so.5. My (C) executable is built directly linking libstdc++.so.5. Now my executable can be started and works.
Note3: I found some numerical differences between the reference results and the new result. Using LD_PRELOAD mechanism has worked to resolve these: LD_PRELOAD=libstdc++.so.5 my_executable
This seems to work nicely. I think by using LD_PRELOAD I make sure that libstdc++.so.5 has precendence in supplying the symbols resolved from libstdc++. What I did not mention before: we used the Intel compiler on the old platform, and I used the math lib libimf.so from it like this: LD_PRELOAD="libimf.so libstdc++.so.5" to provide both libs. As a result the numerical differences vanished, which seem to be caused by the implementation of common math functions.