Calling conventions in Windows on x86 It is 2 AM in the night and i don't feel like sleeping so i thought why not i start my blog and here i am with my first blog entry ever.
People who do programming on Windows in C/C++, might wonder sometime, what is the __cdecl or __stdcall in front of a function declaration? These compiler specific prefixes are basically a way to tell the compiler, how to push the function arguments on the stack and how to pop them off the stack. These prefix defines the contract between Caller (the one who calls a function) and Callee (the called function) for argument passing. This contact is known as Calling convention. Usually we should need only one calling convention for argument passing but Windows compilers provide more than one convention because of historical and performance reasons. The three calling conventions available on windows are:
1. __cdecl
2. __stdcall
3. __fastcall
__fastcall and __stdcall are more efficient than __cdecl but they don't support variable argument functions which is required for functions like printf.
To understand these calling conventions in details, lets take a look at the sample program:
Code1:
int 2: __cdecl testCDecl(int a, int b, int c, int d) 3: { 4: return a; 5: } 6: 7: int 8: __stdcall testStdcall(int a, int b, int c, int d) 9: { 10: return a; 11: } 12: 13: int 14: __fastcall testFastcall(int a, int b, int c, int d) 15: { 16: return a; 17: } 18: 19: 20: int _tmain(int argc, _TCHAR* argv[]) 21: { 22: testCDecl(1, 2, 3, 4); 23: testStdcall(1, 2, 3, 4); 24: testFastcall(1, 2, 3, 4); 25: 26: return 0; 27: }
__cdecl is the original C calling convention. In this convention Caller pushes the function arguments on the stack from right to left. Because this convention allows the caller to push variable number of arguments on the stack (for functions like printf), Callee doesn't know how many bytes it needs to take off from the stack. Hence it is the responsibility of caller to pop these arguments off the stack.
__stdcall (standard calling convention) or also known as PASCAL convention is only supports fixed number of parameters in a function call. In this convention parameters are pushed from right to left but Callee removes them off the stack. This calling convention is the standard calling convention for Win32 APIs.
__fastcall is slightly optimized version of __stdcall convention. It passes first two parameters in ecx and edx respectively but rest of the arguments are pushed on the stack from right to left (similar to other conventions).
Below i will show the disassembly of the code shown above to describe how the parameters are pushed and popped off the stack for the above discussed calling conventions.
Code1:
2: 3: 4: int _tmain(int argc, _TCHAR* argv[]) 5: { 6: push ebp 7: mov ebp,esp 8: 9: testCDecl(1, 2, 3, 4); 10: push 4 <- Push arguments right to left 11: push 3 <- on the stack for __cdecl funciton 12: push 2 <- Each argument takes 4 bytes on x86 13: push 1 14: call testCDecl (401630h) 15: add esp,10h <- testCDecl is called, now pop off the 16: <- arguments off the stack by adding 16 17: <- or 10h from esp (or stack pointer) 18: 19: testStdcall(1, 2, 3, 4); 20: push 4 <- Push arguments right to left 21: push 3 <- on the stack for __stdcall funciton 22: push 2 <- Each argument takes 4 bytes on x86 23: push 1 24: call testStdcall (401640h) 25: <- Notice here that after calling testStdcall 26: <- we don't remove the arguments from the 27: <- stack because in __stdcall functions the 28: <- are removed by the Callee and not caller 29: 30: testFastcall(1, 2, 3, 4); 31: push 4 32: push 3 33: mov edx,2 <- Notice here that first and second arguments 34: mov ecx,1 <- are passed in ecx and edx respectively 35: call testFastcall (401650h) 36: <- same as __stdcall, callee pops the arguments 37: <- off the stack 38: 39: return 0; 40: xor eax,eax 41: } 42: pop ebp 43: ret 44: 45: 46: 47: 48: 49: int 50: __cdecl testCDecl(int a, int b, int c, int d) 51: { 52: push ebp 53: mov ebp,esp 54: return a; 55: mov eax,dword ptr [a] 56: } 57: pop ebp 58: ret 59: 60: 61: 62: 63: 64: int 65: __stdcall testStdcall(int a, int b, int c, int d) 66: { 67: push ebp 68: mov ebp,esp 69: return a; 70: mov eax,dword ptr [a] 71: } 72: pop ebp 73: ret 10h <- return to the return address and remove 74: <- 16 bytes (or 4 arguments a, b, c and d) 75: <- from the stack 76: 77: 78: 79: 80: 81: 82: int 83: __fastcall testFastcall(int a, int b, int c, int d) 84: { 85: push ebp 86: mov ebp,esp 87: sub esp,8 88: mov dword ptr [ebp-8],0CCCCCCCCh 89: mov dword ptr [ebp-4],0CCCCCCCCh 90: mov dword ptr [ebp-8],edx 91: mov dword ptr [ebp-4],ecx 92: return a; 93: mov eax,dword ptr [a] 94: } 95: mov esp,ebp 96: pop ebp 97: ret 8 <- return to the return address and remove 98: <- 8 bytes or 2 arguments c and d from the 99: <- stack (since a and b were passed in 100: <- registers)
For C++ member functions "this" pointer is treated as the first argument and is passed in the ecx register. Other parameters are passed as per the calling convention rules described above.
For x86-64 based systems, Microsoft has unified the calling convention but that on some other sleepless night.
|