Showing posts with label sections. Show all posts
Showing posts with label sections. Show all posts

Jan 28, 2024

Multiple definitions in COFF

A couple of days ago, I was curious about how the linker deals with multiple definitions of a function. So I read Microsoft's online PE documentation about the COFF format and how COFF was dealing with multiple defintions. In this post, I'm going to investigate some simple object files with dumpbin from Microsoft. I'm using Visual studio 2019, C++ 14, 32 bit. The compiler flags will be stated further down in this post. So grab another cup of coffee and let's get started! 

As mentioned above, the online PE documentation is of great help when understanding the structure of the object file. It states that a section is a basic unit of code or data within a PE or COFF file. Below, we will learn that COMDAT is a special type of section, and we will further dig into this. 

First, I will have a brief discussion with an introductory example of the symbol table. Then I will present a couple of setups, where each setup is slightly different. My focus will be on the .text sections, since the code resides inside those. Below is the code for this introductory example.
int main()
{
   return 0;
}
The code resides inside main.cpp and is compiled into main.obj, using these flags.
/JMC /permissive- /ifcOutput "Debug\" /GS /analyze- /W3 /Zc:wchar_t /ZI /Gm- /Od /sdl /Fd"Debug\vc142.pdb" /Zc:inline /fp:precise /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /errorReport:prompt /WX- /Zc:forScope /RTC1 /Gd /Oy- /MDd /FC /Fa"Debug\" /EHsc /nologo /Fo"Debug\" /Fp"Debug\project.pch" /diagnostics:column
I will dump the symbol table by using dumpbin, like this:
dumpbin /symbols main.obj
Dumping the symbols gives output below (truncated).
COFF SYMBOL TABLE
000 010575C9 ABS    notype       Static       | @comp.id
001 80010391 ABS    notype       Static       | @feat.00
002 00000002 ABS    notype       Static       | @vol.md
003 00000000 SECT1  notype       Static       | .drectve
    Section length   85, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
006 00000000 SECT2  notype       Static       | .debug$S
    Section length  1E0, #relocs    2, #linenums    0, checksum        0
    Relocation CRC BE724FAD
009 00000000 SECT3  notype       Static       | .msvcjmc
    Section length    1, #relocs    0, #linenums    0, checksum 77073096
    Relocation CRC 00000000
00C 00000000 SECT3  notype       Static       | __D3C22540_main@cpp
00D 00000000 SECT4  notype       Static       | .text$mn
    Section length    5, #relocs    0, #linenums    0, checksum 672BE856, selection    2 (pick any)
    Relocation CRC 4A6A8444
010 00000000 SECT5  notype       Static       | .debug$S
    Section length   98, #relocs    3, #linenums    0, checksum        0, selection    5 (pick associative Section 0x4)
    Relocation CRC B9D5806A
013 00000000 SECT6  notype       Static       | .text$mn
    Section length   37, #relocs    3, #linenums    0, checksum A2398015, selection    1 (pick no duplicates)
    Relocation CRC 2E7CF6B9
016 00000000 SECT7  notype       Static       | .debug$S
    Section length  104, #relocs    9, #linenums    0, checksum        0, selection    5 (pick associative Section 0x6)
    Relocation CRC 351E60C8
019 00000000 SECT6  notype ()    External     | _main
In the truncated table above, we can see 26 entries from the symbol table, where the 26th entry is telling us that the symbol _main is external and belongs to section #6. 

From the online PE documentation, we can understand that the sections are defined inside the symbol table as well. For such entry, SECT6 (entry 0x13 above), there follows another entry (entry 0x014 above), providing information about the section. This entry is in Auxiliary Format 5 and since section #6 is a COMDAT section (see below for explanation), there is a selection type defined, in this case 1 (pick no duplicates). The selection type tells linker how to deal with multiple definitions of COMDATA sections. In this particular case, the linker only accepts one definition, if there are more, the linker issues a multiple definition error.

