GCC编译过程分解

GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言。GCC 很快地扩展,变得可处理 C++。后来又扩展能够支持更多编程语言,如Fortran、Pascal、Objective-C、Java、Ada、Go以及各类处理器架构上的汇编语言等,所以改名GNU编译器套件(GNU Compiler Collection)。

编译器的工作是将源代码(通常使用高级语言编写)翻译成目标代码(通常是低级的目标代码或者机器语言),在现代编译器的实现中,这个工作一般是分为两个阶段来实现:

  1. 编译器的前端接受输入的源代码,经过词法、语法和语义分析等等得到源程序的某种中间表示方式。
  2. 编译器的后端将前端处理生成的中间表示方式进行一些优化,并最终生成在目标机器上可运行的代码。

GCC 设计中有两个重要的目标:

  • 硬件无关性:在构建支持不同硬件平台的编译器时,它的代码能够最大程度的被复用。
  • 要生成高质量的可执行代码,这就需要对代码进行集中的优化。

为了实现这两个目标,GCC 内部使用了一种硬件平台无关的语言,它能对实际的体系结构做一种抽象,这个中间语言为 RTL(Register Transfer Language)。

GCC的编译过程

GCC的编译过程可以分为以下四个阶段:预处理(或预编译)、编译、汇编、链接,如下图所示:

以下面代码为例:

int main()

    printf("Hello World\n");

    return 0;

}

include两种方式:

#include<> 引用的是编译器的类库路径里面的头文件。

#include” “ 引用的是程序目录的相对路径中的头文件。

gcc命令只是后台程序的包装,它会根据不同的参数要求去调用预编译编译程序cc1、汇编器as、链接器ld。

预编译

# gcc –E test.c –o test.i
或者
# cpp -o test.i test.c

以下为test.i部分内容:

# 1 "test.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 1 "<command-line>" 2
# 1 "test.c"
# 1 "/usr/include/stdio.h" 1 3 4
# 27 "/usr/include/stdio.h" 3 4
# 1 "/usr/include/features.h" 1 3 4
…………
# 2 "test.c" 2
int main()

{    

    printf("Hello World"\n);

    return 0;

}    

将所有的#define删除,并且展开所有的宏定义;预处理过程主要处理那些源代码中以#开始的预处理指令,主要处理规则如下:

  • 处理所有条件编译指令,如#if,#ifdef等;
  • 处理#include预处理指令,将被包含的文件插入到该预处理指令的位置。该过程递归进行,及被包含的文件可能还包含其他文件。
  • 删除所有的注释//和 /**/;
  • 添加行号和文件标识,如#2 “hello.c” 2,以便于编译时编译器产生调试用的行号信息及用于编译时产生编译错误或警告时能够显示行号信息;
  • 保留所有的#pragma编译器指令,因为编译器须要使用它们;

经过预编译后的.i文件不包含任何宏定义,因为所有的宏都已经被展开,并且包含的文件也已经被插入到.i文件中。所以当无法判断宏定义是否正确或头文件包含是否正确使,可以查看预编译后的文件来确定问题。

编译

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。这个过程是整个程序构建的核心部分,也是最复杂的部分之一。

# gcc -S test.i -o test.s
或者
# ccl -o test.s test.i

以下为test.s部分内容:

.file "test.c"
.section .rodata
.LC0:
.string "Hello World"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl $.LC0, %edi
call puts
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-16)"
.section .note.GNU-stack,"",@progbits

汇编

汇编器是将汇编代码转变成机器可以执行的命令,每一个汇编语句几乎都对应一条机器指令。汇编相对于编译过程比较简单,根据汇编指令和机器指令的对照表一一翻译即可。

# gcc -c test.c -o test.o
或者
# as -o test.o test.s

test.o的内容为机器码,不能以文本形式方便的呈现(不过可以利用 objdump -S file 查看源码反汇编)。利用hexdump 查看如下:

