CLR Executables

Microsoft .NET executables are different from typical Windows executables in that they carry not only code and data, but also metadata (see “Metadata” and “Intermediate Language” later in this chapter). In this section, we start off with the code for several .NET applications, and then discuss the .NET PE format.

Hello, World: Managed C++

Let’s start off by examining a simple Hello, World application written in Managed C++, a Microsoft .NET extension to the C++ language. Managed C++ includes a number of new .NET-specific keywords that permit C++ programs to take advantage of .NET’s new features, including garbage collection. Here’s the Managed C++ version of our program:

#using <mscorlib.dll>
using namespace System;

void main(  )
{
  Console::WriteLine(L"C++ Hello, World!");
}

As you can see, this is a simple C++ program with an additional directive, #using (shown in bold). If you have worked with the Microsoft Visual C++ compiler support features for COM, you may be familiar with the #import directive. While #import reverse-engineers type information to generate wrapper classes for COM interfaces, #using makes accessible all types from the specified DLL, similar to a #include directive in C or C++. However, unlike #include, which imports C or C++ types, #using imports types for any .NET assembly, written in any .NET language.

The one and only statement within the main( ) method is self-explanatory—it means that we are invoking a static or class-level method, WriteLine( ), on the Console class. The L that prefixes the literal string tells the C++ compiler to convert the literal into a Unicode string. You may have already guessed that the Console class is a type hosted by mscorlib.dll, and it takes one string parameter.

One thing that you should also notice is that this code signals to the compiler that we’re using the types in the System namespace, as indicated by the using namespace statement. This allows us to refer to Console instead of having to fully qualify this class as System::Console.

Given this simple program, enter the following on the command line to compile it, using the new C++ command-line compiler, shipped with the .NET SDK:

cl hello.cpp /CLR /link /entry:main

The /CLR command-line option is extremely important, because it tells the C++ compiler to generate a .NET PE file instead of a normal Windows PE file.

When this statement is executed, the C++ compiler generates an executable called hello.exe. When you run hello.exe, the CLR loads, verifies, and executes it.

Hello, World: C#

Because .NET is serious about language integration, we’ll illustrate this same program using Microsoft’s new C# language specially designed for .NET. Borrowing from Java and C++ syntax, C# is a simple and object-oriented language that Microsoft has used to write the bulk of the .NET base classes and tools. If you are a Java (or C++) programmer, you should have no problem understanding C# code. Here’s Hello, World in C#:

using System;

class MainApp 
{
  public static void Main(  ) 
  {
    Console.WriteLine("C# Hello, World!");
  }
}

C# is similar to Java in that it doesn’t have the concept of a header file: class definitions and implementations are stored in the same .cs file. Another similarity to Java is that Main( ) is a public, static function of a particular class, as you can see from the code. This is different from C++, where the main( ) method itself is a global function.

The using keyword here functions similar to using namespace in the previous example, in that it signals to the C# compiler that we want to use types within the System namespace. Here’s how to compile this C# program:

csc hello.cs

In this command, csc is the C# compiler that comes with the .NET SDK. Again, the result of executing this command is an executable called hello.exe, which you can execute like a normal EXE but is managed by the CLR.

Hello, World: VB.NET

And since we’re on a roll, here is the same program in Visual Basic.NET (VB.NET):

Imports System

Public Module modmain
  Sub Main(  )
    Console.WriteLine ("VB Hello, World!")
  End Sub
End Module

If you are a VB programmer, you may be in for a surprise. The syntax of the language has changed quite a bit, but luckily these changes make the language mirror other object-oriented languages, such as C# and C++. Look carefully at this code snippet, and you will see that you can translate each line of code here into an equivalent in C#. Whereas C# uses the keywords using and class, VB.NET uses the keywords Import and Module , respectively. Here’s how to compile this program:

vbc /t:exe /out:Hello.exe Hello.vb

Microsoft now provides a command-line compiler, vbc , for VB.NET. The /t option specifies the type of PE file to be created. In this case, since we have specified an EXE, hello.exe will be the output of this command.

Tip

