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

目录

一.源文件到目标文件的转化过程

二.目标文件

三.链接器的主要任务

四.例题解析

一.源文件到目标文件的转化过程
(1)过程

预处理—编译—汇编—链接

(2)图示及解析
  • 图示如下

预处理阶段:预处理器(cpp)根据以字符#开头的命令,修改原始的C程序。结果就得到另外一个C程序,通常是以.i作为文件扩展名。即.c文件转化为.i文件的过程。
编译阶段:编译器(ccl)将文本文件.i翻译成文本文件.s,它包含一个汇编语言程序。即.i文件转化为.s文件的过程。
汇编阶段:汇编器(as)将.s翻译成机器语言指令,把这些指令打包成一种叫做可重定位目标程序 的格式,并将结果保存在目标文件.o中,.o文件是一个二进制文件。即.s文件转化为.o文件的过程。
链接阶段:链接器(ld)将.o文件及一些必要的系统目标文件组合起来,创建一个可执行目标文件 ,可以被加载到内存中,由系统执行。

二.目标文件
1.目标文件的三种形式
  • 可重定位目标文件。包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。
  • 可执行目标文件。包含二进制代码和数据,其形式可以被直接复制到内存并执行。
  • 共享目标文件。一种特殊类型的可重定位目标文件,可以在加载或者运行时被动态地加载进内存并链接。
  • 编译器和汇编器生成可重定位目标文件(包括共享目标文件)。
  • 链接器生成可执行目标文件。
2.可重定位目标文件

在这里插入图片描述

  • 上图为典型的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节中保存的重定位信息。
四.例题解析

1.

解析选B。xp是符号的定义,x是符号的引用。该题主要是考查符号的定义和引用。

在这里插入图片描述

解析选A。该题中变量x在m1.c中为强符号,在m2.c中为弱符号。在调用p1函数后,x处原来存放的100被替换,-1.0的double类型表示为1 0111 1111 111 00…0,十六进制表示为BFF0 0000 0000 0000。因为x、y和z都是初始化变量,同在.data节中,链接后空间被分配在一起,x占4B,随后y和z各占2B。因为IA-32为小端方式,所以,x的机器数为全0,y的机器数也为全0,z的机器数为BFF0H。执行printf函数后x=0, z=-(214+24)=-16400。

在这里插入图片描述

解析选D。重定位最后一步是对引用处的地址进行重定位,重定位的方式有多种,只有绝对地址方式才是将引用处的地址修改为与之关联(绑定)的定义处的首地址,而对于其他重定位方式,就不一定是这样,例如,对于PC相对地址方式,引用处填写的是一个相对地址。

在这里插入图片描述

解析选D。A、因为“int *bufp1 = &buf[1];”是一个声明,也即是对变量bufp1的数据类型的定义和初始化,因此这个需要重定位的初始化值将被存储在.date节中,因而重定位条目在.rel.data节中,并且是绑定buf的一个引用,即引用buf的一个重定位条目。 B、因为buf有2个数组元素,每个元素占4B,因此bufp1的地址为0x8048930+8=0x8048938,重定位时与引用绑定的符号是buf,即绑定的是&buf[0],而真正赋给bufp1的是&buf[1],引用的地址和绑定的地址相差4,所以重定位前的内容为十六进制数04 00 00 00。 C、可执行文件已经进行了重定位,所以,bufp1所在的地址0x8048938处,应该是重定位后的值,显然应该是buf[1]的地址。重定位时通过初始值加上buf的值得到,即4+0x8048930=0x8048934,小端方式下,4个字节分别为34H、89H、04H、08H。 D、在重定位条目中只有对buf的引用,没有对bufp1的引用,这里bufp1是一个定义。

注:此文章参考书目《深入理解计算机系统》和南京大学袁春风老师的网课而写,图片和习题均来自袁老师的网课PPT和课后练习。此文主要为了整合《深入理解计算机系统》第7章链接的知识点,为方便自己日后较系统地复习知识点,也希望能对需要的人有帮助。如有出入的地方,还请大家多指教!
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值