PE/COFF Headers
Figure 3-2 illustrates the structure of operating system specific headers of a PE file. The headers include an MS-DOS stub, the PE signature, the COFF header, the PE header, and section headers. All of these components—and the data directory table in the PE header—are discussed in the following sections.
Figure 3-2 The memory layout of operating system specific headers.
MS-DOS Stub and PE Signature
The MS-DOS stub is present in image files only. Placed at the beginning of an image file, it is a valid application that runs under MS-DOS. (Isn’t that exciting!) The default stub prints the message This program cannot be run in DOS mode when the image file is run in MS-DOS. This is probably the least interesting part of OS-specific headers; the only relevant fact is that the MS-DOS stub, at offset 0x3C, contains the file pointer to the PE signature, which allows the operating system to properly execute the image file.
The PE signature that follows the MS-DOS stub is a 4-byte item, identifying the file as a PE format image file. The signature contains the characters P and E, followed by 2 null bytes.
COFF Header
A standard COFF header is located immediately after the PE signature of an image file. The COFF header provides the most general characteristics of a PE/COFF file, applicable to both object and executable files. The structure of the COFF header and the meaning of its fields are shown in Table 3-1.
Offset | Size | Field Name | Description |
0 | 2 | Machine | Number identifying the type of target machine. (See Table 3-2.) If the managed PE file is intended for various machine types, this field should be set to IMAGE_FILE_MACHINE_I386 (0x014C). |
2 | 2 | NumberOfSections | Number of entries in the section table, which immediately follows the headers. |
4 | 4 | TimeDateStamp | Time and date of file creation. |
8 | 4 | PointerToSymbolTable | File pointer of the COFF symbol table. Because this table is never used in managed PE files, this field must be set to 0. |
12 | 4 | NumberOfSymbols | Number of entries in the COFF symbol table. This field must be set to 0 in managed PE files. |
16 | 2 | SizeOfOptionalHeader | Size of the PE header. This field is specific to PE files; it is set to 0 in COFF files. |
18 | 2 | Characteristics | Flags indicating the attributes of the file. (See Table 3-3.) |
The structure of the standard COFF header is defined in Winnt.h as follows:
typedef struct _IMAGE_FILE_HEADER { WORD Machine; WORD NumberOfSections; DWORD TimeDateStamp; DWORD PointerToSymbolTable; DWORD NumberOfSymbols; WORD SizeOfOptionalHeader; WORD Characteristics; } IMAGE_FILE_HEADER, *PIMAGE_FILE_HEADER;
The Machine types are also defined in Winnt.h, as listed in Table 3-2.
Constant | Value | Description |
IMAGE_FILE_MACHINE_UNKNOWN | 0 | Contents assumed to be applicable to any machine type—for unmanaged PE files only. |
IMAGE_FILE_MACHINE_I386 | 0x014c | Intel 386 or later. For managed PE files, contents are applicable to any machine type. |
IMAGE_FILE_MACHINE_R3000 | 0x0162 | MIPS little endian—the least significant byte precedes the most significant byte. 0x0160 big endian—the most significant byte precedes the least significant byte. |
IMAGE_FILE_MACHINE_R4000 | 0x0166 | MIPS little endian |
IMAGE_FILE_MACHINE_R10000 | 0x0168 | MIPS little endian |
IMAGE_FILE_MACHINE_WCEMIPSV2 | 0x0169 | MIPS little endian running Microsoft Windows CE 2 |
IMAGE_FILE_MACHINE_ALPHA | 0x0184 | Alpha AXP |
IMAGE_FILE_MACHINE_POWERPC | 0x01F0 | IBM PowerPC little endian |
IMAGE_FILE_MACHINE_SH3 | 0x01a2 | SH3 little endian |
IMAGE_FILE_MACHINE_SH3E | 0x01a4 | SH3E little endian |
IMAGE_FILE_MACHINE_SH4 | 0x01a6 | SH4 little endian |
IMAGE_FILE_MACHINE_ARM | 0x01c0 | ARM little endian |
IMAGE_FILE_MACHINE_THUMB | 0x01c2 | ARM processor with Thumb decompressor |
IMAGE_FILE_MACHINE_IA64 | 0x0200 | Intel IA64 |
IMAGE_FILE_MACHINE_MIPS16 | 0x0266 | MIPS |
IMAGE_FILE_MACHINE_MIPSFPU | 0x0366 | MIPS with FPU |
IMAGE_FILE_MACHINE_MIPSFPU16 | 0x0466 | MIPS16 with FPU |
IMAGE_FILE_MACHINE_ALPHA64 | 0x0284 | ALPHA AXP64 |
IMAGE_FILE_MACHINE_AXP64 | 0x0284 | ALPHA AXP64 |
As noted in Tables 3-1 and 3-2, the best strategy for a managed PE file is to specify IMAGE_FILE_MACHINE_I386 in the Machine field. Doing so ensures that the PE file will be able to execute on any machine that has the common language runtime installed. | |
The Characteristics field of a COFF header contains flags that indicate attributes of the PE/COFF file. These flags are defined in Winnt.h as shown in Table 3-3. Notice that the table refers to pure-IL managed PE files; the term pure-IL indicates that the image file contains no embedded native code.
Flag | Value | Description |
IMAGE_FILE_RELOCS_STRIPPED | 0x0001 | Image file only. This flag indicates that the file contains no base relocations and must be loaded at its preferred base address. In the case of base address conflict, the operating system loader reports an error. This flag should not be set for managed PE files. |
IMAGE_FILE_EXECUTABLE_IMAGE | 0x0002 | Flag indicates that the file is an image file (EXE or DLL). This flag should be set for managed PE files. If it is not set, this generally indicates a linker error. |
IMAGE_FILE_LINE_NUMS_STRIPPED | 0x0004 | COFF line numbers have been removed. This flag should be set for managed PE files because they do not use the debug information embedded in the PE file itself. Instead, the debug information is saved in accompanying program database (PDB) files. |
IMAGE_FILE_LOCAL_SYMS_STRIPPED | 0x0008 | COFF symbol table entries for local symbols have been removed. This flag should be set for managed PE files, for the reason given in the preceding entry. |
IMAGE_FILE_AGGRESIVE_WS_TRIM | 0x0010 | Aggressively trim the working set. This flag should not be set for pure-IL managed PE files. |
IMAGE_FILE_LARGE_ADDRESS_AWARE | 0x0020 | Application can handle addresses beyond the 2-GB range. This flag should not be set for pure-IL managed PE files. |
IMAGE_FILE_BYTES_REVERSED_LO | 0x0080 | Little endian. This flag should not be set for pure-IL managed PE files. |
IMAGE_FILE_32BIT_MACHINE | 0x0100 | Machine is based on 32-bit architecture. This flag is set by the current versions of code generators producing managed PE files. |
IMAGE_FILE_DEBUG_STRIPPED | 0x0200 | Debug information has been removed from the image file. |
IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP | 0x0400 | If the image file is on removable media, copy and run it from the swap file. This flag should not be set for pure-IL managed PE files. |
IMAGE_FILE_NET_RUN_FROM_SWAP | 0x0800 | If the image file is on a network, copy and run it from the swap file. This flag should not be set for pure-IL managed PE files. |
IMAGE_FILE_SYSTEM | 0x1000 | The image file is a system file (for example, a device driver). This flag should not be set for pure-IL managed PE files. |
IMAGE_FILE_DLL | 0x2000 | The image file is a DLL rather than an EXE. It cannot be directly run. |
IMAGE_FILE_UP_SYSTEM_ONLY | 0x4000 | The image file should be run on a uniprocessor machine only. This flag should not be set for pure-IL managed PE files. |
IMAGE_FILE_BYTES_REVERSED_HI | 0x8000 | Big endian. This flag should not be set for pure-IL managed PE files. |
The typical Characteristics value produced by existing code generators—the one employed by the MC++ compiler and linker as well as the one used by all the rest of the managed compilers, including ILAsm—for an EXE image file is 0x010E (IMAGE_FILE_EXECUTABLE_IMAGE IMAGE_FILE_LINE_NUMS_ STRIPPED IMAGE_FILE_LOCAL_SYMS_STRIPPED IMAGE_FILE_32BIT_ MACHINE). For a DLL image file, this value is 0x210E (IMAGE_FILE_ EXECUTABLE_IMAGE IMAGE_FILE_LINE_NUMS_STRIPPED IMAGE_FILE_ LOCAL_SYMS_STRIPPED IMAGE_FILE_32BIT_MACHINE IMAGE_FILE_DLL).
PE Header
The PE header, which immediately follows the COFF header, provides the information for the OS loader. Although this header is sometimes referred to as the optional header, it is optional only in the sense that object files usually don’t contain it. For PE files, this header is mandatory.
The size of the PE header is not fixed. It depends on the number of data directories defined in the header and is specified in the SizeOfOptionalHeader field of the COFF header. The structure of the PE header is defined in Winnt.h as follows:
typedef struct _IMAGE_OPTIONAL_HEADER { // Standard fields WORD Magic; BYTE MajorLinkerVersion; BYTE MinorLinkerVersion; DWORD SizeOfCode; DWORD SizeOfInitializedData; DWORD SizeOfUninitializedData; DWORD AddressOfEntryPoint; DWORD BaseOfCode; DWORD BaseOfData; // NT additional fields DWORD ImageBase; DWORD SectionAlignment; DWORD FileAlignment; WORD MajorOperatingSystemVersion; WORD MinorOperatingSystemVersion; WORD MajorImageVersion; WORD MinorImageVersion; WORD MajorSubsystemVersion; WORD MinorSubsystemVersion; DWORD Win32VersionValue; DWORD SizeOfImage; DWORD SizeOfHeaders; DWORD CheckSum; WORD Subsystem; WORD DllCharacteristics; DWORD SizeOfStackReserve; DWORD SizeOfStackCommit; DWORD SizeOfHeapReserve; DWORD SizeOfHeapCommit; DWORD LoaderFlags; DWORD NumberOfRvaAndSizes; IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; } IMAGE_OPTIONAL_HEADER32, *PIMAGE_OPTIONAL_HEADER32;
Table 3-4 describes the fields of the PE header.
Offset 32/64 | Size 32/64 | Field | Description |
0 | 2 | Magic | “Magic number” identifying the state of the image file. Acceptable values are 0x010B for a 32-bit PE file, 0x020B for a 64-bit PE file, and 0x107 for a ROM image file. Managed PE files must have this field set to 0x010B. |
2 | 1 | MajorLinkerVersion | Linker major version number. The MC++ compiler and linker set this field to 7; the pure-IL code generator employed by other compilers sets it to 6. |
3 | 1 | MinorLinkerVersion | Linker minor version number. |
4 | 4 | SizeOfCode | Size of the code section (.text), or the sum of all code sections if multiple code sections exist. The ILAsm compiler always emits the single code section. |
8 | 4 | SizeOfInitializedData | Size of the initialized data section (held in the field SizeOfRawData of the respective section header), or the sum of all such sections. The initialized data is defined as specific values, stored in the disk image file. |
12 | 4 | SizeOfUninitializedData | Size of the uninitialized data section (.bss), or the sum of all such sections. This data is not part of the disk file and does not have specific values, but the OS loader commits space for the data. |
16 | 4 | AddressOfEntryPoint | RVA of the entry point function. For unmanaged DLLs, this can be 0. For managed PE files, this value always points to the common language runtime invocation stub. |
20 | 4 | BaseOfCode | RVA of the beginning of the file’s code section(s). |
24/- | 4/- | BaseOfData | RVA of the beginning of the file’s data section(s). |
28/24 | 4/8 | ImageBase | Image’s preferred starting virtual address. In ILAsm, this field can be specified explicitly by the directive .imagebase <integer value> and/or the command-line option /BASE=<integer value>. The command-line option takes precedence over the directive. |
32 | 4 | SectionAlignment | Alignment of sections when loaded in memory. This setting must be greater than or equal to the value of the FileAlignment field. The default is the memory page size. |
36 | 4 | FileAlignment | Alignment of sections in the disk image file. The value should be a power of 2, from 512 to 64 K. If SectionAlignment is set to less than the memory page size, FileAlignment must match SectionAlignment. In ILAsm, this field can be specified explicitly by the directive .file alignment <integer value> and/or the command-line option /ALIGNMENT= <integer value>. The command-line option takes precedence over the directive. |
40 | 2 | MajorOperatingSystemVersion | Major version number of the required OS. |
42 | 2 | MinorOperatingSystemVersion | Minor version number of the required OS. |
44 | 2 | MajorImageVersion | Major version number of the application. |
46 | 2 | MinorImageVersion | Minor version number of the application. |
48 | 2 | MajorSubsystemVersion | Major version number of the subsystem. |
50 | 2 | MinorSubsystemVersion | Minor version number of the subsystem. |
52 | 4 | Win32VersionValue | Reserved. |
56 | 4 | SizeOfImage | Size of the image file (in bytes), including all headers. This field must be set to a multiple of the SectionAlignment value. |
60 | 4 | SizeOfHeaders | Sum of the sizes of the MS-DOS stub, the COFF header, the PE header, and the section headers, rounded up to a multiple of the FileAlignment value. |
64 | 4 | CheckSum | Checksum of the disk image file. |
68 | 2 | Subsystem | Subsystem required to run this image file. The values are defined in Winnt.h and are as follows:
In ILAsm, this field can be specified explicitly by the directive .subsystem <integer value> and/or the command-line option /SUBSYSTEM=<integer value>. The command-line option takes precedence over the directive. |
70 | 2 | DllCharacteristics | Obsolete, set to 0. |
72 | 4/8 | SizeOfStackReserve | Size of virtual memory to reserve for the initial thread’s stack. Only the SizeOfStackCommit field is committed; the rest is available in one-page increments. The default is 1 MB. |
76/80 | 4/8 | SizeOfStackCommit | Size of virtual memory initially committed for the initial thread’s stack. The default is one page. |
80/88 | 4/8 | SizeOfHeapReserve | Size of virtual memory to reserve for the initial process heap. Only the SizeOfHeapCommit field is committed; the rest is available in one-page increments. The default is 1 MB. |
84/96 | 4/8 | SizeOfHeapCommit | Size of virtual memory initially committed for the process heap. The default is one page. |
88/104 | 4 | LoaderFlags | Obsolete, set to 0. |
92/108 | 4 | NumberOfRvaAndSizes | Number of entries in the DataDirectory array; at least 16. Although it is theoretically possible to emit more than 16 data directories, all existing managed compilers emit exactly 16 data directories, with the sixteenth (last) data directory never used (reserved). |
Data Directory Table
The data directory table starts at offset 96 in a 32-bit PE header and at offset 112 in a 64-bit PE header. Each entry in the data directory table contains the relative virtual address and size of a table or a string used by the operating system. The data directory table entry is an 8-byte structure defined in Winnt.h as follows:
typedef struct _IMAGE_DATA_DIRECTORY { DWORD VirtualAddress; DWORD Size; } IMAGE_DATA_DIRECTORY, *PIMAGE_DATA_DIRECTORY;
The first field, named VirtualAddress, is, however, not a virtual address but rather an RVA; it is the address of the table when the image file is loaded into memory, relative to the base address of the image. The RVAs given in this table do not necessarily point to the beginning of a section, and the sections containing specific tables do not necessarily have specific names. The second field is the size in bytes.
Sixteen standard data directories are defined in the data directory table:
Export Directory table address and size The Export Directory table contains information about four other tables, which hold data on unmanaged exports of the PE file. Among managed compilers, only the MC++ compiler and linker and ILAsm are capable of exposing the managed methods exported by a managed PE file as unmanaged exports, to be consumed by an unmanaged caller. See Chapter 15, “Managed and Unmanaged Code Interoperation,” for details.
Import table address and size This table contains data on unmanaged imports consumed by the PE file. Among managed compilers, only the MC++ compiler and linker make any nontrivial use of this table, importing the unmanaged external functions used in the embedded unmanaged native code. Because other compilers, including the ILAsm compiler, do not embed the unmanaged native code in the managed PE files, Import Address tables (IATs) of the files produced by these compilers contain a single entry, that of the runtime entry function.
Resource table address and size Contains unmanaged resources embedded in the PE file; managed resources aren’t part of this data.
Exception table address and size This table contains information on unmanaged exceptions only.
Certificate table address and size The address entry points to a table of attribute certificates, which are not loaded into memory as part of the image file. As such, the first field of this entry is a file pointer rather than an RVA.
Base Relocation table address and size
Debug data address and size A managed PE file does not carry embedded debug data, so both entries of this data directory are set to 0.
Architecture data address and size
Global pointer RVA of the value to be stored in the global pointer register. The size must be set to 0.
TLS table address and size Among managed compilers, only the MC++ compiler and linker and the ILAsm compiler are able to produce the code that would use the thread local storage data.
Load Configuration table address and size
Bound Import table address and size
Import Address table address and size
Delay Import Descriptor address and size
Common Language Runtime header address and size
Reserved
Section Headers
The table of section headers must immediately follow the PE header. Because the file header has no direct pointer to the section table, the location of this table is calculated as the total size of the file headers plus 1.
The NumberOfSections field of the COFF header defines the number of entries in the section header table. The section header enumeration in the table is one-based, with the order of the sections defined by the linker. The sections follow one another contiguously in the order set in the section header table, with starting RVAs aligned by the value of the SectionAlignment field of the PE header.
A section header is a 40-byte structure defined in Winnt.h as follows:
typedef struct _IMAGE_SECTION_HEADER { BYTE Name[8]; union { DWORD PhysicalAddress; DWORD VirtualSize; } Misc; DWORD VirtualAddress; DWORD SizeOfRawData; DWORD PointerToRawData; DWORD PointerToRelocations; DWORD PointerToLinenumbers; WORD NumberOfRelocations; WORD NumberOfLinenumbers; DWORD Characteristics; } IMAGE_SECTION_HEADER, *PIMAGE_SECTION_HEADER;
The fields contained in the IMAGE_SECTION_HEADER structure can be described as follows:
Name (8-byte ANSI string) Represents the name of the section. Section names start with a dot (for instance, .reloc). If the section name contains exactly eight characters, the null terminator is omitted. If the section name has fewer than eight characters, the array Name is padded with null characters. Image files cannot have section names with more than eight characters. In object files, however, section names can be longer. (Imagine a long-winded code generator emitting a section named .myownsectionnobodyelsecouldevergrok.) In this case, the name is placed in the string table, and the field contains the / (slash) character in the first byte, followed by an ANSI string containing a decimal representation of the respective offset in the string table.
PhysicalAddress/VirtualSize (4-byte unsigned integer) In image files, this field holds the actual (unaligned) size in bytes of the code or data in this section.
VirtualAddress (4-byte unsigned integer) Despite its name, this field holds the RVA of the beginning of the section.
SizeOfRawData (4-byte unsigned integer) In an image file, this field holds the size in bytes of the initialized data on disk, rounded up to a multiple of the FileAlignment value specified in the PE header. If SizeOfRawData is less than VirtualSize, the rest of the section is padded with null bytes.
PointerToRawData (4-byte unsigned integer) This field holds a file pointer to the section’s first page. In image files, this value should be a multiple of the FileAlignment value specified in the PE header.
PointerToRelocations (4-byte unsigned integer) This is a file pointer to the beginning of relocation entries for the section. In image files, this field is not used and should be set to 0.
PointerToLinenumbers (4-byte unsigned integer) This field holds a file pointer to the beginning of line-number entries for the section. In managed PE files, the COFF line numbers are stripped and this field must be set to 0.
NumberofRelocations (2-byte unsigned integer) In managed image files, this field should be set to 0.
NumberOfLinenumbers (2-byte unsigned integer) In managed image files, this field should be set to 0.
Characteristics (4-byte unsigned integer) This field specifies the characteristics of an image file and holds a combination of binary flags, described in Table 3-5.
The section Characteristics flags are defined in Winnt.h. Some of these flags are reserved, and some are relevant to object files only. Table 3-5 lists the flags that are valid for PE files.
Flag | Value | Description |
IMAGE_SCN_SCALE_INDEX | 0x00000001 | TLS index is scaled (.tls section only). |
IMAGE_SCN_CNT_CODE | 0x00000020 | Section contains the executable code. In ILAsm compiler generated PE files, only the .text section carries this flag. |
IMAGE_SCN_CNT_INITIALIZED_DATA | 0x00000040 | Section contains initialized data. |
IMAGE_SCN_CNT_UNINITIALIZED_DATA | 0x00000080 | Section contains uninitialized data. |
IMAGE_SCN_NO_DEFER_SPEC_EXC | 0x00004000 | Reset speculative exception handling bits in the type library (TLB) entries for this section. |
IMAGE_SCN_LNK_NRELOC_OVFL | 0x01000000 | Section contains extended relocations. |
IMAGE_SCN_MEM_DISCARDABLE | 0x02000000 | Section can be discarded as needed. |
IMAGE_SCN_MEM_NOT_CACHED | 0x04000000 | Section cannot be cached. |
IMAGE_SCN_MEM_NOT_PAGED | 0x08000000 | Section cannot be paged. |
IMAGE_SCN_MEM_SHARED | 0x10000000 | Section can be shared in memory. |
IMAGE_SCN_MEM_EXECUTE | 0x20000000 | Section can be executed as code. In ILAsm compiler generated PE files, only the .text section carries this flag. |
IMAGE_SCN_MEM_READ | 0x40000000 | Section can be read. |
IMAGE_SCN_MEM_WRITE | 0x80000000 | Section can be written to. In ILAsm compiler generated PE files, only the .sdata and .tls sections carry this flag. |
The ILAsm compiler generates the following sections in a PE file:
.text A read-only section containing the common language runtime header, the metadata, the IL code, managed structured exception handling information, and managed resources.
.sdata A read/write section containing data.
.reloc A read-only section containing relocations.
.rsrc A read-only section containing unmanaged resources.
.tls A read/write section containing thread local storage data.