Each stream is read from the MSF container as one
run of bytes. Numbers are little-endian; a stream number of 0xFFFF (−1)
means “no stream”. Where Microsoft’s and LLVM’s documentation say a field’s purpose is
unknown, this page says so too.
Stream numbers
| Stream | Name | Holds |
|---|---|---|
| 0 | Old directory | The previous stream directory. |
| 1 | PDB stream | Version, GUID and age to match the program; the named stream map; feature codes. |
| 2 | TPI stream | Type records, and the number of its hash stream. |
| 3 | DBI stream | Modules, section contributions, source files, and the numbers of the module, public, global and symbol record streams. |
| 4 | IPI stream | ID records (functions, strings, build information), in the same layout as TPI. |
| named | /names | The PDB-wide string table, found through the named stream map. |
| named | /LinkInfo | Linker information; layout not documented. |
| named | /src/headerblock | Index of source files embedded in the PDB (such as .natvis files). |
| from DBI | Module streams | One per compiland: its symbol records and line information. |
| from DBI | Public stream | Hash of public symbols, address map, thunk map. |
| from DBI | Global stream | Hash of all global symbols. |
| from DBI | Symbol record stream | The symbol records that the public and global streams point into. |
| from TPI/IPI | Hash streams | Hashes and offsets for looking up type records. |
PDB stream (stream 1)
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | Version | Format version (below); in practice always 20000404 (VC70). |
| 4 | 4 | Signature | A time() stamp from when the PDB was written. Not used for matching. |
| 8 | 4 | Age | How many times the PDB has been written. Must equal the age in the program. |
| 12 | 16 | UniqueId | The PDB’s GUID. Must equal the GUID in the program. |
| 28 | – | Named stream map | See below. |
| – | 4 each | Feature codes | To the end of the stream (below). |
A program matches its PDB through the IMAGE_DEBUG_TYPE_CODEVIEW record
in its PE debug directory, which holds the same GUID and age. The linker makes a new
GUID for a fresh link (or keeps it for an incremental one) and increments the age each
time.
Versions
| Value | Name |
|---|---|
19941610 | VC2 |
19950623 | VC4 |
19950814 | VC41 |
19960307 | VC50 |
19970604 | VC98 |
19990604 | VC70Dep |
20000404 | VC70 (the value seen in practice) |
20030901 | VC80 |
20091201 | VC110 |
20140508 | VC140 |
Named stream map
Maps stream names such as /names to stream numbers. It has no overall
length, so the feature codes can only be found by reading it through.
| Field | Size | Meaning |
|---|---|---|
| StringBufferSize | 4 | Bytes of names that follow. |
| StringBuffer | StringBufferSize | The stream names, each ending in a 0 byte. |
| Hash table | variable | A serialised hash table whose keys are offsets of names in the string buffer and whose values (4 bytes) are stream numbers. |
Feature codes
| Value | Name | Meaning |
|---|---|---|
20091201 | VC110 | There is an IPI stream; no other feature codes follow. |
20140508 | VC140 | There is an IPI stream; other feature codes may follow. |
0x4D544F4E (NOTM) | NoTypeMerge | The TPI stream may hold duplicate types. |
0x494E494D (MINI) | MinimalDebugInfo | Linked with /DEBUG:FASTLINK: there are no TPI or IPI records, and the types stay in the object files. |
Serialised hash table
Used by the named stream map (and the TPI hash adjusters).
| Field | Size | Meaning |
|---|---|---|
| Size | 4 | Number of entries. |
| Capacity | 4 | Number of buckets. Writers keep Size at most about two thirds of it. |
| Present bit vector | 4 + 4 × n | A word count n, then n 32-bit words; bit k (bit k mod 32 of word k / 32) is set when bucket k holds an entry. |
| Deleted bit vector | 4 + 4 × n | The same form; marks buckets whose entry was deleted. |
| Entries | Size × (4 + value size) | A 4-byte key and a value for each present bucket, in bucket order. |
String table (/names)
Strings shared by the whole PDB, chiefly source file names referred to from the line information.
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | Signature | 0xEFFEEFFE. |
| 4 | 4 | HashVersion | 1 or 2: the table is hashed with LLVM’s hashStringV1 or hashStringV2 respectively. |
| 8 | 4 | ByteSize | Bytes of strings that follow. |
| 12 | ByteSize | Strings | Null-terminated strings. Other records refer to a string by its offset here. |
| – | 4 | Hash count | Number of hash slots. |
| – | 4 each | Hash slots | For each slot, a string ID (its offset in the strings). |
| – | 4 | Name count | Number of strings in the table. |
TPI and IPI streams (streams 2 and 4)
Both have the same 56-byte header, followed by the
type records. TPI holds types; IPI holds ID
records (LF_FUNC_ID, LF_MFUNC_ID, LF_BUILDINFO,
LF_SUBSTR_LIST, LF_STRING_ID, LF_UDT_SRC_LINE,
LF_UDT_MOD_SRC_LINE).
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | Version | In practice 20040203 (V80). Others: 19950410 V40, 19951122 V41, 19961031 V50, 19990903 V70. |
| 4 | 4 | HeaderSize | 56. |
| 8 | 4 | TypeIndexBegin | Index of the first record, normally 0x1000; lower indexes are simple types. |
| 12 | 4 | TypeIndexEnd | One more than the last index; the record count is End − Begin. |
| 16 | 4 | TypeRecordBytes | Bytes of type records after the header. |
| 20 | 2 | HashStreamIndex | The hash stream, or −1. |
| 22 | 2 | HashAuxStreamIndex | An auxiliary hash stream; not seen in practice, purpose unknown. |
| 24 | 4 | HashKeySize | Bytes per hash value, normally 4. |
| 28 | 4 | NumHashBuckets | Buckets the hash values were reduced to. |
| 32 | 4 | HashValueBufferOffset | Where the hash values start in the hash stream. |
| 36 | 4 | HashValueBufferLength | Their length: 0, or record count × HashKeySize. |
| 40 | 4 | IndexOffsetBufferOffset | Where the index offsets start. |
| 44 | 4 | IndexOffsetBufferLength | Their length. |
| 48 | 4 | HashAdjBufferOffset | Where the hash adjusters start. |
| 52 | 4 | HashAdjBufferLength | Their length. |
Hash stream
| Part | Meaning |
|---|---|
| Hash values | Either none or one per record, in index order, each HashKeySize bytes. |
| Index offsets | Pairs of 4-byte values: a type index and the offset of its record in the record data, for some of the records, so a reader can binary-search and then walk forward. |
| Hash adjusters | A serialised hash table from hash values to type indexes, which appears to serve incremental linking by pointing at newer versions of types. |
DBI stream (stream 3)
Header (64 bytes)
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | VersionSignature | Always −1. |
| 4 | 4 | VersionHeader | In practice 19990903 (V70). Others: 930803 VC41, 19960307 V50, 19970606 V60, 20091201 V110. |
| 8 | 4 | Age | Same as the PDB stream’s age. |
| 12 | 2 | GlobalStreamIndex | The global symbol stream. |
| 14 | 2 | BuildNumber | Bits 0–7 minor version, bits 8–14 major version of the toolset; bit 15 set for this (new) layout. |
| 16 | 2 | PublicStreamIndex | The public symbol stream. |
| 18 | 2 | PdbDllVersion | Version of the mspdb DLL that wrote it. |
| 20 | 2 | SymRecordStream | The symbol record stream. |
| 22 | 2 | PdbDllRbld | Unknown. |
| 24 | 4 | ModInfoSize | Bytes of the module info substream. |
| 28 | 4 | SectionContributionSize | Bytes of the section contribution substream. |
| 32 | 4 | SectionMapSize | Bytes of the section map substream. |
| 36 | 4 | SourceInfoSize | Bytes of the file info substream. |
| 40 | 4 | TypeServerMapSize | Bytes of the type server map substream. |
| 44 | 4 | MFCTypeServerIndex | Index of the MFC type server in that map. |
| 48 | 4 | OptionalDbgHeaderSize | Bytes of the optional debug header substream. |
| 52 | 4 | ECSubstreamSize | Bytes of the Edit and Continue substream. |
| 56 | 2 | Flags | Bit 0: incrementally linked. Bit 1: private symbols stripped. Bit 2: has conflicting types (/DEBUG:CTYPES). Bits 3–15 reserved. |
| 58 | 2 | Machine | Target machine; common values are 0x14C (x86) and 0x8664 (x64). |
| 60 | 4 | Padding | 0. |
Seven substreams follow in this order, with the sizes above: module info, section contributions, section map, file info, type server map, Edit and Continue, optional debug header. The stream is 64 bytes plus their sizes.
Module info
One record per module (object file, or Import:dll, or the
special * Linker * module).
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | Unused1 | – |
| 4 | 28 | SectionContr | The module’s first section contribution (layout below). |
| 32 | 2 | Flags | Bit 0: dirty (changed since read). Bit 1: has Edit and Continue information. Bits 8–15: type server index. |
| 34 | 2 | ModuleSymStream | The module stream, or −1. |
| 36 | 4 | SymByteSize | Bytes of symbols in that stream, including its 4-byte signature. |
| 40 | 4 | C11ByteSize | Bytes of old (C11) line information. |
| 44 | 4 | C13ByteSize | Bytes of C13 line information. |
| 48 | 2 | SourceFileCount | Number of source files. |
| 50 | 2 | Padding | – |
| 52 | 4 | Unused2 | – |
| 56 | 4 | SourceFileNameIndex | Name index of the primary source file (0 in practice). |
| 60 | 4 | PdbFilePathNameIndex | Name index of a PDB holding this module’s symbols (non-zero only for * Linker *). |
| 64 | – | ModuleName | Null-terminated: the object file’s path, or Import:dll. |
| – | – | ObjFileName | Null-terminated: the same path, or the library the object came from. |
Section contributions
A 4-byte version, then an array of entries, one per piece of a section that a module contributed:
| Version | Entry |
|---|---|
0xEFFE0000 + 19970605 (0xF12EBA2D), Ver60 | 28-byte entry (below). The one seen in practice. |
0xEFFE0000 + 20140516 (0xF13151E4), V2 | The 28-byte entry plus a 4-byte ISectCoff (a COFF section number; purpose not well understood). |
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 2 | Section | Section number in the program, from 1. |
| 2 | 2 | Padding1 | – |
| 4 | 4 | Offset | Offset in that section. |
| 8 | 4 | Size | Bytes contributed. |
| 12 | 4 | Characteristics | Section flags, as in the PE section header (IMAGE_SCN_*). |
| 16 | 2 | ModuleIndex | The contributing module. |
| 18 | 2 | Padding2 | – |
| 20 | 4 | DataCrc | CRC of the contributed data. |
| 24 | 4 | RelocCrc | CRC of its relocations. |
Section map
A 2-byte Count and a 2-byte LogCount (segment descriptors and logical segment descriptors), then Count 20-byte entries:
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 2 | Flags | Bit 0 read, 1 write, 2 execute, 3 32-bit addresses, 8 is a selector, 9 absolute address, 10 is a group. |
| 2 | 2 | Ovl | Logical overlay number. |
| 4 | 2 | Group | Group index into this table. |
| 6 | 2 | Frame | Frame value. |
| 8 | 2 | SectionName | Byte index of the name in the string table, or 0xFFFF. |
| 10 | 2 | ClassName | Byte index of the class name, or 0xFFFF. |
| 12 | 4 | Offset | Offset of the logical segment in the physical one. |
| 16 | 4 | SectionLength | Length in bytes. |
File info
| Field | Size | Meaning |
|---|---|---|
| NumModules | 2 | Number of modules. |
| NumSourceFiles | 2 | Ignored: the real count can exceed 65,535. |
| ModIndices | 2 × NumModules | Not used. |
| ModFileCounts | 2 × NumModules | Source files per module; their sum is the true count. |
| FileNameOffsets | 4 × sum | For each module’s files in turn, an offset into the names buffer. |
| NamesBuffer | rest | Null-terminated file names. |
The type server map (for /Zi type servers) and the Edit and Continue
substream are not publicly documented.
Optional debug header
An array of 2-byte stream numbers, −1 where absent. Each position has a fixed meaning:
| Position | Stream holds |
|---|---|
| 0 | FPO data (from .debug$F sections). |
| 1 | Exception data (IMAGE_DEBUG_TYPE_EXCEPTION). |
| 2 | Fixup data (IMAGE_DEBUG_TYPE_FIXUP). |
| 3 | OMAP to source (IMAGE_DEBUG_TYPE_OMAP_TO_SRC). |
| 4 | OMAP from source (IMAGE_DEBUG_TYPE_OMAP_FROM_SRC). |
| 5 | The program’s section headers. |
| 6 | Token to RID map (.NET); layout not documented. |
| 7 | A copy of .xdata. |
| 8 | A copy of .pdata. |
| 9 | New FPO data (IMAGE_DEBUG_TYPE_FPO). |
| 10 | The original section headers, before binary translation. |
Module streams
| Field | Size | Meaning |
|---|---|---|
| Signature | 4 | 4 (CV_SIGNATURE_C13). Other values: 0 C6, 1 C7, 2 C11. |
| Symbols | SymByteSize − 4 | The module’s symbol records. |
| C11 lines | C11ByteSize | Old-style line information; usually absent. |
| C13 lines | C13ByteSize | Debug subsections: lines, file checksums, inlinee lines and others. |
| GlobalRefsSize | 4 | Bytes that follow. |
| GlobalRefs | GlobalRefsSize | Offsets of global symbols the module refers to; not otherwise documented. |
Public and global symbol streams
Both index the records in the symbol record stream by name. The global stream is just the hash table below; the public stream has a 28-byte header first and more tables after it.
Public stream header
| Offset | Size | Name | Meaning |
|---|---|---|---|
| 0 | 4 | SymHash | Bytes of the hash table that follows. |
| 4 | 4 | AddrMap | Bytes of the address map. |
| 8 | 4 | NumThunks | Entries in the thunk map. |
| 12 | 4 | SizeOfThunk | Size of each incremental-linking thunk. |
| 16 | 2 | ISectThunkTable | Section holding the thunk table. |
| 18 | 2 | Padding | – |
| 20 | 4 | OffThunkTable | Offset of the thunk table in that section. |
| 24 | 4 | NumSections | Entries in the section map. |
After the hash table come the address map (AddrMap / 4 four-byte offsets of the public records in the symbol record stream, sorted by address), the thunk map (NumThunks entries) and the section map (NumSections entries, each a 4-byte offset, a 2-byte section number and 2 bytes of padding). LLVM’s reader calls these “something called” address, thunk and section map: their finer meaning is not publicly documented.
Hash table
| Field | Size | Meaning |
|---|---|---|
| VerSignature | 4 | 0xFFFFFFFF. |
| VerHdr | 4 | 0xEFFE0000 + 19990810 (0xF12F091A). |
| HrSize | 4 | Bytes of hash records. |
| NumBuckets | 4 | Bytes of the bucket part. |
| Hash records | HrSize | 8 bytes each: Off, the record’s offset in the symbol record stream plus 1, and CRef, a reference count. |
| Buckets | NumBuckets | A bitmap of 4,097 bits (IPHR_HASH + 1, rounded up to whole 32-bit words: 129 words), then one 4-byte value for each set bit: where that bucket’s records start, as the index of its first hash record × 12. |
A name’s bucket is LLVM’s hashStringV1 of the name modulo 4096
(IPHR_HASH). Records in a bucket run until the next non-empty bucket’s
start.
Sources
LLVM, The PDB File Format: the
stream table and the pages on the PDB info stream, the serialised hash table, the TPI
and IPI streams, the DBI stream and the module information stream. LLVM source:
RawTypes.h (public stream and hash headers, string table header),
PDBStringTable.cpp (/names layout),
GlobalsStream.cpp and PublicsStream.cpp (hash buckets and
the tables after them). Microsoft’s cvinfo.h in
microsoft-pdb
(CV_SIGNATURE_*). The hexadecimal forms of the 0xEFFE0000
versions are computed from the decimal values.