gcc编译器的工作流程
参考资料:http://www.cnblogs.com/dfcao/p/csapp_intr1_1-2.html
在linux系统上,从源文件到目标文件的转化是由编译器完成的。以hello.c程序的编译为例,如下:
dfcao@linux: gcc -o hello hello.c
在这里,gcc编译器读取源文件hello.c,并把它翻译成一个可执行文件 hello。
这个翻译过程可分为四个阶段逐步完成:预处理,编译,汇编,链接,如下图所示。
逐步做下简单分析:
在未编译前,hello.c 的源代码如下
#include <stdio.h> int main()
{
printf("hello, world\n");
}
第一步、预处理阶段
执行命令: gcc -o hello.i -E hello.c
或者 cpp -o hello.i hello.c (这里cpp不是值c plus plus,而是预处理器the C Preprocessor)
预处理器cpp根据以字符开头#开头的命令,修改原始C程序。比如hello.c中的第一行为 #include <stdio.h>,预处理器便将stdio.h的内容直接插入到程序中。
预处理之后得到文本文件hello.i,打开如下

# 1 "hello.c"
# 1 "<built-in>"
# 1 "<命令行>"
# 1 "hello.c"
# 1 "/usr/include/stdio.h" 1 3 4
...
...
...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# 940 "/usr/include/stdio.h" 3 4 # 2 "hello.c" 2 int main()
{
printf("hello, world\n");
}

在源代码的前面插入了stdio.h,整个hello.i 的行数由hello.c的6行变到了855行!
第二步、编译阶段
执行命令: gcc -o hello.s -S hello.i
或者 ccl -o hello.s hello.i
编译器ccl 将文本文件hello.i 翻译为hello.s,这个文件里面包含一个汇编程序,如下

.file "hello.c"
.section .rodata
.LC0:
.string "hello, world"
.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
andl $-16, %esp
subl $16, %esp
movl $.LC0, (%esp)
call puts
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits

汇编语言是非常有用的,因为它将不同高级语言的不同编译器提供了通用的输出语言。例如,C和Fortran 的在此步编译产生的输出文件都是一样的汇编语言。
第三步、汇编阶段
执行命令: gcc -o hello.o -c hello.s
或者 as -o hello.o hello.s
汇编器as 将hello.s 翻译成机器语言保存在hello.o 中。这是个二进制文件,用命令hexdump hello.o 打开如下

0000000 457f 464c 0101 0001 0000 0000 0000 0000
0000010 0001 0003 0001 0000 0000 0000 0000 0000
0000020 011c 0000 0000 0000 0034 0000 0000 0028
0000030 000d 000a 8955 83e5 f0e4 ec83 c710 2404
0000040 0000 0000 fce8 ffff c9ff 00c3 6568 6c6c
0000050 2c6f 7720 726f 646c 0000 4347 3a43 2820
0000060 6255 6e75 7574 4c2f 6e69 7261 206f 2e34
0000070 2e36 2d33 7531 7562 746e 3575 2029 2e34
0000080 2e36 0033 0014 0000 0000 0000 7a01 0052
0000090 7c01 0108 0c1b 0404 0188 0000 001c 0000
00000a0 001c 0000 0000 0000 0017 0000 4100 080e
00000b0 0285 0d42 5305 0cc5 0404 0000 2e00 7973
00000c0 746d 6261 2e00 7473 7472 6261 2e00 6873
00000d0 7473 7472 6261 2e00 6572 2e6c 6574 7478
00000e0 2e00 6164 6174 2e00 7362 0073 722e 646f
00000f0 7461 0061 632e 6d6f 656d 746e 2e00 6f6e
0000100 6574 472e 554e 732d 6174 6b63 2e00 6572
0000110 2e6c 6865 665f 6172 656d 0000 0000 0000
0000120 0000 0000 0000 0000 0000 0000 0000 0000
*
0000140 0000 0000 001f 0000 0001 0000 0006 0000
0000150 0000 0000 0034 0000 0017 0000 0000 0000
0000160 0000 0000 0004 0000 0000 0000 001b 0000
0000170 0009 0000 0000 0000 0000 0000 03e8 0000
0000180 0010 0000 000b 0000 0001 0000 0004 0000
0000190 0008 0000 0025 0000 0001 0000 0003 0000
00001a0 0000 0000 004c 0000 0000 0000 0000 0000

