我们将向客户分发四种不同的静态库:
- 库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中的代码以包含名称空间也远非简单。
尝试了不同的编译器标志,但都没有帮助。我查阅了链接器文档,什么也没有找到。
1条答案
按热度按时间ddrv8njm1#
我们的客户有没有办法告诉链接器,当他们链接X、A和B时,A中的符号只能在解析B中未定义的符号时使用?
没有。
解决这个问题的一个常用方法是给
libA.a
中的 * all * 非静态符号加上一个唯一的前缀(例如用your_company_A_foo()
代替foo()
),这样就不太可能与其他库发生符号冲突。一种常见的方法是使用宏来定义函数,例如:
以上代码在编译时定义了
my_prefix_a_foo()
和my_prefix_a_bar()
:在本地测试/开发期间,如果需要,您甚至可以完全删除前缀。
这也使得发布
libA.a
的"版本2"变得容易,并保证旧的(版本1)libB.a
永远不会与新的libA.a
链接--只需更改前缀以在其中包含"v2"。问题是A是一个巨大的库(包括它自己版本的一些流行的Linux库)。
1.我希望您已经和您的律师谈过了--流行的Linux库往往是GPL所涵盖的,这可能被认为是一种重新分发,这将要求您采取措施遵守GPL。
1.您仍然可以重命名
libA.a
中的所有全局符号,使其具有唯一的前缀,只需使用objcopy --prefix-symbol=your_company_A
即可,而不必在源代码级别执行此操作。示例:
附言
lld -o myprogram main.o helper1.o -L. -lX -lA -lB
您 * 永远 * 不应该将任何用户级代码与
ld
链接(或lld
)--始终使用适当的编译器驱动程序(gcc
或g++
或clang
)。直接使用ld
的链接命令几乎从不正确(上面的链接行当然不是),当一些常见特性(如C++
析构函数或线程本地存储)神秘地不起作用时,您会发现这一点。