Further, I will dump the headers for each section, like this:
dumpbin /headers main.obj
Dumping the headers gives the output below (only section header #6 pasted).
SECTION HEADER #6
.text$mn name
       0 physical address
       0 virtual address
      37 size of raw data
     501 file pointer to raw data (00000501 to 00000537)
     538 file pointer to relocation table
       0 file pointer to line numbers
       3 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= _main
         16 byte align
         Execute Read
In the truncated output above, we can see section flags 60501020. The online PE documentation (Section Flags) can be used to understand the 60501020. It's also briefly described in the output above; the section contains executable code, the section contains COMDAT data, the section can be executed as code, the section can be read.

Now let's look into the different setups. Each setup contains a main.cpp with a main function and a variation of additional source files. The figure summarize the source files and resulting object files with its symbols. The compiler options are according to the introductory example above. In each setup, after the figure, I will present the important parts of the object files (using the commands described above) and have a brief discussion. Let's look at the first one.

Setup 1
Figure 1
void helperfunc1()
{}
00D 00000000 SECT4  notype       Static       | .text$mn
    Section length   35, #relocs    3, #linenums    0, checksum  DB372CC, selection    1 (pick no duplicates)
    Relocation CRC E4C10A68
019 00000000 SECT4  notype ()    External     | ?helperfunc1@@YAXXZ (void __cdecl helperfunc1(void))
SECTION HEADER #4
.text$mn name
       0 physical address
       0 virtual address
      35 size of raw data
     45A file pointer to raw data (0000045A to 0000048E)
     48F file pointer to relocation table
       0 file pointer to line numbers
       3 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl helperfunc1(void)" (?helperfunc1@@YAXXZ)
         16 byte align
         Execute Read
From above, we see entry 0x00D and entry 0x019 in the symbol table, which defines SECT4 and the external symbol ?helperfunc1@@YAXXZ, which is the name mangled name of the function helperfunc1

Setup 2
Figure 2
static void helperfunc1()
{}
COFF SYMBOL TABLE
000 010575C9 ABS    notype       Static       | @comp.id
001 80010391 ABS    notype       Static       | @feat.00
002 00000002 ABS    notype       Static       | @vol.md
003 00000000 SECT1  notype       Static       | .drectve
    Section length   41, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
006 00000000 SECT2  notype       Static       | .debug$S
    Section length  148, #relocs    2, #linenums    0, checksum        0
    Relocation CRC 3A286AB6
009 00000000 SECT3  notype       Static       | .msvcjmc
    Section length    1, #relocs    0, #linenums    0, checksum 77073096
    Relocation CRC 00000000
00C 00000000 SECT3  notype       Static       | __5291C45E_helperfuncs@cpp
00D 00000000 SECT4  notype       Static       | .debug$T
    Section length   5C, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
010 00000000 SECT5  notype       Static       | .chks64
    Section length   28, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
Running the dumpbin command with the symbol option gives no helperfunc1 symbol. The symbol is not present in the object file, since it is not used in the translation unit and is declared as static.

Setup 3
Figure 3
static void helperfunc1()
{}

void libfunc1()
{
   helperfunc1();
}
00D 00000000 SECT4  notype       Static       | .text$mn
    Section length   35, #relocs    3, #linenums    0, checksum  DB372CC, selection    1 (pick no duplicates)
    Relocation CRC E4C10A68
013 00000000 SECT6  notype       Static       | .text$mn
    Section length   3A, #relocs    4, #linenums    0, checksum 81ED1D3A, selection    1 (pick no duplicates)
    Relocation CRC B844FE03
01F 00000000 SECT4  notype ()    Static       | ?helperfunc1@@YAXXZ (void __cdecl helperfunc1(void))
020 00000000 SECT6  notype ()    External     | ?libfunc1@@YAXXZ (void __cdecl libfunc1(void))
SECTION HEADER #4
.text$mn name
       0 physical address
       0 virtual address
      35 size of raw data
     4AA file pointer to raw data (000004AA to 000004DE)
     4DF file pointer to relocation table
       0 file pointer to line numbers
       3 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl helperfunc1(void)" (?helperfunc1@@YAXXZ)
         16 byte align
         Execute Read

SECTION HEADER #6
.text$mn name
       0 physical address
       0 virtual address
      3A size of raw data
     65B file pointer to raw data (0000065B to 00000694)
     695 file pointer to relocation table
       0 file pointer to line numbers
       4 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl libfunc1(void)" (?libfunc1@@YAXXZ)
         16 byte align
         Execute Read
Now libfunc is calling helperfunc1. From above, we see entry 0x00D and entry 0x013 in the symbol table, which defines SECT4 and SECT6 respectively. We can also see the the symbol ?helperfunc1@@YAXXZ again, this time it's defined as a static symbol, since we defined helperfunc1 as static in the code. Further, we also see the external symbol ?libfunc1@@YAXXZ. Both symbols states for the linker that there is only one defintion allowed.

Setup 4
Figure 4
inline void helperfunc1()
{}
COFF SYMBOL TABLE
000 010575C9 ABS    notype       Static       | @comp.id
001 80010391 ABS    notype       Static       | @feat.00
002 00000002 ABS    notype       Static       | @vol.md
003 00000000 SECT1  notype       Static       | .drectve
    Section length   41, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
006 00000000 SECT2  notype       Static       | .debug$S
    Section length  148, #relocs    2, #linenums    0, checksum        0
    Relocation CRC 3A286AB6
009 00000000 SECT3  notype       Static       | .msvcjmc
    Section length    1, #relocs    0, #linenums    0, checksum 77073096
    Relocation CRC 00000000
00C 00000000 SECT3  notype       Static       | __5291C45E_helperfuncs@cpp
00D 00000000 SECT4  notype       Static       | .debug$T
    Section length   5C, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
010 00000000 SECT5  notype       Static       | .chks64
    Section length   28, #relocs    0, #linenums    0, checksum        0
    Relocation CRC 00000000
Running the dumpbin command with the symbol option gives no helperfunc1 symbol. The symbol is not present in the object file, since it is not used in the translation unit and declared inline. As a matter of fact, this symbols table looks identical with the symbol table from Setup 2.

Setup 5
Figure 5
inline void helperfunc1()
{}

void libfunc1()
{
   helperfunc1();
}
00D 00000000 SECT4  notype       Static       | .text$mn
    Section length   35, #relocs    3, #linenums    0, checksum  DB372CC, selection    2 (pick any)
    Relocation CRC E4C10A68
013 00000000 SECT6  notype       Static       | .text$mn
    Section length   3A, #relocs    4, #linenums    0, checksum 81ED1D3A, selection    1 (pick no duplicates)
    Relocation CRC B844FE03
01F 00000000 SECT4  notype ()    External     | ?helperfunc1@@YAXXZ (void __cdecl helperfunc1(void))
020 00000000 SECT6  notype ()    External     | ?libfunc1@@YAXXZ (void __cdecl libfunc1(void))
SECTION HEADER #4
.text$mn name
       0 physical address
       0 virtual address
      35 size of raw data
     4AA file pointer to raw data (000004AA to 000004DE)
     4DF file pointer to relocation table
       0 file pointer to line numbers
       3 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl helperfunc1(void)" (?helperfunc1@@YAXXZ)
         16 byte align
         Execute Read
SECTION HEADER #6
.text$mn name
       0 physical address
       0 virtual address
      3A size of raw data
     65B file pointer to raw data (0000065B to 00000694)
     695 file pointer to relocation table
       0 file pointer to line numbers
       4 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl libfunc1(void)" (?libfunc1@@YAXXZ)
         16 byte align
         Execute Read 
Now libfunc1 is calling helperfunc1. From above, we see entry 0x00D and entry 0x013 in the symbol table, which defines SECT4 and SECT6 respectively. We can also see the the external symbol ?helperfunc1@@YAXXZ and libfunc1@@YAXXZ. Unlike Setup 3, helperfunc1 is external and its selection type is 1, meaning any symbol can be linked.

Setup 6
Figure 6
inline void helperfunc1()
{}
#include "helperfuncs.hpp"

void libfunc1()
{
   helperfunc1();
}
00E 00000000 SECT4  notype       Static       | .text$mn
    Section length   35, #relocs    3, #linenums    0, checksum  DB372CC, selection    2 (pick any)
    Relocation CRC 16A75AA6
014 00000000 SECT6  notype       Static       | .text$mn
    Section length   3A, #relocs    4, #linenums    0, checksum 81ED1D3A, selection    1 (pick no duplicates)
    Relocation CRC B844FE03
020 00000000 SECT4  notype ()    External     | ?helperfunc1@@YAXXZ (void __cdecl helperfunc1(void))
021 00000000 SECT6  notype ()    External     | ?libfunc1@@YAXXZ (void __cdecl libfunc1(void))
SECTION HEADER #4
.text$mn name
       0 physical address
       0 virtual address
      35 size of raw data
     53B file pointer to raw data (0000053B to 0000056F)
     570 file pointer to relocation table
       0 file pointer to line numbers
       3 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl helperfunc1(void)" (?helperfunc1@@YAXXZ)
         16 byte align
         Execute Read
SECTION HEADER #6
.text$mn name
       0 physical address
       0 virtual address
      3A size of raw data
     6EC file pointer to raw data (000006EC to 00000725)
     726 file pointer to relocation table
       0 file pointer to line numbers
       4 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl libfunc1(void)" (?libfunc1@@YAXXZ)
         16 byte align
         Execute Read
This setup does not differ from Setup 5. The only difference is the created helperfuncs.hpp, which is included in the created libfuncs1.cpp. This is a more typical setup, where the inline function is defined inside the header file. Note that the symbol entries and sections are identical with Setup 5.

Setup 7
Figure 7
inline void helperfunc1()
{}
#include "helperfuncs.hpp"

void libfunc1()
{
   helperfunc1();
}
#include "helperfuncs.hpp"

void libfunc2()
{
   helperfunc1();
}
00E 00000000 SECT4  notype       Static       | .text$mn
    Section length   35, #relocs    3, #linenums    0, checksum  DB372CC, selection    2 (pick any)
    Relocation CRC 16A75AA6
014 00000000 SECT6  notype       Static       | .text$mn
    Section length   3A, #relocs    4, #linenums    0, checksum 81ED1D3A, selection    1 (pick no duplicates)
    Relocation CRC 4796C86E
020 00000000 SECT4  notype ()    External     | ?helperfunc1@@YAXXZ (void __cdecl helperfunc1(void))
021 00000000 SECT6  notype ()    External     | ?libfunc2@@YAXXZ (void __cdecl libfunc2(void))
SECTION HEADER #4
.text$mn name
       0 physical address
       0 virtual address
      35 size of raw data
     533 file pointer to raw data (00000533 to 00000567)
     568 file pointer to relocation table
       0 file pointer to line numbers
       3 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl helperfunc1(void)" (?helperfunc1@@YAXXZ)
         16 byte align
         Execute Read
SECTION HEADER #6
.text$mn name
       0 physical address
       0 virtual address
      3A size of raw data
     6E4 file pointer to raw data (000006E4 to 0000071D)
     71E file pointer to relocation table
       0 file pointer to line numbers
       4 number of relocations
       0 number of line numbers
60501020 flags
         Code
         COMDAT; sym= "void __cdecl libfunc2(void)" (?libfunc2@@YAXXZ)
         16 byte align
         Execute Read
This is even a more typical setup. The inline function is defined inside the header file. The header file is included in two cpp files. 

Above is only the symbol table for the additional file libfuncs2.obj stated. We can compare the symbols from libfuncs1.obj (from Setup 6) with the symbols from libfuncs2.obj in this Setup. Both object files contains the external symbol?helperfunc1@@YAXXZ, with selection type 2. Thanks to this selection type, the linker will not complain about multiple defintions during link time.

You are welcome to leave comments, complaints or questions!

Jan 5, 2015

PE file with empty main()

If you build an empty console program, how many bytes is needed for the Portable Executable (PE)? And what's inside the PE file?

In this post I'm experimenting with an empty console program. I'm reducing the PE file, so it just contains the headers and the binary machine code. I'm using Visual Studio 2010 Express and building the PE files in Release mode. Further, I'm using PE Insider from Cerbero and PEBrowserPro from Smidgeonsoft.

Let's compile and link the following empty program below.
int main()
{
   return 0;
}

A program doing nothing. So simple as it can be. Now, let's check out the size of this program, using file properties.

File properties - size of file


Alright, 6144 bytes of binary data is needed for this empty program. Why is this size needed, and what kind of binary data is in there? Let's fire up PE Insider, and first check out the header size, and then the section table.

PE Insider - size of headers

PE Insider - section table

Above, the SizeOfHeaders present the (HEX)size of the headers, and the SizeOfRawData, present the (HEX)size for each section needed on disk.

Let's try to understand the number 6144. This is actually the sum of the size of the headers and the size of the sections. So let's sum it to verify this:

0x400+0x800+0x600+0x200+0x200+0x200 = 0x1800 (DEC: 6144)

Okay, now we understand why the size is 6144. But what's inside all this binary data?

First let's check out the .text section, i.e. the code. This section needs 0x800 bytes. However, my empty program is doing nothing!

PE Insider - .text section

Above, the .text section starts at offset 0x400 (file on disk), and there is a lot of things going on.

Again, the main function is doing nothing. But there is a lot of other code around in the .text section. It's code from the C Runtime library. For instance, the main function is not the first function called when executing the program, the first function called is the mainCRTStartup function. This is a function in the C Runtime library (and part of the PE file). How do I know this? Well, each program has an entry point, which is specified in one of the headers in the PE file. Let's check it out.

PE Insider - Entry point
 
PEBrowserPro - disassemble view of entry point

Okay, the entry point is at Relative Virtual Address (RVA) 0x12A0 (of course within the .text section). Thanks to the disassemble view from PEBrowserPro, we can see what's going on there. At this RVA, the mainCRTStartup is located.

Next test to do; let's tell Visual Studio to use another entry point (i.e. not the mainCRTStartup). We can do this in the Property Pages dialog.


Visual Studio - specifying my own entry point

After compiling and linking, let's check the file properties again.

File properties - size of file

Wow! The file size is reduced! From 6144 bytes to 3072 bytes. Let's check out the section table again.

PE Insider - section table

Comparing to the screenshots above, the .text section is reduced from 0x800 to 0x200, the .rdata section from 0x600 to 0x200, the .data section 0x200 to 0x000. The .rsrc and the .reloc section remain the same size.

So how do the .text section look like now when we specified our own entry point?

PE Insider - .text section (main entry point)


PEBrowserPro - disassemble view - .text section (main entry point)

The only thing going on in the .text section is the return 0 statement. We have managed to get rid of the C Runtime Library code.

Note that there is a lot of 0's in the .text section. This is just zeropadding, so each section can start at a multiple of 0x200.

Now let's continue with the other sections in the PE file. The purpose for the .reloc section, is to help the loader to do some relocation if the executable not is loaded at its preferred load address. If we remove the DYNAMICBASE switch, the .reloc section will be removed. This means that the executable always will be loaded at its preferred load address, and does not need any relocation information. Let's remove the DYNAMICBASE switch.


Visual Studio - Remove dynamicbase

 Next, compile and link again and check out the file properties, and the section table again.

File properties - size of file
PE Insider - section table

Voila! The file size is decreased from 3072 bytes to 3036 bytes. The .reloc section is gone.

Let's continue with the other sections. What's going on in the .rdata section and the .rsrc section?

PE Insider - .rdata section
 
PE Insider - .rsrc section

Thanks to the ASCII view, we can figure out that Visual Studio embed a manifest in .rsrc section. We can also see that there is a PDB path in the .rdata section.

If we want to get rid of this, we just go to the Property Pages and remove the manifest as well as the debug information.


Visual Studio - Removing manifest



Visual Studio - Removing debugging information

Again, compile and link and check out the file size, the section table, and the .rdata section.

File properties - size of file
PE Insider - section table
PE Insider - .rdata section, remaining data

Not much left. File size is reduced from 3036 bytes to 2048 bytes. The .rsrc section is gone. The .text section contains a couple of bytes of binary machine code and the .data section is empty. There remain some data in the .rdata section. I'm not completely sure what this data is. If you know, please tell me. However, it can be removed by saying no to Whole Program Optimization.


Visual Studio - Whole Program Optimization

Compile and link the PE file and let's have a final look at the file properties and the section table.

File properties - size of file


PE Insider - section table

Well, that's about it! The file size is reduced from 2048 bytes to 1024 bytes. The PE file now just contains the headers and the .text section with a couple of binary machine codes.

You are welcome to leave comments, complaints or questions!

Jan 2, 2015

Base relocation table

A Portable Executable (PE) file usually contains some headers and some sections. One of the sections, that may exist, is the .reloc section, and within this section is a base relocation table. The base relocation table is needed to fix up virtual addresses in the PE file if the PE file not was loaded at its preferred load address.

In this post, I will have a look at a base relocation table and see what's going on there. Further, I will discuss how to read the entries in the base relocation table, and what they are doing in the code. I'm using PE Insider from Cerbero, PEBrowserPro from Smidgeonsoft and WinDbg.

Before checking out a .reloc section in a PE file, let's discuss the .reloc section briefly.

The .reloc section contains a serie of blocks. There is one block for each 4 KB page that contains virtual addresses, which is in need for fix ups. Each block contains an IMAGE_BASE_RELOCATION struct and a serie of entries.

The IMAGE_BASE_RELOCATION is defined as below, according to the header file winnt.h.
typedef struct _IMAGE_BASE_RELOCATION {
    DWORD   VirtualAddress;
    DWORD   SizeOfBlock;
//  WORD    TypeOffset[1];
} IMAGE_BASE_RELOCATION;

The VirtualAddress holds a Relative Virtual Address (RVA) of the 4 KB page, which the relocation applies to. The SizeOfBlock holds the size of the block in bytes (including the size of the IMAGE_BASE_RELOCATION struct).

Each entry is a 2-byte value and represent a location (offset within the 4 KB page pointed out by the VirtualAddress member in the IMAGE_BASE_RELOCATION struct), which needs to be fixed up in case of the PE file is not loaded at its preferred load address.

That was some theory. Now let's take a look at a .reloc section from a simple "Hello world!" program. I've compiled and linked it in Visual Studio 2010 Express in Release mode.

#include <cstdio>

int main()
{
   std::printf("Hello world!");
}

Before proceeding, just a quick note. The program above lack the return 0 statement. This does not cause any trouble, the compiler actually add this statement. This can be seen in the disassembly views which follows below (xor eax, eax, ret).

Let's open the "Hello world!" program in PE Insider.

First, let's check out the preferred load address.

PE Insider - Preferred load address
The preferred load address is the ImageBase value, i.e. 0x00400000.

Next, check out the section table.

PE Insider - section table
Apparently there are five sections needed for my simple "Hello world!" program. We are only interested in the .reloc section, so lets check it out.

PE Insider - .reloc section
The .reloc section contains two blocks (within the red border). Since there is 2 blocks, it means that there is 2 4KB pages that contain virtual addresses which is in need for fix ups.

The first member in the IMAGE_BASE_RELOCATION in the first block is the VirtualAddress member, with the value 0x00001000 (green underscored value). The second member is the SizeOfBlock member, with the value 0x00000128 (black underscored value). In order to calculate the number of entries, we must subtract the the size of the IMAGE_BASE_RELOCATION struct from the SizeOfBlock member, i.e. 0x128-0x8 = 0x120 bytes. Further, we know that each entry is a 2-byte value, which gives the number of entries to 0x90 (DEC: 144).

The VirtualAddress value, in the second block, is 0x00002000 (green underscored value) and the SizeOfBlock value is 0x00000020 (black underscored value). Applying same idea as a for the first block, the number of entries is 0xC (DEC: 12).

So what is the meaning of the information above?

Regarding the first block, the loader understand it must do some fix ups in the 4 KB page that starts at RVA 0x1000. As you can see in the section table above, 0x1000 is where the .text section starts. In other words, the loader must fix up 0x90 (DEC: 144) virtual addresses in the .text section, i.e in the code. Note that the .text section is covered in 1 4 KB page.

Regarding the second block, the loader understand it must do some fix ups in the 4 KB page that starts at RVA 0x2000. As you can see in the section table above, this is the .rdata section. In other words, the loader must fix up 0xC (DEC: 12) virtual addresses in the .rdata section. Note that the. rdata section is covered in 1 4 KB page.

So how do the loader know how to fix up the virtual address, and where it should do it?

In order for the loader to know how to fix up the address, it takes the delta value of the preferred load address and the actual load address. The delta value is then added to the virtual address.

In order for the loader to know where to do the fix ups, it check out each entry in the blocks in the .reloc section. As mentioned above, an entry is a 2-byte value. The 4 most significants bits in the value, represents the type of relocation. In the x86 world, the type is always 3 (HIGHLOW), which means that we add the complete 32 bit value of the delta value to the virtual address that is in need for fix up. The remaining bits (12 bits) of the entry, tells us the offset in the 4 KB page (pointed out by the VirtualAddress member in the IMAGE_BASE_RELOCATION struct), i.e. where to do the fix up in the page.

Let's clarify this with an example. As mentioned above, the .text section will have 0x90 fix ups. Let's go in to detail for the first 2 fix ups.

According to the .reloc section above, the first entry is 0x3001. This means that the type is 3 and the offset, in the page, is 0x001. The second entry is 0x3007, which means that the type is 3 and the offset, in the page, is 0x007.

Thanks to these offset values, the loader now knows that it must fix up the virtual address at RVA 0x1000+0x1 = 0x1001, and RVA 0x1000+0x7 = 0x1007.

Below is the .text section on file, and the affected RVA's within the red border. I've also marked them as 1 and 2, cause I will deal with them seperatly in my explanation. I also use them as references between the screenshots.
PE Insider - .text section - HEXVIEW
So above, we see the binary machine code. This does not really tells us so much, so let's disassemble this part using PEBrowserPro.


PEBrowserPro - .text section - disassembled

Thanks to the disassembled view, we can easily see what's occurs on RVA 0x1001 and RVA 0x1007. Note that the view using virtual addresses (with the preferred load address 0x0040000).

Lets start with case 1, i.e the RVA 0x1001.

At this position, we can see the value 0xF4204000. This is a virtual address (0x004020F4, remember little-endian), which needs to be fixed up according to the .reloc section. Actually, it is a virtual address within the .rdata section. The string literal "Hello world!" is put in the .rdata section. In the disassemble view above, we push the address, to the string literal, on the stack. In case of the PE file not will be loaded at its preferred load address, this value 0xF4204000 needs to be fixed up.

Now let's move on to case 2, i.e. the RVA 0x1007.

At this position, we can see the value 0xA0204000. This is a virtual address (0x004020A0, remember little-endian), which needs to be fixed up according to the .reloc section. This is a virtual address within the .rdata section as well. More specific, the virtual address 0x004020A0 is a part of the Import Address Table (IAT). It can be verified by checking the Directories.

PE Insider - Directories

Each entry in the IAT contains virtual addresses to functions in other modules. In this case, the IAT entry at the virtual address 0x004020A0 contains a virtual address to the function printf in the msvcr100.dll module.

So when executing the CALL instruction (see the disassemble view), the processor finds out printf's virtual address by checking the virtual address 0x004020A0. Before load time, we don't know the virtual address of printf yet, because the DLL (msvcr100.dll) has not been loaded into virtual memory.

As you can understand, if the loader does not load the "Hello world!" program at its preferred load address, the .rdata section will not be located at 0x2000, and the entry, which tells us where to find the printf function is invalid. Therefore the loader must add the delta value to 0x004020F4.

Finally, I will just show what's going on in the .text section, when the "Hello world!" program is executed (loaded in memory). To this, I'm using WinDbg. Note that the "Hello world!" program is linked with the DYNAMICBASE switch, i.e. it's Address Space Layout Randomization (ASLR) compatible.

First, let's find out the load address.

WinDbg - Load address

Remember that according to the PE file, the preferred load address is 0x00400000. The actual load address is 0x00250000, which gives us a delta value 0x0040000-0x00250000 = 0x1B0000 (negative difference).

Second, let's check out the .text section


WinDbg - .text section - Disassembled view

As you see above, the virtual addresses differ from the disassembled view in PEBrowserPro, due to the "Hello world!" program is loaded at 0x00250000.

For instance, according to PEBrowserPro, the push instruction pushes the virtual address 0x004020F4 on the stack. Perfectly OK when the program is loaded at its preferred load address 0x00400000.
But in this case, the program is loaded at 0x00250000. The delta value is 0x1B0000 (calculated above). This is a negative difference. In other words, the loader must fix up virtual address 0x4020F4 by reducing it with 0x1B0000. This gives 0x004020F4-0x1B0000 = 0x002520F4. This is exactly what happened in the screenshot above.

You are welcome to leave comments, complaints or questions!

Nov 23, 2014

Graphical memory layout for notepad.exe

In my previous post, I made a graphical memory layout for the notepad process. I showed the complete 4 GB virtual memory space (or at least the user-mode virtual memory).

In this post, I'm going to do a graphical memory layout for the executable notepad.exe.

The file notepad.exe is a Portable Executable (PE) file. I'm not going to specify the PE format here, there is a lot of good resources on the net where you can find this information. However, I will briefly describe some parts of the PE file here.

Let's inspect notepad.exe. There are several good tools out there to do this. I'm using PE Insider from Cerbero. PE Insider will open the executable and show the file contents in a nice way. Below you can see a screenshot when I've opened notepad.exe. It shows the major parts which builds up the PE file.


When loading notepad.exe into virtual memory (i.e. when starting notepad), the headers and sections are copied into the memory. Exactly how, will be explained below. (To be correct, the information in the PE file are copied to the memory, but the loader may overwrite some information in the memory during load time, which means that the PE file and the memory may not contain exactly the same information. But that does not impact the memory layout.)

Regarding the headers, PE Insider gives us information according to the screenshot below. The headers take up 0x400 bytes of the PE file. However, this is not entirely true. It is actually rounded to this value since the file alignment is 512 bytes (0x200). Offset is the offset in the PE file and Size is the size (in bytes) of the value at this offset.



A PE file contains a couple of sections, more specific, in this case, notepad.exe contains following sections (screenshot from PE Insider). For now, just consider a section as a block of binary information.


Each section are copied from file into the virtual memory (by the loader) according to the VirtualAddress column. The Virtual Address column contains the Relative Virtual Address (RVA). It is an address relative to the image base address (image base address = where the image are loaded in the virtual memory). Since my Windows Vista 32 bit uses Address Space Layout Randomization (ASLR), we don't really know the image base address at forehand. But just let's take an example. During one execution of notepad, the image base address was 0x00eb0000. According to the table above the RVA for the .text section is 0x1000, so the final virtual address for the .text section is 0x00eb1000. The virtual address for the .data section is 0x00eba000. The table also provide us with the size of the section, i.e. the VirtualSize column.

I will not go into detail what each section contains, but I can mention that the .text section mainly contains binary code for the processor to execute. I say mainly, because I inspected this section and realized it also contains the Import Address Table (IAT). I will probably have post about this in the future.

So thanks to the table above, I was able to do this diagram in Excel.


This is what notepad.exe looks like when it is loaded into memory. Note that the diagram does not take the image base address into account. I've just made the diagram from 0 (zero) and not an actual image base address (e.g. 0x00eb0000). The headers are copied to low memory, and then follows the four sections. Note that each section is aligned to a 4 KB page. For instance, the .text section are mapped into RVA 0x1000 (DEC: 4096), which leaves a space with zero padding to headers. The last section is the .reloc section which is 0xd18 in size and starts at RVA 0x27000. The SizeOfImage is 0x28000 bytes (DEC: 163840) and this means that final bytes in the image are zero padded as well.

You are welcome to leave comments, complaints or questions!