函数调用过程与调用约定

函数调用约定

__cdecl 缺省调用方式,函数采用从右到左的压栈方式,调用方清理堆栈,注意可变参数

__stdcall 函数的参数自右向左通过栈传递,被调用方清理堆栈

__fastcall 通过寄存器来传送参数,被调用方清理堆栈

__thiscall用于C++成员函数,使用ecx存放this指针

naked call 裸函数,上面几种方式会产生保存一些寄存器的代码,这种不产生。naked call不是类型修饰符,必须和_declspec共同使用
__declspec(naked) 是告诉编译器 不要对函数进行优化 函数的所有实现包括堆栈的平衡 参数的压栈 ebp的赋值 还原 都要我们来做


决定以下内容:

1)函数参数的压栈顺序
2)由调用者还是被调用者把参数弹出栈
3)以及产生函数修饰名的方法

修饰名

1、修饰名(Decoration name):"C"或者"C++“函数在内部(编译和链接)通过修饰名识别
2、C编译时函数名修饰约定规则:

__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个”@"符号和其参数的字节数,格式为_functionname@number,
例如 :function(int a, int b),其修饰名为:_function@8


__cdecl调用约定仅在输出函数名前加上一个下划线前缀,格式为_functionname。


__fastcall调用约定在输出函数名前加上一个"@“符号,后面也是一个”@"符号和其参数的字节数,格式为@functionname@number。


注:

1
2
3
一般WIN32的函数都是__stdcall
#define CALLBACK __stdcall
#define WINAPI  __stdcall

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>
#include <Windows.h>

// 默认是__cdecl
int cAdd(int a, int b, int c)
{
return a + b + c;
}

//堆栈平衡由函数内部处理
int __stdcall stdAdd(int a, int b, int c)
{
return a + b + c;
}

//前两个参数使用寄存器存储,堆栈平衡由函数内部处理
int __fastcall fastAdd(int a, int b, int c)
{
return a + b + c;
}

//堆栈平衡由我们自己处理
int __declspec(naked) nkAdd(int a, int b, int c)
{
__asm
{
push [esp + 0ch]
push [esp + 0ch]
push [esp + 0ch]
call stdAdd
mov eax,eax
ret
}
}

int main(void)
{
int result;
// 内联汇编
__asm
{
mov eax,eax
mov eax,eax
mov eax,eax
}
result = cAdd(1,2,3);
printf("%d",result);
stdAdd(1,2,3);
printf("%d",result);
fastAdd(1,2,3);
printf("%d",result);
nkAdd(1,2,3);
printf("%d",result);
//CreateThread();
system("pause");
return 0;
}