C/C++ 语言中编译和链接通常都是自动完成的,win上 VS 全部包圆了,什么都不用操心,linux上使用cmake 编写CMakeLists.txt 也可以使用短短几行代码构建一个工程。
那么编译和链接到底在我们看不到的地方做了什么呢?深入理解计算机系统中有一句话 大多数编译系统提供了编译器驱动程序(compiler driver),它代表用户在需要时调用语言预处理器、编译器、汇编器和链接器。
从这句话中可以知道 要编译一个程序包含几个步骤 1.预处理 2.编译 3.汇编 4.链接 下面逐个分析这几个步骤
- 预处理: 这部分由预处理器完成,主要对C源码进行预处理操作,预处理器负责处理源码中的预处理指令,预处理指令以# 开头 比如宏替换,头文件展开等等
- 编译: 简单说就是将C代码转换为汇编代码
- 汇编: 简单说就是将汇编代码转为目标机器可以理解的二进制代码。linux上默认输出文件名为.o文件
- 链接: 这部分重点梳理下,很有意思的一个部分。
链接器以.o文件作为输入,经过加工处理生成可执行程序或者动态库。
那么.o文件中是什么呢?我们知道.o是个二进制文件,二进制文件中包含了什么数据呢? .o文件由各种不同的节组成,每个节都是一个连续的字节序列,这个节用英文标识section更好理解一些,就是一个个不同的部分。
深入理解计算机系统中详细解释了.o中有哪些节,每个节所代表的含义,具体含义这里不做详细解释,只是说明文件结构。
到此我们有了一段段二进制序列,怎么将他们变成可执行程序呢?链接器主要做两件事
1. 符号解析: 什么是符号?符号就是程序中定义的函数、全局变量、静态变量。那么符号解析顾名思义就是解析这些函数、全局变量或静态变量并将程序中对他们的引用和他们的定义位置关联起来。
2. 重定位: 当汇编过程生成.o文件时,它并不知道最终程序在内存中的确切位置,所以编译器和汇编器生成从地址0开始的代码和数据节。在链接阶段,链接器需要处理这些地址,将每个符号定义与一个内存位置关联起来,从而重定位这些节,然后修改所有对这些符号的引用,使得他们都执行这个内存位置。
注意: 链接器使用汇编器产生的重定位条目(relocation entry)的详细指令完成重定位,不自己判断该如何执行重定位逻辑。