NO

Author Topic: 64bit DLLs and __fastcall  (Read 15326 times)

Hydronium

  • Guest
64bit DLLs and __fastcall
« on: December 12, 2012, 04:05:46 AM »
So, tinkering with DLLs and libs and stuff and I noticed some things:

1) 64bit console programs force you to use the __fastcall calling convention. Why?
2) The 64bit DLL that the wizard creates defines WINAPI as __stdcall. Why not __fastcall?
3) Linking the sample 64bit DLL/Lib with a test 64bit console program gives me a compile-time warning, saying SampleFunction cannot be __stdcall and changes it to __cdecl. Why?
4) Regarding 3) with respect to 1) above, why is it allowed to change to __cdecl if the compiler won't let me manually change from __fastcall?

I can't really find anything about why these things are used (I can find plenty of information about what they do, but that doesn't help me in this case).

Does anyone happen to know? Thanks.
« Last Edit: December 12, 2012, 04:13:45 AM by Hydronium »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: 64bit DLLs and __fastcall
« Reply #1 on: December 12, 2012, 11:46:01 AM »
Calling conventions comes from the effort to speed up the process of passing parameters.
Substantially you have to consider only two big classis: _cdecl method and all the others.
The _cdecl method pushes all parameters on the stack in reverse order in respect to the declaration (meaning left to right) and it is a job of the calling routine to clear stack after return. The goal of this method is that the caller can supply any number of parameters to allow for that routines, like printf, scanf, etc, that take a variable number of arguments.
Because the pushing and popping of the stack is samwhat slow they wonders about other ways to speed up the process.
So was born the _stdcall, promoted mainly by MS, that push the arguments on the stack like the _stdcall, but clean the stack in the called routine using one fast instruction (LEAVE or RET n).
Moreover to speedup more they introduced the _fastcall where parameters are passed in registers. Because the old IAPX32 architecture had few available registers that was somewhat limited (with 32bits you can pass only 2 32bits parameters in well defined registers, the others will be placed on the stack anyway).
With 64 bits architecture much more registers where available making the use of _fastcall more convenient, so MS decided to adopt it as standard way (for 64bits).
When you write a consolle application, supposedly to be a C99 or C11 compliant application, the default calling convention is _cdecl, while Win-OS API's are write in _stdcall.
That's why the compiler force you to adopt _cdecl convention for main routine (because the runtime call it in _cdecl fashion).
In the end: WINAPI are compiled as _stdcall by MS choice so you must use that convention to call them. Windows general GUI programming should be _fastcall again for MS choice (but you should be able to overryde it). C-standard functions, and most notably variable arguments functions, must be _cdecl and you cannot override them.
Of course if you mix compiled convention and calling convention the result is 99% a program crash for stack corruption.
I hope this clarifies  :o
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

CommonTater

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #2 on: December 12, 2012, 02:59:45 PM »
In addition to Frankie's well written explaination  (Hi Frankie!)....

So, tinkering with DLLs and libs and stuff and I noticed some things:

1) 64bit console programs force you to use the __fastcall calling convention. Why?

This has to do with MS's standardized conventions...  64 bit programs use FastCall...


Quote
2) The 64bit DLL that the wizard creates defines WINAPI as __stdcall. Why not __fastcall?

Again this is about MS standards... most of their libraries are written with StdCall.
 
Also note that the Wizards in the new Project dialog are only examples, for use by beginners.  Most of us use the empty project set. 
 
You may want to take a look in the AddIns section, read the FAQ and then install the Workspace Manager addin which is designed to allow a more flexible organization of workspaces and projects.
 
Quote

3) Linking the sample 64bit DLL/Lib with a test 64bit console program gives me a compile-time warning, saying SampleFunction cannot be __stdcall and changes it to __cdecl. Why?

MS Standards again ... Entry points are always CDecl ... which has to do with who cleans up what mess on the stack afterwards.  (As Frankie explained)
 
