Linux静态链接简介
什么是静态编译?
静态编译就是编译器在编译可执行文件的时候,将可执行文件需要调用的对应动态链接库(.so)中的部分提取出来,链接到可执行文件中去,使可执行文件在运行的时候不依赖于动态链接库。
这样做的优点是编译成功的可执行文件可以独立执行,而不需要再向外部要求读取函数库的内容。
缺点是虽然可执行文件可以独立执行,但因为函数库是直接整合到可执行文件中,因此若函数库升级时,整个可执行文件必须要重新编译才能将新版的函数库整合到程序中。
连接器如何使用静态库来解析引用
在符号解析阶段,连接器从左到右按照它们在编译器驱动程序命令行上出现的顺序来扫描可重定位目标文件和存档文件(驱动程序自动将命令行中所有的.c文件翻译为.o文件)。在这次扫描中,链接器维护一个可重定位目标文件的集合E(这个集合中的文件会被合并起来形成可执行文件),一个未解析的符号(即引用了但是尚未定义的符号)集合U,以及一个在前面输入文件中已定义的符号集合D。初始时,E、U和D均为空。
- 对于命令行上的每个输入文件f,链接器会判断f是一个目标文件还是一个库文件。如果f是一个目标文件,那么链接器把f添加到E,修改U和D来反映f中的符号定义和引用,并继续下一个输入文件。
- 如果f是一个库文件,那么链接器就尝试匹配U中未解析的符号和由库文件成员定义的符号。如果某个库文件成员m,定义了一个符号来解析U中的一个引用,那么就将m加到E中,并且链接器修改U和D来反映m中的符号定义和引用。对库文件中所有成员,目标文件都一次进行这个过程,直到U和D都不再变化。此时,任何不包含在E中的成员目标文件都简单地被丢弃,而链接器将继续处理下一个输入文件。
- 如果当链接器完成对命令行上输入文件的扫描后,U是非空的,那么链接器就会输出一个错误并终止。否则,它会合并和重定位E中的目标文件,构建输出可执行文件。
编译方法
使用gcc选项-static可以禁止使用动态链接库,即使用静态链接的方式。
示例代码:
#include<stdio.h>
int main(){
printf("hello world.\n");
return 0;
}
编译命令:
gcc -static -o test test.c
编译出错:cannot find -lc
编译此程序需要用到libc库文件,而系统中无法找到libc的静态库文件,需要下载安装:
yum install glibc-static
再次编译,成功!
使用动态链接和静态链接编译的程序大小对比
使用动态链接:
使用静态链接: