关于Linux的编译过程,其实只需要使用gcc这个功能,gcc并非一个编译器,是一个驱动程序。其编译过程也很熟悉:预处理–编译–汇编–链接。在接触底层开发甚至操作系统开发时,我们都需要了解这么一个知识点,如何从我们的代码到机器码。这段过程经历了什么,我们的函数变量又是在哪里?一个个好奇心驱使着我写下这篇文章。于博客中有提及Linux的安装以及gcc基本环境搭建、gcc编译流程、常用gcc指令集:https://blog.youkuaiyun.com/Alkaid2000/article/details/128036290?spm=1001.2014.3001.5501
文章目录
0x01 编译过程
可以使用这么一句指令来观察一个.c文件所需要经历的编译过程:gcc -v main.c。
根据gcc的输出可见,对于一个C程序来说,从源代码构建出可执行文件经历了三个阶段:
- 编译

gcc使用编译器ccl.exe进行编译,产生的编译代码保存在目录/temp下的文件ccelFAGc.s中。
- 汇编

gcc使用汇编器as.exe进行汇编,汇编过程产生汇编文件ccZfpupi.o,将上面生成的ccelFAGc.s进行汇编。
- 链接

调用collect2.exe进行链接。实际上这个collect2只是一个辅助程序,最终他将调用链接器ld来完成真正的链接过程。包括框出来的crtend.o、以及启动文件等等,本质上都是ld在进行链接。
事实上,从gcc看到只有这三个过程,但是对于C程序来说,编译过程也分为两个阶段:预编译和编译。所以软件构建过程通常分为四个阶段:预编译、编译、汇编、链接。

可以通过gcc手动控制以上的编译流程,从而留下中间文件以方便研究:
gcc HelloWorld.c -E -o HelloWorld.i预处理:加入头文件,替换宏。gcc HelloWorld.c -S -c -o HelloWorld.s编译:包含预处理,将 C 程序转换成汇编程序。gcc HelloWorld.c -c -o HelloWorld.o汇编:包含预处理和编译,将汇编程序转换成可链接的二进制程序。gcc HelloWorld.c -o HelloWorld链接:包含以上所有操作,将可链接的二进制程序和其它别的库链接在一起,形成可执行的程序文件。
那么接下来使用下面这段程序对于编译过程来做个总结:
hello.c:
#include <stdio.h>
#include "foo.h"
extern int foo2;
int main(int argc,char *argv[])
{
int result;
int r = 5;
#ifdef AREA
result = PI*r*r;
#else
result = PI*r*2;
#endif
return 0;
}
foo.h
#ifndef _FOO_H
#define _FOO_H
#define PI 3.1415926
#define AREA
struct foo_struct{
int a;
};
#endif
fool2.c
int foo2 = 20;
void foo2_func(int x)
{
int ret = foo2;
}
fool1.c
int fool = 10;
void fool_func()
{
int ret = fool;
}
0x02 预编译
C语言中的预编译是以#开头,常用的预编译指令包括#include、#define、#if等等。在工具链中,一般都提供单独的编译器,比如GCC中提供的编译器为cpp。但是预编译也可以看作编译过程的第一遍,是为编译做的一些工作,所以通常编译器中也包含了预编译的功能。比如前面的gcc并没有单独调用cpp,而是直接调用ccl进行编译,原因就是如上。
gcc -E hello.c -o hello.i
编译之后可以查看文件hello.i:
# 7 "foo.h"
struct foo_struct

本文围绕Linux下C语言编译过程展开,介绍了gcc驱动程序及编译的四个阶段:预编译、编译、汇编、链接。详细阐述了各阶段操作,如预编译处理指令、编译生成汇编代码、汇编生成机器指令与辅助信息、链接合并文件与符号重定位,还提及静态库和动态库链接。
最低0.47元/天 解锁文章
1665

被折叠的 条评论
为什么被折叠?



