Free Web Hosting by Netfirms
Web Hosting by Netfirms | Free Domain Names by Netfirms

@!$h@'s Code World
Welcome to @!$h@'s Free CodeWorld
     .NET Framework
 
   MFC/Win32/COM
 
   C# Section 
 
   Common Language Runtime - CLR 

About the Author

Aisha Ikram is a senior software engineer and has been working mainly in VC++ 6, MFC, ATL, COM. She now a days working on .NET framework and C#. She is also a member of various developer's website like code project, code guru, mind cracker and C# corner. This article is also published on code project and code guru and is being highly appreciated. You can write the author at ais_ikr@yahoo.com for your feedback concerns and problems.

Introduction

This article describes the technical deep downs of Common Language Runtime and .NET framework and demonstrate the nuts and bolts of CLR through command line .NET specific tools using Hello world program.

What exactly is the CLR?

If you are developing a C#, VB.NET or even a COBOL.NET application and it's running on the .NET Framework, it's running on the CLR, that is Common Language Runtime.

CLR is the combination of things.

  1. It's the things that takes the way your program has been compiled into the Intermediate Language, and actutally turns that into the machine code, and actually executes it.
  2. It provides the support services that are needed by running program like memory management, exception handling, compilation services and reflection services.
  3. It's a file format that is used for transferring files between the systems that are using the .NET framework.
  4. It's the runtime library framework or the running functionality which is at the next level of system up.
  5. It's the system for building applications.

In older days, p-code compiler was used for writing Pascal and Modula-2 programs, was kind of multi-language thing sitting on top of it, CLR is the great grandson of the same kind of idea. CLR is the partiuclar design and the instruction set which supports about 15-20 languages (like C++, JAVA, Pascal, Visual Basic, JScript, COBOL, FORTRAN and others i.e; for the time being, although you can write your own language to support for .NET) with support for object oriented and functional languages as well as compilation Just-in-Time compilers which gives high performance.

Initial Objectives of CLR

  1. Multi-language design
    • Support multiple language paradigms.
  2. Security
    • you could write your program, download it from net and run it and know that it's safe.
  3. XML web services
    • Way of building cross platform, cross architecture, scalable services, scalable to devices.
  4. Simplified deployment
    • All versions of a component running at the same time on a single machine, which made DLL installation simple.
    • Installation is simplied by just copying the required files and no need of adding registry entries, although you can do so if you want to.

Application Domain

Scalabilty in CLR is acheived through something called application domain. A normal process takes awhile to create and kill a process. And when a high performance is required, you want one process to be used for many, many things, this is what the transaction processing systems do and this is what CLR is trying to do with an easier programming model. The idea is that you could have as many application domains as you like inside of a single operating system process. The combination of security system, design of instruction set, the virtual instruction set that was designed for .NET guarantees that programs running in this application domain can't read or write the memory that's being used by this application domain.

Deployment And Versioning

Managed code is the stuff that you get when you compile to the Intermediate Language and it does not use the registry mechanism at all. Previously when registry was used for deployment, installing an application needs to write its settings into the registry and when the next version of those things was installed over it writing its settings in the same place in the registry. The result was a collision. you always had the latest version and the previous versions were washed away. Now using CLR, you can't have both of them on the machine.

Mechanism

Application is deployed into a directory, and the components which are referenced are looked in the same directory. If they are not there, shared location is searched. In shared location, everything has a version number on it. So when the code is compiled, CLR captures the version number of the thing you wanted and then easily locate it using the version number. So you have multiple programs using different versions of the same component running in the same process in different application domains. So every time a new version comes in, you can easily install it with out disturbing the older versions. New applications that need the new component will use it and old apps, if it's compatible, will use the new one, otherwise will use the old one. If you want to share the components of your applications, you need to install it otherwise mostly you deploy by just copying it where you want it to be. Installation for shareable components is required because you've got to split up between the shared components and the non shared components. The shared components need to go in a shared place, so you do have to install them as shared. But because the version number was built in, they don't get confused with previous versions and future versions.

Multiple Versions of CLR

On any given machine you can have multiple versions of an application, multiple versions of libraries and so on. What about the CLR, the Common Language Runtime itself? Well, It's possible that as new versions of the CLR come out, you may have a server that has multiple versions of the CLR itself, so within any one given process, you may run application A with version 1 on version 1 of the CLR, and another process, you may have a different application running a different version of that on a different version of the CLR.

CLR and Java Virtual Machine

Comparing CLR from virtual machine standpoint, we talk about JAVA Virtual Machine.
  1. JVM specifically tied to the JAVA language where as CLR currently supports 15-20 languages, but can support more languages, even you can write your own language for .NET.
  2. JVM adds virtual method dispatch, and it adds interfaces and things that were in JAVA but it doesn't support pointers. CLR has both.
  3. CLR is designed from the beginning for compilation and it uses Just-in-Time compilation. In this technique it compiles the methods down to native code and then executed that native code. It goes much faster. The JAVA virtual machine was originally released as an interpreter.

