CHAPTER 1: CLR’S Execution Model
What is a managed
module?
A managed module is a standard 32-bit Microsoft windows
portable executable (PE32) file or a standard 64-bit Windows portable
executable (PE32+) file that requires a CLR to execute. Code from all languages
in .NET is compiled into managed module. A managed module contains the
following sections. Note that a managed module cannot be run directly. It first
needs to be encapsulated into an assembly to executable.
mso-yfti-tbllook:480;mso-padding-alt:0in 5.4pt 0in 5.4pt;mso-border-insideh:
.5pt solid windowtext;mso-border-insidev:.5pt solid windowtext'>
1.
PE32 or PE32+ header
An assembly with the standard PE32 header can be run on
both Windows 32 bit and 64 bit editions, while the assembly with the PE32+
header can only be run on Windows 64 bit editions. This header contains
information such as the type of file (GUI, CUI or DLL) and timestamp. It also
contains information about any native win32 code included in the assembly.
2.
CLR Header
This header is used by CLR to execute the assembly. It
contains information such as the required version of CLR, MethodDef
entry to define the entry point for the assembly, location and size of
metadata and resources, strong name, flags etc.
3.
Metadata
This header contains two tables. One for types and members
stored in the assembly and the other for types and members referenced by the
assembly.
4.
Intermediate Language (IL) code
An intermediate code into which every .NET language is
compiled. It is analogous of Java Byte code.
What is an assembly?
A .NET assembly is the logical groups of one or more managed
modules or resources files. The compiler takes one or more managed modules
and/or resources, merging it into an assembly. The compiles list down all the
managed modules/resource files it added in the manifest (present in PE32 header)
of the assembly. So we have assembly like the one given below

