Home > Net >  Is it possible to tell a linker to use libraries with some duplicated symbols as if the symbols were
Is it possible to tell a linker to use libraries with some duplicated symbols as if the symbols were

Time:01-26

We are distributing four different static libraries to customers:

  • Library A: contains some common functions but also some embedded common libraries from linux which are not intended to be exposed to the customer, but to other of our libraries (so we need to export the symbols)
  • Library B: has some unresolved symbols, to be found on library A
  • Library C: has some unresolved symbols, to be found on library A
  • Library D: has some unresolved symbols, to be found on library A

The issue is that some customers are also using other libraries in their projects (let's call it X) that include duplicated symbols from A, leading to unexpected crashes (but those may be a different version, so they cannot use A). We do not control these libraries (X), as they are prebuilt open source projects.

We cannot distribute all our libraries linked together (stripping the common libraries symbols) as this would be a waste of resources. Some customers only need C or B or D (together with A).

We cannot distribute B/C/D with locally static linked A because some customers may be using C and B. This would lead again to wasted resources as when they link their project they will end up with two copies of A.

Is there any way for our customers to tell the linker that when they are linking against X, A and B, the symbols in A should only be used when resolving undefined symbols in B? E.g.

lld -o myprogram main.o helper1.o -L. -lX -lA -lB

should use A only for the unresolved symbols in B, but never for the unresolved symbols in helper1.o. Is there any combination of flags to achieve so?

We already considered renaming or prefixing symbols in A, but all the libraries are huge and the process is not trivial. Also refactoring the code in A to include namespaces is far from trivial.

Tried different compiler flags, but none of them helped. I went through linker documentation and found nothing.

CodePudding user response:

Is there any way for our customers to tell the linker that when they are linking against X, A and B, the symbols in A should only be used when resolving undefined symbols in B?

No.

A common solution to the problem you have is to prefix all non-static symbols in libA.a with a unique prefix (e.g. your_company_A_foo() instead of foo()). This makes symbol collisions with other libraries exceedingly unlikely.

And a common way to do that is to define your functions using a macro, e.g.

#define PFX_A(name) my_prefix_a_ ## name

int PFX_A(foo) () { return 42; }
int PFX_A(bar) () { return 24; }

Above code defines my_prefix_a_foo() and my_prefix_a_bar() when compiled:

$ nm f.o
000000000000000b T my_prefix_a_bar
0000000000000000 T my_prefix_a_foo

During local testing / development you could even remove the prefix completely if desired.

This also makes it easy to ship "version 2" of libA.a and guarantee that old (version 1) libB.a will never be linked with new libA.a -- just change the prefix to include "v2" in it.

Update:

The issue is that A is a huge library (including its own version of some popular linux libraries).

  1. I hope you've talked to your lawyers -- popular Linux libraries tend to be covered by GPL, and this may be considered a re-distribution, which would require you to take steps to comply with GPL.
  2. You can still rename all the global symbols in libA.a to have a unique prefix. You just do that using objcopy --prefix-symbol=your_company_A instead of doing it at the source level.

Example:

$ gcc -c f.c
$ objcopy --prefix-symbol=my_prefix_a_ f.o f2.o
$ nm f.o f2.o

f.o:
000000000000000b T bar
0000000000000000 T foo

f2.o:
000000000000000b T my_prefix_a_bar
0000000000000000 T my_prefix_a_foo

P.S.

lld -o myprogram main.o helper1.o -L. -lX -lA -lB

You should never link any user-level code with ld (or lld) on UNIX -- always use appropriate compiler driver (gcc or g or clang). The link command using ld directly is almost never correct (above link line certainly isn't), as you'll discover when some common feature (like C destructors or thread-local storage) mysteriously doesn't work.

  • Related