计算机工作原理
一、存储程序计算机工作模型
- 冯·诺依曼体系结构几乎是所有计算机平台的基础,由CPU(运算器、控制器、存储器)、内存以及I/O设备组成,它们之间是用总线连接起来的。
- 在计算机内部采用二进制来表示指令和数据,都可以存储到内存中。
- API:程序员与计算机的接口界面。ABI:程序与CPU的接口界面
- 在x86-32的计算机中,EIP是指向指令地址的一个指针,它是自动加一(这里的一是指一条指令的大小),其值可以被call、ret、jmp等指令修改。
二、x86-32汇编基础
- 所有以E开头的寄存器,一般都是32位的。
- 我们经常能用到的寄存器如下:
- EAX:累加寄存器
- EBX:基址寄存器
- ECX:计数寄存器
- EDX:数据寄存器
- EBP:堆栈基址地址
- ESP:堆栈栈顶寄存器
- EIP:指令指针寄存器
- 32位的寄存器中,EAX、EBX、ECX、EDX不仅可以传送数据、暂存数据保存算术逻辑运算结果,还可以作为指针寄存器,具备很好的通用性。
- 寻址方式有寄存器寻址、立即寻址、直接寻址、间接寻址、变址寻址。其中寄存器寻址和立即寻址和内存没有关系。
- 32位寄存器中,32位数称为双字;64位寄存器中,64位数称为四字。
三、汇编一个C程序,并分析其汇编指令
在实验楼的实验品平台上,先创建一个main.c文件
先通过gcc编译该main.c文件得到a.out,在通过./a.out以及echo $?来查看结果。
再通过gcc -S -o main.s main.c -m32得到32位的x86环境中的汇编代码文件main.s
在通过简化main.s文件(删除.开头的语句)之后,得到简化版main.s汇编文件
在这个main.s汇编文件中,主要用到了pushl、popl、movl、addl、subl、call、ret、leave指令。其中,程序从main函数开始:
第一条指令pushl %ebp,将EBP寄存器的值进行压栈。ESP的地址减4,将EBP寄存器的值放到栈顶,并且EIP的值加一,指向movl %esp,%esp。
第二条指令movl %esp,%ebp,将EBP指向ESP所指的位置,并且EIP的值加一,指向subl $4,%esp。
第三条指令subl $4,%esp,ESP寄存器减4,并且EIP的值加一,指向movl $8,(%esp)。
第四条指令指向movl $8,(%esp),将立即数8放入到ESP所指向的位置,为即将调用的f函数做准备,并且EIP的值加一,指向call f。
第五条指令call f,把EIP的值先压倒栈顶,再将函数f的第一条指令pusjl %ebp位置放到EIP中。
第六条指令pusjl %ebp,将EBP寄存器的值进行压栈。ESP的地址减4,将EBP寄存器的值放到栈顶,并且EIP的值加一,指向movl %esp,%ebp。
第七条指令movl %esp,%ebp,将EBP指向ESP所指的位置,并且EIP的值加一,指向subl $4,%esp。(所有函数的头两条指令指令用于初始化函数自己的函数调用堆栈空间)
第八条指令subl $4,%esp,ESP寄存器减4,并且EIP的值加一,指向movl $8(%ebp),%eax。
第九条指令movl $8(%ebp),%eax,变址寻址,EBP寄存器的值加8,指向立即数为8的位置,将立即数8放到了EAX寄存器中,并且EIP的值加一,指向movl %eax,(%esp)。
第十条指令movl %eax,(%esp),将EAX中存储的立即数8放到ESP所指的位置,并且EIP的值加一,指向call g。
第十一条指令call g,把EIP的值先压倒栈顶,再将函数g的第一条指令pusjl %ebp位置放到EIP中。
第十二条指令pusjl %ebp,将EBP寄存器的值进行压栈。ESP的地址减4,将EBP寄存器的值放到栈顶,并且EIP的值加一,指向movl %esp,%ebp。
第十三条指令movl %esp,%ebp,将EBP指向ESP所指的位置,并且EIP的值加一,指向8(%ebp),%eax。
第十四条指令8(%ebp),%eax,变址寻址,EBP寄存器的值加8,指向立即数为8的位置,将立即数8放到了EAX寄存器中,并且EIP的值加一,指向add $70,%eax。
第十五条指令add $70,%eax,将立即数70加到EAX里面,EAX的值就是70+8为78,并且EIP的值加一,指向popl %ebp。
第十六条指令popl %ebp,恢复函数f的函数调用堆栈基址EBP寄存器,ESP加4个字节,并且EIP的值加一,指向ret。
第十七条指令ret,将ESP寄存器所指向的栈空间存储单元放到EIP寄存器中,即EIP指向指令函数f的ret中。
第十八条指令ret,将ESP寄存器所指向的栈空间存储单元放到EIP寄存器中,即EIP指向指令函数main中的add $12,%eax.
第十九条指令add $12,%eax,把EAX寄存器立即数加12,也就是78+12为90。EAX存储器是默认存储函数返回值的寄存器,并且EIP的值加一,指向leave。
第二十条指令leave,,撤销函数main的堆栈,并且EIP的值加一,指向ret。
第二十一条指令ret,此时,堆栈空间已经回到了main函数开始执行之前的状态,ESP和EBP也恢复了初始状态。
整个流程中,函数通过调用堆栈框架暂存函数上下文状态信息,整个程序的执行过程编程了一个指令流,从CPU中“流”了一遍。