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

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

本文关键字:自己的 静态 副本 共享      更新时间:2023-10-16

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

//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重建的情况下实现预期的行为?

如果 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;

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

您可以复制静态库并重命名所有使用全局状态的符号。 因为符号是用 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