What is the meaning and usage of __stdcall?

48,033

Solution 1

All functions in C/C++ have a particular calling convention. The point of a calling convention is to establish how data is passed between the caller and callee and who is responsible for operations such as cleaning out the call stack.

The most popular calling conventions on windows are

  • __stdcall, Pushes parameters on the stack, in reverse order (right to left)
  • __cdecl, Pushes parameters on the stack, in reverse order (right to left)
  • __clrcall, Load parameters onto CLR expression stack in order (left to right).
  • __fastcall, Stored in registers, then pushed on stack
  • __thiscall, Pushed on stack; this pointer stored in ECX

Adding this specifier to the function declaration essentially tells the compiler that you want this particular function to have this particular calling convention.

The calling conventions are documented here

Raymond Chen also did a long series on the history of the various calling conventions (5 parts) starting here.

Solution 2

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.

Solution 3

__stdcall is a calling convention: a way of determining how parameters are passed to a function (on the stack or in registers) and who is responsible for cleaning up after the function returns (the caller or the callee).

Raymond Chen wrote a blog about the major x86 calling conventions, and there's a nice CodeProject article too.

For the most part, you shouldn't have to worry about them. The only case in which you should is if you're calling a library function that uses something other than the default -- otherwise the compiler will generate the wrong code and your program will probably crash.

Solution 4

Unfortunately, there is no easy answer for when to use it and when not.

__stdcall means that the arguments to a function are pushed onto the stack from the first to the last. This is as opposed to __cdecl, which means that the arguments are pushed from last to first, and __fastcall, which places the first four (I think) arguments in registers, and the rest go on the stack.

You just need to know what the callee expects, or if you are writing a library, what your callers are likely expect, and make sure you document your chosen convention.

Solution 5

It specifies a calling convention for a function. A calling convention is a set of rules how parameters are passed to a function: in which order, per address or per copy, who is to clean up the parameters (caller or callee) etc.

Share:
48,033

Related videos on Youtube

vehomzzz
Author by

vehomzzz

He adapts a lazy approach to a complex learning. His eclectic personality coupled with eccentric character caused much harm to masses, and to himself.

Updated on February 23, 2021

Comments

  • vehomzzz
    vehomzzz about 3 years

    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.

  • a3f
    a3f almost 9 years
    __stdcall and __cdecl only differ in responsibility for cleaning up after return (and the decoration). Argument passing is the same for both of them (right to left). What you are describing is the Pascal calling convention.
  • Pablo Ariel
    Pablo Ariel over 4 years
    What about x64 builds?