CHAPTER 18 UNDERSTANDING CIL AND THE ROLE OF DYNAMIC ASSEMBLIES
}
}
.entrypoint
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Hello CIL code!"
IL_0006: call void [mscorlib]System.Console::WriteLine(string)
IL_000b: nop
IL_000c: call string [mscorlib]System.Console::ReadLine()
IL_0011: pop
IL_0012: ret
// The default constructor.
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
}
First, notice that the *.il file opens by declaring each externally referenced assembly the current
assembly is compiled against. Here, you can see a single .assembly extern token set for the always
present mscorlib.dll. Of course, if your class library made use of types within other referenced
assemblies, you would find additional .assembly extern directives.
Next, you find the formal definition of your HelloProgram.exe assembly, which has been assigned a
default version of 0.0.0.0 (given that you did not specify a value using the [AssemblyVersion] attribute).
The assembly is further described using various CIL directives (such as .module, .imagebase, and so
forth).
After documenting the externally referenced assemblies and defining the current assembly, you find
a definition of the Program type. Note that the .class directive has various attributes (many of which are
actually optional) such as extends, shown here, which marks the base class of the type:
.class private auto ansi beforefieldinit Program
extends [mscorlib]System.Object
{ ... }
The bulk of the CIL code represents the implementation of the class’s default constructor and the
Main() method, both of which are defined (in part) with the .method directive. Once the members have
been defined using the correct directives and attributes, they are implemented using various opcodes.
It is critical to understand that when interacting with .NET types (s Ս