最近在看CSAPP(Computer System: A Programmer’s Perspective, 深入理解计算机系统),看到第三章 程序的机器级表示,第二节讲了一下C程序的编译执行过程,以前在《C Primer Plus》上也看到过类似的内容,感觉比较有意思,遂记录一下。
源文件只是以字节序列的形式存储在磁盘上,它是如何运行在机器上的呢?
源文件会经历以下历程:
预处理(Preprocessing):将#include等头文件进行置换,形成完整的程序文件,一般预处理后,文件会变大。得到的是文本文件。
例如:1
2
3
4
5
6
7// preprocessing.c
int main()
{
int a = PI;
return 0;
}使用
gcc -E preprocessing.c -o preprocessing.i
预编译后:1
2
3
4
5
6
7
8
9
10
11
12
13
14// preprocessing.i
# 1 "preprocessing.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "preprocessing.c"
int main()
{
int a = 3.14;
return 0;
}可以看到
int a = PI;
被宏定义的PI值进行了替换。编译(Compilation):使用编译器,将完整的C代码编译成汇编代码。得到的仍然是文本文件。
以3.2.2的程序为例:1
2
3
4
5
6
7// mstore.c
long mult2(long, long);
void multstore(long x, long y, long *dest){
long t = mult2(x, y);
*dest = t;
}使用
gcc -S mstore.i -o mstore.s
进行编译后:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38"mstore.c"
multstore
multstore, @function
multstore:
.LFB0:
endbr64
pushq %rbx
16
3, -16
movq %rdx, %rbx
call mult2@PLT
movq %rax, (%rbx)
popq %rbx
8
ret
.LFE0:
multstore, .-multstore
"GCC: (Ubuntu 10.2.0-13ubuntu1) 10.2.0"
.GNU-stack,"",@progbits .note
.gnu.property,"a" .note
8
1f - 0f
4f - 1f
5
0:
"GNU"
1:
8
0xc0000002
3f - 2f
2:
0x3
3:
8
4:以“.”开头的都是指导汇编器和链接器工作的伪指令,可以忽略。
汇编(Assemble):将汇编代码转换成机器代码,得到的是二进制的目标文件。
使用gcc -c mstore.s -o mstore.o
进行汇编;因为二进制文件无法直接查看,因此可以使用objdump -d mstore.o
反汇编进行查看:1
2
3
4
5
6
7
8
9
10
11
12
13mstore.o: 文件格式 elf64-x86-64
Disassembly of .text:
0000000000000000 <multstore>:
0: f3 0f 1e fa endbr64
4: 53 push %rbx
5: 48 89 d3 mov %rdx,%rbx
8: e8 00 00 00 00 callq d <multstore+0xd>
d: 48 89 03 mov %rax,(%rbx)
10: 5b pop %rbx
11: c3 retq链接(Linking):链接多个目标文件以及库文件等,最终得到可执行文件。
总结这些只是为了了解C代码是如何变成机器代码,并最终被CPU执行的。当然我可以一步到位,直接由C代码得到可执行程序:gcc mstore.c -o mstore