函数栈帧的创建和销毁
Posted 北川_
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了函数栈帧的创建和销毁相关的知识,希望对你有一定的参考价值。
目录
各种寄存器的作用
eax是“累加器”(accumulator),它是很多加法乘法指令的缺省寄存器
ebx是“基地址”(base)寄存器,在内存寻址时存放基地址
ecx是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器。
edx:总是被用来放整数除法产生的余数。
esp:寄存器存放当前线程的栈顶指针
ebp:寄存器存放当前线程的栈底指针
main()函数的调用
VS2013中mainCRTStartup()函数内部调用__tmainCRTStartup()函数,
__tmainCRTStartup()函数内部调用main()函数
通过汇编观察函数调用过程
int Add(int x, int y)
int z = 0;
z = x + y;
return z;
int main(void)
int a = 10;
int b = 20;
int c = 0;
c = Add(a, b);
printf("%d\\n", c);
return 0;
汇编代码
int main(void)
00301900 push ebp
00301901 mov ebp,esp
00301903 sub esp,0E4h
00301909 push ebx
0030190A push esi
0030190B push edi
0030190C lea edi,[ebp+FFFFFF1Ch]
00301912 mov ecx,39h
00301917 mov eax,0CCCCCCCCh
0030191C rep stos dword ptr es:[edi]
0030191E mov ecx,30C003h
00301923 call 0030132A
int a = 10;
00301928 mov dword ptr [ebp-8],0Ah
int b = 20;
0030192F mov dword ptr [ebp-14h],14h
int c = 0;
00301936 mov dword ptr [ebp-20h],0
c = Add(a, b);
0030193D mov eax,dword ptr [ebp-14h]
00301940 push eax
00301941 mov ecx,dword ptr [ebp-8]
00301944 push ecx
00301945 call 003010B9
0030194A add esp,8
0030194D mov dword ptr [ebp-20h],eax
printf("%d\\n", c);
00301950 mov eax,dword ptr [ebp-20h]
00301953 push eax
00301954 push 307B30h
00301959 call 003010D7
0030195E add esp,8
return 0;
00301961 xor eax,eax
上面提到main函数也是被调用的,所以当代码走到main函数中时,调用main函数的函数的栈帧已经被创建好了。
main()函数栈帧开辟过程
第3行把ebp压栈,第4行把esp的值赋给ebp,第5行esp减去0E4h,此时esp和ebp之间就是main函数的栈帧
6-8行把ebx、esi、edi压栈
lea表示load effective address加载有效地址
第9行到第12行将main函数栈帧的内容全部初始化位cccccccc
此时main函数栈帧开辟好了。
第16行将ebp-8的位置初始化为10,这是变量a的空间,18行将ebp-14h的位置赋值给20,为变量b,20行ebp-20h的位置赋值为0,为变量c
接下来第22行把ebp-14h的值(也就是b的20)赋给eax,并把eax压栈,把ebp-8的值(也就是a的10)赋给ecx,把ecx压栈。然后第26行call指令调用Add()函数,在调用call指令时会把call指令的下一条指令的地址压栈
Add()函数栈帧开辟过程
接下来调用Add()函数
Add()函数的汇编
int Add(int x, int y)
00301790 push ebp
00301791 mov ebp,esp
00301793 sub esp,0CCh
00301799 push ebx
0030179A push esi
0030179B push edi
0030179C lea edi,[ebp+FFFFFF34h]
003017A2 mov ecx,33h
003017A7 mov eax,0CCCCCCCCh
003017AC rep stos dword ptr es:[edi]
003017AE mov ecx,30C003h
003017B3 call 0030132A
int z = 0;
003017B8 mov dword ptr [ebp-8],0
z = x + y;
003017BF mov eax,dword ptr [ebp+8]
003017C2 add eax,dword ptr [ebp+0Ch]
003017C5 mov dword ptr [ebp-8],eax
return z;
003017C8 mov eax,dword ptr [ebp-8]
003017CB pop edi
003017CC pop esi
003017CD pop ebx
003017CE add esp,0CCh
003017D4 cmp ebp,esp
003017D6 call 0030124E
003017DB mov esp,ebp
003017DD pop ebp
003017DE ret
从第3行开始的一系列操作和上面的main函数中一样,依然是为函数开辟栈帧,并且把Add()函数栈帧空间初始化为cccccccc。
第16行把ebp-8的位置赋值为0,作为变量z的空间,18行把ebp+8位置的值放到eax中,19行把ebp+12的位置的值放到eax中,完成了两个变量的相加
第20行把eax中的30放到ebp-8也就是变量z的空间
return z,局部变量z在函数执行结束后销毁了,所以第22行的意思是把放在z变量中的两个数求和的结果保存在eax中。
Add()函数栈帧销毁过程
第24-26行把edi、esi、ebx出栈,然后把ebp赋给esp,pop掉ebp让ebp重新指向main函数栈底的位置,ret返回到之前保存的call指令的下一条指令的地址,自此Add函数调用结束,继续执行main函数
返回main函数后形参x和形参y没用了,把他们也出栈
过程如下:
回到main()函数后把eax中保存的30赋给ebp-20也就是c,此时完成了Add()函数栈帧的创建,传递形参,求值并保存在eax中,Add函数销毁,将函数返回值赋给变量c的一系列操作。
以上是关于函数栈帧的创建和销毁的主要内容,如果未能解决你的问题,请参考以下文章