Reputation: 477
I have some static library that I can't change or rebuild. The library uses global variables. Something like this:
//lib A
#include <iostream>
static int i = 0;
void printA(){
std::cout << i++ << std::endl;
}
I want to create two shared libraries that have their own "copy" of static library and its global state:
//lib B
#include "liba.h"
void printB(){
printA();
}
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
//lib C
#include "liba.h"
void printC(){
printA();
}
... and use them simultaneously:
#include "libb.h"
#include "libc.h"
int main(){
printB();
printB();
printC();
printC();
}
I expect following output:
0
1
0
1
.. but actually get:
0
1
2
3
Seems like libB
and libC
share common counter variable. If had access to libA
source code, I would rebuild it with -fvisibility=hidden
. But unfortunately I have only binary.
Is there any way to achieve expected behavior without libA
rebuilding?
Upvotes: 9
Views: 1656
Reputation: 140880
You can copy the static library and rename all symbols that use the global state. Because the symbols are compiled with c++, you are out of luck, the symbols are mangled.
You can write a C interface for all the accesses and recompile the static library hiding it's symbols and then use some objcopy --prefix-symbols
org++ -Wl,--wrap=printA
to prefix/rename the C symbols.
Or you need to know beforehand the already mangled C++ names, and then call objcopy --redefine-sym _Z6printAv=_Z10printAcopyv
etc. for each symbol the library exports.
Below is the test setup that calls the objcopy
on the mangled names. I found out the symbol names by inspecting the object files, nm a.o
and nm c.o
. Here it is:
cat <<EOF >Makefile
all: liba.a b.o main.o c.o
# we have access only to liba.a only
objcopy --redefine-sym _Z6printAv=_Z10printAcopyv liba.a libacopy.a
g++ main.o b.o c.o liba.a libacopy.a -o a.out
./a.out
liba.a: a.o
ar rcs liba.a a.o
clean:
rm -fr *.o *.a *.out tmp
EOF
cat <<EOF >a.cpp
#include <iostream>
static int i = 0;
void printA(){
std::cout << i++ << std::endl;
}
EOF
cat <<EOF >b.cpp
void printA();
void printB(){
printA();
}
EOF
cat <<EOF >c.cpp
void printAcopy();
void printC(){
printAcopy();
}
EOF
cat <<EOF >main.cpp
void printB();
void printC();
int main(){
printB();
printB();
printC();
printC();
}
EOF
You can compile with make
and run:
g++ -c -o a.o a.cpp
ar rcs liba.a a.o
g++ -c -o b.o b.cpp
g++ -c -o main.o main.cpp
g++ -c -o c.o c.cpp
# we have access only to liba.a only
objcopy --redefine-sym _Z6printAv=_Z10printAcopyv liba.a libacopy.a
g++ main.o b.o c.o liba.a libacopy.a -o a.out
./a.out
0
1
0
1
Upvotes: 3
Reputation: 15556
If LibA uses a static counter which libB and libC increment by calling printA
, then there's no way to do what you want without object file manipulation or non-portable hacks.
The linker resolves all references to global variables (even static
s) to the same symbol at link time.
If you're willing to manipulate object files, then the following should work for you:
$ objcopy --prefix-symbols=copy_ liba.a liba-copy.a
#define printA copy_ printA
#include "liba.h"
/* ... */
If you can get the symbols from the static library using nm
(the name you'll be looking for will be in the form of <counter name>.<process ID>
) and you do something like the following, then you can read and write the static counter variable at runtime:
int counter asm("<counter name>.<process ID>");
counter = 0;
Note that this process will have to be repeated after every update of the library.
Upvotes: 3