Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c

linux

gcc

linker

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?

like image 323
yrHeTateJlb Avatar asked May 28 '19 10:05

yrHeTateJlb


People also ask

Are static libraries linked?

Static libraries are just collections of object files that are linked into the program during the linking phase of compilation and are not relevant during runtime.

Can shared library use another shared library?

A library does not use another library as such. You reference the header library of shared library a from library b. They can both be shared. Then, when you link your executable you include both so files in the link stage.

When use static library vs shared library?

Static libraries take longer to execute, because loading into the memory happens every time while executing. While Shared libraries are faster because shared library code is already in the memory.

How do I link a static library in Visual Studio?

On Property Pages, go to C/C++->General->Additional Include Directories and provide the path, where the header file of the library that you want to use is located. Then go to Linker->General->Additional Library Directories and specify the path, where your . lib file is located.


2 Answers

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.

like image 103
S.S. Anne Avatar answered Sep 17 '22 12:09

S.S. Anne


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
like image 20
KamilCuk Avatar answered Sep 21 '22 12:09

KamilCuk