# hexdump test.o
0000000 457f 464c 0102 0001 0000 0000 0000 0000
0000010 0001 003e 0001 0000 0000 0000 0000 0000
0000020 0000 0000 0000 0000 0298 0000 0000 0000
0000030 0000 0000 0040 0000 0000 0040 000d 000a
0000040 4855 e589 00bf 0000 e800 0000 0000 00b8
0000050 0000 5d00 48c3 6c65 6f6c 5720 726f 646c
0000060 0000 4347 3a43 2820 4e47 2955 3420 382e
0000070 352e 3220 3130 3035 3236 2033 5228 6465
0000080 4820 7461 3420 382e 352e 312d 2936 0000
0000090 0014 0000 0000 0000 7a01 0052 7801 0110
00000a0 0c1b 0807 0190 0000 001c 0000 001c 0000
00000b0 0000 0000 0015 0000 4100 100e 0286 0d43
00000c0 5006 070c 0008 0000 2e00 7973 746d 6261
00000d0 2e00 7473 7472 6261 2e00 6873 7473 7472
00000e0 6261 2e00 6572 616c 742e 7865 0074 642e
00000f0 7461 0061 622e 7373 2e00 6f72 6164 6174
0000100 2e00 6f63 6d6d 6e65 0074 6e2e 746f 2e65
0000110 4e47 2d55 7473 6361 006b 722e 6c65 2e61
0000120 6865 665f 6172 656d 0000 0000 0000 0000
0000130 0000 0000 0000 0000 0000 0000 0000 0000

链接

链接器ld将各个目标文件组装在一起,解决符号依赖,库依赖关系,并生成可执行文件。如下形式:

ld –static crt1.o crti.o crtbeginT.o test.o –start-group –lgcc –lgcc_eh –lc-end-group crtend.o crtn.o (省略了文件的路径名)

# gcc -o test test.o或者# ld -o test test.o


test程序调用了printf 函数,这个函数是标准C库中的一个函数,它保存在一个名为printf.o 的文件中,这个文件必须以某种方式合并到test.o的程序中。

链接器ld负责处理这种合并。结果得到test可执行文件,可以被加载到内存中由系统执行。

小结

以上过程可以参考下图:

