《深入理解计算机系统》 Chapter 7 读书笔记
《深入理解计算机系统》Chapter 7 读书笔记
链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(货被拷贝)到存储器并执行。
链接的时机
- 编译时,也就是在源代码被翻译成机器代码时
- 加载时,也就是在程序被加载器加载到存储器并执行时
- 运行时,由应用程序执行
链接器使分离编译称为可能。
一、编译器驱动程序
大部分编译系统提供编译驱动程序:代表用户在需要时调用语言预处理器、编译器、汇编器和链接器。
1.将示例程序从ASCⅡ码源文件翻译成可执行目标文件的步骤
()运行C预处理器:源程序main.c->ASCII码中间文件main.i ()运行C编译器:main.i->ASCII码汇编语言文件main.s ()运行汇编器:main.s->可重定位目标文件
二、静态链接
静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件由各种不同的代码和数据节(section)组成。指令在一个节中,初始化的全局变量在另一个节中,而未初始化的变量又在另外一个节中。
为了构造可执行文件,链接器必须完成两个任务:符号解析,重定位三、目标文件
1.三种形式
- 可重定位目标文件。包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。
- 可执行目标文件。包含二进制代码和数据,其形式可以被直接拷贝到存储器并执行。
- 共享目标文件。一种特殊类型的可重定位目标文件,可以在加载或者运行地被动态地加载到存储器并链接。
四、可重定位目标文件
1.一个典型的ELF可重定位目标文件的格式:

