| ... | @@ -666,158 +666,160 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { | ... | @@ -666,158 +666,160 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { |
| 666 | | 666 | |
| 667 | /// TODO resources https://github.com/ziglang/zig/issues/4353 | 667 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 668 | fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { | 668 | fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo { |
| 669 | const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{}); | 669 | noasync { |
| 670 | errdefer coff_file.close(); | 670 | const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{ .always_blocking = true }); |
| | 671 | errdefer coff_file.close(); |
| 671 | | 672 | |
| 672 | const coff_obj = try allocator.create(coff.Coff); | 673 | const coff_obj = try allocator.create(coff.Coff); |
| 673 | coff_obj.* = coff.Coff.init(allocator, coff_file); | 674 | coff_obj.* = coff.Coff.init(allocator, coff_file); |
| 674 | | 675 | |
| 675 | var di = ModuleDebugInfo{ | 676 | var di = ModuleDebugInfo{ |
| 676 | .base_address = undefined, | 677 | .base_address = undefined, |
| 677 | .coff = coff_obj, | 678 | .coff = coff_obj, |
| 678 | .pdb = undefined, | 679 | .pdb = undefined, |
| 679 | .sect_contribs = undefined, | 680 | .sect_contribs = undefined, |
| 680 | .modules = undefined, | 681 | .modules = undefined, |
| 681 | }; | 682 | }; |
| 682 | | 683 | |
| 683 | try di.coff.loadHeader(); | 684 | try di.coff.loadHeader(); |
| 684 | | 685 | |
| 685 | var path_buf: [windows.MAX_PATH]u8 = undefined; | 686 | var path_buf: [windows.MAX_PATH]u8 = undefined; |
| 686 | const len = try di.coff.getPdbPath(path_buf[0..]); | 687 | const len = try di.coff.getPdbPath(path_buf[0..]); |
| 687 | const raw_path = path_buf[0..len]; | 688 | const raw_path = path_buf[0..len]; |
| 688 | | 689 | |
| 689 | const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); | 690 | const path = try fs.path.resolve(allocator, &[_][]const u8{raw_path}); |
| 690 | | 691 | |
| 691 | try di.pdb.openFile(di.coff, path); | 692 | try di.pdb.openFile(di.coff, path); |
| 692 | | 693 | |
| 693 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; | 694 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; |
| 694 | const version = try pdb_stream.inStream().readIntLittle(u32); | 695 | const version = try pdb_stream.inStream().readIntLittle(u32); |
| 695 | const signature = try pdb_stream.inStream().readIntLittle(u32); | 696 | const signature = try pdb_stream.inStream().readIntLittle(u32); |
| 696 | const age = try pdb_stream.inStream().readIntLittle(u32); | 697 | const age = try pdb_stream.inStream().readIntLittle(u32); |
| 697 | var guid: [16]u8 = undefined; | 698 | var guid: [16]u8 = undefined; |
| 698 | try pdb_stream.inStream().readNoEof(&guid); | 699 | try pdb_stream.inStream().readNoEof(&guid); |
| 699 | if (version != 20000404) // VC70, only value observed by LLVM team | 700 | if (version != 20000404) // VC70, only value observed by LLVM team |
| 700 | return error.UnknownPDBVersion; | 701 | return error.UnknownPDBVersion; |
| 701 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) | 702 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) |
| 702 | return error.PDBMismatch; | 703 | return error.PDBMismatch; |
| 703 | // We validated the executable and pdb match. | 704 | // We validated the executable and pdb match. |
| 704 | | 705 | |
| 705 | const string_table_index = str_tab_index: { | 706 | const string_table_index = str_tab_index: { |
| 706 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); | 707 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); |
| 707 | const name_bytes = try allocator.alloc(u8, name_bytes_len); | 708 | const name_bytes = try allocator.alloc(u8, name_bytes_len); |
| 708 | try pdb_stream.inStream().readNoEof(name_bytes); | 709 | try pdb_stream.inStream().readNoEof(name_bytes); |
| 709 | | 710 | |
| 710 | const HashTableHeader = packed struct { | 711 | const HashTableHeader = packed struct { |
| 711 | Size: u32, | 712 | Size: u32, |
| 712 | Capacity: u32, | 713 | Capacity: u32, |
| 713 | | 714 | |
| 714 | fn maxLoad(cap: u32) u32 { | 715 | fn maxLoad(cap: u32) u32 { |
| 715 | return cap * 2 / 3 + 1; | 716 | return cap * 2 / 3 + 1; |
| 716 | } | 717 | } |
| 717 | }; | 718 | }; |
| 718 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); | 719 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); |
| 719 | if (hash_tbl_hdr.Capacity == 0) | 720 | if (hash_tbl_hdr.Capacity == 0) |
| 720 | return error.InvalidDebugInfo; | 721 | return error.InvalidDebugInfo; |
| 721 | | 722 | |
| 722 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) | 723 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) |
| 723 | return error.InvalidDebugInfo; | 724 | return error.InvalidDebugInfo; |
| 724 | | 725 | |
| 725 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); | 726 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 726 | if (present.len != hash_tbl_hdr.Size) | 727 | if (present.len != hash_tbl_hdr.Size) |
| 727 | return error.InvalidDebugInfo; | 728 | return error.InvalidDebugInfo; |
| 728 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); | 729 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 729 | | 730 | |
| 730 | const Bucket = struct { | 731 | const Bucket = struct { |
| 731 | first: u32, | 732 | first: u32, |
| 732 | second: u32, | 733 | second: u32, |
| 733 | }; | 734 | }; |
| 734 | const bucket_list = try allocator.alloc(Bucket, present.len); | 735 | const bucket_list = try allocator.alloc(Bucket, present.len); |
| 735 | for (present) |_| { | 736 | for (present) |_| { |
| 736 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); | 737 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); |
| 737 | const name_index = try pdb_stream.inStream().readIntLittle(u32); | 738 | const name_index = try pdb_stream.inStream().readIntLittle(u32); |
| 738 | const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset)); | 739 | const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset)); |
| 739 | if (mem.eql(u8, name, "/names")) { | 740 | if (mem.eql(u8, name, "/names")) { |
| 740 | break :str_tab_index name_index; | 741 | break :str_tab_index name_index; |
| | 742 | } |
| 741 | } | 743 | } |
| 742 | } | 744 | return error.MissingDebugInfo; |
| 743 | return error.MissingDebugInfo; | 745 | }; |
| 744 | }; | | |
| 745 | | 746 | |
| 746 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; | 747 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) orelse return error.MissingDebugInfo; |
| 747 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; | 748 | di.pdb.dbi = di.pdb.getStream(pdb.StreamType.Dbi) orelse return error.MissingDebugInfo; |
| 748 | | 749 | |
| 749 | const dbi = di.pdb.dbi; | 750 | const dbi = di.pdb.dbi; |
| 750 | | 751 | |
| 751 | // Dbi Header | 752 | // Dbi Header |
| 752 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); | 753 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); |
| 753 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team | 754 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team |
| 754 | return error.UnknownPDBVersion; | 755 | return error.UnknownPDBVersion; |
| 755 | if (dbi_stream_header.Age != age) | 756 | if (dbi_stream_header.Age != age) |
| 756 | return error.UnmatchingPDB; | 757 | return error.UnmatchingPDB; |
| 757 | | 758 | |
| 758 | const mod_info_size = dbi_stream_header.ModInfoSize; | 759 | const mod_info_size = dbi_stream_header.ModInfoSize; |
| 759 | const section_contrib_size = dbi_stream_header.SectionContributionSize; | 760 | const section_contrib_size = dbi_stream_header.SectionContributionSize; |
| 760 | | 761 | |
| 761 | var modules = ArrayList(Module).init(allocator); | 762 | var modules = ArrayList(Module).init(allocator); |
| 762 | | 763 | |
| 763 | // Module Info Substream | 764 | // Module Info Substream |
| 764 | var mod_info_offset: usize = 0; | 765 | var mod_info_offset: usize = 0; |
| 765 | while (mod_info_offset != mod_info_size) { | 766 | while (mod_info_offset != mod_info_size) { |
| 766 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); | 767 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); |
| 767 | var this_record_len: usize = @sizeOf(pdb.ModInfo); | 768 | var this_record_len: usize = @sizeOf(pdb.ModInfo); |
| 768 | | 769 | |
| 769 | const module_name = try dbi.readNullTermString(allocator); | 770 | const module_name = try dbi.readNullTermString(allocator); |
| 770 | this_record_len += module_name.len + 1; | 771 | this_record_len += module_name.len + 1; |
| 771 | | 772 | |
| 772 | const obj_file_name = try dbi.readNullTermString(allocator); | 773 | const obj_file_name = try dbi.readNullTermString(allocator); |
| 773 | this_record_len += obj_file_name.len + 1; | 774 | this_record_len += obj_file_name.len + 1; |
| 774 | | 775 | |
| 775 | if (this_record_len % 4 != 0) { | 776 | if (this_record_len % 4 != 0) { |
| 776 | const round_to_next_4 = (this_record_len | 0x3) + 1; | 777 | const round_to_next_4 = (this_record_len | 0x3) + 1; |
| 777 | const march_forward_bytes = round_to_next_4 - this_record_len; | 778 | const march_forward_bytes = round_to_next_4 - this_record_len; |
| 778 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); | 779 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); |
| 779 | this_record_len += march_forward_bytes; | 780 | this_record_len += march_forward_bytes; |
| 780 | } | 781 | } |
| 781 | | 782 | |
| 782 | try modules.append(Module{ | 783 | try modules.append(Module{ |
| 783 | .mod_info = mod_info, | 784 | .mod_info = mod_info, |
| 784 | .module_name = module_name, | 785 | .module_name = module_name, |
| 785 | .obj_file_name = obj_file_name, | 786 | .obj_file_name = obj_file_name, |
| 786 | | 787 | |
| 787 | .populated = false, | 788 | .populated = false, |
| 788 | .symbols = undefined, | 789 | .symbols = undefined, |
| 789 | .subsect_info = undefined, | 790 | .subsect_info = undefined, |
| 790 | .checksum_offset = null, | 791 | .checksum_offset = null, |
| 791 | }); | 792 | }); |
| 792 | | 793 | |
| 793 | mod_info_offset += this_record_len; | 794 | mod_info_offset += this_record_len; |
| 794 | if (mod_info_offset > mod_info_size) | 795 | if (mod_info_offset > mod_info_size) |
| 795 | return error.InvalidDebugInfo; | 796 | return error.InvalidDebugInfo; |
| 796 | } | 797 | } |
| 797 | | 798 | |
| 798 | di.modules = modules.toOwnedSlice(); | 799 | di.modules = modules.toOwnedSlice(); |
| 799 | | 800 | |
| 800 | // Section Contribution Substream | 801 | // Section Contribution Substream |
| 801 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); | 802 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); |
| 802 | var sect_cont_offset: usize = 0; | 803 | var sect_cont_offset: usize = 0; |
| 803 | if (section_contrib_size != 0) { | 804 | if (section_contrib_size != 0) { |
| 804 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); | 805 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); |
| 805 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) | 806 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) |
| 806 | return error.InvalidDebugInfo; | 807 | return error.InvalidDebugInfo; |
| 807 | sect_cont_offset += @sizeOf(u32); | 808 | sect_cont_offset += @sizeOf(u32); |
| 808 | } | 809 | } |
| 809 | while (sect_cont_offset != section_contrib_size) { | 810 | while (sect_cont_offset != section_contrib_size) { |
| 810 | const entry = try sect_contribs.addOne(); | 811 | const entry = try sect_contribs.addOne(); |
| 811 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); | 812 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); |
| 812 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); | 813 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); |
| 813 | | 814 | |
| 814 | if (sect_cont_offset > section_contrib_size) | 815 | if (sect_cont_offset > section_contrib_size) |
| 815 | return error.InvalidDebugInfo; | 816 | return error.InvalidDebugInfo; |
| 816 | } | 817 | } |
| 817 | | 818 | |
| 818 | di.sect_contribs = sect_contribs.toOwnedSlice(); | 819 | di.sect_contribs = sect_contribs.toOwnedSlice(); |
| 819 | | 820 | |
| 820 | return di; | 821 | return di; |
| | 822 | } |
| 821 | } | 823 | } |
| 822 | | 824 | |
| 823 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { | 825 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { |
| ... | @@ -1476,151 +1478,153 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { | ... | @@ -1476,151 +1478,153 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1476 | } | 1478 | } |
| 1477 | | 1479 | |
| 1478 | fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { | 1480 | fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { |
| 1479 | // Translate the VA into an address into this object | 1481 | noasync { |
| 1480 | const relocated_address = address - self.base_address; | 1482 | // Translate the VA into an address into this object |
| | 1483 | const relocated_address = address - self.base_address; |
| | 1484 | |
| | 1485 | var coff_section: *coff.Section = undefined; |
| | 1486 | const mod_index = for (self.sect_contribs) |sect_contrib| { |
| | 1487 | if (sect_contrib.Section > self.coff.sections.len) continue; |
| | 1488 | // Remember that SectionContribEntry.Section is 1-based. |
| | 1489 | coff_section = &self.coff.sections.toSlice()[sect_contrib.Section - 1]; |
| | 1490 | |
| | 1491 | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; |
| | 1492 | const vaddr_end = vaddr_start + sect_contrib.Size; |
| | 1493 | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { |
| | 1494 | break sect_contrib.ModuleIndex; |
| | 1495 | } |
| | 1496 | } else { |
| | 1497 | // we have no information to add to the address |
| | 1498 | return SymbolInfo{}; |
| | 1499 | }; |
| 1481 | | 1500 | |
| 1482 | var coff_section: *coff.Section = undefined; | 1501 | const mod = &self.modules[mod_index]; |
| 1483 | const mod_index = for (self.sect_contribs) |sect_contrib| { | 1502 | try populateModule(self, mod); |
| 1484 | if (sect_contrib.Section > self.coff.sections.len) continue; | 1503 | const obj_basename = fs.path.basename(mod.obj_file_name); |
| 1485 | // Remember that SectionContribEntry.Section is 1-based. | | |
| 1486 | coff_section = &self.coff.sections.toSlice()[sect_contrib.Section - 1]; | | |
| 1487 | | 1504 | |
| 1488 | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; | 1505 | var symbol_i: usize = 0; |
| 1489 | const vaddr_end = vaddr_start + sect_contrib.Size; | 1506 | const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { |
| 1490 | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { | 1507 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); |
| 1491 | break sect_contrib.ModuleIndex; | 1508 | if (prefix.RecordLen < 2) |
| 1492 | } | 1509 | return error.InvalidDebugInfo; |
| 1493 | } else { | 1510 | switch (prefix.RecordKind) { |
| 1494 | // we have no information to add to the address | 1511 | .S_LPROC32, .S_GPROC32 => { |
| 1495 | return SymbolInfo{}; | 1512 | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); |
| 1496 | }; | 1513 | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; |
| | 1514 | const vaddr_end = vaddr_start + proc_sym.CodeSize; |
| | 1515 | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { |
| | 1516 | break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); |
| | 1517 | } |
| | 1518 | }, |
| | 1519 | else => {}, |
| | 1520 | } |
| | 1521 | symbol_i += prefix.RecordLen + @sizeOf(u16); |
| | 1522 | if (symbol_i > mod.symbols.len) |
| | 1523 | return error.InvalidDebugInfo; |
| | 1524 | } else "???"; |
| | 1525 | |
| | 1526 | const subsect_info = mod.subsect_info; |
| | 1527 | |
| | 1528 | var sect_offset: usize = 0; |
| | 1529 | var skip_len: usize = undefined; |
| | 1530 | const opt_line_info = subsections: { |
| | 1531 | const checksum_offset = mod.checksum_offset orelse break :subsections null; |
| | 1532 | while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { |
| | 1533 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); |
| | 1534 | skip_len = subsect_hdr.Length; |
| | 1535 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); |
| | 1536 | |
| | 1537 | switch (subsect_hdr.Kind) { |
| | 1538 | .Lines => { |
| | 1539 | var line_index = sect_offset; |
| | 1540 | |
| | 1541 | const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); |
| | 1542 | if (line_hdr.RelocSegment == 0) |
| | 1543 | return error.MissingDebugInfo; |
| | 1544 | line_index += @sizeOf(pdb.LineFragmentHeader); |
| | 1545 | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; |
| | 1546 | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; |
| | 1547 | |
| | 1548 | if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) { |
| | 1549 | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) |
| | 1550 | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, |
| | 1551 | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. |
| | 1552 | const subsection_end_index = sect_offset + subsect_hdr.Length; |
| | 1553 | |
| | 1554 | while (line_index < subsection_end_index) { |
| | 1555 | const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); |
| | 1556 | line_index += @sizeOf(pdb.LineBlockFragmentHeader); |
| | 1557 | const start_line_index = line_index; |
| | 1558 | |
| | 1559 | const has_column = line_hdr.Flags.LF_HaveColumns; |
| | 1560 | |
| | 1561 | // All line entries are stored inside their line block by ascending start address. |
| | 1562 | // Heuristic: we want to find the last line entry |
| | 1563 | // that has a vaddr_start <= relocated_address. |
| | 1564 | // This is done with a simple linear search. |
| | 1565 | var line_i: u32 = 0; |
| | 1566 | while (line_i < block_hdr.NumLines) : (line_i += 1) { |
| | 1567 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); |
| | 1568 | line_index += @sizeOf(pdb.LineNumberEntry); |
| | 1569 | |
| | 1570 | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; |
| | 1571 | if (relocated_address < vaddr_start) { |
| | 1572 | break; |
| | 1573 | } |
| | 1574 | } |
| 1497 | | 1575 | |
| 1498 | const mod = &self.modules[mod_index]; | 1576 | // line_i == 0 would mean that no matching LineNumberEntry was found. |
| 1499 | try populateModule(self, mod); | 1577 | if (line_i > 0) { |
| 1500 | const obj_basename = fs.path.basename(mod.obj_file_name); | 1578 | const subsect_index = checksum_offset + block_hdr.NameIndex; |
| 1501 | | 1579 | const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); |
| 1502 | var symbol_i: usize = 0; | 1580 | const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; |
| 1503 | const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { | 1581 | try self.pdb.string_table.seekTo(strtab_offset); |
| 1504 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); | 1582 | const source_file_name = try self.pdb.string_table.readNullTermString(self.allocator()); |
| 1505 | if (prefix.RecordLen < 2) | 1583 | |
| 1506 | return error.InvalidDebugInfo; | 1584 | const line_entry_idx = line_i - 1; |
| 1507 | switch (prefix.RecordKind) { | 1585 | |
| 1508 | .S_LPROC32, .S_GPROC32 => { | 1586 | const column = if (has_column) blk: { |
| 1509 | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); | 1587 | const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; |
| 1510 | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; | 1588 | const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; |
| 1511 | const vaddr_end = vaddr_start + proc_sym.CodeSize; | 1589 | const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); |
| 1512 | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { | 1590 | break :blk col_num_entry.StartColumn; |
| 1513 | break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); | 1591 | } else 0; |
| 1514 | } | 1592 | |
| 1515 | }, | 1593 | const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); |
| 1516 | else => {}, | 1594 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); |
| 1517 | } | 1595 | const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); |
| 1518 | symbol_i += prefix.RecordLen + @sizeOf(u16); | 1596 | |
| 1519 | if (symbol_i > mod.symbols.len) | 1597 | break :subsections LineInfo{ |
| 1520 | return error.InvalidDebugInfo; | 1598 | .allocator = self.allocator(), |
| 1521 | } else "???"; | 1599 | .file_name = source_file_name, |
| 1522 | | 1600 | .line = flags.Start, |
| 1523 | const subsect_info = mod.subsect_info; | 1601 | .column = column, |
| 1524 | | 1602 | }; |
| 1525 | var sect_offset: usize = 0; | | |
| 1526 | var skip_len: usize = undefined; | | |
| 1527 | const opt_line_info = subsections: { | | |
| 1528 | const checksum_offset = mod.checksum_offset orelse break :subsections null; | | |
| 1529 | while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { | | |
| 1530 | const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]); | | |
| 1531 | skip_len = subsect_hdr.Length; | | |
| 1532 | sect_offset += @sizeOf(pdb.DebugSubsectionHeader); | | |
| 1533 | | | |
| 1534 | switch (subsect_hdr.Kind) { | | |
| 1535 | .Lines => { | | |
| 1536 | var line_index = sect_offset; | | |
| 1537 | | | |
| 1538 | const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]); | | |
| 1539 | if (line_hdr.RelocSegment == 0) | | |
| 1540 | return error.MissingDebugInfo; | | |
| 1541 | line_index += @sizeOf(pdb.LineFragmentHeader); | | |
| 1542 | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; | | |
| 1543 | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; | | |
| 1544 | | | |
| 1545 | if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) { | | |
| 1546 | // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records) | | |
| 1547 | // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in, | | |
| 1548 | // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection. | | |
| 1549 | const subsection_end_index = sect_offset + subsect_hdr.Length; | | |
| 1550 | | | |
| 1551 | while (line_index < subsection_end_index) { | | |
| 1552 | const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]); | | |
| 1553 | line_index += @sizeOf(pdb.LineBlockFragmentHeader); | | |
| 1554 | const start_line_index = line_index; | | |
| 1555 | | | |
| 1556 | const has_column = line_hdr.Flags.LF_HaveColumns; | | |
| 1557 | | | |
| 1558 | // All line entries are stored inside their line block by ascending start address. | | |
| 1559 | // Heuristic: we want to find the last line entry | | |
| 1560 | // that has a vaddr_start <= relocated_address. | | |
| 1561 | // This is done with a simple linear search. | | |
| 1562 | var line_i: u32 = 0; | | |
| 1563 | while (line_i < block_hdr.NumLines) : (line_i += 1) { | | |
| 1564 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]); | | |
| 1565 | line_index += @sizeOf(pdb.LineNumberEntry); | | |
| 1566 | | | |
| 1567 | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; | | |
| 1568 | if (relocated_address < vaddr_start) { | | |
| 1569 | break; | | |
| 1570 | } | 1603 | } |
| 1571 | } | 1604 | } |
| 1572 | | 1605 | |
| 1573 | // line_i == 0 would mean that no matching LineNumberEntry was found. | 1606 | // Checking that we are not reading garbage after the (possibly) multiple block fragments. |
| 1574 | if (line_i > 0) { | 1607 | if (line_index != subsection_end_index) { |
| 1575 | const subsect_index = checksum_offset + block_hdr.NameIndex; | 1608 | return error.InvalidDebugInfo; |
| 1576 | const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); | | |
| 1577 | const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; | | |
| 1578 | try self.pdb.string_table.seekTo(strtab_offset); | | |
| 1579 | const source_file_name = try self.pdb.string_table.readNullTermString(self.allocator()); | | |
| 1580 | | | |
| 1581 | const line_entry_idx = line_i - 1; | | |
| 1582 | | | |
| 1583 | const column = if (has_column) blk: { | | |
| 1584 | const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; | | |
| 1585 | const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; | | |
| 1586 | const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); | | |
| 1587 | break :blk col_num_entry.StartColumn; | | |
| 1588 | } else 0; | | |
| 1589 | | | |
| 1590 | const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); | | |
| 1591 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); | | |
| 1592 | const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); | | |
| 1593 | | | |
| 1594 | break :subsections LineInfo{ | | |
| 1595 | .allocator = self.allocator(), | | |
| 1596 | .file_name = source_file_name, | | |
| 1597 | .line = flags.Start, | | |
| 1598 | .column = column, | | |
| 1599 | }; | | |
| 1600 | } | 1609 | } |
| 1601 | } | 1610 | } |
| | 1611 | }, |
| | 1612 | else => {}, |
| | 1613 | } |
| 1602 | | 1614 | |
| 1603 | // Checking that we are not reading garbage after the (possibly) multiple block fragments. | 1615 | if (sect_offset > subsect_info.len) |
| 1604 | if (line_index != subsection_end_index) { | 1616 | return error.InvalidDebugInfo; |
| 1605 | return error.InvalidDebugInfo; | 1617 | } else { |
| 1606 | } | 1618 | break :subsections null; |
| 1607 | } | | |
| 1608 | }, | | |
| 1609 | else => {}, | | |
| 1610 | } | 1619 | } |
| | 1620 | }; |
| 1611 | | 1621 | |
| 1612 | if (sect_offset > subsect_info.len) | 1622 | return SymbolInfo{ |
| 1613 | return error.InvalidDebugInfo; | 1623 | .symbol_name = symbol_name, |
| 1614 | } else { | 1624 | .compile_unit_name = obj_basename, |
| 1615 | break :subsections null; | 1625 | .line_info = opt_line_info, |
| 1616 | } | 1626 | }; |
| 1617 | }; | 1627 | } |
| 1618 | | | |
| 1619 | return SymbolInfo{ | | |
| 1620 | .symbol_name = symbol_name, | | |
| 1621 | .compile_unit_name = obj_basename, | | |
| 1622 | .line_info = opt_line_info, | | |
| 1623 | }; | | |
| 1624 | } | 1628 | } |
| 1625 | }, | 1629 | }, |
| 1626 | .linux, .netbsd, .freebsd, .dragonfly => struct { | 1630 | .linux, .netbsd, .freebsd, .dragonfly => struct { |