深入理解计算机系统_第7章 链接

链接动作可以出现在程序的三个时期:
1) 链接可以执行于编译时,即在源代码被翻译成机器代码的时候;
2) 链接可以执行于加载的时候, 即程序从硬盘被加载到内存的过程中;
3) 链接可以执行于运行的时候, 即由应用程序来执行链接工作;

链接器的出现使得分离编译成为可能,将一个大型程序分解成各个模块,每一个模块单独编译。再将各个模块编译后的文件链接到一起,得到最终的二进制文件。分离编译的好处是,如果大型程序中某一个模块的源代码修改了, 只需要重新编译这部分代码和重新链接。未修改部分的代码不再需要重新编译。

7.1 编译器驱动程序

大多数编译系统提供编译器驱动程序,它代表用户在需要时候调用语言预处理器、编译器、汇编器和链接器;
源程序main.c 到可执行目标文件的过程:
1) 预处理器替换掉源程序中的字符,main.c --> main.i
2) 编译器将源程序翻译成汇编语言文件, main.i --> main.s
3) 汇编器将汇编语言翻译成一个可重定位目标文件, main.s --> main.o
4) 编译器驱动程序之后调用链接器, 将可重定位目标文件与一些必要的系统目标文件组合起来,创建一个可执行目标文件;
(注意区分可重定位目标文件和可执行目标文件)
之后,操作系统中的加载器将可执行目标文件加载到内存中,程序进入变成一个进程;

7.2 静态链接

静态链接器的输入:
一组可重定位目标文件+命令行参数
静态链接器输出:
一个完全链接到、可以加载和运行的可执行目标文件;

可重定位目标文件是由各种不同的代码和数据节组成的,每一个节都是一个连续的字节序列;
(代码节,全局变量数据节, 未初始化数据节,…)

链接器必须完成两个主要任务:
1) 符号解析
目标文件或是定义符号,或是引用了符号。每一个符号对应一个函数、一个全局变量、或是一个静态变量;
符号解析的目的是将每个符号引用正好和一个符号定义关联起来;
2) 重定位
汇编器和链接器生成从地址0开始的代码和数据节。连接器通过将每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,是这些引用指向这个内存位置。链接器使用汇编器产生的重定位条目的详细指令, 不加甄别地执行这样的重定位;

关于链接器的一些常识:
目标文件是纯粹的字节块的集合。(包含代码的块, 包含数据的块, 包含引导链接器和加载器的数据结构块);
链接器将这些块连接起来,确定被连接块的运行时位置, 并且修改代码和数据块中的各种位置。

7.3 目标文件

三种目标文件
1) 可重定位目标文件
二进制文件,包括代码块和数据块。多个可重定位目标文件可以合并,组合成可执行目标文件;
2) 可执行目标文件
可以被加载到内存中执行
3) 共享目标文件
一种特殊类型的可重定位目标文件。可以在程序加载到内存的时候,或是程序运行的时候被动态链接;

7.4 可重定位目标文件

在这里插入图片描述
在ELF头和节头部表之间的都是节:
1) .text 已经编译的机器代码
2) .rodata 只读数据。如字符串常量或是开关语句的跳转表
3) .data 已经初始化的全局变量或是初始化的静态c变量
4) .bss 未初始化的全局和静态c变量, 以及所有初始化为ie的全局或静态变量。 目标文件中,.bss 这个节不占有实际的空间,仅仅只是一个占位符。当加载到内存空间的时候才真正分配具体的空间;
5) .symtab 一个符号表,存放在程序中定义和引用的函数的和全局变量的信息。与编译器中的符号表不一样, 可执行文件中的符号表不包含局部变量的条目;(因为局部变量在栈中,不是链接器的对象);
6) .rel.text 一个.text节中位置的列表, 重定位信息表。当链接器把可执行目标文件和其他文件组合的时候, 需要修改这些位置。 一般而言, 任何调用外部函数或者引用全局变量的指令都需要修改这个节;调用本地函数的指令不需要修改;
7) .rel.data 被模块引用或定义的所有全局变量的重定位信息。任何已初始化的全局变量,如果他的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改;

7.5 符号和符号表

每一个可重定位目标模块都有一个符号表,包含有m中定义或是引用的符号的信息。链接器上下文中, 有三种不同的符号:
1) 由模块m定义并能被其他模块引用的全局符号。全局链接器符号对应非静态的C函数和全局变量;
2) 在其他模块中定义但是被模块m引用的全局符号。即外部符号,对应于在其他文件中定义的非静态函数和全局变量;
3) 只被模块m定义和引用的局部符号。对应于带static属性的C函数和static全局变量。这些符号在m模块中都可见,但是不能被其他模块引用;
C程序员使用static属性隐藏模块内部的变量和函数声明,使其只在本文件中可见;
符号表中包含一个条目的数组,每个条目的数据格式如下

typedef struct{
   
	int name;
	char type:4,
		binding:4;
	char reserved;
	short section;
	long value;
	long size;
}Elf64_Synbol;

value:是距定义目标的节的起始位置的偏移。对于可执行文件而言, value是一个绝对运行时地址。
size:是目标的大小;
type:表示是数据还是函数;
binding:表示是本地的还是全局的;
section:每个符号被分配到目标文件中的某个节,由section字段表示, 该字段也是一个到节头部表的索引(即根据section可以得到一个节头部表);
有三个特殊的伪节:
1) ABS, 代表不该被重定位的符号;
2) UNDEF, 代表未定义的符号,在本目标模块引用但是定义在其他文件中的;
3) COMMON, 表示还没有被分配位置的未初始化的数据目标;
只有在可重定位目标文件中才有这些伪节,可执行目标文件中没有这些;

COMMON和.bss的区别:
1) COMMON:未初始化的全局变量
2) .bss未初始化的静态变量以及初始化为0 的全局或静态变量;

7.6 符号解析

链接器解析符号引用的方法是将每个引用与输入链接器中的可重定位目标文件中的符号表中的一个确定的符号定义相关联起来;
对于引用和定义都在同一个文件中的局部符号的引用, 符号解析很简单&

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值