SA****6343 孙洪菠 信息安全
一、C程序的编译过程
1.1 C程序的编译过程:
GCC接受example.c作为输入,最后生成可执行代码example的看似简单的流程所经历的复杂步骤
编译预处理:读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理;
编译阶段:通过词法分析和语法分析,在确认所有的指令都符合语法规则后,将其翻译成等价的中间代码表示或汇编代码;
汇编阶段:把汇编代码翻译成目标机器指令;
链接:将有关的目标文件彼此连接。
1.2 源文件Example.c
1 //Example.c 2 3 int g(int x) 4 { 5 return x + 3; 6 } 7 8 int f(int x) 9 { 10 return g(x); 11 } 12 13 int main() 14 { 15 return f(8)+1; 16 }
1.3 预处理后的程序Example.cpp
命令:gcc -E -o Exmple.cpp example
1 # 1 "Example.c" 2 # 1 "<built-in>" 3 # 1 "<command-line>" 4 # 1 "Example.c" 5 6 7 int g(int x) 8 { 9 return x + 3; 10 } 11 12 int f(int x) 13 { 14 return g(x); 15 } 16 17 int main() 18 { 19 return f(8)+1; 20 }
1.4 编译后的汇编程序Example.s
命令:gcc -x cpp-output -S -o example.s example.cpp 或者 gcc -S -o Example.c Example.c
1 .file "Example.c" 2 .text 3 .globl g 4 .type g, @function 5 g: 6 pushl %ebp 7 movl %esp, %ebp 8 movl 8(%ebp), %eax 9 addl $3, %eax 10 popl %ebp 11 ret 12 .size g, .-g 13 .globl f 14 .type f, @function 15 f: 16 pushl %ebp 17 movl %esp, %ebp 18 subl $4, %esp 19 movl 8(%ebp), %eax 20 movl %eax, (%esp) 21 call g 22 leave 23 ret 24 .size f, .-f 25 .globl main 26 .type main, @function 27 main: 28 leal 4(%esp), %ecx 29 andl $-16, %esp 30 pushl -4(%ecx) 31 pushl %ebp 32 movl %esp, %ebp 33 pushl %ecx 34 subl $4, %esp 35 movl $8, (%esp) 36 call f 37 addl $1, %eax 38 addl $4, %esp 39 popl %ecx 40 popl %ebp 41 leal -4(%ecx), %esp 42 ret 43 .size main, .-main 44 .ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3" 45 .section .note.GNU-stack,"",@progbits
1.5 汇编成目标代码Example.o
命令:gcc –x assembler –c example.s -o example.o 或者 gcc –c example.c -o example.o
1.6 连接成二进制可执文件Example
命令:gcc -o example example.o 或者 gcc -o example example.c
1.7 执行
命令:./Example
二、汇编代码在cpu上执行过程中,堆栈的变化
汇编代码Example.s在cpu上执行过程中,堆栈如何实现函数调用和返回的?
三条特殊的汇编指令对应的动作:
call 0x12345 : pushl %eip(*);
movl $0x12345 , %eip(*)
leave: movl %ebp , %esp;
popl %ebp
ret: pop %eip(*)
在参考了《程序员自我修养》内存一章的绘图之后,我对上诉汇编代码的做如下分析:
虚线指向该指令执行后的栈状态,实线表示程序的跳转情况。
图中SS段的Initialize代表堆栈中框架的建立,对应的两条汇编语句为:push %ebp ;
mov %esp , %ebp .
ebp1,ebp2,ebp3 分别表示 main栈、f栈、g栈的栈底指针。
函数的执行过程中堆栈的变化
三、计算机是如何工作的
单任务计算机是怎样工作的:计算机的最小模型由cpu和内存组成,对于单任务,计算机先将机器码加载入内存,然后控制器将eip所指向的内容取出即取指,然后顺序执行。执行过程中遇到控制指令,可能跳转。在指令执行过程中,如果遇到函数调用,要借助堆栈来实现,先将调用栈基地址压入堆栈,再压入调用函数的返回地址(下一条指令的地址即当前eip中的内容),此时的栈顶esp作为被调用函数栈的栈底ebp1,之后进入被调用函数继续执行。函数返回时栈的操作是相反的过程。通常函数的返回值由eax寄存器来保存。
多任务计算机是怎样工作的:多任务的顺利工作要借助中断,进程间调度机制时间片轮转方法等来实现多任务在计算机上工作,在多任务计算机工作过程中,由于要在多个任务间进行切换,所以由单任务计算机工作中函数调用得到启发。多任务的切换类似于单任务中函数的调用。在各个任务之间进行切换之前,要保存前一个任务的栈顶,eip,标志位等信息以及保护现场,以保证该任务再次获得cpu时能够继续执行。就像单任务工作时函数调用后能正常返回继续去执行调用函数下一条指令一样。