Как использовать собственную копию статической библиотеки в каждой разделяемой библиотеке - PullRequest
6 голосов
/ 28 мая 2019

У меня есть статическая библиотека, которую я не могу изменить или перестроить. Библиотека использует глобальные переменные. Примерно так:

//lib A
#include <iostream>

static int i = 0;

void printA(){
    std::cout << i++ << std::endl;
}

Я хочу создать две общие библиотеки, которые имеют свою собственную «копию» статической библиотеки и ее глобальное состояние:

//lib B
#include "liba.h"

void printB(){
    printA();
}

⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀

//lib C
#include "liba.h"

void printC(){
    printA();
}

... и используйте их одновременно:

#include "libb.h"
#include "libc.h"

int main(){
    printB();
    printB();
    printC();
    printC();
}

Я ожидаю следующий вывод:

0
1
0
1

.. но на самом деле получается:

0
1
2
3

Похоже, libB и libC имеют общую переменную счетчика. Если бы у меня был доступ к libA исходному коду, я бы перестроил его с помощью -fvisibility=hidden. Но, к сожалению, у меня есть только двоичный файл.

Есть ли способ достичь ожидаемого поведения без libA восстановления?

Ответы [ 2 ]

2 голосов
/ 28 мая 2019

Вы можете скопировать статическую библиотеку и переименовать все символы, которые используют глобальное состояние. Поскольку символы скомпилированы с c ++, вам не повезло, символы искажены.

Вы можете написать интерфейс C для всех обращений и перекомпилировать статическую библиотеку, скрывая ее символы, а затем использовать некоторые objcopy --prefix-symbols или g++ -Wl,--wrap=printA для префикса / переименования символов C.

Или вам нужно заранее знать уже искаженные имена C ++, а затем вызывать objcopy --redefine-sym _Z6printAv=_Z10printAcopyv и т. Д. Для каждого символа, который экспортирует библиотека.

Ниже приведена тестовая настройка, которая вызывает objcopy для искаженных имен. Я узнал имена символов, проверив объектные файлы nm a.o и nm c.o. Вот оно:

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

Вы можете скомпилировать с make и запустить:

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
2 голосов
/ 28 мая 2019

Если LibA использует статический счетчик, который libB и libC увеличивают, вызывая printA, то нет никакого способа сделать то, что вы хотите, без манипулирования объектными файлами или непереносимых хаков.

Компоновщик разрешает все ссылкиглобальным переменным (даже static s) к одному и тому же символу во время ссылки.

Если вы хотите манипулировать объектными файлами, тогда вам должно работать следующее:

$ objcopy --prefix-symbols=copy_ liba.a liba-copy.a

#define printA copy_ printA
#include "liba.h"
/* ... */

Если вы можете получить символы из статической библиотеки, используя nm (имя, которое вы будете искать, будет в форме <counter name>.<process ID>), и вы сделаете что-то вроде следующего, то выможет читать и записывать переменную статического счетчика во время выполнения:

int counter asm("<counter name>.<process ID>");
counter = 0;

Обратите внимание, что этот процесс придется повторять после каждого обновления библиотеки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...