Microsoft's first JIT compiler was designed for the interpreter, and that's a important engineering difference. So the language design of the CLR virtual machine was changed to make it assume that there's a compiler there which resulted in compact code and faster native code.

CLR Executable Format

 Executable of CLR is an Assembly which consists of three parts of information.

PE Header 

Which was present before CLR came along and consists of a table of contents telling you where the rest of the information in that file lies.

Metatdata 

Any source file no matter how big it is divided into two parts. The first part which contains declarations like classes, functions, properties and events is captured as metadata. metadata is new for CLR.

IL (Intermediate Language) converted into Binary

The second part of the source file is the code, which is inside the braces, ends up as IL binary. This part of the exe file is new for CLR which never existed before.

 In version before Windows XP, OS was not fimiliar with this new type of file contents and there was a fancy   way of lauching the CLR before the code was reached. From Xp forward, OS is aware of these types of files.

Step by Step Demo of CLR Thing

  1. Basic Requirements

    1. Install .NET Framework, if not already installed.

    2. Set environment variables by running "VSVAR32.BAT" file which should be located in your C:\Program Files\Microsoft Visual Studio .NET\Common 7\Tools (Default installation directory of the Visual Studio).

    3. Create a working directory somewhere on your system.

    4. Copy the C# program shown below and save it in a text file named "Hello.cs". (Hello.cs is also packed in the demo project download).

    // Hello World Program in C#
    class Hello
    {
    	public static void Main()
    	{	
    		System.Console.WriteLine("Hello C# World");
    	}
    }
    
  2. Visual Studio .NET Command Prompt

    Goto the Visual Studio .NET Command Prompt (From Visual Studio .NET Tools). 1. Type csc /debug hello.cs at command prompt (CSC is the command line C# compiler), it compiles hello.cs and produces two output files, hello.exe and hello.pdb file which contains the debug symbols. 

    2. Simply type hello and it will execute the hello.exe program.

    Snapshot of command prompt showing compilation

  3. .NET PE File or Assembly

    1. Hello.exe in terms of CLR is called an assembly and important to note here is that this exe file is NOT the same that we build using Visual C 6 or Visual Basic 6 and so on. This is assembly packed in exe file, and consists of three sets of information mentioned above i.e; PE Header, metadata and IL code.

    2. In order to view the contents of hello.exe; type dumpbim /all hello.exe at the command prompt. The first part is the PE header .

    dumpbin snap shot

    3. Look for the number of directory entries in the PE file, it shows Hexadecimal 10, which is equal to 16 in decimal. New is the 15th entry is that of COM descriptive directory which tells you from this table of contents where to reach the metadata and IL. The presence of this COM entry tells that PE file is a managed file executed by the CLR.

    4. By scolling down, you can see the metadata, figure highlights the matadata part. It can be identified easily by location the four letters B-S-J-B. They are always BSJB, and it's the first four characters of the string that constitutes the metadata. BSJB is actually the initials of the names of four guys worked on building metadata engine. you can also see the version v1.0.3705 of the runtime.

    metadata part

    5. Next is the IL code , which is not the native code to execute. IL code is converted to native through JIT or Just in Time Compiler which is the part of CLR.

    6. Another part of this dump is the mscoree.dll which is under _CorExeMain, is the front door to CLR. The details of what actually this mscoree.dll does is described under Just-in-Time Compiler .

    mscoree

  4. .NET Framework IL Disassembler

    1. In order to view the metadata and IL itself, a .NET tool called ILDASM is used, which produces from IL binary the friendly and easy to detect IL source code which is NOT quite a de-compiler to convert the binary into the higher language code, yet it's easy to read.
    Type  ILDASM hello.exe at the command prompt to launch IL DASM. IL DASM shows hello.exe in a tree view. From the view menu you can change the information you want to see and you can also dump the tree and data into a text and IL file.

    IL DASM

    Double clicking the tree item will open up a new window with the assembly code. Here is collective code of all tree items which i saved in hello.il (packed in the demo project).
    //  Microsoft (R) .NET Framework IL Disassembler.  Version 1.0.3705.0
    //  Copyright (C) Microsoft Corporation 1998-2001. All rights reserved.
    
    
    // warning : THIS IS A PARTIAL DISASSEMBLY, NOT SUITABLE FOR RE-ASSEMBLING
    
    // Only shown items having accessibility: 
    //Public Private Family Assembly
    .assembly extern 'mscorlib'
    {
      .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )// .z\V.4..
      .ver 1:0:3300:0
    }
    .assembly 'Hello'
    {
      // --- The following custom attribute is added automatically
     //, do not uncomment -------
      //  .custom instance void 
    
    ['mscorlib']'System.Diagnostics'.'DebuggableAttribute'
    ::.ctor(bool,
      //      bool) = ( 01 00 01 01 00 00 ) 
      .hash algorithm 0x00008004
      .ver 0:0:0:0
    }
    .module 'Hello.exe'
    // MVID: {46BA2A86-BC48-4EB4-A189-817DCB801FE3}
    .imagebase 0x00400000
    .subsystem 0x00000003
    .file alignment 512
    .corflags 0x00000001
    // Image base: 0x02e50000
    //
    // ========== CLASS STRUCTURE DECLARATION =========
    //
    .class private  auto ansi beforefieldinit 'Hello'
           extends ['mscorlib']'System'.'Object'
    {
    } // end of class 'Hello' 
    
    // ============================================
    // ======== GLOBAL FIELDS AND METHODS ========
    
    // =============================================
    
    // ======= CLASS MEMBERS DECLARATION ===========
    //   note that class flags, 'extends' and 'implements' clauses
    //          are provided here for information only
    .class private  auto ansi beforefieldinit 'Hello'
           extends ['mscorlib']'System'.'Object'
    {
      .method public hidebysig static void  'Main'() cil managed
      {
        .entrypoint
        // Code size       11 (0xb)
        .maxstack  1
        .language '{3F5162F8-07C6-11D3-9053-00C04FA302A1}',
    '{994B45C4-E6E9-11D2-903F-00C04FA302A1}',
    	 '{5A869D0B-6611-11D3-BD2A-0000F80849BD}'
    
    	// Source File 'D:\Code World\My Code\dotNET\CLR\Hello.cs' 
    	//000005: 	System.Console.WriteLine("Hello C# World");
    
        IL_0000:  ldstr    "Hello C# World"
        IL_0005:  call void ['mscorlib']'System'.'Console'::'WriteLine'(string)
    	//000006: 	}
        IL_000a:  ret
    
      } // end of method 'Hello'::'Main'
    
      .method public hidebysig specialname rtspecialname 
              instance void  .ctor() cil managed
      {
        // Code size       7 (0x7)
        .maxstack  1
        IL_0000:  ldarg.0
        IL_0001:  call instance void ['mscorlib']'System'.'Object'::.ctor()
        IL_0006:  ret
      } // end of method 'Hello'::.ctor
    
    } // end of class 'Hello'
    
    // ================================================
    //********* DISASSEMBLY COMPLETE ****************	

    2. .ctor is the constructor for class instance Hello, In original C# code, there is no constructor, but all classes in C# are parented by the top level class called System.Object.
    Main
    method constains the string instruction, the call to the WriteLine method and simply returns.

  5. Cordbg - Command Line Debugger

    As we are down at the nuts and bolts level, we will use a low-level tool and it's called cordbg which is the command line debugger.
    Type cordbg hello at the command-line prompt. The screen shows the following output. cordbg automatically loads the hello program by run hello command.

    C:\dotNET\CLR>cordbg hello
    Microsoft (R) Common Language Runtime Test Debugger \
    Shell Version 1.0.3705.0 
    Copyright (C) Microsoft Corporation 1998-2001. All rights reserved. 
    
    (cordbg) run hello 
    Process 1976/0x7b8 created. 
    Warning: couldn't load symbols for 
    C:\windows\microsoft.net\framework\v1.0.3705\mscorlib.dll 
    [thread 0x1d8] Thread created. 
    005: System.Console.WriteLine("Hello C# World"); 
    
    Type show. The asterisk (*) in the line 005 shows the instruction that we are about to execute. The original code is displayed because we have a source code there and PDB symbols.
    (cordbg) show
      
    001: class Hello 
    002: { 
    003: public static void Main() 
    004: { 
    005:* System.Console.WriteLine("Hello C# World"); 
    006: }
    007: }
  6. Tracking Application domain and assembly load/unload events

    Application domain and assembly related load events can be seen by enabling the app mode, setting the value 1 and then running the hello application shows additional load information. (app is a short for AppDomainLoads, command line can also accepts the shorts)
    (cordbg) mode app 1
    AppDomain and Assembly load events are displayed
    
    (cordbg) run hello    
    Terminating current process...
    Process exited.
    Process 940/0x3ac created.
    Appdomain #1, DefaultDomain -- Created
    Assembly 0x00095b94, c:\windows\microsoft.net\framework\v1.0
            in appdomain #1, DefaultDomain
    Warning: couldn't load symbols for c:\windows\microsoft.net\
    [thread 0x778] Thread created.
    Assembly 0x000989ec, C:\dotNET\CLR\hello.
            in appdomain #1, hello
    
    005:      System.Console.WriteLine("Hello C# World");
    
    (cordbg) go 
    Hello C# World
    [thread 0xaf4] Thread created.
    Assembly 0x0009a60c, C:\dotNET\CLR\hello.exe -- Unloaded
    Assembly 0x0009ba34, c:\windows\microsoft.net\framework\
    v1.0.3705\mscorlib.dll -- Unloaded
    Appdomain #1, hello.exe -- Exited
    [thread 0xaec] Thread exited.
    Error: hr=0x80131301, The debuggee has terminated
    Process exited.
    

    The Assembly loaded, that is the mscorlib is the sizeable DLL that holds lots and lots of base classes that come with the system.
    You can switch the application domain and assembly load event tracking off, by Typing mode app 0.

    Tip: You can also turn on ModuleLoads to see each file coming in. You can enable ClassLoads to see every single class as it came in and then got unloaded at the end of the program. There are lots of commands in cordbg, just type help at the cordbg prompt and it will list all the commands.


  7. Looking for multiple CLR versioning

    when we were looking inside the program itself there was a thing called mscoree and that in fact is the front door to the CLR. There's only one version of mscoree on any machine, and it's in the system 32 subdirectory of Window. It's a very small DLL which is called the shim DLL and its sole job in life is to look on the machine and find out if there's one or more possible versions installed of the CLR itself, and figure out which is the correct one to launch for this application. Having made that decision, it then loads in that version of the CLR as required. That version of the CLR then looks at the entry point for the code it's about to execute, in our case the Main method for the hello world program. CLR invokes the JIT, the Just-in-Time compiler to convert the IL method by method as required by IL down to Pentium native language, and then hand it off to the chip to execute.

  8. Just-in-Time Compiler

    Load the hello program by typing run, and then type wt at the command line.
    (cordbg) wt
           2         Hello::Main
          13          Console::.cctor
    	.
    	.
    	.
          31               __ConsoleStream::Write
    Hello C# world!
    	.
    	.
    	.
        3383 instructions total
  9. Now one thing to be noted, the program is being executed within the environment of a de-bugger and when we compiled it with \debug, JIT compiler do NOT do any optimizations on the code. So it simply takes the IL code as given, convert that into native machine code that can be executed. But within cordbg we can override this behavior of JIT compiler by enabling JIT mode using the mode command mode JIT 1. After setting this mode JIT will produce optimized code. Now if you load that hello program, you can see the difference. Type WT, hey it seems a bit faster.
    (cordbg) mode jit 1
    JIT's will produce optimized code
    
    (cordbg) run hello
    Terminating current process...
    Process exited.
    Process 3236/0xca4 created.
    Warning: couldn't load symbols for
       c:\winnt\microsoft.net\framework\v1.0.3427\ms
    corlib.dll
    [thread 0x514] Thread created.
    
    003:            System.Console.WriteLine("Hello C# world!");
    (cordbg) wt
           4         Hello::Main
          10          SyncTextWriter::WriteLine
          27           TextWriter::WriteLine
          30          String::CopyTo
          26           TextWriter::WriteLine
          50            StreamWriter::Write
          23             StreamWriter::Flush
           7              DefaultEncoder::GetBytes
          46             CodePageEncoding::GetBytes
           1            DefaultEncoder::GetBytes
          10             StreamWriter::Flush
          30              __ConsoleStream::Write
    Hello C# world!
          41             __ConsoleStream::WriteFileNative
           8            __ConsoleStream::Write
           6             StreamWriter::Flush
           9              __ConsoleStream::Flush
          12             IntPtr::op_Equality
           5              __ConsoleStream::Flush
           2             __ConsoleStream::get_CanWrite
           5            __ConsoleStream::Flush
           5           StreamWriter::Flush
           5          StreamWriter::Write
           6         TextWriter::WriteLine
           5        SyncTextWriter::WriteLine
           1        Hello::Main
    
         374 instructions total
  10. How does CLR JIT optimize?

    The number of instructions there is 374 it reports, and that's about ten times less than what we had originally. One of the very significant code optimizations it does is in-lining. So when it goes down compiling a given method, if it sees a call to another method, it goes and inspects that method if it's a small enough method, instead of calling out, it just embed it in the calling routine.

    So it's just one thing to be aware, if, when you come to write code and analyze your performance on the CLR, be aware that if you're launching from within a debugger, the JIT is being told beneath the covers to ease back in its optimizations.

    After the JIT compilation native code is handed off to the Pentium processor to actually execute the raw instructions.

Now CLR works the same way for all other applications written in any language like VB.NET or in COBOL.

Although the routine developers don't need to come down to this level, It's real fun to go from the low level stuff to the high level to see just how easy and how smooth it is for a development experience, to put together applications so fast, compared with this low level stuff.

Sources Referred:

MSDN .NET Show

Keep visiting the site for updated and new articles.
Sign my guest book OR send me your feedback at ais_ikr@yahoo.com

Copyright 2003, Aisha Ikram