gcc编译过程简述
在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,打开如下
# "hello.c"
# "<built-in>"
# "<命令行>"
# "hello.c"
# "/usr/include/stdio.h"
...
...
...
extern void funlockfile (FILE *__stream) __attribute__ ((__nothrow__ , __leaf__));
# "/usr/include/stdio.h" # "hello.c" 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
.cfi_offset , -
movl %esp, %ebp
.cfi_def_cfa_register
andl $-, %esp
subl $, %esp
movl $.LC0, (%esp)
call puts
leave
.cfi_restore
.cfi_def_cfa ,
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 打开如下
457f 464c 011c
000d 000a 83e5 f0e4 ec83 c710
fce8 ffff c9ff 00c3 6c6c
2c6f 726f 646c 3a43
6e75 4c2f 6e69 206f 2e34
2e36 2d33 746e 2e34
2e36 7a01
7c01 0c1b 001c
00000a0 001c 080e
00000b0 0d42 0cc5 2e00
00000c0 746d 2e00 2e00
00000d0 2e00 2e6c
00000e0 2e00 2e00 722e 646f
00000f0 632e 6d6f 656d 746e 2e00 6f6e
472e 554e 732d 6b63 2e00
2e6c 665f 656d *
001f 001b
03e8
000b 00001a0 004c
第四步、链接阶段
执行命令: gcc -o hello hello.o
或者 ld -o hello hello.o
注意!hello程序调用了printf 函数,这个函数是标准C库中的一个函数,他保存在一个名为printf.o 的文件中,这个文件必须以某种方式合并到我们的hello.o的程序中。
链接器ld 负责处理这种合并。结果得到hello 可执行文件,可以被加载到内存中由系统执行。
./hello
总结
编译器的编译过程:
源文件-->预处理-->编译/优化-->汇编-->链接-->可执行文件。
平常用的最多、看起来一句命令就搞定的一次编译背后其实非常不简单。此博作简单记录,能力到了之后再做深入分析。
#---------------------------------------------------------------------------------#
参考文献
《Computer Systems: A Programmer's Perspective》 by Randal Bryant.
“编译过程” by itwilliamlee, http://www.lisdn.com/html/95/n-15195.html
gcc编译过程简述的更多相关文章
- Linux学习---GCC编译过程
(一)GCC编译过程 预处理 cpp -o a.i a.c //生成预处理文件 等同于[gcc -E] //预处理为将宏定义(#define)等进行替换. 编译 /user/lib/gcc/i ...
- GCC编译过程与动态链接库和静态链接库
1. 库的介绍 库是写好的现有的,成熟的,可以复用的代码.现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常. 本质上来说库是一种可执行代码的二进制形式,可 ...
- 1.GCC编译过程
一. GCC编译过程 gcc -E hello.c -o hello.i // 预处理.将代码中包含的头文件和宏进行替换 gcc -S hello.i -o hello.s // 汇编.将当前文本转换 ...
- unix gcc编译过程
gcc编译过程 现代编译器常见的编译过程: 源文件-->预处理-->编译/优化-->汇编-->链接-->可执行文件 对于gcc而言: 第一步 预处理 命令: ...
- gcc 编译过程
gcc 编译过程从 hello.c 到 hello(或 a.out)文件, 必须历经 hello.i. hello.s. hello.o,最后才得到 hello(或a.out)文件,分别对应着预处理. ...
- GCC编译过程
以下是C程序一般的编译过程: gcc的编译流程分为四个步骤,分别为:· 预处理(Pre-Processing) 对C语言进行预处理,生成*.i文件.· 编译(Compiling) 将上一步生成的*.i ...
- Linux系统GCC常用命令和GCC编译过程描述
前言: GCC 原名为 GNU C 语言编译器(GNU C Compiler),因为它原本只能处理 C语言.GCC 很快地扩展,变得可处理 C++.后来又 扩展能够支持更多编程语言,如Fortran. ...
- system 系统调用、gcc编译过程
system 库函数的功能是执行操作系统的命令或者运行指定的程序 #include <stdio.h> #include <stdlib.h>//引入库 int main() ...
- 和菜鸟一起学c之gcc编译过程及其常用编译选项【转】
转自:http://blog.csdn.net/eastmoon502136/article/details/8162626 版权声明:本文为博主东月之神原创文章,未经博主允许不得转载. 上篇文章,知 ...
随机推荐
- jQuery打造智能提示插件
插件根据实际需要在单功能上封装的,实现传入后台数据地址,要保存值的input,前台要传入的参数(过滤条件),来返回下拉提示数据,数据过多可上下滚动选择,选择后显示文本与对应的值,供后台操作,如图: j ...
- [译] MongoDB Java异步驱动快速指南
导读 mongodb-java-driver是mongodb的Java驱动项目. 本文是对MongoDB-java-driver官方文档 MongoDB Async Driver Quick Tour ...
- Django admin 权威指南(一)
版本: Django 1.10 此部分由官方文档<6.5.1 The Django admin site>翻译而来. 6.5.1.1 概览 默认情况下,使用startproject的时候, ...
- linux源码分析(三)-start_kernel
前置:这里使用的linux版本是4.8,x86体系. start_kernel是过了引导阶段,进入到了内核启动阶段的入口.函数在init/main.c中. set_task_stack_end_mag ...
- 自定义UIButton
偶尔逛简书能看见很多值得记下来的东西,有的接触过有的没接触过,接触过的也可能过段时间就忘记了,再上手的时候可能手足无措,所以决定有些觉得值得记下来的东西还是记录一下.博客是个好地方,因为很多人都能搜索 ...
- EF中的开放式并发(EF基础系列--28)
好久没更新EF这个系列了,现在又重新开始. 这次学习,开放式并发.首先拿出数据库脚本: 说明一下,这个数据库脚本是之前的章节中稍作修改的: USE [SchoolDB] GO /****** Obje ...
- deviceFilters与设备过滤
本主题是ASP.NET在移动设备上展示的方面的内容 起初看起来deviceFilters与前面的browserCaps类似.同样也会关联到HttpBrowserCapabilities,而在这里用到的 ...
- WPF数据编辑的提交与撤销
当为一个集合(通常绑定在DataGrid或其它ItemsControl控件)添加或编辑一个项时,通常会弹出一个编辑界面编辑项的属性,编辑结束再提交,或者我们不想编辑数据了,此时选择取消,数据项的内容没 ...
- SQL性能优化常见措施(Lock wait timeout exceeded)
SQL性能优化常见措施 目 录 1.mysql中explain命令使用 2.mysql中mysqldumpslow的使用 3.mysql中修改my.ini配置文件记录日志 4.mysql中如何加索引 ...
- java web学习总结(十九) -------------------监听器简单使用场景
一.统计当前在线人数 在JavaWeb应用开发中,有时候我们需要统计当前在线的用户数,此时就可以使用监听器技术来实现这个功能了. 1 package me.gacl.web.listener; 2 3 ...