Wrapping different versions of a static library in dynamic libraries

My project has a dependency on a static library (just called libsomething from now on) on a third party. Recently, libsomething become available in a different version. My task is to provide my software to support the old and new versions. Only one version of libsomething used at runtime at any given time, but which version should be customizable between program runs.

I use MSVC2005 on WinXP, the secondary goal is to prepare for the switch to Linux and GCC.

Since both versions of libsomething use the same characters, the link to both of them in my executable file is out of the question, since the characters of both versions will collide everywhere during the link.

Although I could create two executable files (one of which refers to the old version, and the other to the new version), I cannot implement the decision about which executable to call in the final deployment environment (outdated reasons).

I came up with the idea of ​​creating a dynamic library wrapper for each version of libsomething and linking it at runtime depending on some configuration file. With MSCV, this would mean following the path of using LoadLibrary() , GetProcAddress() , etc., whereas on Linux I would have to use dlopen() and dlsym() .

I understand that using libtool (i.e. libtldl ) wraps this platform dependency for using shared libraries. Is this the right way? Are there any better (or at least different) ways? Are there alternatives to libtldl as open source?

+4
source share
3 answers

Several years have passed, but I would like to mention another solution for completeness. Instead of the manual dlopen and dlsym you can create simple stubs for all the necessary functions and at the first call (or when the program starts) decide which version of the library you need, load and resolve addresses.

You can write a script specifically for your project or use the Implib.so tool:

 # This will generate mylib.so.init.c and mylib.so.tramp.S # which implement stubs. These need to be linked to your # executable. $ gen-implib.py mylib.so 

Implib.so is only for Linux, but it should be easily adapted to Windows.

0
source

I know that you said that you cannot use two executable files because of the decision about what to execute, but whether you could exec execute the back and forth command between the executable files, depending on which version is selected in the configuration ?

+2
source

On Linux, it will be easier for you to link to the shared library and use symbolic links to fix the version - IMO is much easier than using dlopen() + dlsym() .

This way you will create shared libraries for the old and new versions of your library:

g++ -shared -o libshared.so.1.1 -Wl,-soname=libshared.so.1 -L. -Wl,--whole-archive libstatic.a.old -Wl,-no-whole-archive

and

g++ -shared -o libshared.so.1.2 -Wl,-soname=libshared.so.1 -L. -Wl,--whole-archive libstatic.a.new -Wl,-no-whole-archive

Create symbolic links:

 ln -s libshared.so.1.1 libshared.so.1 ln -s libshared.so.1 libshared.so 

Create an application by linking it to an old version of the library. I believe that both versions are binary compatible (ABI is not broken), but the new one may have several new characters.

g++ -o myapp myapp.cpp -L. -lshared

Since the SONAME shared library is libshared.so.1 , your application will depend on it and will look for libshared.so.1 in the paths from /etc/ld.so.conf or LD_LIBRARY_PATH

Before starting the application, you can set the libshared.so.1 symbolic link to point to libshared.so.1.2 or libshared.so.1.1 .


Some information on the linker options used here:

- all-archive
For each archive specified on the command line after the -whole-archive option, include each object file in the archive in the link, rather than search the archive for the necessary object files. This is usually used to turn an archive file into a shared library, forcing each object to be included in the resulting shared library. This option can be used several times.
Two notes when using this option from gcc: firstly, gcc does not know about this option, so you need to use -Wl, -all archive. Secondly, do not forget to use -Wl, -no-whole-archive after the list of archives, because gcc will add its own list of archives to yours and you may not want this flag to affect them either.

-soname = name
When creating a shared ELF object, set the DT_SONAME internal field to the specified name. When the executable is associated with a shared object that has a DT_SONAME field, then when the executable is launched, the dynamic linker will try to load the shared object specified by the DT_SONAME field, rather than the file name assigned to the linker.

+2
source

Source: https://habr.com/ru/post/1309305/


All Articles