Edit: I found out later that the technique described below will only work in limited conditions. In particular, your shared libraries should only contain functions, without any global variables. If the libraries you want to send have global variables, then you will get a dynamic runtime linker error. This happens because global variables are moved before calling shared library constructors . Therefore, the linker needs to resolve these links early before the dispatch scheme described here can start.
One way to achieve what you want is to (ab) use the DT_SONAME field in the ELF header of your shared library. This can be used to change the name of the file that the dynamic loader ( ld-linux-so* ) loads at runtime to resolve the dependency of the shared library. This is best explained by example. Let's say I am compiling the libtest.so shared library with the following command line:
g++ test.cc -shared -o libtest.so -Wl,-soname,libtest_dispatch.so
This will create a shared library with the file name libtest.so , but the DT_SONAME field DT_SONAME set to libtest_dispatch.so . Let's see what happens when we associate the program with it:
g++ testprog.cc -o test -ltest
Let's look at the runtime library dependencies for the resulting binary test application:
> ldd test linux-vdso.so.1 => (0x00007fffcc5fe000) libtest_dispatch.so => not found libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fd1e4a55000) /lib64/ld-linux-x86-64.so.2 (0x00007fd1e4e4f000)
Note that instead of looking for libtest.so , the dynamic loader wants to load libtest_dispatch.so . You can use this to implement the required dispatch functions. Here's how I do it:
Create different versions of your shared library. I assume that there is some βgenericβ version that can always be used, with other optimized versions used at runtime, if necessary. I would name the generic version with the name "plain" library libtest.so and name the rest, but you chose (for example, libtest_sse2.so , libtest_avx.so , etc.).
When linking a generic version of a library, override its DT_SONAME with something else, such as libtest_dispatch.so .
Create a dispatcher library called libtest_dispatch.so . When the dispatcher boots at application startup, it is responsible for loading the appropriate library implementation. Here's the pseudocode that the libtest_dispatch.so implementation might look like:
#include <dlfcn.h>
When linking the application to your library, link it to the "vanilla" libtest.so , which has its own DT_SONAME , redefined to point to the dispatcher library. This makes sending essentially transparent to any application authors who use your library.
This should work as described above on Linux. On Mac OS, shared libraries have a "name for installation", which is similar to DT_SONAME used in ELF shared libraries, so instead you can use a process very similar to the one above. I'm not sure if something like this can be used on Windows.
Note. . The above makes one important assumption: ABI compatibility between different library implementations. That is, your library should be designed so that you can safely communicate with the most common version at connection time using an optimized version (e.g. libtest_avx.so ) at runtime.