|
//! Program Data Base debugging information format. //! //! This namespace contains unopinionated types and data definitions only. For //! an implementation of parsing and caching PDB information, see //! `std.debug.Pdb`. //! //! Most of this is based on information gathered from LLVM source code, //! documentation and/or contributors. |
DbiStreamHeaderhttps://llvm.org/docs/PDB/DbiStream.html#stream-header |
const std = @import("std.zig");
const math = std.math;
const mem = std.mem;
const coff = std.coff;
const fs = std.fs;
const File = std.fs.File;
const debug = std.debug;
|
SectionContribEntryCOFF Section index, 1-based |
const ArrayList = std.ArrayList; |
ModInfoNumber of segment descriptors |
/// https://llvm.org/docs/PDB/DbiStream.html#stream-header
pub const DbiStreamHeader = extern struct {
version_signature: i32,
version_header: u32,
age: u32,
global_stream_index: u16,
build_number: u16,
public_stream_index: u16,
pdb_dll_version: u16,
sym_record_stream: u16,
pdb_dll_rbld: u16,
mod_info_size: u32,
section_contribution_size: u32,
section_map_size: u32,
source_info_size: i32,
type_server_size: i32,
mfc_type_server_index: u32,
optional_dbg_header_size: i32,
ec_substream_size: i32,
flags: u16,
machine: u16,
padding: u32,
};
|
SectionMapHeaderNumber of logical segment descriptors |
pub const SectionContribEntry = extern struct {
/// COFF Section index, 1-based
section: u16,
padding1: [2]u8,
offset: u32,
size: u32,
characteristics: u32,
module_index: u16,
padding2: [2]u8,
data_crc: u32,
reloc_crc: u32,
};
|
SectionMapEntrySee the SectionMapEntryFlags enum below. |
pub const ModInfo = extern struct {
unused1: u32,
section_contr: SectionContribEntry,
flags: u16,
module_sym_stream: u16,
sym_byte_size: u32,
c11_byte_size: u32,
c13_byte_size: u32,
source_file_count: u16,
padding: [2]u8,
unused2: u32,
source_file_name_index: u32,
pdb_file_path_name_index: u32,
// These fields are variable length
//module_name: char[],
//obj_file_name: char[],
};
|
StreamTypeLogical overlay number |
pub const SectionMapHeader = extern struct {
/// Number of segment descriptors
count: u16,
|
SymbolKindGroup index into descriptor array. |
/// Number of logical segment descriptors
log_count: u16,
};
|
TypeIndexByte index of segment / group name in string table, or 0xFFFF. |
pub const SectionMapEntry = extern struct {
/// See the SectionMapEntryFlags enum below.
flags: u16,
|
ProcSymByte index of class in string table, or 0xFFFF. |
/// Logical overlay number
ovl: u16,
|
ProcSymFlagsByte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group. |
/// Group index into descriptor array.
group: u16,
frame: u16,
|
SectionContrSubstreamVersionByte count of the segment or group. |
/// Byte index of segment / group name in string table, or 0xFFFF.
section_name: u16,
|
RecordPrefixDuplicate copy of SymbolRecordKind, but using the official CV names. Useful for reference purposes and when dealing with unknown record types. |
/// Byte index of class in string table, or 0xFFFF.
class_name: u16,
|
LineFragmentHeaderRecord length, starting from &record_kind. |
/// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group.
offset: u32,
|
LineFlagsRecord kind enum (SymRecordKind or TypeRecordKind) |
/// Byte count of the segment or group.
section_length: u32,
};
|
LineBlockFragmentHeaderThe following variable length array appears immediately after the header.
The structure definition follows.
LineBlockFragmentHeader Blocks[]
Each |
pub const StreamType = enum(u16) {
pdb = 1,
tpi = 2,
dbi = 3,
ipi = 4,
};
|
LineNumberEntryCode offset of line contribution. |
/// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful
/// for reference purposes and when dealing with unknown record types.
pub const SymbolKind = enum(u16) {
compile = 1,
register_16t = 2,
constant_16t = 3,
udt_16t = 4,
ssearch = 5,
skip = 7,
cvreserve = 8,
objname_st = 9,
endarg = 10,
coboludt_16t = 11,
manyreg_16t = 12,
@"return" = 13,
entrythis = 14,
bprel16 = 256,
ldata16 = 257,
gdata16 = 258,
pub16 = 259,
lproc16 = 260,
gproc16 = 261,
thunk16 = 262,
block16 = 263,
with16 = 264,
label16 = 265,
cexmodel16 = 266,
vftable16 = 267,
regrel16 = 268,
bprel32_16t = 512,
ldata32_16t = 513,
gdata32_16t = 514,
pub32_16t = 515,
lproc32_16t = 516,
gproc32_16t = 517,
thunk32_st = 518,
block32_st = 519,
with32_st = 520,
label32_st = 521,
cexmodel32 = 522,
vftable32_16t = 523,
regrel32_16t = 524,
lthread32_16t = 525,
gthread32_16t = 526,
slink32 = 527,
lprocmips_16t = 768,
gprocmips_16t = 769,
procref_st = 1024,
dataref_st = 1025,
@"align" = 1026,
lprocref_st = 1027,
oem = 1028,
ti16_max = 4096,
register_st = 4097,
constant_st = 4098,
udt_st = 4099,
coboludt_st = 4100,
manyreg_st = 4101,
bprel32_st = 4102,
ldata32_st = 4103,
gdata32_st = 4104,
pub32_st = 4105,
lproc32_st = 4106,
gproc32_st = 4107,
vftable32 = 4108,
regrel32_st = 4109,
lthread32_st = 4110,
gthread32_st = 4111,
lprocmips_st = 4112,
gprocmips_st = 4113,
compile2_st = 4115,
manyreg2_st = 4116,
lprocia64_st = 4117,
gprocia64_st = 4118,
localslot_st = 4119,
paramslot_st = 4120,
annotation = 4121,
gmanproc_st = 4122,
lmanproc_st = 4123,
reserved1 = 4124,
reserved2 = 4125,
reserved3 = 4126,
reserved4 = 4127,
lmandata_st = 4128,
gmandata_st = 4129,
manframerel_st = 4130,
manregister_st = 4131,
manslot_st = 4132,
manmanyreg_st = 4133,
manregrel_st = 4134,
manmanyreg2_st = 4135,
mantypref = 4136,
unamespace_st = 4137,
st_max = 4352,
with32 = 4356,
manyreg = 4362,
lprocmips = 4372,
gprocmips = 4373,
manyreg2 = 4375,
lprocia64 = 4376,
gprocia64 = 4377,
localslot = 4378,
paramslot = 4379,
manframerel = 4382,
manregister = 4383,
manslot = 4384,
manmanyreg = 4385,
manregrel = 4386,
manmanyreg2 = 4387,
unamespace = 4388,
dataref = 4390,
annotationref = 4392,
tokenref = 4393,
gmanproc = 4394,
lmanproc = 4395,
attr_framerel = 4398,
attr_register = 4399,
attr_regrel = 4400,
attr_manyreg = 4401,
sepcode = 4402,
local_2005 = 4403,
defrange_2005 = 4404,
defrange2_2005 = 4405,
discarded = 4411,
lprocmips_id = 4424,
gprocmips_id = 4425,
lprocia64_id = 4426,
gprocia64_id = 4427,
defrange_hlsl = 4432,
gdata_hlsl = 4433,
ldata_hlsl = 4434,
local_dpc_groupshared = 4436,
defrange_dpc_ptr_tag = 4439,
dpc_sym_tag_map = 4440,
armswitchtable = 4441,
pogodata = 4444,
inlinesite2 = 4445,
mod_typeref = 4447,
ref_minipdb = 4448,
pdbmap = 4449,
gdata_hlsl32 = 4450,
ldata_hlsl32 = 4451,
gdata_hlsl32_ex = 4452,
ldata_hlsl32_ex = 4453,
fastlink = 4455,
inlinees = 4456,
end = 6,
inlinesite_end = 4430,
proc_id_end = 4431,
thunk32 = 4354,
trampoline = 4396,
section = 4406,
coffgroup = 4407,
@"export" = 4408,
lproc32 = 4367,
gproc32 = 4368,
lproc32_id = 4422,
gproc32_id = 4423,
lproc32_dpc = 4437,
lproc32_dpc_id = 4438,
register = 4358,
pub32 = 4366,
procref = 4389,
lprocref = 4391,
envblock = 4413,
inlinesite = 4429,
local = 4414,
defrange = 4415,
defrange_subfield = 4416,
defrange_register = 4417,
defrange_framepointer_rel = 4418,
defrange_subfield_register = 4419,
defrange_framepointer_rel_full_scope = 4420,
defrange_register_rel = 4421,
block32 = 4355,
label32 = 4357,
objname = 4353,
compile2 = 4374,
compile3 = 4412,
frameproc = 4114,
callsiteinfo = 4409,
filestatic = 4435,
heapallocsite = 4446,
framecookie = 4410,
callees = 4442,
callers = 4443,
udt = 4360,
coboludt = 4361,
buildinfo = 4428,
bprel32 = 4363,
regrel32 = 4369,
constant = 4359,
manconstant = 4397,
ldata32 = 4364,
gdata32 = 4365,
lmandata = 4380,
gmandata = 4381,
lthread32 = 4370,
gthread32 = 4371,
};
|
FlagsCode segment of line contribution. |
pub const TypeIndex = u32; |
ColumnNumberEntryCode size of this line contribution. |
// TODO According to this header:
// https://github.com/microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L3722
// we should define RecordPrefix as part of the ProcSym structure.
// This might be important when we start generating PDB in self-hosted with our own PE linker.
pub const ProcSym = extern struct {
parent: u32,
end: u32,
next: u32,
code_size: u32,
dbg_start: u32,
dbg_end: u32,
function_type: TypeIndex,
code_offset: u32,
segment: u16,
flags: ProcSymFlags,
name: [1]u8, // null-terminated
};
|
FileChecksumEntryHeaderCV_LINES_HAVE_COLUMNS |
pub const ProcSymFlags = packed struct {
has_fp: bool,
has_iret: bool,
has_fret: bool,
is_no_return: bool,
is_unreachable: bool,
has_custom_calling_conv: bool,
is_no_inline: bool,
has_optimized_debug_info: bool,
};
|
DebugSubsectionKindThe following two variable length arrays appear immediately after the header. The structure definitions follow. LineNumberEntry Lines[NumLines]; ColumnNumberEntry Columns[NumLines]; |
pub const SectionContrSubstreamVersion = enum(u32) {
Ver60 = 0xeffe0000 + 19970605,
V2 = 0xeffe0000 + 20140516,
_,
};
|
DebugSubsectionHeaderOffset of FileChecksum entry in File checksums buffer. The checksum entry then contains another offset into the string table of the actual name. |
pub const RecordPrefix = extern struct {
/// Record length, starting from &record_kind.
record_len: u16,
|
StringTableHeadercode size of block, in bytes |
/// Record kind enum (SymRecordKind or TypeRecordKind)
record_kind: SymbolKind,
};
|
SuperBlockOffset to start of code bytes for line number |
/// The following variable length array appears immediately after the header.
/// The structure definition follows.
/// LineBlockFragmentHeader Blocks[]
/// Each `LineBlockFragmentHeader` as specified below.
pub const LineFragmentHeader = extern struct {
/// Code offset of line contribution.
reloc_offset: u32,
|
expect_magicStart line number |
/// Code segment of line contribution.
reloc_segment: u16,
flags: LineFlags,
/// Code size of this line contribution.
code_size: u32,
};
pub const LineFlags = packed struct {
/// CV_LINES_HAVE_COLUMNS
have_columns: bool,
unused: u15,
};
/// The following two variable length arrays appear immediately after the
/// header. The structure definitions follow.
/// LineNumberEntry Lines[NumLines];
/// ColumnNumberEntry Columns[NumLines];
pub const LineBlockFragmentHeader = extern struct {
/// Offset of FileChecksum entry in File
/// checksums buffer. The checksum entry then
/// contains another offset into the string
/// table of the actual name.
name_index: u32,
num_lines: u32,
/// code size of block, in bytes
block_size: u32,
};
pub const LineNumberEntry = extern struct {
/// Offset to start of code bytes for line number
offset: u32,
flags: Flags,
pub const Flags = packed struct(u32) {
/// Start line number
start: u24,
/// Delta of lines to the end of the expression. Still unclear.
// TODO figure out the point of this field.
end: u7,
is_statement: bool,
};
};
pub const ColumnNumberEntry = extern struct {
start_column: u16,
end_column: u16,
};
/// Checksum bytes follow.
pub const FileChecksumEntryHeader = extern struct {
/// Byte offset of filename in global string table.
file_name_offset: u32,
/// Number of bytes of checksum.
checksum_size: u8,
/// FileChecksumKind
checksum_kind: u8,
};
pub const DebugSubsectionKind = enum(u32) {
none = 0,
symbols = 0xf1,
lines = 0xf2,
string_table = 0xf3,
file_checksums = 0xf4,
frame_data = 0xf5,
inlinee_lines = 0xf6,
cross_scope_imports = 0xf7,
cross_scope_exports = 0xf8,
// These appear to relate to .Net assembly info.
il_lines = 0xf9,
func_md_token_map = 0xfa,
type_md_token_map = 0xfb,
merged_assembly_input = 0xfc,
coff_symbol_rva = 0xfd,
};
pub const DebugSubsectionHeader = extern struct {
/// codeview::DebugSubsectionKind enum
kind: DebugSubsectionKind,
/// number of bytes occupied by this record.
length: u32,
};
pub const StringTableHeader = extern struct {
/// PDBStringTableSignature
signature: u32,
/// 1 or 2
hash_version: u32,
/// Number of bytes of names buffer.
byte_size: u32,
};
// https://llvm.org/docs/PDB/MsfFile.html#the-superblock
pub const SuperBlock = extern struct {
/// The LLVM docs list a space between C / C++ but empirically this is not the case.
pub const expect_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
file_magic: [expect_magic.len]u8,
/// The block size of the internal file system. Valid values are 512, 1024,
/// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending
/// on the block sizes. For the purposes of LLVM, we handle only block sizes of
/// 4KiB, and all further discussion assumes a block size of 4KiB.
block_size: u32,
/// The index of a block within the file, at which begins a bitfield representing
/// the set of all blocks within the file which are “free” (i.e. the data within
/// that block is not used). See The Free Block Map for more information. Important:
/// FreeBlockMapBlock can only be 1 or 2!
free_block_map_block: u32,
/// The total number of blocks in the file. NumBlocks * BlockSize should equal the
/// size of the file on disk.
num_blocks: u32,
/// The size of the stream directory, in bytes. The stream directory contains
/// information about each stream’s size and the set of blocks that it occupies.
/// It will be described in more detail later.
num_directory_bytes: u32,
unknown: u32,
/// The index of a block within the MSF file. At this block is an array of
/// ulittle32_t’s listing the blocks that the stream directory resides on.
/// For large MSF files, the stream directory (which describes the block
/// layout of each stream) may not fit entirely on a single block. As a
/// result, this extra layer of indirection is introduced, whereby this
/// block contains the list of blocks that the stream directory occupies,
/// and the stream directory itself can be stitched together accordingly.
/// The number of ulittle32_t’s in this array is given by
/// ceil(NumDirectoryBytes / BlockSize).
// Note: microsoft-pdb code actually suggests this is a variable-length
// array. If the indices of blocks occupied by the Stream Directory didn't
// fit in one page, there would be other u32 following it.
// This would mean the Stream Directory is bigger than BlockSize / sizeof(u32)
// blocks. We're not even close to this with a 1GB pdb file, and LLVM didn't
// implement it so we're kind of safe making this assumption for now.
block_map_addr: u32,
};
|
| Generated by zstd-live on 2025-10-12 02:30:37 UTC. |