一、GCC编译过程

2024-01-08 18:40:04

编译过程

编译过程可大致分为以下五个步骤
(1)词法分析:将源文件流拆分为以Token表示的字符表,给语法分析程序使用
(2)语法分析:根据各个词法单元的第一个分量构造语法树
(3)语义分析:根据符号表和语法树检测源程序
(4)中间代码生成和优化:根据语义分析输出,生成机器语言的中间表示,如三地址码
(5)代码生成和优化:把中间表示形式映射到目标机器语言

GCC编译过程

hello.c的源码如下:

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

在编译时添加–save-temps,用于保存中间文件,添加–verbose用于查看详细工作流程

gcc hello.c -o hello --save-temps --verbose

请添加图片描述
可以看到GCC的编译主要包括四个阶段,即
预处理 Preprocess
编译 Compile
汇编 Assemble
链接 Link

该过程分别使用了cc1,as和collect2三个工具:
其中cc1是编译器,保证hello.c->hello.i->hello.s
之后由as汇编器将hello.s汇编为hello.o目标文件
最后由链接器将ld命令封装,将C语言运行库CRT中的目标文件以及动态链接库.so进行链接,使程序可执行

预处理阶段

gcc -E hello.c -o hello.i

请添加图片描述
…………
请添加图片描述

预处理阶段的主要功能有:

包含头文件:预编译器负责处理#include预处理指令,将指定的头文件内容复制到源代码文件中。

宏替换:预编译器处理#define预处理指令,将定义的宏在源代码中进行替换。

条件编译:预编译器处理#if,#ifdef,#ifndef,#else,#elif和#endif等预处理指令,根据指定的条件决定是否编译某部分代码。

删除注释:预编译器会移除源代码中的所有注释,使其不会出现在生成的目标代码中。

引用其他预处理指令:预编译器还可以处理如#error,#pragma等其他预处理指令。

编译阶段

gcc -S hello.i -o --masm=intel -fno-asynchronous-unwind-tables

GCC默认使用AT&T格式的汇编语言,编译时添加选项–masm=intel可以将其指定为intel模式,-fno-asynchronous-unwind-tables用于生成没有cfi宏的汇编指令,以提高可读性
在这里插入图片描述
编译阶段将经过预处理的代码转换为机器语言,通常为汇编语言

汇编阶段

gcc -c hello.s -o hello.o
file hello.o
objdump -sd hello.o -M intel

请添加图片描述
此时的目标文件hello.o是一个可重定位文件,可以使用objdump命令查看。
在其中可以发现,由于未进行链接,对象文件中符号的虚拟地址还未确定,字符串“hello”的地址被设定为了0x0000,作为参数传递字符串地址的rdi寄存器被设置为0x0,而call puts指令中puts()函数的地址被设置为下一条指令的地址0x18

链接阶段

gcc hello.o -o hello
objdump -sd hello -M intel

请添加图片描述

链接分为动态链接和静态链接,gcc默认动态链接,可加-static改为静态。
链接阶段主要包括地址和空间分配,符号绑定和重定位

文章来源:https://blog.csdn.net/mirocky/article/details/135461513
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。