The history of calling conventions, part 3


Okay, here we go: The 32-bit x86 calling conventions.

(By the way, in case people didn't get it: I'm only talking in the context of calling conventions you're likely to encounter when doing Windows programming or which are used by Microsoft compilers. I do not intend to cover calling conventions for other operating systems or that are specific to a particular language or compiler vendor.)

Remember: If a calling convention is used for a C++ member function, then there is a hidden "this" parameter that is the implicit first parameter to the function.

All
The 32-bit x86 calling conventions all preserve the EDI, ESI, EBP, and EBX registers, using the EDX:EAX pair for return values.

C (__cdecl)

The same constraints apply to the 32-bit world as in the 16-bit world. The parameters are pushed from right to left (so that the first parameter is nearest to top-of-stack), and the caller cleans the parameters. Function names are decorated by a leading underscore.

__stdcall

This is the calling convention used for Win32, with exceptions for variadic functions (which necessarily use __cdecl) and a very few functions that use __fastcall. Parameters are pushed from right to left [corrected 10:18am] and the callee cleans the stack. Function names are decorated by a leading underscore and a trailing @-sign followed by the number of bytes of parameters taken by the function.

__fastcall

The first two parameters are passed in ECX and EDX, with the remainder passed on the stack as in __stdcall. Again, the callee cleans the stack. Function names are decorated by a leading @-sign and a trailing @-sign followed by the number of bytes of parameters taken by the function (including the register parameters).

thiscall

The first parameter (which is the "this" parameter) is passed in ECX, with the remainder passed on the stack as in __stdcall. Once again, the callee cleans the stack. Function names are decorated by the C++ compiler in an extraordinarily complicated mechanism that encodes the types of each of the parameters, among other things. This is necessary because C++ permits function overloading, so a complex decoration scheme must be used so that the various overloads have different decorated names.

There are some nice diagrams on MSDN illustrating some of these calling conventions.

Remember that a calling convention is a contract between the caller and the callee. For those of you crazy enough to write in assembly language, this means that your callback functions need to preserve the registers mandated by the calling convention because the caller (the operating system) is relying on it. If you corrupt, say, the EBX register across a call, don't be surprised when things fall apart on you. More on this in a future entry.

Comments (35)
  1. Ian Hanschen says:

    Raymond,

    Great series. Do you know the what & when as far as the 0xDCBAABCD cardinal passed in when calling wndprocs?

    -Ian

  2. Raymond Chen says:

    "More on this in a future entry."

  3. Frederik Slijkerman says:

    Honorable mention for Borland’s __fastcall convention, borrowed from Delphi, which passes the first three parameters in EAX, EDX, ECX and can be slightly more efficient.

  4. Florian says:

    Hmmm, you said that for __cdecl the parameters are pushed from right to left and for __stdcall they are pushed from left to right. But in the diagram on MSDN the stack looks identical for both calling conventions. I assume you are right and they are wrong?

  5. Raymond Chen says:

    D’oh, you’re right. __stdcall goes right to left, just like __cdecl. I’ll fix the body text.

  6. Gene Hamilton says:

    I was stepping through some code with the debugger to take a look for myself these calling conventions in action. (using VS.NET 2002 btw) And I noticed something odd. The ‘call’ instruction jumpes to an address which contains a single jmp instruction, which in turn ‘jmp’s to the real function. This is only in the ‘Debug’ version, not ‘Release’

    I also noticed after the call instruction the address it moves to, there are other jmp instructions in surrounding memory to other functions.

    Here is an example.



    call myfunc

    myfunc:

    jmp realmyfunc

    realmyfunc:





    ret

    My Question: What is the purpose of this jmp instruction between call and the actual function? I might be answering my own question here, but here it goes.

    I know if you include __declspec(dllimport) to an imported function from a dll, the code looks something like this:

    CALL DWORD PTR [0x00405030]

    otherwise if you don’t it generates this:

    CALL 0x0040100C

    •••

    0x0040100C:

    JMP DWORD PTR [0x00405030]

    Similar to was I was seeing. I know this happens because the compiler does not know if the function is static or in a dll. So it generates code like this CALL XXXXXXXX and leaves the linker to fill in the rest.

    Or is it done for another reason?

  7. Raymond Chen says:

    You probably turned on incremental linking.

    http://msdn.microsoft.com/library/en-us/vccore98/HTML/core.2f.incremental.asp

    On of the consequences of incremental linking called out in the documentation is "May contain jump thunks to handle relocation of functions to new addresses."

    If you understand what incremental linking is trying to do, the need for this becomes more obvious.

  8. Terry Denham says:

    This may be off topic but does the virtual function table behave similarly to these jump to address and then jump to final address like the incremental linking comment above.

  9. Mr. X says:

    Calling conventions are stupid. Any good compilers (such as GCC) won’t have them, except for the mandatory ‘extern "C"’ construct so that C++ programs can call C functions correctly.

  10. Raymond Chen says:

    Clearly you need a calling convention or the caller and callee could never communicate with each other. Perhaps you mean "multiple calling conventions are stupid". The hard part then is choosing the "one true" calling convention that works great for everybody.

  11. Mike Dimmick says:

    IIRC, the virtual function table is normally implemented simply as a table of addresses. The compiled code dereferences the vptr, which is the first member of the object, to locate the vtbl, then makes an indirect call through that.

    On some processors (e.g. the Itanium) the table also includes a global pointer value (2MB of module-relative data can be accessed via this pointer).

    I was about to ask why x86 has three calling conventions on 32-bit desktop Windows, but I assume it’s for much the same reasons as 16-bit code did (which you mentioned in part 1).

    IIRC, Windows CE on x86 only ever uses __cdecl, unless you’re using one of the old (Pocket PC 2000 or earlier) emulators, in which case it uses __stdcall (probably a misconfiguration when compiled…)

  12. asdf says:

    GCC has them, what are you talking about? http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#Function%20Attributes

    Search for stdcall, cdecl, fastcall, etc.

  13. project says:

    In "thiscall" you write that function names are decorated with information about every parameter in order to allow overloading. I think this should be also true about __cdecl functions that are not extern "C". Am I right?

  14. Raymond Chen says:

    You’re right, I confused calling convention with C++ decoration. Sorry, everybody.

  15. CW user says:

    Anyone using CodeWarrior?

    When I had code like this:

    #define EXPORT __declspec(dllexport)

    EXPORT BOOL CALLBACK EdrCenterText(…)

    the function GetProcAddress would return NULL.

    But call like this:

    GetProcAddress (…, "_EdrCenterText@16");

    worked and I could use the DLL.

    And then I changed the declaration to

    EXPORT BOOL __cdecl EdrCenterText (…)

    and

    GetProcAddress (…, "EdrCenterText")

    worked as advertised.

    I do not have VC to check this but it all seems odd to me!

  16. project says:

    To make the first GetProcAddress work, you should have a .def file like this:

    EXPORTS

    EdrCenterText

  17. Raymond Chen says:

    CW: The GetProcAddress issue is the subject that I alluded to in the opening paragraph of Part 2 of the calling conventions series. I’ll expand on it in a future entry.

  18. WC user says:

    I’ve looked back to Part 2 and now I apologise if I’ve had spoiled

    something, Raymond.

    It was just something that bothered me since last summer when

    it was for the first time I looked for some DLL stuff in Petzold and

    then spent whole night to force GetProcAddress() return

    something.

  19. Raymond Chen says:

    No problem. You couldn’t have known. It’s amusing to me that somebody guessed what I was referring to there completely by accident.

  20. Ben Combee says:

    <blockquote>Anyone using CodeWarrior?

    When I had code like this:

    #define EXPORT __declspec(dllexport)

    EXPORT BOOL CALLBACK EdrCenterText(…)

    the function GetProcAddress would return NULL.</blockquote>

    The problem is that you’re not putting the "__declspec(dllexport)" on the function, but instead putting it on the type.

    If you’d written

    #define EXPORT __declspec(dllexport)

    BOOL CALLBACK EXPORT EdrCenterText(…)

    it would have been OK. I know — I once was the x86 compiler engineer at Metrowerks, and I had my hand at implementing the declspec handling code.

  21. CW user says:

    Seems like I could have asked if there’s "Anyone here who wrote

    CodeWarrior?"

    Just shows how relevant Raymond’s blog is.

    And, no, reordering keywords didn’t help. Still worked only with _ and

    @16 mangling.

    And as an interresting note, all this time I was casting to CALLBACK function pointer:

    EXPORT BOOL __cdecl EdrCenterText (…);

    typedef BOOL (CALLBACK *EdrCenterTextProcType) (…);

    and then later in code:

    EdrCenterTextProcType plugProc;

    plugProc = (EdrCenterTextProcType) GetProcAddress (…, "EdrCenterText");

    This code worked OK, even though I casted __cdecl proc to __stdcall

    proc. I am not much of an expert for stack handling and calling

    conventions, but I would like to know for sure if this is bad in any way.

    (Windows or my app never crashed, but maybe I did’t wait long

    enough)

  22. Raymond Chen says:

    You people are too fast for me. I have an entry planned for Thursday to discuss this.

  23. Raymond Chen says:

    "does the virtual function table behave similarly to these jump to address and then jump to final address like the incremental linking comment above"

    The jump-to-jump is an artifact of incremental linking and is not part of the vtable layout rules.

  24. ??????,??????????? ?????????? calling convention: The history of calling conventions, part 1 The history of calling conventions, part 2 The history of calling conventions, part 3 The history of calling conventions, part 4: ia64 Why do member functions need to be…

  25. Hi,

    PVCAM is an ANSI C library of camera control and data acquisition functions.

    I’m trying to use PVCAM functions on my computer running under Windows XP but I didn’t manage to.

    The problem occures when I run the project in my CodeWarrior IDE which is the programming environment I choose in order to compile my

    project.

    The compiling process works well, but when I link the project that’s the message I’m given back:

    " Error : Undefined symbol: ‘__stdcall(0) pl_pvcam_init

    (_pl_pvcam_init@0)’

    referenced from ‘_main’ in Acquisition.c:15

    Acquisition.c line 15 "

    ("Acquistion.c" is my C code source)

    and I get the same error for each PVCAM functions (pl_pvcam_uninit, pl_seq_exp, etc.)

    I am a beginner in programming on CW 8, and I don’t really know where to search the problem.

    This could be due to the linker, the IDE, a confusion between libraries…

    Could you help me with this topic?

    Thanks.

  26. Putting together some skills you’ve already learned.

  27. Flier's Sky says:

    The history of calling conventions

  28. Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….

  29. Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….

  30. Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….

  31. Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….

Comments are closed.