之前在工作种有遇到静态库链接符号查找的问题,今天查查资料总结一下。
image.png
# rm -rf main.o a1.o a2.o b1.o liba.a libb.a a.out
# g++ -c a1.cpp
# g++ -c a2.cpp
# g++ -c b1.cpp
# g++ -c main.cpp
# ar cqs liba.a a1.o a2.o
# ar cqs libb.a b1.o
# g++ main.o liba.a libb.a
libb.a(b1.o): In function `FuncB1()':
b1.cpp:(.text+0x5): undefined reference to `FuncA1()'
collect2: error: ld returned 1 exit status
image.png
静态链接的算法
链接器在链接库文件(.a)的时候,不是把库文件看做一个整体,而是将打包在其中的目标文件(.o)作为链接单元。在整个连接过程中,如果某个目标文件中的符号被用到了,那么这个目标文件会单独从库文件中提取出来,而不会把整个库文件链接进来。
然后,链接器在工作过程中,维护3个集合:需要参与链接的目标文件集合、一个未解析符号集合
、一个在E中所有目标文件定义过的所有符号集合
。
以上面 g++ -o a.out main.o liba.a libb.a
为例:
当输入 main.o 后,由于 main 调用了 a2.o 和 b1.o 中的函数,而此时并没有在中找到该符号,于是将引用的两个函数保存在
中。
E | U | D |
---|---|---|
main.o | FuncA2,FuncB1 |
# nm -C main.o
...
0000000000000000 T main
U FuncA2()
U FuncB1()
...
接下来,输入liba.a,链接器发现,FuncA2 存在于 liba.a 的 a2.o 中,于是将 a2.o 加入到 ,并在
中加入 a2.o 中所有定义的符号,其中包括 FuncA2 ,最后移除
中的 FuncA2 ,因为这个符号已经在 a2.o 中找到了的。
E | U | D |
---|---|---|
main.o,a2.o | FuncB1 | FuncA2 |
# nm -C liba.a
a1.o
...
0000000000000000 T FuncA1()
...
a2.o
...
0000000000000000 T FuncA2()
...
接着,输入libb.a,同理,链接器发现 FuncB1 定义在b1.o中,所以在中加入 b1.o,移除
中的 FuncB1,在 D 中加入 b1.o 里面所有定义的符号,其中包括FuncB1,也包括定义在其他模块的FuncA1。
E | U | D |
---|---|---|
main.o,a2.o,b1.o | FuncA1 | FuncA2,FuncB1 |
# nm -C libb.a
...
0000000000000000 T FuncB1()
U FuncA1()
...
至此,输入结束了,但是链接器发现中还有未解析的符号,所以报错。
所以解决办法:仔细分析依赖关系,并按照正确的顺序书写库文件的引用。上面使用 g++ -o a.out main.o libb.a liba.a