master
1//! Program Data Base debugging information format.
2//!
3//! This namespace contains unopinionated types and data definitions only. For
4//! an implementation of parsing and caching PDB information, see
5//! `std.debug.Pdb`.
6//!
7//! Most of this is based on information gathered from LLVM source code,
8//! documentation and/or contributors.
9
10const std = @import("std.zig");
11const math = std.math;
12const mem = std.mem;
13const coff = std.coff;
14const fs = std.fs;
15const File = std.fs.File;
16const debug = std.debug;
17
18const ArrayList = std.ArrayList;
19
20/// https://llvm.org/docs/PDB/DbiStream.html#stream-header
21pub const DbiStreamHeader = extern struct {
22 version_signature: i32,
23 version_header: u32,
24 age: u32,
25 global_stream_index: u16,
26 build_number: u16,
27 public_stream_index: u16,
28 pdb_dll_version: u16,
29 sym_record_stream: u16,
30 pdb_dll_rbld: u16,
31 mod_info_size: u32,
32 section_contribution_size: u32,
33 section_map_size: u32,
34 source_info_size: i32,
35 type_server_size: i32,
36 mfc_type_server_index: u32,
37 optional_dbg_header_size: i32,
38 ec_substream_size: i32,
39 flags: u16,
40 machine: u16,
41 padding: u32,
42};
43
44pub const SectionContribEntry = extern struct {
45 /// COFF Section index, 1-based
46 section: u16,
47 padding1: [2]u8,
48 offset: u32,
49 size: u32,
50 characteristics: u32,
51 module_index: u16,
52 padding2: [2]u8,
53 data_crc: u32,
54 reloc_crc: u32,
55};
56
57pub const ModInfo = extern struct {
58 unused1: u32,
59 section_contr: SectionContribEntry,
60 flags: u16,
61 module_sym_stream: u16,
62 sym_byte_size: u32,
63 c11_byte_size: u32,
64 c13_byte_size: u32,
65 source_file_count: u16,
66 padding: [2]u8,
67 unused2: u32,
68 source_file_name_index: u32,
69 pdb_file_path_name_index: u32,
70 // These fields are variable length
71 //module_name: char[],
72 //obj_file_name: char[],
73};
74
75pub const SectionMapHeader = extern struct {
76 /// Number of segment descriptors
77 count: u16,
78
79 /// Number of logical segment descriptors
80 log_count: u16,
81};
82
83pub const SectionMapEntry = extern struct {
84 /// See the SectionMapEntryFlags enum below.
85 flags: u16,
86
87 /// Logical overlay number
88 ovl: u16,
89
90 /// Group index into descriptor array.
91 group: u16,
92 frame: u16,
93
94 /// Byte index of segment / group name in string table, or 0xFFFF.
95 section_name: u16,
96
97 /// Byte index of class in string table, or 0xFFFF.
98 class_name: u16,
99
100 /// Byte offset of the logical segment within physical segment. If group is set in flags, this is the offset of the group.
101 offset: u32,
102
103 /// Byte count of the segment or group.
104 section_length: u32,
105};
106
107pub const StreamType = enum(u16) {
108 pdb = 1,
109 tpi = 2,
110 dbi = 3,
111 ipi = 4,
112};
113
114/// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful
115/// for reference purposes and when dealing with unknown record types.
116pub const SymbolKind = enum(u16) {
117 compile = 1,
118 register_16t = 2,
119 constant_16t = 3,
120 udt_16t = 4,
121 ssearch = 5,
122 skip = 7,
123 cvreserve = 8,
124 objname_st = 9,
125 endarg = 10,
126 coboludt_16t = 11,
127 manyreg_16t = 12,
128 @"return" = 13,
129 entrythis = 14,
130 bprel16 = 256,
131 ldata16 = 257,
132 gdata16 = 258,
133 pub16 = 259,
134 lproc16 = 260,
135 gproc16 = 261,
136 thunk16 = 262,
137 block16 = 263,
138 with16 = 264,
139 label16 = 265,
140 cexmodel16 = 266,
141 vftable16 = 267,
142 regrel16 = 268,
143 bprel32_16t = 512,
144 ldata32_16t = 513,
145 gdata32_16t = 514,
146 pub32_16t = 515,
147 lproc32_16t = 516,
148 gproc32_16t = 517,
149 thunk32_st = 518,
150 block32_st = 519,
151 with32_st = 520,
152 label32_st = 521,
153 cexmodel32 = 522,
154 vftable32_16t = 523,
155 regrel32_16t = 524,
156 lthread32_16t = 525,
157 gthread32_16t = 526,
158 slink32 = 527,
159 lprocmips_16t = 768,
160 gprocmips_16t = 769,
161 procref_st = 1024,
162 dataref_st = 1025,
163 @"align" = 1026,
164 lprocref_st = 1027,
165 oem = 1028,
166 ti16_max = 4096,
167 register_st = 4097,
168 constant_st = 4098,
169 udt_st = 4099,
170 coboludt_st = 4100,
171 manyreg_st = 4101,
172 bprel32_st = 4102,
173 ldata32_st = 4103,
174 gdata32_st = 4104,
175 pub32_st = 4105,
176 lproc32_st = 4106,
177 gproc32_st = 4107,
178 vftable32 = 4108,
179 regrel32_st = 4109,
180 lthread32_st = 4110,
181 gthread32_st = 4111,
182 lprocmips_st = 4112,
183 gprocmips_st = 4113,
184 compile2_st = 4115,
185 manyreg2_st = 4116,
186 lprocia64_st = 4117,
187 gprocia64_st = 4118,
188 localslot_st = 4119,
189 paramslot_st = 4120,
190 annotation = 4121,
191 gmanproc_st = 4122,
192 lmanproc_st = 4123,
193 reserved1 = 4124,
194 reserved2 = 4125,
195 reserved3 = 4126,
196 reserved4 = 4127,
197 lmandata_st = 4128,
198 gmandata_st = 4129,
199 manframerel_st = 4130,
200 manregister_st = 4131,
201 manslot_st = 4132,
202 manmanyreg_st = 4133,
203 manregrel_st = 4134,
204 manmanyreg2_st = 4135,
205 mantypref = 4136,
206 unamespace_st = 4137,
207 st_max = 4352,
208 with32 = 4356,
209 manyreg = 4362,
210 lprocmips = 4372,
211 gprocmips = 4373,
212 manyreg2 = 4375,
213 lprocia64 = 4376,
214 gprocia64 = 4377,
215 localslot = 4378,
216 paramslot = 4379,
217 manframerel = 4382,
218 manregister = 4383,
219 manslot = 4384,
220 manmanyreg = 4385,
221 manregrel = 4386,
222 manmanyreg2 = 4387,
223 unamespace = 4388,
224 dataref = 4390,
225 annotationref = 4392,
226 tokenref = 4393,
227 gmanproc = 4394,
228 lmanproc = 4395,
229 attr_framerel = 4398,
230 attr_register = 4399,
231 attr_regrel = 4400,
232 attr_manyreg = 4401,
233 sepcode = 4402,
234 local_2005 = 4403,
235 defrange_2005 = 4404,
236 defrange2_2005 = 4405,
237 discarded = 4411,
238 lprocmips_id = 4424,
239 gprocmips_id = 4425,
240 lprocia64_id = 4426,
241 gprocia64_id = 4427,
242 defrange_hlsl = 4432,
243 gdata_hlsl = 4433,
244 ldata_hlsl = 4434,
245 local_dpc_groupshared = 4436,
246 defrange_dpc_ptr_tag = 4439,
247 dpc_sym_tag_map = 4440,
248 armswitchtable = 4441,
249 pogodata = 4444,
250 inlinesite2 = 4445,
251 mod_typeref = 4447,
252 ref_minipdb = 4448,
253 pdbmap = 4449,
254 gdata_hlsl32 = 4450,
255 ldata_hlsl32 = 4451,
256 gdata_hlsl32_ex = 4452,
257 ldata_hlsl32_ex = 4453,
258 fastlink = 4455,
259 inlinees = 4456,
260 end = 6,
261 inlinesite_end = 4430,
262 proc_id_end = 4431,
263 thunk32 = 4354,
264 trampoline = 4396,
265 section = 4406,
266 coffgroup = 4407,
267 @"export" = 4408,
268 lproc32 = 4367,
269 gproc32 = 4368,
270 lproc32_id = 4422,
271 gproc32_id = 4423,
272 lproc32_dpc = 4437,
273 lproc32_dpc_id = 4438,
274 register = 4358,
275 pub32 = 4366,
276 procref = 4389,
277 lprocref = 4391,
278 envblock = 4413,
279 inlinesite = 4429,
280 local = 4414,
281 defrange = 4415,
282 defrange_subfield = 4416,
283 defrange_register = 4417,
284 defrange_framepointer_rel = 4418,
285 defrange_subfield_register = 4419,
286 defrange_framepointer_rel_full_scope = 4420,
287 defrange_register_rel = 4421,
288 block32 = 4355,
289 label32 = 4357,
290 objname = 4353,
291 compile2 = 4374,
292 compile3 = 4412,
293 frameproc = 4114,
294 callsiteinfo = 4409,
295 filestatic = 4435,
296 heapallocsite = 4446,
297 framecookie = 4410,
298 callees = 4442,
299 callers = 4443,
300 udt = 4360,
301 coboludt = 4361,
302 buildinfo = 4428,
303 bprel32 = 4363,
304 regrel32 = 4369,
305 constant = 4359,
306 manconstant = 4397,
307 ldata32 = 4364,
308 gdata32 = 4365,
309 lmandata = 4380,
310 gmandata = 4381,
311 lthread32 = 4370,
312 gthread32 = 4371,
313};
314
315pub const TypeIndex = u32;
316
317// TODO According to this header:
318// https://github.com/microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/include/cvinfo.h#L3722
319// we should define RecordPrefix as part of the ProcSym structure.
320// This might be important when we start generating PDB in self-hosted with our own PE linker.
321pub const ProcSym = extern struct {
322 parent: u32,
323 end: u32,
324 next: u32,
325 code_size: u32,
326 dbg_start: u32,
327 dbg_end: u32,
328 function_type: TypeIndex,
329 code_offset: u32,
330 segment: u16,
331 flags: ProcSymFlags,
332 name: [1]u8, // null-terminated
333};
334
335pub const ProcSymFlags = packed struct {
336 has_fp: bool,
337 has_iret: bool,
338 has_fret: bool,
339 is_no_return: bool,
340 is_unreachable: bool,
341 has_custom_calling_conv: bool,
342 is_no_inline: bool,
343 has_optimized_debug_info: bool,
344};
345
346pub const SectionContrSubstreamVersion = enum(u32) {
347 Ver60 = 0xeffe0000 + 19970605,
348 V2 = 0xeffe0000 + 20140516,
349 _,
350};
351
352pub const RecordPrefix = extern struct {
353 /// Record length, starting from &record_kind.
354 record_len: u16,
355
356 /// Record kind enum (SymRecordKind or TypeRecordKind)
357 record_kind: SymbolKind,
358};
359
360/// The following variable length array appears immediately after the header.
361/// The structure definition follows.
362/// LineBlockFragmentHeader Blocks[]
363/// Each `LineBlockFragmentHeader` as specified below.
364pub const LineFragmentHeader = extern struct {
365 /// Code offset of line contribution.
366 reloc_offset: u32,
367
368 /// Code segment of line contribution.
369 reloc_segment: u16,
370 flags: LineFlags,
371
372 /// Code size of this line contribution.
373 code_size: u32,
374};
375
376pub const LineFlags = packed struct {
377 /// CV_LINES_HAVE_COLUMNS
378 have_columns: bool,
379 unused: u15,
380};
381
382/// The following two variable length arrays appear immediately after the
383/// header. The structure definitions follow.
384/// LineNumberEntry Lines[NumLines];
385/// ColumnNumberEntry Columns[NumLines];
386pub const LineBlockFragmentHeader = extern struct {
387 /// Offset of FileChecksum entry in File
388 /// checksums buffer. The checksum entry then
389 /// contains another offset into the string
390 /// table of the actual name.
391 name_index: u32,
392 num_lines: u32,
393
394 /// code size of block, in bytes
395 block_size: u32,
396};
397
398pub const LineNumberEntry = extern struct {
399 /// Offset to start of code bytes for line number
400 offset: u32,
401 flags: Flags,
402
403 pub const Flags = packed struct(u32) {
404 /// Start line number
405 start: u24,
406 /// Delta of lines to the end of the expression. Still unclear.
407 // TODO figure out the point of this field.
408 end: u7,
409 is_statement: bool,
410 };
411};
412
413pub const ColumnNumberEntry = extern struct {
414 start_column: u16,
415 end_column: u16,
416};
417
418/// Checksum bytes follow.
419pub const FileChecksumEntryHeader = extern struct {
420 /// Byte offset of filename in global string table.
421 file_name_offset: u32,
422 /// Number of bytes of checksum.
423 checksum_size: u8,
424 /// FileChecksumKind
425 checksum_kind: u8,
426};
427
428pub const DebugSubsectionKind = enum(u32) {
429 none = 0,
430 symbols = 0xf1,
431 lines = 0xf2,
432 string_table = 0xf3,
433 file_checksums = 0xf4,
434 frame_data = 0xf5,
435 inlinee_lines = 0xf6,
436 cross_scope_imports = 0xf7,
437 cross_scope_exports = 0xf8,
438
439 // These appear to relate to .Net assembly info.
440 il_lines = 0xf9,
441 func_md_token_map = 0xfa,
442 type_md_token_map = 0xfb,
443 merged_assembly_input = 0xfc,
444
445 coff_symbol_rva = 0xfd,
446};
447
448pub const DebugSubsectionHeader = extern struct {
449 /// codeview::DebugSubsectionKind enum
450 kind: DebugSubsectionKind,
451
452 /// number of bytes occupied by this record.
453 length: u32,
454};
455
456pub const StringTableHeader = extern struct {
457 /// PDBStringTableSignature
458 signature: u32,
459 /// 1 or 2
460 hash_version: u32,
461 /// Number of bytes of names buffer.
462 byte_size: u32,
463};
464
465// https://llvm.org/docs/PDB/MsfFile.html#the-superblock
466pub const SuperBlock = extern struct {
467 /// The LLVM docs list a space between C / C++ but empirically this is not the case.
468 pub const expect_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
469
470 file_magic: [expect_magic.len]u8,
471
472 /// The block size of the internal file system. Valid values are 512, 1024,
473 /// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending
474 /// on the block sizes. For the purposes of LLVM, we handle only block sizes of
475 /// 4KiB, and all further discussion assumes a block size of 4KiB.
476 block_size: u32,
477
478 /// The index of a block within the file, at which begins a bitfield representing
479 /// the set of all blocks within the file which are “free” (i.e. the data within
480 /// that block is not used). See The Free Block Map for more information. Important:
481 /// FreeBlockMapBlock can only be 1 or 2!
482 free_block_map_block: u32,
483
484 /// The total number of blocks in the file. NumBlocks * BlockSize should equal the
485 /// size of the file on disk.
486 num_blocks: u32,
487
488 /// The size of the stream directory, in bytes. The stream directory contains
489 /// information about each stream’s size and the set of blocks that it occupies.
490 /// It will be described in more detail later.
491 num_directory_bytes: u32,
492
493 unknown: u32,
494 /// The index of a block within the MSF file. At this block is an array of
495 /// ulittle32_t’s listing the blocks that the stream directory resides on.
496 /// For large MSF files, the stream directory (which describes the block
497 /// layout of each stream) may not fit entirely on a single block. As a
498 /// result, this extra layer of indirection is introduced, whereby this
499 /// block contains the list of blocks that the stream directory occupies,
500 /// and the stream directory itself can be stitched together accordingly.
501 /// The number of ulittle32_t’s in this array is given by
502 /// ceil(NumDirectoryBytes / BlockSize).
503 // Note: microsoft-pdb code actually suggests this is a variable-length
504 // array. If the indices of blocks occupied by the Stream Directory didn't
505 // fit in one page, there would be other u32 following it.
506 // This would mean the Stream Directory is bigger than BlockSize / sizeof(u32)
507 // blocks. We're not even close to this with a 1GB pdb file, and LLVM didn't
508 // implement it so we're kind of safe making this assumption for now.
509 block_map_addr: u32,
510};