第四步、链接阶段
执行命令: gcc -o hello hello.o
或者 ld -o hello hello.o
注意!hello程序调用了printf 函数,这个函数是标准C库中的一个函数,他保存在一个名为printf.o 的文件中,这个文件必须以某种方式合并到我们的hello.o的程序中。
链接器ld 负责处理这种合并。结果得到hello 可执行文件,可以被加载到内存中由系统执行。
./hello
总结
GCC编译器的工作过程:
源文件-->预处理-->编译/优化-->汇编-->链接-->可执行文件。
值得注意的是:我们在大学课本所学的编译原理是指高级语言程序编译生成汇编代码这一过程,并没有包含汇编和链接这一过程。汇编代码同样也需要经过汇编器来进行汇编,汇编器的全称是汇编编译器,也是编译器的一种。最后的链接是由链接器(Linker)完成的。
严格来说编译器只是将源代码转换成了目标代码,而链接器则将目标代码变成了可执行文件。但是我们平时并没有把编译器与链接器严格的区分出来,一般指的编译器是包含了链接这一功能的。
gcc编译器的工作流程的更多相关文章
- [Inside HotSpot] C1编译器工作流程及中间表示
1. C1编译器线程 C1编译器(aka Client Compiler)的代码位于hotspot\share\c1.C1编译线程(C1 CompilerThread)会阻塞在任务队列,当发现队列有编 ...
- GCC编译器使用
一.GCC简介 通常所说的GCC是GUN Compiler Collection的简称,除了编译程序之外,它还含其他相关工具,所以它能把易于人类使用的高级语言编写的源代码构建成计算机能够直接执行的二进 ...
- arm-linux-gcc 常用参数讲解 gcc编译器使用方法
我们需要编译出运行在ARM平台上的代码,所使用的交叉编译器为 arm-linux-gcc.下面将arm-linux-gcc编译工具的一些常用命令参数介绍给大家.在此之前首先介绍下编译器的工作过程,在使 ...
- windows下安装,配置gcc编译器
在Windows下使用gcc编译器: 1.首先介绍下MinGW MinGW是指仅仅用自由软件来生成纯粹的Win32可运行文件的编译环境,它是Minimalist GNU on Windows的略称. ...
- gcc编译器与基本类型3
C语言发展史 1969年贝尔实验室 肯尼斯·蓝·汤普逊,丹尼斯·李奇开发了B语言 ->Unix,New B语言,改名C语言83年提出C语言标准 1989年十二月正式通过C语言标准,C89标准 C ...
- C语言的本质(33)——GCC编译器入门
GCC(GNU CompilerCollection,GNU编译器套装),是由 GNU 开发的编程语言编译器.它是以GPL许可证所发行的自由软件,也是 GNU计划的关键部分.GCC原本作为GNU操作系 ...
- C语言_来了解一下GCC编译器编译C可执行脚本的过程
GCC简介 Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大.性能优越的多平台编译器,是GNU的代表作品之一.gcc是可以在多种硬体平台上编译出可执行程序的超级编译 ...
- GCC编译器原理(一)------交叉编译器制作和GCC组件及命令
1.1 交叉编译器制作 默认安装的 GCC 编译系统所产生的代码适用于本机,即运行 GCC 的机器,但也可将 GCC 安装成能够生成其他的机器代码.安装一些必须的模块,就可产生多种目标机器代码,而且可 ...
- 2、GCC编译器的使用
GCC编译器是一个非常强大和流行的C编译器,适用于各种Linux发行版.本文解释了一些流行的GCC编译器选项. GCC编译器选项 1.指定输出可执行文件名称 在最基本的形式中,gcc编译器可以用作: ...
随机推荐
- JAVA_SE基础——编码规范&代码编写规则
这次我来给大家说明下编码规范&代码编写规则 ↓ 编码规范可以帮助程序员在编程时注意一些细节问题,提高程序的可读性,让程序员能够尽快地理解新的代码,并帮助大家编写出规范的利于维护的Java代码 ...
- mysql数据库的三范式的设计与理解
一般的数据库设计都需要满足三范式,这是最基本的要求的,最高达到6NF,但是一般情况下3NF达到了就可以 一:1NF一范式的理解: 1NF是关系型数据库中的最基本要求,就是要求记录的属性是原子性,不可分 ...
- zuul入门(2)zuul的过滤器分类和加载
一.Groovy编写的Filter 1.可以放到指定目录加载 创建一个pre类型的filter,在run方法中获取HttpServletRequest 然后答应header信息 在代码中加入groov ...
- 命名参数名(含*args , * *kw的区别)
要限制关键字参数的名字,就可以用命名关键字参数 # coding=utf-8 # 命名关键字参数需要一个特殊分隔符*,*后面的参数被视为命名关键字参数.调用方式如下 def person(name, ...
- java设计模式—— 工厂模式
菜鸡互啄... 工厂模式通过定义一个创建对象的接口,让其子类决定实例化哪个工厂类.因此我们要解决接口选择的问题,实现不同的计划创建不同的对象. 首先我们定义一个轿车接口 public interfac ...
- SpringMVC(十二):SpringMVC 处理输出模型数据之@ModelAttribute
Spring MVC提供了以下几种途径输出模型数据:1)ModelAndView:处理方法返回值类型为ModelAndView时,方法体即可通过该对象添加模型数据:2)Map及Model:处理方法入参 ...
- 对return函数的认识
例1: def funOut(): def funIn(): print('宾果!你成功访问到我啦!') return funIn() #注意这里return的是funIn()即是一个函数 funOu ...
- delphi 10.1 Berlin 中使用自带的 Base64 编码
delphi 10.1 berlin版本中,有好几个 base64 编码的单元可以使用,例如 Indy, MessageDigest_5,还有 CnBase64,我现在使用自带的 System.Net ...
- 【转载】Linux下安装、配置、启动Apache
原文地址:http://www.cnblogs.com/zhuque/archive/2012/11/03/2763352.html 安装Apache前准备: 1.检查该环境中是否已经存在httpd服 ...
- 那些年,曾踩过的Spark坑
1.报错18/01/25 14:56:58 ERROR executor.CoarseGrainedExecutorBackend: Driver 127.0.0.1:37159 disassocia ...