windows編程--x64調用約定


windows32位程序包括stdcall,thiscall,fastcall,cdecl,clrcall,vectorcall,nakedcall等調用方式,x64位程序默認使用新的fastcall調用方式。

這種調用方式得益於x64平台寄存器數量的增加。  

x64 fastcall調用約定

空間大於8字節的參數用參照傳遞,不能把一個參數分割到多個寄存器中進行傳遞;前四個整型或指針類型參數從左到右由RCX,RDX,R8,R9依次傳遞,前四個浮點類型參數從左到右由XMM0,XMM1,XMM2,XMM3依次傳遞。第五個和其后的參數通過棧傳遞,參數從右到左一次入棧。

__m128 類型, 數組和字符串不能通過值傳遞,只能通過指針。

結構體和union如果大小是1,2,4,8字節,用整數的傳參方式;否則通過指針傳遞。

小於等於64位的整型或指針類型返回值由RAX傳遞。
浮點返回值由XMM0傳遞。

更大的返回值(比如結構體),由調用方在棧上分配空間,並由RCX持有該空間的指針並傳遞給被調用函數,因
此整型參數使用的寄存器依次右移一格,實際只可以利用3個寄存器,其余參數入棧。函數調用結束后,RAX返
回該空間的指針(就是rcx的值)。

Varargs(可變參數)也通過上述方式進行傳參。

調用函數為前四個參數在調用棧上保留相應的空間,稱作shadow space或spill slot。即使被調用方沒有或小
於4個參數,調用函數仍然保留那么多的棧空間,這有助於在某些特殊情況下簡化調用約定。
由調用函數負責清理調用棧。

除RCX,RDX,R8,R9以外,RAX、R10、R11、XMM4 和 XMM5也是易變化的(volatile)寄存器。


RBX, RBP, RDI, RSI, R12, R14, R14, and R15寄存器則必須在使用時進行保護。
在寄存器中,所有參數都是右對齊的。小於64位的參數並不進行高位零擴展,也就是高位是無法預測的垃圾數據.

參數傳遞示例

 1 int func1(int a, int b, int c, int d, int e, double f) {
 2     return 9;
 3 }
 4 
 5 int func2(int a, int b, int c, int d, int e, double f,int g) {
 6     return 90;
 7 }
 8 
 9 int main() {
10     int r = func1(1, 2, 3, 4, 5, 6.6);
11     int r2 = func2(1, 2, 3, 4, 5, 6.6,7);
12     return 0;
13 }

反匯編:

 1 int r2 = func2(1, 2, 3, 4, 5, 6.6,7);
 2 00007FF65F263D0E  mov         dword ptr [rsp+30h],7  
 3 00007FF65F263D16  movsd       xmm0,mmword ptr [__real@401a666666666666 (07FF65F269BB0h)]  
 4 00007FF65F263D1E  movsd       mmword ptr [rsp+28h],xmm0  
 5 00007FF65F263D24  mov         dword ptr [rsp+20h],5  
 6 00007FF65F263D2C  mov         r9d,4  
 7 00007FF65F263D32  mov         r8d,3  
 8 00007FF65F263D38  mov         edx,2  
 9 00007FF65F263D3D  mov         ecx,1  
10 00007FF65F263D42  call        func2 (07FF65F261375h)  
11 00007FF65F263D47  mov         dword ptr [r2],eax  
12     return 0;
13 00007FF65F263D4A  xor         eax,eax  

返回結構體示例:

 1 S1 func3(int a, int b, int c, int d) {
 2     S1 s{6,7,8,9,11,12};
 3     return s;
 4 }
 5 int main() {
 6     int r = func1(1, 2, 3, 4, 5, 6.6);
 7     int r2 = func2(1, 2, 3, 4, 5, 6.6,7);
 8     S1 r3 = func3(1, 2, 3, 4);
 9     return 0;
10 }

反匯編

 1 S1 func3(int a, int b, int c, int d) {
 2 00007FF68A271750  mov         dword ptr [rsp+20h],r9d  
 3 00007FF68A271755  mov         dword ptr [rsp+18h],r8d  
 4 00007FF68A27175A  mov         dword ptr [rsp+10h],edx  
 5 00007FF68A27175E  mov         qword ptr [rsp+8],rcx   //這里rcx存放着調用函數提前分配好的用於存放返回的結構體的指針,由於rcx用來存放返回值地址,第一個參數改由edx傳遞,后面的參數一次后延。
 6 00007FF68A271763  push        rbp  
 7 00007FF68A271764  push        rsi  
 8 00007FF68A271765  push        rdi  
 9 00007FF68A271766  sub         rsp,110h  
10 00007FF68A27176D  lea         rbp,[rsp+20h]  
11 00007FF68A271772  mov         rdi,rsp  
12 00007FF68A271775  mov         ecx,44h  
13 00007FF68A27177A  mov         eax,0CCCCCCCCh  
14 00007FF68A27177F  rep stos    dword ptr [rdi]  
15 00007FF68A271781  mov         rcx,qword ptr [rsp+138h]  
16 00007FF68A271789  lea         rcx,[__AD28E9E4_源@cpp (07FF68A280000h)]  
17 00007FF68A271790  call        __CheckForDebuggerJustMyCode (07FF68A271082h)  
18     S1 s{6,7,8,9,11,12};
19 00007FF68A271795  mov         dword ptr [s],6  
20 00007FF68A27179C  mov         dword ptr [rbp+0Ch],7  
21 00007FF68A2717A3  mov         dword ptr [rbp+10h],8  
22 00007FF68A2717AA  mov         dword ptr [rbp+14h],9  
23 00007FF68A2717B1  mov         dword ptr [rbp+18h],0Bh  
24 00007FF68A2717B8  mov         dword ptr [rbp+1Ch],0Ch  
25     return s;
26 00007FF68A2717BF  lea         rax,[s]  
27 00007FF68A2717C3  mov         rdi,qword ptr [rbp+110h]  //rbp+110h里存放的是從rcx里傳進來的調用函數分配的用戶存放返回值的指針
28 00007FF68A2717CA  mov         rsi,rax  
29 00007FF68A2717CD  mov         ecx,18h  
30 00007FF68A2717D2  rep movs    byte ptr [rdi],byte ptr [rsi]  //上面幾行是將s結構體的數據復制到調用函數分配好的空間里(rcx傳進來的)
31 00007FF68A2717D4  mov         rax,qword ptr [rbp+110h]  //將返回值指針賦值給rax
32 }

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM