C函数调用过程分析

    技术2022-05-19  21

    一.环境:

        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


    最新回复(0)