I am having an annoying problem with the linker. I want to link some symbols from a shared library to a static library, but not export its symbols (ie, I cannot simply merge the libraries or link with --whole-archive
). What I want is link (as in, like linking an executable, solving undefined symbols) my shared library to a static one and remove the undefined symbols.
The thing I am looking for is probably just a linker option but I can't put my finger on it.
I'll try to describe the problem the best I can (it's not that easy) and then provide a toy minimal example to play with.
Quick description:
I want to use the LD_PRELOAD
trick to trap some function calls in an executable. This executable is linked against a third party shared library, which contains the function definition of the functions I want to trap.
This third party library also contains symbols from yet another library, which I am also using in my library, but with a different (non-compatible) version.
What I want to do is compile my shared library and link it at compile time with the definitions of the last (static) library, without exporting the symbols, so that my shared library uses a different version from the one I want to trap.
Simplified problem description
I have a third party library called libext.so
, for which I don't have the source code. This defines a function bar
and uses a function foo
from another library, but the symbols are both defined there:
$> nm libext.so
0000000000000a16 T bar
00000000000009e8 T foo
As I mentioned, foo
is an external dependency, for which I want to use a newer version. I have an updated library for it, let's call it libfoo.a
:
$> nm libfoo.a
0000000000000000 T foo
Now the problem is that I want to create a dynamic library which re-defines bar
, but I want my library to use the the definition of foo
from libfoo.a
and i want the functions from libext.so
to call the function foo
from libext.so
. In other words, I want a compile time linkage of my library to libfoo.a
.
What I am looking for is define a library which uses libfoo.a
but doesn't export its symbols. If I link my library to libfoo.a
, I get:
$> nm libmine.so
0000000000000a78 T bar
0000000000000b2c T foo
Which means I overload both foo
and bar
(i don't want to override foo
). If i don't link my library to libfoo.a
, I get:
$> nm libmine.so
0000000000000a78 T bar
U foo
So my library will use their version of foo
, which I don't want either. What I want is:
$> nm libmine.so
0000000000000a78 T bar
Where foo
is linked at compile time and its symbol not exported.
Minimal example
You don't need to read this, but you can use it to play around and find a solution.
bar.cpp
: represents the third party app I don't have the code for:
#include <iostream>
extern "C" void foo(){ std::cerr << "old::foo" << std::endl; }
extern "C" void bar(){ std::cerr << "old::bar" << std::endl; foo(); }
foo.cpp
: represents a newer version of a function used by both my lib and the third party:
#include <iostream>
extern "C" void foo(){ std::cerr << "new::foo" << std::endl; }
trap.cpp
: the code from my library, it traps bar
, calls the new foo
and forwards:
#include <iostream>
extern "C" {
#include <dlfcn.h>
}
extern "C" void foo();
extern "C" void bar(){
std::cerr << "new::bar" << std::endl;
foo(); // Should be new::foo
void (*fwd)() = (void(*)())dlsym(RTLD_NEXT, "bar");
fwd(); // Should use old::foo
}
exec.cpp
: a dummy executable to call bar
:
extern "C" void bar();
int main(){
bar();
}
Makefile
: Unix only, sorry
default:
# The third party library
g++ -c -o bar.o bar.cpp -fpic
gcc -shared -Wl,-soname,libext.so -o libext.so bar.o
# The updated library
g++ -c -o foo.o foo.cpp -fPIC
ar rcs libfoo.a foo.o
# My trapping library
g++ -c -o trap.o trap.cpp -fPIC
gcc -shared -Wl,-soname,libmine.so -o libmine.so trap.o -ldl -L. -lfoo
# The dummy executable
g++ -o test exec.cpp -L. libext.so
In this case, bar
calls foo
; the normal execution is:
$> ./test
old::bar
old::foo
Preloading my library intercepts bar
, calls my foo
and forwards bar
, the current execution is:
$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
new::foo
The last line is wrong, the desired output is:
$> LD_PRELOAD=libmine.so ./test
new::bar
new::foo
old::bar
old::foo
Static Linking and Static Libraries is the result of the linker making copy of all used library functions to the executable file. Static Linking creates larger binary files, and need more space on disk and main memory.
You can't statically link a shared library (or dynamically link a static one). The flag -static will force the linker to use static libraries (. a) instead of shared (.
Disadvantages: Constant load time every time a file is compiled and recompiled. Larger in size because the file has to be recompiled every time when adding new code to the executable.
You should #include "libstatic. h" , i.e. use the appropriate header file in your code (that's why your code doesn't compile) and include the path to your libstatic. a in the linker options as one of your input libraries. This webpage has some examples on linking to a static library, e.g.
You want to use a linker version script, which exports the symbol(s) you want (bar
here) and hides everything else.
Example here.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With