GCC编译过程和原理浅析 1. 什么是GCC 2. GCC的主要特征
GCC是一个可移植的编译器,支持多种硬件平台。
GCC不仅仅是本地编译器,它还能跨平台交叉编译。
GCC有多种语言前段,用于解析不同的语言。
GCC是按模块化设计的,可以加入新的语言和新CPU架构的支持。
GCC是自由软件。
3. GCC编译程序的过程例如使用一个hello.c文件编译的过程如下图所示:
预处理(Pre-Processing):主要包括宏定义,文件包含,条件编译三部分。预处理过程读入源代码,检查包含预处理指令的语句和宏定义,并对其进行响应和替换。预处理过程还会删除程序中的注释和多余空白字符。最后会生成 .i 文件。
编译器(Compiling):编译器会将预处理完的 .i 文件进行一些列的语法分析,并优化后生成对应的汇编代码。会生成 .s 文件。
汇编器(Assembling):汇编器会将编译器生成的 .s 汇编程序汇编为机器语言或指令,也就是可以机器可以执行的二进制程序。会生成 .o 文件。
链接器(Linking):链接器会来链接程序运行的所需要的目标文件,以及依赖的库文件,最后生成可执行文件,以二进制形式存储在磁盘中。
4. GCC编译过程 4.1 GCC常用选项-o:生成目标( .i 、 .s 、 .o 、可执行文件等)
-c:通知 gcc 取消链接步骤,即编译源码并在最后生成目标文件。
-E:只运行 C 预编译器
-S:告诉编译器产生汇编语言文件后停止编译,产生的汇编语言文件扩展名为 .s
-Wall:使 gcc 对源文件的代码有问题的地方发出警告
-Idir:将dir目录加入搜索头文件的目录路径
-Ldir:将dir目录加入搜索库的目录路径
-llib:连接lib库
-g:在目标文件中嵌入调试信息,以便gdb之类的调试程序调试
4.2 预处理过程我们以 hello.c 程序为例:
#include <stdio.h> #define HELLOWORLD ("hello world\n") int main(void) { printf(HELLOWORLD); return 0; }使用gcc -E hello.c -o hello.i命令,将 hello.c 文件预处理并且生成 hello.i 目标文件。
之前说道,预处理会将头文件包含进来并且会将宏定义进行替换,因此替换后的 hello.i 文件如下:
# 1 "hello.c" # 1 "<built-in>" # 1 "<command-line>" # 1 "/usr/include/stdc-predef.h" 1 3 4 # 1 "<command-line>" 2 # 1 "hello.c" # 1 "/usr/include/stdio.h" 1 3 4 # 27 "/usr/include/stdio.h" 3 4 ......... ......... typedef unsigned char __u_char; typedef unsigned short int __u_short; typedef unsigned int __u_int; typedef unsigned long int __u_long; typedef signed char __int8_t; typedef unsigned char __uint8_t; ......... ......... extern struct _IO_FILE *stdin; extern struct _IO_FILE *stdout; extern struct _IO_FILE *stderr; extern FILE *fopen (const char *__restrict __filename, const char *__restrict __modes) ; ......... ......... int main(void) { printf(("hello world\n")); return 0; }可以看到将 stdio.h 文件包含进来,并且原封不动的将 HELLOWORLD 宏进行了替换。
4.3 编译过程使用gcc -S hello.i -o hello.s,将生成的hello.i文件编译为汇编程序hello.s。
.file "hello.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: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609" .section .note.GNU-stack,"",@progbits可以看到hello.s文件中全部都是汇编指令,说明已经生成成功了。
4.4 汇编过程汇编就是要将hello.s文件中的汇编指令全部转换为二进制的机器指令。