Monday 21 October 2013

__stdcall vs __cdecl.

During the long, hard, but yet beautiful process of learning C++ programming for Windows, you have probably been curious about the strange specifiers that sometime appear in front of function declarations, like __cdecl, __stdcall,__fastcall, WINAPI, etc. After looking through MSDN, or some other reference, you probably found out that these specifiers specify the calling conventions for functions. In this article, I will try to explain different calling conventions used by Visual C++ (and probably other Windows C/C++ compilers). 

Traditionally, C function calls are made with the caller pushing some parameters onto the stack, calling the function, and then popping the stack to clean up those pushed arguments.


/* example of __cdecl */

push arg1
push arg2
push arg3
call function
add sp,12 // effectively "pop; pop; pop"

Note: The default convention — shown above — is known as __cdecl.


The other most popular convention is __stdcall. In it the parameters are again pushed by the caller, but the stack is cleaned up by the callee. It is the standard convention for Win32 API functions (as defined by the WINAPI macro in <windows.h>), and it's also sometimes called the "Pascal" calling convention.

/* example of __stdcall */
push arg1 
push arg2 
push arg3 
call function // no stack cleanup - callee does this

This looks like a minor technical detail, but if there is a disagreement on how the stack is managed between the caller and the callee, the stack will be destroyed in a way that is unlikely to be recovered.

Since __stdcall does stack cleanup, the (very tiny) code to perform this task is found in only one place, rather than being duplicated in every caller as it is in__cdecl. This makes the code very slightly smaller, though the size impact is only visible in large programs.

Variadic functions like printf() are almost impossible to get right with__stdcall, because only the caller really knows how many arguments were passed in order to clean them up. The callee can make some good guesses (say, by looking at a format string), but the stack cleanup would have to be determined by the actual logic of the function, not the calling-convention mechanism itself. Hence only __cdecl supports variadic functions so that the caller can do the cleanup.       


Linker symbol name decorations:

As mentioned in a bullet point above, calling a function with the "wrong" convention can be disastrous, so Microsoft has a mechanism to avoid this from happening. It works well, though it can be maddening if one does not know what the reasons are.
They have chosen to resolve this by encoding the calling convention into the low-level function names with extra characters (which are often called "decorations"), and these are treated as unrelated names by the linker. The default calling convention is__cdecl, but each one can be requested explicitly with the /G? parameter to the compiler.

__cdecl   (cl /Gd ...)


All function names of this type are prefixed with an underscore, and the number of parameters does not really matter because the caller is responsible for stack setup and stack cleanup. It is possible for a caller and callee to be confused over the number of parameters actually passed, but at least the stack discipline is maintained properly.


__stdcall   (cl /Gz ...)

These function names are prefixed with an underscore and appended with the number of bytes of parameters passed. By this mechanism, it's not possible to call a function with the "wrong" type, or even with the wrong number of parameters.

__fastcall   (cl /Gr ...)

These function names start with an @ sign and are suffixed with the @parameter count, much like 

__stdcall.

Examples:
Declaration                            decorated name
----------------------------------------------------
void __cdecl    foo(void);               _foo
void __cdecl    foo(int a);               _foo
void __cdecl    foo(int a, int b);      _foo
void __stdcall  foo(void);             _foo@0
void __stdcall  foo(int a);             _foo@4
void __stdcall   foo(int a, int b);       _foo@8
void __fastcall   foo(void);              @foo@0
void __fastcall   foo(int a);             @foo@4
void __fastcall   foo(int a, int b);     @foo@8

We'll note that the decorated names are never visible to a C program: they are strictly a linker facility, and the linker will never resolve one kind of reference with the "wrong" one.


C calling convention (__cdecl)

This convention is the default for C/C++ programs (compiler option /Gd). If a project is set to use some other calling convention, we can still declare a function to use __cdecl:

int __cdecl sumExample (int a, int b);


The main characteristics of __cdecl calling convention are:

1. Arguments are passed from right to left, and placed on the stack.
2. Stack cleanup is performed by the caller.
3. Function name is decorated by prefixing it with an underscore character '_' .

Standard calling convention (__stdcall)

This convention is usually used to call Win32 API functions. In fact, WINAPI is nothing but another name for__stdcall:

#define WINAPI __stdcall

We can explicitly declare a function to use the __stdcall convention:

int __stdcall sumExample (int a, int b);


Also, we can use the compiler option /Gz to specify __stdcall for all functions not explicitly declared with some other calling convention.

The main characteristics of __stdcall calling convention are:
1. Arguments are passed from right to left, and placed on the stack.
2. Stack cleanup is performed by the called function.
3. Function name is decorated by prepending an underscore character and appending a '@' character and the number of bytes of stack space required.
Because the stack is cleaned by the called function, the __stdcall calling convention creates smaller executables than __cdecl, in which the code for stack cleanup must be generated for each function call. On the other hand, functions with the variable number of arguments (like printf()) must use __cdecl, because only the caller knows the number of arguments in each function call; therefore only the caller can perform the stack cleanup.

Note:

__cdecl is the default calling convention for C and C++ programs. The advantage of this calling convetion is that it allows functions with a variable number of arguments to be used. The disadvantage is that it creates larger executables.
__stdcall is used to call Win32 API functions. It does not allow functions to have a variable number of arguments.
__fastcall attempts to put arguments in registers, rather than on the stack, thus making function calls faster.
Thiscall calling convention is the default calling convention used by C++ member functions that do not use variable arguments.

No comments: