I hope everyone has heard and tried the new .NET technology by now. And we have started loving all those cool features in .NET, we’ve been dreaming for long time. So in this article, I’m going to drill in to the detail level of the PE (Portable Executable file) structure and what are the primary differences made out to make .NET so interesting.
The PE File Structure
Well, I think a simple Hello World program I wrote in C# when the .NET beta 1 was released would be enough for this. So here, I present my code in figure 01.
It does nothing than printing “Hello C# world” in the console window. Well, when you compile and build this, you will have a binary image. In my case, it is “HelloWorld.exe”. This is a Microsoft COFF (Common Object File Format) PE (Portable Executable) file. Now, we are going to see what’s inside this PE file. Actually I can say, in a .NET compatible PE file, there are 3 sections.
The first one, we call the PE header. This section has the information about the contents of the rest of the file. Basically, you can think of it as an index. Also, it defines the RVA of the one and only entry point to the executable code.
Ok, let me show you some interesting stuff. If you are a C++ fan like me, you should be able to get the dump image of this file using a dump tool. Ok, in figure2, I have shown a part of the first header, I got from the dump.
Well, if you are familiar with Win32 days dumps, you don’t see a big difference between the 2 dumps. But take a look at the last 2 rows in this list. Earlier it was reserved placeholder and did not hold any RVA. But now it’s replaced by the entry “COM descriptor directory”. This is the place the .NET runtime recognizes this file as a managed executable. This RVA points to the location of the metadata. I’ll talk about metadata very soon. Until then, I have few more things to show you in my dump. If we go down a little further, we can see the section shown in figure 3.
In that you can see, it has the location of the PDB file, which will be used for debugging. You will see this only if you compile the program with the debug setting. Then right after that, there are four letters BSJB. It’s always BSJB. So what is this? This is the first entry in the metadata table. If you convert it to a hexadecimal value, it will turn out to be 0X42534A42. So this is a combination of the first 4 letters of the names of the 4 guys who worked on metadata engine. Then next to that, you can see the runtime version of .NET supported by the PE file. In my case, it’s framework 1.0.3705. Following the version, it has the names and addresses of the heap data structures. A PE file contains 4 heap data structures named String, User String, GUID and Blob. I will tell what the heap data structures are, in the next few paragraphs.
I only got one more thing to show you in my dump. Look at figure 04.
This section is called CLR header. And this will be examined by the common language runtime to obtain the RVA of the metadata directory. There is a simple logic to obtain the offset to the table out of the RVA shown there. In my program, it’s 20DC. But explaining it is not in the scope of this article. So I’ll leave it to the next one. Also, this section contains the entry point token to the executable code. Here it is 6000001.
The following section to the above is also a bit interesting. There we have said that there is link to another module mscoree.dll. That is the core of common language runtime. It also specifies the entry point to the mscoree.dll, which allows the module to be attached to the current process. Here it’s shown as 0 _CorExeMain.
So that’s a little about the PE header. Next one is the Metadata section. When .NET compiles a program, it does not compile it to native code. Rather it compiles to an intermediate language (MSIL). The metadata describes the objects. Metadata contains a set of tables that contain the description of types available in the PE and their behaviors. For example, there is one table to hold the classes and there is another to hold the methods supported by those classes. Each table and rows in the tables have identifiers. And one more thing I should mention is that Metadata does not store the user readable names of the classes, methods, custom types and so on. Instead, it uses the heap data structures I mentioned above to store them. And the tables have references to those values in the heap data structures. For example, if I have a method called
foo() in my code. The metadata table, which contains the methods, would not store the name
foo in it. It stores the reference in the heap data structure (in this case it is String Heap Data Structure). I think now you have the answer that you’ve been looking for a long time. Why no IDL in .NET? The answer is simply because of the metadata the PE files are said to be self-describing components. That’s how .NET achieves features as cross language compatibility, xCopy deployment, Easy component Versioning and so on.
We have looked at PE header and metadata. But still the most important part for me is missing. That is my code. Well actually the section 3 contains my code compiled to IL assembler. This MSIL code is stored along with the metadata tokens. A metadata token is a four-byte number. The top byte denotes the metadata table to which a particular token refers (method, type, and so on). The remaining three bytes specify the row in the metadata table that corresponds to the programming element being described. If you define a method in C# and compile it into a PE file, the following metadata token might exist in the MSIL portion of the PE file:
The top byte (0x06) indicates that this is a
MethodDef token. The lower three bytes (000004) tell the common language runtime to look in the fourth row of the
MethodDef table for the information that describes this method definition. So when a function is called in a PE file, it finds the corresponding location of the MSIL code using the metadata tokens and compiles it using the JIT compiler. When it comes to compiling, .NET thinks that some functions will not be called during the program execution. So it compiles the MSIL on demand which in turn gives you the best experience in program execution.
So that’s about the inside of the PE file structure and how it interacts with the program execution.