GCC编译过程

发布于 2021-10-13  535 次阅读


Linux GCC的编译过程主要分为四个阶段
预处理(Preprocess)
编译(Compile)
汇编(Assemble)
连接(Link)

本文使用最基础的hello world程序作为演示

#include<stdio.h>
int main()
{
    printf("hello, world\n");
}

预处理(Preprocess)

预处理是GCC进行编译的第一阶段,主要处理源代码中以“#”开头的预处理指令,比如“#include”,“#define”。在这个阶段中会使用编译器ccl作为工具,将其转换后直接插入到程序文本中得到另一个c程序,通常以“.i”作为文件拓展名。
如果想要仅生成生成预处理产生的 .i 文件可以使用下面的命令行
$ gcc -E hello.c -o hello.i
另注:gcc中最简单的编译命令为
$ gcc -o 指定文件名 文件
于是我们将得到一个杂乱的文件


提取其中重点内容得到以下代码

# 1 "hello.c"
# 1 "<biult-in>"
# 1 "<command-line>"
...
extern int printf (const char *__restrict __format, ...);
...
int main()
{
 printf("Hello,World\n");
 return 0;
}


预处理的一些处理规则如下

  • 递归处理“#include”预处理指令,将对应文件的内容复制到该指令的位置;
  • 删除所有的“#define”指令,并且在其被引用的位置递归的展开所有的宏定义;
  • 处理所有的条件预处理指令:“#if”、“#ifdef”、“#elif”、“#else”、“endif”等
  • 删除所有的注释;
  • 添加行号和文件标识

编译阶段


编译阶段是GCC编译的第二阶段,该阶段进行将对预处理文件进行一系列的词法分析,语法分析,语义分析以及优化,最终进行优化,编译器的语法检测也是从此时开始的,出错后转至出错处理器进行处理。经过编译后最后会形成汇编代码,GCC默认使用AT&T格式。

在命令中添加-S可查看生成的汇编文件,-S的操作对象可以是hello.i或者hello.s
$ gcc -S hello.c -o hello.s
如果想生成符合我们使用习惯的intel格式,可以添加命令 -masm=intel,添加命令-fno-asynchronous-unwind-tables后可生成没有cfi宏的汇编指令,增加可读性

汇编阶段


汇编阶段是GCC编译的第三阶段,汇编器根据汇编指令与机器指令的对照表进行翻译,将hello.s 汇编成目标文件hello.o。可以使用-c命令生成目标文件

$ gcc -c hello.c -o hello.o
$ gcc -c hello.i -o hello.o


由于此时文件是可重定向文件,所以需要指令读取

$ objdump -sd hello.o -M intel

因为还未进行链接,所以对象文件中的虚拟地址无法确认

链接阶段

链接阶段是GCC编译的第四阶段,可分为静态链接和动态链接两种,在这一阶段中,编译器将目标文件和依赖库进行链接生成可执行文件,主要包括地址和空间分配,符号绑定,和重定位等操作,可以通过以下指令来完成

$ gcc hello.o -o hello -static

关于静态链接和动态链接可以参考以下三篇博客
https://www.cnblogs.com/tracylee/archive/2012/10/15/2723816.html

https://www.zhihu.com/question/20484931

https://docs.microsoft.com/zh-cn/cpp/build/walkthrough-creating-and-using-a-dynamic-link-library-cpp?view=msvc-160