zoukankan      html  css  js  c++  java
  • 调用约定

      编译器:VS2015

       调用约定的直观认识:

      __stdcall是微软自己搞出来的,

      c、c++的默认调用方式是_cdecl,

      另外还有优化的__fastcall(通过寄存器传递参数)

      以及c++的,thiscall(不能直接使用)

      还有naked call

      (可参考http://blog.csdn.net/ghevinn/article/details/43668825)

    0x01   x86  calling convention

    0x01   x86平台下常用三种调用约定,__cdecl、__stdcall、__fastcall

        

         1.__cdecl

        __cdecl是C/C++的默认调用约定,如果不显示声明调用约定的情况下,就是该调用约定.

        

    #include "stdafx.h"
    int __cdecl SHCdeclCallTest(int , int , int , int , int );
    
    int main()
    {
    	SHCdeclCallTest(1, 2, 3, 4, 5);
        return 0;
    }
    
    
    int __cdecl SHCdeclCallTest(int v1, int v2, int v3, int v4, int v5)
    {
    	return v1 + v2 + v3 + v4 + v5;
    }
    

      

      反汇编看出__cdecl参数从右至左入栈,然后由调用者(caller)清理栈区。

      2.__stdcall 标准调用

    #include "stdafx.h"
    int __stdcall SHCdeclCallTest(int , int , int , int , int );
    
    int main()
    {
    	SHCdeclCallTest(1, 2, 3, 4, 5);
        return 0;
    }
    
    
    int __stdcall SHCdeclCallTest(int v1, int v2, int v3, int v4, int v5)
    {
    	return v1 + v2 + v3 + v4 + v5;
    }
    

      

      反汇编看出__stdcall参数跟__cdecl入栈方式相同,从右至左入栈,被调用者(callee)清理栈区。

      3.__fastcall

    int __fastcall SHCdeclCallTest(int v1, int v2, int v3, int v4, int v5)
    {
    	return v1 + v2 + v3 + v4 + v5;
    }
    

      

      __fastcall参数从右至左入栈,前两个参数被分别放进了ecx、edx寄存器,如果少于或等于两个参数,会先将参数放入寄存器。同样由被调用者(callee)清理栈区。

    0x02  x64  calling convention

        微软的C编译器在目标平台为x64的时候,就不再支持__stdcall ,__fastcall关键字,无需再考虑调用协议。

        x64位平台下只有一种__fastcall的调用约定,前4参数则先放入ecx、edx、r8、r9寄存器,更多的参数放入栈区,由调用者(caller)清理栈区空间。

        第一点值得注意的是,在64位下,编译器还是为前4个参数预留了栈区空间(每个栈空间大小为8字节,共32字节大小),然后将寄存器的值放入所预留的栈区空间,这是为了防止在传递参数的过程中,寄存器需要接收其他的值而导致参数无法传递,或者其他值无法接收的情况。

        第二点值得注意的是,x64平台下与通过 PUSH 和 POP 指令在堆栈中显式添加和移除参数的 x86 编译器不同,x64 代码生成器会预留足够的堆栈空间。在进入某个函数之后,就使用sub rsp,n来开辟一个栈空间,除了给自己保存内部变量之外,也给自己所调用的函数传递参数用,这样参数就不用一个个地push,而可以直接用mov指令来填写,函数内的每个被调用函数也不再需要去每一次得恢复栈空间了,直接函数结束时再一次性恢复栈空间。

    0x03   x86下存在不同调用约定的理由

         1.可变参数的情况(比如printf()函数参数不定长),其他调用约定也在32位平台下全部会由编译器修正为__cdecl。(stdcall 无法处理变长参数,)

       2.混合语言函数调用的情况,如果是C调用C,它当然知道参数与返回值是如何压栈;但如果是vb或pascal调用C,它怎么知道参数与返回值是如何压栈的?那么怎么清栈?所以无法使用cdecl,只能使用stdcall。

    unix主要使用C作为程序语言,但在Windows上vb或pascal与C使用一样广泛,所以,必须考虑不同语言间如何调用问题。(大多数API都采用__stdcall调用规范,这是因为几乎所有的语言都支持__stdcall调用. 所以在跨(开发)平台的调用中,我们都使用__stdcall(虽然有时是以WINAPI的样子出现)。)

        

  • 相关阅读:
    170325 第六章应用层 域名系统 DNS
    文件操作(Linux系统调用)
    进程优先级,进程nice值和%nice的解释
    常用的操作系统进程调度算法
    fork函数返回值问题
    进度条的实现
    find命令
    单链表的插入排序
    B树
    排序
  • 原文地址:https://www.cnblogs.com/lsh123/p/7598700.html
Copyright © 2011-2022 走看看