Quote

 4) Regarding 3) with respect to 1) above, why is it allowed to change to __cdecl if the compiler won't let me manually change from __fastcall?

Again, it's the conventions talking ... FastCall is the standard for 64 bit programs and is the only one used by the 64 bit compiler.  You can change this setting outside the IDE, but be ready for things to get ugly if you do...
 
Quote
I can't really find anything about why these things are used (I can find plenty of information about what they do, but that doesn't help me in this case).

Does anyone happen to know? Thanks.

It pretty much comes down to decisions made by other people....
Sometimes you just gotta play along :D
 
 
 
« Last Edit: December 12, 2012, 03:02:12 PM by CommonTater »

Hydronium

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #3 on: December 12, 2012, 11:59:25 PM »
Again this is about MS standards... most of their libraries are written with StdCall.
 
Also note that the Wizards in the new Project dialog are only examples, for use by beginners.  Most of us use the empty project set. 

So does the Pelles C compiler ignore the __stdcall calling convention if it appears in a 64bit DLL, and replaces it with __fastcall?
And if so, (out of curiousity) why doesn't the wizard define it as __fastcall from the start, given that it's a 64bit DLL? Or is it merely following the Microsoft practice?

Also, I assume I am free to declare __fastcall as the calling convention in DLLs I may make?

MS Standards again ... Entry points are always CDecl ... which has to do with who cleans up what mess on the stack afterwards.  (As Frankie explained)
I'm not sure I follow you here. SampleFunction is not the entry point, DLLMain is. Are you talking about the DLLMain entry point being __cdecl? And if it is, do you mean that every other exported function must follow the same calling convention as the entry point? (Which I didn't think was the case, but again, I'm new to this DLL stuff).


Again, it's the conventions talking ... FastCall is the standard for 64 bit programs and is the only one used by the 64 bit compiler.  You can change this setting outside the IDE, but be ready for things to get ugly if you do...

