静态链接 到底做了什么?
模块间符号的引用,其实就是链接器的主要职责,即把各个模块之间相互引用的部分都处理好,使得各个模块之间能够正确地衔接,通俗一点来讲:
两个目标文件如何链接成一个可执行文件
。实质 上是将各个 目标文件 中的 地址,结合其他目标文件,包括了 运行时库(Runtime Library),确定其正确的地址,以下:任务一: 空间与地址分配(Address and Storage):合并-重组各个目标文件的段,以完成空间和地址的分配
共识:可执行文件中的代码段和数据段都是由输入的目标文件中合并而来的
【摒弃】 方案一:按序叠加:直接将各个目标文件中的相应段依次叠加到一起,缺点就是:内存空间大量碎片化。
方案二:相似段合并:1. 空间与地址分配:先扫描所有输入的目标文件,获取各个段的长度,属性,位置,将输入目标文件中的符号表中所有的符号定义和引用统一放到一个 全局符号表 中。2. 符号解析与重定位:根据第一部收集的所有信息,进行符号解析、重定位、调整代码中的地址(增加偏移量) 等
任务二: 符号解析与重定位(Resolution and Relocation)
重定位 :输入目标文件中,凡是引用了 外部符号,全用 0x0000000 这 4 个字节来代替,凡是引用了 外部函数,全用 0xFFFFFFFC 这四个字节来代替。链接器负责把目标文件被外部引用的函数符号的 偏移量,来 替换 这些初始代替量,链接写入输出 可执行文件 中
重定位表 :链接器用于识别哪些引用的符号或者函数需要重定位,使用
objdump -r a.o
来查看符号解析 :之所以链接是因为目标文件中用到的符号被定义在其他目标文件中,使用
objdump -s a.o
来查看符号表,其中 UND 的符号就是未定义类型,就是 待链接对象,即链接的过程经历了符号解析指令修正方式:由于不同处理器的指令千差万别,寻址的方式也不尽相同,因此在链接过程中替换目标文件地址,需要经历指令修正来确保地址有效性,其中 绝对寻址修正 后的地址为该符号的实际地址,相对寻址修成 后的地址为符号距离被修正位置的地址差
番外介绍:特殊模块:COMMON块
- 作用:用于链接时存放待链接的 多个命名相同的弱符号,作为储备池,因为链接器无法区分符号类型,故无法判断是否一致,因此链接器链接的选择原则是: 取占用空间最大的那个弱符号为准
任务三: 静态库链接(.a/.lib)
静态库可以简单的看成 一组目标文件的集合,用
ar
lib
等工具可以看静态库的内容静态库里面的一个目标文件只包含一个函数,避免链接函数过多导致程序臃肿,空间浪费
使用
gcc -static --verbose -fno-builtin hello.c
查看详细的链接过程,涉及到的目标文件非常多,过程也相当复杂
综上:链接的 过程控制
链接过程要确定的内容有:使用哪些目标文件?使用哪些库文件?是否在最终可执行文件中保留调试信息?输出文件格式是什么?是否导出符号表等
链接器支持 使用命令行参数控制、将链接指令存放在目标文件里、使用链接控制脚本 来完成以上内容
ld 链接器的链接脚本语法继承于 AT&T 链接器命令语言的语法,风格有点像 C 语言,分为 命令语句 和 赋值语句
BFD库 的出现,旨在解决不同软硬件平台上,通过一种 统一 的接口来处理不同的目标文件格式