【问题标题】:How to use own copy of static library in each shared library如何在每个共享库中使用自己的静态库副本
【发布时间】:2019-10-13 21:25:42
【问题描述】:

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

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

【问题讨论】:

  • If had access to I have only binary. - 所以你是否可以访问libA.a?您可以将 libA.a 解压缩为 .o 文件,然后使用 ex 重命名 all 符号。 objcopy --redefine-sym printA=printAcopy,然后重建 libAcopy.a,然后从 printC() 调用 printAcopy - printC() { printAcopy(); }
  • 将您的库副本放在单独的命名空间中。或者,围绕静态库构建一个包装共享对象。包装器将所有符号设为私有,并且仅导出具有不同名称的感兴趣的函数。
  • 两个库是分开链接的吗?
  • @KamilCuk,是的,我有libA.a,但我不想改变它。因为它可能会更新一次。
  • 不应该用“Linux”来标记吗?因为共享库的行为在 Linux 和 Windows 之间有所不同。在 Windows 下,共享库 (dll) 将拥有自己的这些全局变量版本,正如 OP 所期望的那样。

标签: c++ c linux gcc linker


【解决方案1】:

如果 LibA 使用静态计数器,libB 和 libC 通过调用 printA 递增,那么如果没有目标文件操作或非便携式黑客,就没有办法做你想做的事。

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

如果您愿意操作目标文件,那么以下内容应该适合您:

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

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

如果您可以使用nm(您要查找的名称将采用&lt;counter name&gt;.&lt;process ID&gt; 的形式)从静态库中获取符号并且您执行以下操作,那么您可以读写运行时的静态计数器变量:

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

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

【讨论】:

  • 你不需要ar x 只需要objcopy --prefix-symbols=copy_ liba.a liba-copy.a 但是有一个真正的问题,因为名字被打乱了。
  • @KamilCuk 谢谢。不知道。
【解决方案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

【讨论】:

  • Makefile 不能工作,除非它们有标签。 8 个空格不起作用。
  • Steckoverflow 搞砸了格式,只是 sed 's/^[[:space:]]\+/\t/' it ; )
  • 也许您应该将其添加到您的答案中。
猜你喜欢
  • 2019-07-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-13
  • 2012-07-29
  • 2023-03-03
相关资源
最近更新 更多