深入理解计算机系统-链接

链接

什么是链接?
链接是将各种代码和数据部分收集起来并且组合成一个单一文件的过程,这个文件可以被加载到存储器并执行。链接要分为以下四个步骤:
一.源文件到目标文件的转化过程
整个编译的过程是:预处理,编译,汇编,链接。链接后生成可执行的目标文件,汇编后生成的目标文件被链接器使用,结合成可执行的目标文件。

  1. 预处理阶段:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。结果就得到另外一个C程序,通常是以.i作为文件扩展名。即.c文件转化为.i文件的过程。

  2. 编译阶段:编译器(ccl)将文本文件.i翻译成文本文件.s,它包含一个汇编语言程序。即.i文件转化为.s文件的过程。

  3. 汇编阶段:汇编器(as)将.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序 的格式,并将结果保存在目标文件.o中,.o文件是一个二进制文件。即.s文件转化为.o文件的过程。

  4. 链接阶段:链接器(ld)将.o文件及一些必要的系统目标文件组合起来,创建一个可执行目标文件 ,可以被加载到内存中,由系统执行。
    在这里插入图片描述

二.目标文件

目标文件有三种形式

1.可重定位的目标文件:包含二进制代码和数据,其形式可以再编译时与其他可重定位的目标文件合并起来,创建一个可执行目标文件。通常对一个程序模块进行汇编以后,生成的就是可重定位的目标文件。
2.可执行目标文件:包含二进制代码和数据,可以直接拷贝到存储器并执行。
3.共享目标文件:可以再加载或者运行时被动态的加载到存储器并链接。
总结一下其实就是编译器和汇编器一起生成可重定位的目标文件,链接器生成可执行目标文件。从技术上说一个目标模块就是一个字节序列,一个目标文件就是存放在磁盘上的目标模块。
在这里插入图片描述

上图为典型的ELF可重定位目标文件,夹在ELF头和节头部表之间的都是节,一个典型的ELF可重定位目标文件包含下面几个节:

  • .text:已编译程序的机器代码。

  • .rodata:只读数据,比如printf语句中的格式串和开关语句的跳转表。

  • .data:已初始化的全局和静态C变量。局部C变量在运行时被保存在栈中,既不出现在.data节中,也不出现在.bss节中。

  • .bss:未初始化的全局和静态C变量,以及所有被初始化为0的全局或静态变量。

  • .symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。

  • .rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时,需要修改这些位置。

  • .rel.data:被模块引用或定义的所有全局变量的重定位信息。

  • .debug:一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。

  • .line:原始C源程序中的行号和.text节中机器指令之间的映射。只有以-g选项调用编译器驱动程序时,才会得到这张表。

  • .strtab:一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串系列。

三.链接器的主要任务

在这里插入图片描述

**

1. .符号解析**

目标文件定义和引用符号,每个符号对应于一个函数、一个全局变量或一个静态变量(即C语言中任何以static属性声明的变量)。符号解析的目的是将每个符号引用 正好和一个符号定义 关联起来。
(1)符号和符号表
每个可重定位目标模块m都有一个符号表,它包含m定义和引用的符号信息。在链接器的上下文中,有三种不同的符号:
由模块m定义并能被其他模块引用的全局符号。 全局链接器符号对应于非静态C函数 和全局变量。
由其他模块定义并被模块m引用的全局符号。 其称为外部符号,对应于其他模块中定义的非静态C函数和全局变量。
只被模块m定义和引用的局部符号。 对应于带static属性的C函数和全局变量,这些符号在模块m中的任何位置都可见,但不能被其他模块引用。
(2)全局符号的强弱性
强符号:函数名和已初始化的全局变量名。

弱符号:未初始化的全局变量名。

(3)链接器对符号解析的规则
Rule1:不允许有多个同名的强符号
强符号只能被定义一次,否则链接错误。
Rule2:如果有一个强符号和多个弱符号同名,那么选择强符号。
对弱符号的引用被解析为其强符号的定义。
Rule3: 如果有多个弱符号同名,那么从这些弱符号中任意选择一个。

使用gcc -fno-common链接时,会告诉链接器在遇到多个弱定义的全局符号时输出一条警告信息。
(4)多重定义全局符号的问题
尽量避免使用全局变量
一定需要用的话,请按以下规则使用
尽量使用本地变量(static)
全局变量要赋初值
外部全局变量要使用extern

2. 重定位

编译器和汇编器生成从地址0开始的代码和数据节。 链接器通过把每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使它们指向这个内存位置。链接器使用汇编器产生的重定位条目的详细指令,不加甄别地执行这样地重定位。

重定位的过程

合并相同的节
– 将集合E的所有目标模块中相同的节合并成新节,例如所有.text节合并作为可执行文件中的.text节
对定义符号进行重定位(确定地址)
– 确定新节中所有定义符号在虚拟地址空间中的地址
– 完成这一步后,每条指令和每个全局或局部变量都可确定地址
对引用符号进行重定位(确定地址)
– 修改.text和.data节中对每个符号的引用(地址),需要用到在.rel.data和.rel.text节中保存的重定位信息。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值