0 前提

0.1 4个过程

预处理(preprocessing) ----------------- gcc -E
编译(compilation) ------------------ gcc -S
汇编(assembly) -------------------- as
连接(linking) --------------------- ld

0.2 gcc指令

  • -o:指定生成的输出文件;
  • -E:仅执行编译预处理;
  • -S:将C代码转换为汇编代码
  • -wall:显示警告信息;
  • -c:仅执行编译操作,不进行链接操作。

1. 预编译

1.1 预编译主要做以下4件事:

  • 展开所有头文件
  • 宏替换
  • 去掉注释
  • 条件编译
  • 即对#ifndef #endif进行判断检查

1.2 示例

源码:

//#include <stdio.h>
// #include "calculation.h"
#include "cpp_add.h"

#define TEST

#ifndef TEST
#define MAX_NUM 100
#else
#define MAX_NUM 10
#endif

int main()
{
    int x = 1;
    int y = 3;
    int a = MAX_NUM;

    square(a);

    return 0;
}

预编译:

预编译文件 linemarkers 预编译文件和ascll_预编译

2. 编译

2.1 编译主要做以下2件事

编译将代码转为汇编代码,并且这个步骤做了2件很重要的工作:

  • 编译器在每个文件中保存一个函数地址符表,该表中存储着当前文件内包含的各个函数的地址
  • 这一步要生成汇编代码,即一条一条的指令,而调用函数的代码会被编译成一条call指令,指令后面跟的是jmp指令的汇编代码地址,而jmp指令后面跟的才是“被调用的函数编译成汇编代码后的第一条指令”的地址,但是给call指令后面补充上地址的工作是链接时候的事情。

2.2 示例

还是上面的demo执行:gcc -S main.c
查看生成的main.s文件,也是汇编文件:

.file	"main.c"
	.text
	.globl	main
	.type	main, @function
main:
.LFB0:
	.cfi_startproc
	endbr64
	pushq	%rbp
	.cfi_def_cfa_offset 16
	.cfi_offset 6, -16
	movq	%rsp, %rbp
	.cfi_def_cfa_register 6
	subq	$16, %rsp
	movl	$1, -12(%rbp)
	movl	$3, -8(%rbp)
	movl	$10, -4(%rbp)
	movl	-4(%rbp), %eax
	movl	%eax, %edi
	call	square@PLT
	movl	$0, %eax
	leave
	.cfi_def_cfa 7, 8
	ret
	.cfi_endproc
.LFE0:
	.size	main, .-main
	.ident	"GCC: (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0"
	.section	.note.GNU-stack,"",@progbits
	.section	.note.gnu.property,"a"
	.align 8
	.long	 1f - 0f
	.long	 4f - 1f
	.long	 5
0:
	.string	 "GNU"
1:
	.align 8
	.long	 0xc0000002
	.long	 3f - 2f
2:
	.long	 0x3
3:
	.align 8
4:

3. 汇编

汇编实际上指把汇编语言代码翻译成目标机器指令的过程。
目标文件由段组成。通常一个目标文件中至少有两个段:

  • 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
  • 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

3.1 主要完成以下3件事

  • 根据汇编指令和特定平台,把汇编指令翻译成二进制形式;
  • 合并各个section,合并符号表;
  • 生成.o文件

3.2 示例

预编译文件 linemarkers 预编译文件和ascll_预编译文件 linemarkers_02

4. 链接

4.1 主要完成以下3件事:

  • 合并各个.obj文件的section,合并符号表,进行符号解析;
  • 符号地址重定位;
  • 生成可执行文件

4.2 示例

预编译文件 linemarkers 预编译文件和ascll_#define_03