The symbol table belongs to COFF object files,
where it names every function and variable for the linker. Microsoft’s specification
calls it deprecated in images, and Microsoft’s linker leaves it out, putting debug
information in a PDB instead. GNU ld keeps it: programs and DLLs built with MinGW,
Cygwin or Wine often still have it, with the long section names of their DWARF debug
information in its string table. The COFF
file header locates it: PointerToSymbolTable is its file offset and
NumberOfSymbols the number of records. Like the certificate table, it lies after the
sections and is not loaded. Wine’s 64-bit kernel32.dll, for example, has
20,938 symbol records at 0x1B7000 and a string table of 118,585 bytes after
them, and the explorer uses its function names in the disassembly, such as
__wine_spec_unimplemented_stub.
Symbol records
IMAGE_SYMBOL, 18 bytes, with no padding between records:
| Offset | Size | Type | Field | Meaning |
|---|---|---|---|---|
| 0 | 8 | char[8] or u32 × 2 | Name | The name, zero-padded, when it fits in 8 bytes. Otherwise 4 zero bytes, then the offset of the name in the string table. |
| 8 | 4 | u32 | Value | Depends on the storage class: for a function or variable, its offset in its section. |
| 12 | 2 | i16 | SectionNumber | The section it is in, counting from 1. 0: undefined (external); −1: an absolute value, not an address; −2: a debugging symbol. |
| 14 | 2 | u16 | Type | The symbol’s type. A value of 0x20 (function) in bits 4–7 marks a function; the other bits are rarely used. |
| 16 | 1 | u8 | StorageClass | See Storage classes. |
| 17 | 1 | u8 | NumberOfAuxSymbols | The number of auxiliary records that follow this one. |
The explorer names code in the disassembly from this table: every symbol in a code section that is marked a function or has the EXTERNAL class, at the section’s address plus Value, unless that address already has a name.
Storage classes
| Value | Name | Value | Name |
|---|---|---|---|
| 0 | NULL | 13 | TYPE_DEFINITION |
| 1 | AUTOMATIC | 14 | UNDEFINED_STATIC |
| 2 | EXTERNAL | 15 | ENUM_TAG |
| 3 | STATIC | 16 | MEMBER_OF_ENUM |
| 4 | REGISTER | 17 | REGISTER_PARAM |
| 5 | EXTERNAL_DEF | 18 | BIT_FIELD |
| 6 | LABEL | 100 | BLOCK (.bb and .eb) |
| 7 | UNDEFINED_LABEL | 101 | FUNCTION (.bf and .ef) |
| 8 | MEMBER_OF_STRUCT | 102 | END_OF_STRUCT |
| 9 | ARGUMENT | 103 | FILE |
| 10 | STRUCT_TAG | 104 | SECTION |
| 11 | MEMBER_OF_UNION | 105 | WEAK_EXTERNAL |
| 12 | UNION_TAG | 107 | CLR_TOKEN |
| 255 | END_OF_FUNCTION |
The ones met in images are mainly EXTERNAL (2) for global functions and variables, STATIC (3) for local ones and for section symbols, and FILE (103) for the names of the source files.
Auxiliary records
A symbol can be followed by auxiliary records, 18 bytes each, counted in the record numbering. Their layout depends on the symbol before them:
- Function definitions (an EXTERNAL function): the tag index, the function’s size, pointers to its line numbers and to the next function.
.bfand.ef(FUNCTION class): line numbers of the start and end of a function.- Weak externals: TagIndex (4), the symbol to use instead, and Characteristics (4), how to search for it.
- Files (FILE class): the source file’s name in the record’s 18 bytes, continuing over as many records as it needs.
- Section definitions (a STATIC symbol named after a section): Length (4), NumberOfRelocations (2), NumberOfLinenumbers (2), CheckSum (4), Number (2), Selection (1) and 3 unused bytes.
The string table
The string table follows the last symbol record at once, at PointerToSymbolTable +
18 × NumberOfSymbols. It starts with its size, 4 bytes, which counts those 4 bytes;
then come the strings, each ending in a zero byte. A symbol or section name refers to
a string by its offset from the start of the table, so the first string is at offset
4. A section named /4 in the section
table has its real name, often .debug_info, at offset 4 here.