函数调用和函数栈桢

一、进程空间分布

32位进程的虚拟内存空间分布如下图所示:
32位进程的虚拟内存空间分布

二、函数调用方式

Stdcall (WINAPI)

1.windows api的调用方式。
2.参数由右到左入栈,由被调用者进行栈清理。

Thiscall

1.C++类成员函数缺省的调用约定。
2.参数从右向左入栈;
3.如果参数个数确定,this指针通过ecx传递给被调用者;如果参数个数不确定this指针在所有参数压栈后被压入栈;
4.对参数个数不定的,调用者清理栈,否则函数自己清理栈。

Cdecl

1.C调用约定,是C语言缺省的调用约定
2.参数从左向右压入堆栈。
3.调用者负表平衡栈空间。
4.允许参数个数不定。

Fastcall

X86:
1.规定将前两个(或若干个)参数由寄存器传递,其余参数还是通过栈传递(从右到左)。
2.不同编译器编译的程序规定的寄存器不同。在Intel 386平台上,使用ECX和EDX寄存器,故使用__fastcall方式无法用作跨编译器的接口。

X64:

1.x64下的调用方式,参数从右向左传入。
2.函数参数的前4个寄存器分别为rcx,rdx,r8,r9。前四个浮点类型参数由XMM0,XMM1,XMM2,XMM3依次传递。
3.由调用函数负责清理调用栈
4.浮点返回值由XMM0传递。
5.虽然前4个数据由寄存器传入,但系统会为其留栈空间

如下函数C语言采用不同的调用方式

int  fun(char a,char b)
{
    int x = a;
    int y = b;
    return x+y;
}
void main()
{
    int k = fun(1,2);
}

stdcall调用方式

int k = fun(1,2);
00CF13EE  push        2  
00CF13F0  push        1  
00CF13F2  call        fun (0CF11C2h)  
00CF13F7  mov         dword ptr [k],eax

cdecl调用方式

int k = fun(1,2);
00CF13EE  push        2  
00CF13F0  push        1  
00CF13F2  call        fun (0CF11C2h)  
00CF13F7  mov         dword ptr [k],eax

cdecl调用方式

int k = fun(1,2);
003E13EE  push        2  
003E13F0  push        1  
003E13F2  call        fun (3E11BDh)  
003E13F7  add         esp,8  
003E13FA  mov         dword ptr [k],eax

fastcall调用方式

int k = fun(1,2);
008313EE  mov         dl,2  
008313F0  mov         cl,1  
008313F2  call        fun (8311C7h)  
008313F7  mov         dword ptr [k],eax

三、函数的栈帧

函数的栈帧是当前函数的运行时空间,只对当前函数内有效。
函数的栈帧包括参数,函数返回地址,栈桢基址和函数内部的临时变量。
对于如下函数,fun的函数栈桢如下图所示:

int  fun(int a,int b)
{
    int x = a+1;
    int y = b+2;
    return x+y;
}


void main()
{
    int k = fun(1,2);
    printf("%d",k);
}

fun的函数栈桢
反汇编如下:

text:00411380 sub_411380      proc near               ; CODE XREF: .text:004111BDj
.text:00411380                                         ; .text:004111C2j ...
.text:00411380
.text:00411380 var_D8          = byte ptr -0D8h
.text:00411380 var_14          = dword ptr -14h
.text:00411380 var_8           = dword ptr -8
.text:00411380 arg_0           = dword ptr  8
.text:00411380 arg_4           = dword ptr  0Ch
.text:00411380
.text:00411380                 push    ebp
.text:00411381                 mov     ebp, esp
.text:00411383                 sub     esp, 0D8h
.text:00411389                 push    ebx
.text:0041138A                 push    esi
.text:0041138B                 push    edi
.text:0041138C                 lea     edi, [ebp+var_D8]
.text:00411392                 mov     ecx, 36h
.text:00411397                 mov     eax, 0CCCCCCCCh
.text:0041139C                 rep stosd
.text:0041139E                 mov     eax, [ebp+arg_0]
.text:004113A1                 add     eax, 1
.text:004113A4                 mov     [ebp+var_8], eax
.text:004113A7                 mov     eax, [ebp+arg_4]
.text:004113AA                 add     eax, 2
.text:004113AD                 mov     [ebp+var_14], eax
.text:004113B0                 mov     eax, [ebp+var_8]
.text:004113B3                 add     eax, [ebp+var_14]
.text:004113B6                 pop     edi
.text:004113B7                 pop     esi
.text:004113B8                 pop     ebx
.text:004113B9                 mov     esp, ebp
.text:004113BB                 pop     ebp
.text:004113BC                 retn
.text:004113BC sub_411380      endp