四、函数参数的传递规则
函数参数的传递规则
函数传递的规则现在一般有两种,一种是WINAPI方式,也就是_stdcall还有一种是_cdecl方式
_stdcall调用规则:
(1)参数从右到左进入堆栈
(2)被调用者在返回前自动清理堆栈
_cdecl C方式调用规则:
(1)参数从右到左进入堆栈
(2)由调用者负责清理堆栈
生成的程序代码一般会比_stdcall的大。
C编译器默认采用_cdecl方式,而windows API默认采用_stdcall。
在windows中,无论哪种方式,返回值都放在eax中,然后返回,外部从eax中得到返回值。
_cdecl使用的一般过程:
(1)保持ebp 。ebp总是被我们用来保存这个函数执行前esp的值,执行函数完毕后,用来恢复esp。当然,这个函数的上层函数也是这样做的,所以我们要先把ebp压入堆栈,避免ebp被我们改动。
(2)保存esp到ebp中
上面的两步的代码如下:
push ebp
mov ebp,esp
(3)为局部变量申请空间,一般是用sub esp,空间大小 来进行分配,相当于压入堆栈。完成后记得平衡堆栈
(4)保存其它寄存器的值。ebx、esi、edi压入堆栈,调用完后恢复。
sub esp,0cch
push ebp
push esi
push edi
(5)将局部变量区域初始化为0cccccccch,也就是int3指令的机器码。
lea edi,[ebp-0cch]
mov ecx,33h
mov eax,0cccccccch
rep stos dword ptr [edi]
(6)做函数要做的事
(7)恢复ebx、esi、edi、esp、ebp,然后返回
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
对应的c代码:
1 #include "stdafx.h" 2 3 4 int _tmain(int argc, _TCHAR* argv[]) 5 { 6 void fun(int a,int b); 7 fun(11,22); 8 return 0; 9 } 10 11 void fun(int a,int b) 12 { 13 int c=a+b; 14 }
对应的函数汇编代码
1 void fun(int a,int b) 2 { 3 00411780 push ebp 4 00411781 mov ebp,esp 5 00411783 sub esp,0CCh 6 00411789 push ebx 7 0041178A push esi 8 0041178B push edi 9 0041178C lea edi,[ebp-0CCh] 10 00411792 mov ecx,33h 11 00411797 mov eax,0CCCCCCCCh 12 0041179C rep stos dword ptr es:[edi] 13 int c=a+b; 14 0041179E mov eax,dword ptr [a] 15 004117A1 add eax,dword ptr [b] 16 004117A4 mov dword ptr [c],eax 17 } 18 004117A7 pop edi 19 004117A8 pop esi 20 004117A9 pop ebx 21 004117AA mov esp,ebp 22 004117AC pop ebp 23 004117AD ret
..................................