如何在每个共享库中使用静态库的自己的副本

时间:2019-05-28 10:58:23

标签: c++ c linux gcc linker

我有一些无法更改或重建的静态库。该库使用全局变量。像这样:

//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

似乎libBlibC共享公共计数器变量。如果可以访问libA源代码,我将使用-fvisibility=hidden对其进行重建。但是不幸的是我只有二进制文件。

是否可以通过不重建libA来实现预期的行为?

2 个答案:

答案 0 :(得分:2)

如果LibA使用通过调用printA来增加libB和libC的静态计数器,那么没有对象文件操纵或不可移植的黑客攻击就无法做您想要的事情。

链接器在链接时会将对全局变量(甚至static的所有引用解析为同一符号)。

如果您愿意处理目标文件,那么以下内容将为您工作:

$ 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;

请注意,每次更新库后都必须重复此过程。

答案 1 :(得分:2)

您可以复制静态库并重命名所有使用全局状态的符号。因为这些符号是使用c ++编译的,所以您很不走运,因此这些符号被弄乱了。

您可以为所有访问编写C接口,并重新编译隐藏其符号的静态库,然后使用一些objcopy --prefix-symbolsg++ -Wl,--wrap=printA来前缀/重命名C符号。

或者您需要事先知道已经损坏的C ++名称,然后为库导出的每个符号调用objcopy --redefine-sym _Z6printAv=_Z10printAcopyv等。

下面是测试设置,该调用在整齐的名称上调用objcopy。我通过检查目标文件nm a.onm 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