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.Io.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
317pub const ProcSym = extern struct {
318 record_len: u16,
319 record_kind: SymbolKind,
320 parent: u32,
321 end: u32,
322 next: u32,
323 code_size: u32,
324 dbg_start: u32,
325 dbg_end: u32,
326 function_type: TypeIndex,
327 code_offset: u32,
328 segment: u16,
329 flags: ProcSymFlags,
330 name: [1]u8, // null-terminated
331};
332
333pub const ProcSymFlags = packed struct(u8) {
334 has_fp: bool,
335 has_iret: bool,
336 has_fret: bool,
337 is_no_return: bool,
338 is_unreachable: bool,
339 has_custom_calling_conv: bool,
340 is_no_inline: bool,
341 has_optimized_debug_info: bool,
342};
343
344pub const SectionContrSubstreamVersion = enum(u32) {
345 Ver60 = 0xeffe0000 + 19970605,
346 V2 = 0xeffe0000 + 20140516,
347 _,
348};
349
350pub const RecordPrefix = extern struct {
351 /// Record length, starting from &record_kind.
352 record_len: u16,
353
354 /// Record kind enum (SymRecordKind or TypeRecordKind)
355 record_kind: SymbolKind,
356};
357
358/// The following variable length array appears immediately after the header.
359/// The structure definition follows.
360/// LineBlockFragmentHeader Blocks[]
361/// Each `LineBlockFragmentHeader` as specified below.
362pub const LineFragmentHeader = extern struct {
363 /// Code offset of line contribution.
364 reloc_offset: u32,
365
366 /// Code segment of line contribution.
367 reloc_segment: u16,
368 flags: LineFlags,
369
370 /// Code size of this line contribution.
371 code_size: u32,
372};
373
374pub const LineFlags = packed struct(u16) {
375 /// CV_LINES_HAVE_COLUMNS
376 have_columns: bool,
377 unused: u15,
378};
379
380/// The following two variable length arrays appear immediately after the
381/// header. The structure definitions follow.
382/// LineNumberEntry Lines[NumLines];
383/// ColumnNumberEntry Columns[NumLines];
384pub const LineBlockFragmentHeader = extern struct {
385 /// Offset of FileChecksum entry in File
386 /// checksums buffer. The checksum entry then
387 /// contains another offset into the string
388 /// table of the actual name.
389 name_index: u32,
390 num_lines: u32,
391
392 /// code size of block, in bytes
393 block_size: u32,
394};
395
396pub const LineNumberEntry = extern struct {
397 /// Offset to start of code bytes for line number
398 offset: u32,
399 flags: Flags,
400
401 pub const Flags = packed struct(u32) {
402 /// Start line number
403 start: u24,
404 /// Delta of lines to the end of the expression. Still unclear.
405 // TODO figure out the point of this field.
406 end: u7,
407 is_statement: bool,
408 };
409};
410
411pub const ColumnNumberEntry = extern struct {
412 start_column: u16,
413 end_column: u16,
414};
415
416/// Checksum bytes follow.
417pub const FileChecksumEntryHeader = extern struct {
418 /// Byte offset of filename in global string table.
419 file_name_offset: u32,
420 /// Number of bytes of checksum.
421 checksum_size: u8,
422 /// FileChecksumKind
423 checksum_kind: u8,
424};
425
426pub const DebugSubsectionKind = enum(u32) {
427 none = 0,
428 symbols = 0xf1,
429 lines = 0xf2,
430 string_table = 0xf3,
431 file_checksums = 0xf4,
432 frame_data = 0xf5,
433 inlinee_lines = 0xf6,
434 cross_scope_imports = 0xf7,
435 cross_scope_exports = 0xf8,
436
437 // These appear to relate to .Net assembly info.
438 il_lines = 0xf9,
439 func_md_token_map = 0xfa,
440 type_md_token_map = 0xfb,
441 merged_assembly_input = 0xfc,
442
443 coff_symbol_rva = 0xfd,
444};
445
446pub const DebugSubsectionHeader = extern struct {
447 /// codeview::DebugSubsectionKind enum
448 kind: DebugSubsectionKind,
449
450 /// number of bytes occupied by this record.
451 length: u32,
452};
453
454pub const StringTableHeader = extern struct {
455 /// PDBStringTableSignature
456 signature: u32,
457 /// 1 or 2
458 hash_version: u32,
459 /// Number of bytes of names buffer.
460 byte_size: u32,
461};
462
463// https://llvm.org/docs/PDB/MsfFile.html#the-superblock
464pub const SuperBlock = extern struct {
465 /// The LLVM docs list a space between C / C++ but empirically this is not the case.
466 pub const expect_magic = "Microsoft C/C++ MSF 7.00\r\n\x1a\x44\x53\x00\x00\x00";
467
468 file_magic: [expect_magic.len]u8,
469
470 /// The block size of the internal file system. Valid values are 512, 1024,
471 /// 2048, and 4096 bytes. Certain aspects of the MSF file layout vary depending
472 /// on the block sizes. For the purposes of LLVM, we handle only block sizes of
473 /// 4KiB, and all further discussion assumes a block size of 4KiB.
474 block_size: u32,
475
476 /// The index of a block within the file, at which begins a bitfield representing
477 /// the set of all blocks within the file which are “free” (i.e. the data within
478 /// that block is not used). See The Free Block Map for more information. Important:
479 /// FreeBlockMapBlock can only be 1 or 2!
480 free_block_map_block: u32,
481
482 /// The total number of blocks in the file. NumBlocks * BlockSize should equal the
483 /// size of the file on disk.
484 num_blocks: u32,
485
486 /// The size of the stream directory, in bytes. The stream directory contains
487 /// information about each stream’s size and the set of blocks that it occupies.
488 /// It will be described in more detail later.
489 num_directory_bytes: u32,
490
491 unknown: u32,
492 /// The index of a block within the MSF file. At this block is an array of
493 /// ulittle32_t’s listing the blocks that the stream directory resides on.
494 /// For large MSF files, the stream directory (which describes the block
495 /// layout of each stream) may not fit entirely on a single block. As a
496 /// result, this extra layer of indirection is introduced, whereby this
497 /// block contains the list of blocks that the stream directory occupies,
498 /// and the stream directory itself can be stitched together accordingly.
499 /// The number of ulittle32_t’s in this array is given by
500 /// ceil(NumDirectoryBytes / BlockSize).
501 // Note: microsoft-pdb code actually suggests this is a variable-length
502 // array. If the indices of blocks occupied by the Stream Directory didn't
503 // fit in one page, there would be other u32 following it.
504 // This would mean the Stream Directory is bigger than BlockSize / sizeof(u32)
505 // blocks. We're not even close to this with a 1GB pdb file, and LLVM didn't
506 // implement it so we're kind of safe making this assumption for now.
507 block_map_addr: u32,
508};
509
510pub const IpiStreamVersion = enum(u32) {
511 v40 = 19950410,
512 v41 = 19951122,
513 v50 = 19961031,
514 v70 = 19990903,
515 v80 = 20040203,
516 _,
517};
518
519pub const IpiStreamHeader = extern struct {
520 version: IpiStreamVersion,
521 header_size: u32,
522 type_index_begin: u32,
523 type_index_end: u32,
524 type_record_bytes: u32,
525 hash_stream_index: u16,
526 hash_aux_stream_index: u16,
527 hash_key_size: u32,
528 num_hash_buckets: u32,
529 hash_value_buffer_offset: i32,
530 hash_value_buffer_length: u32,
531 index_offset_buffer_offset: i32,
532 index_offset_buffer_length: u32,
533 hash_adj_buffer_offset: i32,
534 hash_adj_buffer_length: u32,
535};
536
537pub const LfRecordPrefix = extern struct {
538 len: u16,
539 kind: LfRecordKind,
540};
541
542pub const LfRecordKind = enum(u16) {
543 pointer = 0x1002,
544 modifier = 0x1001,
545 procedure = 0x1008,
546 mfunction = 0x1009,
547 label = 0x000e,
548 arglist = 0x1201,
549 fieldlist = 0x1203,
550 array = 0x1503,
551 class = 0x1504,
552 structure = 0x1505,
553 interface = 0x1519,
554 @"union" = 0x1506,
555 @"enum" = 0x1507,
556 typeserver2 = 0x1515,
557 vftable = 0x151d,
558 vtshape = 0x000a,
559 bitfield = 0x1205,
560 func_id = 0x1601,
561 mfunc_id = 0x1602,
562 buildinfo = 0x1603,
563 substr_list = 0x1604,
564 string_id = 0x1605,
565 udt_src_line = 0x1606,
566 udt_mod_src_line = 0x1607,
567 methodlist = 0x1206,
568 precomp = 0x1509,
569 endprecomp = 0x0014,
570 bclass = 0x1400,
571 binterface = 0x151a,
572 vbclass = 0x1401,
573 ivbclass = 0x1402,
574 vfunctab = 0x1409,
575 stmember = 0x150e,
576 method = 0x150f,
577 member = 0x150d,
578 nesttype = 0x1510,
579 onemethod = 0x1511,
580 enumerate = 0x1502,
581 index = 0x1404,
582 pad0 = 0xf0,
583 _,
584};
585
586pub const LfFuncId = extern struct {
587 len: u16,
588 kind: LfRecordKind,
589 scope_id: u32,
590 type: u32,
591 name: [1]u8, // null-terminated
592};
593
594pub const LfMFuncId = extern struct {
595 len: u16,
596 kind: LfRecordKind,
597 parent_type: u32,
598 type: u32,
599 name: [1]u8, // null-terminated
600};
601
602pub const InlineSiteSym = extern struct {
603 record_len: u16,
604 record_kind: SymbolKind,
605 parent: u32,
606 end: u32,
607 inlinee: u32,
608};
609
610pub const InlineSiteSym2 = extern struct {
611 record_len: u16,
612 record_kind: SymbolKind,
613 parent: u32,
614 end: u32,
615 inlinee: u32,
616 invocations: u32,
617};
618
619pub const InlineeSourceLineSignature = enum(u32) { normal = 0, ex = 1, _ };
620
621pub const InlineeSourceLine = extern struct {
622 inlinee: u32,
623 file_id: u32,
624 source_line_num: u32,
625};
626
627pub const InlineeSourceLineEx = extern struct {
628 inlinee: u32,
629 file_id: u32,
630 source_line_num: u32,
631 count_of_extra_files: u32,
632};
633
634pub const BinaryAnnotationOpcode = enum(u8) {
635 invalid = 0,
636 code_offset = 1,
637 change_code_offset_base = 2,
638 change_code_offset = 3,
639 change_code_length = 4,
640 change_file = 5,
641 change_line_offset = 6,
642 change_line_end_delta = 7,
643 change_range_kind = 8,
644 change_column_start = 9,
645 change_column_end_delta = 10,
646 change_code_offset_and_line_offset = 11,
647 change_code_length_and_code_offset = 12,
648 change_column_end = 13,
649};