[Moved an old post from 2006 to my new blog]
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:
- __cdecl
- __stdcall
- __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:
int __cdecl testCDecl(int a, int b, int c, int d) { return a; } int __stdcall testStdcall(int a, int b, int c, int d) { return a; } int __fastcall testFastcall(int a, int b, int c, int d) { return a; } int _tmain(int argc, _TCHAR* argv[]) { testCDecl(1, 2, 3, 4); testStdcall(1, 2, 3, 4); testFastcall(1, 2, 3, 4); return 0; }
__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.
; Disassembly of _tmain ; int _tmain(int argc, _TCHAR* argv[]) ; { push ebp mov ebp,esp ; testCDecl(1, 2, 3, 4); push 4 ; <- Push arguments right to left push 3 ; <- on the stack for __cdecl funciton push 2 ; <- Each argument takes 4 bytes on x86 push 1 call testCDecl (401630h) add esp,10h ; <- testCDecl is called, now pop off the ; <- arguments off the stack by adding 16 ; <- or 10h from esp (or stack pointer) ; testStdcall(1, 2, 3, 4); push 4 ; <- Push arguments right to left push 3 ; <- on the stack for __stdcall funciton push 2 ; <- Each argument takes 4 bytes on x86 push 1 call testStdcall (401640h) ; <- Notice here that after calling testStdcall ; <- we don't remove the arguments from the ; <- stack because in __stdcall functions the ; <- are removed by the Callee and not caller ; testFastcall(1, 2, 3, 4); push 4 push 3 mov edx,2 ; <- Notice here that first and second arguments mov ecx,1 ; <- are passed in ecx and edx respectively call testFastcall (401650h) ; <- same as __stdcall, callee pops the arguments ; <- off the stack ; return 0; xor eax,eax ; } pop ebp ret ; Disassembly of testCDecl ; int ; __cdecl testCDecl(int a, int b, int c, int d) ; { push ebp mov ebp,esp ; return a; mov eax,dword ptr [a] ; } pop ebp ret ; Disassembly of testStdCall ; int ; __stdcall testStdcall(int a, int b, int c, int d) ; { push ebp mov ebp,esp ; return a; mov eax,dword ptr [a] ; } pop ebp ret 10h ; <- return to the return address and remove ; <- 16 bytes (or 4 arguments a, b, c and d) ; <- from the stack ; Disassembly of testFastcall ; int ; __fastcall testFastcall(int a, int b, int c, int d) ; { push ebp mov ebp,esp sub esp,8 mov dword ptr [ebp-8],0CCCCCCCCh mov dword ptr [ebp-4],0CCCCCCCCh mov dword ptr [ebp-8],edx mov dword ptr [ebp-4],ecx ; return a; mov eax,dword ptr [a] ; } mov esp,ebp pop ebp ret 8 ; <- return to the return address and remove ; <- 8 bytes or 2 arguments c and d from the ; <- stack (since a and b were passed in ; <- 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, there is unified the calling convention but that on some other sleepless night.
This posting is provided “AS IS” with no warranties and confers no rights.