目录
编译原理
GCC编译过程
Preprocess阶段
File命令
Compile阶段
Assemble阶段
Link阶段
高级语言编写的程序想在操作系统运行,需要被翻译为机器指令,在按照可执行目标文件格式打包并以二进制形式存储在文件中
编译原理
编译器作用:把高级语言编写的程序转换为目标语言编写的程序
编译器的结构可分为前端(Frontend)和后端(Backend)两部分
- 前端是机器无关的,其功能是把源程序分解成组成要素和相应的语法结构’通过这个结构创建源程序的中间表示 同时收集和源程序相关的信息,存放到符号表中;
- 后端则是机器相关的其功能是根据中间表示和符号表信息构造目标程序。
编译过程大概有下面五个部分:
- 词法分析:读人源程序的字符流,输出为有意义的词素
- 语法分析:根据各个词法单元的第一个分量来创建树型的中间表示形式通常是语法树
- 语义分析:使用语法树和符号表中的信息,检测源程序是否满足语言定义的语义约束同时收集类型信息用于代码生成、类型检查和类型转换;
- 中间代码生成和优化:根据语义分析输出,生成类机器语言的中间表示,如三地址码。然后对生成的中间代码进行分析和优化;
- 代码生成和优化:把中间表示形式映射到目标机器语言。
GCC编译过程
测试代码 hello.c
#include<stdio.h>
int main()
{
printf("Hello World!\n");
}
编译时添加"-save-temps"和"--verbose"编译选项
- 前者用于将编译过程中生成的中间文件保存下来
- 后者用于查看GCC编译的详细工作流程
编译hello.c
gcc hello.c -o -save-temps --verbose
GCC的编译经过四个阶段:预处理(Preprocess),编译(Compile),汇编(Assemble),链接(Link)
cc1是编译器,对应一二阶段,hello.c ---> hello.s
as是汇编器,对应第三阶段,hello.s ---> hellos.o
collect2链接器,对应第四阶段,把程序运行库(CRT)中的目标文件(crt1.o,文件以.o结尾)和动态链接库(libgcc.so,文件以.so结尾)链接到hello
Preprocess阶段
主要处理源代码的 "#include","#define"这样的预处理指令,转换后插入程序文本得到 .i 文件
gcc -E参数:可以单独处理预处理过程
gcc -E hello.c -o hello.i
程序处理规则:
- 递归处理 #include ,把文件内容复制到文件中
- 删除所有 #define,在其被引用的位置递归展开所有宏定义
- 预处理所有条件预处理指令:#if # ifdef #elf等
- 删除所有注释
- 添加行号以及文件名标识
File命令
可以查看文件的类型
Compile阶段
对 .i 文件分析词法,语法,语义以及优化,生成汇编代码
gcc -S:单独处理编译阶段,这个指令包含预处理和编译阶段
gcc -S hello.c -o hello.s
Assemble阶段
汇编器会把根据汇编指令和机器指令的对照表进行编译,生成.o文件
gcc -C:操作对象可以是.c
gcc -c hello.c -o hello.o
.o是一个可重定向文件,使用objdump命令可查看
objdump -sd hello.o -M intel
objdump
是一个反汇编工具,用于查看二进制可执行文件或目标文件的汇编代码。通过使用 -sd
选项,可以显示详细的符号表和反汇编代码。-M intel
选项指定以 Intel 格式显示汇编代码。
输出结果将包含以下信息:
- 符号表:包含在目标文件中定义的全局符号和局部符号的地址和名称。
- 反汇编代码:以汇编语言的形式显示二进制代码的内容,可以看到每行汇编指令的地址、机器码和对应的汇编代码。
此时由于还未Link,对象文件中符号的虚拟地址无法确定。
Link阶段
把目标文件及其依赖库进行链接,生成可执行文件;链接操作由链接器(id.so)完成,主要工作是地址和空间重新分配,符号绑定和重定位操作;链接有静态,动态两种,gcc默认动态;-static 可指定使用静态链接。
gcc hello.o -o hello -static
通过链接操作,对象文件无法确定的符号,地址都已修正,程序可加载到内存正常执行。
linux可执行文件是elf格式,对应的二进制特征 7F 45 4C 46