函数调用约定
函数调用约定,是指当一个函数被调用时,函数的 参数会被传递给被调用的函数和 返回值会被返回给调用函数。函数的调用约定就是描述参数是怎么传递和由谁平衡 堆栈的,当然还有返回值。
windows平台下主要又三种函数调用约定
分别是:
_cdecl , _stdcall 和 _fastcall
__cdecl #
__cdecl函数约定由四个特点:
- 使用栈空间传递参数
- 函数参数按从左往右的方向传递
- 调用者负责释放参数空间
- 返回值在寄存器中
下面对一段代码进行分析,代码是:
#include <stdio.h>
int __cdecl sum(int a,int b)
{
return a+b;
}
int main()
{
int m = 1 , n = 2;
int y = sum(1,2);
}
看看这段代码的汇编
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
int sum(int,int) PROC ; sum
push ebp
mov ebp, esp
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp]
pop ebp
ret 0
int sum(int,int) ENDP ; sum
_y$ = -12 ; size = 4
_n$ = -8 ; size = 4
_m$ = -4 ; size = 4
_main PROC
push ebp
mov ebp, esp
sub esp, 12 ; 0000000cH
mov DWORD PTR _m$[ebp], 1
mov DWORD PTR _n$[ebp], 2
push 2
push 1
call int sum(int,int) ; sum
add esp, 8
mov DWORD PTR _y$[ebp], eax
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
之前提到__cdecl是用栈传递参数,且传参方向是从右往左的
对应下图两个push
反回值被存在寄存器eax里
调用者(这里是main函数)负责释放参数空间
将栈顶指针加8以达到释放目的
__stdcall #
_stdcall函数调用约定方式于 _cdecl 基本一直,唯一不同的是释放参数空间是被调用者
- 使用栈空间传递参数
- 函数参数按从左往右的方向传递
- 被调用者负责释放参数空间
- 返回值在寄存器中
还用刚刚那段代码做例子
#include <stdio.h>
int __stdcall sum(int a,int b)
{
return a+b;
}
int main()
{
int m = 1 , n = 2;
int y = sum(1,2);
}
对应的汇编是
_a$ = 8 ; size = 4
_b$ = 12 ; size = 4
int sum(int,int) PROC ; sum
push ebp
mov ebp, esp
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp]
pop ebp
ret 8
int sum(int,int) ENDP ; sum
_y$ = -12 ; size = 4
_n$ = -8 ; size = 4
_m$ = -4 ; size = 4
_main PROC
push ebp
mov ebp, esp
sub esp, 12 ; 0000000cH
mov DWORD PTR _m$[ebp], 1
mov DWORD PTR _n$[ebp], 2
push 2
push 1
call int sum(int,int) ; sum
mov DWORD PTR _y$[ebp], eax
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
这边是被调用者(也就是sum函数)示范参数空间
可以看的这边sum函数返回的ret指令后面跟了个8
代表栈顶指针值加8,以释放参数空间
__fastcall #
_fastcall 顾名思义更fast
为什么更快呢,因为他前两个参数是用寄存器进行传递的
在计算机中,cpu访问寄存器内容的速度是远远高于访问内存的
如果有三个及以上个参数的话,剩下的参数用栈进行传递
- 前两个参数使用寄存器传递
- 其余参数使用栈从右往左的方向传递
- 被调用函数负责释放参数空间
- 返回值在寄存器中
这边把刚刚代码改一下,多加2个参数
#include <stdio.h>
int __fastcall sum(int a,int b, int c, int d)
{
return a+b+c+d;
}
int main()
{
int y = sum(1,2,3,4);
}
对应的汇编是
_b$ = -8 ; size = 4
_a$ = -4 ; size = 4
_c$ = 8 ; size = 4
_d$ = 12 ; size = 4
int sum(int,int,int,int) PROC ; sum
push ebp
mov ebp, esp
sub esp, 8
mov DWORD PTR _b$[ebp], edx
mov DWORD PTR _a$[ebp], ecx
mov eax, DWORD PTR _a$[ebp]
add eax, DWORD PTR _b$[ebp]
add eax, DWORD PTR _c$[ebp]
add eax, DWORD PTR _d$[ebp]
mov esp, ebp
pop ebp
ret 8
int sum(int,int,int,int) ENDP ; sum
_y$ = -4 ; size = 4
_main PROC
push ebp
mov ebp, esp
push ecx
push 4
push 3
mov edx, 2
mov ecx, 1
call int sum(int,int,int,int) ; sum
mov DWORD PTR _y$[ebp], eax
xor eax, eax
mov esp, ebp
pop ebp
ret 0
_main ENDP
这边传了四个参数,可以看的前两个参数1和2分别放入ecx和edx寄存器里传递的
然后其余参数从右往左压入栈内传参
也是被调用者释放参数空间
返回值也是被存入寄存器eax中返回的