首页 > 代码库 > 函数堆栈平衡

函数堆栈平衡

int func(int a,int b,int c, int d)
{
01243CE0  push        ebp  
01243CE1  mov         ebp,esp  
01243CE3  sub         esp,0CCh  
01243CE9  push        ebx  
01243CEA  push        esi  
01243CEB  push        edi  
01243CEC  lea         edi,[ebp-0CCh]  
01243CF2  mov         ecx,33h  
01243CF7  mov         eax,0CCCCCCCCh  
01243CFC  rep stos    dword ptr es:[edi]  
    int v;
    v=a+b+c+d;
01243CFE  mov         eax,dword ptr [a]  
01243D01  add         eax,dword ptr [b]  
01243D04  add         eax,dword ptr [c]  
01243D07  add         eax,dword ptr [d]  
01243D0A  mov         dword ptr [v],eax  
    return v;
01243D0D  mov         eax,dword ptr [v]  
}
01243D10  pop         edi  
01243D11  pop         esi  
01243D12  pop         ebx  
01243D13  mov         esp,ebp  
01243D15  pop         ebp  
01243D16  ret  

int main()
{
01241B50  push        ebp  
01241B51  mov         ebp,esp  
01241B53  sub         esp,0CCh  
01241B59  push        ebx  
01241B5A  push        esi  
01241B5B  push        edi  
01241B5C  lea         edi,[ebp-0CCh]  
01241B62  mov         ecx,33h  
01241B67  mov         eax,0CCCCCCCCh  
01241B6C  rep stos    dword ptr es:[edi]  
    int x;
    x=func(1,2,3,4);
01241B6E  push        4  
01241B70  push        3  
01241B72  push        2  
01241B74  push        1  
01241B76  call        func (012411EFh)  
01241B7B  add         esp,10h  
01241B7E  mov         dword ptr [x],eax  
    return 0;
01241B81  xor         eax,eax  
}
01241B83  pop         edi  
01241B84  pop         esi  
01241B85  pop         ebx  
01241B86  add         esp,0CCh  
01241B8C  cmp         ebp,esp  
01241B8E  call        __RTC_CheckEsp (01241140h)  
01241B93  mov         esp,ebp  
01241B95  pop         ebp  
01241B96  ret  

看上面划线部分,很明显是 _cdecl 调用方式,调用方平衡堆栈。

下面来看看 _stdcall 调用方式,这是被调用方平衡堆栈。

int _stdcall func(int a,int b,int c, int d)
{
010C3CE0  push        ebp  
010C3CE1  mov         ebp,esp  
010C3CE3  sub         esp,0CCh  
010C3CE9  push        ebx  
010C3CEA  push        esi  
010C3CEB  push        edi  
010C3CEC  lea         edi,[ebp-0CCh]  
010C3CF2  mov         ecx,33h  
010C3CF7  mov         eax,0CCCCCCCCh  
010C3CFC  rep stos    dword ptr es:[edi]  
    int v;
    v=a+b+c+d;
010C3CFE  mov         eax,dword ptr [a]  
010C3D01  add         eax,dword ptr [b]  
010C3D04  add         eax,dword ptr [c]  
010C3D07  add         eax,dword ptr [d]  
010C3D0A  mov         dword ptr [v],eax  
    return v;
010C3D0D  mov         eax,dword ptr [v]  
}
010C3D10  pop         edi  
010C3D11  pop         esi  
010C3D12  pop         ebx  
010C3D13  mov         esp,ebp  
010C3D15  pop         ebp  
010C3D16  ret         10h  

