c++ 有没有可能告诉链接器使用带有一些重复符号的库,就好像这些符号实际上是不同的?

woobm2wo  于 2023-01-28  发布在  其他
关注(0)|答案(1)|浏览(112)

我们将向客户分发四种不同的静态库:

  • 库A:包含一些通用函数,但也包含一些来自Linux的嵌入式通用库,这些库不打算暴露给客户,而是暴露给我们的其他库(因此我们需要导出符号)
  • 库B:有一些未解决的符号,可在库A中找到
  • 库C:有一些未解决的符号,可在库A中找到
  • 库D:有一些未解决的符号,可在库A中找到

问题是一些客户在他们的项目中也使用了其他库(我们称之为X),这些库包含了来自A的重复符号,导致了意外的崩溃(但它们可能是不同的版本,因此他们无法使用A)。我们无法控制这些库(X),因为它们是预构建的开源项目。
我们不能将所有的库链接在一起(去掉公共库符号),因为这会浪费资源。有些客户只需要C或B或D(与A一起)。
我们不能将B/C/D与本地静态链接的A一起分发,因为某些客户可能正在使用C和B。这将再次导致资源浪费,因为当他们链接其项目时,最终将获得A的两个副本。
我们的客户有没有办法告诉链接器,当他们链接X、A和B时,A中的符号只能在解析B中未定义的符号时使用?

lld -o myprogram main.o helper1.o -L. -lX -lA -lB

应该只对B中未解析的符号使用A,但决不对helper1.o中未解析的符号使用A。是否有任何标志组合可以实现这一点?
我们已经考虑过在A中重命名符号或给符号加前缀,但是所有的库都很庞大,而且过程也不简单,而且重构A中的代码以包含名称空间也远非简单。
尝试了不同的编译器标志,但都没有帮助。我查阅了链接器文档,什么也没有找到。

ddrv8njm

ddrv8njm1#

我们的客户有没有办法告诉链接器,当他们链接X、A和B时,A中的符号只能在解析B中未定义的符号时使用?
没有。
解决这个问题的一个常用方法是给libA.a中的 * all * 非静态符号加上一个唯一的前缀(例如用your_company_A_foo()代替foo()),这样就不太可能与其他库发生符号冲突。
一种常见的方法是使用宏来定义函数,例如:

#define PFX_A(name) my_prefix_a_ ## name

int PFX_A(foo) () { return 42; }
int PFX_A(bar) () { return 24; }

以上代码在编译时定义了my_prefix_a_foo()my_prefix_a_bar()

$ nm f.o
000000000000000b T my_prefix_a_bar
0000000000000000 T my_prefix_a_foo

在本地测试/开发期间,如果需要,您甚至可以完全删除前缀。
这也使得发布libA.a的"版本2"变得容易,并保证旧的(版本1)libB.a永远不会与新的libA.a链接--只需更改前缀以在其中包含"v2"。

    • 更新日期:**

问题是A是一个巨大的库(包括它自己版本的一些流行的Linux库)。
1.我希望您已经和您的律师谈过了--流行的Linux库往往是GPL所涵盖的,这可能被认为是一种重新分发,这将要求您采取措施遵守GPL。
1.您仍然可以重命名libA.a中的所有全局符号,使其具有唯一的前缀,只需使用objcopy --prefix-symbol=your_company_A即可,而不必在源代码级别执行此操作。
示例:

$ gcc -c f.c
$ objcopy --prefix-symbol=my_prefix_a_ f.o f2.o
$ nm f.o f2.o

f.o:
000000000000000b T bar
0000000000000000 T foo

f2.o:
000000000000000b T my_prefix_a_bar
0000000000000000 T my_prefix_a_foo

附言
lld -o myprogram main.o helper1.o -L. -lX -lA -lB
您 * 永远 * 不应该将任何用户级代码与ld链接(或lld)--始终使用适当的编译器驱动程序(gccg++clang)。直接使用ld的链接命令几乎从不正确(上面的链接行当然不是),当一些常见特性(如C++析构函数或线程本地存储)神秘地不起作用时,您会发现这一点。

相关问题