• 【原创+整理】简述何为调用约定,函数导出名以及extern C


    何为调用约定

        调用约定指的是函数在调用时会按照不同规则,翻译成不同的汇编代码。这和参数的压栈顺序和栈的清理方式相关,也就是说不同的调用约定,这些方式会做相应改变。一般编译器是以默认的调用约定编译一份代码,但当一个项目使用不同调用约定的库会产生链接错误。

     
    何为函数导出名
        同一个函数,在不同的编译器编译出来的符号名是不一样的,程序目标文件链接的时候不知道源程序的函数名,而是通过目标文件(.obj)中寻找相应的函数符号表。在下面中会指出不同调用约定对应的函数导出名。
     
    三种调用约定
     
    (1)__fastcall
    特点:
    参数传递方式:前两个参数-寄存器,剩余参数-栈(右到左)
    栈的清理者:被调函数
    函数导出名:
    按C的编译方式:@函数名@参数字节数
    按C++的编译方式:
     
    (2)__cdecl
    特点:C语言调用约定,文件比__stdcall大
    参数传递方式:栈(右到左)
    栈的清理者:调用者
    函数导出名:
    按C的编译方式:_函数名
    按C++的编译方式:规则同下面的_stdcall调用约定,只是参数表的开始标识由上面的"@@YG"变为"@@YA"。
    (3)__stdcall
    特点:标准调用约定,   Pascal程序的缺省调用方式,通常用于Win32 Api中
    参数传递方式:栈(右到左)
    栈的清理者:被调用者
    函数导出名:
    按C的编译方式:_函数名@参数字节数
    按C++的编译方式:
    1)、以"?"标识函数名的开始,后跟函数名;
    2)、函数名后面以"@@YG"标识参数表的开始,后跟参数表;
    3)、参数表以代号表示:
      X--void ,
      D--char,
      E--unsigned char,
      F--short,
      H--int,
      I--unsigned int,
      J--long,
      K--unsigned long,
      M--float,
      N--double,
      _N--bool,
      PA--表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以"0"代替,一个"0"代表一次重复;
    4)、参数表的第一项为该函数的返回值类型,其后依次为参数的数据类型,指针标识在其所指数据类型前;
    5)、参数表后以"@Z"标识整个名字的结束,如果该函数无参数,则以"Z"标识结束。 
        上面这段代码,源文件后缀必须是.c,同时使用windows系统的dumpbin工具即可获得对应目标文件的内容。
     
        int __cdecl f1(int a)
        {
        return a +1;
        }
        int __stdcall f2(int b)
        {
        return b +1;
        }
        int __fastcall f3(int c)
        {
        return c +1;
        }
        int main()
        {
        //函数导出名为_f1、_f2@4、@f3@4
        int i = f1(1);
        int j = f2(2);
        int k = f3(3);
        return0;
        }
        @f3@4:
        00000000:55 push ebp
        00000001:8B EC mov ebp,esp
        00000003:81 EC CC 000000 sub esp,0CCh
        00000009:53 push ebx
        0000000A:56 push esi
        0000000B:57 push edi
        0000000C:51 push ecx
        0000000D:8D BD 34 FF FF FF lea edi,[ebp+FFFFFF34h]
        00000013: B9 33000000 mov ecx,33h
        00000018: B8 CC CC CC CC mov eax,0CCCCCCCCh
        0000001D: F3 AB rep stos dword ptr es:[edi]
        0000001F:59 pop ecx
        00000020:894D F8 mov dword ptr [ebp-8],ecx
        00000023:8B45 F8 mov eax,dword ptr [ebp-8]
        00000026:83 C0 01 add eax,1
        00000029:5F pop edi
        0000002A:5E pop esi
        0000002B:5B pop ebx
        0000002C:8B E5 mov esp,ebp
        0000002E:5D pop ebp
        0000002F: C3 ret
        _f1:
        00000000:55 push ebp
        00000001:8B EC mov ebp,esp
        00000003:81 EC C0 000000 sub esp,0C0h
        00000009:53 push ebx
        0000000A:56 push esi
        0000000B:57 push edi
        0000000C:8D BD 40 FF FF FF lea edi,[ebp+FFFFFF40h]
        00000012: B9 30000000 mov ecx,30h
        00000017: B8 CC CC CC CC mov eax,0CCCCCCCCh
        0000001C: F3 AB rep stos dword ptr es:[edi]
        0000001E:8B4508 mov eax,dword ptr [ebp+8]
        00000021:83 C0 01 add eax,1
        00000024:5F pop edi
        00000025:5E pop esi
        00000026:5B pop ebx
        00000027:8B E5 mov esp,ebp
        00000029:5D pop ebp
        0000002A: C3 ret
        _f2@4:
        00000000:55 push ebp
        00000001:8B EC mov ebp,esp
        00000003:81 EC C0 000000 sub esp,0C0h
        00000009:53 push ebx
        0000000A:56 push esi
        0000000B:57 push edi
        0000000C:8D BD 40 FF FF FF lea edi,[ebp+FFFFFF40h]
        00000012: B9 30000000 mov ecx,30h
        00000017: B8 CC CC CC CC mov eax,0CCCCCCCCh
        0000001C: F3 AB rep stos dword ptr es:[edi]
        0000001E:8B4508 mov eax,dword ptr [ebp+8]
        00000021:83 C0 01 add eax,1
        00000024:5F pop edi
        00000025:5E pop esi
        00000026:5B pop ebx
        00000027:8B E5 mov esp,ebp
        00000029:5D pop ebp
        0000002A: C2 0400 ret 4
        _main:
        00000000:55 push ebp
        00000001:8B EC mov ebp,esp
        00000003:81 EC E4 000000 sub esp,0E4h
        00000009:53 push ebx
        0000000A:56 push esi
        0000000B:57 push edi
        0000000C:8D BD 1C FF FF FF lea edi,[ebp-0E4h]
        00000012: B9 39000000 mov ecx,39h
        00000017: B8 CC CC CC CC mov eax,0CCCCCCCCh
        0000001C: F3 AB rep stos dword ptr es:[edi]
        0000001E:6A01 push 1
        00000020: E8 00000000 call _f1
        00000025:83 C4 04 add esp,4
        00000028:8945 F8 mov dword ptr [ebp-8],eax
        0000002B:6A02 push 2
        0000002D: E8 00000000 call _f2@4
        00000032:8945 EC mov dword ptr [ebp-14h],eax
        00000035: B9 03000000 mov ecx,3
        0000003A: E8 00000000 call @f3@4
        0000003F:8945 E0 mov dword ptr [ebp-20h],eax
        00000042:33 C0 xor eax,eax
        00000044:5F pop edi
        00000045:5E pop esi
        00000046:5B pop ebx
        00000047:81 C4 E4 000000 add esp,0E4h
        0000004D:3B EC cmp ebp,esp
        0000004F: E8 00000000 call __RTC_CheckEsp
        00000054:8B E5 mov esp,ebp
        00000056:5D pop ebp
        00000057: C3 ret
    View Code
     
    相关命令是:
    1 //把获得obj函数导出名,存储到d:\1.txt文件
    2 dumpbin OBJ文件路径/all /rawdata:none > d:\1.txt
    3 
    4 //获得汇编代码,存储到d:\2.txt
    5 dumpbin OBJ文件路径/disasm d:\2.txt
    View Code
        
    C编译的函数如何在C++中使用
        解决办法是采用extern "C"修饰符。使用方法是,把该修饰符添加到调用约定必须是__cdecl的C函数前,如DriverEntry,windows驱动函数入口函数规定为_DriverEntry@8,因此用C++编译器
    一些库是用C编译而成的,而在C++平台想要使用这些库,我们可以这样引入C库函数头文件
    1. 1 extern"C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject,IN PUNICODE_STRING pRegistry)
      2  {
      3     //do something
      4     return STATUS_SUCCESS;
      5  }
      #ifdef __cplusplus
      extern"C"
      {
      #endif
      #include<NTDDK.h>
      #ifdef __cplusplus
      }
      #endif
    参考链接:
     
  • 相关阅读:
    jira:7.12.3版本搭建(破解版)
    traefik添加多证书
    人肉分析sorted(lst, key=lambda x: (x.isdigit(), x.isdigit() and int(x) % 2 == 0, x.islower(), x.isupper(), x))过程
    jquery实现checkbox全选/反选/取消
    k8s简单集群搭建
    第十二周编程总结
    第十周作业
    第九周编程总结
    第七周编程总结
    第五周编程总结
  • 原文地址:https://www.cnblogs.com/cposture/p/4686480.html
Copyright © 2020-2023  润新知