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

编译和链接

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库