静态库、动态库------深入理解计算机系统
相关的函数可以被编译为独立的目标模块(.o),然后封装成一个单独的静态库文件(.a)。在链接时,链接器将只复制被程序应用的目标模块。eg
//addvec.c 函数实现 void addvec(){} //multvec.c 函数实现 void multvec(){}
创建这些函数的一个静态库,将用AR工具如下
gcc -c addvec.c multvec.c //将生成 addvec.o multvec.o -c表示只编译(compile)源文件但不链接 ar rcs libvector.a addvec.o multvec.o
为了使用这个库,我们可以编写一个应用,它调用addvec库例程。头文件 vector.h定义了libvector.a中例程的函数原型。
//main2.c #include"vector.h" int main(){ addvec();return 0;}
为了创建这个可执行文件,我们要编译和链接输入文件main.o和libvector.a:
gcc -c main2.c gcc -static -o prog2c main2.o ./libvector.a 等价于使用 gcc -c main2.c gcc -static -o prog2c main2.o -L. -lvector
-static参数告诉编译器,链接器应该构建一个完全链接的可执行目标文件,它可以加载到内存并运行,在加载时无需更进一步的链接。-lvector参数时libvector.a的缩写,-L. 参数告诉链接器在当前目录下查找lib-vector.a。
当链接器运行时,它判定main2.o引用了addvec.o定义的addvec符号,所以复制addvec.o到可执行文件。因为程序不引用任何由multvec.o定义的符号,所以链接器就不会复制这个模块到可执行文件。链接器还有复制许多c运行时系统中的其他模块。
在linux中,静态库以一种称为存档的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置。存档文件名可由后缀.a标识。
在符号解析阶段,链接器从左到右按照它们在编译器命令行上出现的顺序来扫描可重定位目标文件和存档文件。(编译器自动将命令行中所有的.c编译成.o)
命令行上的库和目标文件的顺序非常重要。在命令行中,如果定义一个符号的库出现在引用这个符号的目标文件之前,那么引用就不能被解析,链接会失败。
如果有重复依赖可以
gcc foo.c libx.a libz.a libx.a
另一种方法时,将libx.a和liby.a合并成一个单独的存档文件。
存档文件
由于动态库是共享的,所以在装载时再进行重定位就行不通了。解决方法:在编译共享库时,用一个特殊的编译选项告知编译器,不要产生使用绝对地址的指令。相反只能产生使用相对地址的指令。只使用相对偏移量的代码被称为位置无关代码