An MS-DOS program can be loaded at any segment, but its code holds segment addresses: far calls and jumps, far pointers, and the values it loads into DS and ES. The linker writes them as if the program were loaded at segment 0 and lists where each one is. DOS adds the real load segment to every place the list names. That list is the relocation table.
Entries
The table has e_crlc
entries of 4 bytes, starting at the file offset
e_lfarlc:
| Offset | Size | Field | Meaning |
|---|---|---|---|
| 0 | 2 | offset | The offset of the word to adjust, within its segment. |
| 2 | 2 | segment | The segment of the word, in paragraphs from the start of the load module. |
An entry is written segment first, as segment:offset, though the offset comes first in the file. The word it names is at
file offset = e_cparhdr × 16 + segment × 16 + offset
The offset can be anything up to 0xFFFF, so the entries for one segment
of the program usually all give that segment and differ only in the offset.
Applying them
When DOS has copied the load module into memory, at the load segment (the PSP’s
segment plus 0x10), it goes through the entries in turn. For each, it
finds the word at load segment + segment, offset offset, and adds
the load segment to it. It adds the load segment to e_cs and
e_ss from the header too, but those are not in the table.
Each word the table names therefore holds, in the file, a segment relative to the load module. The explorer uses these values to split the load module into the segments the program uses; see The load module. Its Relocations table lists every entry with the file offset it points at and the word there.
Worked example
ORGART.EXE has 95 entries from 0x1C, straight after the header’s fields;
its load module starts at 0x1A0.
0000001C 08 00 00 00 3A 00 00 00 E9 00 00 00 20 01 00 00
0000002C 5D 01 00 00 8A 01 00 00 A1 01 00 00 AF 01 00 00
…
00000178 01 00 AE 00 1B 01 AE 00 9D 30 AE 00 F1 32 AE 00
The first entry, 0000:0008, names the word at 0x1A0 + 8 =
0x1A8. It holds 0x00AE:
000001A0 55 89 E5 31 C0 9A 30 05 AE 00 66 31 FF 66 31 C0 U..1..0...f1.f1.
The bytes 9A 30 05 AE 00 at 0x1A5 are a far call to
00AE:0530 (see CALL
in the x86 instruction reference). The relocation is its
segment half. If DOS loads the program at segment 0x1234, it changes the
word to 0x12E2, and the call reaches 12E2:0530. The entry
00AE:0001 later in the table names a word inside that segment itself, at
0x1A0 + 0xAE0 + 1 = 0xC81.