文章目录
- 前言
- GCC介绍
- GCC编译过程
- 预处理
- 编译
- 汇编
- 链接
- 关于链接:
前言
学习的过程遇到了.s后缀的文件,原来是gcc的编译过程,复习一下。
又牵扯到了各种C编译器,诸如MSVC、MinGW、Clang+LLVM等,挖个坑先。
还有关于动态链接和静态链接的知识,等等补。
GCC介绍
GCC编译器套装(GNU Compiler Collectin,缩写为GCC)是GNU计划制作的一种优化的编译器,支持各种编程语言、操作系统、计算机系统结构。
该编译器是以GPL及LGPL许可证所发行的自由软件,也是GUN计划的关键部分,还是GNU工具链的主要组成部分之一。 原名为GNU C语言编译器(GNU C Compiler)因为它原本只能处理C语言。如今GCC编译器已经被移植到比其他编译器更多的平台和指令架构上,并被广泛部署在开发自由和专有软件的工具中。
GCC还可以用于许多嵌入式系统,包括ARM和Power ISA的芯片。 GCC不仅是GNU操作系统得官方编译器,还是许多UNIX系统和Linux发行版的标准编译器。
GCC的设计:
GCC的外部接口遵循UNIX使用惯例。用户输入特定语言的驱动程序码(C语言为gcc,C++为g++,如此不一而足)该程序解释命令语句,调用实际编译器,在输出界面上运行汇编器,然后选择性地运行链接器,然后产生一个完整的可执行二进制文件。
GCC的扩展编译流程概览,包括专门的程序如预处理器、汇编器和链接器。
GCC编译过程
如下图所示,从hello.c到hello(或a.out)文件,必须经历hello.i,hello.s,hello.out,最后才得到hello(或a.out)文件,分别对应着预处理、编译、汇编和链接4个步骤。
大致工作过程:
1.预处理(Preprocessing)
2.编译(Compilation),
3.汇编(Assemble),
4.链接(Linking)。
准备工作,先写一个C源代码:
#include <stdio.h>
int main() {
printf("Hello, World!\n");
return 0; // 这是一行注释
}
保存为hello.c
预处理
GCC编译的第一阶段是预处理(Preprocessing),在这个阶段,预处理器会对C/C++源代码进行一些操作,部分操作如下:
- 移除注释
预处理器会删除源代码中的注释,包括单行注释(//)和多行注释(/* … */). - 处理预处理指令
预处理器会处理源代码中的预处理指令,这些指令以(#)开头,例如 #include、#define、#ifdef、#ifndef 等。其中,#include 指令用于包含头文件,#define 用于宏定义,#ifdef 和 #ifndef 用于条件编译等。 - 宏展开
预处理器会展开源代码中的宏,将宏调用替换成宏定义的内容。 - 生成预处理后的代码文件
预处理器的最终输出是一个去除了注释,展开了宏等操作的中间代码文件,通常以" .i "扩展名保存。这个文件可以用于接下来的编译步骤。
操作指令:
gcc -E hello.c -o hello.i
-E是预处理指令,-o是生成的文件名称
实际操作如下图:
将会得到一个hello.i文件,这是hello.c经过预处理后的文件,用vim打开hello.i观察
hello.c文件原来只有6行,经过预处理后增加到了七百多行,多了许多额外的变量、函数等内容,用G跳转到最下面可以发现,源代码部分 注释已经被删除。
编译
GCC 编译过程的第二阶段是编译(Compilation)。在这个阶段,编译器将经过预处理的源代码转换成汇编语言代码。
在这个阶段,GCC会对经过预处理的源代码进行一些词法语法的分析,生成中间表示(介于源代码和目标代码之间的表达形式),并对源代码进行一定程度的优化,最后GCC将刚刚生成的中间表示转化成目标平台的汇编语言代码。也就是 .s 文件
在gcc编译参数上加-S 可以将hello.i编译成hello.s文件,
指令如下:
gcc -S hello.i
实际操作如下图:
hello.s是一个汇编文件,可以用Vim打开查看,如下图
汇编
GCC 编译过程的下一个阶段是汇编(Assembly),在这个阶段,编译器将中间表示(通常是汇编语言)转化为目标平台的机器码。
得到.s汇编文件后,就可以用gcc得到机器码了,命令如下:
gcc -c hello.s
实际操作如下:
生成了一个hello.o文件,这一步产生的文件叫做目标文件,是二进制格式。
链接
在 GCC 编译过程的链接(Linking)阶段,多个目标文件(通常是编译多个源文件生成的)以及库文件将被合并到一个可执行文件中。链接的主要任务是解析符号引用,将各个目标文件中的代码和数据段组合在一起,解决外部依赖关系,生成最终的可执行文件。
命令如下:
gcc hello.o
这样会默认输出a.out文件,也可以用-o指定新的文件名,例如加上 -o hello,这样就会生成hello文件。
如下图:
执行一下:
关于链接:
链接分为动态链接和静态链接
动态链接使用动态链接库进行链接,生成的程序在执行时需要加载所需的动态库才能运行,动态链接生成的程序小巧,但是必须依赖动态库,否则无法执行。
linux下的动态链接库实际是共享目标文件(shared object),一般是.so文件,作用类似于windows下的.dll文件。
静态链接使用静态库进行链接,生成的程序包含程序运行所需要的全部库,可以直接运行,不过体积较大。
linux下的静态库是汇编产生的.o文件的集合,一般以.a的文件形式出现,gcc默认是动态链接,加上-static参数则采用静态链接,
文献参考
https://www.cnblogs.com/wjchao/p/7460375.html
https://zhuanlan.zhihu.com/p/111500914