When Windows loads a segment, it does not know in advance where the segment’s far calls and pointers lead: other segments are put wherever there is room, and functions in other modules are found only then. So each place in a segment that holds such an address is listed in a relocation record, and Windows writes the real address there when it loads the segment. The records are also how a module imports functions.
Records
A segment whose flags have
0x0100 set has its relocations straight after its
data: a 16-bit count, then that many
records of 8 bytes:
| Offset | Size | Field | Meaning |
|---|---|---|---|
| 0 | 1 | address type | What kind of value is patched; see Address types. |
| 1 | 1 | relocation type | What the target is, and whether it is added; see Relocation types. |
| 2 | 2 | offset | The offset in the segment of the first place to patch; see Chains. |
| 4 | 4 | target | Two fields that depend on the relocation type; see Targets. |
Address types
| Value | The place holds | Size |
|---|---|---|
| 0 | The low byte of an offset | 1 byte |
| 2 | A selector (a segment) | 2 bytes |
| 3 | A 16:16 far pointer: an offset, then a selector | 4 bytes |
| 5 | A 16-bit offset | 2 bytes |
| 11 | A 16:32 far pointer | 6 bytes |
| 13 | A 32-bit offset | 4 bytes |
Relocation types
| Bits | Meaning |
|---|---|
0x03 = 0 | An internal reference: a place in this module. |
0x03 = 1 | An imported ordinal: a function in another module, by number. |
0x03 = 2 | An imported name: a function in another module, by name. |
0x03 = 3 | An operating system fixup. |
0x04 | Additive: add the target to what is already there; see Chains. |
Targets
| Type | Bytes 4–7 |
|---|---|
| Internal, fixed segment | The segment number (1 byte), 0 (1 byte), the offset in that segment (2 bytes). |
| Internal, movable segment | 0xFF (1 byte), 0 (1 byte), an entry table ordinal (2 bytes): the address is that entry point’s. |
| Imported ordinal | The module’s index in the module reference table, from 1 (2 bytes), the function’s ordinal (2 bytes). |
| Imported name | The module’s index (2 bytes), the offset of the function’s name in the imported names table (2 bytes). |
| Operating system fixup | A fixup type (2 bytes), 0 (2 bytes). |
Wine’s winedump calls the operating system fixups floating-point emulator fixups and ignores them. Microsoft’s own list of the fixup type numbers could not be checked, so they are not given here; the explorer shows the number.
Chains
One record can patch many places. Unless the record is additive, each place to patch
holds, in the file, the offset of the next place to patch with the same target, and
0xFFFF ends the chain. Windows starts at the record’s offset, reads the
next offset there, writes the target, and goes on until it meets 0xFFFF.
So a function called from twenty places can need just one record.
With the additive bit set, there is no chain: the target is added to the value already at the one place the record names.
The explorer follows the chains to find every place a record patches. In the Code
section it names each call to an imported function, as → MODULE.ordinal
or → MODULE!Name; for a far pointer it puts the name on the far
CALL instruction,
9A, just before it.
Worked example
TEST1.EXE’s segment 1 has 2,669 bytes of data at 0x300, so its
relocations start at 0xD6D:
00000D6D 96 00 03 00 09 00 FF 00 1E 00 03 00 29 00 FF 00 ............)...
00000D7D 14 00 03 00 39 00 FF 00 19 00 03 00 44 00 FF 00 ....9.......D...
…
00000E17 03 01 5E 03 04 00 5B 00 ..^...[.
03 00 09 00 FF 00 1E 00: a 16:16 far pointer, an internal reference, at offset 9 of the segment, to a movable segment’s entry point, ordinal 30. The bytes before it, at offset 8, are9A: it is a far call.03 01 5E 03 04 00 5B 00: a 16:16 far pointer, an imported ordinal, at offset0x035E, from module 4, KERNEL, ordinal 91.
The program’s entry point is 1:035D, so the very first instruction is the
far call 9A FF FF 00 00 whose pointer the second record patches. Its
placeholder offset, 0xFFFF, also ends the chain at once. Elsewhere the
explorer would label such a call → KERNEL.91; at this address the entry
point’s own label comes first.