int main()
{
010C1B50  push        ebp  
010C1B51  mov         ebp,esp  
010C1B53  sub         esp,0CCh  
010C1B59  push        ebx  
010C1B5A  push        esi  
010C1B5B  push        edi  
010C1B5C  lea         edi,[ebp-0CCh]  
010C1B62  mov         ecx,33h  
010C1B67  mov         eax,0CCCCCCCCh  
010C1B6C  rep stos    dword ptr es:[edi]  
    int x;
    x=func(1,2,3,4);
010C1B6E  push        4  
010C1B70  push        3  
010C1B72  push        2  
010C1B74  push        1  
010C1B76  call        func (010C11F4h)  
010C1B7B  mov         dword ptr [x],eax  
    return 0;
010C1B7E  xor         eax,eax  
}
010C1B80  pop         edi  
010C1B81  pop         esi  
010C1B82  pop         ebx  
010C1B83  add         esp,0CCh  
010C1B89  cmp         ebp,esp  
010C1B8B  call        __RTC_CheckEsp (010C1140h)  
010C1B90  mov         esp,ebp  
010C1B92  pop         ebp  
010C1B93  ret  

下面看看win下x64(Linux与此不同):

在x64下函数调用的前4个参数总是放在寄存器中传递,剩余的参数则压入堆栈中。

而x86上则是全部压入堆栈中(除了fastcall方式)。这4个用于存放参数的寄存器分别是

存放整数参数的RCX,RDX,R8,R9。存放浮点数参数的XMM0,XMM1,XMM2,XMM3。

double func3(double a,double b,double c, double d)
{
000000013FD83240  movsd       mmword ptr [rsp+20h],xmm3  
000000013FD83246  movsd       mmword ptr [rsp+18h],xmm2  
000000013FD8324C  movsd       mmword ptr [v],xmm1  
000000013FD83252  movsd       mmword ptr [rsp+8],xmm0  
000000013FD83258  push        rdi  
000000013FD83259  sub         rsp,20h  
000000013FD8325D  mov         rdi,rsp  
000000013FD83260  mov         ecx,8  
000000013FD83265  mov         eax,0CCCCCCCCh  
000000013FD8326A  rep stos    dword ptr [rdi]  
    double v;
    v=a+b+c+d;
000000013FD8326C  movsd       xmm0,mmword ptr [a]  
000000013FD83272  addsd       xmm0,mmword ptr [b]  
000000013FD83278  addsd       xmm0,mmword ptr [c]  
000000013FD8327E  addsd       xmm0,mmword ptr [d]  
000000013FD83284  movsd       mmword ptr [v],xmm0  
    return v;
000000013FD8328A  movsd       xmm0,mmword ptr [v]  
}
000000013FD83290  add         rsp,20h  
000000013FD83294  pop         rdi  
000000013FD83295  ret  

int func2(int a,int b,int c, int d,int e,int f)
{
000000013FD832A0  mov         dword ptr [a],r9d  
000000013FD832A5  mov         dword ptr [rsp+18h],r8d  
000000013FD832AA  mov         dword ptr [rsp+10h],edx  
000000013FD832AE  mov         dword ptr [rsp+8],ecx  
000000013FD832B2  push        rdi  
000000013FD832B3  sub         rsp,10h  
000000013FD832B7  mov         rdi,rsp  
000000013FD832BA  mov         ecx,4  
000000013FD832BF  mov         eax,0CCCCCCCCh  
000000013FD832C4  rep stos    dword ptr [rdi]  
000000013FD832C6  mov         ecx,dword ptr [a]  
    int v;
    v=a+b+c+d+e+f;
000000013FD832CA  mov         eax,dword ptr [b]  
000000013FD832CE  mov         ecx,dword ptr [a]  
000000013FD832D2  add         ecx,eax  
000000013FD832D4  mov         eax,ecx  
000000013FD832D6  add         eax,dword ptr [c]  
000000013FD832DA  add         eax,dword ptr [d]  
000000013FD832DE  add         eax,dword ptr [e]  
000000013FD832E2  add         eax,dword ptr [f]  
000000013FD832E6  mov         dword ptr [rsp],eax  
    return v;
000000013FD832E9  mov         eax,dword ptr [rsp]  
}
000000013FD832EC  add         rsp,10h  
000000013FD832F0  pop         rdi  
000000013FD832F1  ret  

