1

When you compile a program using MSVC and Microsoft's CRT you'll notice it auto-magically figures out where your main function is, what the prototype is and calls into it.

This doesn't have much to do with calling conventions (as posted before in a "similar" question) since you can override the default calling convention AND on Windows x64 the only calling convention is __fastcall.

With that being said, how does Microsoft's CRT implementation figure out which main function you've declared and then includes a header file with definitions such as _SCRT_STARTUP_MAIN, _SCRT_STARTUP_WMAIN, _SCRT_STARTUP_WINMAIN, _SCRT_STARTUP_WWINMAIN, etc etc?

For example, if you make a function like so and compile as an exe: int __cdecl main()... the linker will somehow do the steps as follows:

  • Somehow figure out which function you're using, no matter the function prototype. As long as it's one of the valid main prototypes, it'll work regardless of the arguments or return types.

  • Then it will somehow use the file exe_main.cpp which has a definition _SCRT_STARTUP_MAIN defined right before including exe_common.inl.

  • Finally, inside exe_common.inl there's a check for the macro _SCRT_STARTUP_MAIN and will finally invoke the correct main function (once again, magically, without having issues with multiple definitions).

Is there any way whatsoever to mimic this behavior in my own project so I can provide any type of main function and auto-magically figure out what type of main function is being used?

