4.1 空间与地址分配
- 问题:当有多个目标文件时,是如何合并到一个文件中的(生成可执行文件),是在哪个位置插入相关代码的?以什么规则进行插入?
4.1.1 按序叠加
- 最简单的方式就是按序叠加,这样做非常浪费空间,因为每个段都需要地址和空间对齐,并且按章class 1,class 2,顺序添加,出现.text段重复,导致内存空间中大量碎片
4.1.2 相似段合并
- 将.text段合并
- 链接器一般都采用两步链接:
1.空间与地址分配:扫描所有的输入目标文件,并且获得他们的各个段的长度,属性和位置,并且将输入目标文件中的符号表中所有的符号定义和符号引用收集起来,同意放到一个全局符号表中,这一步链接器能获取所有目标文件的段长度,并且将它们合并,计算出输出文件中各个段合并后的长度与位置,并建立映射关系
2.符号解析与重定位:使用第一步收集到的信息,读取文件中段内容数据、重定位信息,并且进行符号解析与重定位、调整代码中的地址等,事实上第二步是链接过程的核心,特别是重定位过程
4.2 符号解析与重定位
4.2.1 重定位
- 代码里面使用的地址都是虚拟地址
4.2.2 重定位表
- 链接器需要进行重定位,哪些指令需要重定位,怎么重定位,需要依赖重定位表,他在ELF文件中是一个或多个段
,有时候也叫重定位段 - 通俗的理解a.o文件引用外部的符号,都需要依赖重定位表进行重定位
4.2.3 符号解析
- 重定位过程伴随着符号解析过程,当链接器需要对某个符号进行重定位时,它就要确定这个符号的目标地址
4.3 COMMON块
- 链接器本身并不支持符号的类型,变量类型对i 链接器来说是透明的,它只知道一个符号的名字,并不知道类型是否一致,所以会出现多个强符号相同报错的现象
4.4 C++相关问题
- C++一些语言特性必须由编译器和链接器共同支持才能完成
4.4.1 重复代码消除
- C++编译期提供了一个编译选项叫函数级别链接,这个选项的作用是所有函数都单独保存在一个段里面,当链接器需要用到某个函数时候,就将它合并到输出文件中,对那些没有用到的函数则抛弃掉,好处是减少输出文件的大小,坏处是编译和链接过程会变慢,编译器会计算各个函数的依赖关系
4.4.2 全局构造与析构
- 一般的C/C++程序是从main开始,main函数结束而结束
- 但是在main函数调用之前,为了让程序顺利执行,要初始化进程执行环境
4.4.3 C++与ABI
- ABI:采用同样的目标文件格式、拥有同样的符号修饰标准、变量的内存分布方式相同、函数的调用方式相同,等等。其中我们把符号修饰标准、变量内存布局、函数调用方式等这些跟可执行代码二进制兼容性相关的内容叫ABI
- API往往是源代码级别的接口,ABI往往是二进制层面的接口
4.5 静态库链接
- 静态库可以简单的看成一组目标文件的集合
4.6 链接过程控制
4.6.2 最小的程序
todo