int func(int a,int b,int c, int d)
{
000000013FD83300  mov         dword ptr [a],r9d  
000000013FD83305  mov         dword ptr [rsp+18h],r8d  
000000013FD8330A  mov         dword ptr [rsp+10h],edx  
000000013FD8330E  mov         dword ptr [rsp+8],ecx  
000000013FD83312  push        rdi  
000000013FD83313  sub         rsp,10h  
000000013FD83317  mov         rdi,rsp  
000000013FD8331A  mov         ecx,4  
000000013FD8331F  mov         eax,0CCCCCCCCh  
000000013FD83324  rep stos    dword ptr [rdi]  
000000013FD83326  mov         ecx,dword ptr [a]  
    int v;
    v=a+b+c+d;
000000013FD8332A  mov         eax,dword ptr [b]  
000000013FD8332E  mov         ecx,dword ptr [a]  
000000013FD83332  add         ecx,eax  
000000013FD83334  mov         eax,ecx  
000000013FD83336  add         eax,dword ptr [c]  
000000013FD8333A  add         eax,dword ptr [d]  
000000013FD8333E  mov         dword ptr [rsp],eax  
    return v;
000000013FD83341  mov         eax,dword ptr [rsp]  
}
000000013FD83344  add         rsp,10h  
000000013FD83348  pop         rdi  
000000013FD83349  ret  

int main()
{
000000013FD83350  push        rdi  
000000013FD83352  sub         rsp,40h  
000000013FD83356  mov         rdi,rsp  
000000013FD83359  mov         ecx,10h  
000000013FD8335E  mov         eax,0CCCCCCCCh  
000000013FD83363  rep stos    dword ptr [rdi]  
    int x,y;
    double z;
    //4个参数,用RCX、RDX、R8、R9传参
    x=func(1,2,3,4);
000000013FD83365  mov         r9d,4  
000000013FD8336B  mov         r8d,3  
000000013FD83371  mov         edx,2  
000000013FD83376  mov         ecx,1  
000000013FD8337B  call        func (013FD8100Ah)  
000000013FD83380  mov         dword ptr [x],eax  
    //用4个寄存器+堆栈传参
    y=func2(1,2,3,4,5,6);
000000013FD83384  mov         dword ptr [rsp+28h],6  
000000013FD8338C  mov         dword ptr [rsp+20h],5  
000000013FD83394  mov         r9d,4  
000000013FD8339A  mov         r8d,3  
000000013FD833A0  mov         edx,2  
000000013FD833A5  mov         ecx,1  
000000013FD833AA  call        func2 (013FD81014h)  
000000013FD833AF  mov         dword ptr [y],eax  
    //XMM0、XMM1、XMM2、XMM3 传参
    z=func3(1.0,2.0,3.0,4.0);
000000013FD833B3  movsd       xmm3,mmword ptr [__real@4010000000000000 (013FD86CB8h)]  
000000013FD833BB  movsd       xmm2,mmword ptr [__real@4008000000000000 (013FD86A88h)]  
000000013FD833C3  movsd       xmm1,mmword ptr [__real@4000000000000000 (013FD86918h)]  
000000013FD833CB  movsd       xmm0,mmword ptr [__real@3ff0000000000000 (013FD868B8h)]  
000000013FD833D3  call        func2 (013FD81019h)  
000000013FD833D8  movsd       mmword ptr [z],xmm0  
    return 0;
000000013FD833DE  xor         eax,eax  
}
000000013FD833E0  add         rsp,40h  
000000013FD833E4  pop         rdi  
000000013FD833E5  ret  

