You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

生成共享库时独立目标文件与静态归档文件为何处理不同?

问题

我阅读了一篇关于GCC符号导出控制的文章,其中「静态库被共享库使用的问题」部分引发了我的兴趣。我做了额外实验,但有个点没搞明白:

  1. 先创建简易静态库:
g++ util.cpp -o util.o -c -fPIC
ar r libutil.a util.o
  1. 创建独立目标文件:
g++ code.cpp -o code.o -c -fPIC
  1. 结合两者生成共享库,此时导出表符合预期:
g++ code.o libutil.a -shared -o libcode.so
$ nm -CD libcode.so | grep " T "
000000000000124c T _fini
0000000000001000 T _init
0000000000001167 T entry_point()
00000000000011d1 T util_function()
0000000000001145 T function1()

但如果只使用静态归档文件生成共享库:

g++ libutil.a -shared -o libcode.so

导出表完全为空(仅剩下_init/_fini):

$ nm -CD libcode.so | grep " T "
00000000000010f8 T _fini
0000000000001000 T _init

只有添加-Wl,--whole-archive选项才能得到预期结果:

g++ -Wl,--whole-archive libutil.a -Wl,--no-whole-archive -shared -o libcode.so

此时归档文件中的符号正常导出:

$ nm -CD libcode.so | grep " T "
00000000000011a0 T _fini
0000000000001000 T _init
0000000000001125 T util_function()

静态归档文件本质是独立目标文件的集合,为什么生成共享库时两者的处理方式不同?为什么单独用静态归档生成共享库必须加-Wl,--whole-archive选项?

解答

这是因为链接器处理静态库的逻辑和单独目标文件完全不同:

  • 对于单独的目标文件(比如code.o),链接器会无条件把所有内容都包含进最终的共享库中。
  • 但对于静态归档文件(.a),链接器的默认行为是「按需引入」:它只会扫描归档里的目标文件,把那些能解决当前未定义符号引用的文件拉进产物里。

回到你的实验:

  • 当你把code.o和libutil.a一起编译时,code.o里的代码肯定引用了util_function之类的符号,链接器发现有未定义的符号需要解决,就会从libutil.a里找出对应的util.o,把它包含进共享库,自然这些符号就被导出了。
  • 但当你只编译libutil.a时,链接器一开始没有任何未定义符号需要解析,所以它不会从归档里取出任何目标文件。最终的共享库就只有链接器自动生成的_init和_fini,没有任何来自util.o的内容,自然导出表是空的。

-Wl,--whole-archive选项的作用就是强制链接器把归档文件里的所有目标文件都无条件包含进去,不管有没有符号引用它。这样util.o的内容就会被加入共享库,对应的符号也就正常导出了。


内容的提问来源于stack exchange,提问作者DaveC

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 23:20:36