Gcc 编译过程

gcc编译

gcc hello.c -o hello

hello.c 预处理——>hello.i 编译——>hello.s 汇编——>hello.o 链接——>hello

示例

hello.c

#include<stdio.h>

#define TEST_STRING "hello world!"

int main()
{
    printf("%s\n", TEST_STRING);
    return 0;
}

预处理

gcc -E hello.c -o hello.i

预编译过程主要处理那些源代码文件中以“#”开始的预编译指令,比如“#include”、“#define”等。

主要处理规则有:

  1. 将所有的“#define”删除,并且展开所有的宏定义。
  2. 处理所有的条件预编译指令,比如:“#if”、“#ifdef”、“#elif”、“#else”、“#endif”
  3. 处理“include”预编译指令,将被包含的文件插入到该预编译指令的位置,该过程是递归进行的。
  4. 删除所有的注释“//”和“/**/”。
  5. 添加行号和文件名标识,以便于编译时编译器产生调试用的行号信息及用于编译时产出错误或警告时能够显示行号。
  6. 保留所有的#pragma编译器指令,因为编译器需要使用它们。

hello.i 文件末尾内容如下,头文件已展开,并将 TEST_STRING 替换为 hello world

/**
 * bala bala 省略部分
 */
# 940 "/usr/include/stdio.h" 3 4

# 9 "hello.c" 2

int main()
{
    printf("%s\n", "hello world!");
    return 0;
}

编译

编译过程就是把预处理完的文件进行一系列词法分析、语法分析、语义分析及优化后生成相应的汇编代码文件。

gcc -S hello.i -o hello.s

编译生成汇编代码 hello.s

	.file	"hello.c"
	.section	.rodata
.LC0:
	.string	"hello world!"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	pushl	%ebp
	.cfi_def_cfa_offset 8
	.cfi_offset 5, -8
	movl	%esp, %ebp
	.cfi_def_cfa_register 5
	andl	$-16, %esp
	subl	$16, %esp
	movl	$.LC0, (%esp)
	call	puts
	movl	$0, %eax
	leave
	.cfi_restore 5
	.cfi_def_cfa 4, 4
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
	.section	.note.GNU-stack,"",@progbits

汇编

汇编器是将汇编代码转变成机器可以执行的指令,每一个汇编语句几乎都对应一条机器指令。

汇编后输出 目标文件,还不可执行!

gcc -c hello.s -o hello.o

链接

链接的过程主要包括:地址和空间分配、符号决议和重定位

主要工作是把一些指令对其它符号地址的引用加以修正,把各个模块之间的相互引用的部分处理好。
比如 A 模块用到 B 模块的某个函数,但是在编译 A 模块时,并不知道 B 模块中的函数的地址,所以需要链接器来处理。

符号决议:

确保所有目标文件中的符号引用都有唯一的定义

重定位:

链接时,确定变量或函数的地址后,把指令的目标地址修改成确定后的地址,这个过程就是重定位

gcc hello.o -o hello
posted @ 2022-04-05 18:47  shelmean  阅读(47)  评论(0编辑  收藏  举报