从最简单的add函数调用过程分析函数栈桢创建和销毁的过程

来源:互联网 发布:数据挖掘公司排名 编辑:程序博客网 时间:2024/05/20 02:21

这是一个简单的add()函数,我们可以很直接的看出它的调用过程和运行结果;但是对于机器来说,要怎样来执行这个程序呢,这个过程又是怎样的呢。
在编译器(vs2013)中运行这个程序,我们可以从生成的汇编代码来逐步分析这个函数运行的过程;截取部分反汇编,我们来进行逐句分析。

#include<stdio.h>#include<stdlib.h>int add(int x, int y){    int ret = 0;    ret = x + y;    return ret;}int main(){    int a = 20;    int b = 10;    int ret = 0;    int ret = add(a, b);    ststem("pause");    return 0;}

在main函数之前,系统会用—mainCRStartup 来调用main函数,所以main函数的调用过程为:
这里写图片描述
1:ebp和esp是用来维护函数栈底和栈顶的指针;ebp和esp一开始维护的是为-mainCRTStartup这个函数开辟的空间,esp指向栈顶,ebp指向栈底;在main函数一被调用的时候,push ebp,就是压栈,在栈顶开辟一片空间,用来存放指向-mainCRTStartup栈底的指针ebp,这时候esp指向新开辟空间的栈顶,因为esp总是指向栈顶的;
2:move ebp,esp 意思是把esp的内容给ebp,ebp存了esp里面的内容,所以现在也指向栈顶空间;
3:esp减去0E4h大小的值,因为栈空间是从高地址向低地址走的,所以这个步骤就是在ebp的上面开辟了0E4h这个大小的一个空间;
4:push ebx, push esi, push edi,分别进行三次压栈,随着空间向上不断增长,esp也随之向上挪动,因为esp总是指向栈顶;
5:lea 加载有效地址 ,把ebp-0E4h这个地址放在edi这个寄存器中;为后续ret的执行做准备;
6:把 39h存到ecx里面;
7:把0cccccccch存到eax这个寄存器中;
8:rep 重复的意思,dword就是doubleword; 6 7 8表示的意思是按双字从ebp-0E4h往下拷贝39h次cccccccc;;因为edi中存放的就是ebp-0E4h的地址;
这个步骤可以理解为初始化过程;
这里写图片描述

9:把0Ah双字拷贝到ebp-8指向的这个地址空间;
10:把14h双字拷贝到ebp-14h里面;其实这两个过程就是局部变量的创建和初始化,所以我们在创建局部变量是如果不初始化的话,里面的内容就是cccccccc,就是随机值得 产生;
11:为调用add()函数做准备;把ebp-14h的内容也就是b的值20双字拷贝到寄存器eax里面;push eax,然后进行压栈,esp指向存放寄存eax的空间地址;
12:把ebp-8的内容也就是a的值10双字拷贝到ecx里面,然后进行压栈;
13:call指令,调用函数,在执行call指令同时,在栈顶ecx又开辟了一开新空间,用于存放call指令下一条指令的地址;这个地址的作用是call指令调用add()函数结束时jump指令能够找到call指令下一条指令的地址,从而回到main函数中;这个很重要,不然函数就不知回到哪里了。
这里写图片描述

14:add()函数调用的第一步 push ebp 就是 压栈 ,注意这个时候压的是main函数的ebp的地址;
15:这个时候add()函数空间的开辟和初始化和main函数的步骤一样;

16:把0双字拷贝到 ebp-8这个空间里面去(创建临时变量ret 且初始化为0);
17:ebp+8 就是ecx中保存的a的值;这一步就是把a的值拷贝到eax这个寄存器中间去;
18:ebp+0ch也就是b的值,这步骤就是把b的值加上a的值加起来放在eax里面;
19:再把eax里面的值拷给ebp-8也就是ret里面,ret的值变为30 ,再将ret的值放回到eax中,这表示一个返回机制,ret的值将由eax带回到main函数中,不然add()函数销毁了,这个值就不存在了;
这里写图片描述
20:接着就开始执行三次pop指令,pop是弹出的意思,将edi,esi,ebx这三块空间弹出,不并不是代表着三块空间不存在了,只是他们不属于add()函数了;
21:将ebp给esp,意味着esp回到ebp所指向的地址,刚刚创建的add函数的空间瞬间被销毁;
22:pop ebp 这里弹出的ebp是main函数的ebp,所以ebp栈底指针回到main函数ebp的位置:;
23:ret是指重复的意思,继续执行pop指令,pop这次弹出的是call指令,call指令面存放的是call指令的下一条指令的地址;所以ret执行按完成后,程序回到call指令下一条指令的地址;
这里写图片描述
24:call指令的下一条指令就是esp+8;把esp向下挪动8个字节,所以形参实例化的两个空间ecx,eax被销毁;10和20 倍弹出去;
25:把eax带回来的值付给ebp-20;也就是ret所在的空间,此时ret复制为30,到此add该函数的调用完全结束,下面是system的调用,我们就不看了;
26:xor是异或的意思,把两个相同的eax异或,是归零的操作,因为此时寄存器已经没有了用处,需要归零把它还给硬盘
这里写图片描述
27:main函数栈桢的销毁过程和add函数差不多;直到esp和ebp回到-mainCRTStartup中相应的位置,整个程序调用结束;

整个过程用图片表带如下:
这里写图片描述

阅读全文
1 0