一.环境:
x86/WinXP/VC 6.0
二.用例:int swap(int a, int b){ int v; v = a; a = b; b = v; return v;}
void main(void){ int a = 7; int b = 10; int c = 0; c = swap(a,b); return;}
三.分析:1: int swap(int a, int b)2: {00401020 push ebp00401021 mov ebp,esp00401023 sub esp,44h00401026 push ebx00401027 push esi00401028 push edi00401029 lea edi,[ebp-44h]0040102C mov ecx,11h00401031 mov eax,0CCCCCCCCh00401036 rep stos dword ptr [edi]3: int v;4: v = a;00401038 mov eax,dword ptr [ebp+8]0040103B mov dword ptr [ebp-4],eax5: a = b;0040103E mov ecx,dword ptr [ebp+0Ch]00401041 mov dword ptr [ebp+8],ecx6: b = v;00401044 mov edx,dword ptr [ebp-4]00401047 mov dword ptr [ebp+0Ch],edx7: return v;0040104A mov eax,dword ptr [ebp-4]8: }0040104D pop edi0040104E pop esi0040104F pop ebx00401050 mov esp,ebp00401052 pop ebp00401053 ret
swap函数内部参数处理: 1.将基址指针EBP压栈; 2.将堆栈指针ESP拷贝一份到EBP中; 3.将ESP值减0x44(为了将这68个字节填充为0xCC); 4.将EBX/ESI/EDI压栈; 5.将EBP-0x44的地址偏移量存到目标地址指针EDI; 6.将计数器ECX置为0x11(即17个整数); 7.将EDI所开始的大小为17的内存空间填充为0xCCCCCCCC; 8.取出堆栈中被压的a的值(EBP+8所存的值)放到EAX中; 9.将EAX中的值(参数a的值)赋给到v; 10.取出堆栈中被压的b的值(EBP+0CH所存的值)放到ECX中; 11.将ECX中的值(参数b的值)赋给到a; 12.取出堆栈中被压的b的值(EBP-4所存的值)放到EDX中; 13.将EDX中的值(变量v的值)赋给到a; 14.将返回值(v的值)拷贝到EAX中; 15.恢复EDI/ESI/EBX; 16.恢复ESP为EBP(即进入swap时的ESP); 17.恢复EBP为调用swap前的值; 18.swap函数返回;
10: void main(void)11: {00401070 push ebp00401071 mov ebp,esp00401073 sub esp,4Ch00401076 push ebx00401077 push esi00401078 push edi00401079 lea edi,[ebp-4Ch]0040107C mov ecx,13h00401081 mov eax,0CCCCCCCCh00401086 rep stos dword ptr [edi]12: int a = 7;00401088 mov dword ptr [ebp-4],713: int b = 10;0040108F mov dword ptr [ebp-8],0Ah14: int c = 0;00401096 mov dword ptr [ebp-0Ch],015: c = swap(a,b);0040109D mov eax,dword ptr [ebp-8]004010A0 push eax004010A1 mov ecx,dword ptr [ebp-4]004010A4 push ecx004010A5 call @ILT+5(_swap) (0040100a)004010AA add esp,8004010AD mov dword ptr [ebp-0Ch],eax16: return;17: }004010B0 pop edi004010B1 pop esi004010B2 pop ebx004010B3 add esp,4Ch004010B6 cmp ebp,esp004010B8 call __chkesp (004010e0)004010BD mov esp,ebp004010BF pop ebp004010C0 ret
15: c = swap(a,b);0040109D mov eax,dword ptr [ebp-8]004010A0 push eax004010A1 mov ecx,dword ptr [ebp-4]004010A4 push ecx004010A5 call @ILT+5(_swap) (0040100a)004010AA add esp,8004010AD mov dword ptr [ebp-0Ch],eax分析函数swap调用过程: 1.将参数值b拷到EAX; 2.将EAX值压栈; 3.将参数值a拷到ECX; 4.将ECX值压栈; 5.CALL swap函数 6.将堆栈指针加8,丢弃堆栈中a,b的值; 7.将EAX中的返回值拷贝到变量c;
调用swap时的堆栈情况:
0012FEC8 | EDI |<-- ESP (执行到 v = a语句时堆栈指针) 0012FECC | ESI | 0012FED0 | EBX | 0012FED4 | 0xCCCCCCCC | | . | | . | | . | 0012FF14 | 0xCCCCCCCC | 0012FF18 | EBP |<-- EBP (执行到 v = a语句时堆栈指针) 0012FF1C | 0x004010AA | 执行完调用swap后的返回地址 0012FF20 | ECX | a的值 0012FF24 | EAX | b的值
四.总结:
1.实参压栈过程是从右至左,然后压入函数调用后的返回地址;2.被调用的函数从堆栈中取实参值;3.如果采用编译器进行优化,那么被调用的函数内部是从寄存器还是从堆栈中取实参值?具体情况根据CPU架构和参数个数有关(可以继续分析哦!);4.如果函数参数个数超过较多(如果超过5个参数),函数参数如何传递呢?5.函数的返回值一般会存在一个寄存器,如EAX.
本文转自:http://blog.csdn.net/zengwh/archive/2007/03/24/1539486.aspx