《Linux内核设计与实现》第7章读书笔记
第七章 链接
一、 链接的概念
链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程。可以执行于编译、加载和运行时,由叫做链接器(可实现分离编译)的程序自动执行。
二、静态链接
为了创建静态链接,链接器完成两个主要任务:
- 符号解析:将每个符号引用和一个符号定义联系起来。
- 重定位:编译器和汇编器生成从0地址开始的代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符 号的引用,使得它们指向这个存储器位置,从而重定位这些节。
三、目标文件
- 可重定位目标文件:包含二进制代码和数据,其形式可以在编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件。
- 可执行目标文件:包含二进制代码和数据,其形式可以直接拷贝到存储器并执行。
- 共享目标文件:一种特殊类型的可重定位目标文件,可以在加载或运行时被动态地加载到存储器并链接。
四、可重定位目标文件
ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或是共享的)、机器类型(如IA32)、节头部表的文件偏移,以及节头部表中的条目大小和数量。不同的节的位置和大小是由节头部表描述的,其中目标文件中每个节都有一个固定大小的条目。
五、符号和符号表
每个可重定位目标模块m都有一个符号表,它包含m所定义和引用的符号的信息。在链接器上下文中有三种不同的符号:
- 由m定义并能被其他模块引用的全局符号。全局链接器符号对应于非静态的C函数以及被定义为不带C static属性的全局变量
- 由其他模块定义并被模块m引用的全局符号。这些符号称为外部符号,对应于定义在其它函数中的C函数和变量
- 只被模块m定义和引用的本地符号。有的本地链接器符号对应于带static属性的C函数和全局变量。这些符号在模块m中随处可见,但是不能被其他模块引用。目标文件中对应于模块m的节和相应的源文件的名字也能获得本地符号
六、符号解析
C++和Java中能使用重载函数,是因为编译器将每个惟一的方法和参数列表组合编码成一个对链接器来说惟一的名字。这种编码过程叫做毁坏,而相反的过程叫恢复。
C++和Java使用的是兼容的毁坏策略。一个已毁坏类的名字是由名字中字符的整数数量,后面跟原始名字组成的。比如类Foo被编译成3Foo;方法被编译成:原始方法名 + __ + 已毁坏的类名+ 再加上每个类参数的一个字母。如Foo::bar(int, long)被编译成bar__3Fooil。毁坏全局变量和模板名字的策略是相似的。
1. 链接器如何解析多处定义的全局符号
函数和已初始化的全局变量是强符号(strong);未初始化的全局变量是弱符号(weak)。
根据强弱符号的定义,Unix链接器使用下面的规则来处理多重定义的符号:
- 规则1:不允许有多个强符号
- 规则2:如果有一个强符号和多个弱符号,那么选择强符号
- 规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个
2.与静态库链接
静态库:有的编译系统都提供一种机制,将所有相关的目标模块打包成为的一个单独的文件。
unix中,静态库以一种称为存档(archive)的特殊文件格式存放在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部描述每个成员目标文件的大小和位置;后辍名为.a。
3.链接器如何使用静态库来解析引用
(1)在符号解析的阶段,链接器从左到右按照它们在编译器驱动程序命令行上出现的相同顺序来扫描可重定位目标文件和存档文件。在这次扫描中,链接器维持一个可重定位目标文件的集合E(这个集合中的文件会被合并起来形成可执行文件),一个未解析的符号(即引用了但是尚未定义的符号)集合U,以及一个在前面输入文件中已定义的符号集合D。初始时,E、U和D都是空的。
(2)命令行上的库和目标文件的顺序非常重要。在命令行中,如果定义一个符号的库出现在引用这个符号的目标文件之前,那么引用就不能被解析,链接会失败。
(3)关于库的一般准则:
将它们放在命令行的结尾;
另一方面,如果库不是相互独立的,那么它们必须排序,使得对于每个被存档文件的成员外部引用的符号s,在命令行中至少有一个s的定义实在对s的引用之后的;
如果需要满足依赖需求,可以在命令行上重复库。
七、重定位
重定位由两步组成:
重定位节和符号定义。在这一步中,链接器将所有相同类型的节合并为同一类型的新的聚合节。然后,链接器将运行时存储器地址赋给新的聚合节,赋给输入模块定义的每个节,以及赋给输入模块定义的每个符号。当这一步完成时,程序中的每个指令和全局变量都有唯一的运行时存储器地址。
重定位节中的符号引用。在这一步中,链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址。为了执行这一步,链接器依赖于称为重定位条目的可重定位目标模块中的数据结构。
八、可执行目标文件
可执行目标文件的格式类似于可重定位目标文件的格式。ELF头部描述文件的总体格式。它还包括程序的入口点,也就是当程序运行时要执行的第一条指令的地址。.text 、.rodata和.data 节和可重定位目标文件中的节是相似的,除了这些节已经被重定位到它们最终的运行时存储器地址以外。.init节定义了一个小函数,叫做_init,程序的初始化代码会调用它。因为可执行文件是完全链接的(已被重定位了),所以它不再需要.rel节。
ELF可执行文件被设计得很容易加载到存储器,可执行文件的连续的片被映射到连续的存储器段。段头部表描述了这种映射关系。
九、加载可执行目标文件
要运行可执行目标文件p,可以在Unix外壳的命令行中输入它的名字:unix> ./p
任何Unix程序都可以通过调用execve函数来调用加载器。加载器将可执行目标文件中的代码和数据从磁盘拷贝到存储器中,然后通过跳转到程序的第1条指令,即入口点(Entry Point),来运行该程序。将程序拷贝到存储器并运行的过程叫做加载(loading)。
十、动态链接共享库
共享库是一个目标模块,在运行时,可以加载到任意的地存储器地址,并在存储器中和一个程序链接起来。这个过程称为动态链接,是由一个叫做动态链接器的程序执行的。
共享库也称为共享目标,在Unix系统中通常用.so后缀来表示。(在MS OS 中为DLL文件)
【注意:静态链接与动态链接的区别:静态链接是把程序所需要的库代码和数据拷贝和嵌入到引用它们的可执行文件中;而动态链接是所有引用该库的可执行文件文件共享这个.so(dll)文件中的代码和数据。】
十一、与位置无关的代码
共享库的一个主要目的就是允许多个正在运行的进程共享存储器中相同的库代码,因而节约存储器资源。
PIC:编译库代码,不需要链接器修改库代码,就可以在任何地址加载和执行这些代码。
在一个IA32系统中,对同一个目标模块中过程的调用不需要特殊处理的,因为引用是PC相关的,已知偏移量,就是PIC了。然而,对外部定义的过程调用和对全局变量的引用通常不是PIC,因为它们都要求在链接时重定位。
如何对全局变量生成PIC引用呢?基于以下事实:无论我们在存储器中的何处加载一个目标模块(包括共享模块),数据段总是分配为紧随在代码段后面。因此,代码段中任何指令和数据段中任何变量之间的距离都是一个运行时常量。
《Linux内核设计与实现》第7章读书笔记的更多相关文章
- 《Linux内核设计与实现》第四周读书笔记——第五章
<Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...
- LINUX内核设计与实现第三周读书笔记
LINUX内核设计与实现第三周读书笔记 第一章 LINUX内核简介 1.1 Unix的历史 1969年的夏天,贝尔实验室的程序员们在一台PDR-7型机上实现了Unix这个全新的操作系统. 1973年, ...
- 《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 5 读书笔记
<Linux内核设计与实现>Chapter 5 读书笔记 在现代操作系统中,内核提供了用户进程与内核进行交互的一组接口,这些接口的作用是: 使应用程序受限地访问硬件设备 提供创建新进程与已 ...
- 《Linux内核设计与实现》Chapter 18 读书笔记
<Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...
- Linux内核设计与实现第十周读书笔记
第十七章 设备与模块 关于设备驱动与设备管理,我们讨论四种内核成分. 设备类型 模块 内核对象 sysfs 17.1设备类型 在Linux以及所有Unix系统中,设备被分为以下三种类型: 块设备,块设 ...
- Linux内核设计与实现第八周读书笔记
第四章 进程调度 进程在操作系统看来是程序的运行态表现形式. 4.1多任务 多任务操作系统就是能同时并发地交互执行多个进程的操作系统. 多任务操作系统会使多个进程处于堵塞或者睡眠状态.这些任务尽管位于 ...
- Linux内核设计与实现第六周读书笔记
第三章 进程管理 3.1 进程 进程是处于执行期的代码.通常进程还要包含其他资源,像打开的文件.挂起的信号.内核的内部数据.处理器状态.一个或多个具有内存映射的内存地址空间及一个或多个执行线程,当然还 ...
随机推荐
- 在vsphere6.5启用Tesla K80
基础环境: vsphere6.5 VMware vCenter6.5 宝德服务器2750S Tesla K80 0x01 选择主机,配置→硬件→PCI设备→添加K80显卡 注意:1.添加完显卡后,主机 ...
- centos7 上安装mysql5.7后登录报错ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: Yes 或者No)
原文转载自以下链接:https://blog.csdn.net/keepd/article/details/77151006 安装完mysql后会有个临时密码去日志查看,但是查看登录修改密后还是不行 ...
- python数据分析系列(1)
目录 python基础 python语言基础 Ipython的一些特性 Python语法基础 Python控制流 lambda表达式 Python的数据结构 元组 列表 字典 集合 列表.集合.字典推 ...
- (二)Hyperledger Fabric 1.1安装部署-Fabric Samples
Hyperledger Fabric Samples是官方推荐的First Network,对于熟悉fabric和测试基础环境很有好处. Fabric Samples源码下载:使用git下载源码,进入 ...
- Scrum立会报告+燃尽图(Final阶段第一次)
此作业要求参见:https://edu.cnblogs.com/campus/nenu/2018fall/homework/2480 项目地址:https://coding.net/u/wuyy694 ...
- "Hello World!"团队负责人领跑衫感言
时间:2017年12月7日 团队名称:“Hello World!” 团队项目:空天猎 团队成员:陈建宇(项目负责人).刘淑霞.黄泽宇.方铭.贾男男.刘耀泽.刘成志 感言正文: 记<软件工程> ...
- 20162328蔡文琛 week05 大二
20162328 2017-2018-1 <程序设计与数据结构>第5周学习总结 教材学习内容总结 集合是收集元素并组织其他对象的对象. 集合中的元素一般由加入集合的次序或元素之间的某些固有 ...
- android随机运算器开发小结1
想到第一天自己写了一个简单的四则运算程序的情景:我便想起了引起我们不断迭代开发的程序背景是:二柱子接受老师安排的给孩子出题的任务,每次需要给孩子设置出题任务,生成相应的小学运算题目,所以我们面对的需求 ...
- CentOS中Intel i350T4驱动安装
2015.3.31 在linux*中直接按解决方法中安装i350驱动即可 *************************************************************** ...
- (2016.2.2)1001.A+B Format (20)解题思路
https://github.com/UNWILL2LOSE/object-oriented 解题思路 目标: *首先运算要求实现输入2个数后,输出类似于银行的支票上的带分隔符规则的数字. 代码实现思 ...