Checking for .NET
Runtime:
One can determine if .NET runtime is installed in the target
system or not by checking for MSCoreEE.dll in the %SystemRoo%\System32 folder.
To check for a particular version of .NET, we need to look inside the windows
registry. In the register find the subkey for the
corresponding version under the registry key
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\.NETFramework\policy
The subkey for versions start with
a ‘v’, so if you are looking for version 2.0 of .NET frame look for class=SpellE>subkey v2.0 etc.
How a .NET assembly
is executed:
When a .NET executable assembly is run, Windows first check
the PE header in the assembly to determine if the application requires a 32 bit
address space (PE32) or a 64 bit address space (PE32+). It also checks for the
CPU architecture information in the header. If all is well and the Windows
determine that it can execute the assembly, it loads the MsCoreEE.dll into the
process’s address space. The MsCoreEE.dll is usually found in the C:\Windows\System32
directory. Windows now calls a method inside MsCoreEE.dll which initialized the
CLR, loads the executable assembly and calls its entry point method (
causing the assembly to execute.
Just before the CLR start executing the
(entry point method) function, it performs the following tasks
- CLR
detects all the types present in the Main function. - CLR
stores an internal data structure for each type with an entry for every
function in the type. - CLR
point the function in the type to point to a special function of Just in
Time (JIT) compiler
Let’s suppose we have the following Main function
public
static void
{
style='mso-tab-count:1'> class=GramE>Console.Write(“Hello”);
style='mso-tab-count:1'> class=GramE>Console.Write(“World”);
}
Now when the Main function is executed, it calls the “Write”
function of the “Console” class. As at the start of execution all methods point
to the JIT compiler, a call of JIT compiler is made. The JIT compiler determines
which methods is being called and to which type it belongs. It then searches
for the defining assembly. It then verifies and compiles the code for the
function into (from IL) native CPU instructions.
First
style='position:absolute;mso-ignore:vglayout;z-index:9'>
class=shape>
Call to Console.Write function
src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwGIaw6dQ1iJOU82oXFezwd0POqZtetZcu7IHF0L55q68gF1ORpVebJgYMCLJgPlrq3MtEGJDWYKufLyQ_u5qP2O-iTroSEzPP9psFDGa1E_DrW8IjudEv4Wi6o3SHVW9wNihPCH8DgVfo/s320/image002.gif" v:shapes="_x0000_s1039 _x0000_s1038 _x0000_s1040 _x0000_s1041 _x0000_s1042 _x0000_s1043 _x0000_s1044 _x0000_s1045 _x0000_s1046 _x0000_s1049 _x0000_s1050 _x0000_s1051 _x0000_s1071 _x0000_s1074 _x0000_s1075">
Now it updates the reference set in the data structure of
“Console” class for the “Write” function to now point to Native CPU instructions
just compiled. Finally JIT transfers control to the native CPU instructions for
the “Write” function of “Console” class. When execution to the first call to
“Write” function completes, the next line in the “Main” function executes. This
line again call the “Write” function of “Console” class. This time however the
internal data structure for the “Write” function of “Console” class point to
the native CPU instructions. So the native CPU code is directly executed
without any involvement of JIT compiler.
Second
style='position:absolute;mso-ignore:vglayout;z-index:10'>
class=shape>
Call to Console.Write function
src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMYGA9J03U2Ghx1qRdQQUBnW7LM924jHU8AEJucJrYT06R8JtBE7zs2fJ7TFv8szaXKKKdO7OJFpYDe9ETUATtYnoY5aI_sVdTDZvm7IgiQgglaRQAXP9a_Fed53bDUoGJ-OASx7jAmfd5/s320/image003.gif" v:shapes="_x0000_s1052 _x0000_s1053 _x0000_s1054 _x0000_s1055 _x0000_s1056 _x0000_s1057 _x0000_s1058 _x0000_s1059 _x0000_s1060 _x0000_s1061 _x0000_s1062 _x0000_s1063 _x0000_s1076 _x0000_s1077">
Native Code Generator
(NGEN.exe):
NGen.exe is a tool that ships with Microsoft .NET framework.
It can be used to compile IL code to native win32 code at install time. It can
improve the performance of an assembly eliminating the JIT phase completely.
When you run NGEN.exe on an assembly, NGEN compiles the code
into a file in a directory such as “C:\Windows\Assembly\NativeImages_v2.0.50727_32”.
The name of the directory includes the version of the CLR as well as the architecture
(32bit or 64bit).
Now when the CLR loads an assembly, it looks under the “class=SpellE>NativeImages” folder to see if there is a corresponding class=SpellE>NGen’d file (compiled code) for the assembly. If the CLR fails
to find a corresponding native image for the assembly, it loads JIT compiles
the code as usual. However, if a corresponding native image for the assembly
does exist, the CLR execute the compiled code in the native image, instead of
JIT compiling the assembly.
However, there are several problems associated with NGEN.exe
- It
cannot be used to protect intellectual property rights
Files produced by NGEN tool cannot
be used as stand-alone files thereby protecting the intellectual property from infiltration.
CLR also needs the managed assembly to execute the code.
- class=SpellE>NGen files can get out of synchronization
Before executing the class=SpellE>NGen’d file the CLR, checks various characters of the
compiled code and the execution. In case of any mismatch the CLR reverts back
to the JIT compiler to complete its job. Some of the characteristics include
Assembly module version ID, Reference assembly’s version IDs, Processor Type,
CLR version, Build type etc.
- Bad
Load time performance on Rebasing
An NGen’d
file is compiled with a base address (with csc
/baseline command line switch). When a Windows executes an NGen’d
file, it checks to see if it can load the file at it’s
specified address. If it cannot, it needs to reallocate the file to a different
address and in doing so it needs to recalculate all the addresses present in
the assembly. This can slow down the starting time of an NGen’d
file to a significant level. In comparison a .NET assembly doesn’t incur such lag
because all of its addresses are calculated at runtime.
- Moderate
compilation
NGen’d
code is also inferior as compared to its JIT compiled counterpart, mainly due
to the reason that the compiler cannot make enough assumption but the execution
environment.