We are distributing four different static libraries to customers:
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.
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).
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.