下面摘自:http://openwares.net/misc/windows_x64_function_call_convention.html
  • 调用函数为前四个参数在调用栈上保留相应的空间,称作shadow space或spill slot。即使被调用方没有或小于4个参数,调用函数仍然保留那么多的栈空间,这有助于在某些特殊情况下简化调用约定。
  • 除前四个参数以外的任何其他参数通过栈来传递,从右至左依次入栈。
  • 由调用函数负责清理调用栈。
  • 小于等于64位的整型或指针类型返回值由RAX传递。
  • 浮点返回值由XMM0传递。
  • 更大的返回值(比如结构体),由调用方在栈上分配空间,并有RCX持有该空间的指针并传递给被调用函数,因此整型参数使用的寄存器依次右移一格,实际只可以利用3个寄存器,其余参数入栈。函数调用结束后,RAX返回该空间的指针。
  • 除RCX,RDX,R8,R9以外,RAX、R10、R11、XMM4 和 XMM5也是易变化的(volatile)寄存器。
  • RBX, RBP, RDI, RSI, R12, R14, R14, and R15寄存器则必须在使用时进行保护。
  • 在寄存器中,所有参数都是右对齐的。小于64位的参数并不进行高位零扩展,也就是高位是无法预测的垃圾数据。
; int __cdecl main()
main proc near

var_28= dword ptr -28h  ;40
var_20= dword ptr -20h  ;32
var_18= dword ptr -18h  ;24 x
var_14= dword ptr -14h  ;20 y
var_10= qword ptr -10h  ;16 z

push    rdi
sub     rsp, 40h        ;64
mov     rdi, rsp
mov     ecx, 10h
mov     eax, 0CCCCCCCCh
rep stosd
mov     r9d, 4          ; d
mov     r8d, 3          ; c
mov     edx, 2          ; b
mov     ecx, 1          ; a
call    j_?func@@YAHHHHH@Z ; func(int,int,int,int)
mov     [rsp+48h+var_18], eax ; x
mov     [rsp+48h+var_20], 6
mov     [rsp+48h+var_28], 5
mov     r9d, 4          ; d
mov     r8d, 3          ; c
mov     edx, 2          ; b
mov     ecx, 1          ; a
call    j_?func2@@YAHHHHHHH@Z ; func2(int,int,int,int,int,int)
mov     [rsp+48h+var_14], eax ; y
movsd   xmm3, cs:__real@4010000000000000
movsd   xmm2, cs:__real@4008000000000000
movsd   xmm1, cs:__real@4000000000000000
movsd   xmm0, cs:__real@3ff0000000000000
call    j_?func2@@YANNNNN@Z ; func2(double,double,double,double)
movsd   [rsp+48h+var_10], xmm0 ;z
xor     eax, eax 
add     rsp, 40h
pop     rdi
retn
main endp

func2:

int __cdecl func2(int a, int b, int c, int d, int e, int f)
?func2@@YAHHHHHHH@Z proc near

var_18= dword ptr -18h
arg_0= dword ptr  8
arg_8= dword ptr  10h
arg_10= dword ptr  18h
arg_18= dword ptr  20h
e= dword ptr  28h
f= dword ptr  30h

mov     [rsp+arg_18], r9d
mov     [rsp+arg_10], r8d
mov     [rsp+arg_8], edx
mov     [rsp+arg_0], ecx
push    rdi
sub     rsp, 10h
mov     rdi, rsp
mov     ecx, 4
mov     eax, 0CCCCCCCCh
rep stosd
mov     ecx, [rsp+18h+arg_0]
mov     eax, [rsp+18h+arg_8]
mov     ecx, [rsp+18h+arg_0]
add     ecx, eax
mov     eax, ecx
add     eax, [rsp+18h+arg_10]
add     eax, [rsp+18h+arg_18]
add     eax, [rsp+18h+e]
add     eax, [rsp+18h+f]
mov     [rsp+18h+var_18], eax
mov     eax, [rsp+18h+var_18]
add     rsp, 10h
pop     rdi
retn
?func2@@YAHHHHHHH@Z endp