A PE file keeps its code and data in sections. The section table, straight after the optional header, has one entry for each section, NumberOfSections in all. Each entry says where the section goes in memory, where its bytes are in the file, and what may be done with it: read, write or execute.
The section header
IMAGE_SECTION_HEADER, 40 bytes:
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 8 | char[8] | Name | The name, padded with zero bytes, with no terminator when it is 8 characters long; see Names. |
| 8 | 4 | u32 | VirtualSize | The size of the section in memory. When it is larger than SizeOfRawData, the rest is filled with zeros. |
| 12 | 4 | u32 | VirtualAddress | The RVA of its first byte, a multiple of SectionAlignment. |
| 16 | 4 | u32 | SizeOfRawData | The size of its data in the file, a multiple of FileAlignment. |
| 20 | 4 | u32 | PointerToRawData | The file offset of its data, a multiple of FileAlignment; 0 when it has none. |
| 24 | 4 | u32 | PointerToRelocations | 0 in images (COFF relocations are for object files). |
| 28 | 4 | u32 | PointerToLinenumbers | 0; COFF line numbers are deprecated. |
| 32 | 2 | u16 | NumberOfRelocations | 0 in images. |
| 34 | 2 | u16 | NumberOfLinenumbers | 0. |
| 36 | 4 | u32 | Characteristics | Flags; see Flags. |
000001F0 2E 74 65 78 74 00 00 00
00000200 9E 7F 00 00 00 10 00 00 00 80 00 00 00 04 00 00
00000210 00 00 00 00 00 00 00 00 00 00 00 00 20 00 00 60
0x1F8: .text; 0x7F9E bytes in memory at RVA 0x1000; 0x8000 bytes in the file at 0x400; flags 0x60000020: CNT_CODE, MEM_EXECUTE and MEM_READThat file’s five sections:
| Name | RVA | Size in memory | File offset | Size in file | Flags |
|---|---|---|---|---|---|
.text | 0x1000 | 0x7F9E | 0x400 | 0x8000 | 0x60000020 code, read, execute |
.rdata | 0x9000 | 0x2B04 | 0x8400 | 0x2C00 | 0x40000040 data, read |
.data | 0xC000 | 0xCE4 | 0xB000 | 0x200 | 0xC0000040 data, read, write |
.rsrc | 0xD000 | 0x1B4 | 0xB200 | 0x200 | 0x40000040 data, read |
.reloc | 0xE000 | 0x86C | 0xB400 | 0xA00 | 0x42000040 data, read, discardable |
.data is 0xCE4 bytes in memory but only 0x200
in the file: the rest is uninitialised data, which the loader fills with zeros.
Names
The name is 8 bytes. A longer name is written as / and a decimal number:
the offset of the full name in the COFF
string table. Microsoft’s linker truncates long names in images instead, but MinGW
and Wine files use the long form, such as /4 for
.debug_info. The loader finds the tables through the data directories,
not by section name.
Names seen most in the files examined for this site:
| Name | Usually holds |
|---|---|
.text | Code. Borland’s linkers call it CODE. |
.rdata | Read-only data: constants, the import and export tables, the debug directory and the load configuration. |
.data | Initialised writable data. Borland: DATA; Watcom: DGROUP and AUTO. |
.bss | Uninitialised data, with no bytes in the file. Borland: BSS. |
.idata | The import tables, in files from older and non-Microsoft linkers. |
.edata | The export tables, likewise. |
.rsrc | The resources. |
.reloc | The base relocations. |
.pdata | The exception table, on machines that have one. |
.tls | The thread-local storage template. |
.CRT | The C run-time library’s initialiser lists. |
.debug_* | DWARF debug information (MinGW and Wine). |
.rodata, .eh_frame | Read-only data and unwind tables in GNU-built files. |
Rarer names include .E32_UID in Symbian emulator DLLs and
.nv_fatb, NVIDIA’s CUDA fat binaries.
Flags
| Bit | Name | Meaning |
|---|---|---|
0x00000008 | TYPE_NO_PAD | Obsolete. |
0x00000020 | CNT_CODE | Holds executable code. |
0x00000040 | CNT_INITIALIZED_DATA | Holds initialised data. |
0x00000080 | CNT_UNINITIALIZED_DATA | Holds uninitialised data. |
0x00000100 | LNK_OTHER | Reserved. |
0x00000200 | LNK_INFO | Comments or other information (object files). |
0x00000800 | LNK_REMOVE | Not to become part of the image (object files). |
0x00001000 | LNK_COMDAT | COMDAT data (object files). |
0x00008000 | GPREL | Data referred to through the global pointer. |
0x00020000 | MEM_PURGEABLE, MEM_16BIT | Reserved. |
0x00040000 | MEM_LOCKED | Reserved. |
0x00080000 | MEM_PRELOAD | Reserved. |
0x00100000–0x00E00000 | ALIGN_1BYTES … ALIGN_8192BYTES | Alignment of the section’s data, in object files only. |
0x01000000 | LNK_NRELOC_OVFL | More COFF relocations than fit in NumberOfRelocations (object files). |
0x02000000 | MEM_DISCARDABLE | Can be discarded once loaded, as .reloc is. |
0x04000000 | MEM_NOT_CACHED | Cannot be cached. |
0x08000000 | MEM_NOT_PAGED | Cannot be paged out. |
0x10000000 | MEM_SHARED | Shared between every process that loads the image. |
0x20000000 | MEM_EXECUTE | Can be executed. |
0x40000000 | MEM_READ | Can be read. |
0x80000000 | MEM_WRITE | Can be written. |
Section data
Each section’s bytes are at PointerToRawData, SizeOfRawData long, after the headers and in the order of the section table. The loader maps each one to ImageBase + VirtualAddress, and zero-fills the memory from SizeOfRawData up to VirtualSize. The headers themselves are loaded at RVA 0, SizeOfHeaders long.
Mapping an RVA to a file offset. Find the section with
VirtualAddress ≤ RVA < VirtualAddress + max(VirtualSize, SizeOfRawData). The file
offset is PointerToRawData + (RVA − VirtualAddress), and it has bytes in the file only
while RVA − VirtualAddress < SizeOfRawData. An RVA below the first section is in the
headers, and its file offset is the RVA itself. In the example, the export directory
at RVA 0xA490 is in .rdata, so it is at
0x8400 + (0xA490 − 0x9000) =
0x9890 in the file.
The explorer shows each section’s raw data as one part, in file order. Inside it are
the tables that lie there, then the bytes no table covers: “Code” in a section with
CNT_CODE or MEM_EXECUTE, which is disassembled,
and “Data” in the others. The bytes after VirtualSize, up to SizeOfRawData, are
“Alignment padding”. In the 32-bit glfw3.dll, .rdata holds the import
address tables, the debug directory, the load configuration, the SafeSEH table, the
POGO debug data, the export tables and the import descriptors, with data between
them.
- A PointerToRawData of 0 means the section has no data in the file, whatever
SizeOfRawData says. Watcom’s linker gives
.bssa SizeOfRawData but a pointer of 0. - The Windows loader rounds PointerToRawData down to a multiple of 512, and some packers rely on it. The explorer does not round, so such files are laid out as written.
- A section whose data lies inside an earlier part, or runs past the end of the file, is reported. In the files examined for this site, truncated downloads were one cause.