《程序员的自我修养》第二章 编译和链接 第三章 目标文件里有什么 第四章 静态链接

编译和链接

4个步骤:预处理,编译,汇编(输出的是目标文件),链接

这里写图片描述

编译器做了什么

词法分析:产生记号,对记号进行分类
语法分析:产生语法树,以表达式为节点
语义分析:分析的是静态语义,看是否语义出错,并对隐式转换进行节点插入。
中间语言生成:进行小部分的优化,然后转成中间代码的形式。(使得编译器划分为前端和后端,中间代码与机器无关)
接着就是后端包括的代码生成器和目标代码优化器了。(生成的就是目标代码了,好像汇编器的作用有时候会被忽略掉)

经过以上步骤之后,还未链接,有些变量的地址还不知道。经过链接器链接起来才能形成可执行文件。

模块拼接–静态链接

链接过程主要包括了:地址和空间分配,符号决议,重定位等步骤。

库其实是一组目标文件的包。
这里写图片描述
地址修正的过程也被叫做重定位


目标文件里有什么

这里写图片描述
这里写图片描述

这里写图片描述

文件头描述了整个文件的属性,(可执行,静态链接,动态链接,目标操作系统等,还有一个段表,描述各个段的偏移位置还有属性)
执行语句保存在text段,初始化的全局和局部静态变量放在data段,未初始化的放在bss段(默认值为0,预留位置,没有内容,不实际占据空间)

指令和数据分开的好处:
1. 指令可读,数据可读可写,防止程序指令被恶意修改
2. CPU的缓存一般设计为数据缓存和指令缓存分离,因此分开能够提高缓存命中率
3. 最重要的一点:运行程序的多个副本时,只需保持一份指令部分即可,节省大量空间

对于每个需要重定位的代码段或数据段,都会有一个相对应的重定位表。

字符串表:保存字符串

在链接中,目标文件之间相互拼合实际上是目标文件之间对地址的引用,即对函数和变量的地址的引用。

整个链接过程是基于符号才能够正确完成,每一个目标文件都会有一个相应的符号表。每个符号都有一个对应的值称为符号值,对于函数和变量来说就是地址。

函数签名:包含了一个函数的信息,包括函数名,参数类型,它所在的类和名称空间及其他信息。在编译器及链接器处理符号时,它们使用某种名称修饰的方法,使得每个函数签名对应一个修饰后名称。(C++允许多个不同参数类型的函数拥有一样的名字的原因)

这里写图片描述

编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号。
这里写图片描述

这里写图片描述
这里写图片描述


静态链接

这里写图片描述

链接过程:

这里写图片描述

在链接之前,目标文件中的所有段的虚拟内存地址都是0,因为虚拟空间还没有被分配。链接之后就有了。

这里写图片描述

4.3COMMON块

重复代码消除:以模板为例,链接器在最终链接的时候可以区分相同的模板实例段,然后合并入最终的代码段。

C++的全局对象的构造函数在main之前被执行,全局对象的析构函数在main之后被执行。

这里写图片描述

统一的C++二进制兼容标准(C++ABI)很难实现
这里写图片描述

一种语言的开发环境往往会附带有语言库(或运行库),这些库就是对操作系统API的封装(比如printf就是对Linux下write系统调用的封装)

静态运行库里面一个目标文件只包含一个函数,因为链接器在链接静态库的时候是以目标文件为单位的。

ld链接脚本

BFD库