【原创+整理】简述何为调用约定,函数导出名以及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、[email protected]、@[email protected]
    int i = f1(1);
    int j = f2(2);
    int k = f3(3);
    return0;
    }
    @[email protected]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
    [email protected]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 [email protected]4
    00000032:8945 EC mov dword ptr [ebp-14h],eax
    00000035: B9 03000000 mov ecx,3
    0000003A: E8 00000000 call @[email protected]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

相关命令是:

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

C编译的函数如何在C++中使用

解决办法是采用extern "C"修饰符。使用方法是,把该修饰符添加到调用约定必须是__cdecl的C函数前,如DriverEntry,windows驱动函数入口函数规定为[email protected],因此用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

参考链接:

http://www.cnblogs.com/dragon2012/p/3884597.html

http://blog.csdn.net/fengbingchun/article/details/43956673

http://www.2cto.com/kf/201405/301756.html

本文链接:http://www.cnblogs.com/cposture/p/4686480.html

时间: 2024-08-24 03:37:36

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

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值.

逆向知识十一讲,识别函数的调用约定,函数参数,函数返回值. 在反汇编中,我们常常的会看到各种的函数调用,或者通过逆向的手段,单独的使用这个函数,那么此时,我们就需要认识一下怎么识别函数了. 一丶识别__cdecl 函数(俗称C Call),函数参数,函数返回值 首先写一个C Call的函数 1.返回值 int类型, 参数int 类型 高级代码: int __cdecl MyAdd(int a,int b) { return a + b; } int main(int argc, char* ar

C/C++:函数的编译方式与调用约定以及extern “C”的使用

函数在C++编译方式与C编译方式下的主要不同在于:由于C++引入了函数重载(overload),因此编译器对同名函数进行了名称重整(name mangle).因此,在C++中引 用其他C函数库时,需要对声明使用的函数做适当的处理,以告知编译器做出适应的名称处理. 函数的调用约定涉及了函数参数的入栈顺序.清栈主体(负责清理栈的主体:函数自身还是调用函数者?).部分名称重整. 如,在C编译方式下有_stdcall._cdecl等调用约定,在C++编译方式下也有_stdcall._cedecl等调用约

DLL中调用约定和名称修饰(一)

DLL中调用约定和名称修饰(一) 调用约定(Calling Convention)是指在程序设计语言中为了实现函数调用而建立的一种协议.这种协议规定了该语言的函数中的参数传送方式.参数是否可变和由谁来处理堆栈等问题.不同的语言定义了不同的调用约定. 在C++中,为了允许操作符重载和函数重载,C++编译器往往按照某种规则改写每一个入口点的符号名,以便允许同一个名字(具有不同的参数类型或者是不同的作用域)有多个用法,而不会打破现有的基于C的链接器.这项技术通常被称为名称改编(Name Manglin

JDBC 调用存储函数 存储过程

JDBC调用存储过程 步骤: 1:通过Connection 对象的prepareCall()方法创建一个CallableStatement对象的实例, 在使用Connection对象的prepareCall()方法时,需要传入一个String类型的字符串, 该方法指明如何调用存储过程. {?= call <procedure-name>[(<arg1>,<arg2>, ...)]} {call <procedure-name>[(<arg1>,&

调用约定_stdcall _cdecl _fastcall的区别

1.函数调用约定 函数的调用约定,顾名思义就是对函数调用的一个约束和规定(规范),描述了函数参数是怎么传递和由谁清除堆栈的.它决定以下内容: (1) 函数参数的压栈顺序: (2) 由调用者还是被调用者把参数弹出栈: (3) 产生函数修饰名的方法: 在看C++ primer中就提到函数声明包括:返回值类型,函数名,形参列表 int function(); int add(int a,int b); 上面的函数声明方式是我们经常用到的,其实还有一部分就是调用约定,目前存在:_cdecl._stdca

VC与JavaScript交互(二) ———— 调用JS函数

这一章,我们来动手实践VC调用JS函数. 我们动手写一个HTML,其中包含这样一段JS代码: [html] view plaincopy <script type="text/javascript"> function Add(value1, value2) { return value1 + value2; } </script> 然后我们用WebBrowser加载这个HTML后,在VC中这样来调用这个函数名为Add的JS函数: [cpp] view plai

DLL-动态链接库(导入导出符/调用约定)

1. DLLs in Visual C++ 1.1 __declspec(llexport) and __declspec(dllimport) 首先,如题,这是VC的东西.*nix下不需要. 在VC中使用DLL的过程如下 1)新建一个Win32项目,右键项目→属性→常规,把配置属性改为dll,如图 2)在要导出(本工程外使用)的类.方法.变量之前,加上导出符号 __declspec(dllexport) [必须] 这就引出了本节的主角,先看微软的定义 You can import public

x64 stack walking、调用约定、函数参数识别

k = <rsp> <rip> <frame_count>x64下manual stack walking与x86不同,x86一般情况下有ebp chain,x64没有ebp chain,类似x86的FPOx64下,rsp在函数执行完prologue之后就不会变化(调用约定):所以0.如果函数内执行了call指令,call指令返回地址压栈后,rsp就会减8:1.也就是说,在stack reconstruction时,识别到返回地址所在的栈地址,再加8,就是当前函数执行完

在lldb调试中调用c++函数

在lldb调试时,调用oc对象的方法不足为奇,因为msgSend是有原型导出的,oc对象的方法都运行期绑定的,绑定信息都在objc_class中.只要在调试中[receiver sel]之类,lldb就自动完成的整个由SEL通过msgSend路由到receiver的IMP方法并执行的整个过程.但是要调用c++函数则没有这么方便,虽然c++函数(包括成员函数和非成员函数)的链接符号有着函数原型的详细信息,但却不包括类的定义和名字空间的定义,即使lldb翻译出这样一个符号(symbol)Quartz