| ... | ... | @@ -666,158 +666,160 @@ pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo { |
| 666 | 666 | |
| 667 | 667 | /// TODO resources https://github.com/ziglang/zig/issues/4353 |
| 668 | 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, .{}); |
| 670 | | errdefer coff_file.close(); |
| 669 | noasync { |
| 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 | | coff_obj.* = coff.Coff.init(allocator, coff_file); |
| 673 | const coff_obj = try allocator.create(coff.Coff); |
| 674 | coff_obj.* = coff.Coff.init(allocator, coff_file); |
| 674 | 675 | |
| 675 | | var di = ModuleDebugInfo{ |
| 676 | | .base_address = undefined, |
| 677 | | .coff = coff_obj, |
| 678 | | .pdb = undefined, |
| 679 | | .sect_contribs = undefined, |
| 680 | | .modules = undefined, |
| 681 | | }; |
| 676 | var di = ModuleDebugInfo{ |
| 677 | .base_address = undefined, |
| 678 | .coff = coff_obj, |
| 679 | .pdb = undefined, |
| 680 | .sect_contribs = undefined, |
| 681 | .modules = undefined, |
| 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 | | const len = try di.coff.getPdbPath(path_buf[0..]); |
| 687 | | const raw_path = path_buf[0..len]; |
| 686 | var path_buf: [windows.MAX_PATH]u8 = undefined; |
| 687 | const len = try di.coff.getPdbPath(path_buf[0..]); |
| 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 | | const version = try pdb_stream.inStream().readIntLittle(u32); |
| 695 | | const signature = try pdb_stream.inStream().readIntLittle(u32); |
| 696 | | const age = try pdb_stream.inStream().readIntLittle(u32); |
| 697 | | var guid: [16]u8 = undefined; |
| 698 | | try pdb_stream.inStream().readNoEof(&guid); |
| 699 | | if (version != 20000404) // VC70, only value observed by LLVM team |
| 700 | | return error.UnknownPDBVersion; |
| 701 | | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) |
| 702 | | return error.PDBMismatch; |
| 703 | | // We validated the executable and pdb match. |
| 694 | var pdb_stream = di.pdb.getStream(pdb.StreamType.Pdb) orelse return error.InvalidDebugInfo; |
| 695 | const version = try pdb_stream.inStream().readIntLittle(u32); |
| 696 | const signature = try pdb_stream.inStream().readIntLittle(u32); |
| 697 | const age = try pdb_stream.inStream().readIntLittle(u32); |
| 698 | var guid: [16]u8 = undefined; |
| 699 | try pdb_stream.inStream().readNoEof(&guid); |
| 700 | if (version != 20000404) // VC70, only value observed by LLVM team |
| 701 | return error.UnknownPDBVersion; |
| 702 | if (!mem.eql(u8, &di.coff.guid, &guid) or di.coff.age != age) |
| 703 | return error.PDBMismatch; |
| 704 | // We validated the executable and pdb match. |
| 704 | 705 | |
| 705 | | const string_table_index = str_tab_index: { |
| 706 | | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); |
| 707 | | const name_bytes = try allocator.alloc(u8, name_bytes_len); |
| 708 | | try pdb_stream.inStream().readNoEof(name_bytes); |
| 706 | const string_table_index = str_tab_index: { |
| 707 | const name_bytes_len = try pdb_stream.inStream().readIntLittle(u32); |
| 708 | const name_bytes = try allocator.alloc(u8, name_bytes_len); |
| 709 | try pdb_stream.inStream().readNoEof(name_bytes); |
| 709 | 710 | |
| 710 | | const HashTableHeader = packed struct { |
| 711 | | Size: u32, |
| 712 | | Capacity: u32, |
| 711 | const HashTableHeader = packed struct { |
| 712 | Size: u32, |
| 713 | Capacity: u32, |
| 713 | 714 | |
| 714 | | fn maxLoad(cap: u32) u32 { |
| 715 | | return cap * 2 / 3 + 1; |
| 716 | | } |
| 717 | | }; |
| 718 | | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); |
| 719 | | if (hash_tbl_hdr.Capacity == 0) |
| 720 | | return error.InvalidDebugInfo; |
| 715 | fn maxLoad(cap: u32) u32 { |
| 716 | return cap * 2 / 3 + 1; |
| 717 | } |
| 718 | }; |
| 719 | const hash_tbl_hdr = try pdb_stream.inStream().readStruct(HashTableHeader); |
| 720 | if (hash_tbl_hdr.Capacity == 0) |
| 721 | return error.InvalidDebugInfo; |
| 721 | 722 | |
| 722 | | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) |
| 723 | | return error.InvalidDebugInfo; |
| 723 | if (hash_tbl_hdr.Size > HashTableHeader.maxLoad(hash_tbl_hdr.Capacity)) |
| 724 | return error.InvalidDebugInfo; |
| 724 | 725 | |
| 725 | | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 726 | | if (present.len != hash_tbl_hdr.Size) |
| 727 | | return error.InvalidDebugInfo; |
| 728 | | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 726 | const present = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 727 | if (present.len != hash_tbl_hdr.Size) |
| 728 | return error.InvalidDebugInfo; |
| 729 | const deleted = try readSparseBitVector(&pdb_stream.inStream(), allocator); |
| 729 | 730 | |
| 730 | | const Bucket = struct { |
| 731 | | first: u32, |
| 732 | | second: u32, |
| 733 | | }; |
| 734 | | const bucket_list = try allocator.alloc(Bucket, present.len); |
| 735 | | for (present) |_| { |
| 736 | | const name_offset = try pdb_stream.inStream().readIntLittle(u32); |
| 737 | | const name_index = try pdb_stream.inStream().readIntLittle(u32); |
| 738 | | const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset)); |
| 739 | | if (mem.eql(u8, name, "/names")) { |
| 740 | | break :str_tab_index name_index; |
| 731 | const Bucket = struct { |
| 732 | first: u32, |
| 733 | second: u32, |
| 734 | }; |
| 735 | const bucket_list = try allocator.alloc(Bucket, present.len); |
| 736 | for (present) |_| { |
| 737 | const name_offset = try pdb_stream.inStream().readIntLittle(u32); |
| 738 | const name_index = try pdb_stream.inStream().readIntLittle(u32); |
| 739 | const name = mem.toSlice(u8, @ptrCast([*:0]u8, name_bytes.ptr + name_offset)); |
| 740 | if (mem.eql(u8, name, "/names")) { |
| 741 | break :str_tab_index name_index; |
| 742 | } |
| 741 | 743 | } |
| 742 | | } |
| 743 | | return error.MissingDebugInfo; |
| 744 | | }; |
| 744 | return error.MissingDebugInfo; |
| 745 | }; |
| 745 | 746 | |
| 746 | | 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; |
| 747 | di.pdb.string_table = di.pdb.getStreamById(string_table_index) 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 | | 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 | | return error.UnknownPDBVersion; |
| 755 | | if (dbi_stream_header.Age != age) |
| 756 | | return error.UnmatchingPDB; |
| 752 | // Dbi Header |
| 753 | const dbi_stream_header = try dbi.inStream().readStruct(pdb.DbiStreamHeader); |
| 754 | if (dbi_stream_header.VersionHeader != 19990903) // V70, only value observed by LLVM team |
| 755 | return error.UnknownPDBVersion; |
| 756 | if (dbi_stream_header.Age != age) |
| 757 | return error.UnmatchingPDB; |
| 757 | 758 | |
| 758 | | const mod_info_size = dbi_stream_header.ModInfoSize; |
| 759 | | const section_contrib_size = dbi_stream_header.SectionContributionSize; |
| 759 | const mod_info_size = dbi_stream_header.ModInfoSize; |
| 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 | | var mod_info_offset: usize = 0; |
| 765 | | while (mod_info_offset != mod_info_size) { |
| 766 | | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); |
| 767 | | var this_record_len: usize = @sizeOf(pdb.ModInfo); |
| 764 | // Module Info Substream |
| 765 | var mod_info_offset: usize = 0; |
| 766 | while (mod_info_offset != mod_info_size) { |
| 767 | const mod_info = try dbi.inStream().readStruct(pdb.ModInfo); |
| 768 | var this_record_len: usize = @sizeOf(pdb.ModInfo); |
| 768 | 769 | |
| 769 | | const module_name = try dbi.readNullTermString(allocator); |
| 770 | | this_record_len += module_name.len + 1; |
| 770 | const module_name = try dbi.readNullTermString(allocator); |
| 771 | this_record_len += module_name.len + 1; |
| 771 | 772 | |
| 772 | | const obj_file_name = try dbi.readNullTermString(allocator); |
| 773 | | this_record_len += obj_file_name.len + 1; |
| 773 | const obj_file_name = try dbi.readNullTermString(allocator); |
| 774 | this_record_len += obj_file_name.len + 1; |
| 774 | 775 | |
| 775 | | if (this_record_len % 4 != 0) { |
| 776 | | const round_to_next_4 = (this_record_len | 0x3) + 1; |
| 777 | | const march_forward_bytes = round_to_next_4 - this_record_len; |
| 778 | | try dbi.seekBy(@intCast(isize, march_forward_bytes)); |
| 779 | | this_record_len += march_forward_bytes; |
| 780 | | } |
| 776 | if (this_record_len % 4 != 0) { |
| 777 | const round_to_next_4 = (this_record_len | 0x3) + 1; |
| 778 | const march_forward_bytes = round_to_next_4 - this_record_len; |
| 779 | try dbi.seekBy(@intCast(isize, march_forward_bytes)); |
| 780 | this_record_len += march_forward_bytes; |
| 781 | } |
| 781 | 782 | |
| 782 | | try modules.append(Module{ |
| 783 | | .mod_info = mod_info, |
| 784 | | .module_name = module_name, |
| 785 | | .obj_file_name = obj_file_name, |
| 783 | try modules.append(Module{ |
| 784 | .mod_info = mod_info, |
| 785 | .module_name = module_name, |
| 786 | .obj_file_name = obj_file_name, |
| 786 | 787 | |
| 787 | | .populated = false, |
| 788 | | .symbols = undefined, |
| 789 | | .subsect_info = undefined, |
| 790 | | .checksum_offset = null, |
| 791 | | }); |
| 788 | .populated = false, |
| 789 | .symbols = undefined, |
| 790 | .subsect_info = undefined, |
| 791 | .checksum_offset = null, |
| 792 | }); |
| 792 | 793 | |
| 793 | | mod_info_offset += this_record_len; |
| 794 | | if (mod_info_offset > mod_info_size) |
| 795 | | return error.InvalidDebugInfo; |
| 796 | | } |
| 794 | mod_info_offset += this_record_len; |
| 795 | if (mod_info_offset > mod_info_size) |
| 796 | return error.InvalidDebugInfo; |
| 797 | } |
| 797 | 798 | |
| 798 | | di.modules = modules.toOwnedSlice(); |
| 799 | di.modules = modules.toOwnedSlice(); |
| 799 | 800 | |
| 800 | | // Section Contribution Substream |
| 801 | | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); |
| 802 | | var sect_cont_offset: usize = 0; |
| 803 | | if (section_contrib_size != 0) { |
| 804 | | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); |
| 805 | | if (ver != pdb.SectionContrSubstreamVersion.Ver60) |
| 806 | | return error.InvalidDebugInfo; |
| 807 | | sect_cont_offset += @sizeOf(u32); |
| 808 | | } |
| 809 | | while (sect_cont_offset != section_contrib_size) { |
| 810 | | const entry = try sect_contribs.addOne(); |
| 811 | | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); |
| 812 | | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); |
| 801 | // Section Contribution Substream |
| 802 | var sect_contribs = ArrayList(pdb.SectionContribEntry).init(allocator); |
| 803 | var sect_cont_offset: usize = 0; |
| 804 | if (section_contrib_size != 0) { |
| 805 | const ver = @intToEnum(pdb.SectionContrSubstreamVersion, try dbi.inStream().readIntLittle(u32)); |
| 806 | if (ver != pdb.SectionContrSubstreamVersion.Ver60) |
| 807 | return error.InvalidDebugInfo; |
| 808 | sect_cont_offset += @sizeOf(u32); |
| 809 | } |
| 810 | while (sect_cont_offset != section_contrib_size) { |
| 811 | const entry = try sect_contribs.addOne(); |
| 812 | entry.* = try dbi.inStream().readStruct(pdb.SectionContribEntry); |
| 813 | sect_cont_offset += @sizeOf(pdb.SectionContribEntry); |
| 813 | 814 | |
| 814 | | if (sect_cont_offset > section_contrib_size) |
| 815 | | return error.InvalidDebugInfo; |
| 816 | | } |
| 815 | if (sect_cont_offset > section_contrib_size) |
| 816 | return error.InvalidDebugInfo; |
| 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 | 825 | fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize { |
| ... | ... | @@ -1476,151 +1478,153 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1476 | 1478 | } |
| 1477 | 1479 | |
| 1478 | 1480 | fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo { |
| 1479 | | // Translate the VA into an address into this object |
| 1480 | | const relocated_address = address - self.base_address; |
| 1481 | noasync { |
| 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; |
| 1483 | | const mod_index = for (self.sect_contribs) |sect_contrib| { |
| 1484 | | if (sect_contrib.Section > self.coff.sections.len) continue; |
| 1485 | | // Remember that SectionContribEntry.Section is 1-based. |
| 1486 | | coff_section = &self.coff.sections.toSlice()[sect_contrib.Section - 1]; |
| 1501 | const mod = &self.modules[mod_index]; |
| 1502 | try populateModule(self, mod); |
| 1503 | const obj_basename = fs.path.basename(mod.obj_file_name); |
| 1487 | 1504 | |
| 1488 | | const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset; |
| 1489 | | const vaddr_end = vaddr_start + sect_contrib.Size; |
| 1490 | | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { |
| 1491 | | break sect_contrib.ModuleIndex; |
| 1492 | | } |
| 1493 | | } else { |
| 1494 | | // we have no information to add to the address |
| 1495 | | return SymbolInfo{}; |
| 1496 | | }; |
| 1505 | var symbol_i: usize = 0; |
| 1506 | const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { |
| 1507 | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); |
| 1508 | if (prefix.RecordLen < 2) |
| 1509 | return error.InvalidDebugInfo; |
| 1510 | switch (prefix.RecordKind) { |
| 1511 | .S_LPROC32, .S_GPROC32 => { |
| 1512 | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); |
| 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]; |
| 1499 | | try populateModule(self, mod); |
| 1500 | | const obj_basename = fs.path.basename(mod.obj_file_name); |
| 1501 | | |
| 1502 | | var symbol_i: usize = 0; |
| 1503 | | const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) { |
| 1504 | | const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]); |
| 1505 | | if (prefix.RecordLen < 2) |
| 1506 | | return error.InvalidDebugInfo; |
| 1507 | | switch (prefix.RecordKind) { |
| 1508 | | .S_LPROC32, .S_GPROC32 => { |
| 1509 | | const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]); |
| 1510 | | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; |
| 1511 | | const vaddr_end = vaddr_start + proc_sym.CodeSize; |
| 1512 | | if (relocated_address >= vaddr_start and relocated_address < vaddr_end) { |
| 1513 | | break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym)); |
| 1514 | | } |
| 1515 | | }, |
| 1516 | | else => {}, |
| 1517 | | } |
| 1518 | | symbol_i += prefix.RecordLen + @sizeOf(u16); |
| 1519 | | if (symbol_i > mod.symbols.len) |
| 1520 | | return error.InvalidDebugInfo; |
| 1521 | | } else "???"; |
| 1522 | | |
| 1523 | | const subsect_info = mod.subsect_info; |
| 1524 | | |
| 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; |
| 1576 | // line_i == 0 would mean that no matching LineNumberEntry was found. |
| 1577 | if (line_i > 0) { |
| 1578 | const subsect_index = checksum_offset + block_hdr.NameIndex; |
| 1579 | const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]); |
| 1580 | const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset; |
| 1581 | try self.pdb.string_table.seekTo(strtab_offset); |
| 1582 | const source_file_name = try self.pdb.string_table.readNullTermString(self.allocator()); |
| 1583 | |
| 1584 | const line_entry_idx = line_i - 1; |
| 1585 | |
| 1586 | const column = if (has_column) blk: { |
| 1587 | const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines; |
| 1588 | const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx; |
| 1589 | const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]); |
| 1590 | break :blk col_num_entry.StartColumn; |
| 1591 | } else 0; |
| 1592 | |
| 1593 | const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry); |
| 1594 | const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]); |
| 1595 | const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags); |
| 1596 | |
| 1597 | break :subsections LineInfo{ |
| 1598 | .allocator = self.allocator(), |
| 1599 | .file_name = source_file_name, |
| 1600 | .line = flags.Start, |
| 1601 | .column = column, |
| 1602 | }; |
| 1570 | 1603 | } |
| 1571 | 1604 | } |
| 1572 | 1605 | |
| 1573 | | // line_i == 0 would mean that no matching LineNumberEntry was found. |
| 1574 | | if (line_i > 0) { |
| 1575 | | const subsect_index = checksum_offset + block_hdr.NameIndex; |
| 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 | | }; |
| 1606 | // Checking that we are not reading garbage after the (possibly) multiple block fragments. |
| 1607 | if (line_index != subsection_end_index) { |
| 1608 | return error.InvalidDebugInfo; |
| 1600 | 1609 | } |
| 1601 | 1610 | } |
| 1611 | }, |
| 1612 | else => {}, |
| 1613 | } |
| 1602 | 1614 | |
| 1603 | | // Checking that we are not reading garbage after the (possibly) multiple block fragments. |
| 1604 | | if (line_index != subsection_end_index) { |
| 1605 | | return error.InvalidDebugInfo; |
| 1606 | | } |
| 1607 | | } |
| 1608 | | }, |
| 1609 | | else => {}, |
| 1615 | if (sect_offset > subsect_info.len) |
| 1616 | return error.InvalidDebugInfo; |
| 1617 | } else { |
| 1618 | break :subsections null; |
| 1610 | 1619 | } |
| 1620 | }; |
| 1611 | 1621 | |
| 1612 | | if (sect_offset > subsect_info.len) |
| 1613 | | return error.InvalidDebugInfo; |
| 1614 | | } else { |
| 1615 | | break :subsections null; |
| 1616 | | } |
| 1617 | | }; |
| 1618 | | |
| 1619 | | return SymbolInfo{ |
| 1620 | | .symbol_name = symbol_name, |
| 1621 | | .compile_unit_name = obj_basename, |
| 1622 | | .line_info = opt_line_info, |
| 1623 | | }; |
| 1622 | return SymbolInfo{ |
| 1623 | .symbol_name = symbol_name, |
| 1624 | .compile_unit_name = obj_basename, |
| 1625 | .line_info = opt_line_info, |
| 1626 | }; |
| 1627 | } |
| 1624 | 1628 | } |
| 1625 | 1629 | }, |
| 1626 | 1630 | .linux, .netbsd, .freebsd, .dragonfly => struct { |