本文只记录学习过程中整理后的知识结构,不涉及具体细节,具体细节参考man手册和相关书籍,我再怎么说肯定都不如它们说的正确。
内容很浅,如果需要详细细节的可以点叉叉了,对此表示抱歉。
而且本来想写的详细一些,但是写着写着就跑题了,而且因为修改了多次,结果感觉中间错误很多
一、简单介绍
编译过程很复杂,而且不同的编程语言的编译过程也有所不同。
但是万变不离其踪,该有的过程还是得有,这个过程就是:
(Source Code)-->Scanner-->(Tokens)-->Parser-->(Syntax Tree)-->Semantic Analyzer-->(Commented Syntax Tree)-->Source Code Optimizer-->(Intermediate Representation)-->Code Generator-->(Target Code)-->Code Optimizer-->(Final Target Code)
这个过程很长,大致的过程就是:
扫描(词法分析)--》语法分析--》语义分析--》源代码优化--》源代码优化--》代码生成--》目标代码优化
1、1 词法分析
源代码被输入到扫描器(Scanner)将代码分割成一系列的记号(Token)。
例如:lex就是一个词法扫描器。
1、2 语法分析
在1、1中产生的标记,被送入语法分析器,产生语法树(Syntax Tree)
例如:yacc就是一个语法分析器
1、3 语义分析
这部分工作由语义分析器完成,但是它能分析的仅仅是静态语义。
1、4 源码优化
这部分由源码优化器完成,生成相应的中间代码。
常见中间代码有:三地址码,P -码等
1、1~1、4中的内容由被归纳成编译器前端,主要负责产生机器无关的中间代码。
1、5目标代码
这部分由代码生成器和目标代码优化器完成
1、5中的内容又被划分到编译器后端。主要将中间码变成机器码。
上面的只是一些概念性的东西,其实也没什么大用。
编译过程其实很复杂,不仅和源代码使用的语言类型有关,还和具体的CPU体系相关,所以就以C语言为例,使用GCC编译,CPU是X86架构,下面分两个部分介绍:
(1)从C源码到目标文件
(2)分析目标文件
使用工具:
gcc、objdump,readelf。
2、从C源码到目标文件
2.1 预编译
功能:
- 宏展开
- 处理与编译指令,如“#if”,“#ifdef”等
- 删除注释
- 添加行号--便于编译时候产生调试用的行号信息和产生错误时候能够显示行号
- 其他
使用指令1编译代码1,得到test.i,内容如下:
# 1 "test.c"
# 1 "<command-line>"
# 1 "test.c"
int main()
{
int a = 100;
returun;
}
与test.c比较,容易发现
- 宏定义被展开
- 注释被删除
- 预编译指令也被处理
- 注:这里没有使用头文件
注意:预编译不检查任何错误,测试代码中,return语句拼写错误,并且不带返回值,但是可以执行成功。
2.2 编译
功能
生成汇编源代码
使用指令2编译代码1,得到test.s,其内容如下:
.file "test.c"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
subl $16, %esp
movl $100, -4(%ebp)
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.7.1 20120721 (prerelease)"
.section .note.GNU-stack,"",@progbits
这个是X86下的汇编原代码。以后如果开始看链接过程,就需要对这个语法有一定了解。
汇编我也学的不怎么样。感觉比较重要的就是:
(1)弄明白ebp和esp寄存器作用,因为后面涉及到栈的操作,尤其是函数调用时候的栈操作。
(2)弄明白eax,ebx……几个寄存器的作用,尤其是eax寄存器,系统调用以及函数返回值都需要借助它保存一些临时变量。
(3)当然还有几个在代码中没有体现的,段寄存器和控制寄存器,内存寻址都依靠这些寄存器。
其他的基本看名字都能猜明白是什么意思。应该压力不大了。
2.3 汇编
功能:
将汇编源代码,编译成可执行指令
使用指令3编译代码1,得到test.o文件
使用指令4,查看得到的test.o文件,显示如下:
00000000 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 03 00 01 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 fc 00 00 00 00 00 00 00 34 00 00 00 00 00 28 00 |........4.....(.|
00000030 0b 00 08 00 55 89 e5 83 ec 10 c7 45 fc 64 00 00 |....U......E.d..|
00000040 00 b8 00 00 00 00 c9 c3 00 47 43 43 3a 20 28 47 |.........GCC: (G|
00000050 4e 55 29 20 34 2e 37 2e 31 20 32 30 31 32 30 37 |NU) 4.7.1 201207|
00000060 32 31 20 28 70 72 65 72 65 6c 65 61 73 65 29 00 |21 (prerelease).|
00000070 14 00 00 00 00 00 00 00 01 7a 52 00 01 7c 08 01 |.........zR..|..|
00000080 1b 0c 04 04 88 01 00 00 1c 00 00 00 1c 00 00 00 |................|
00000090 00 00 00 00 14 00 00 00 00 41 0e 08 85 02 42 0d |.........A....B.|
000000a0 05 50 c5 0c 04 04 00 00 00 2e 73 79 6d 74 61 62 |.P........symtab|
000000b0 00 2e 73 74 72 74 61 62 00 2e 73 68 73 74 72 74 |..strtab..shstrt|
000000c0 61 62 00 2e 74 65 78 74 00 2e 64 61 74 61 00 2e |ab..text..data..|
000000d0 62 73 73 00 2e 63 6f 6d 6d 65 6e 74 00 2e 6e 6f |bss..comment..no|
000000e0 74 65 2e 47 4e 55 2d 73 74 61 63 6b 00 2e 72 65 |te.GNU-stack..re|
000000f0 6c 2e 65 68 5f 66 72 61 6d 65 00 00 00 00 00 00 |l.eh_frame......|
00000100 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
下文忽略
………………………………
内容很乱