- .text:已编译程序的机器代码。
- .rodata:只读数据,比如printf语句中的格式串和开关语句的跳转表。
- .data:已初始化的全局C变量。
- .bss:未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。
- .symtab:一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。
- .rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件结合时,需要修改这些位置。
- .rel.data:被模块引用或定义的任何全局变量的重定位信息。
- .debug:一个调试符号表,其条目是程序中定义的局部变量和类型定义,程序中定义和引用的全局变量,以及原始的C源文件。只有以-g选项调用编译驱动程序时才会得到这张表。
- .line:原始C源程序中的行号和.text节中机器指令之间的映射。
- .strtab:一个字符串表,其内容包括:.symtab和.debug节中的符号表,以及节头部中的节名字。字符串表就是以null结尾的字符串序列。
五、符号和符号表
每个可重定位目标模块m都有一个符号表,包含m所定义和引用的符号的信息。在链接器的上下文中,三种不同的符号:1.由m定义并能被其他模块引用的全局符号。全局链接器对应于非静态的C函数以及被定义为Cstatic 属性的全局变量。2.由其他模块定义并被模块m以引用的全局符号——外部符号,对应于定义在其他模块中的C函数和变量3.只被模块m定义和引用的本地符号。六、符号解析
1.链接器如何解析多重定义的全局符号
在编译时,编译器向汇编器输出每个全局符号,或者是强或者是弱,而汇编器把这个信息隐含地编码在可重定位目标文件的符号表里。
函数和已初始化的全局变量时强符号;
未初始化的全局变量是弱符号。
根据强弱符号的定义,Unix链接器使用下面的规则来处理多重定义的符号:
规则1:不允许有多个强符号。
规则2:如果有一个强符号和多个弱符号,那么选择强符号。
规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个。
2.与静态库链接
所有的编译系统都提供一种机制,将所有相关的目标模块打包成为一个单独的文件,称为静态库。
3.链接器如何使用静态库来解析引用
- 对于命令行上的每个输入文件f,链接器会判断f是一个目标文件还是一个存档文件。如果f是一个目标文件,那么链接器吧f添加到E, 修改U和D来反映f中的符号定义和引用,并继续下一个输入文件。
- 如果f是一个存档文件,那么链接器就尝试匹配U中未解析的符号和由存档文件成员定义的符号。如果某个存档文件成员m,定义了一个符号来解析U中的一个引用,那么就将m加到E中,并且链接器修改U和D来反映m中的符号定义和引用。对存档文件中所有的成员目标文件都反复进行这个过程,直到U和D都不再发生变化。在此时,任何不包含在E中的目标文件都简单地被丢弃,而链接器将继续处理下一个输入文件。
- 如果当链接器完成对命令行上输入文件的扫描后,U是非空的,那么链接器就好输出一个错误并终止。否则,它会合并和重定位E中的目标文件,从而构建输出的可执行文件。
七、重定位
一旦链接器完成了符号解析这一步,它就是把代码中的每个符号引用和确定的一个符号定义(即它的一个输入目标模块中的一个符号表条目)联系起来。
重定位由两步组成:
- 重定位节和符号定义。在这一步中,链接器将所有相同类型的节合并为同一类型的新的聚合节。然后,链接器将运行时存储器地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。当这一步完成时,程序中的每个指令和全局变量都有唯一的运行时存储器地址了。
- 重定位节中的符号引用。在这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。为了执行这一步,链接器依赖于称为重定位条目的可重定位目标模块中的数据结构。
1.重定位条目
当汇编器生成一个目标模块时,它并不知道数据和代码最终存放在存储器中的什么位置。它也不知道这个模块引用的任何外部定义的函数或者全局变量的位置。所以,无论何时汇编器遇到对最终位置位置的目标引用,它就会生成一个重定位条目,告诉链接器在将目标文件合并成可执行文件时如何修改这个引用。代码的重定位条目放在.rel.text中。 已初始化的数据的重定位条目放在.rel.data中。
八、可执行目标文件
可执行目标文件的格式类似于可重定位目标文件的格式。ELF头部描述文件的总体格式。它还包括程序的入口点,也就是当程序运行时要执行的第一条指令的地址。.text 、.rodata和.data 节和可重定位目标文件中的节是相似的,除了这些节已经被重定位到它们最终的运行时存储器地址以外。.init节定义了一个小函数,叫做_init,程序的初始化代码会调用它。因为可执行文件是完全链接的(已被重定位了),所以它不再需要.rel节。
九、加载可执行目标文件
加载器将可执行目标文件中的执行代码和数据从磁盘拷贝到存储器中,然后通过跳转到程序的第一条指令或入口点来运行该程序。这个将程序拷贝到存储器并运行的过程叫做加载。
要运行可执行目标文件p,可以在Unix外壳的命令行中输入它的名字:
unix> ./p
十、动态链接共享库
共享库是一个目标模块,在运行时,可以加载到任意的存储器地址,并和一个在存储器中的程序链接起来。这个过程称为动态链接,是由一个叫做动态链接器的程序来执行的。
共享库也称为共享目标,在Unix系统中通常用.so后缀来表示。微软的操作系统大量地利用了共享库,它们称为DLL(动态链接库)。
《深入理解计算机系统》 Chapter 7 读书笔记的更多相关文章
- 《Linux内核设计与实现》Chapter 5 读书笔记
<Linux内核设计与实现>Chapter 5 读书笔记 在现代操作系统中,内核提供了用户进程与内核进行交互的一组接口,这些接口的作用是: 使应用程序受限地访问硬件设备 提供创建新进程与已 ...
- 《Linux内核设计与实现》Chapter 3 读书笔记
<Linux内核设计与实现>Chapter 3 读书笔记 进程管理是所有操作系统的心脏所在. 一.进程 1.进程就是处于执行期的程序以及它所包含的资源的总称. 2.线程是在进程中活动的对象 ...
- 《Linux内核设计与实现》Chapter 1 读书笔记
<Linux内核设计与实现>Chapter 1 读书笔记 一.Unix的特点 Unix从Multics中产生,是一个强大.健壮和稳定的操作系统. 特点 1.很简洁 2.在Unix系统中,所 ...
- 《Linux内核设计与实现》Chapter 2 读书笔记
<Linux内核设计与实现>Chapter 2 读书笔记 一.获取内核源码 1.使用Git 我们曾经在以前的学习中使用过Git方法 $ git clone git://git.kernel ...
- 《Linux内核设计与实现》Chapter 18 读书笔记
<Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...
- 《深入理解Linux内核》 读书笔记
深入理解Linux内核 读书笔记 一.概论 操作系统基本概念 多用户系统 允许多个用户登录系统,不同用户之间的有私有的空间 用户和组 每个用于属于一个组,组的权限和其他人的权限,和拥有者的权限不一样. ...
- 《深入理解 Java 虚拟机》读书笔记
第二章 Java 内存区域与内存溢出溢出 程序计数器 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器.字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的 ...
- 深入理解javascript系列,读书笔记
深入理解JavaScript系列(2):揭秘命名函数表达式 1.讲了函数声明和函数表达式的区别,包括一些在函数提升上的区别 2.如果给函数表达式的函数也取名,会在调试的时候受益 3.不要在block( ...
- Modern C++ CHAPTER 2(读书笔记)
CHAPTER 2 Recipe 2-1. Initializing Variables Recipe 2-2. Initializing Objects with Initializer Lists ...
随机推荐
- [Weblogic]如何清理缓存
背景:在开发调试或测试时,很多时候重新更新部署服务后会发现某些更新并没有体现到,还是之前的情况,也或者会出现其他错误问题,这个时候就要考虑到可能是weblogic缓存未清理引起. 清理缓存步骤如下: ...
- InfluxDB数据备份与恢复
数据备份与恢复 Example:(192.167.8.13 InfluxDB:DeviceHistory备份到192.167.8.52,然后恢复到该服务器上) steps: login 192 ...
- java使用httpcomponents发送get请求
一.适用场景 在ESTful webservice中,get方法一般都是用来获取数据.我们可以使用httpcomponents组件来完成调用. 如我们需要发起get请求,携带的参数都是附加到请求的ur ...
- JS生成UUID的方法实例
<!DOCTYPE html> <html> <head> <script src="http://libs.baidu.com/jquery/1. ...
- Linux 常用命令行
Linux常用命令行 第一部分: cd命令 第二部分:文件操作 第三部分:压缩包操作
- APMServ 配置记录
下载版本是5.2.6,它采用压缩率更高的7zip 解压后拷贝到c盘 点击APMServ.exe就可以启动了 注意: 1. SSL 有时需要关掉,否则apache起不来 2. 多数时候我只用apache ...
- linux 创建和删除目录
创建目录命令 mkdir 目录名 [root@wang whp]# mkdir catalog[root@wang whp]# lscatalog [root@wang whp]# mkdir cat ...
- rabbitmq server的安装以及常用的命令
Centos 源代码编译 安装 ErlangErlang依赖哪些库? A fully working GCC compiler environment Ncurses developm ...
- sqlserver 2005 分布式架构 对等事务复制 .
http://www.cnblogs.com/qanholas/archive/2012/03/22/2412444.html 一.为什么要使用对等事务复制 首先要说明的是使用sqlserve ...
- redis 非集群的主从配置及切换
单纯的master-slave不能称之为集群,只能叫做读写分离.此案例只针对master为单点服务,且程序端写死master为可写,slave为只读.若master宕机则不可用,若主从未开启持久化,不 ...