VC6对函数返回过程的优化(下)

1:    // test.cpp : Defines the entry point for the console application.
2:    //
3:
4:    #include "stdafx.h"
5:
6:    struct complex{
7:        int i; //x
8:        int j; //y
9:        int k; //z
10:       int t; //time. w=-ict
11:   };
12:
13:   complex test( int a, int b, int c , int d);
14:
15:   int main(int argc, char* argv[])
16:   {

0040D440   push        ebp
0040D441   mov         ebp,esp
0040D443   sub         esp,80h
0040D449   push        ebx
0040D44A   push        esi
0040D44B   push        edi
0040D44C   lea         edi,[ebp-80h]
0040D44F   mov         ecx,20h
0040D454   mov         eax,0CCCCCCCCh
0040D459   rep stos    dword ptr [edi]

17:       int a, b, c, d;
18:       struct complex z;
19:
20:       a = 0x11;
0040D45B   mov         dword ptr [ebp-4],11h
21:       b = 0x30;
0040D462   mov         dword ptr [ebp-8],30h
22:       c = 0x88;
0040D469   mov         dword ptr [ebp-0Ch],88h
23:       d = 0x9302;
0040D470   mov         dword ptr [ebp-10h],9302h
24:
25:       z = test( a, b, c, d);
0040D477   mov         eax,dword ptr [ebp-10h]
0040D47A   push        eax
0040D47B   mov         ecx,dword ptr [ebp-0Ch]
0040D47E   push        ecx
0040D47F   mov         edx,dword ptr [ebp-8]
0040D482   push        edx
0040D483   mov         eax,dword ptr [ebp-4]
0040D486   push        eax
0040D487   lea         ecx,[ebp-40h]
0040D48A   push        ecx
0040D48B   call        @ILT+15(test) (00401014)
0040D490   add         esp,14h
0040D493   mov         edx,dword ptr [eax]
0040D495   mov         dword ptr [ebp-30h],edx
0040D498   mov         ecx,dword ptr [eax+4]
0040D49B   mov         dword ptr [ebp-2Ch],ecx
0040D49E   mov         edx,dword ptr [eax+8]
0040D4A1   mov         dword ptr [ebp-28h],edx
0040D4A4   mov         eax,dword ptr [eax+0Ch]
0040D4A7   mov         dword ptr [ebp-24h],eax
0040D4AA   mov         ecx,dword ptr [ebp-30h]
0040D4AD   mov         dword ptr [ebp-20h],ecx
0040D4B0   mov         edx,dword ptr [ebp-2Ch]
0040D4B3   mov         dword ptr [ebp-1Ch],edx
0040D4B6   mov         eax,dword ptr [ebp-28h]
0040D4B9   mov         dword ptr [ebp-18h],eax
0040D4BC   mov         ecx,dword ptr [ebp-24h]
0040D4BF   mov         dword ptr [ebp-14h],ecx

26:
27:       return 0;
0040D4C2   xor         eax,eax
28:   }
0040D4C4   pop         edi
0040D4C5   pop         esi
0040D4C6   pop         ebx
0040D4C7   add         esp,80h
0040D4CD   cmp         ebp,esp
0040D4CF   call        __chkesp (0040d400)
0040D4D4   mov         esp,ebp
0040D4D6   pop         ebp
0040D4D7   ret

29:
30:
31:   struct complex test( int a, int b, int c, int d)
32:   {
0040D4E0   push        ebp
0040D4E1   mov         ebp,esp
0040D4E3   sub         esp,50h
0040D4E6   push        ebx
0040D4E7   push        esi
0040D4E8   push        edi
0040D4E9   lea         edi,[ebp-50h]
0040D4EC   mov         ecx,14h
0040D4F1   mov         eax,0CCCCCCCCh
0040D4F6   rep stos    dword ptr [edi]

33:       struct complex z;
34:       z.i = a;
0040D4F8   mov         eax,dword ptr [ebp+0Ch]
0040D4FB   mov         dword ptr [ebp-10h],eax

35:       z.j = b;
0040D4FE   mov         ecx,dword ptr [ebp+10h]
0040D501   mov         dword ptr [ebp-0Ch],ecx

36:       z.k = c;
0040D504   mov         edx,dword ptr [ebp+14h]
0040D507   mov         dword ptr [ebp-8],edx

37:       z.t = d;
0040D50A   mov         eax,dword ptr [ebp+18h]
0040D50D   mov         dword ptr [ebp-4],eax

38:
39:       return z;
0040D510   mov         ecx,dword ptr [ebp+8]
0040D513   mov         edx,dword ptr [ebp-10h]
0040D516   mov         dword ptr [ecx],edx
0040D518   mov         eax,dword ptr [ebp-0Ch]
0040D51B   mov         dword ptr [ecx+4],eax
0040D51E   mov         edx,dword ptr [ebp-8]
0040D521   mov         dword ptr [ecx+8],edx
0040D524   mov         eax,dword ptr [ebp-4]
0040D527   mov         dword ptr [ecx+0Ch],eax
0040D52A   mov         eax,dword ptr [ebp+8]

40:   }
0040D52D   pop         edi
0040D52E   pop         esi
0040D52F   pop         ebx
0040D530   mov         esp,ebp
0040D532   pop         ebp
0040D533   ret

