| ... | @@ -92,65 +92,6 @@ const Module = struct { | ... | @@ -92,65 +92,6 @@ const Module = struct { |
| 92 | obj_file_name: []u8, | 92 | obj_file_name: []u8, |
| 93 | }; | 93 | }; |
| 94 | | 94 | |
| 95 | /// Distinguishes individual records in the Symbols subsection of a .debug$S | | |
| 96 | /// section. Equivalent to SYM_ENUM_e in cvinfo.h. | | |
| 97 | pub const SymbolRecordKind = enum(u16) { | | |
| 98 | InlineesSym = 4456, | | |
| 99 | ScopeEndSym = 6, | | |
| 100 | InlineSiteEnd = 4430, | | |
| 101 | ProcEnd = 4431, | | |
| 102 | Thunk32Sym = 4354, | | |
| 103 | TrampolineSym = 4396, | | |
| 104 | SectionSym = 4406, | | |
| 105 | CoffGroupSym = 4407, | | |
| 106 | ExportSym = 4408, | | |
| 107 | ProcSym = 4367, | | |
| 108 | GlobalProcSym = 4368, | | |
| 109 | ProcIdSym = 4422, | | |
| 110 | GlobalProcIdSym = 4423, | | |
| 111 | DPCProcSym = 4437, | | |
| 112 | DPCProcIdSym = 4438, | | |
| 113 | RegisterSym = 4358, | | |
| 114 | PublicSym32 = 4366, | | |
| 115 | ProcRefSym = 4389, | | |
| 116 | LocalProcRef = 4391, | | |
| 117 | EnvBlockSym = 4413, | | |
| 118 | InlineSiteSym = 4429, | | |
| 119 | LocalSym = 4414, | | |
| 120 | DefRangeSym = 4415, | | |
| 121 | DefRangeSubfieldSym = 4416, | | |
| 122 | DefRangeRegisterSym = 4417, | | |
| 123 | DefRangeFramePointerRelSym = 4418, | | |
| 124 | DefRangeSubfieldRegisterSym = 4419, | | |
| 125 | DefRangeFramePointerRelFullScopeSym = 4420, | | |
| 126 | DefRangeRegisterRelSym = 4421, | | |
| 127 | BlockSym = 4355, | | |
| 128 | LabelSym = 4357, | | |
| 129 | ObjNameSym = 4353, | | |
| 130 | Compile2Sym = 4374, | | |
| 131 | Compile3Sym = 4412, | | |
| 132 | FrameProcSym = 4114, | | |
| 133 | CallSiteInfoSym = 4409, | | |
| 134 | FileStaticSym = 4435, | | |
| 135 | HeapAllocationSiteSym = 4446, | | |
| 136 | FrameCookieSym = 4410, | | |
| 137 | CallerSym = 4442, | | |
| 138 | CalleeSym = 4443, | | |
| 139 | UDTSym = 4360, | | |
| 140 | CobolUDT = 4361, | | |
| 141 | BuildInfoSym = 4428, | | |
| 142 | BPRelativeSym = 4363, | | |
| 143 | RegRelativeSym = 4369, | | |
| 144 | ConstantSym = 4359, | | |
| 145 | ManagedConstant = 4397, | | |
| 146 | DataSym = 4364, | | |
| 147 | GlobalData = 4365, | | |
| 148 | ManagedLocalData = 4380, | | |
| 149 | ManagedGlobalData = 4381, | | |
| 150 | ThreadLocalDataSym = 4370, | | |
| 151 | GlobalTLS = 4371, | | |
| 152 | }; | | |
| 153 | | | |
| 154 | /// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful | 95 | /// Duplicate copy of SymbolRecordKind, but using the official CV names. Useful |
| 155 | /// for reference purposes and when dealing with unknown record types. | 96 | /// for reference purposes and when dealing with unknown record types. |
| 156 | pub const SymbolKind = packed enum(u16) { | 97 | pub const SymbolKind = packed enum(u16) { |
| ... | @@ -434,6 +375,32 @@ const ColumnNumberEntry = packed struct { | ... | @@ -434,6 +375,32 @@ const ColumnNumberEntry = packed struct { |
| 434 | EndColumn: u16, | 375 | EndColumn: u16, |
| 435 | }; | 376 | }; |
| 436 | | 377 | |
| | 378 | /// Checksum bytes follow. |
| | 379 | const FileChecksumEntryHeader = packed struct { |
| | 380 | FileNameOffset: u32, /// Byte offset of filename in global string table. |
| | 381 | ChecksumSize: u8, /// Number of bytes of checksum. |
| | 382 | ChecksumKind: u8, /// FileChecksumKind |
| | 383 | }; |
| | 384 | |
| | 385 | const DebugSubsectionKind = packed enum(u32) { |
| | 386 | None = 0, |
| | 387 | Symbols = 0xf1, |
| | 388 | Lines = 0xf2, |
| | 389 | StringTable = 0xf3, |
| | 390 | FileChecksums = 0xf4, |
| | 391 | FrameData = 0xf5, |
| | 392 | InlineeLines = 0xf6, |
| | 393 | CrossScopeImports = 0xf7, |
| | 394 | CrossScopeExports = 0xf8, |
| | 395 | |
| | 396 | // These appear to relate to .Net assembly info. |
| | 397 | ILLines = 0xf9, |
| | 398 | FuncMDTokenMap = 0xfa, |
| | 399 | TypeMDTokenMap = 0xfb, |
| | 400 | MergedAssemblyInput = 0xfc, |
| | 401 | |
| | 402 | CoffSymbolRVA = 0xfd, |
| | 403 | }; |
| 437 | | 404 | |
| 438 | pub const Pdb = struct { | 405 | pub const Pdb = struct { |
| 439 | in_file: os.File, | 406 | in_file: os.File, |
| ... | @@ -569,14 +536,17 @@ pub const Pdb = struct { | ... | @@ -569,14 +536,17 @@ pub const Pdb = struct { |
| 569 | const prefix = @ptrCast(*RecordPrefix, &symbols[symbol_i]); | 536 | const prefix = @ptrCast(*RecordPrefix, &symbols[symbol_i]); |
| 570 | if (prefix.RecordLen < 2) | 537 | if (prefix.RecordLen < 2) |
| 571 | return error.InvalidDebugInfo; | 538 | return error.InvalidDebugInfo; |
| 572 | if (prefix.RecordKind == SymbolKind.S_LPROC32) { | 539 | switch (prefix.RecordKind) { |
| 573 | const proc_sym = @ptrCast(*ProcSym, &symbols[symbol_i + @sizeOf(RecordPrefix)]); | 540 | SymbolKind.S_LPROC32 => { |
| 574 | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; | 541 | const proc_sym = @ptrCast(*ProcSym, &symbols[symbol_i + @sizeOf(RecordPrefix)]); |
| 575 | const vaddr_end = vaddr_start + proc_sym.CodeSize; | 542 | const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset; |
| 576 | std.debug.warn(" {}\n", proc_sym); | 543 | const vaddr_end = vaddr_start + proc_sym.CodeSize; |
| 577 | if (address >= vaddr_start and address < vaddr_end) { | 544 | std.debug.warn(" {}\n", proc_sym); |
| 578 | break proc_sym; | 545 | if (address >= vaddr_start and address < vaddr_end) { |
| 579 | } | 546 | break proc_sym; |
| | 547 | } |
| | 548 | }, |
| | 549 | else => {}, |
| 580 | } | 550 | } |
| 581 | symbol_i += prefix.RecordLen + @sizeOf(u16); | 551 | symbol_i += prefix.RecordLen + @sizeOf(u16); |
| 582 | if (symbol_i > symbols.len) | 552 | if (symbol_i > symbols.len) |
| ... | @@ -592,73 +562,94 @@ pub const Pdb = struct { | ... | @@ -592,73 +562,94 @@ pub const Pdb = struct { |
| 592 | return error.MissingDebugInfo; | 562 | return error.MissingDebugInfo; |
| 593 | } | 563 | } |
| 594 | | 564 | |
| 595 | const line_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize); | 565 | const subsect_info = try self.allocator.alloc(u8, mod.mod_info.C13ByteSize); |
| 596 | std.debug.warn("read C13 line info {} bytes\n", line_info.len); | 566 | std.debug.warn("read C13 line info {} bytes\n", subsect_info.len); |
| 597 | try modi.stream.readNoEof(line_info); | 567 | const line_info_file_pos = modi.getFilePos(); |
| 598 | | 568 | try modi.stream.readNoEof(subsect_info); |
| 599 | var line_index: usize = 0; | 569 | |
| 600 | while (line_index != line_info.len) { | 570 | const DebugSubsectionHeader = packed struct { |
| 601 | std.debug.warn("unknown bytes: {x2} {x2} {x2} {x2} {x2} {x2} {x2} {x2}\n", | 571 | Kind: DebugSubsectionKind, /// codeview::DebugSubsectionKind enum |
| 602 | line_info[line_index+0], | 572 | Length: u32, /// number of bytes occupied by this record. |
| 603 | line_info[line_index+1], | 573 | }; |
| 604 | line_info[line_index+2], | 574 | var sect_offset: usize = 0; |
| 605 | line_info[line_index+3], | 575 | var skip_len: usize = undefined; |
| 606 | line_info[line_index+4], | 576 | var have_line_info: bool = false; |
| 607 | line_info[line_index+5], | 577 | subsections: while (sect_offset != subsect_info.len) : (sect_offset += skip_len) { |
| 608 | line_info[line_index+6], | 578 | const subsect_hdr = @ptrCast(*DebugSubsectionHeader, &subsect_info[sect_offset]); |
| 609 | line_info[line_index+7], | 579 | skip_len = subsect_hdr.Length; |
| 610 | ); | 580 | sect_offset += @sizeOf(DebugSubsectionHeader); |
| 611 | line_index += 8; | 581 | |
| 612 | | 582 | switch (subsect_hdr.Kind) { |
| 613 | const line_hdr = @ptrCast(*LineFragmentHeader, &line_info[line_index]); | 583 | DebugSubsectionKind.Lines => { |
| 614 | if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; | 584 | if (have_line_info) |
| 615 | std.debug.warn("{}\n", line_hdr); | 585 | continue :subsections; |
| 616 | line_index += @sizeOf(LineFragmentHeader); | 586 | |
| 617 | | 587 | var line_index: usize = sect_offset; |
| 618 | const block_hdr = @ptrCast(*LineBlockFragmentHeader, &line_info[line_index]); | 588 | |
| 619 | std.debug.warn("{}\n", block_hdr); | 589 | const line_hdr = @ptrCast(*LineFragmentHeader, &subsect_info[line_index]); |
| 620 | line_index += @sizeOf(LineBlockFragmentHeader); | 590 | if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo; |
| 621 | | 591 | std.debug.warn("{}\n", line_hdr); |
| 622 | const has_column = line_hdr.Flags.LF_HaveColumns; | 592 | line_index += @sizeOf(LineFragmentHeader); |
| 623 | std.debug.warn("has column: {}\n", has_column); | 593 | |
| 624 | | 594 | const block_hdr = @ptrCast(*LineBlockFragmentHeader, &subsect_info[line_index]); |
| 625 | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; | 595 | std.debug.warn("{}\n", block_hdr); |
| 626 | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; | 596 | line_index += @sizeOf(LineBlockFragmentHeader); |
| 627 | if (address >= frag_vaddr_start and address < frag_vaddr_end) { | 597 | |
| 628 | std.debug.warn("found line listing\n"); | 598 | const has_column = line_hdr.Flags.LF_HaveColumns; |
| 629 | var line_i: usize = 0; | 599 | std.debug.warn("has column: {}\n", has_column); |
| 630 | const start_line_index = line_index; | 600 | |
| 631 | while (line_i < block_hdr.NumLines) : (line_i += 1) { | 601 | const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset; |
| 632 | const line_num_entry = @ptrCast(*LineNumberEntry, &line_info[line_index]); | 602 | const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize; |
| 633 | line_index += @sizeOf(LineNumberEntry); | 603 | if (address >= frag_vaddr_start and address < frag_vaddr_end) { |
| 634 | const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); | 604 | std.debug.warn("found line listing\n"); |
| 635 | std.debug.warn("{} {}\n", line_num_entry, flags); | 605 | var line_i: usize = 0; |
| 636 | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; | 606 | const start_line_index = line_index; |
| 637 | const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; | 607 | while (line_i < block_hdr.NumLines) : (line_i += 1) { |
| 638 | std.debug.warn("test {x} <= {x} < {x}\n", vaddr_start, address, vaddr_end); | 608 | const line_num_entry = @ptrCast(*LineNumberEntry, &subsect_info[line_index]); |
| 639 | if (address >= vaddr_start and address < vaddr_end) { | 609 | line_index += @sizeOf(LineNumberEntry); |
| 640 | std.debug.warn("{} line {}\n", block_hdr.NameIndex, flags.Start); | 610 | const flags = @ptrCast(*LineNumberEntry.Flags, &line_num_entry.Flags); |
| 641 | if (has_column) { | 611 | std.debug.warn("{} {}\n", line_num_entry, flags); |
| 642 | line_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines; | 612 | const vaddr_start = frag_vaddr_start + line_num_entry.Offset; |
| 643 | line_index += @sizeOf(ColumnNumberEntry) * line_i; | 613 | const vaddr_end = if (flags.End == 0) frag_vaddr_end else vaddr_start + flags.End; |
| 644 | const col_num_entry = @ptrCast(*ColumnNumberEntry, &line_info[line_index]); | 614 | std.debug.warn("test {x} <= {x} < {x}\n", vaddr_start, address, vaddr_end); |
| 645 | std.debug.warn("col {}\n", col_num_entry.StartColumn); | 615 | if (address >= vaddr_start and address < vaddr_end) { |
| | 616 | std.debug.warn("{} line {}\n", block_hdr.NameIndex, flags.Start); |
| | 617 | if (has_column) { |
| | 618 | line_index = start_line_index + @sizeOf(LineNumberEntry) * block_hdr.NumLines; |
| | 619 | line_index += @sizeOf(ColumnNumberEntry) * line_i; |
| | 620 | const col_num_entry = @ptrCast(*ColumnNumberEntry, &subsect_info[line_index]); |
| | 621 | std.debug.warn("col {}\n", col_num_entry.StartColumn); |
| | 622 | } |
| | 623 | have_line_info = true; |
| | 624 | continue :subsections; |
| | 625 | } |
| 646 | } | 626 | } |
| 647 | return; | 627 | return error.MissingDebugInfo; |
| 648 | } | 628 | } |
| 649 | } | 629 | |
| 650 | return error.MissingDebugInfo; | 630 | }, |
| 651 | } else { | 631 | DebugSubsectionKind.FileChecksums => { |
| 652 | line_index += @sizeOf(LineNumberEntry) * block_hdr.NumLines; | 632 | var chksum_index: usize = sect_offset; |
| 653 | if (has_column) | 633 | |
| 654 | line_index += @sizeOf(ColumnNumberEntry) * block_hdr.NumLines; | 634 | while (chksum_index != subsect_info.len) { |
| | 635 | const chksum_hdr = @ptrCast(*FileChecksumEntryHeader, &subsect_info[chksum_index]); |
| | 636 | std.debug.warn("{}\n", chksum_hdr); |
| | 637 | const len = @sizeOf(FileChecksumEntryHeader) + chksum_hdr.ChecksumSize; |
| | 638 | chksum_index += len + (len % 4); |
| | 639 | if (chksum_index > subsect_info.len) |
| | 640 | return error.InvalidDebugInfo; |
| | 641 | } |
| | 642 | |
| | 643 | }, |
| | 644 | else => { |
| | 645 | std.debug.warn("ignore subsection {}\n", @tagName(subsect_hdr.Kind)); |
| | 646 | }, |
| 655 | } | 647 | } |
| 656 | | 648 | |
| 657 | if (line_index > mod.mod_info.C13ByteSize) | 649 | if (sect_offset > subsect_info.len) |
| 658 | return error.InvalidDebugInfo; | 650 | return error.InvalidDebugInfo; |
| 659 | } | 651 | } |
| 660 | | 652 | std.debug.warn("end subsections\n"); |
| 661 | std.debug.warn("end line info\n"); | | |
| 662 | } | 653 | } |
| 663 | }; | 654 | }; |
| 664 | | 655 | |