If I understand what you mean: the compiler knows what it is doing, so it changes the __stdcall to __cdecl  instead of to __fastcall (even though I think in the end __cdecl is ignored by the compiler and __fastcall is used, from what I've read), but I am restricted to __fastcall in the compiler options to prevent me from messing around (because the compiler is smarter).

This was not meant to sound in any way sarcastic, I'm just trying to figure out the rationale between the calling convention conversions the compiler tells me it is doing which go against the user restrictions it is imposing on me. On the other hand, does the __fastcall calling convention option in the compiler options merely indicate the default convention to be used by function definitions/declarations that don't explicitly state one?

It pretty much comes down to decisions made by other people....
Sometimes you just gotta play along :D

Sounds about right. On the other hand, I would certainly like to know (even just a little) how Pelles C is implementing these conventions/decisions, and why. I just found it odd that everything I've seen regarding the sample DLL, etc, was everything other than __fastcall, and the compiler then changed it something that was still not __fastcall.

Thanks to you & Frankie, though the "why" is still haunting me a bit. :P

CommonTater

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #4 on: December 13, 2012, 01:17:47 AM »
Quote
Sounds about right. On the other hand, I would certainly like to know (even just a little) how Pelles C is implementing these conventions/decisions, and why. I just found it odd that everything I've seen regarding the sample DLL, etc, was everything other than __fastcall, and the compiler then changed it something that was still not __fastcall.

Thanks to you & Frankie, though the "why" is still haunting me a bit. :P

The "Why?" question is very simple... Because that's the way they decided to do it.
 
Seriously... it's no more complex than that. 
 
I make these same decisions a hundred times a day when writing code... Is do-while better than while in this case?  Should I error trap this?  Do I need an exception handler here? etc. They made decisions about how DLLs would be called.  If we want our software to be interoperable with other software, we play along.  Do remember that not every DLL is for use only with our own code ... The AddIns we write for POIDE are a perfect example; if POIDE can't call the entry point, it can't load the DLL.
 
The thing to understand here is that C stores variables --ints, structs, arrays etc-- in the executable module's stack. When these variables are created the stack is no longer as the calling function left it.  Some clean up process is going to have to happen when a function exits or the program is going to crash.

Why _cdecl at entry points?  Because in CDecl convention the calling process is responsible for cleaning up it's own stack space... DLLs have their own separate memory segments --thus their own stacks-- and cannot reach into the calling program to clean up when returning... CDecl lets the caller do it.
 
Why _stdcall for exported functions?  Because you want the parameters passed to the DLL's function to end up in the DLL's stack... requiring the DLL code, not the caller to clean up when returning from a function.
 
_fastcall may seem an ideal answer to that --values in registers and all that-- until you realize that some functions pass 10 and 20 parameters, many of which are still pushed to the stack when fastcall falls back to stdcall. For an example of "too many parameters" check out the CreateWindowEx() function...

Now you are faced with a simple "square peg <-> square hole" problem... so you play along.
 
Oh, and ....
For the question of the compiler being smarter than we are... Yes sometimes it is.
But in this case we can both be ceratin the compiler writer (Pelle) is...
 
« Last Edit: December 13, 2012, 01:40:07 AM by CommonTater »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: 64bit DLLs and __fastcall
« Reply #5 on: December 13, 2012, 09:52:42 AM »
I'm sorry I don't have a 64bits machine to check (for me 32 is still enough), but I also think that there is something strange if things moves as Hydronium says.
I stated that, because PellesC try to be as much compliant with MS as it can, it follows MS standard definitions.
But you should be able to override calling convention or whatever everytime you want. Behind the scene there is a generic C99-C11 compliant compiler after all, not an OS specific one.
Compiler apply forcing only if you don't 'explicitely' declare what you want, means that if you declare a function _stdcall in prototype and in function itself it 'must' follow your request.
The _cdeclare must be not overridable only for those C library functions, as the 'main' function, but not for your self defined one.
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

CommonTater

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #6 on: December 13, 2012, 11:14:10 AM »
I'm sorry I don't have a 64bits machine to check (for me 32 is still enough), but I also think that there is something strange if things moves as Hydronium says.

In the 64 bit compiler things are a little different, Frankie... 
The only calling convention the IDE allows for 64 bit is FastCall.

http://msdn.microsoft.com/en-us/library/ms235286(v=vs.100).aspx
 
From the Pelles C help file...
Quote
FASTCALL calling convention
 
x86:
The caller will pass the first two arguments which fit into ECX and EDX, and push all other arguments onto the stack before the call (right-to-left). Arguments are only passed in registers if they are of integral or pointer type. The callee must remove any arguments from the stack; an assembly procedure should use the ret n form. The name of a FASTCALL function is decorated as @funcname@numbytes, where numbytes is the total size of all (promoted) arguments to the function. This calling convention is rarely an improvement on X86; use the CDECL or STDCALL convention.

X64:
The caller will pass the first argument in register RCX, the second argument in register RDX, the third argument in register R8, the fourth argument in register R9, and push all other arguments onto the stack before the call (right-to-left). Arguments are only passed in registers if they are of integral or pointer type. The callee must remove the arguments from the stack. The name of a function is not decorated. This is the only calling convention used by X64.
« Last Edit: December 13, 2012, 11:29:48 AM by CommonTater »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: 64bit DLLs and __fastcall
« Reply #7 on: December 13, 2012, 12:26:16 PM »
Tater, as I said I cannot actually make any check, but if the compiler is really tied to MS diktat is a terrible mistake in my opinion!
Moreover if it is really standard compliant it *must* permit to the user to choose.
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

CommonTater

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #8 on: December 13, 2012, 03:37:43 PM »
Tater, as I said I cannot actually make any check, but if the compiler is really tied to MS diktat is a terrible mistake in my opinion!
Moreover if it is really standard compliant it *must* permit to the user to choose.

I agree.

I havent tested from the command line... the restriction is in the IDE... so I can't be 100% certain either.

Does anyone  know what Mingw64 or LCC64 does?




Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: 64bit DLLs and __fastcall
« Reply #9 on: December 13, 2012, 03:43:38 PM »
Maybe the MS extensions in compiler options?
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

CommonTater

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #10 on: December 13, 2012, 04:11:20 PM »
Maybe the MS extensions in compiler options?

LOL... good luck writing a Windows program without those!

Just tried that... The restriction is still in force ... 
 
You can specify in code. Just tested this...
Code: [Select]

 
void _stdcall test(void)  // _cdecl test(void)
  {
    MessageBox(0, L"This is a test", L" ", 0);
  } 
 
// cold start entry point
INT APIENTRY wWinMain(HINSTANCE Inst, HINSTANCE PrevInst, LPWSTR Cmdline, int CmdShow)
  { MSG msg = {0};
   
    test();
   
    return msg.wParam; }
« Last Edit: December 13, 2012, 04:41:02 PM by CommonTater »

Offline frankie

  • Global Moderator
  • Member
  • *****
  • Posts: 2096
Re: 64bit DLLs and __fastcall
« Reply #11 on: December 13, 2012, 04:55:18 PM »
It's very difficult to write a WIN program without WIN extensions ......  :D
Anyway the forcing to use MS formats is not a good thing anyway  :-\
It is better to be hated for what you are than to be loved for what you are not. - Andre Gide

CommonTater

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #12 on: December 13, 2012, 07:35:45 PM »
Anyway the forcing to use MS formats is not a good thing anyway  :-\

Agreed ...  they should be available but not forced.

Hydronium

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #13 on: December 13, 2012, 11:21:34 PM »

Why _stdcall for exported functions?  Because you want the parameters passed to the DLL's function to end up in the DLL's stack... requiring the DLL code, not the caller to clean up when returning from a function.


Ok. But the compiler switches the call to __cdecl, which goes against what Frankie says:

means that if you declare a function _stdcall in prototype and in function itself it 'must' follow your request.

Let me show you some pictures of the compiler warnings i get. Maybe I've been mistaken in my explanation/questions. Note: My DLL works, I just don't understand why Pelles C is so damn adamant about fiddling with calling conventions.

I've had "Enable Microsoft extensions" unchecked. This is a console program, so I didn't think to select it. Checking it removes the compiler calling convention strangeness, but changes main to __cdecl (which is mandatory, so that's fine). Would using a DLL require that option to be selected (I'm assuming not strictly any DLL, but DLLs with WINAPI defined perhaps?)? And what is it about Microsoft extensions that lets Pelles C relax and stop tweaking imported calls, but now it tweaks main?


CommonTater

  • Guest
Re: 64bit DLLs and __fastcall
« Reply #14 on: December 14, 2012, 12:51:50 AM »
The following is a bit inelligent but I'm not exactly sure how else to approach it,
so please bear with me...
 
 

Why _stdcall for exported functions?  Because you want the parameters passed to the DLL's function to end up in the DLL's stack... requiring the DLL code, not the caller to clean up when returning from a function.

Ok. But the compiler switches the call to __cdecl, which goes against what Frankie says:
 
Because the program calling the function *expects* a given calling convention. 
 
This is not mix and match time... it does matter.
 
 
Quote
My DLL works, I just don't understand why Pelles C is so damn adamant about fiddling with calling conventions.

Because it matters...  Really it does!
 

Quote
I've had "Enable Microsoft extensions" unchecked.

Frankly I'm surprised you could write a DLL without Microsoft Extensions... A DLL IS a Microsoft extension... Normal C-99 or C-11 does not create DLLs.
 
About your attachments...
In the first image it's telling you that your entry point can't be _fastcall ... which is correct.  The startup code that calls main() or winmain() expects a _cdecl function...
 
In the second case, the compiler is warning you that you can't use _declspec() to export functions without Microsoft extensions... which is also appropriate.
 
I'm sorry but I just don't see what your boggle is...
 
 
« Last Edit: December 14, 2012, 01:04:33 AM by CommonTater »