哈哈,终于没办法优化了。现在的方法就是把常规的432bits的返回值放到stack里面去,指向这个返回值的地址放到EAX返回。

额外的话题,几种函数调用的方式:
★__cdecl
cb
的默认值,它会在输出函数名前加_,并保留此函数名不变,参数按照从右到左的顺序依次传递给栈,也可以写成_cdeclcdecl形式。和_stdcall最大的不同是,返回的时候栈由调用者(Caller)而非被调用子程序(Callee)来调整。这样的好处是支持变长度参数列表,因为Callee并不方便知道变量的长度。
★__fastcall
她修饰的函数的参数将尽可能地使用寄存器来处理,其函数名前加@,参数按照从左到右的顺序压栈;
★__pascal
它说明的函数名使用Pascal格式的命名约定。这时函数名全部大写。参数按照从左到右的顺序压栈;
★__stdcall
使用标准约定的函数名。函数名不会改变。使用__stdcall修饰时。参数按照由右到左的顺序压栈,也可以是_stdcall

对于_cdecl, 函数调用的堆栈变化的解说:
1.将函数参数入栈,第一个参数在栈顶,最后一个参数在栈底。
2.CALL指令下一行代码的地址入栈。
2.跳转--eip的值被重新设置为被调函数的起始地址。
3.进入函数代码空间后,将基址指针EBP入栈,然后让基址指针EBP指向当前堆栈栈顶,并使用它访问存在堆栈中的函数输入参数及堆栈中的其他数据。
4.sub espN
堆栈指针ESP减少一个值,如44H,向上移动一个距离,留出一个空间给该函数作为临时存储区。也就是说,子函数的变量都是存储在栈中。这里N是函数内局部变量的总字节数加上一个整数,一般为40。此后esp即为被调函数的堆栈指针了。
5.初始化esp ~ esp-N之间的N字节空间
这是对堆栈中已分配给局部变量使用的内存空间的初始化,一般全部设置为
0xcc
6. 顺序执行函数内语句。
此时函数的堆栈位于所有局部变量的内存空间之后,二者之间一般有
40字节的隔离带。
{
   //
以上准备工作做好后,函数正式被执行,如下所示。
   1.将其他指针或寄存器中的值入栈,以便在函数中使用这些寄存器。
   2.执行代码。
   3.执行return()返回执行结果,将要返回的值存入EAX中或栈中。
   4.步骤6.1中的指针出栈。
}
7.
EBP的值传给堆栈指针ESP,使ESP复原为2.c之前的值。此时进入函数时EBP的值在栈顶。
8.基址指针EBP出栈,复原为2.b之前的EBP的值。
9.执行RET指令,调用函数的地址出栈,本函数返回到CALL指令的下一行。
3.函数返回到CALL指令下一行,将堆栈指针加一个数值,以使堆栈指针恢复到以上步骤1执行之前的值。该数值是上面第一步入栈参数的总长度。

最后的那个check ESP的函数我也打印出来,其实就是检查堆栈调整后EBP是否和ESP相等。如果不等,调用错误处理代码。
__chkesp:
0040D400   jne         __chkesp+3 (0040d403)
0040D402   ret
0040D403   push        ebp
0040D404   mov         ebp,esp
0040D406   sub         esp,0
0040D409   push        eax
0040D40A   push        edx
0040D40B   push        ebx
0040D40C   push        esi
0040D40D   push        edi
0040D40E   push        offset string "The value of ESP was not properl"... (00422e90)
0040D413   push        offset string "" (00422c50)
0040D418   push        2Ah
0040D41A   push        offset string "i386//chkesp.c" (00422e80)
0040D41F   push        1
0040D421   call        _CrtDbgReport (00408b10)
0040D426   add         esp,14h
0040D429   cmp         eax,1
0040D42C   jne         __chkesp+2Fh (0040d42f)
0040D42E   int         3
0040D42F   pop         edi
0040D430   pop         esi
0040D431   pop         ebx
0040D432   pop         edx
0040D433   pop         eax
0040D434   mov         esp,ebp
0040D436   pop         ebp
0040D437   ret
0040D438   int         3

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值