转载于:http://chengqian90.com/2017/C%E8%AF%AD%E8%A8%80/GCC%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B%E8%AF%A6%E8%A7%A3/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
张素琴老师编写的第二版,中英文课件全套 第1章 引论 1.1 什么是编译程序 1.2 编译过程编译程序的结构 1.2.1 编译过程概述 1.2.2 编译程序的结构 1.2.3 编译阶段的组合 1.3 解释程序和一些软件工具 1.3.1 解释程序 1.3.2 处理源程序的软件工具 1.4 程序设计语言范型 练习第2章 PL/0编程程序的实现 2.1 PL/0语言描述 2.1.1 PL/0语言的语汉描述图 2.1.2 PL/0语言文法的EBNF表示 2.2 PL/0编译程序的结构 2.3 PL/0编译程序的词法分析 2.4 PL/0编译程序的语法语义分析 2.5 PL/0编译程序的目标代码结构和代码生成 2.6 PL/0编译程序的语法错误处理 2.7 PL/0编译程序的目标代码解释执行时的存储分配 练习第3章 文法和语言 3.1 文法的直观概念 3.2 符号和符号串 3.3 文法和语言的形式定义 3.4 文法的类型 3.5 上下文无关文法及其语法树 3.6 句型的分析 3.6.1 自上而下的分析方法 3.6.2 自下而上的分析方法 3.6.3 句型分析的有关问题 3.7 有关文法实用中的一些说明 3.7.1 有关文法的实用限制 3.7.2 上下文无关文法中的规则 3.8 典型例题解答 练习第4章 词法分析 4.1 词法分析程序的设计 4.1.1 词法分析程序与语法分析程序的接口方式 4.1.2 词法分析程序的输出 4.1.3 将词法分析工作分离的考虑 4.2 单词的描述工具 4.2.1 正规文法 4.2.2 正规式 4.2.3 正规文法和正规式的等性 4.3 有穷自动机 4.3.1 确定的有穷自动机(DFA) 4.3.2 不确定的有穷自动机(NFA) 4.3.3 NFA转换为等价的DFA 4.3.4 确定有穷自动机的化简 4.4 正规式和有穷自动机的等价性 4.5 正规文法和有穷自动机的等价性 4.6 词法分析程序的自动构造工具 4.7 典型例题及解答 练习第5章 自顶向下语法分析方法 5.1 确定的自顶向下分析思想 5.2 LL(1)文法的判别 5.3 某些非LL(1)文法到LL(1)文法的等价变换 5.4 不确定的自顶向下分析思想 5.5 确定的自顶向下分析方法 5.5.1 递归子程序法 5.5.2 预测分析方法 5.6 典型例题及解答 练习第6章 自底向上优先分析 6.1 自底向上优先分析概述 6.2 简单优先分析法 6.2.1 优先关系 6.2.2 简单优先文法的定义 6.2.3 简单优先分析法的操作步骤 6.3 算符优先分析法 6.3.1 直观算符优先分析法 6.3.2 算符优先文法的定义 6.3.3 算符优先关系表的构造 6.3.4 算符优先分析算法 6.3.5 优先函数 6.3.6 算符优先分析法的局限性 6.4 典型例题及解答 练习第7章 LR分析 7.1 LR分析概述 7.2 LR(0)分析 7.2.1 可归前缀和子前缀 7.2.2 识别活前缀的有限自动机 7.2.3 活前缀及其可归前缀的一般计算方法 7.2.4 LR(0)项目集规范族的构造 7.3 SLR(1)分析 7.4 LR(1)分析 7.4.1 LR(1)项目集族的构造 7.4.2 LR(1)分析表的构造 7.5 LALR(1)分析 7.6 二义性文法在LR分析中的应用 7.7 语法分析程序的自动构造工具YACC 7.8 典型例题及解答 练习第8章 语法制导翻译和中间代码生成 8.1 属性文法 8.2 语法制导翻译概论 8.2.1 计算语义规则 8.2.2 S-属性方法和自下而上翻译 8.2.3 L-属性文法在自上而下分析中的实现 8.2.4 L-属性文法在自下而上分析中的实现 8.3 中间代码的形式 8.3.1 逆波壮大记号 8.3.2 三元式和树表表示 8.3.3 四元式 8.4 简单赋值语句的翻译 8.5 布尔表达式的翻译 8.5.1 布尔表达式的翻译方法 8.5.2 控制语句中布尔表达式的翻译 8.6 控制结构的翻译 8.6.1 条件转移 8.6.2 开关语句 8.6.3 for循环语句 8.6.4 出口语句 8.6.5 goto语句 8.6.6 过程调用的四元式产生 8.7 说明语句的翻译 8.7.1 简单说明语句的翻译 8.7.2 过程中的说明 8.8 数组和结构的翻译 8.8.1 数组说明和数组元素的引用 8.8.2 结构(记录)说明和引用的翻译 练习 第9章 符号表 9.1 符号表的作用和地位 9.2 符号的主要属性及作用 9.3 符号表的组织 9.3.1 符号表的总体组织 9.3.2 符号表项的排列 9.3.3 关键字域的组织 9.3.4 其他域的组织 9.3.5 下堆链域的组织 9.4 符号表的管理 9.4.1 符号表的初始化 9.4.2 符号的登录 9.4.3 符号的查找 9.4.4 符号表的分程序结构层次的管理 第10章 目标程序运行时的存储组织 10.1 数据空间的三种不同使用方法和管理方法 10.1.1 静态存储分配 10.1.2 动态存储分配 10.1.3 栈式动态存储分配 10.1.4 堆式动态存储分配 10.2 栈式存储分配的实现 10.2.1 简单的栈式存储分配的实现 10.2.2 嵌套过程语言的栈式实现 10.2.3 分程序结构的存储管理 10.3 参数传递 10.3.1 传值 10.3.2 传地址 10.3.3 过程参数 10.4 过程调用、过程进入和过程返回 练习 第11章 代码优化 11.1 优化技术简介 11.2 局部优化 11.2.1 基本块的划分 11.2.2 基本块的变换 11.2.3 基本块的有向图DAG(Directed Acyclic Graph)表示 11.2.4 DAG的应用 11.3 控制流分析和循环优化 11.3.1 程序流图 11.3.2 循环的查找 11.3.3 循环优化 11.4 数据流的分析与全局优化 11.4.1 一些主要的概念 11.4.2 数据流言程的一般形式 11.4.3 到达-定值数据流方程 11.4.4 可用表达式及其数据流方程 11.4.5 活跃变量数据流方程 11.4.6 复写传播 练习 第12章 代码生成 12.1 代码生成概述 12.1.1 代码生成程序在编译系统中的位置 12.1.2 设计代码生成程序的基本问题 12.2 一个简单的代码生成程序 12.2.1 计算机模型 12.2.2 待用信息链表法 12.2.3 代码生成算法 12.3 几种常用的代码生成程序的开发方法 12.3.1 解释性代码生成法 12.3.2 模式匹配代码生成法 12.3.3 表驱动代码生成法 12.4 全局寄存器分配(图着色法) 12.4.1 概述 12.4.2 图着色寄存器分配法的相关技术 12.4.3 示例 12.5 代码生成程序的自动化构造 12.5.1 模式匹配与动态规划 12.5.2 基于语法制导的代码生成程序自动构造技术 12.5.3 基于语义制导的代码生成程序自动构造技术 练习 第13章 编译程序的构造 13.1 编译程序的书写 13.1.1 编译程序的书写语言与T型图 13.1.2 编译程序的自展技术 13.2 可重定向编译程序 13.2.1 概述 13.2.2 支持可重定向编译的关键技术 13.2.3 常用的可重定编译程序 13.3 GCC的剖析 13.3.1 GCC的总体结构 13.3.2 GCC的中间表示 13.3.3 GCC的机器描述 13.3.4 GCC的代码生成与机器描述的接口 13.4 GCC的定制 13.4.1 GCC的剪裁 13.4.2 GCC编译程序的安装与配置 13.5 GCC的优化 13.5.1 概述 13.5.2 窥孔优化 13.5.3 基于机器描述的窥孔优化 13.5.4 修改GCC源程序的窥孔优化 练习 第14章 面向对象语言的编译 14.1 面向对象语言的基本概念 14.2 面向对象语言语法结构及语义处理的特征 14.2.1 面向对象语言的类的语法结构及语义 14.2.2 面向对象语言的有效类、延迟类及延迟成员 14.2.3 面向对象语言的类属类 14.2.4 面向对象语言的继承类 14.3 多态实例变量、多态引用的类型检查及绑定 14.3.1 实例变量和多态引用 14.3.2 静态类型检查及动态类型检查 14.3.3 对象的创建 14.4 面向对象操作的语义 14.4.1 类名的属性构造 14.4.2 类名的属性及其结构 14.5 类成员名的属性及其结构 14.5.1 类名的属性及其结构 14.5.2 类成员名的属性及其结构 14.6 对象的存储管理及废弃单元回收 14.6.1 对象的三种存储区组织管理方式 14.6.2 静态模型和栈式模型废弃单元的回收 14.6.3 堆式模型废弃单元的回收 练习 第15章 编译程序的面向对象构造 15.1 编译程序面向对象构造的基本概念 15.1.1 编译程序的需求 15.1.2 编译程序的分解 15.1.3 类的构造层次 15.1.4 类的特性定义 15.2 构造编译程序的面向对象类库 15.2.1 对传统编译程序构造中软件复用的分析 15.2.2 面向对象编译类库的地位 15.2.3 语言编译论的面向对象论域分析 15.3面向对象编译程序的符号表构造 练习 附录A PL、0编译程序文本 A.1 Pascal版本 A.2 C版本 参考文献
gcc是一款开源的编译器,它可以将C、C++、Objective-C等高级语言编写的源代码转换成可执行文件。gcc编译器的工作原理可以分为四个阶段:预处理、编译、汇编和链接。 1. 预处理阶段:在这个阶段,gcc会对源代码进行预处理,主要包括以下几个步骤: - 处理头文件:将#include指令所包含的头文件内容插入到源代码中。 - 宏替换:将源代码中的宏替换成宏定义所对应的内容。 - 条件编译:根据#if、#ifdef、#ifndef、#else、#elif和#endif指令来判断哪些代码需要编译,哪些代码需要忽略。 - 去注释:将源代码中的注释删除。 2. 编译阶段:在这个阶段,gcc会将预处理后的源代码转换成汇编代码,主要包括以下几个步骤: - 词法分析:将源代码分解成一个个单词。 - 语法分析:将单词组成语句,并检查语法是否正确。 - 语义分析:检查语句是否有意义,如变量是否定义过等。 - 中间代码生成:将源代码转换成中间代码。 3. 汇编阶段:在这个阶段,gcc会将汇编代码转换成机器码,主要包括以下几个步骤: - 语法分析:将汇编代码分解成指令和操作数。 - 语义分析:检查指令和操作数是否有意义。 - 目标代码生成:将汇编代码转换成目标代码。 4. 链接阶段:在这个阶段,gcc会将目标代码和库文件链接成可执行文件,主要包括以下几个步骤: - 符号解析:将目标代码中的符号(如函数名、变量名等)与库文件中的符号进行匹配。 - 重定位:将目标代码中的地址与库文件中的地址进行匹配。 - 可执行文件生成:将目标代码和库文件链接成可执行文件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值