转自:http://blog.csdn.net/21aspnet/article/details/167420
嵌入式Linux编译器GCC的使用
1、GCC概述
作为自由软件的旗舰项目,Richard Stallman在十多年前刚开始写作GCC的时候,还只是仅仅把它当作一个C程序语言的编译器,GCC的意思也只是GNU C Compiler而已。
经过了这么多年的发展,GCC已经不仅仅能支持C语言,它现在还支持Ada语言、C++语言、Java语言、Objective C语言、PASCAL语言、COBOL语言,并支持函数式编程和逻辑编程的Mercury语言等。而GCC也不再单只GNU C语言编译器的意思了,而是变成了GNU编译器家族了。
GCC的编译流程分为了4个步骤,分别为:
l
预处理(Pre-Processing)
l
编译(Compiling)
l
汇编(Assembling)
l
链接(Linking)
编译器通过程序的扩展名可分辨编写原始程序源码所用的语言,由于不同的程序所需要执行编译的步骤是不同的,因此GCC根据不同的后缀名对它们进行分别处理,下表指出了不同后缀名的处理方式
:
GCC所支持后缀名解释
后缀名
所对应的语言
编译流程
.c
C原始程序
预处理、编译、汇编
.C/.cc/.cxx
C++原始程序
预处理、编译、汇编
.m
Objective-C原始程序
预处理、编译、汇编
.i
已经过预处理的C原始程序
编译、汇编
.ii
已经过预处理的C++原始程序
编译、汇编
.s/.S
汇编语言原始程序
汇编
.h
预处理文件(头文件)
(一般不出现在指令行)
.o
目标文件
链接
.a/.so
编译后的库文件
链接
2、GCC编译流程分析
GCC使用的基本语法为:
Gcc [ option | filename ]
l option是GCC使用时的一些选项,通过指定不同的选项GCC可以实现其强大的功能。
l 这里的filename则是GCC要编译的文件,GCC会根据用户所指定的
编译选项以及所识别的
文件后缀名来对编译文件进行相应的处理。
这里从编译流程的角度讲解GCC的常见使用方法。首先,这里有一段简单的C语言程序,该程序由两个文件组成,其中“hello.h”为头文件,在“hello.c”中包含了“hello.h”,其源文件如下所示。
/* hello.h */
#ifndef_HELLO_H_
#define_HELLO_H_
typedef unsigned long
val32_t;
#endif
/* hello.c */
#include
#include
#include "hello.h"
int main(int argc, char *argv[])
{
val32_t I = 5;
printf("hello,embedded world%d
", i);
}
A. 预处理阶段
将头文件内容插入到源文件中,再进行宏替换。
GCC的选项“-E”可以使编译器在预处理结束时就停止编译,选项“-o”是指定GCC输出的结果,其命令格式为如下所示。
Gcc –E –o [目标文件] [编译文件]
我们已经知道,后缀名为“.i”的文件是经过预处理的C原始程序。要注意,“hello.h”文件是不能进行编译的,因此,使编译器在预处理后停止的命令如下所示。
[root@localhost gcc]# gcc –E –o hello.i hello.c
在此处,选项‘-o’是指目标文件,而‘.i’文件为已经过预处理的C原始程序。以下列出了hello.i文件的部分内容。
#2"hello.c"2
#1"hello.h"1
typedef unsigned long val32_t;
#3"hello.c"2
int main()
{
val32_t i=5;
printf("hello,embedded world%d
",i);
}
由此可见,GCC确实进行了预处理,它把“hello.h”的内容插入到hello.i文件中了。
B. 编译阶段
编译器在预处理结束之后,就进入编译阶段,GCC在编译阶段首先要检查代码的规范性、是否有语法错误等,以确定代码的实际要做的工作,在检查无误后,就开始把代码翻译成汇编语言,GCC的选项“-S”能使编译器在进行完汇编之前就停止。我们已经知道,“.s”是汇编语言原始程序,因此,此处的目标文件就可设为“.s”类型。
[root@localhost gcc]# gcc –S –o hello.s hello.i
以下列出了hello.s的内容,可见GCC已经将其转化为汇编了,感兴趣的话可以分析一下这一行简单的C语言小程序用汇编代码是如何实现的。
.file"hello.c"
.section.rodata
.LC0:
.string"hello,embedded world%d
"
.text
.globl main
.type main,@function
main:
pushl%ebp
movl%esp,%ebp
subl$8,%esp
andl$-16,%esp
movl$0,%eax
addl$15,%eax
addl$15,%eax
shrl$4,%eax
sall$4,%eax
subl%eax,%esp
movl$5,-4(%ebp)
subl$8,%esp
pushl-4(%ebp)
pushl$.LC0
call printf
addl$16,%esp
leave
ret
.size main,.-main
.section.note.GNU-stack,"",@progbits
..ident"GCC:(GNU)4.0.0 20050519(Red Hat 4.0.0-8)"
我们可以看到,这一小段C语言的程序在汇编中已经复杂很多了,这也是C语言作为所谓中级语言的优势所在。
C. 汇编阶段
汇编阶段是把编译阶段生成的“.s”文件生成目标文件,读者在此使用选项“-c”就可看到汇编代码已转化为“.o”的二进制目标代码了。如下所示。
[root@localhost gcc]# gcc –c hello.s –o hello.o
D. 链接阶段
在成功编译之后,就进入了链接阶段。在这里涉及一个重要的概念:函数库。
问题:在这个程序中并没有定义“printf”的函数实现,在预编译中包含进的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现“printf”函数的呢?
答案:
系统把这些函数实现都已经被放入名为libc.so.6的库文件中去了,在没有特别指定时,GCC会到系统默认的搜索路径“/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数“printf”了,而这也就是链接的作用。
完成了链接之后,GCC就可以生成可执行文件,其命令如下所示:
[root@localhost gcc]# gcc hello.o –o hello
运行该可执行文件,出现正确的结果。
[root@localhost gcc]# ./hello
hello,embedded world 5