GCC编译过程分解
http://blog.chinaunix.net/uid-20196318-id-28797.html
以helloworld为例分析gcc编译过程:
#include
int main()
{
printf(“Hello World\n”);
return 0;
}
通常我们使用gcc来生成可执行程序,命令为:gcc hello.c,生成可执行文件a.out
实际上gcc hello.c可以分解为4个步骤,分别是预处理(Preprocess),编译(Compilation),汇编(Assembly)和链接(Linking)。
1 预编译
gcc –E hello.c –o hello.i,以下为预处理后的输出文件hello.i的内容
# 1 "hello.c"
# 1 "
# 1 "
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 28 "/usr/include/stdio.h" 3 4
/***** 省略了部分内容,包括stdio.h中的一些声明及定义 *****/
# 2 "hello.c" 2
int main()
{
printf("Hello World\n");
return 0;
}
预编译过程主要处理那些源代码中以#开始的预编译指令,主要处理规则如下:
l 将所有的#define删除,并且展开所有的宏定义;
l 处理所有条件编译指令,如#if,#ifdef等;
l 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。该过程递归进行,及被包含的文件可能还包含其他文件。
l 删除所有的注释//和 /**/;
l 添加行号和文件标识,如#2 “hello.c” 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号信息;
l 保留所有的#pragma编译器指令,因为编译器须要使用它们;
2 编译
编译过程就是把预处理完的文件进行一系列词法分析(词法分析阶段是编译过程的第一个阶段,是编译的基础。这个阶段的任务是从左到右一个字符一个字符地读入源程序,即对构成源程序的字符流进行扫描然后根据构词规则识别单词(也称单词符号或符号)。词法分析程序实现这个任务。词法分析程序可以使用Lex等工具自动生成。),语法分析(语法分析是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成各类语法短语,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确.源程序的结构由上下文无关文法描述.语法分析程序可以用YACC等工具自动生成。 ),语义分析(语义分析是编译过程的一个逻辑阶段, 语义分析的任务是对结构上正确的源程序进行上下文有关性质的审查,进行类型审查。语义分析是审查源程序有无语义错误,为代码生成阶段收集类型信息。比如语义分析的一个工作是进行类型审查,审查每个算符是否具有语言规范允许的运算对象,当不符合语言规范时,编译程序应报告错误。)及优化后生成相应的汇编代码文件。
gcc –S hello.i –o hello.s,以下为编译后的输出文件hello.s的内容
.file "hello.c"
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (GNU) 4.4.0 20090506 (Red Hat 4.4.0-4)"
.section .note.GNU-stack,"",@progbits
3 汇编
汇编器是将汇编代码转变成机器可以执行的命令,每一个汇编语句几乎都对应一条机器指令。汇编相对于编译过程比较简单,根据汇编指令和机器指令的对照表一一翻译即可。
gcc –c hello.c –o hello.o,由于hello.o的内容为机器码,不能以文本形式方便的呈现。
4 链接
链接器ld将各个目标文件组装在一起,解决符号依赖,库依赖关系,并生成可执行文件。
ld –static crt1.o crti.o crtbeginT.o hello.o –start-group –lgcc –lgcc_eh –lc-end-group crtend.o crtn.o (省略了文件的路径名)。