Re: StdCall vs. CDecl
- From: Jeroen Mostert <jmostert@xxxxxxxxx>
- Date: Sat, 12 Jul 2008 01:30:33 +0200
Bob Altman wrote:
Yes and no. That's because the frame pointer will save the day. Just before returning, the callee will restore the caller's frame pointer, and in turn the caller will restore the stack pointer from that just before returning to its caller. By convention, local variables are addressed from the frame pointer, not the stack pointer, and the return address is just before the frame pointer, so everything works out.Because the worst thing that happens when you call a cdecl function as a stdcall function is that you leave the arguments on the stack after the function returns.
Thanks Jeroen, it's comforting to know that code that has seemed to work for a long time really should be expected to work. But...
I don't understand why "leaving arguments on the stack" is benign. The caller has his own data on the stack, which he expects to access relative to the stack pointer. If the caller expects the callee to "clean up the stack" (that is, restore the stack pointer to the value it had before the caller pushed a call frame onto the stack), then I would expect the caller to be thoroughly messed up after the call returns and the caller tries to get to its own data on the stack.
That is to say, after the call, the stack pointer will be *wrong*, but unless the caller uses stack-based addressing, they will simply not notice. There's extraneous crud on the stack, but as long as this doesn't cause the stack to overflow (which could happen if you call the function in a loop), nothing happens. The net effect is as if the caller allocated extra local variables without being aware of it.
That's for calling a cdecl function as stdcall. It's much worse if you call a stdcall function as cdecl, because this will cause the stack pointer to be wrong in the other direction on returning. If the caller now calls more functions, it must push the function arguments onto the (wrong) stack. In doing so, it will likely corrupt its own local variables, and eventually the return address. Needless to say, not a pretty sight.
The interop layer simply counts on the frame pointer staying sane. It has no local variables and it doesn't call any other functions, so it doesn't care about a wrong stack pointer -- it will simply be overwritten as part of returning. So I was sort of wrong, in that it's *both* not detected *and* silently fixed up.This is either not detected or silently fixed up, I don't know which one. My money's on the latter.
As I said above, I don't understand the "not detected" option. The "silently fixed up option" requires that the interop layer defends against this scenario by storing the old stack pointer away somewhere before the call so that it can detect that it must restore it after the call. I guess that's possible...
This is why it's perfectly safe to call a stdcall function as cdecl, or a cdecl function as stdcall from interop, as long as you get the number and size of arguments right. Disclaimer: it happens to be this way for 32-bit x86 in the current version of the framework. It is of course highly platform-specific, so using the right calling convention is still very much recommended.
You may want to reread what I wrote. stdcall is the default, cdecl is the odd one out (and stdcall is the default for DLL imports, unless you're on Windows CE). If you're asking why cdecl is the default in the C compiler, or why it's *not* the default for exports: cdecl allows varargs, which are used in C, but stdcall is more efficient since the cleanup code only occurs once (in the called function).On a related subject, is there an easy way to look at a DLL and figure out what calling convention it expects clients to use?No, you just have to know (or disassemble the functions). That's why it's customary to use stdcall for exported DLL functions, just like the Win32 API does across the board.
So, why is StdCall the default if it's usually incorrect? (I'm going to guess that it's a COM thing.)
COM, I'm happy to say, has absolutely nothing to do with this. But yes, in case you were wondering: all COM methods use stdcall too, with the special addition that they all pass a "this" pointer as their first argument.
--
J.
.
- Follow-Ups:
- Re: StdCall vs. CDecl
- From: Bob Altman
- Re: StdCall vs. CDecl
- From: Bob Altman
- Re: StdCall vs. CDecl
- References:
- StdCall vs. CDecl
- From: Bob Altman
- Re: StdCall vs. CDecl
- From: Jeroen Mostert
- Re: StdCall vs. CDecl
- From: Bob Altman
- StdCall vs. CDecl
- Prev by Date: Re: StdCall vs. CDecl
- Next by Date: Re: StdCall vs. CDecl
- Previous by thread: Re: StdCall vs. CDecl
- Next by thread: Re: StdCall vs. CDecl
- Index(es):
Relevant Pages
|