Stdcall and Cdecl

Calling a function that can be either cdecl or stdcall

It can be done following way:

          mov     esi, esp

push arg3
push arg2
push arg1
call [SomeExternalProc]

mov esp, esi ; now the stack is always properly cleaned

The external procedure will preserve esi. Or you can use any other register preserved by the external procedure or even memory variable - local or global.

Good, the order of the arguments is the same for CDECL and STDCALL - in reverse order. (Left-most arg at the lowest address.) So they're compatible except for where ESP points on return. Both conventions agree on which registers are call-preserved vs. call-clobbered.

Confused about __cdecl and __stdcall calling convention

"Clean up the stack" actually means "adjust the stack pointer so that it matches the changes done to stack pointer when arguments and return address were pushed onto stack". In your example it would be irrelevant who cleans up the stack - you would get the same undefined behavior, because you try to access a stack-allocated variable that is overwritten by a series of later pushes of arguments needed to perform printf().

If you only write code in C or C++ you should not care of the exact details like "who cleans up the stack" - those details are critical only when writing in assembly. In C and C++ just be sure to mark function pointers with right calling conventions before calling a function through those pointers and the compiler will do the rest.

__cdecl or __stdcall on Windows?

I just did some real-world testing (compiling DLLs and applications with MSVC++ and MinGW, then mixing them). As it appears, I had better results with the cdecl calling convention.

More specifically: the problem with stdcall is that MSVC++ mangles names in the DLL export table, even when using extern "C". For example foo becomes _foo@4. This only happens when using __declspec(dllexport), not when using a DEF file; however, DEF files are a maintenance hassle in my opinion, and I don't want to use them.

The MSVC++ name mangling poses two problems:

  • Using GetProcAddress on the DLL becomes slightly more complicated;
  • MinGW by default doesn't prepend an undescore to the decorated names (e.g. MinGW will use foo@4 instead of _foo@4), which complicates linking. Also, it introduces the risk of seeing "non-underscore versions" of DLLs and applications pop up in the wild which are incompatible with the "underscore versions".

I've tried the cdecl convention: interoperability between MSVC++ and MinGW works perfectly, out-of-the-box, and names stay undecorated in the DLL export table. It even works for virtual methods.

For these reasons, cdecl is a clear winner for me.

Which one to choose, stdcall or cdecl?

As a general rule, functions exported from Microsoft's CRT implementations use the cdecl calling convention.



Related Topics



Leave a reply



Submit