静态链接和动态链接最大的区别就在于链接的时机不一样,静态链接是在生车可执行程序前,而动态链接的进行则是在程序执行时,而且静态链接库的位置不同将会影响二进制文件生成。
链接顺序
在使用静态库的时候,经常会遇到因为连接顺序不同导致出现 undefined reference
的报错,可以通过 nm libxx.a | grep xxx
命令查看是否存在对应的符号。注意,对于 C++ 来说,为了支持函数重载,编译器就引入了 name mangling
机制,原名就需要通过 c++filt
转换。
如果仍然存在,那么就可能是因为链接顺序导致的问题,如下是一个简单的示例。
----- 只进行编译,会分别生成add.o和main.o文件
# gcc -c add.c
# gcc -c main.c
----- 然后打包成一个静态库
# ar -rc math.a add.o
在生成最终二进制文件时就会涉及到了链接过程中的顺序问题。
----- 这里会报add未定义的错误
# gcc -o main math.a main.o
/usr/bin/ld: main.o: in function `main':
main.c:(.text+0xf): undefined reference to `add'
collect2: error: ld returned 1 exit status
----- 这里可以编译成功
# gcc -o main main.o math.a
也就是说,正常越基础的库越应该放在右边。
基本原理
静态库实际上包含了所有的编译后的 *.o
文件,链接器从左至右搜索的同时维护了一个 undefined
列表,一旦遇到没有定义的内容,就会将它加到列表中,如果搜索到了定义的内容,则抽取出进行链接,并将其从 undefined
中移除,为了减少目标的体积,其它对象文件会被删除。
这样就意味着,如果一个静态库在搜索过程中不需要,那么就会被丢弃,而后续一旦遇到了依赖的库就会报未定义的错误。
其它
链接参数
但是可以通过 --start-group
和 --end-group
取消依赖顺序,会在该范围内循环查找未定义的符号,直到不再存在未定义,这样就可以规避掉依赖关系,但是会导致最终链接时间增加。
----- 添加链接选项同样可以编译成功
# gcc -o main -Wl,--start-group math.a main.o -Wl,--end-group
CMake
还有就是 CMake 会自动在链接过程中解决依赖,简单来说就是通过 target_link_libraries()
指定依赖以及参数是,其依赖库对应的依赖会添加到真正链接时的末尾,详细可以参考最终生成的 link.txt
文件,该文件可以直接修改。
所以,如果使用的 start-group
参数,同时有相关的依赖,那么就可能会导致莫名其妙的 undefined reference
报错。