yrHeTateJlb
yrHeTateJlb

Reputation: 477

How to use own copy of static library in each shared library

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

Answers (2)

KamilCuk
KamilCuk

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

S.S. Anne
S.S. Anne

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 statics) 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

Related Questions