An image holds some absolute addresses, worked out for the image loaded at its
ImageBase: pointers stored
in its data, and in the load configuration and the TLS directory. When the image is
loaded somewhere else, because the address is taken or because ASLR moves it, every
such address must be adjusted. The base relocation table, at data directory 5 and
usually in the .reloc section, lists where they are. A file with the
RELOCS_STRIPPED flag has none and can only load at its image base.
x64 code needs few of them. Most instructions reach data relative to the instruction
pointer, as in lea rax, [rip + 0xe041], the first instruction of the
example below, which is right wherever the image is loaded. So the relocations of an
x64 image are mostly for pointers in its data.
Blocks
The table is a run of blocks, one for each 4 KB page that has addresses to fix. Each block starts on a 4-byte boundary:
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 4 | u32 | VirtualAddress | The RVA of the page. |
| 4 | 4 | u32 | SizeOfBlock | The size of the block in bytes, these 8 included. |
| 8 | 2 each | u16 | Entries | (SizeOfBlock − 8) ÷ 2 entries. The top 4 bits of each are its type, and the low 12 bits its offset in the page. |
To apply an entry, the loader works out the difference between the address the image was loaded at and its ImageBase, a 64-bit number, and adds it to the value at VirtualAddress + offset. A block whose entries come to an odd number ends with a type 0 entry, padding, to keep the next block on a 4-byte boundary.
0000DC00 00 A0 00 00 0C 00 00 00 30 A4 38 A4 00 B0 00 00
0000DC10 14 00 00 00 F8 A3 00 A4 88 A4 A0 A4 A8 A4 00 00
0x12000 (file 0xDC00): page 0xA000, 12 bytes, two DIR64 entries; page 0xB000, 20 bytes, five DIR64 entries and a padding entry| Entry | Fixes | Holds | What it is |
|---|---|---|---|
0xA430 | 0xA430 | 0x180009B10 | The two function pointers in .00cfg that Control Flow Guard uses. |
0xA438 | 0xA438 | 0x180009BA0 | |
0xA3F8 | 0xB3F8 | 0x18000E050 | Pointers to data in .data. |
0xA400 | 0xB400 | 0x18000E0F0 | |
0xA488 | 0xB488 | 0x18000E000 | The load configuration’s SecurityCookie. |
0xA4A0 | 0xB4A0 | 0x18000A430 | Its GuardCFCheckFunctionPointer. |
0xA4A8 | 0xB4A8 | 0x18000A438 | Its GuardCFDispatchFunctionPointer. |
Seven relocations for 35 KB of code; the same library built for 32-bit x86 has over a thousand, because 32-bit code holds absolute addresses in its instructions.
Types
| Type | Name | What it fixes |
|---|---|---|
| 0 | ABSOLUTE | Nothing: padding, skipped. |
| 1 | HIGH | The high 16 bits of a 32-bit address, in a 16-bit field. |
| 2 | LOW | The low 16 bits of a 32-bit address, in a 16-bit field. |
| 3 | HIGHLOW | A whole 32-bit address, the usual type in 32-bit files. |
| 4 | HIGHADJ | The high 16 bits, adjusted for the low 16 bits held in the next entry, which is a parameter rather than an entry of its own. |
| 5 | MIPS_JMPADDR, ARM_MOV32, RISCV_HIGH20 | Depends on the machine: a MIPS jump, an ARM MOVW/MOVT pair, or a RISC-V high 20 bits. |
| 6 | – | Reserved. |
| 7 | THUMB_MOV32, RISCV_LOW12I | A Thumb-2 MOVW/MOVT pair, or a RISC-V low 12 bits in an I-type instruction. |
| 8 | RISCV_LOW12S, LOONGARCH32_MARK_LA, LOONGARCH64_MARK_LA | A RISC-V low 12 bits in an S-type instruction, or a LoongArch address load. |
| 9 | MIPS_JMPADDR16 | A MIPS16 jump. winnt.h also names it IA64_IMM64, a 64-bit immediate in an Itanium instruction bundle. |
| 10 | DIR64 | A whole 64-bit address. The usual type in PE32+ files, and the one x64 images use. |
The explorer names types 5, 7, 8 and 9 by the file’s machine, and for HIGHLOW and
DIR64 shows the value each entry patches, as in “DIR64 at 0xB488 ·
patches 0x18000E000”.