In all three versions of this Hello, World program, the Console class and the WriteLine( ) method have remained constant. That is, no matter which language you’re using, once you know how to do something in one language, you can do it in all the other languages. This is an extreme change from traditional Windows programming, in which if you know how to write to a file in C++, you may not necessarily know how to do it for VB, Java, or Cobol.

.NET Portable Executable File

A Windows executable, EXE or DLL, must conform to a file format called the PE file format, which is a derivative of the Microsoft Common Object File Format (COFF). Both of these formats are fully specified and publicly available. The Windows OS knows how to load and execute DLLs and EXEs because it understands the format of a PE file. Given this, any compiler that wants to generate Windows executables must obey the PE/COFF specification.

Standard Windows PE files are divided into two major sections. The first section includes the PE/COFF headers that reference the contents within the PE file. In addition to the header section, the PE file holds a number of native image sections, including the .data, .rdata, .rsrc, and .text sections. These are the standard sections of a typical Windows executable, but Microsoft’s C/C++ compiler allows you to add your own custom sections into the PE file using a compiler pragma statement. For example, you can create your own data section to hold encrypted data that only you can read. Taking advantage of this ability, Microsoft has added a few new sections to the normal PE file specifically to support the CLR’s functionality. The CLR understands and manages the new sections. For example, the CLR will read these sections and determine how to load classes and execute your code at runtime.

As shown in Figure 2-2, the sections that Microsoft has added to the normal PE format are the CLR header and the CLR data sections. While the CLR header stores information to indicate that the PE file is a .NET executable, the CLR data section contains metadata and IL code, both of which determine how the program will be executed.

The format of a .NET PE file

Figure 2-2. The format of a .NET PE file

If you want to prove to yourself that a .NET executable contains both of these sections, use the dumpbin.exe utility, which dumps the content of a Windows executable in readable text. For example, running the following command on the command prompt:

dumpbin.exe hello.exe /all

generates the following data. For brevity, we have shown only the main elements that we want to illustrate:

Microsoft (R) COFF/PE Dumper Version 7.00.9188
Copyright (C) 1992-2000 Microsoft Corporation.  All rights reserved.

Dump of file hello.exe
PE signature found
File Type: EXECUTABLE IMAGE

FILE HEADER VALUES[MS-DOS/COFF HEADERS]
  14C machine (x86)
   3 number of sections
   . . .

OPTIONAL HEADER VALUES  [PE HEADER]
  10B magic # (PE32)
  . . .

SECTION HEADER #1  [SECTION DATA]
  . . .Code
               Execute Read

RAW DATA #1
  . . . 
clr Header:
    . . .

  Section contains the following imports:
    mscoree.dll
      402000 Import Address Table
      402300 Import Name Table
        . . .
0 _CorExeMain

Looking at this text dump of a .NET PE file, you can see that a PE file starts off with the MS-DOS and COFF headers, which all Windows programs must include. Following these headers, you will find the PE header that supports Windows 32-bit programs. Immediately after the PE header, you will find the first data section in the executable file. In a .NET PE file, this is the section (SECTION HEADER #1 as shown here) that stores the CLR header and data. Notice that it is marked as Code and Execute Read, telling the OS loader and the CLR that this section includes code to be executed at runtime by the CLR.

In the CLR Header, you should note that there is an imported function called _CorExeMain, which is implemented by mscoree.dll, the core execution engine of the CLR.[4] At the time of this writing, Windows 98, 2000, and Me have an OS loader that knows how to load standard PE files. To prevent massive changes to these operating systems and still allow .NET applications to run on them, Microsoft has updated the OS loaders for all these platforms. The updated loaders know how to check for the CLR header, and, if this header exists, it executes _CorExeMain, thus not only jumpstarting the CLR but also surrendering to it. You can then guess that your Main( ) function will eventually be called by the CLR.

Now that we’ve looked at the contents of the CLR header, let’s examine the contents of the CLR data, including metadata and code, which are arguably the most import elements in .NET.



[4] We invite to you run dumpbin.exe and view the exports of mscoree.dll at your convenience. You will find that there are also _CorDllMain, _CorClassMain, _CorImageUnloading, and other interesting exports. It’s also interesting to note that this DLL is an in-process COM server, attesting that .NET is created using COM techniques.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.67.251