一、linux上静态库和动态库的编译和使用
1.1 特点
动态库特点:
1、库的代码不会编译进程序里面,所以动态库编译的程序比较小。
2、由动态库编译的程序依赖于系统的环境变量有没有这个库文件,没有则运行不了。
静态库特点:
1、库的代码会编译进程序里面,所以静态库编译的程序比较大
2、由静态库编译的程序不用依赖于系统的环境变量,所以环境变量有没有这个库文件,也可以运行。
参考地址
https://www.cnblogs.com/Harley-Quinn/p/6360363.html
https://blog.csdn.net/nanfeibuyi/article/details/81203021
二、调试
查看linux静态库*.a中或者动态库中的函数和文件,这对某些时候是非常有用的。
2.1 nm
1、可以通过shell脚本去遍历查看动态库中链接的符号。
nm -g --defined-only libxxx.a
2、可以和 grep 命令 配合使用。
nm * ·|grep 函数名称。
例如:nm -A /usr/lib/* 2>/dev/null | grep "T memset"
在 /usr/lib/ 目录下找出哪个库文件定义了memset函数.
参考地址:https://www.cnblogs.com/bonelee/p/6524322.html
2.2 objdump
objdump是gcc工具,用来查看编译后目标文件的组成。
我们在 Linux 下运行一个程序,有时会无法启动,报缺少某某库。这时需要查看可执行程序或者动态库中的符号表,动态库的依赖项, Linux 有现成的工具可用:objdump 。
例如:objdump -x xxx.so | grep "NEEDED"
参考地址:https://blog.csdn.net/tao546377318/article/details/51727696
2.3 其他技巧
可以通过命令ar查看静态库编译了那些.o文件
ar t libxxx.a
ldd命令可以查询程序运行时需要的依赖库
三、库的依赖问题
为什么会有库的依赖问题?
一、静态库解析符号引用:
链接器ld是如何使用静态库来解析引用的。在符号解析阶段,链接器从左至右,依次扫描可重定位目标文件(*.o)和静态库(*.a)。
在这个过程中,链接器将维持三个集合:
集合E:可重定位目标文件(*.o文件)的集合。
集合U:未解析(未定义)的符号集,即符号表中UNDEF的符号。
集合D: 已定义的符号集。
初始情况下,E、U、D均为空。
1、对于每个输入文件f,如果是目标文件(.o),则将f加入E,并用f中的符号表修改U、D(在文件f中定义实现的符号是D,在f中引用的符号是U),然后继续下个文件。
2、如果f是一个静态库(.a),那么链接器将尝试匹配U中未解析符号与静态库成员(静态库的成员就是.o文件)定义的符号。如果静态库中某个成员m(某个.o文件)定义了一个符号来解析U中引用,那么将m加入E中,
同时使用m的符号表,来更新U、D。对静态库中所有成员目标文件反复进行该过程,直至U和D不再发生变化。此时,静态库f中任何不包含在E中的成员目标文件都将丢弃,链接器将继续下一个文件。
3、当所有输入文件完成后,如果U非空,链接器则会报错,否则合并和重定位E中目标文件,构建出可执行文件。
到这里,为什么会有库的依赖问题已经得到解答:
因为libchild.a依赖于libbase.a,但是libbase.a在libchild.a的左边,导致libbase.a中的目标文件(*.o)根本就没有被加载到E中,所以解决方法就是交换两者的顺序。当然也可以使用-lbase -lchild -lbase的方法。