authorgravatar for thatlemon@gmail.comLemonBoy <thatlemon@gmail.com> 2021-06-11 11:44:55+02:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-06-12 10:51:43+03:00
log986a71234bf2b40284117cac0537e68071b29b33
treeb40c288f1f1efe2b5df5ef716b4308168ad12728
parentcaad5c49607a910e7f06864dca31ca7a68333ef3

std: Move PDB-related code into its own file

No functional changes are expected, this patch is only moving some code in order to slim the huge bowl of spaghetti that is debug.zig. The amount of memory leaked on error is much less than before but not zero, some further work is required to smooth the edges of this old part of the stdlib.

2 files changed, 416 insertions(+), 367 deletions(-)

lib/std/debug.zig+30-338
......@@ -30,17 +30,6 @@ pub const runtime_safety = switch (builtin.mode) {
3030 .ReleaseFast, .ReleaseSmall => false,
3131};
3232
33const Module = struct {
34 mod_info: pdb.ModInfo,
35 module_name: []u8,
36 obj_file_name: []u8,
37
38 populated: bool,
39 symbols: []u8,
40 subsect_info: []u8,
41 checksum_offset: ?usize,
42};
43
4433pub const LineInfo = struct {
4534 line: u64,
4635 column: u64,
......@@ -53,6 +42,18 @@ pub const LineInfo = struct {
5342 }
5443};
5544
45pub const SymbolInfo = struct {
46 symbol_name: []const u8 = "???",
47 compile_unit_name: []const u8 = "???",
48 line_info: ?LineInfo = null,
49
50 fn deinit(self: @This()) void {
51 if (self.line_info) |li| {
52 li.deinit();
53 }
54 }
55};
56
5657var stderr_mutex = std.Thread.Mutex{};
5758
5859/// Deprecated. Use `std.log` functions for logging or `std.debug.print` for
......@@ -528,53 +529,6 @@ pub const TTY = struct {
528529 };
529530};
530531
531/// TODO resources https://github.com/ziglang/zig/issues/4353
532fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
533 if (mod.populated)
534 return;
535 const allocator = getDebugInfoAllocator();
536
537 // At most one can be non-zero.
538 if (mod.mod_info.C11ByteSize != 0 and mod.mod_info.C13ByteSize != 0)
539 return error.InvalidDebugInfo;
540
541 if (mod.mod_info.C13ByteSize == 0)
542 return;
543
544 const modi = di.pdb.getStreamById(mod.mod_info.ModuleSymStream) orelse return error.MissingDebugInfo;
545
546 const signature = try modi.reader().readIntLittle(u32);
547 if (signature != 4)
548 return error.InvalidDebugInfo;
549
550 mod.symbols = try allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
551 try modi.reader().readNoEof(mod.symbols);
552
553 mod.subsect_info = try allocator.alloc(u8, mod.mod_info.C13ByteSize);
554 try modi.reader().readNoEof(mod.subsect_info);
555
556 var sect_offset: usize = 0;
557 var skip_len: usize = undefined;
558 while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) {
559 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &mod.subsect_info[sect_offset]);
560 skip_len = subsect_hdr.Length;
561 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
562
563 switch (subsect_hdr.Kind) {
564 .FileChecksums => {
565 mod.checksum_offset = sect_offset;
566 break;
567 },
568 else => {},
569 }
570
571 if (sect_offset > mod.subsect_info.len)
572 return error.InvalidDebugInfo;
573 }
574
575 mod.populated = true;
576}
577
578532fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
579533 var min: usize = 0;
580534 var max: usize = symbols.len - 1; // Exclude sentinel.
......@@ -716,8 +670,6 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
716670 .base_address = undefined,
717671 .coff = coff_obj,
718672 .pdb = undefined,
719 .sect_contribs = undefined,
720 .modules = undefined,
721673 };
722674
723675 try di.coff.loadHeader();
......@@ -727,157 +679,19 @@ fn readCoffDebugInfo(allocator: *mem.Allocator, coff_file: File) !ModuleDebugInf
727679 const raw_path = path_buf[0..len];
728680
729681 const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path});
682 defer allocator.free(path);
730683
731 try di.pdb.openFile(di.coff, path);
732
733 var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo;
734 const version = try pdb_stream.reader().readIntLittle(u32);
735 const signature = try pdb_stream.reader().readIntLittle(u32);
736 const age = try pdb_stream.reader().readIntLittle(u32);
737 var guid: [16]u8 = undefined;
738 try pdb_stream.reader().readNoEof(&guid);
739 if (version != 20000404) // VC70, only value observed by LLVM team
740 return error.UnknownPDBVersion;
741 if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age)
742 return error.PDBMismatch;
743 // We validated the executable and pdb match.
744
745 const string_table_index = str_tab_index: {
746 const name_bytes_len = try pdb_stream.reader().readIntLittle(u32);
747 const name_bytes = try allocator.alloc(u8, name_bytes_len);
748 try pdb_stream.reader().readNoEof(name_bytes);
749
750 const HashTableHeader = packed struct {
751 Size: u32,
752 Capacity: u32,
753
754 fn maxLoad(cap: u32) u32 {
755 return cap * 2 / 3 + 1;
756 }
757 };
758 const hash_tbl_hdr = try pdb_stream.reader().readStruct(HashTableHeader);
759 if (hash_tbl_hdr.Capacity == 0)
760 return error.InvalidDebugInfo;
761
762 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
763 return error.InvalidDebugInfo;
764
765 const present = try readSparseBitVector(&pdb_stream.reader(), allocator);
766 if (present.len != hash_tbl_hdr.Size)
767 return error.InvalidDebugInfo;
768 const deleted = try readSparseBitVector(&pdb_stream.reader(), allocator);
769
770 const Bucket = struct {
771 first: u32,
772 second: u32,
773 };
774 const bucket_list = try allocator.alloc(Bucket, present.len);
775 for (present) |_| {
776 const name_offset = try pdb_stream.reader().readIntLittle(u32);
777 const name_index = try pdb_stream.reader().readIntLittle(u32);
778 const name = mem.spanZ(std.meta.assumeSentinel(name_bytes.ptr + name_offset, 0));
779 if (mem.eql(u8, name, "/names")) {
780 break :str_tab_index name_index;
781 }
782 }
783 return error.MissingDebugInfo;
784 };
785
786 di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo;
787 di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo;
788
789 const dbi = di.pdb.dbi;
684 di.pdb = try pdb.Pdb.init(allocator, path);
685 try di.pdb.parseInfoStream();
686 try di.pdb.parseDbiStream();
790687
791 // Dbi Header
792 const dbi_stream_header = try dbi.reader().readStruct(pdb.DbiStreamHeader);
793 if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team
794 return error.UnknownPDBVersion;
795 if (dbi_stream_header.Age != age)
796 return error.UnmatchingPDB;
797
798 const mod_info_size = dbi_stream_header.ModInfoSize;
799 const section_contrib_size = dbi_stream_header.SectionContributionSize;
800
801 var modules = ArrayList(Module).init(allocator);
802
803 // Module Info Substream
804 var mod_info_offset: usize = 0;
805 while (mod_info_offset != mod_info_size) {
806 const mod_info = try dbi.reader().readStruct(pdb.ModInfo);
807 var this_record_len: usize = @sizeOf(pdb.ModInfo);
808
809 const module_name = try dbi.readNullTermString(allocator);
810 this_record_len += module_name.len + 1;
811
812 const obj_file_name = try dbi.readNullTermString(allocator);
813 this_record_len += obj_file_name.len + 1;
814
815 if (this_record_len % 4 != 0) {
816 const round_to_next_4 = (this_record_len | 0x3) + 1;
817 const march_forward_bytes = round_to_next_4 - this_record_len;
818 try dbi.seekBy(@intCast(isize, march_forward_bytes));
819 this_record_len += march_forward_bytes;
820 }
821
822 try modules.append(Module{
823 .mod_info = mod_info,
824 .module_name = module_name,
825 .obj_file_name = obj_file_name,
826
827 .populated = false,
828 .symbols = undefined,
829 .subsect_info = undefined,
830 .checksum_offset = null,
831 });
832
833 mod_info_offset += this_record_len;
834 if (mod_info_offset > mod_info_size)
835 return error.InvalidDebugInfo;
836 }
837
838 di.modules = modules.toOwnedSlice();
839
840 // Section Contribution Substream
841 var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator);
842 var sect_cont_offset: usize = 0;
843 if (section_contrib_size != 0) {
844 const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.reader().readIntLittle(u32));
845 if (ver != pdb.SectionContrSubstreamVersion.Ver60)
846 return error.InvalidDebugInfo;
847 sect_cont_offset += @sizeOf(u32);
848 }
849 while (sect_cont_offset != section_contrib_size) {
850 const entry = try sect_contribs.addOne();
851 entry.* = try dbi.reader().readStruct(pdb.SectionContribEntry);
852 sect_cont_offset += @sizeOf(pdb.SectionContribEntry);
853
854 if (sect_cont_offset > section_contrib_size)
855 return error.InvalidDebugInfo;
856 }
857
858 di.sect_contribs = sect_contribs.toOwnedSlice();
688 if (!mem.eql(u8, &di.coff.guid, &di.pdb.guid) or di.coff.age != di.pdb.age)
689 return error.InvalidDebugInfo;
859690
860691 return di;
861692 }
862693}
863694
864fn readSparseBitVector(stream: anytype, allocator: *mem.Allocator) ![]usize {
865 const num_words = try stream.readIntLittle(u32);
866 var word_i: usize = 0;
867 var list = ArrayList(usize).init(allocator);
868 while (word_i != num_words) : (word_i += 1) {
869 const word = try stream.readIntLittle(u32);
870 var bit_i: u5 = 0;
871 while (true) : (bit_i += 1) {
872 if (word & (@as(u32, 1) << bit_i) != 0) {
873 try list.append(word_i * 32 + bit_i);
874 }
875 if (bit_i == maxInt(u5)) break;
876 }
877 }
878 return list.toOwnedSlice();
879}
880
881695fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
882696 const start = try math.cast(usize, offset);
883697 const end = start + try math.cast(usize, size);
......@@ -1353,18 +1167,6 @@ pub const DebugInfo = struct {
13531167 }
13541168};
13551169
1356const SymbolInfo = struct {
1357 symbol_name: []const u8 = "???",
1358 compile_unit_name: []const u8 = "???",
1359 line_info: ?LineInfo = null,
1360
1361 fn deinit(self: @This()) void {
1362 if (self.line_info) |li| {
1363 li.deinit();
1364 }
1365 }
1366};
1367
13681170pub const ModuleDebugInfo = switch (native_os) {
13691171 .macos, .ios, .watchos, .tvos => struct {
13701172 base_address: usize,
......@@ -1531,8 +1333,6 @@ pub const ModuleDebugInfo = switch (native_os) {
15311333 base_address: usize,
15321334 pdb: pdb.Pdb,
15331335 coff: *coff.Coff,
1534 sect_contribs: []pdb.SectionContribEntry,
1535 modules: []Module,
15361336
15371337 pub fn allocator(self: @This()) *mem.Allocator {
15381338 return self.coff.allocator;
......@@ -1543,7 +1343,7 @@ pub const ModuleDebugInfo = switch (native_os) {
15431343 const relocated_address = address - self.base_address;
15441344
15451345 var coff_section: *coff.Section = undefined;
1546 const mod_index = for (self.sect_contribs) |sect_contrib| {
1346 const mod_index = for (self.pdb.sect_contribs) |sect_contrib| {
15471347 if (sect_contrib.Section > self.coff.sections.items.len) continue;
15481348 // Remember that SectionContribEntry.Section is 1-based.
15491349 coff_section = &self.coff.sections.items[sect_contrib.Section - 1];
......@@ -1558,126 +1358,18 @@ pub const ModuleDebugInfo = switch (native_os) {
15581358 return SymbolInfo{};
15591359 };
15601360
1561 const mod = &self.modules[mod_index];
1562 try populateModule(self, mod);
1563 const obj_basename = fs.path.basename(mod.obj_file_name);
1564
1565 var symbol_i: usize = 0;
1566 const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) {
1567 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
1568 if (prefix.RecordLen < 2)
1569 return error.InvalidDebugInfo;
1570 switch (prefix.RecordKind) {
1571 .S_LPROC32, .S_GPROC32 => {
1572 const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
1573 const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
1574 const vaddr_end = vaddr_start + proc_sym.CodeSize;
1575 if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
1576 break mem.spanZ(@ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym));
1577 }
1578 },
1579 else => {},
1580 }
1581 symbol_i += prefix.RecordLen + @sizeOf(u16);
1582 if (symbol_i > mod.symbols.len)
1583 return error.InvalidDebugInfo;
1584 } else "???";
1585
1586 const subsect_info = mod.subsect_info;
1587
1588 var sect_offset: usize = 0;
1589 var skip_len: usize = undefined;
1590 const opt_line_info = subsections: {
1591 const checksum_offset = mod.checksum_offset orelse break :subsections null;
1592 while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
1593 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
1594 skip_len = subsect_hdr.Length;
1595 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
1596
1597 switch (subsect_hdr.Kind) {
1598 .Lines => {
1599 var line_index = sect_offset;
1600
1601 const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
1602 if (line_hdr.RelocSegment == 0)
1603 return error.MissingDebugInfo;
1604 line_index += @sizeOf(pdb.LineFragmentHeader);
1605 const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
1606 const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
1607
1608 if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) {
1609 // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
1610 // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
1611 // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
1612 const subsection_end_index = sect_offset + subsect_hdr.Length;
1613
1614 while (line_index < subsection_end_index) {
1615 const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
1616 line_index += @sizeOf(pdb.LineBlockFragmentHeader);
1617 const start_line_index = line_index;
1618
1619 const has_column = line_hdr.Flags.LF_HaveColumns;
1620
1621 // All line entries are stored inside their line block by ascending start address.
1622 // Heuristic: we want to find the last line entry
1623 // that has a vaddr_start <= relocated_address.
1624 // This is done with a simple linear search.
1625 var line_i: u32 = 0;
1626 while (line_i < block_hdr.NumLines) : (line_i += 1) {
1627 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
1628 line_index += @sizeOf(pdb.LineNumberEntry);
1629
1630 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
1631 if (relocated_address < vaddr_start) {
1632 break;
1633 }
1634 }
1635
1636 // line_i == 0 would mean that no matching LineNumberEntry was found.
1637 if (line_i > 0) {
1638 const subsect_index = checksum_offset + block_hdr.NameIndex;
1639 const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
1640 const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
1641 try self.pdb.string_table.seekTo(strtab_offset);
1642 const source_file_name = try self.pdb.string_table.readNullTermString(self.allocator());
1643
1644 const line_entry_idx = line_i - 1;
1645
1646 const column = if (has_column) blk: {
1647 const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
1648 const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx;
1649 const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]);
1650 break :blk col_num_entry.StartColumn;
1651 } else 0;
1652
1653 const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry);
1654 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]);
1655 const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
1656
1657 break :subsections LineInfo{
1658 .allocator = self.allocator(),
1659 .file_name = source_file_name,
1660 .line = flags.Start,
1661 .column = column,
1662 };
1663 }
1664 }
1665
1666 // Checking that we are not reading garbage after the (possibly) multiple block fragments.
1667 if (line_index != subsection_end_index) {
1668 return error.InvalidDebugInfo;
1669 }
1670 }
1671 },
1672 else => {},
1673 }
1361 const module = (try self.pdb.getModule(mod_index)) orelse
1362 return error.InvalidDebugInfo;
1363 const obj_basename = fs.path.basename(module.obj_file_name);
16741364
1675 if (sect_offset > subsect_info.len)
1676 return error.InvalidDebugInfo;
1677 } else {
1678 break :subsections null;
1679 }
1680 };
1365 const symbol_name = self.pdb.getSymbolName(
1366 module,
1367 relocated_address - coff_section.header.virtual_address,
1368 ) orelse "???";
1369 const opt_line_info = try self.pdb.getLineNumberInfo(
1370 module,
1371 relocated_address - coff_section.header.virtual_address,
1372 );
16811373
16821374 return SymbolInfo{
16831375 .symbol_name = symbol_name,
lib/std/pdb.zig+386-29
......@@ -13,6 +13,7 @@ const warn = std.debug.warn;
1313const coff = std.coff;
1414const fs = std.fs;
1515const File = std.fs.File;
16const debug = std.debug;
1617
1718const ArrayList = std.ArrayList;
1819
......@@ -345,6 +346,7 @@ pub const ProcSymFlags = packed struct {
345346pub const SectionContrSubstreamVersion = enum(u32) {
346347 Ver60 = 0xeffe0000 + 19970605,
347348 V2 = 0xeffe0000 + 20140516,
349 _,
348350};
349351
350352pub const RecordPrefix = packed struct {
......@@ -465,21 +467,382 @@ pub const PDBStringTableHeader = packed struct {
465467 ByteSize: u32,
466468};
467469
470fn readSparseBitVector(stream: anytype, allocator: *mem.Allocator) ![]u32 {
471 const num_words = try stream.readIntLittle(u32);
472 var list = ArrayList(u32).init(allocator);
473 errdefer list.deinit();
474 var word_i: u32 = 0;
475 while (word_i != num_words) : (word_i += 1) {
476 const word = try stream.readIntLittle(u32);
477 var bit_i: u5 = 0;
478 while (true) : (bit_i += 1) {
479 if (word & (@as(u32, 1) << bit_i) != 0) {
480 try list.append(word_i * 32 + bit_i);
481 }
482 if (bit_i == std.math.maxInt(u5)) break;
483 }
484 }
485 return list.toOwnedSlice();
486}
487
468488pub const Pdb = struct {
469489 in_file: File,
490 msf: Msf,
470491 allocator: *mem.Allocator,
471 coff: *coff.Coff,
472 string_table: *MsfStream,
473 dbi: *MsfStream,
492 string_table: ?*MsfStream,
493 dbi: ?*MsfStream,
494 modules: []Module,
495 sect_contribs: []SectionContribEntry,
496 guid: [16]u8,
497 age: u32,
498
499 pub const Module = struct {
500 mod_info: ModInfo,
501 module_name: []u8,
502 obj_file_name: []u8,
503 // The fields below are filled on demand.
504 populated: bool,
505 symbols: []u8,
506 subsect_info: []u8,
507 checksum_offset: ?usize,
508 };
474509
475 msf: Msf,
510 pub fn init(allocator: *mem.Allocator, path: []const u8) !Pdb {
511 const file = try fs.cwd().openFile(path, .{ .intended_io_mode = .blocking });
512 errdefer file.close();
513
514 return Pdb{
515 .in_file = file,
516 .allocator = allocator,
517 .string_table = null,
518 .dbi = null,
519 .msf = try Msf.init(allocator, file),
520 .modules = &[_]Module{},
521 .sect_contribs = &[_]SectionContribEntry{},
522 .guid = undefined,
523 .age = undefined,
524 };
525 }
526
527 pub fn deinit(self: *Pdb) void {
528 self.in_file.close();
529 self.allocator.free(self.modules);
530 self.allocator.free(self.sect_contribs);
531 }
532
533 pub fn parseDbiStream(self: *Pdb) !void {
534 var stream = self.getStream(StreamType.Dbi) orelse
535 return error.InvalidDebugInfo;
536 const reader = stream.reader();
537
538 const header = try reader.readStruct(DbiStreamHeader);
539 if (header.VersionHeader != 19990903) // V70, only value observed by LLVM team
540 return error.UnknownPDBVersion;
541 // if (header.Age != age)
542 // return error.UnmatchingPDB;
543
544 const mod_info_size = header.ModInfoSize;
545 const section_contrib_size = header.SectionContributionSize;
546
547 var modules = ArrayList(Module).init(self.allocator);
548 errdefer modules.deinit();
549
550 // Module Info Substream
551 var mod_info_offset: usize = 0;
552 while (mod_info_offset != mod_info_size) {
553 const mod_info = try reader.readStruct(ModInfo);
554 var this_record_len: usize = @sizeOf(ModInfo);
555
556 const module_name = try reader.readUntilDelimiterAlloc(self.allocator, 0, 1024);
557 errdefer self.allocator.free(module_name);
558 this_record_len += module_name.len + 1;
559
560 const obj_file_name = try reader.readUntilDelimiterAlloc(self.allocator, 0, 1024);
561 errdefer self.allocator.free(obj_file_name);
562 this_record_len += obj_file_name.len + 1;
563
564 if (this_record_len % 4 != 0) {
565 const round_to_next_4 = (this_record_len | 0x3) + 1;
566 const march_forward_bytes = round_to_next_4 - this_record_len;
567 try stream.seekBy(@intCast(isize, march_forward_bytes));
568 this_record_len += march_forward_bytes;
569 }
570
571 try modules.append(Module{
572 .mod_info = mod_info,
573 .module_name = module_name,
574 .obj_file_name = obj_file_name,
575
576 .populated = false,
577 .symbols = undefined,
578 .subsect_info = undefined,
579 .checksum_offset = null,
580 });
581
582 mod_info_offset += this_record_len;
583 if (mod_info_offset > mod_info_size)
584 return error.InvalidDebugInfo;
585 }
586
587 // Section Contribution Substream
588 var sect_contribs = ArrayList(SectionContribEntry).init(self.allocator);
589 errdefer sect_contribs.deinit();
590
591 var sect_cont_offset: usize = 0;
592 if (section_contrib_size != 0) {
593 const version = reader.readEnum(SectionContrSubstreamVersion, .Little) catch |err| switch (err) {
594 error.InvalidValue => return error.InvalidDebugInfo,
595 else => |e| return e,
596 };
597 sect_cont_offset += @sizeOf(u32);
598 }
599 while (sect_cont_offset != section_contrib_size) {
600 const entry = try sect_contribs.addOne();
601 entry.* = try reader.readStruct(SectionContribEntry);
602 sect_cont_offset += @sizeOf(SectionContribEntry);
603
604 if (sect_cont_offset > section_contrib_size)
605 return error.InvalidDebugInfo;
606 }
607
608 self.modules = modules.toOwnedSlice();
609 self.sect_contribs = sect_contribs.toOwnedSlice();
610 }
611
612 pub fn parseInfoStream(self: *Pdb) !void {
613 var stream = self.getStream(StreamType.Pdb) orelse
614 return error.InvalidDebugInfo;
615 const reader = stream.reader();
616
617 // Parse the InfoStreamHeader.
618 const version = try reader.readIntLittle(u32);
619 const signature = try reader.readIntLittle(u32);
620 const age = try reader.readIntLittle(u32);
621 const guid = try reader.readBytesNoEof(16);
622
623 if (version != 20000404) // VC70, only value observed by LLVM team
624 return error.UnknownPDBVersion;
625
626 self.guid = guid;
627 self.age = age;
628
629 // Find the string table.
630 const string_table_index = str_tab_index: {
631 const name_bytes_len = try reader.readIntLittle(u32);
632 const name_bytes = try self.allocator.alloc(u8, name_bytes_len);
633 defer self.allocator.free(name_bytes);
634 try reader.readNoEof(name_bytes);
635
636 const HashTableHeader = extern struct {
637 Size: u32,
638 Capacity: u32,
639
640 fn maxLoad(cap: u32) u32 {
641 return cap * 2 / 3 + 1;
642 }
643 };
644 const hash_tbl_hdr = try reader.readStruct(HashTableHeader);
645 if (hash_tbl_hdr.Capacity == 0)
646 return error.InvalidDebugInfo;
647
648 if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity))
649 return error.InvalidDebugInfo;
650
651 const present = try readSparseBitVector(&reader, self.allocator);
652 defer self.allocator.free(present);
653 if (present.len != hash_tbl_hdr.Size)
654 return error.InvalidDebugInfo;
655 const deleted = try readSparseBitVector(&reader, self.allocator);
656 defer self.allocator.free(deleted);
657
658 for (present) |_| {
659 const name_offset = try reader.readIntLittle(u32);
660 const name_index = try reader.readIntLittle(u32);
661 if (name_offset > name_bytes.len)
662 return error.InvalidDebugInfo;
663 const name = mem.spanZ(std.meta.assumeSentinel(name_bytes.ptr + name_offset, 0));
664 if (mem.eql(u8, name, "/names")) {
665 break :str_tab_index name_index;
666 }
667 }
668 return error.MissingDebugInfo;
669 };
670
671 self.string_table = self.getStreamById(string_table_index) orelse
672 return error.MissingDebugInfo;
673 }
674
675 pub fn getSymbolName(self: *Pdb, module: *Module, address: u64) ?[]const u8 {
676 std.debug.assert(module.populated);
677
678 var symbol_i: usize = 0;
679 while (symbol_i != module.symbols.len) {
680 const prefix = @ptrCast(*RecordPrefix, &module.symbols[symbol_i]);
681 if (prefix.RecordLen < 2)
682 return null;
683 switch (prefix.RecordKind) {
684 .S_LPROC32, .S_GPROC32 => {
685 const proc_sym = @ptrCast(*ProcSym, &module.symbols[symbol_i + @sizeOf(RecordPrefix)]);
686 if (address >= proc_sym.CodeOffset and address < proc_sym.CodeOffset + proc_sym.CodeSize) {
687 return mem.spanZ(@ptrCast([*:0]u8, proc_sym) + @sizeOf(ProcSym));
688 }
689 },
690 else => {},
691 }
692 symbol_i += prefix.RecordLen + @sizeOf(u16);
693 }
476694
477 pub fn openFile(self: *Pdb, coff_ptr: *coff.Coff, file_name: []const u8) !void {
478 self.in_file = try fs.cwd().openFile(file_name, .{ .intended_io_mode = .blocking });
479 self.allocator = coff_ptr.allocator;
480 self.coff = coff_ptr;
695 return null;
696 }
697
698 pub fn getLineNumberInfo(self: *Pdb, module: *Module, address: u64) !debug.LineInfo {
699 std.debug.assert(module.populated);
700 const subsect_info = module.subsect_info;
701
702 var sect_offset: usize = 0;
703 var skip_len: usize = undefined;
704 const checksum_offset = module.checksum_offset orelse return error.MissingDebugInfo;
705 while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
706 const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &subsect_info[sect_offset]);
707 skip_len = subsect_hdr.Length;
708 sect_offset += @sizeOf(DebugSubsectionHeader);
709
710 switch (subsect_hdr.Kind) {
711 .Lines => {
712 var line_index = sect_offset;
713
714 const line_hdr = @ptrCast(*LineFragmentHeader, &subsect_info[line_index]);
715 if (line_hdr.RelocSegment == 0)
716 return error.MissingDebugInfo;
717 line_index += @sizeOf(LineFragmentHeader);
718 const frag_vaddr_start = line_hdr.RelocOffset;
719 const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
720
721 if (address >= frag_vaddr_start and address < frag_vaddr_end) {
722 // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
723 // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
724 // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
725 const subsection_end_index = sect_offset + subsect_hdr.Length;
726
727 while (line_index < subsection_end_index) {
728 const block_hdr = @ptrCast(*LineBlockFragmentHeader, &subsect_info[line_index]);
729 line_index += @sizeOf(LineBlockFragmentHeader);
730 const start_line_index = line_index;
731
732 const has_column = line_hdr.Flags.LF_HaveColumns;
733
734 // All line entries are stored inside their line block by ascending start address.
735 // Heuristic: we want to find the last line entry
736 // that has a vaddr_start <= address.
737 // This is done with a simple linear search.
738 var line_i: u32 = 0;
739 while (line_i < block_hdr.NumLines) : (line_i += 1) {
740 const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[line_index]);
741 line_index += @sizeOf(LineNumberEntry);
742
743 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
744 if (address < vaddr_start) {
745 break;
746 }
747 }
748
749 // line_i == 0 would mean that no matching LineNumberEntry was found.
750 if (line_i > 0) {
751 const subsect_index = checksum_offset + block_hdr.NameIndex;
752 const chksum_hdr = @ptrCast(*FileChecksumEntryHeader, &module.subsect_info[subsect_index]);
753 const strtab_offset = @sizeOf(PDBStringTableHeader) + chksum_hdr.FileNameOffset;
754 try self.string_table.?.seekTo(strtab_offset);
755 const source_file_name = try self.string_table.?.reader().readUntilDelimiterAlloc(self.allocator, 0, 1024);
756
757 const line_entry_idx = line_i - 1;
758
759 const column = if (has_column) blk: {
760 const start_col_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines;
761 const col_index = start_col_index + @sizeOf(ColumnNumberEntry) * line_entry_idx;
762 const col_num_entry = @ptrCast(*ColumnNumberEntry, &subsect_info[col_index]);
763 break :blk col_num_entry.StartColumn;
764 } else 0;
765
766 const found_line_index = start_line_index + line_entry_idx * @sizeOf(LineNumberEntry);
767 const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[found_line_index]);
768 const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags);
769
770 return debug.LineInfo{
771 .allocator = self.allocator,
772 .file_name = source_file_name,
773 .line = flags.Start,
774 .column = column,
775 };
776 }
777 }
778
779 // Checking that we are not reading garbage after the (possibly) multiple block fragments.
780 if (line_index != subsection_end_index) {
781 return error.InvalidDebugInfo;
782 }
783 }
784 },
785 else => {},
786 }
787
788 if (sect_offset > subsect_info.len)
789 return error.InvalidDebugInfo;
790 }
481791
482 try self.msf.openFile(self.allocator, self.in_file);
792 return error.MissingDebugInfo;
793 }
794
795 pub fn getModule(self: *Pdb, index: usize) !?*Module {
796 if (index >= self.modules.len)
797 return null;
798
799 const mod = &self.modules[index];
800 if (mod.populated)
801 return mod;
802
803 // At most one can be non-zero.
804 if (mod.mod_info.C11ByteSize != 0 and mod.mod_info.C13ByteSize != 0)
805 return error.InvalidDebugInfo;
806 if (mod.mod_info.C13ByteSize == 0)
807 return error.InvalidDebugInfo;
808
809 const stream = self.getStreamById(mod.mod_info.ModuleSymStream) orelse
810 return error.MissingDebugInfo;
811 const reader = stream.reader();
812
813 const signature = try reader.readIntLittle(u32);
814 if (signature != 4)
815 return error.InvalidDebugInfo;
816
817 mod.symbols = try self.allocator.alloc(u8, mod.mod_info.SymByteSize - 4);
818 errdefer self.allocator.free(mod.symbols);
819 try reader.readNoEof(mod.symbols);
820
821 mod.subsect_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize);
822 errdefer self.allocator.free(mod.subsect_info);
823 try reader.readNoEof(mod.subsect_info);
824
825 var sect_offset: usize = 0;
826 var skip_len: usize = undefined;
827 while (sect_offset != mod.subsect_info.len) : (sect_offset += skip_len) {
828 const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &mod.subsect_info[sect_offset]);
829 skip_len = subsect_hdr.Length;
830 sect_offset += @sizeOf(DebugSubsectionHeader);
831
832 switch (subsect_hdr.Kind) {
833 .FileChecksums => {
834 mod.checksum_offset = sect_offset;
835 break;
836 },
837 else => {},
838 }
839
840 if (sect_offset > mod.subsect_info.len)
841 return error.InvalidDebugInfo;
842 }
843
844 mod.populated = true;
845 return mod;
483846 }
484847
485848 pub fn getStreamById(self: *Pdb, id: u32) ?*MsfStream {
......@@ -499,7 +862,7 @@ const Msf = struct {
499862 directory: MsfStream,
500863 streams: []MsfStream,
501864
502 fn openFile(self: *Msf, allocator: *mem.Allocator, file: File) !void {
865 fn init(allocator: *mem.Allocator, file: File) !Msf {
503866 const in = file.reader();
504867
505868 const superblock = try in.readStruct(SuperBlock);
......@@ -526,14 +889,14 @@ const Msf = struct {
526889 for (dir_blocks) |*b| {
527890 b.* = try in.readIntLittle(u32);
528891 }
529 self.directory = MsfStream.init(
892 var directory = MsfStream.init(
530893 superblock.BlockSize,
531894 file,
532895 dir_blocks,
533896 );
534897
535 const begin = self.directory.pos;
536 const stream_count = try self.directory.reader().readIntLittle(u32);
898 const begin = directory.pos;
899 const stream_count = try directory.reader().readIntLittle(u32);
537900 const stream_sizes = try allocator.alloc(u32, stream_count);
538901 defer allocator.free(stream_sizes);
539902
......@@ -542,12 +905,12 @@ const Msf = struct {
542905 // and must be taken into account when resolving stream indices.
543906 const Nil = 0xFFFFFFFF;
544907 for (stream_sizes) |*s, i| {
545 const size = try self.directory.reader().readIntLittle(u32);
908 const size = try directory.reader().readIntLittle(u32);
546909 s.* = if (size == Nil) 0 else blockCountFromSize(size, superblock.BlockSize);
547910 }
548911
549 self.streams = try allocator.alloc(MsfStream, stream_count);
550 for (self.streams) |*stream, i| {
912 const streams = try allocator.alloc(MsfStream, stream_count);
913 for (streams) |*stream, i| {
551914 const size = stream_sizes[i];
552915 if (size == 0) {
553916 stream.* = MsfStream{
......@@ -557,7 +920,7 @@ const Msf = struct {
557920 var blocks = try allocator.alloc(u32, size);
558921 var j: u32 = 0;
559922 while (j < size) : (j += 1) {
560 const block_id = try self.directory.reader().readIntLittle(u32);
923 const block_id = try directory.reader().readIntLittle(u32);
561924 const n = (block_id % superblock.BlockSize);
562925 // 0 is for SuperBlock, 1 and 2 for FPMs.
563926 if (block_id == 0 or n == 1 or n == 2 or block_id * superblock.BlockSize > try file.getEndPos())
......@@ -573,9 +936,14 @@ const Msf = struct {
573936 }
574937 }
575938
576 const end = self.directory.pos;
939 const end = directory.pos;
577940 if (end - begin != superblock.NumDirectoryBytes)
578941 return error.InvalidStreamDirectory;
942
943 return Msf{
944 .directory = directory,
945 .streams = streams,
946 };
579947 }
580948};
581949
......@@ -649,17 +1017,6 @@ const MsfStream = struct {
6491017 return stream;
6501018 }
6511019
652 pub fn readNullTermString(self: *MsfStream, allocator: *mem.Allocator) ![]u8 {
653 var list = ArrayList(u8).init(allocator);
654 while (true) {
655 const byte = try self.reader().readByte();
656 if (byte == 0) {
657 return list.items;
658 }
659 try list.append(byte);
660 }
661 }
662
6631020 fn read(self: *MsfStream, buffer: []u8) !usize {
6641021 var block_id = @intCast(usize, self.pos / self.block_size);
6651022 if (block_id >= self.blocks.len) return 0; // End of Stream