f4rw3llvv
  • 53
  • 5
  • 2
    [*/ENTRY*](https://learn.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol?view=vs-2019) – RbMm Jul 20 '20 at 16:16
  • @RbMm I'm aware of this setting, but it's unrelated to my question. This will just set the "real" entry point but doesn't explain the behaviour of how MSVC++ is pulling in files based on function declarations. Maybe a "hidden" linker script is being ran? Who knows, but my question still stands. – f4rw3llvv Jul 20 '20 at 18:34
  • no, the entry point choice determinate **all**. every entry point ( *mainCRTStartup*, *wmainCRTStartup*, *WinMainCRTStartup*, *wWinMainCRTStartup* ) call different your function - *main*, *wmain*, *WinMain*, *wWinMain* ) – RbMm Jul 20 '20 at 18:41
  • Windows x64 does also have `__vectorcall`. It only differs from the default 64-bit `__fastcall` for SIMD vector args, though. – Peter Cordes Jul 20 '20 at 22:44
  • @RbMm Read my question again, I'm well aware of what `/ENTRY` does on MSVC++. My question is asking how microsoft pulls in the `exe_common.inl` file and chooses the correct macro to define for ANY of the entry points used. I'm trying to re-implement the behavior like microsofts CRT somehow auto-magically does. It's literally somehow telling the linker to include a specific cpp file to choose your entry point. It has nothing to do with `/ENTRY` and doesn't help me in this case at all. – f4rw3llvv Jul 20 '20 at 23:05
  • @PeterCordes Yeah, I'm aware. Thank you. My question still stands. – f4rw3llvv Jul 20 '20 at 23:08
  • you mistake. linker not use any *c/c++/h/inl* and not choose/expand any macro. all answer in select entry point - (*mainCRTStartup, wmainCRTStartup, WinMainCRTStartup, wWinMainCRTStartup*) - every entrypoint call separate function ( *main, wmain, WinMain, wWinMain*) - this is all – RbMm Jul 20 '20 at 23:11
  • @RbMm Sorry, but unless you can give an explanation to my question then please stop posting about the /ENTRY compiler option and the CRT entry point names. It’s not relevant to my question and does not explain how the default CRT behavior works. As it currently stands in the latest CRT provides by Microsoft — when you compile an application the linker DOES pull in the files I mentioned and defines the Macro I specified. Go search for “ _SCRT_STARTUP_MAIN” in the CRT code shipped with Visual Studio and you’ll see for yourself. – f4rw3llvv Jul 21 '20 at 09:11
  • @f4rw3llvv look like you not understand how linker work. linker not work with any macros, src files, etc. linker have at input several obj/lib files. and entry point. if you not explicit set entrypoint - linker based on subsystem option try use some default. after entrypoint selected - all became unequivocally. and your question/problem i be say unclear in current edition – RbMm Jul 21 '20 at 09:59
  • *auto-magically figures out where your main function is, what the prototype is and calls into it... Somehow figure out which function you're using, no matter the function prototype.* - all this is **false**. when you build with CRT entry point not inside your code but inside CRT. and you direct or indirect specify entry point for linker. this entry point call your function. the prototype must be absolute concrete, it not flexible. but different entry points call different your functions. also need note that in case wmain and main - this is __cdecl functions - so let use|declare less arguments – RbMm Jul 21 '20 at 10:07
  • *Finally, inside exe_common.inl there's a check for the macro _SCRT_STARTUP_MAIN..* - **no**. you look for internal CRT code. all this is used when *CRT* compiled (so already done). not related direct to your code. *CRT* you got already in binary (lib/dll) format. here of course much more functions that you use. but only referenced will be linked to your binary. *CRT* containing **several** standard entry points in *lib* which you pass to linker. concrete entry point in crt call absolute concrete your "entry". question only in which crt entry will be selected – RbMm Jul 21 '20 at 10:15
  • @RbMm Right, I’m aware of all of this. My question states how can I mimic this behavior. I’m well aware of how this is all packaged into their CRT libs by default. If you’re able to explain and show example code on how to declare ALL of the different CRT entry points and mimic the behavior of how the linker finds multiple versions of your main function (wether it has no args, 2 args or 3 args) without spewing errors about “already declared” then I will mark you as an answer. I don’t want to set /ENTRY as a single static entry point — i want to mimic the CRT to automatically find ANY entry – f4rw3llvv Jul 21 '20 at 14:59
  • better ask - what you concrete need implement and for what – RbMm Jul 21 '20 at 15:01
  • @RbMm The internal CRT code almost seems magical and mostly undocumented. If someone can provide example code of how to implement CRT entry points (mainCrtStartup, WinMainCrtStartup) and call into main/wmain/winmain all while being able to call main no matter the function prototype — thay’s the goal. I’ve tried this all myself and it becomes a problem when you need to call “main” with 0/2/3 arguments. The linker will complain about multiple definitions being changed. Also if MSVC CRT code isnt bringing in a macro definiton in an inl file, then why is it there in the first place? it finds main. – f4rw3llvv Jul 21 '20 at 15:07
  • every crt entry point call concrete function prototype and this is hard-coded. only 4 prototypes ( *main, wmain, WinMain, wWinMain*) but need take to account that it *c* style functions and *main, wmain* - *__cdecl* - so agruments count not part of signature (unlike *WinMain, wWinMain* for x86) – RbMm Jul 21 '20 at 15:59
  • @RbMm So how do we mimic this behavior in our own code in a C++ like manner? You can’t declare multiple “main” functions and call it — it will just error since the definition differs from the first one. This is where i believe the compiler pulls in the .INL file to choose the correct entry point. This must be some “magic” the MSVCRT provides when you don’t use /NODEDAULTLIB. Could you please provide code for your claims? I’m just curious how to expect to get around having multiple main definitions. – f4rw3llvv Jul 21 '20 at 17:29
  • compiler choose not entry point, not pulls in the .INL files. and i not view any "magic" here. *So how do we mimic this behavior* - ask another question - what concrete you need and for what (sense) – RbMm Jul 21 '20 at 18:29
  • @RbMm To re-implement a CRT of your choosing, I don't have a neeed for SEH or any of the other stuff the CRT provides (and is also always compiled into a binary despite turning it off). Also just as a note, if you don't think it's magic then I urge you to go try it yourself and you can see what happens. There is an INL file which checks the entry point based on a macro, it's definitely used as it's shipped with Visual Studio. I have my reasons and unless you can provide a proof of concept on how to provide all the entry points to your own CRT. I'll edit my main proof with my broken POC. – f4rw3llvv Jul 21 '20 at 20:23

0 Answers0