生成可执行程序一共需要分四个过程,从预编译->编译->汇编->链接一共四个步骤,那在这四个步骤中到底做了一些什么事情呢?
预编译
预编译主要处理代码中以#开头的预编译指令,其编译的规则如下:
- 删除所有的#define,展开所有的宏定义
- 处理所有的预编译指令,例如:#f、#ifend等
- 处理#include预编译指令,将文件内容替换到它的位置,这个过程是递归进行的,文件包含其他文件
- 删除所有的注释,例如://、/**/
- 保留所有的#pragma编译器指令,例如:#pragma once是为了防止有文件重复引用
- 添加行号和文件标识,便于编译时编译器产生调试用的行号信息,编译时产生错误或警告能够显示在第几行
预编译是由main.c文件生成main.i文件
指令操作:gcc -E main.c -o main.i
编译
将上面预编译阶段生成的main.i文件进行操作
- 词法分析
- 语法分析
- 语义分析
- 优化
- 目标代码生成
- 目标代码优化
在编译阶段main.i文件生成main.s
指令操作:gcc -S main.i -o main.s
汇编
汇编过程相对来说比较简单,没有复杂的语法,也没有语义,更不需要做指令优化,只是根据汇编指令和机器指令的对照表意义翻译过来,生成可重定位的二进制文件。
在汇编阶段main.s文件生成main.o
指令操作:gcc -c main.s -o main.o
链接
链接分为静态链接和动态链接
主要处理了如下几件事情,合并段表,调整偏移量,合并符号表,完成符号重定位,分配地址和空间。
合并段表:将多个二进制可重定位文件中的段信息整合到一个文件中去
调整偏移量:段表经过合并之后大小发生了变化,所以地址需要适当的便宜
合并符号表:将多个二进制可重定位文件中的符号整合到一个文件中
完成符号的重定位:链接器把每个符号定义与一个虚拟地址联系起来,然后修改所有对这些符号的引用,使得他们指向这个存储位置,从而重定位这些符号
对于链接阶段想了解的更详细的话请看下面链接: