Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the meaning and usage of __stdcall?

I've come across __stdcall a lot these days.

MSDN doesn't explain very clearly what it really means, when and why should it be used, if at all.

I would appreciate if someone would provide an explanation, preferably with an example or two.

like image 759
vehomzzz Avatar asked Aug 20 '09 14:08

vehomzzz


People also ask

What are cdecl and Stdcall used for?

__cdecl is the default calling convention for C and C++ programs. Because the stack is cleaned up by the caller, it can do vararg functions. The __cdecl calling convention creates larger executables than __stdcall, because it requires each function call to include stack cleanup code.

What is the use of __ cdecl?

The __cdecl keyword instructs the compiler to read and write a parameter list by using C linkage conventions. To set the __cdecl calling convention for a function, place the linkage keyword immediately before the function name or at the beginning of the declarator.

How does the Stdcall calling convention work?

The stdcall calling convention is a variation on the Pascal calling convention in which the callee is responsible for cleaning up the stack, but the parameters are pushed onto the stack in right-to-left order, as in the _cdecl calling convention. Registers EAX, ECX, and EDX are designated for use within the function.

What is Fastcall C++?

The __fastcall calling convention specifies that arguments to functions are to be passed in registers, when possible. This calling convention only applies to the x86 architecture.


1 Answers

This answer covers 32-bit mode. (Windows x64 only uses 2 conventions: the normal one (which is called __fastcall if it has a name at all) and __vectorcall, which is the same except for how SIMD vector args like __m128i are passed).


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 esp,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.

(Optimizing compilers can sometimes leave space for args allocated across multiple cdecl calls made from the same function and mov args into it, instead of always add esp, n / push. That saves instructions but can increase code-size. For example gcc -maccumulate-outgoing-args always does this, and was good for performance on older CPUs before push was efficient.)

Variadic functions like printf() are 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 it's legal in C to pass more args to printf than the format-string references (they'll be silently ignored). Hence only __cdecl supports variadic functions, where the caller does 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 @ plus the number of bytes of parameters passed. By this mechanism, it's not possible to call a function with the wrong amount of parameters. The caller and callee definitely agree on returning with a ret 12 instruction for example, to pop 12 bytes of stack args along with the return address.

You'll get a link-time or runtime DLL error instead of having a function return with ESP pointing somewhere the caller isn't expecting. (For example if you added a new arg and didn't recompile both the main program and the library. Assuming you didn't fool the system by making an earlier arg narrower, like int64_t -> int32_t.)

__fastcall (cl /Gr ...)

These function names start with an @ sign and are suffixed with the @bytes count, much like __stdcall. The first 2 args are passed in ECX and EDX, the rest are passed on the stack. The byte count includes the register args. As with __stdcall, a narrow arg like char still uses up a 4-byte arg-passing slot (a register, or a dword on the stack). 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 

Note that in C++, the normal name-mangling mechanism that allows function overloading is used instead of @8, not as well. So you'll only see actual numbers in extern "C" functions. For example, https://godbolt.org/z/v7EaWs for example.

like image 82
edge Avatar answered Oct 12 '22 05:10

edge