在一个函数调用另一个函数过程中到底发生了什么?
举例,函数A调用了函数B,形如
int A(void)
{
int i=B(int arg1,int arg2);
return i;
}
我想描述下在调用B这个过程中所发生的故事:
这里是cdcel的调用方式
1,将传递给B的参数压入堆栈,压入顺序为从右向左,先将arg2压入,然后压入arg1
2,call B--调用函数B。这个过程会将B函数后执行后的地址,即return 的地址压入堆栈。然后前IP改为函数B的地址。开始执行B。
3,函数B在执行时,也要做些准备工作。
a,保存ebp,因为ebp总是被我们用来保存函数执行前,即A的esp值。执行完B后我们要用ebp恢复esp;同样A对它的上层函数也是一样的过程。
push ebp;
b,保存esp到ebp中。
mov ebp,esp
c,堆栈中通过减少esp的值来空出空间保存B的局部变量,比如大小为0c2h。
sub esp,0c2h
d,保存要使用到的寄存器值,如ebx,esi,edi
push ebx
push esi
push edi
d,将局部变量初始化为0cccccccch----0cch是int 3指令的机器码,这里把这部分空间初始为int 3主要是因为这些局部变量是不能被执行,但如果程序意外要执行它们就会引发一个调试中断来提示开发者。
lea edi,[ebp-0cch]
mov ecx,33h
mov eax,0cccccccc
rep stos dword ptr [edi] ;串写入
这里我们把从ebp-0cch开始的区域都初始化为0cch
d,开始执行函数体的正常功能。在执行中如果要取得A传递来的参数。参数的获取是ebp+12字节为第二个参数,ebp+8为第一个参数(倒序插入),依次增加。最后ebp+4正好是我们保存的返回地址。 最后在处理中我们要将函数的返回值,将返回值放在eax中,外部(A)通过eax得到返回的值。
e,恢复ebx,esi,edi,esp,ebp,最后返回。:
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
这样一个完整的函数调用就结束了,A继续执行。。。。
继续执行中。。。。。。。。。。。。。。。。。。。。。。。。。
还在执行中。。。。。。。。。。。。。。。。
快结束 了。。。。。。。。。。。。。。。!!!
马上就要结束 了。。。。。。。。。。。。。。。。。。
啊!函数A结束了
C函数继续执行中!!!!!!!!!!! - -!
: )
对于_stdcall方式的调用,堆栈是由被调用函数B在返回前自行处理的。