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:

  1. Verify the strong name signature of the assembly, if there is one present, via the StrongNameSignatureVerification() function (implemented in sscli\clr\src\dlls\mscorsn\strongname.cpp
  2. Handle all command line arguments, and store them for later use
  3. Initialize the CLR Execution Engine
  4. Execute the actual Main() method of the module loaded
  5. 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:

using System;
class Test
{
public static void Main(String[] args)
{
Console.WriteLine(args.Length);
}
}

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: EEStartup().

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 sscli\clr\src\vm\eeconfig.cpp

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!



Tomas Restrepo

Software developer located in Colombia.