Linux - gcc 如何完成编译、链接

前言

我们在 Linux 系统写下一段代码(C/C++),

以下面的代码为例:
文件名为:main.c在这里插入图片描述

要想让它变成一个可以执行的文件,
那么就少不了 编译、链接 的过程。
那细分的话:

编译 又可以分为:预编译、编译、汇编 三个阶段。

编译

1. 预编译(preprocessing)

完成的事情:

1)删除所有的 “#define”,并展开所有的宏定义
2)处理所有的条件预编译指令,“#if”、“#ifdef”、“#endif” 等;
3)处理 “#include” 预编译指令,将被包含的文件插入到预编译指令所在的文件;
4)删除所有的注释;
5)添加行号和文件名标识,以便于编译器产生调试用的符号信息及纠正错误和警告时显示行号;
6)保留所有的 #pragma 编译器指令,编译时需要使用。

gcc -E main.c -o main.i

其中的 main.c ------》源文件
main.i ------》生成的预编译后的文件
在这里插入图片描述
我们可以发现,预编译以后生成了一个名为 main.i 的文件,我们进去看看:
在这里插入图片描述
我们不难发现:
上面800多行的代码其实就是我们头文件 <stdio.h> 里的内容(#include的展开);
最终输出时定义的宏 MAX 被直接替换成了数值(宏替换);
第二个输出语句后面的注释被删除了(删除注释);
其实如果有#if,#endif这样的语句的话也会被处理(处理预编译指令)。

2. 编译(compilation)

完成的事情:

1)将源码转成汇编指令
2)进行词法、语法、语义的解析
3)代码优化
4)汇总符号 {[数据(全局、静态变量)、函数] 会生成符号}

gcc -S(大写) main.i ----> 生成了 main.s(小写) 的文件

在这里插入图片描述
同样的,我们可以通过 vim 指令进去看看 main.s 文件里都有什么:
在这里插入图片描述
在这里插入图片描述
可以看到:
编译后生成的文件中包含了汇编指令:push、call等等;(生成汇编指令
还有一些这个例子不太能体现的,但是也在编译阶段做的事情:
进行代码优化词法、语法解析汇总符号

3. 汇编(assembly)

完成的事情:

1)将汇编指令翻译成二进制格式
2)生成各个 section(段)
3)生成符号表

gcc -c main.s ----> 生成了 main.o 的文件main.o----可重定位的二进制目标文件

在这里插入图片描述
同理我们通过 vim 指令进去看一看:
在这里插入图片描述
我们会发现,这里面全部是“乱码”,根本无法查阅,读取;
这是因为经过汇编后生成了二进制文件
那有什么方法可以看 main.o 文件呢?
其实也是有的——objdump、readelf(这俩方法下次我们单独讨论)在这里插入图片描述

链接(Link)

经过前面编译的准备,最终来到了链接部分:

完成的事情:

1)合并所有文件的各个 section,调整段的大小及段的起始位置。合并符号表,进行符号解析,并给符号分配一个虚拟地址。
2)进行符号重定位,在使用符号的地方,全部替换成符号的虚拟地址。

链接:

  • gcc 所有生成的中间文件 ------》 a.out
    在这里插入图片描述
  • gcc 所有生成的中间文件 -o 自己命名的文件(可执行文件名)
    在这里插入图片描述

其实,我们在链接的时候,就自动完成了前面的编译工作;
所以,日常操作中,我们为了提高工作效率,
一般不会挨个进行编译,然后最后再链接,
我们可以通过链接,一次性完成编译链接的工作,生成可执行文件。
在这里插入图片描述
如上图所示,是我们一般习惯用的方式,
也可以用 gcc -o main main.c 的方法,效果是一样的:在这里插入图片描述

That’s all , thanks for watching !在这里插入图片描述