Date: | January 8, 2004 / year-entry #8 |
Tags: | history |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040108-00/?p=41163 |
Comments: | 35 |
Summary: | 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... |
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.
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)
Comments are closed. |
Raymond,
Great series. Do you know the what & when as far as the 0xDCBAABCD cardinal passed in when calling wndprocs?
-Ian
"More on this in a future entry."
Sorry :)
-Ian
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.
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?
D’oh, you’re right. __stdcall goes right to left, just like __cdecl. I’ll fix the body text.
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?
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.
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.
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.
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.
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…)
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.
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?
You’re right, I confused calling convention with C++ decoration. Sorry, everybody.
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!
To make the first GetProcAddress work, you should have a .def file like this:
EXPORTS
EdrCenterText
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.
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.
No problem. You couldn’t have known. It’s amusing to me that somebody guessed what I was referring to there completely by accident.
<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.
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)
You people are too fast for me. I have an entry planned for Thursday to discuss this.
"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.
??????,??????????? ?????????? 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…
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.
Putting together some skills you’ve already learned.
The history of calling conventions
Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….
Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….
Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….
Ever since v1, corprof.idl has contained the following ominous comment above the typedefs for FunctionEnter/Leave/Tailcall….