zoukankan      html  css  js  c++  java
  • VC 函数调用的 汇编代码 浅析

    摘要:主要谈谈vc里面函数调用汇编成汇编代码的情形,首先针对之前的一个小程序,说说vc编译器的优化。

    例子程序:

    #include <iostream>
    using namespace std;
    int main(int argc, char* argv[]) 

     int i=10; 
     int a = i; 
     cout << "i=" << a << endl;

     //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 
     __asm  
     { 
      mov dword ptr [ebp-4], 20h 
     } 
     int b = i; 
     cout << "i=" << b << endl;

     return 0;
    }

    这段代码很简洁,但汇编代码可不简洁,首先看看release模式下的汇编代码概况:

    ……

    ; 14   : 
    ; 15   :  //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 
    ; 16   :  __asm  
    ; 17   :  { 
    ; 18   :   mov dword ptr [ebp-4], 20h

     mov DWORD PTR [ebp-4], 32   ; 00000020H

    ; 19   :  } 
    ; 20   :  int b = i; 
    ; 21   :  cout << "i=" << b << endl;

     push 10     ; 0000000aH
     push OFFSET FLAT:??_C@_02HDOK@i?$DN?$AA@ ; `string'
     push OFFSET FLAT:?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; std::cout
     call ??6std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<
     add esp, 8
     mov ecx, eax
     call ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z ; std::basic_ostream<char,std::char_traits<char> >::operator<<
     mov esi, eax
     push 10     ; 0000000aH
     mov ecx, esi
     call ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z ; std::basic_ostream<char,std::char_traits<char> >::put
     mov ecx, DWORD PTR [esi]
     xor edi, edi

    ……

    调用cout前面,直接一个push 10,这是函数调用前压参数的过程,压了个常数在里面,呵呵,其实i已经被修改了,但是编译器不知道,以为i仍然是10,顾做了优化,压参压了常量在里面。

    再看看debug模式下的汇编代码情况:

    16:       __asm
    17:       {
    18:           mov dword ptr [ebp-4], 20h
    004017DE   mov         dword ptr [ebp-4],20h
    19:       }
    20:       int b = i;
    004017E5   mov         edx,dword ptr [ebp-4]
    004017E8   mov         dword ptr [ebp-0Ch],edx
    21:       cout << "i=" << b << endl;
    004017EB   push        offset @ILT+195(std::endl) (004010c8)
    004017F0   mov         eax,dword ptr [ebp-0Ch]
    004017F3   push        eax
    004017F4   push        offset string "i=" (0046c01c)
    004017F9   push        offset std::cout (00477a10)
    004017FE   call        @ILT+640(std::operator<<) (00401285)
    00401803   add         esp,8
    00401806   mov         ecx,eax

    b = i,赋值这句成了从i的地址去取值送往b了,

    mov         edx,dword ptr [ebp-4]
    mov         dword ptr [ebp-0Ch],edx

    注意release版本是没有这两句的,所以现在b取值就是肯定正确的了,后面压参的时候,也把b地址指向的值压入了堆栈,呵呵,这也是之前说的为什么两个版本下运行结果不同的原因。

    把这个程序稍加修改,开始我们下面的函数调用汇编浅析:

    #include <iostream>

    using namespace std;

    int ChangNum(int, int);
    int main(int argc, char* argv[]) 

     int i=10; 
     int a = i; 
     cout << "i=" << a << endl;

     //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道 
     __asm  
     { 
      mov dword ptr [ebp-4], 20h 
     } 
     int b = i; 
     cout << "i=" << b << endl; 
     ChangNum(50, 100);

     return 0;
    }

    int ChangNum(int nParam, int nW)
    {
     int i = 10; 
     int a = i; 
     cout << "i=" << a << endl;

     __asm  
     { 
      mov dword ptr [ebp - 4], 20h
      mov dword ptr [ebp + 12], 0h 
     } 
     int b = i; 
     cout << "i=" << b << endl;

     return 0;
    }

    主要看看函数调用那段的汇编代码:

    1、函数调用点汇编代码:

    23:       ChangNum(50, 100);
    00401824   push        64h
    00401826   push        32h
    00401828   call        @ILT+590(ChangNum) (00401253)
    0040182D   add         esp,8
    分别是两个参数入栈,call这句有两个作用,下一行地址入栈,同时进行函数调用,最后一句是恢复栈空间,两个整型参数每个四字节,所以esp堆栈指针要加上8字节。

    2、函数体中的汇编代码:

    004018C0   push        ebp
    004018C1   mov         ebp,esp
    004018C3   sub         esp,4Ch
    004018C6   push        ebx
    004018C7   push        esi
    004018C8   push        edi
    004018C9   lea         edi,[ebp-4Ch]
    004018CC   mov         ecx,13h
    004018D1   mov         eax,0CCCCCCCCh
    004018D6   rep stos    dword ptr [edi]
    30:       int i = 10;
    004018D8   mov         dword ptr [ebp-4],0Ah
    31:       int a = i;
    004018DF   mov         eax,dword ptr [ebp-4]
    004018E2   mov         dword ptr [ebp-8],eax
    32:       cout << "i=" << a << endl;
    004018E5   push        offset @ILT+195(std::endl) (004010c8)
    004018EA   mov         ecx,dword ptr [ebp-8]
    004018ED   push        ecx
    004018EE   push        offset string "i=" (0046c01c)
    004018F3   push        offset std::cout (00477a10)
    004018F8   call        @ILT+645(std::operator<<) (0040128a)
    004018FD   add         esp,8
    00401900   mov         ecx,eax
    00401902   call        @ILT+250(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010ff)
    00401907   mov         ecx,eax
    00401909   call        @ILT+475(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e0)
    33:
    34:       __asm
    35:       {
    36:           mov dword ptr [ebp - 4], 20h
    0040190E   mov         dword ptr [ebp-4],20h
    37:           mov dword ptr [ebp + 12], 0h
    00401915   mov         dword ptr [ebp+0Ch],0
    38:       }
    39:       int b = i;
    0040191C   mov         edx,dword ptr [ebp-4]
    0040191F   mov         dword ptr [ebp-0Ch],edx
    40:       cout << "i=" << b << endl;
    00401922   push        offset @ILT+195(std::endl) (004010c8)
    00401927   mov         eax,dword ptr [ebp-0Ch]
    0040192A   push        eax
    0040192B   push        offset string "i=" (0046c01c)
    00401930   push        offset std::cout (00477a10)
    00401935   call        @ILT+645(std::operator<<) (0040128a)
    0040193A   add         esp,8
    0040193D   mov         ecx,eax
    0040193F   call        @ILT+250(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004010ff)
    00401944   mov         ecx,eax
    00401946   call        @ILT+475(std::basic_ostream<char,std::char_traits<char> >::operator<<) (004011e0)
    41:
    42:       return 0;
    0040194B   xor         eax,eax
    43:   }
    0040194D   pop         edi
    0040194E   pop         esi
    0040194F   pop         ebx
    00401950   add         esp,4Ch
    00401953   cmp         ebp,esp
    00401955   call        __chkesp (00406df0)
    0040195A   mov         esp,ebp
    0040195C   pop         ebp
    0040195D   ret
    ebp入栈,然后将esp的值传给ebp,现在ebp是指向此时的堆栈了,注意后面除了函数返回前,ebp的值一直是固定的,通过这种机制来访问参数和局部变量,esp减少了一个比较大的值,留给局部变量使用的,然后是通用寄存器入栈,接着就是实际的工作的代码了,这里就不说了,到那个return后面再看,是通用寄存器出栈,esp恢复,ebp出栈,ret回到函数调用的下一条指令。

    http://blog.csdn.net/magictong/article/details/3447982

  • 相关阅读:
    JavaScript 将数组中具有相同值的对象 取出组成新的数组
    css样式初始化
    javaScript如何跳出多重循环break、continue
    vue-cli中webpack配置解析
    Invalid prop: type check failed for prop "XXX". Expected String, got Object.
    vue-cli构建项目添加网站ico的logo
    vue-cli构建的vue项目打包后css引入的背景图路径不对的问题
    从零开始使用vue-cli搭建一个vue项目及注意事项
    安装npm及cnpm(Windows)
    面试题8:旋转数组中的最小数字
  • 原文地址:https://www.cnblogs.com/findumars/p/6145921.html
Copyright © 2011-2022 走看看