《链接、装载与库》——静态链接

ELF文件格式

ELF是Linux下的文件格式,可分为:

  • 可执行文件(bin);
  • 可重定位文件(.o, .a);
  • 共享目标文件(.so);
  • 核心转储文件(coredump);

ELF文件结构:ELF文件头,各种section(.text, .data, .bss等),各种表(section表、symbol表、string表等)。文件头里存放了各种元数据,比如ELF类型、入口地址、表偏移、段数量等;各种section即为代码和数据;section表存放的是各section的信息,比如类型、名字、偏移、大小等;string表里存放着ELF中的字符串;rel表和symbol表是链接中很重要的结构,因为链接本质就是”填空”,在哪里填空,怎么填空,都依赖于这两张表。

符号相关

在链接中,函数和变量统称为符号,因此可知,每个目标文件都可以定义符号或引用符号。从链接角度来看,符号也有很多类型,局部符号(static)、全局符号(全局变量、函数)、外部符号(引用的外部符号)、特殊符号(ld根据链接脚本添加)。关于名字,符号名通常与变量、函数名不一致,这称为符号修饰,这也是extern “C”的原因。

强符号和弱符号:默认的,函数和初始化全局变量为强符号,未初始化全局变量为弱符号,规则如下:

  • 强符号不能重复多次定义,否则链接报错;
  • 一个强符号和多个弱符号,链接器选择强符号;
  • 都是弱符号,选择占用空间最大的那个;

强引用和弱引用:链接时的外部符号是必须要被重定位的,如果无法正确决议,链接器会报未定义错误,这是强引用,如果不报错则是弱引用。

弱符号和弱引用的作用:弱符号可以被用户定义的强符号覆盖,使得程序可以自定义某些功能;弱引用使得程序即使缺少某些模块也能链接,提高程序的组合性。

静态链接

预编译->编译(词法分析、语法分析、语义分析、中间代码生成、目标代码生成和优化)->汇编->静态链接:

  • 空间与地址分配:即虚拟地址空间分配,扫描所有输入目标文件,得到每个section的长度与属性,并收集所有symbol放到全局symbol表中,然后合并性质相同的section并分配虚拟空间地址,填入全局symbol表中;
  • 符号解析和重定位:根据读入的重定位表,得到重定位入口,进行符号解析,也就是在全局symbol表中查找相应的符号,得到符号地址后进行指令修正(重定位),修改代码中对外部符号的引用处;

静态库链接

静态库可以看成是一组目标文件的集合,可以用ar打包或解压静态库,链接过程则和静态链接基本一致。

链接过程控制

链接是个很复杂的过程,不过,可以用命令行参数、目标文件或链接控制脚本对链接过程进行控制,脚本语法在此不再赘述。

ABI兼容性

ABI是二进制层面的接口,这比API要更严格一些,API仅仅是源码级别的兼容,而ABI则要求内存模型、符号修饰标准、函数调用方式、寄存器使用约定等都要一致。