zoukankan      html  css  js  c++  java
  • C/C++/动态链接库DLL中函数的调用约定与名称修饰

    参见:http://blog.twofei.com/cc/impl/calling-convension.html

        调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议.
    这种协议规定了该语言的函数中的参数传送方式,参数是否可变和由谁来处理堆栈等问题. 不同的语
    言定义了不同的调用约定.

        在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,
    以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链
    接器. 这项技术通常被称为名称改编(Name Mangling)或者名称修饰(Name Decoration). 许多C++编译
    器厂商选择了自己的名称修饰方案.

        因此,为了使其它语言编写的模块(如Visual Basic应用程序,Pascal或Fortran的应用程序等)可以调
    用C/C++编写的函数,必须使用正确的调用约定来导出函数,并且不要让编译器对要导出的函数进行任何名称修饰.



    1.调用约定(Calling Convention)
        调用约定用来处理决定函数参数传送时入栈和出栈的顺序(由调用者还是被调用者把参数弹出栈),以及编译
    器用来识别函数名称的名称修饰约定等问题. 在Microsoft VC++ 6.0中定义了下面几种调用约定,我们将结合汇
    编语言来一一分析它们:

      *** C语言中的调用约定 ***
        1. __cdecl
            __cdecl是C/C++(非类成员函数以及静态类成员函数)程序默认使用的调用约定,也可以在函数声明时
        加上 __cdecl 关键字 来手工指定. 采用__cdecl约定时,函数参数按照 从右到左 顺序入栈,并且由调用
        函数者把参数弹出栈以清理堆栈. 因此,实现可变参数的函数只能使用该调用约定. 由于每一个使用
        __cdecl约定的函数都要包含清理堆栈   的代码,所以产生的可执行文件大小会稍大一丁点.

        下面将通过一个具体实例来分析__cdecl约定:

            int __cdecl Add(int a,int b)
            {
                return a+b;
            }
    
            void main(void)
            {
                Add(1,2);
            }

       函数调用处反汇编代码如下:

            main:
            0134474E 6A 02                push        2                 ;压入参数2(最右边)
            01344750 6A 01                push        1                 ;压入参数1(右边倒数第2个)
            01344752 E8 F5 CC FF FF       call        Add (0134144Ch)   ;调用目标函数
            01344757 83 C4 08             add         esp,8             ;恢复堆栈
            
            Add:
            00A8291E 8B 45 08             mov         eax,dword ptr [a] 
            00A82921 03 45 0C             add         eax,dword ptr [b]  
            00A8292A C3                   ret                           ;被调用者不清理堆栈

      由此可见,在__cdecl中:
            1.参数从右到左依次传递
            2.参数个数等于函数声明时的个数(变参除外)
            3.由调用者清理堆栈



      2. __stdcall
            __stdcall调用约定用于调用Win32 API函数 Win32API的WINAPI/CALLBACK等都是它. 采用__stdcall
        约定时,函数参数按照 从右到左 的顺序入栈,被调用的函数在返回前清理参数堆栈,函数参数个数固定.
        由于函数体本身知道传进来的参数个数,因此被调用的函数可以在返回前用一条ret n指令直接清理传递参数的堆栈.

        还是上面那个例子,将__cdecl约定换成__stdcall:

            int __stdcall Add(int a,int b)
            {
                return a+b;
            }
    
            void main(void)
            {
                Add(1,2);
            }

        函数调用处反汇编代码(从简):

            main:
            ;Add(1,2);
            0131474E 6A 02                push        2                 ;参数依然是从右到左传递
            01314750 6A 01                push        1  
            01314752 E8 FA CC FF FF       call        Add (01311451h) 
            01314757 33 C0                xor         eax,eax           ;调用函数并不清理堆栈
            0131476C C3                   ret
            
            int __stdcall Add(int a,int b):
            0131291E 8B 45 08             mov         eax,dword ptr [a] ;完成相加操作
            01312921 03 45 0C             add         eax,dword ptr [b]
            0131292A C2 08 00             ret         8                 ;返回时清理参数栈
            

          由此可见,在__stdcall中:
               1.函数参数从右到左传递
               2.参数个数等于函数声明时的个数
               3.由被调用者清理参数栈



        3. __fastcall
            __fastcall约定用于对性能要求非常高的场合.
            __fastcall约定将函数的从左边开始的两个大小不大于4个字节(DWORD)的参数分别放在ECX和EDX寄存器,
        其余的参数仍旧自右向左压栈传送,被调用的函数在返回前清理传送参数的堆栈.
            (暂无代码)
            1.函数参数从右到左传递
            2.由被调用者清理堆栈



      *** C++中的调用约定 ***
            以上三种C语言中可用的调用约定均可以用于修饰C++中的非静态成员函数,只是会将this指针当作一个普通的
        参数一样压入(__cdecl,__stdcall)/传递到ECX(EDX)(__fastcall).
            __thiscall调用约定是C++中专用的,是非静态类成员函数的默认调用约定(注:在VC6中没有此关键字).
        采用 __thiscall约定时,函数参数按照从右到左的顺序入栈,被调用的函数在返回前清理传送参数的栈,
        只是它是通过ECX寄存器传送一个额外的参数:this指针

        这次的例子中将定义一个类,并在类中定义一个成员函数,代码如下:

            class A
            {
            public:
                int __cdecl Add1(int a,int b)
                {
                    return a+b;
                }
    
                int __stdcall Add2(int a,int b)
                {
                    return a+b;
                }
    
                int __fastcall Add3(int a,int b)
                {
                    return a+b;
                }
    
                int __thiscall Add4(int a,int b)
                {
                    return a+b;
                }
            };
    
            void main(void)
            {
                A a;
                a.Add1(1,2);
                a.Add2(3,4);
                a.Add3(5,6);
                a.Add4(7,8);
            }


        再来看看这个稍微复杂一点的C++类调用的反汇编代码:
            1. 除 Add3 的__fastcall外其余全部通过从右到左参数
            2. 除默认的函数需要的参数个数外, 额外传入一个this指针
            3. 清理堆栈的方式保持不变

                a.Add1(1,2);
            001C1528 6A 02                push        2  
            001C152A 6A 01                push        1  
            001C152C 8D 45 F7             lea         eax,[a]  
            001C152F 50                   push        eax                   ;传入this指针
            001C1530 E8 1B FB FF FF       call        A::Add1 (01C1050h)  
            001C1535 83 C4 0C             add         esp,0Ch               ;由于多传入了一个this,所以是0Ch
            
                a.Add2(3,4);
            001C1538 6A 04                push        4  
            001C153A 6A 03                push        3  
            001C153C 8D 45 F7             lea         eax,[a]               ;传入this指针
            001C153F 50                   push        eax  
            001C1540 E8 DD FB FF FF       call        A::Add2 (01C1122h)  
            
                a.Add3(5,6);
            001C1545 6A 06                push        6  
            001C1547 BA 05 00 00 00       mov         edx,5                 ;将参数保存到edx
            001C154C 8D 4D F7             lea         ecx,[a]               ;将this保存到ecx
            001C154F E8 60 FB FF FF       call        A::Add3 (01C10B4h)  
            
                a.Add4(7,8);
            001C1554 6A 08                push        8  
            001C1556 6A 07                push        7  
            001C1558 8D 4D F7             lea         ecx,[a]               ;由ecx传递this指针
            001C155B E8 B4 FA FF FF       call        A::Add4 (01C1014h)  
            
            类成员反汇编代码(仅保留返回部分,其余地方同C语言中一样):
                __cdecl:    001C141A C3                   ret               ;__cdecl由调用者清理堆栈
                __stdcall:  001C145A C2 0C 00             ret         0Ch   ;加上this,总共3个参数
                __fastcall: 001C14A2 C2 04 00             ret         4     ;this和其中一个由寄存器传递
                __thiscall: 001C14EF C2 08 00             ret         8     ;this通过ecx传递,堆栈不受影响
        

    <名称修饰,后面补上>
    ----------------
    女孩不哭 @ 2013-09-11 00:18:11 @ http://www.cnblogs.com/nbsofer

  • 相关阅读:
    Automated Telephone Exchange
    Babelfish
    又见回文
    487-3279
    Robot Motion
    99. Recover Binary Search Tree
    98. Validate Binary Search Tree
    97. Interleaving String
    96. Unique Binary Search Trees
    95. Unique Binary Search Trees II
  • 原文地址:https://www.cnblogs.com/memset/p/calling_convention.html
Copyright © 2011-2022 走看看