Today, I started looking at the runtime startup code. This journey begins in _CorExeMain2(), implemented in
sscli\clr\src\vm\ceemain.cpp, which is declared like this:
__int32 STDMETHODCALLTYPE _CorExeMain2( // Executable exit code.
PBYTE pUnmappedPE, // -> memory mapped code
DWORD cUnmappedPE, // Size of memory mapped code
LPWSTR pImageNameIn, // -> Executable Name
LPWSTR pLoadersFileName, // -> Loaders Name
LPWSTR pCmdLine) // -> Command Line
The names of the arguments the function takes should be self-explanatory, specially if you look at the code in clix.exe. The function itself is fairly short, consisting mostly of error handling code. In a nutshell, it does this:
- Verify the strong name signature of the assembly, if there is one present, via the StrongNameSignatureVerification() function (implemented in
- Handle all command line arguments, and store them for later use
- Initialize the CLR Execution Engine
- Execute the actual Main() method of the module loaded
- Once this ends, cleanly shutdown the Execution Engine
There isn't much more to say about this, but I can't help but nitpick and poke around the things that are probably far less relevant than anything you might read elsewhere :) For example, I had no idea the Runtime could take command line arguments itself.... just try writing a simple application like this:
public static void Main(String args)
Now, try running it with a command line like "
App.exe -COR somethinghere"... and low-and-behold, it prints 0! The magic of this is inside the
ParseCor() method of the
CorCommandLine class, implemented in
sscli\clr\src\vm\clsload.cpp. I must admit, though, that I was unable to actually find anywhere in the code they where used.
OK, now going back to the serious stuff. The EE initialization is done through the
CoInitializeEE(), which handles some locking stuff and then calls the
TryEEStartup() function, which itself calls the real potato:
At a glance, EEStartup does, among other things, initialize the Stack Probes and the event store, load configuration data (not the application .config file, though, not yet.), initialize the thread manager, remoting services and context system, the JIT, the GC, SyncBlock cache, the P/Invoke services, security subsystem, and, of course, setup the default domain. The configuration loading code is done by the
EEConfig class, implemented in
While this function is not really complicated per se, it sets up the entire VM execution environment. Thus, really understanding everything that's going on will take some more time. This is where the good stuff begins!