本篇文章所有摘抄自学长博客供之后学习:linux
排版因与博客园编辑器不一样而稍做修改。编程
输出hello world!系统发生了什么?网络
经典的hello world!编辑器
1 #include
2
3 intmain ()4 {5 printf("hello world!");6 }
该段程序,在hello world过程当中,系统发生了什么?函数
0X00 新建hello.c学习
hello.c文件,是文件由0/1的位(bit)序列,8位组成一组,称为字节,一个个字节表示文件中的一个个字符。ui
由此引出,系统中的全部信息(磁盘文件,系统程序,网络传输的数据等等),都是一些0/1序列,当咱们打开hello.c文件时,系统会按某种规则文件进行解析,最后呈现出人类能看懂的字符,而不是二进制0/1。编码
区分不一样数据对象的惟一方法,就是系统读取这个文件时上下文(能够理解为当时的环境),好比,在不一样的上下文中,一个一样的字节序列,有可能表示一个整数,浮点数,字符数或者机器指令。spa
0x01 解析hello.c
C语言是高级语言,因此这个形式的代码能让人读懂。但系统不认识,为了让系统能认识,须要将hello.c转化成一系机器能认识的语言指令,而后将这些指令按照一种称为可执行目标程序的格式进行打包,并以二进制磁盘文件形式存放,目标程序也能够称为可执行文件。
这里,我用GCC编译器,编译解析hello.c
预处理阶段:-E
预处理器(cpp)主要处理根据以字符#开头的命令,修改原始的C程序。好比hello.c中第一行的#include 命令会告诉预处理其读区系统头文件stdio.h的内容.并把它直接插入到程序文本中。此过程,会获得以.i做为扩展名。
`$ gcc -E hello.c -o hello.i`//预编译hello.c文件 输出hello.i文件
hello.i文件内容都有些什么呢?
1 //hello.i
2 # 1 "hello.c"
3 # 1 "" 1
4 # 1 "" 3
5 # 325 "" 3
6 # 1 "" 1
7 # 1 "" 2
8 # 1 "hello.c" 2
9 # 1 "/usr/include/stdio.h" 1 3 4
10 # 64 "/usr/include/stdio.h" 3 4
11 # 1 "/usr/include/sys/cdefs.h" 1 3 4
12 # 533 "/usr/include/sys/cdefs.h" 3 4
13 # 1 "/usr/include/sys/_symbol_aliasing.h" 1 3 4
14 # 534 "/usr/include/sys/cdefs.h" 2 3 4
15 # 599 "/usr/include/sys/cdefs.h" 3 4
16 # 1 "/usr/include/sys/_posix_availability.h" 1 3 4
17 # 600 "/usr/include/sys/cdefs.h" 2 3 4
18 # 65 "/usr/include/stdio.h" 2 3 4
19 # 1 "/usr/include/Availability.h" 1 3 4
20 # 162 "/usr/include/Availability.h" 3 4
21 # 1 "/usr/include/AvailabilityInternal.h" 1 3 4
22 # 163 "/usr/include/Availability.h" 2 3 4
23 # 66 "/usr/include/stdio.h" 2 3 4
24 # 1 "/usr/include/_types.h" 1 3 4
25 # 27 "/usr/include/_types.h" 3 4
26 # 1 "/usr/include/sys/_types.h" 1 3 4
27 # 33 "/usr/include/sys/_types.h" 3 4
28 # 1 "/usr/include/machine/_types.h" 1 3 4
29 # 32 "/usr/include/machine/_types.h" 3 4
30 # 1 "/usr/include/i386/_types.h" 1 3 4
31 # 37 "/usr/include/i386/_types.h" 3 4
32 typedef signed char__int8_t;33 typedef unsigned char__uint8_t;34 typedef short__int16_t;35 typedef unsigned short__uint16_t;36 typedef int__int32_t;37 typedef unsigned int__uint32_t;38 typedef long long__int64_t;39 typedef unsigned long long__uint64_t;40 typedef long__darwin_intptr_t;41 typedef unsigned int__darwin_natural_t;42 # 70 "/usr/include/i386/_types.h" 3 4
43 typedef int __darwin_ct_rune_t;
编译阶段:-S
编译器(ccl)将hello.i翻译成文件文件hello.s文件,它包含一个汇编语言程序,汇编程序中的每条语句都以一种标准的文本格式确切地描述了一条条低级机器语言指令.因此该过程会检查代码规范,语法,词法分析,具体以下图.只有编译成功以后,才能生成具体的汇编代码。
$ gcc -S hello.i -o hello.s
1 //hello.s
2 .section __TEXT,__text,regular,pure_instructions3 .macosx_version_min 10, 11
4 .globl _main5 .align 4, 0x90
6 _main: ## @main7 .cfi_startproc8 ## BB#0:9 pushq %rbp10 Ltmp0:11 .cfi_def_cfa_offset 16
12 Ltmp1:13 .cfi_offset %rbp, -16
14 movq %rsp, %rbp15 Ltmp2:16 .cfi_def_cfa_register %rbp17 subq $16, %rsp18 leaq L_.str(%rip), %rdi19 movl $0, -4(%rbp)20 movb $0, %al21 callq _printf22 xorl %ecx, %ecx23 movl %eax, -8(%rbp) ## 4-byteSpill24 movl %ecx, %eax25 addq $16, %rsp26 popq %rbp27 retq28 .cfi_endproc29
30 .section __TEXT,__cstring,cstring_literals31 L_.str: ## @.str32 .asciz "hello world!"
33 .subsections_via_symbols
汇编阶段:-c
汇编器(as)将hello.s 文件翻译成机器语言指令,,把这些指令打包成一种叫作可重定向目标程序的格式,而且保存在hello.o文件中.该文件是一个二进制文件,他的字节编码是机器语言指令而不是字符,若是用编辑器打开将是一段乱码。
连接阶段:
注意,hello程序调用了printf函数,他是每一个C编译器都会提供的标准C库的一个函数.该函数存在于一个名为printf.o的单独的预编译好的文件中,必须将该文件以某种方式合并到咱们的hello.o的文件中。链接器(ld),就复制处理这种合并。结果就获得一个hello文件,是一个可执行文件。