Linking two shared libraries with some of the same symbols
Asked Answered
C

3

54

I link with two different shared libraries. Both libraries define some symbols that share a name but have different implementations. I can't make each library use its own implementation over the other.

For example, both libraries define a global function bar() that each calls internally. Library one calls it from foo1() and library two calls it from foo2().

Lib1.so:

T bar
T foo1()     // calls bar()

Lib2.so:

T bar
T foo2()     // calls bar()

If I link my application against Lib1.so and then Lib2.so the bar implementation from Lib1.so is called even when calling foo2(). If on the other hand, I link my application against Lib2.so and then Lib1.so, then bar is always called from Lib2.so.

Is there a way to make a library always prefer its own implementation above any other library?

Cladoceran answered 30/6, 2011 at 17:11 Comment(0)
S
59

There are several ways to solve this:

  • Pass -Bsymbolic or -Bsymbolic-functions to the linker. This has a global effect: every reference to a global symbol (of function type for -Bsymbolic-functions) that can be resolved to a symbol in the library is resolved to that symbol. With this you lose the ability to interpose internal library calls to those symbols using LD_PRELOAD. The symbols are still exported, so they can be referenced from outside the library.

  • Use a version script to mark symbols as local to the library, e.g. use something like: {local: bar;}; and pass --version-script=versionfile to the linker. The symbols are not exported.

  • Mark symbols with an approppiate visibility (GCC info page for visibility), which will be either hidden, internal, or protected. protected visibility symbols are exported as .protected, hidden symbols are not exported, and internal symbols are not exported and you compromise not to call them from outside the library, even indirectly through function pointers.

You can check which symbols are exported with objdump -T.

Shoemaker answered 30/6, 2011 at 19:27 Comment(1)
Hi, can you tell me please whether this applies to clang as well?Rest
J
5

You will have to create two 'wrapper' shared libs, one for each of your existing libs. Each one should be built with a --dynamic-list that lists only a few non-conflicting symbols that define an API. You will also need -Bsymbolic to avoid any global combination.

It might be less stressful to access the resulting libs via dlopen with suitable options, as well.

Juxtapose answered 30/6, 2011 at 17:20 Comment(1)
Thanks so much! Just the -Bsymbolic option (passed to the linker using the -Wl option) for both shared library linkings solved the problem for me.Cladoceran
D
2

Another way to solve this problem is using macro to change namespace.

Prerequisites

  • All elements (functions, classes, global variables, ...) are in a namespace.
  • The library doesn't heavily depend on macros in headers.

Solution

  • When compiling the library, define macro with namespace name to define it to something different. For example if the namespace is LibNS, use -DLibNS=LibNSv1 for one case and -DLibNS=LibNSv2 for the other.
  • When using libraries in the code, define macro according your current situation;

    #define LibNS LibNSv1
    #include "my_lib.h"
    #undef LibNS
    

Reasons why use this instead of other solutions

  • When the problematic library is used (at least partially) in header files (for example templates, inlines, ...); when you include them in your executable's code, resolver doesn't have idea whether these functions should be called from Lib1.so or Lib2.so.
  • Your compiler has poor/none support for other solutions (shouldn't happen with our intel/amd 32/64 bit CPUs, but it seems from Google search that some other platforms might have the problem).

Potential problems

  • It might be problematic to use both version in one cpp file of your executable; #include "my_lib.h" probably uses macro to protect against multiple-inclusion and undefining them to avoid this might cause lots of different problems (library author might change the macro name in the future, header defines some other macros, etc.).
  • The name LibNS might be used for something else in the library (variable, function, etc.); in that case, this name will be also changed to LibNSv1 or LibNSv2. This might lead to other problems depending on the library and how it's used.

Notes

  • This isn't meant to replace currently accepted answer (from ninjalj; feel free to copy-paste it), but extend it with another approach.
  • Main reason why I posted this answer is that I encountered this problem today, but answer didn't help due to problematic code being in header files.
  • My source: https://spin.atomicobject.com/2014/06/03/static-linking-c-plus-plus/
Digitalize answered 20/2, 2018 at 10:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.