authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-26 11:10:38-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-02-26 11:10:38-05:00
logaa2aad229c96427f8b9130a3665a1f6ad768ec4c
treea8de0b136884e9672ddaaf0d16ec53a0dbcd1967
parenta55e5363917befcb93575c256a3ce8fc150e0666
parent08047cd6d710603a5391c9e88a7369f5dcfa196f
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4497 from LemonBoy/do-do-do

The great stack-trace race (Part 1 of N)

5 files changed, 2204 insertions(+), 1926 deletions(-)

lib/std/debug.zig+603-1226
...@@ -38,6 +38,18 @@ const Module = struct {...@@ -38,6 +38,18 @@ const Module = struct {
38 checksum_offset: ?usize,38 checksum_offset: ?usize,
39};39};
4040
41pub const LineInfo = struct {
42 line: u64,
43 column: u64,
44 file_name: []const u8,
45 allocator: ?*mem.Allocator,
46
47 fn deinit(self: LineInfo) void {
48 const allocator = self.allocator orelse return;
49 allocator.free(self.file_name);
50 }
51};
52
41/// Tries to write to stderr, unbuffered, and ignores any error returned.53/// Tries to write to stderr, unbuffered, and ignores any error returned.
42/// Does not append a newline.54/// Does not append a newline.
43var stderr_file: File = undefined;55var stderr_file: File = undefined;
...@@ -378,175 +390,6 @@ pub fn writeCurrentStackTraceWindows(...@@ -378,175 +390,6 @@ pub fn writeCurrentStackTraceWindows(
378 }390 }
379}391}
380392
381/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
382/// make this `noasync fn` and remove the individual noasync calls.
383pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
384 if (builtin.os == .windows) {
385 return noasync printSourceAtAddressWindows(debug_info, out_stream, address, tty_config);
386 }
387 if (comptime std.Target.current.isDarwin()) {
388 return noasync printSourceAtAddressMacOs(debug_info, out_stream, address, tty_config);
389 }
390 return noasync printSourceAtAddressPosix(debug_info, out_stream, address, tty_config);
391}
392
393/// TODO resources https://github.com/ziglang/zig/issues/4353
394fn printSourceAtAddressWindows(
395 di: *DebugInfo,
396 out_stream: var,
397 relocated_address: usize,
398 tty_config: TTY.Config,
399) !void {
400 const allocator = getDebugInfoAllocator();
401 const base_address = process.getBaseAddress();
402 const relative_address = relocated_address - base_address;
403
404 var coff_section: *coff.Section = undefined;
405 const mod_index = for (di.sect_contribs) |sect_contrib| {
406 if (sect_contrib.Section > di.coff.sections.len) continue;
407 // Remember that SectionContribEntry.Section is 1-based.
408 coff_section = &di.coff.sections.toSlice()[sect_contrib.Section - 1];
409
410 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
411 const vaddr_end = vaddr_start + sect_contrib.Size;
412 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
413 break sect_contrib.ModuleIndex;
414 }
415 } else {
416 // we have no information to add to the address
417 return printLineInfo(out_stream, null, relocated_address, "???", "???", tty_config, printLineFromFileAnyOs);
418 };
419
420 const mod = &di.modules[mod_index];
421 try populateModule(di, mod);
422 const obj_basename = fs.path.basename(mod.obj_file_name);
423
424 var symbol_i: usize = 0;
425 const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) {
426 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
427 if (prefix.RecordLen < 2)
428 return error.InvalidDebugInfo;
429 switch (prefix.RecordKind) {
430 .S_LPROC32, .S_GPROC32 => {
431 const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
432 const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
433 const vaddr_end = vaddr_start + proc_sym.CodeSize;
434 if (relative_address >= vaddr_start and relative_address < vaddr_end) {
435 break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym));
436 }
437 },
438 else => {},
439 }
440 symbol_i += prefix.RecordLen + @sizeOf(u16);
441 if (symbol_i > mod.symbols.len)
442 return error.InvalidDebugInfo;
443 } else "???";
444
445 const subsect_info = mod.subsect_info;
446
447 var sect_offset: usize = 0;
448 var skip_len: usize = undefined;
449 const opt_line_info = subsections: {
450 const checksum_offset = mod.checksum_offset orelse break :subsections null;
451 while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
452 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
453 skip_len = subsect_hdr.Length;
454 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
455
456 switch (subsect_hdr.Kind) {
457 pdb.DebugSubsectionKind.Lines => {
458 var line_index = sect_offset;
459
460 const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
461 if (line_hdr.RelocSegment == 0) return error.MissingDebugInfo;
462 line_index += @sizeOf(pdb.LineFragmentHeader);
463 const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
464 const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
465
466 if (relative_address >= frag_vaddr_start and relative_address < frag_vaddr_end) {
467 // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
468 // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
469 // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
470 const subsection_end_index = sect_offset + subsect_hdr.Length;
471
472 while (line_index < subsection_end_index) {
473 const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
474 line_index += @sizeOf(pdb.LineBlockFragmentHeader);
475 const start_line_index = line_index;
476
477 const has_column = line_hdr.Flags.LF_HaveColumns;
478
479 // All line entries are stored inside their line block by ascending start address.
480 // Heuristic: we want to find the last line entry that has a vaddr_start <= relative_address.
481 // This is done with a simple linear search.
482 var line_i: u32 = 0;
483 while (line_i < block_hdr.NumLines) : (line_i += 1) {
484 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
485 line_index += @sizeOf(pdb.LineNumberEntry);
486
487 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
488 if (relative_address < vaddr_start) {
489 break;
490 }
491 }
492
493 // line_i == 0 would mean that no matching LineNumberEntry was found.
494 if (line_i > 0) {
495 const subsect_index = checksum_offset + block_hdr.NameIndex;
496 const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
497 const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
498 try di.pdb.string_table.seekTo(strtab_offset);
499 const source_file_name = try di.pdb.string_table.readNullTermString(allocator);
500
501 const line_entry_idx = line_i - 1;
502
503 const column = if (has_column) blk: {
504 const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
505 const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx;
506 const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]);
507 break :blk col_num_entry.StartColumn;
508 } else 0;
509
510 const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry);
511 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]);
512 const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
513
514 break :subsections LineInfo{
515 .allocator = allocator,
516 .file_name = source_file_name,
517 .line = flags.Start,
518 .column = column,
519 };
520 }
521 }
522
523 // Checking that we are not reading garbage after the (possibly) multiple block fragments.
524 if (line_index != subsection_end_index) {
525 return error.InvalidDebugInfo;
526 }
527 }
528 },
529 else => {},
530 }
531
532 if (sect_offset > subsect_info.len)
533 return error.InvalidDebugInfo;
534 } else {
535 break :subsections null;
536 }
537 };
538
539 try printLineInfo(
540 out_stream,
541 opt_line_info,
542 relocated_address,
543 symbol_name,
544 obj_basename,
545 tty_config,
546 printLineFromFileAnyOs,
547 );
548}
549
550pub const TTY = struct {393pub const TTY = struct {
551 pub const Color = enum {394 pub const Color = enum {
552 Red,395 Red,
...@@ -618,7 +461,7 @@ pub const TTY = struct {...@@ -618,7 +461,7 @@ pub const TTY = struct {
618};461};
619462
620/// TODO resources https://github.com/ziglang/zig/issues/4353463/// TODO resources https://github.com/ziglang/zig/issues/4353
621fn populateModule(di: *DebugInfo, mod: *Module) !void {464fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
622 if (mod.populated)465 if (mod.populated)
623 return;466 return;
624 const allocator = getDebugInfoAllocator();467 const allocator = getDebugInfoAllocator();
...@@ -650,7 +493,7 @@ fn populateModule(di: *DebugInfo, mod: *Module) !void {...@@ -650,7 +493,7 @@ fn populateModule(di: *DebugInfo, mod: *Module) !void {
650 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);493 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
651494
652 switch (subsect_hdr.Kind) {495 switch (subsect_hdr.Kind) {
653 pdb.DebugSubsectionKind.FileChecksums => {496 .FileChecksums => {
654 mod.checksum_offset = sect_offset;497 mod.checksum_offset = sect_offset;
655 break;498 break;
656 },499 },
...@@ -682,41 +525,37 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach...@@ -682,41 +525,37 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach
682 return null;525 return null;
683}526}
684527
685fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {528/// TODO resources https://github.com/ziglang/zig/issues/4353
686 const base_addr = process.getBaseAddress();529pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
687 const adjusted_addr = 0x100000000 + (address - base_addr);530 const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
688531 error.MissingDebugInfo, error.InvalidDebugInfo => {
689 const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {532 return printLineInfo(
690 return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs);533 out_stream,
691 };534 null,
692535 address,
693 const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx));536 "???",
694 const compile_unit_name = if (symbol.ofile) |ofile| blk: {537 "???",
695 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));538 tty_config,
696 break :blk fs.path.basename(ofile_path);539 printLineFromFileAnyOs,
697 } else "???";540 );
698541 },
699 const line_info = getLineNumberInfoMacOs(di, symbol.*, adjusted_addr) catch |err| switch (err) {
700 error.MissingDebugInfo, error.InvalidDebugInfo => null,
701 else => return err,542 else => return err,
702 };543 };
703 defer if (line_info) |li| li.deinit();
704544
705 try printLineInfo(545 const symbol_info = try module.getSymbolAtAddress(address);
546 defer symbol_info.deinit();
547
548 return printLineInfo(
706 out_stream,549 out_stream,
707 line_info,550 symbol_info.line_info,
708 address,551 address,
709 symbol_name,552 symbol_info.symbol_name,
710 compile_unit_name,553 symbol_info.compile_unit_name,
711 tty_config,554 tty_config,
712 printLineFromFileAnyOs,555 printLineFromFileAnyOs,
713 );556 );
714}557}
715558
716pub fn printSourceAtAddressPosix(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
717 return debug_info.printSourceAtAddress(out_stream, address, tty_config, printLineFromFileAnyOs);
718}
719
720fn printLineInfo(559fn printLineInfo(
721 out_stream: var,560 out_stream: var,
722 line_info: ?LineInfo,561 line_info: ?LineInfo,
...@@ -772,29 +611,32 @@ pub const OpenSelfDebugInfoError = error{...@@ -772,29 +611,32 @@ pub const OpenSelfDebugInfoError = error{
772/// TODO resources https://github.com/ziglang/zig/issues/4353611/// TODO resources https://github.com/ziglang/zig/issues/4353
773/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,612/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
774/// make this `noasync fn` and remove the individual noasync calls.613/// make this `noasync fn` and remove the individual noasync calls.
775pub fn openSelfDebugInfo(allocator: *mem.Allocator) !DebugInfo {614pub fn openSelfDebugInfo(allocator: *mem.Allocator) anyerror!DebugInfo {
776 if (builtin.strip_debug_info)615 if (builtin.strip_debug_info)
777 return error.MissingDebugInfo;616 return error.MissingDebugInfo;
778 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {617 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
779 return noasync root.os.debug.openSelfDebugInfo(allocator);618 return noasync root.os.debug.openSelfDebugInfo(allocator);
780 }619 }
781 if (builtin.os == .windows) {620 switch (builtin.os) {
782 return noasync openSelfDebugInfoWindows(allocator);621 .linux,
783 }622 .freebsd,
784 if (comptime std.Target.current.isDarwin()) {623 .macosx,
785 return noasync openSelfDebugInfoMacOs(allocator);624 .windows,
625 => return DebugInfo.init(allocator),
626 else => @compileError("openSelfDebugInfo unsupported for this platform"),
786 }627 }
787 return noasync openSelfDebugInfoPosix(allocator);
788}628}
789629
790fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {630/// TODO resources https://github.com/ziglang/zig/issues/4353
791 const self_file = try fs.openSelfExe();631fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo {
792 defer self_file.close();632 const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{});
633 errdefer coff_file.close();
793634
794 const coff_obj = try allocator.create(coff.Coff);635 const coff_obj = try allocator.create(coff.Coff);
795 coff_obj.* = coff.Coff.init(allocator, self_file);636 coff_obj.* = coff.Coff.init(allocator, coff_file);
796637
797 var di = DebugInfo{638 var di = ModuleDebugInfo{
639 .base_address = undefined,
798 .coff = coff_obj,640 .coff = coff_obj,
799 .pdb = undefined,641 .pdb = undefined,
800 .sect_contribs = undefined,642 .sect_contribs = undefined,
...@@ -958,36 +800,21 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {...@@ -958,36 +800,21 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
958 return list.toOwnedSlice();800 return list.toOwnedSlice();
959}801}
960802
961fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section {803fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
962 const elf_header = (try elf_file.findSection(name)) orelse return null;804 const start = try math.cast(usize, offset);
963 return DwarfInfo.Section{805 const end = start + try math.cast(usize, size);
964 .offset = elf_header.sh_offset,806 return ptr[start..end];
965 .size = elf_header.sh_size,
966 };
967}
968
969/// Initialize DWARF info. The caller has the responsibility to initialize most
970/// the DwarfInfo fields before calling. These fields can be left undefined:
971/// * abbrev_table_list
972/// * compile_unit_list
973pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
974 di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
975 di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
976 di.func_list = ArrayList(Func).init(allocator);
977 try di.scanAllFunctions();
978 try di.scanAllCompileUnits();
979}807}
980808
981/// TODO resources https://github.com/ziglang/zig/issues/4353809/// TODO resources https://github.com/ziglang/zig/issues/4353
982pub fn openElfDebugInfo(810pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
983 allocator: *mem.Allocator,811 const mapped_mem = try mapWholeFile(elf_file_path);
984 data: []u8,812
985) !DwarfInfo {813 var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);
986 var seekable_stream = io.SliceSeekableInStream.init(data);
987 var efile = try elf.Elf.openStream(814 var efile = try elf.Elf.openStream(
988 allocator,815 allocator,
989 @ptrCast(*DwarfSeekableStream, &seekable_stream.seekable_stream),816 @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),
990 @ptrCast(*DwarfInStream, &seekable_stream.stream),817 @ptrCast(*DW.DwarfInStream, &seekable_stream.stream),
991 );818 );
992 defer efile.close();819 defer efile.close();
993820
...@@ -1001,66 +828,57 @@ pub fn openElfDebugInfo(...@@ -1001,66 +828,57 @@ pub fn openElfDebugInfo(
1001 return error.MissingDebugInfo;828 return error.MissingDebugInfo;
1002 const opt_debug_ranges = try efile.findSection(".debug_ranges");829 const opt_debug_ranges = try efile.findSection(".debug_ranges");
1003830
1004 var di = DwarfInfo{831 var di = DW.DwarfInfo{
1005 .endian = efile.endian,832 .endian = efile.endian,
1006 .debug_info = (data[@intCast(usize, debug_info.sh_offset)..@intCast(usize, debug_info.sh_offset + debug_info.sh_size)]),833 .debug_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size),
1007 .debug_abbrev = (data[@intCast(usize, debug_abbrev.sh_offset)..@intCast(usize, debug_abbrev.sh_offset + debug_abbrev.sh_size)]),834 .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size),
1008 .debug_str = (data[@intCast(usize, debug_str.sh_offset)..@intCast(usize, debug_str.sh_offset + debug_str.sh_size)]),835 .debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size),
1009 .debug_line = (data[@intCast(usize, debug_line.sh_offset)..@intCast(usize, debug_line.sh_offset + debug_line.sh_size)]),836 .debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size),
1010 .debug_ranges = if (opt_debug_ranges) |debug_ranges|837 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
1011 data[@intCast(usize, debug_ranges.sh_offset)..@intCast(usize, debug_ranges.sh_offset + debug_ranges.sh_size)]838 try chopSlice(mapped_mem, debug_ranges.sh_offset, debug_ranges.sh_size)
1012 else839 else
1013 null,840 null,
1014 };841 };
1015842
1016 try openDwarfDebugInfo(&di, allocator);843 try DW.openDwarfDebugInfo(&di, allocator);
1017 return di;844
845 return ModuleDebugInfo{
846 .base_address = undefined,
847 .dwarf = di,
848 .mapped_memory = mapped_mem,
849 };
1018}850}
1019851
1020/// TODO resources https://github.com/ziglang/zig/issues/4353852/// TODO resources https://github.com/ziglang/zig/issues/4353
1021fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo {853fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo {
1022 var exe_file = try fs.openSelfExe();854 const mapped_mem = try mapWholeFile(macho_file_path);
1023 errdefer exe_file.close();
1024855
1025 const exe_len = math.cast(usize, try exe_file.getEndPos()) catch856 const hdr = @ptrCast(
1026 return error.DebugInfoTooLarge;857 *const macho.mach_header_64,
1027 const exe_mmap = try os.mmap(858 @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
1028 null,
1029 exe_len,
1030 os.PROT_READ,
1031 os.MAP_SHARED,
1032 exe_file.handle,
1033 0,
1034 );859 );
1035 errdefer os.munmap(exe_mmap);860 if (hdr.magic != macho.MH_MAGIC_64)
1036861 return error.InvalidDebugInfo;
1037 return openElfDebugInfo(allocator, exe_mmap);
1038}
1039
1040/// TODO resources https://github.com/ziglang/zig/issues/4353
1041fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
1042 const hdr = &std.c._mh_execute_header;
1043 assert(hdr.magic == std.macho.MH_MAGIC_64);
1044862
1045 const hdr_base = @ptrCast([*]u8, hdr);863 const hdr_base = @ptrCast([*]const u8, hdr);
1046 var ptr = hdr_base + @sizeOf(macho.mach_header_64);864 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
1047 var ncmd: u32 = hdr.ncmds;865 var ncmd: u32 = hdr.ncmds;
1048 const symtab = while (ncmd != 0) : (ncmd -= 1) {866 const symtab = while (ncmd != 0) : (ncmd -= 1) {
1049 const lc = @ptrCast(*std.macho.load_command, ptr);867 const lc = @ptrCast(*const std.macho.load_command, ptr);
1050 switch (lc.cmd) {868 switch (lc.cmd) {
1051 std.macho.LC_SYMTAB => break @ptrCast(*std.macho.symtab_command, ptr),869 std.macho.LC_SYMTAB => break @ptrCast(*const std.macho.symtab_command, ptr),
1052 else => {},870 else => {},
1053 }871 }
1054 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);872 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
1055 } else {873 } else {
1056 return error.MissingDebugInfo;874 return error.MissingDebugInfo;
1057 };875 };
1058 const syms = @ptrCast([*]macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];876 const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
1059 const strings = @ptrCast([*]u8, hdr_base + symtab.stroff)[0..symtab.strsize];877 const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize :0];
1060878
1061 const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);879 const symbols_buf = try allocator.alloc(MachoSymbol, syms.len);
1062880
1063 var ofile: ?*macho.nlist_64 = null;881 var ofile: ?*const macho.nlist_64 = null;
1064 var reloc: u64 = 0;882 var reloc: u64 = 0;
1065 var symbol_index: usize = 0;883 var symbol_index: usize = 0;
1066 var last_len: u64 = 0;884 var last_len: u64 = 0;
...@@ -1108,8 +926,10 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {...@@ -1108,8 +926,10 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
1108 // This sort is so that we can binary search later.926 // This sort is so that we can binary search later.
1109 std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);927 std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);
1110928
1111 return DebugInfo{929 return ModuleDebugInfo{
1112 .ofiles = DebugInfo.OFileTable.init(allocator),930 .base_address = undefined,
931 .mapped_memory = mapped_mem,
932 .ofiles = ModuleDebugInfo.OFileTable.init(allocator),
1113 .symbols = symbols,933 .symbols = symbols,
1114 .strings = strings,934 .strings = strings,
1115 };935 };
...@@ -1148,8 +968,8 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {...@@ -1148,8 +968,8 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
1148}968}
1149969
1150const MachoSymbol = struct {970const MachoSymbol = struct {
1151 nlist: *macho.nlist_64,971 nlist: *const macho.nlist_64,
1152 ofile: ?*macho.nlist_64,972 ofile: ?*const macho.nlist_64,
1153 reloc: u64,973 reloc: u64,
1154974
1155 /// Returns the address from the macho file975 /// Returns the address from the macho file
...@@ -1162,1057 +982,614 @@ const MachoSymbol = struct {...@@ -1162,1057 +982,614 @@ const MachoSymbol = struct {
1162 }982 }
1163};983};
1164984
1165pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);985fn mapWholeFile(path: []const u8) ![]const u8 {
1166pub const DwarfInStream = io.InStream(anyerror);986 const file = try fs.openFileAbsolute(path, .{});
1167987 defer file.close();
1168pub const DwarfInfo = struct {
1169 endian: builtin.Endian,
1170 // No memory is owned by the DwarfInfo
1171 debug_info: []u8,
1172 debug_abbrev: []u8,
1173 debug_str: []u8,
1174 debug_line: []u8,
1175 debug_ranges: ?[]u8,
1176 // Filled later by the initializer
1177 abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined,
1178 compile_unit_list: ArrayList(CompileUnit) = undefined,
1179 func_list: ArrayList(Func) = undefined,
1180
1181 pub fn allocator(self: DwarfInfo) *mem.Allocator {
1182 return self.abbrev_table_list.allocator;
1183 }
1184988
1185 /// This function works in freestanding mode.989 const file_len = try math.cast(usize, try file.getEndPos());
1186 /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void990 const mapped_mem = try os.mmap(
1187 pub fn printSourceAtAddress(991 null,
1188 self: *DwarfInfo,992 file_len,
1189 out_stream: var,993 os.PROT_READ,
1190 address: usize,994 os.MAP_SHARED,
1191 tty_config: TTY.Config,995 file.handle,
1192 comptime printLineFromFile: var,996 0,
1193 ) !void {997 );
1194 const compile_unit = self.findCompileUnit(address) catch {998 errdefer os.munmap(mapped_mem);
1195 return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFile);
1196 };
1197999
1198 const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name);1000 return mapped_mem;
1199 const symbol_name = self.getSymbolName(address) orelse "???";1001}
1200 const line_info = self.getLineNumberInfo(compile_unit.*, address) catch |err| switch (err) {
1201 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1202 else => return err,
1203 };
1204 defer if (line_info) |li| li.deinit();
1205
1206 try printLineInfo(
1207 out_stream,
1208 line_info,
1209 address,
1210 symbol_name,
1211 compile_unit_name,
1212 tty_config,
1213 printLineFromFile,
1214 );
1215 }
12161002
1217 fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {1003pub const DebugInfo = struct {
1218 for (di.func_list.toSliceConst()) |*func| {1004 allocator: *mem.Allocator,
1219 if (func.pc_range) |range| {1005 address_map: std.AutoHashMap(usize, *ModuleDebugInfo),
1220 if (address >= range.start and address < range.end) {
1221 return func.name;
1222 }
1223 }
1224 }
12251006
1226 return null;1007 pub fn init(allocator: *mem.Allocator) DebugInfo {
1008 return DebugInfo{
1009 .allocator = allocator,
1010 .address_map = std.AutoHashMap(usize, *ModuleDebugInfo).init(allocator),
1011 };
1227 }1012 }
12281013
1229 fn scanAllFunctions(di: *DwarfInfo) !void {1014 pub fn deinit(self: *DebugInfo) void {
1230 var s = io.SliceSeekableInStream.init(di.debug_info);1015 // TODO: resources https://github.com/ziglang/zig/issues/4353
1231 var this_unit_offset: u64 = 0;1016 self.address_map.deinit();
1232
1233 while (true) {
1234 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
1235 error.EndOfStream => return,
1236 else => return err,
1237 };
1238
1239 var is_64: bool = undefined;
1240 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
1241 if (unit_length == 0) return;
1242 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
1243
1244 const version = try s.stream.readInt(u16, di.endian);
1245 if (version < 2 or version > 5) return error.InvalidDebugInfo;
1246
1247 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
1248
1249 const address_size = try s.stream.readByte();
1250 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
1251
1252 const compile_unit_pos = try s.seekable_stream.getPos();
1253 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
1254
1255 try s.seekable_stream.seekTo(compile_unit_pos);
1256
1257 const next_unit_pos = this_unit_offset + next_offset;
1258
1259 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
1260 const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
1261 const after_die_offset = try s.seekable_stream.getPos();
1262
1263 switch (die_obj.tag_id) {
1264 DW.TAG_subprogram, DW.TAG_inlined_subroutine, DW.TAG_subroutine, DW.TAG_entry_point => {
1265 const fn_name = x: {
1266 var depth: i32 = 3;
1267 var this_die_obj = die_obj;
1268 // Prenvent endless loops
1269 while (depth > 0) : (depth -= 1) {
1270 if (this_die_obj.getAttr(DW.AT_name)) |_| {
1271 const name = try this_die_obj.getAttrString(di, DW.AT_name);
1272 break :x name;
1273 } else if (this_die_obj.getAttr(DW.AT_abstract_origin)) |ref| {
1274 // Follow the DIE it points to and repeat
1275 const ref_offset = try this_die_obj.getAttrRef(DW.AT_abstract_origin);
1276 if (ref_offset > next_offset) return error.InvalidDebugInfo;
1277 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
1278 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
1279 } else if (this_die_obj.getAttr(DW.AT_specification)) |ref| {
1280 // Follow the DIE it points to and repeat
1281 const ref_offset = try this_die_obj.getAttrRef(DW.AT_specification);
1282 if (ref_offset > next_offset) return error.InvalidDebugInfo;
1283 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
1284 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
1285 } else {
1286 break :x null;
1287 }
1288 }
1289
1290 break :x null;
1291 };
1292
1293 const pc_range = x: {
1294 if (die_obj.getAttrAddr(DW.AT_low_pc)) |low_pc| {
1295 if (die_obj.getAttr(DW.AT_high_pc)) |high_pc_value| {
1296 const pc_end = switch (high_pc_value.*) {
1297 FormValue.Address => |value| value,
1298 FormValue.Const => |value| b: {
1299 const offset = try value.asUnsignedLe();
1300 break :b (low_pc + offset);
1301 },
1302 else => return error.InvalidDebugInfo,
1303 };
1304 break :x PcRange{
1305 .start = low_pc,
1306 .end = pc_end,
1307 };
1308 } else {
1309 break :x null;
1310 }
1311 } else |err| {
1312 if (err != error.MissingDebugInfo) return err;
1313 break :x null;
1314 }
1315 };
1316
1317 try di.func_list.append(Func{
1318 .name = fn_name,
1319 .pc_range = pc_range,
1320 });
1321 },
1322 else => {},
1323 }
1324
1325 try s.seekable_stream.seekTo(after_die_offset);
1326 }
1327
1328 this_unit_offset += next_offset;
1329 }
1330 }1017 }
13311018
1332 fn scanAllCompileUnits(di: *DwarfInfo) !void {1019 pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1333 var s = io.SliceSeekableInStream.init(di.debug_info);1020 if (comptime std.Target.current.isDarwin())
1334 var this_unit_offset: u64 = 0;1021 return self.lookupModuleDyld(address)
13351022 else if (builtin.os == .windows)
1336 while (true) {1023 return self.lookupModuleWin32(address)
1337 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {1024 else
1338 error.EndOfStream => return,1025 return self.lookupModuleDl(address);
1339 else => return err,1026 }
1340 };
1341
1342 var is_64: bool = undefined;
1343 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
1344 if (unit_length == 0) return;
1345 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
13461027
1347 const version = try s.stream.readInt(u16, di.endian);1028 fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1348 if (version < 2 or version > 5) return error.InvalidDebugInfo;1029 const image_count = std.c._dyld_image_count();
13491030
1350 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);1031 var i: u32 = 0;
1032 while (i < image_count) : (i += 1) {
1033 const base_address = std.c._dyld_get_image_vmaddr_slide(i);
13511034
1352 const address_size = try s.stream.readByte();1035 if (address < base_address) continue;
1353 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
13541036
1355 const compile_unit_pos = try s.seekable_stream.getPos();1037 const header = std.c._dyld_get_image_header(i) orelse continue;
1356 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);1038 // The array of load commands is right after the header
1039 var cmd_ptr = @intToPtr([*]u8, @ptrToInt(header) + @sizeOf(macho.mach_header_64));
13571040
1358 try s.seekable_stream.seekTo(compile_unit_pos);1041 var cmds = header.ncmds;
1042 while (cmds != 0) : (cmds -= 1) {
1043 const lc = @ptrCast(
1044 *macho.load_command,
1045 @alignCast(@alignOf(macho.load_command), cmd_ptr),
1046 );
1047 cmd_ptr += lc.cmdsize;
1048 if (lc.cmd != macho.LC_SEGMENT_64) continue;
13591049
1360 const compile_unit_die = try di.allocator().create(Die);1050 const segment_cmd = @ptrCast(
1361 compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;1051 *const std.macho.segment_command_64,
1052 @alignCast(@alignOf(std.macho.segment_command_64), lc),
1053 );
13621054
1363 if (compile_unit_die.tag_id != DW.TAG_compile_unit) return error.InvalidDebugInfo;1055 const rebased_address = address - base_address;
1056 const seg_start = segment_cmd.vmaddr;
1057 const seg_end = seg_start + segment_cmd.vmsize;
13641058
1365 const pc_range = x: {1059 if (rebased_address >= seg_start and rebased_address < seg_end) {
1366 if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| {1060 if (self.address_map.getValue(base_address)) |obj_di| {
1367 if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| {1061 return obj_di;
1368 const pc_end = switch (high_pc_value.*) {
1369 FormValue.Address => |value| value,
1370 FormValue.Const => |value| b: {
1371 const offset = try value.asUnsignedLe();
1372 break :b (low_pc + offset);
1373 },
1374 else => return error.InvalidDebugInfo,
1375 };
1376 break :x PcRange{
1377 .start = low_pc,
1378 .end = pc_end,
1379 };
1380 } else {
1381 break :x null;
1382 }1062 }
1383 } else |err| {
1384 if (err != error.MissingDebugInfo) return err;
1385 break :x null;
1386 }
1387 };
1388
1389 try di.compile_unit_list.append(CompileUnit{
1390 .version = version,
1391 .is_64 = is_64,
1392 .pc_range = pc_range,
1393 .die = compile_unit_die,
1394 });
13951063
1396 this_unit_offset += next_offset;1064 const obj_di = try self.allocator.create(ModuleDebugInfo);
1397 }1065 errdefer self.allocator.destroy(obj_di);
1398 }
13991066
1400 fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {1067 const macho_path = mem.toSliceConst(u8, std.c._dyld_get_image_name(i));
1401 for (di.compile_unit_list.toSlice()) |*compile_unit| {1068 obj_di.* = openMachODebugInfo(self.allocator, macho_path) catch |err| switch (err) {
1402 if (compile_unit.pc_range) |range| {1069 error.FileNotFound => return error.MissingDebugInfo,
1403 if (target_address >= range.start and target_address < range.end) return compile_unit;
1404 }
1405 if (di.debug_ranges) |debug_ranges| {
1406 if (compile_unit.die.getAttrSecOffset(DW.AT_ranges)) |ranges_offset| {
1407 var s = io.SliceSeekableInStream.init(debug_ranges);
1408
1409 // All the addresses in the list are relative to the value
1410 // specified by DW_AT_low_pc or to some other value encoded
1411 // in the list itself.
1412 // If no starting value is specified use zero.
1413 var base_address = compile_unit.die.getAttrAddr(DW.AT_low_pc) catch |err| switch (err) {
1414 error.MissingDebugInfo => 0,
1415 else => return err,1070 else => return err,
1416 };1071 };
1072 obj_di.base_address = base_address;
14171073
1418 try s.seekable_stream.seekTo(ranges_offset);1074 try self.address_map.putNoClobber(base_address, obj_di);
14191075
1420 while (true) {1076 return obj_di;
1421 const begin_addr = try s.stream.readIntLittle(usize);
1422 const end_addr = try s.stream.readIntLittle(usize);
1423 if (begin_addr == 0 and end_addr == 0) {
1424 break;
1425 }
1426 // This entry selects a new value for the base address
1427 if (begin_addr == maxInt(usize)) {
1428 base_address = end_addr;
1429 continue;
1430 }
1431 if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
1432 return compile_unit;
1433 }
1434 }
1435 } else |err| {
1436 if (err != error.MissingDebugInfo) return err;
1437 continue;
1438 }1077 }
1439 }1078 }
1440 }1079 }
1441 return error.MissingDebugInfo;
1442 }
1443
1444 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
1445 /// seeks in the stream and parses it.
1446 fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
1447 for (di.abbrev_table_list.toSlice()) |*header| {
1448 if (header.offset == abbrev_offset) {
1449 return &header.table;
1450 }
1451 }
1452 try di.abbrev_table_list.append(AbbrevTableHeader{
1453 .offset = abbrev_offset,
1454 .table = try di.parseAbbrevTable(abbrev_offset),
1455 });
1456 return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
1457 }
1458
1459 fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
1460 var s = io.SliceSeekableInStream.init(di.debug_abbrev);
1461
1462 try s.seekable_stream.seekTo(offset);
1463 var result = AbbrevTable.init(di.allocator());
1464 errdefer result.deinit();
1465 while (true) {
1466 const abbrev_code = try leb.readULEB128(u64, &s.stream);
1467 if (abbrev_code == 0) return result;
1468 try result.append(AbbrevTableEntry{
1469 .abbrev_code = abbrev_code,
1470 .tag_id = try leb.readULEB128(u64, &s.stream),
1471 .has_children = (try s.stream.readByte()) == DW.CHILDREN_yes,
1472 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
1473 });
1474 const attrs = &result.items[result.len - 1].attrs;
1475
1476 while (true) {
1477 const attr_id = try leb.readULEB128(u64, &s.stream);
1478 const form_id = try leb.readULEB128(u64, &s.stream);
1479 if (attr_id == 0 and form_id == 0) break;
1480 try attrs.append(AbbrevAttr{
1481 .attr_id = attr_id,
1482 .form_id = form_id,
1483 });
1484 }
1485 }
1486 }
1487
1488 fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
1489 const abbrev_code = try leb.readULEB128(u64, in_stream);
1490 if (abbrev_code == 0) return null;
1491 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
14921080
1493 var result = Die{1081 return error.MissingDebugInfo;
1494 .tag_id = table_entry.tag_id,
1495 .has_children = table_entry.has_children,
1496 .attrs = ArrayList(Die.Attr).init(di.allocator()),
1497 };
1498 try result.attrs.resize(table_entry.attrs.len);
1499 for (table_entry.attrs.toSliceConst()) |attr, i| {
1500 result.attrs.items[i] = Die.Attr{
1501 .id = attr.attr_id,
1502 .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64),
1503 };
1504 }
1505 return result;
1506 }1082 }
15071083
1508 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {1084 fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1509 var s = io.SliceSeekableInStream.init(di.debug_line);1085 const process_handle = windows.kernel32.GetCurrentProcess();
1510
1511 const compile_unit_cwd = try compile_unit.die.getAttrString(di, DW.AT_comp_dir);
1512 const line_info_offset = try compile_unit.die.getAttrSecOffset(DW.AT_stmt_list);
15131086
1514 try s.seekable_stream.seekTo(line_info_offset);1087 // Find how many modules are actually loaded
1088 var dummy: windows.HMODULE = undefined;
1089 var bytes_needed: windows.DWORD = undefined;
1090 if (windows.kernel32.K32EnumProcessModules(
1091 process_handle,
1092 @ptrCast([*]windows.HMODULE, &dummy),
1093 0,
1094 &bytes_needed,
1095 ) == 0)
1096 return error.MissingDebugInfo;
15151097
1516 var is_64: bool = undefined;1098 const needed_modules = bytes_needed / @sizeOf(windows.HMODULE);
1517 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);1099
1518 if (unit_length == 0) {1100 // Fetch the complete module list
1101 var modules = try self.allocator.alloc(windows.HMODULE, needed_modules);
1102 defer self.allocator.free(modules);
1103 if (windows.kernel32.K32EnumProcessModules(
1104 process_handle,
1105 modules.ptr,
1106 try math.cast(windows.DWORD, modules.len * @sizeOf(windows.HMODULE)),
1107 &bytes_needed,
1108 ) == 0)
1519 return error.MissingDebugInfo;1109 return error.MissingDebugInfo;
1520 }
1521 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
15221110
1523 const version = try s.stream.readInt(u16, di.endian);1111 // There's an unavoidable TOCTOU problem here, the module list may have
1524 // TODO support 3 and 51112 // changed between the two EnumProcessModules call.
1525 if (version != 2 and version != 4) return error.InvalidDebugInfo;1113 // Pick the smallest amount of elements to avoid processing garbage.
1114 const needed_modules_after = bytes_needed / @sizeOf(windows.HMODULE);
1115 const loaded_modules = math.min(needed_modules, needed_modules_after);
1116
1117 for (modules[0..loaded_modules]) |module| {
1118 var info: windows.MODULEINFO = undefined;
1119 if (windows.kernel32.K32GetModuleInformation(
1120 process_handle,
1121 module,
1122 &info,
1123 @sizeOf(@TypeOf(info)),
1124 ) == 0)
1125 return error.MissingDebugInfo;
15261126
1527 const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);1127 const seg_start = @ptrToInt(info.lpBaseOfDll);
1528 const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;1128 const seg_end = seg_start + info.SizeOfImage;
15291129
1530 const minimum_instruction_length = try s.stream.readByte();1130 if (address >= seg_start and address < seg_end) {
1531 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;1131 if (self.address_map.getValue(seg_start)) |obj_di| {
1132 return obj_di;
1133 }
15321134
1533 if (version >= 4) {1135 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
1534 // maximum_operations_per_instruction1136 // openFileAbsoluteW requires the prefix to be present
1535 _ = try s.stream.readByte();1137 mem.copy(u16, name_buffer[0..4], &[_]u16{ '\\', '?', '?', '\\' });
1138 const len = windows.kernel32.K32GetModuleFileNameExW(
1139 process_handle,
1140 module,
1141 @ptrCast(windows.LPWSTR, &name_buffer[4]),
1142 windows.PATH_MAX_WIDE,
1143 );
1144 assert(len > 0);
1145
1146 const obj_di = try self.allocator.create(ModuleDebugInfo);
1147 errdefer self.allocator.destroy(obj_di);
1148
1149 obj_di.* = openCoffDebugInfo(self.allocator, name_buffer[0..:0]) catch |err| switch (err) {
1150 error.FileNotFound => return error.MissingDebugInfo,
1151 else => return err,
1152 };
1153 obj_di.base_address = seg_start;
1154
1155 try self.address_map.putNoClobber(seg_start, obj_di);
1156
1157 return obj_di;
1158 }
1536 }1159 }
15371160
1538 const default_is_stmt = (try s.stream.readByte()) != 0;1161 return error.MissingDebugInfo;
1539 const line_base = try s.stream.readByteSigned();1162 }
1540
1541 const line_range = try s.stream.readByte();
1542 if (line_range == 0) return error.InvalidDebugInfo;
1543
1544 const opcode_base = try s.stream.readByte();
15451163
1546 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);1164 fn lookupModuleDl(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1165 var ctx: struct {
1166 // Input
1167 address: usize,
1168 // Output
1169 base_address: usize = undefined,
1170 name: []const u8 = undefined,
1171 } = .{ .address = address };
1172 const CtxTy = @TypeOf(ctx);
1173
1174 if (os.dl_iterate_phdr(&ctx, anyerror, struct {
1175 fn callback(info: *os.dl_phdr_info, size: usize, context: *CtxTy) !void {
1176 // The base address is too high
1177 if (context.address < info.dlpi_addr)
1178 return;
15471179
1548 {1180 const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
1549 var i: usize = 0;1181 for (phdrs) |*phdr| {
1550 while (i < opcode_base - 1) : (i += 1) {1182 if (phdr.p_type != elf.PT_LOAD) continue;
1551 standard_opcode_lengths[i] = try s.stream.readByte();1183
1184 const seg_start = info.dlpi_addr + phdr.p_vaddr;
1185 const seg_end = seg_start + phdr.p_memsz;
1186
1187 if (context.address >= seg_start and context.address < seg_end) {
1188 // Android libc uses NULL instead of an empty string to mark the
1189 // main program
1190 context.name = if (info.dlpi_name) |dlpi_name|
1191 mem.toSliceConst(u8, dlpi_name)
1192 else
1193 "";
1194 context.base_address = info.dlpi_addr;
1195 // Stop the iteration
1196 return error.Found;
1197 }
1198 }
1552 }1199 }
1200 }.callback)) {
1201 return error.MissingDebugInfo;
1202 } else |err| switch (err) {
1203 error.Found => {},
1204 else => return error.MissingDebugInfo,
1553 }1205 }
15541206
1555 var include_directories = ArrayList([]u8).init(di.allocator());1207 if (self.address_map.getValue(ctx.base_address)) |obj_di| {
1556 try include_directories.append(compile_unit_cwd);1208 return obj_di;
1557 while (true) {
1558 const dir = try readStringRaw(di.allocator(), &s.stream);
1559 if (dir.len == 0) break;
1560 try include_directories.append(dir);
1561 }1209 }
15621210
1563 var file_entries = ArrayList(FileEntry).init(di.allocator());1211 const elf_path = if (ctx.name.len > 0)
1564 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);1212 ctx.name
15651213 else blk: {
1566 while (true) {1214 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1567 const file_name = try readStringRaw(di.allocator(), &s.stream);1215 break :blk try fs.selfExePath(&buf);
1568 if (file_name.len == 0) break;1216 };
1569 const dir_index = try leb.readULEB128(usize, &s.stream);
1570 const mtime = try leb.readULEB128(usize, &s.stream);
1571 const len_bytes = try leb.readULEB128(usize, &s.stream);
1572 try file_entries.append(FileEntry{
1573 .file_name = file_name,
1574 .dir_index = dir_index,
1575 .mtime = mtime,
1576 .len_bytes = len_bytes,
1577 });
1578 }
15791217
1580 try s.seekable_stream.seekTo(prog_start_offset);1218 const obj_di = try self.allocator.create(ModuleDebugInfo);
1219 errdefer self.allocator.destroy(obj_di);
15811220
1582 const next_unit_pos = line_info_offset + next_offset;1221 obj_di.* = openElfDebugInfo(self.allocator, elf_path) catch |err| switch (err) {
1222 error.FileNotFound => return error.MissingDebugInfo,
1223 else => return err,
1224 };
1225 obj_di.base_address = ctx.base_address;
15831226
1584 while ((try s.seekable_stream.getPos()) < next_unit_pos) {1227 try self.address_map.putNoClobber(ctx.base_address, obj_di);
1585 const opcode = try s.stream.readByte();
15861228
1587 if (opcode == DW.LNS_extended_op) {1229 return obj_di;
1588 const op_size = try leb.readULEB128(u64, &s.stream);
1589 if (op_size < 1) return error.InvalidDebugInfo;
1590 var sub_op = try s.stream.readByte();
1591 switch (sub_op) {
1592 DW.LNE_end_sequence => {
1593 prog.end_sequence = true;
1594 if (try prog.checkLineMatch()) |info| return info;
1595 prog.reset();
1596 },
1597 DW.LNE_set_address => {
1598 const addr = try s.stream.readInt(usize, di.endian);
1599 prog.address = addr;
1600 },
1601 DW.LNE_define_file => {
1602 const file_name = try readStringRaw(di.allocator(), &s.stream);
1603 const dir_index = try leb.readULEB128(usize, &s.stream);
1604 const mtime = try leb.readULEB128(usize, &s.stream);
1605 const len_bytes = try leb.readULEB128(usize, &s.stream);
1606 try file_entries.append(FileEntry{
1607 .file_name = file_name,
1608 .dir_index = dir_index,
1609 .mtime = mtime,
1610 .len_bytes = len_bytes,
1611 });
1612 },
1613 else => {
1614 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
1615 try s.seekable_stream.seekBy(fwd_amt);
1616 },
1617 }
1618 } else if (opcode >= opcode_base) {
1619 // special opcodes
1620 const adjusted_opcode = opcode - opcode_base;
1621 const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
1622 const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
1623 prog.line += inc_line;
1624 prog.address += inc_addr;
1625 if (try prog.checkLineMatch()) |info| return info;
1626 prog.basic_block = false;
1627 } else {
1628 switch (opcode) {
1629 DW.LNS_copy => {
1630 if (try prog.checkLineMatch()) |info| return info;
1631 prog.basic_block = false;
1632 },
1633 DW.LNS_advance_pc => {
1634 const arg = try leb.readULEB128(usize, &s.stream);
1635 prog.address += arg * minimum_instruction_length;
1636 },
1637 DW.LNS_advance_line => {
1638 const arg = try leb.readILEB128(i64, &s.stream);
1639 prog.line += arg;
1640 },
1641 DW.LNS_set_file => {
1642 const arg = try leb.readULEB128(usize, &s.stream);
1643 prog.file = arg;
1644 },
1645 DW.LNS_set_column => {
1646 const arg = try leb.readULEB128(u64, &s.stream);
1647 prog.column = arg;
1648 },
1649 DW.LNS_negate_stmt => {
1650 prog.is_stmt = !prog.is_stmt;
1651 },
1652 DW.LNS_set_basic_block => {
1653 prog.basic_block = true;
1654 },
1655 DW.LNS_const_add_pc => {
1656 const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
1657 prog.address += inc_addr;
1658 },
1659 DW.LNS_fixed_advance_pc => {
1660 const arg = try s.stream.readInt(u16, di.endian);
1661 prog.address += arg;
1662 },
1663 DW.LNS_set_prologue_end => {},
1664 else => {
1665 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
1666 const len_bytes = standard_opcode_lengths[opcode - 1];
1667 try s.seekable_stream.seekBy(len_bytes);
1668 },
1669 }
1670 }
1671 }
1672
1673 return error.MissingDebugInfo;
1674 }1230 }
1231};
16751232
1676 fn getString(di: *DwarfInfo, offset: u64) ![]u8 {1233const SymbolInfo = struct {
1677 if (offset > di.debug_str.len)1234 symbol_name: []const u8 = "???",
1678 return error.InvalidDebugInfo;1235 compile_unit_name: []const u8 = "???",
1679 const casted_offset = math.cast(usize, offset) catch1236 line_info: ?LineInfo = null,
1680 return error.InvalidDebugInfo;
16811237
1682 // Valid strings always have a terminating zero byte1238 fn deinit(self: @This()) void {
1683 if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {1239 if (self.line_info) |li| {
1684 return di.debug_str[casted_offset..last];1240 li.deinit();
1685 }1241 }
1686
1687 return error.InvalidDebugInfo;
1688 }1242 }
1689};1243};
16901244
1691pub const DebugInfo = switch (builtin.os) {1245pub const ModuleDebugInfo = switch (builtin.os) {
1692 .macosx, .ios, .watchos, .tvos => struct {1246 .macosx, .ios, .watchos, .tvos => struct {
1247 base_address: usize,
1248 mapped_memory: []const u8,
1693 symbols: []const MachoSymbol,1249 symbols: []const MachoSymbol,
1694 strings: []const u8,1250 strings: [:0]const u8,
1695 ofiles: OFileTable,1251 ofiles: OFileTable,
16961252
1697 const OFileTable = std.HashMap(1253 const OFileTable = std.StringHashMap(DW.DwarfInfo);
1698 *macho.nlist_64,
1699 DwarfInfo,
1700 std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
1701 std.hash_map.getTrivialEqlFn(*macho.nlist_64),
1702 );
17031254
1704 pub fn allocator(self: DebugInfo) *mem.Allocator {1255 pub fn allocator(self: @This()) *mem.Allocator {
1705 return self.ofiles.allocator;1256 return self.ofiles.allocator;
1706 }1257 }
1707 },
1708 .uefi, .windows => struct {
1709 pdb: pdb.Pdb,
1710 coff: *coff.Coff,
1711 sect_contribs: []pdb.SectionContribEntry,
1712 modules: []Module,
1713 },
1714 else => DwarfInfo,
1715};
1716
1717const PcRange = struct {
1718 start: u64,
1719 end: u64,
1720};
1721
1722const CompileUnit = struct {
1723 version: u16,
1724 is_64: bool,
1725 die: *Die,
1726 pc_range: ?PcRange,
1727};
17281258
1729const AbbrevTable = ArrayList(AbbrevTableEntry);1259 fn loadOFile(self: *@This(), o_file_path: []const u8) !DW.DwarfInfo {
1260 const mapped_mem = try mapWholeFile(o_file_path);
17301261
1731const AbbrevTableHeader = struct {1262 const hdr = @ptrCast(
1732 // offset from .debug_abbrev1263 *const macho.mach_header_64,
1733 offset: u64,1264 @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
1734 table: AbbrevTable,1265 );
1735};1266 if (hdr.magic != std.macho.MH_MAGIC_64)
17361267 return error.InvalidDebugInfo;
1737const AbbrevTableEntry = struct {
1738 has_children: bool,
1739 abbrev_code: u64,
1740 tag_id: u64,
1741 attrs: ArrayList(AbbrevAttr),
1742};
17431268
1744const AbbrevAttr = struct {1269 const hdr_base = @ptrCast([*]const u8, hdr);
1745 attr_id: u64,1270 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
1746 form_id: u64,1271 var ncmd: u32 = hdr.ncmds;
1747};1272 const segcmd = while (ncmd != 0) : (ncmd -= 1) {
1273 const lc = @ptrCast(*const std.macho.load_command, ptr);
1274 switch (lc.cmd) {
1275 std.macho.LC_SEGMENT_64 => {
1276 break @ptrCast(
1277 *const std.macho.segment_command_64,
1278 @alignCast(@alignOf(std.macho.segment_command_64), ptr),
1279 );
1280 },
1281 else => {},
1282 }
1283 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
1284 } else {
1285 return error.MissingDebugInfo;
1286 };
17481287
1749const FormValue = union(enum) {1288 var opt_debug_line: ?*const macho.section_64 = null;
1750 Address: u64,1289 var opt_debug_info: ?*const macho.section_64 = null;
1751 Block: []u8,1290 var opt_debug_abbrev: ?*const macho.section_64 = null;
1752 Const: Constant,1291 var opt_debug_str: ?*const macho.section_64 = null;
1753 ExprLoc: []u8,1292 var opt_debug_ranges: ?*const macho.section_64 = null;
1754 Flag: bool,1293
1755 SecOffset: u64,1294 const sections = @ptrCast(
1756 Ref: u64,1295 [*]const macho.section_64,
1757 RefAddr: u64,1296 @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)),
1758 String: []u8,1297 )[0..segcmd.nsects];
1759 StrPtr: u64,1298 for (sections) |*sect| {
1760};1299 // The section name may not exceed 16 chars and a trailing null may
1300 // not be present
1301 const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last|
1302 sect.sectname[0..last]
1303 else
1304 sect.sectname[0..];
1305
1306 if (mem.eql(u8, name, "__debug_line")) {
1307 opt_debug_line = sect;
1308 } else if (mem.eql(u8, name, "__debug_info")) {
1309 opt_debug_info = sect;
1310 } else if (mem.eql(u8, name, "__debug_abbrev")) {
1311 opt_debug_abbrev = sect;
1312 } else if (mem.eql(u8, name, "__debug_str")) {
1313 opt_debug_str = sect;
1314 } else if (mem.eql(u8, name, "__debug_ranges")) {
1315 opt_debug_ranges = sect;
1316 }
1317 }
17611318
1762const Constant = struct {1319 const debug_line = opt_debug_line orelse
1763 payload: u64,1320 return error.MissingDebugInfo;
1764 signed: bool,1321 const debug_info = opt_debug_info orelse
1322 return error.MissingDebugInfo;
1323 const debug_str = opt_debug_str orelse
1324 return error.MissingDebugInfo;
1325 const debug_abbrev = opt_debug_abbrev orelse
1326 return error.MissingDebugInfo;
17651327
1766 fn asUnsignedLe(self: *const Constant) !u64 {1328 var di = DW.DwarfInfo{
1767 if (self.signed) return error.InvalidDebugInfo;1329 .endian = .Little,
1768 return self.payload;1330 .debug_info = try chopSlice(mapped_mem, debug_info.offset, debug_info.size),
1769 }1331 .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.offset, debug_abbrev.size),
1770};1332 .debug_str = try chopSlice(mapped_mem, debug_str.offset, debug_str.size),
1333 .debug_line = try chopSlice(mapped_mem, debug_line.offset, debug_line.size),
1334 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
1335 try chopSlice(mapped_mem, debug_ranges.offset, debug_ranges.size)
1336 else
1337 null,
1338 };
17711339
1772const Die = struct {1340 try DW.openDwarfDebugInfo(&di, self.allocator());
1773 tag_id: u64,
1774 has_children: bool,
1775 attrs: ArrayList(Attr),
17761341
1777 const Attr = struct {1342 // Add the debug info to the cache
1778 id: u64,1343 try self.ofiles.putNoClobber(o_file_path, di);
1779 value: FormValue,
1780 };
17811344
1782 fn getAttr(self: *const Die, id: u64) ?*const FormValue {1345 return di;
1783 for (self.attrs.toSliceConst()) |*attr| {
1784 if (attr.id == id) return &attr.value;
1785 }1346 }
1786 return null;
1787 }
1788
1789 fn getAttrAddr(self: *const Die, id: u64) !u64 {
1790 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
1791 return switch (form_value.*) {
1792 FormValue.Address => |value| value,
1793 else => error.InvalidDebugInfo,
1794 };
1795 }
17961347
1797 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {1348 fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
1798 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;1349 // Translate the VA into an address into this object
1799 return switch (form_value.*) {1350 const relocated_address = address - self.base_address;
1800 FormValue.Const => |value| value.asUnsignedLe(),1351 assert(relocated_address >= 0x100000000);
1801 FormValue.SecOffset => |value| value,
1802 else => error.InvalidDebugInfo,
1803 };
1804 }
1805
1806 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
1807 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
1808 return switch (form_value.*) {
1809 FormValue.Const => |value| value.asUnsignedLe(),
1810 else => error.InvalidDebugInfo,
1811 };
1812 }
1813
1814 fn getAttrRef(self: *const Die, id: u64) !u64 {
1815 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
1816 return switch (form_value.*) {
1817 FormValue.Ref => |value| value,
1818 else => error.InvalidDebugInfo,
1819 };
1820 }
1821
1822 fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]u8 {
1823 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
1824 return switch (form_value.*) {
1825 FormValue.String => |value| value,
1826 FormValue.StrPtr => |offset| di.getString(offset),
1827 else => error.InvalidDebugInfo,
1828 };
1829 }
1830};
18311352
1832const FileEntry = struct {1353 // Find the .o file where this symbol is defined
1833 file_name: []const u8,1354 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse
1834 dir_index: usize,1355 return SymbolInfo{};
1835 mtime: usize,
1836 len_bytes: usize,
1837};
18381356
1839pub const LineInfo = struct {1357 // XXX: Return the symbol name
1840 line: u64,1358 if (symbol.ofile == null)
1841 column: u64,1359 return SymbolInfo{};
1842 file_name: []const u8,
1843 allocator: ?*mem.Allocator,
18441360
1845 fn deinit(self: LineInfo) void {1361 assert(symbol.ofile.?.n_strx < self.strings.len);
1846 const allocator = self.allocator orelse return;1362 const o_file_path = mem.toSliceConst(u8, self.strings.ptr + symbol.ofile.?.n_strx);
1847 allocator.free(self.file_name);
1848 }
1849};
18501363
1851const LineNumberProgram = struct {1364 // Check if its debug infos are already in the cache
1852 address: usize,1365 var o_file_di = self.ofiles.getValue(o_file_path) orelse
1853 file: usize,1366 (self.loadOFile(o_file_path) catch |err| switch (err) {
1854 line: i64,1367 error.MissingDebugInfo, error.InvalidDebugInfo => {
1855 column: u64,1368 // XXX: Return the symbol name
1856 is_stmt: bool,1369 return SymbolInfo{};
1857 basic_block: bool,1370 },
1858 end_sequence: bool,1371 else => return err,
18591372 });
1860 default_is_stmt: bool,
1861 target_address: usize,
1862 include_dirs: []const []const u8,
1863 file_entries: *ArrayList(FileEntry),
1864
1865 prev_address: usize,
1866 prev_file: usize,
1867 prev_line: i64,
1868 prev_column: u64,
1869 prev_is_stmt: bool,
1870 prev_basic_block: bool,
1871 prev_end_sequence: bool,
1872
1873 // Reset the state machine following the DWARF specification
1874 pub fn reset(self: *LineNumberProgram) void {
1875 self.address = 0;
1876 self.file = 1;
1877 self.line = 1;
1878 self.column = 0;
1879 self.is_stmt = self.default_is_stmt;
1880 self.basic_block = false;
1881 self.end_sequence = false;
1882 // Invalidate all the remaining fields
1883 self.prev_address = 0;
1884 self.prev_file = undefined;
1885 self.prev_line = undefined;
1886 self.prev_column = undefined;
1887 self.prev_is_stmt = undefined;
1888 self.prev_basic_block = undefined;
1889 self.prev_end_sequence = undefined;
1890 }
18911373
1892 pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {1374 // Translate again the address, this time into an address inside the
1893 return LineNumberProgram{1375 // .o file
1894 .address = 0,1376 const relocated_address_o = relocated_address - symbol.reloc;
1895 .file = 1,
1896 .line = 1,
1897 .column = 0,
1898 .is_stmt = is_stmt,
1899 .basic_block = false,
1900 .end_sequence = false,
1901 .include_dirs = include_dirs,
1902 .file_entries = file_entries,
1903 .default_is_stmt = is_stmt,
1904 .target_address = target_address,
1905 .prev_address = 0,
1906 .prev_file = undefined,
1907 .prev_line = undefined,
1908 .prev_column = undefined,
1909 .prev_is_stmt = undefined,
1910 .prev_basic_block = undefined,
1911 .prev_end_sequence = undefined,
1912 };
1913 }
19141377
1915 pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo {1378 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
1916 if (self.target_address >= self.prev_address and self.target_address < self.address) {1379 return SymbolInfo{
1917 const file_entry = if (self.prev_file == 0) {1380 .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
1918 return error.MissingDebugInfo;1381 .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) {
1919 } else if (self.prev_file - 1 >= self.file_entries.len) {1382 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1920 return error.InvalidDebugInfo;1383 else => return err,
1921 } else1384 },
1922 &self.file_entries.items[self.prev_file - 1];1385 .line_info = o_file_di.getLineNumberInfo(compile_unit.*, relocated_address_o) catch |err| switch (err) {
1386 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1387 else => return err,
1388 },
1389 };
1390 } else |err| switch (err) {
1391 error.MissingDebugInfo, error.InvalidDebugInfo => {
1392 return SymbolInfo{};
1393 },
1394 else => return err,
1395 }
19231396
1924 const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {1397 unreachable;
1925 return error.InvalidDebugInfo;
1926 } else
1927 self.include_dirs[file_entry.dir_index];
1928 const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name });
1929 errdefer self.file_entries.allocator.free(file_name);
1930 return LineInfo{
1931 .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
1932 .column = self.prev_column,
1933 .file_name = file_name,
1934 .allocator = self.file_entries.allocator,
1935 };
1936 }1398 }
1399 },
1400 .uefi, .windows => struct {
1401 base_address: usize,
1402 pdb: pdb.Pdb,
1403 coff: *coff.Coff,
1404 sect_contribs: []pdb.SectionContribEntry,
1405 modules: []Module,
19371406
1938 self.prev_address = self.address;1407 pub fn allocator(self: @This()) *mem.Allocator {
1939 self.prev_file = self.file;1408 return self.coff.allocator;
1940 self.prev_line = self.line;1409 }
1941 self.prev_column = self.column;
1942 self.prev_is_stmt = self.is_stmt;
1943 self.prev_basic_block = self.basic_block;
1944 self.prev_end_sequence = self.end_sequence;
1945 return null;
1946 }
1947};
19481410
1949// TODO the noasyncs here are workarounds1411 fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
1950fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {1412 // Translate the VA into an address into this object
1951 var buf = ArrayList(u8).init(allocator);1413 const relocated_address = address - self.base_address;
1952 while (true) {
1953 const byte = try noasync in_stream.readByte();
1954 if (byte == 0) break;
1955 try buf.append(byte);
1956 }
1957 return buf.toSlice();
1958}
19591414
1960// TODO the noasyncs here are workarounds1415 var coff_section: *coff.Section = undefined;
1961fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {1416 const mod_index = for (self.sect_contribs) |sect_contrib| {
1962 const buf = try allocator.alloc(u8, size);1417 if (sect_contrib.Section > self.coff.sections.len) continue;
1963 errdefer allocator.free(buf);1418 // Remember that SectionContribEntry.Section is 1-based.
1964 if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile;1419 coff_section = &self.coff.sections.toSlice()[sect_contrib.Section - 1];
1965 return buf;
1966}
19671420
1968fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {1421 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
1969 const buf = try readAllocBytes(allocator, in_stream, size);1422 const vaddr_end = vaddr_start + sect_contrib.Size;
1970 return FormValue{ .Block = buf };1423 if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
1971}1424 break sect_contrib.ModuleIndex;
1425 }
1426 } else {
1427 // we have no information to add to the address
1428 return SymbolInfo{};
1429 };
19721430
1973// TODO the noasyncs here are workarounds1431 const mod = &self.modules[mod_index];
1974fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {1432 try populateModule(self, mod);
1975 const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size);1433 const obj_basename = fs.path.basename(mod.obj_file_name);
1976 return parseFormValueBlockLen(allocator, in_stream, block_len);1434
1977}1435 var symbol_i: usize = 0;
1436 const symbol_name = if (!mod.populated) "???" else while (symbol_i != mod.symbols.len) {
1437 const prefix = @ptrCast(*pdb.RecordPrefix, &mod.symbols[symbol_i]);
1438 if (prefix.RecordLen < 2)
1439 return error.InvalidDebugInfo;
1440 switch (prefix.RecordKind) {
1441 .S_LPROC32, .S_GPROC32 => {
1442 const proc_sym = @ptrCast(*pdb.ProcSym, &mod.symbols[symbol_i + @sizeOf(pdb.RecordPrefix)]);
1443 const vaddr_start = coff_section.header.virtual_address + proc_sym.CodeOffset;
1444 const vaddr_end = vaddr_start + proc_sym.CodeSize;
1445 if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
1446 break mem.toSliceConst(u8, @ptrCast([*:0]u8, proc_sym) + @sizeOf(pdb.ProcSym));
1447 }
1448 },
1449 else => {},
1450 }
1451 symbol_i += prefix.RecordLen + @sizeOf(u16);
1452 if (symbol_i > mod.symbols.len)
1453 return error.InvalidDebugInfo;
1454 } else "???";
1455
1456 const subsect_info = mod.subsect_info;
1457
1458 var sect_offset: usize = 0;
1459 var skip_len: usize = undefined;
1460 const opt_line_info = subsections: {
1461 const checksum_offset = mod.checksum_offset orelse break :subsections null;
1462 while (sect_offset != subsect_info.len) : (sect_offset += skip_len) {
1463 const subsect_hdr = @ptrCast(*pdb.DebugSubsectionHeader, &subsect_info[sect_offset]);
1464 skip_len = subsect_hdr.Length;
1465 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
1466
1467 switch (subsect_hdr.Kind) {
1468 .Lines => {
1469 var line_index = sect_offset;
1470
1471 const line_hdr = @ptrCast(*pdb.LineFragmentHeader, &subsect_info[line_index]);
1472 if (line_hdr.RelocSegment == 0)
1473 return error.MissingDebugInfo;
1474 line_index += @sizeOf(pdb.LineFragmentHeader);
1475 const frag_vaddr_start = coff_section.header.virtual_address + line_hdr.RelocOffset;
1476 const frag_vaddr_end = frag_vaddr_start + line_hdr.CodeSize;
1477
1478 if (relocated_address >= frag_vaddr_start and relocated_address < frag_vaddr_end) {
1479 // There is an unknown number of LineBlockFragmentHeaders (and their accompanying line and column records)
1480 // from now on. We will iterate through them, and eventually find a LineInfo that we're interested in,
1481 // breaking out to :subsections. If not, we will make sure to not read anything outside of this subsection.
1482 const subsection_end_index = sect_offset + subsect_hdr.Length;
1483
1484 while (line_index < subsection_end_index) {
1485 const block_hdr = @ptrCast(*pdb.LineBlockFragmentHeader, &subsect_info[line_index]);
1486 line_index += @sizeOf(pdb.LineBlockFragmentHeader);
1487 const start_line_index = line_index;
1488
1489 const has_column = line_hdr.Flags.LF_HaveColumns;
1490
1491 // All line entries are stored inside their line block by ascending start address.
1492 // Heuristic: we want to find the last line entry
1493 // that has a vaddr_start <= relocated_address.
1494 // This is done with a simple linear search.
1495 var line_i: u32 = 0;
1496 while (line_i < block_hdr.NumLines) : (line_i += 1) {
1497 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[line_index]);
1498 line_index += @sizeOf(pdb.LineNumberEntry);
1499
1500 const vaddr_start = frag_vaddr_start + line_num_entry.Offset;
1501 if (relocated_address < vaddr_start) {
1502 break;
1503 }
1504 }
1505
1506 // line_i == 0 would mean that no matching LineNumberEntry was found.
1507 if (line_i > 0) {
1508 const subsect_index = checksum_offset + block_hdr.NameIndex;
1509 const chksum_hdr = @ptrCast(*pdb.FileChecksumEntryHeader, &mod.subsect_info[subsect_index]);
1510 const strtab_offset = @sizeOf(pdb.PDBStringTableHeader) + chksum_hdr.FileNameOffset;
1511 try self.pdb.string_table.seekTo(strtab_offset);
1512 const source_file_name = try self.pdb.string_table.readNullTermString(self.allocator());
1513
1514 const line_entry_idx = line_i - 1;
1515
1516 const column = if (has_column) blk: {
1517 const start_col_index = start_line_index + @sizeOf(pdb.LineNumberEntry) * block_hdr.NumLines;
1518 const col_index = start_col_index + @sizeOf(pdb.ColumnNumberEntry) * line_entry_idx;
1519 const col_num_entry = @ptrCast(*pdb.ColumnNumberEntry, &subsect_info[col_index]);
1520 break :blk col_num_entry.StartColumn;
1521 } else 0;
1522
1523 const found_line_index = start_line_index + line_entry_idx * @sizeOf(pdb.LineNumberEntry);
1524 const line_num_entry = @ptrCast(*pdb.LineNumberEntry, &subsect_info[found_line_index]);
1525 const flags = @ptrCast(*pdb.LineNumberEntry.Flags, &line_num_entry.Flags);
1526
1527 break :subsections LineInfo{
1528 .allocator = self.allocator(),
1529 .file_name = source_file_name,
1530 .line = flags.Start,
1531 .column = column,
1532 };
1533 }
1534 }
19781535
1979fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {1536 // Checking that we are not reading garbage after the (possibly) multiple block fragments.
1980 // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.1537 if (line_index != subsection_end_index) {
1981 // `noasync` should be removed from all the function calls once it is fixed.1538 return error.InvalidDebugInfo;
1982 return FormValue{1539 }
1983 .Const = Constant{1540 }
1984 .signed = signed,1541 },
1985 .payload = switch (size) {1542 else => {},
1986 1 => try noasync in_stream.readIntLittle(u8),
1987 2 => try noasync in_stream.readIntLittle(u16),
1988 4 => try noasync in_stream.readIntLittle(u32),
1989 8 => try noasync in_stream.readIntLittle(u64),
1990 -1 => blk: {
1991 if (signed) {
1992 const x = try noasync leb.readILEB128(i64, in_stream);
1993 break :blk @bitCast(u64, x);
1994 } else {
1995 const x = try noasync leb.readULEB128(u64, in_stream);
1996 break :blk x;
1997 }1543 }
1998 },
1999 else => @compileError("Invalid size"),
2000 },
2001 },
2002 };
2003}
2004
2005// TODO the noasyncs here are workarounds
2006fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
2007 return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32));
2008}
2009
2010// TODO the noasyncs here are workarounds
2011fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
2012 if (@sizeOf(usize) == 4) {
2013 // TODO this cast should not be needed
2014 return @as(u64, try noasync in_stream.readIntLittle(u32));
2015 } else if (@sizeOf(usize) == 8) {
2016 return noasync in_stream.readIntLittle(u64);
2017 } else {
2018 unreachable;
2019 }
2020}
2021
2022// TODO the noasyncs here are workarounds
2023fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
2024 return FormValue{
2025 .Ref = switch (size) {
2026 1 => try noasync in_stream.readIntLittle(u8),
2027 2 => try noasync in_stream.readIntLittle(u16),
2028 4 => try noasync in_stream.readIntLittle(u32),
2029 8 => try noasync in_stream.readIntLittle(u64),
2030 -1 => try noasync leb.readULEB128(u64, in_stream),
2031 else => unreachable,
2032 },
2033 };
2034}
2035
2036// TODO the noasyncs here are workarounds
2037fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
2038 return switch (form_id) {
2039 DW.FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
2040 DW.FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
2041 DW.FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
2042 DW.FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
2043 DW.FORM_block => x: {
2044 const block_len = try noasync leb.readULEB128(usize, in_stream);
2045 return parseFormValueBlockLen(allocator, in_stream, block_len);
2046 },
2047 DW.FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
2048 DW.FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
2049 DW.FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
2050 DW.FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
2051 DW.FORM_udata, DW.FORM_sdata => {
2052 const signed = form_id == DW.FORM_sdata;
2053 return parseFormValueConstant(allocator, in_stream, signed, -1);
2054 },
2055 DW.FORM_exprloc => {
2056 const size = try noasync leb.readULEB128(usize, in_stream);
2057 const buf = try readAllocBytes(allocator, in_stream, size);
2058 return FormValue{ .ExprLoc = buf };
2059 },
2060 DW.FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 },
2061 DW.FORM_flag_present => FormValue{ .Flag = true },
2062 DW.FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
2063
2064 DW.FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
2065 DW.FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
2066 DW.FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
2067 DW.FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
2068 DW.FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
2069
2070 DW.FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
2071 DW.FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) },
2072
2073 DW.FORM_string => FormValue{ .String = try readStringRaw(allocator, in_stream) },
2074 DW.FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
2075 DW.FORM_indirect => {
2076 const child_form_id = try noasync leb.readULEB128(u64, in_stream);
2077 const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64));
2078 var frame = try allocator.create(F);
2079 defer allocator.destroy(frame);
2080 return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64);
2081 },
2082 else => error.InvalidDebugInfo,
2083 };
2084}
20851544
2086fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {1545 if (sect_offset > subsect_info.len)
2087 for (abbrev_table.toSliceConst()) |*table_entry| {1546 return error.InvalidDebugInfo;
2088 if (table_entry.abbrev_code == abbrev_code) return table_entry;1547 } else {
2089 }1548 break :subsections null;
2090 return null;1549 }
2091}1550 };
20921551
2093/// TODO resources https://github.com/ziglang/zig/issues/43531552 return SymbolInfo{
2094fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo {1553 .symbol_name = symbol_name,
2095 const ofile = symbol.ofile orelse return error.MissingDebugInfo;1554 .compile_unit_name = obj_basename,
2096 const gop = try di.ofiles.getOrPut(ofile);1555 .line_info = opt_line_info,
2097 const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: {1556 };
2098 errdefer _ = di.ofiles.remove(ofile);1557 }
2099 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));1558 },
21001559 .linux, .freebsd => struct {
2101 var exe_file = try std.fs.openFileAbsoluteC(ofile_path, .{});1560 base_address: usize,
2102 errdefer exe_file.close();1561 dwarf: DW.DwarfInfo,
21031562 mapped_memory: []const u8,
2104 const exe_len = math.cast(usize, try exe_file.getEndPos()) catch1563
2105 return error.DebugInfoTooLarge;1564 fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
2106 const exe_mmap = try os.mmap(1565 // Translate the VA into an address into this object
2107 null,1566 const relocated_address = address - self.base_address;
2108 exe_len,1567
2109 os.PROT_READ,1568 if (self.dwarf.findCompileUnit(relocated_address)) |compile_unit| {
2110 os.MAP_SHARED,1569 return SymbolInfo{
2111 exe_file.handle,1570 .symbol_name = self.dwarf.getSymbolName(relocated_address) orelse "???",
2112 0,1571 .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) {
2113 );1572 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
2114 errdefer os.munmap(exe_mmap);1573 else => return err,
21151574 },
2116 const hdr = @ptrCast(1575 .line_info = self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) {
2117 *const macho.mach_header_64,1576 error.MissingDebugInfo, error.InvalidDebugInfo => null,
2118 @alignCast(@alignOf(macho.mach_header_64), exe_mmap.ptr),1577 else => return err,
2119 );1578 },
2120 if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;1579 };
21211580 } else |err| switch (err) {
2122 const hdr_base = @ptrCast([*]const u8, hdr);1581 error.MissingDebugInfo, error.InvalidDebugInfo => {
2123 var ptr = hdr_base + @sizeOf(macho.mach_header_64);1582 return SymbolInfo{};
2124 var ncmd: u32 = hdr.ncmds;
2125 const segcmd = while (ncmd != 0) : (ncmd -= 1) {
2126 const lc = @ptrCast(*const std.macho.load_command, ptr);
2127 switch (lc.cmd) {
2128 std.macho.LC_SEGMENT_64 => {
2129 break @ptrCast(
2130 *const std.macho.segment_command_64,
2131 @alignCast(@alignOf(std.macho.segment_command_64), ptr),
2132 );
2133 },1583 },
2134 else => {},1584 else => return err,
2135 }1585 }
2136 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
2137 } else {
2138 return error.MissingDebugInfo;
2139 };
21401586
2141 var opt_debug_line: ?*const macho.section_64 = null;1587 unreachable;
2142 var opt_debug_info: ?*const macho.section_64 = null;
2143 var opt_debug_abbrev: ?*const macho.section_64 = null;
2144 var opt_debug_str: ?*const macho.section_64 = null;
2145 var opt_debug_ranges: ?*const macho.section_64 = null;
2146
2147 const sections = @ptrCast([*]const macho.section_64, @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)))[0..segcmd.nsects];
2148 for (sections) |*sect| {
2149 // The section name may not exceed 16 chars and a trailing null may
2150 // not be present
2151 const name = if (mem.indexOfScalar(u8, sect.sectname[0..], 0)) |last|
2152 sect.sectname[0..last]
2153 else
2154 sect.sectname[0..];
2155
2156 if (mem.eql(u8, name, "__debug_line")) {
2157 opt_debug_line = sect;
2158 } else if (mem.eql(u8, name, "__debug_info")) {
2159 opt_debug_info = sect;
2160 } else if (mem.eql(u8, name, "__debug_abbrev")) {
2161 opt_debug_abbrev = sect;
2162 } else if (mem.eql(u8, name, "__debug_str")) {
2163 opt_debug_str = sect;
2164 } else if (mem.eql(u8, name, "__debug_ranges")) {
2165 opt_debug_ranges = sect;
2166 }
2167 }1588 }
21681589 },
2169 var debug_line = opt_debug_line orelse1590 else => DW.DwarfInfo,
2170 return error.MissingDebugInfo;
2171 var debug_info = opt_debug_info orelse
2172 return error.MissingDebugInfo;
2173 var debug_str = opt_debug_str orelse
2174 return error.MissingDebugInfo;
2175 var debug_abbrev = opt_debug_abbrev orelse
2176 return error.MissingDebugInfo;
2177
2178 gop.kv.value = DwarfInfo{
2179 .endian = .Little,
2180 .debug_info = exe_mmap[@intCast(usize, debug_info.offset)..@intCast(usize, debug_info.offset + debug_info.size)],
2181 .debug_abbrev = exe_mmap[@intCast(usize, debug_abbrev.offset)..@intCast(usize, debug_abbrev.offset + debug_abbrev.size)],
2182 .debug_str = exe_mmap[@intCast(usize, debug_str.offset)..@intCast(usize, debug_str.offset + debug_str.size)],
2183 .debug_line = exe_mmap[@intCast(usize, debug_line.offset)..@intCast(usize, debug_line.offset + debug_line.size)],
2184 .debug_ranges = if (opt_debug_ranges) |debug_ranges|
2185 exe_mmap[@intCast(usize, debug_ranges.offset)..@intCast(usize, debug_ranges.offset + debug_ranges.size)]
2186 else
2187 null,
2188 };
2189 try openDwarfDebugInfo(&gop.kv.value, di.allocator());
2190
2191 break :blk &gop.kv.value;
2192 };
2193
2194 const o_file_address = address - symbol.reloc;
2195 const compile_unit = try dwarf_info.findCompileUnit(o_file_address);
2196 return dwarf_info.getLineNumberInfo(compile_unit.*, o_file_address);
2197}
2198
2199const Func = struct {
2200 pc_range: ?PcRange,
2201 name: ?[]u8,
2202};1591};
22031592
2204fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
2205 const first_32_bits = try in_stream.readIntLittle(u32);
2206 is_64.* = (first_32_bits == 0xffffffff);
2207 if (is_64.*) {
2208 return in_stream.readIntLittle(u64);
2209 } else {
2210 if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
2211 // TODO this cast should not be needed
2212 return @as(u64, first_32_bits);
2213 }
2214}
2215
2216/// TODO multithreaded awareness1593/// TODO multithreaded awareness
2217var debug_info_allocator: ?*mem.Allocator = null;1594var debug_info_allocator: ?*mem.Allocator = null;
2218var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;1595var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
lib/std/dwarf.zig+889-682
...@@ -1,682 +1,889 @@...@@ -1,682 +1,889 @@
1pub const TAG_padding = 0x00;1const std = @import("std.zig");
2pub const TAG_array_type = 0x01;2const builtin = @import("builtin");
3pub const TAG_class_type = 0x02;3const debug = std.debug;
4pub const TAG_entry_point = 0x03;4const fs = std.fs;
5pub const TAG_enumeration_type = 0x04;5const io = std.io;
6pub const TAG_formal_parameter = 0x05;6const mem = std.mem;
7pub const TAG_imported_declaration = 0x08;7const math = std.math;
8pub const TAG_label = 0x0a;8const leb = @import("debug/leb128.zig");
9pub const TAG_lexical_block = 0x0b;9
10pub const TAG_member = 0x0d;10const ArrayList = std.ArrayList;
11pub const TAG_pointer_type = 0x0f;11
12pub const TAG_reference_type = 0x10;12usingnamespace @import("dwarf_bits.zig");
13pub const TAG_compile_unit = 0x11;13
14pub const TAG_string_type = 0x12;14pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
15pub const TAG_structure_type = 0x13;15pub const DwarfInStream = io.InStream(anyerror);
16pub const TAG_subroutine = 0x14;16
17pub const TAG_subroutine_type = 0x15;17const PcRange = struct {
18pub const TAG_typedef = 0x16;18 start: u64,
19pub const TAG_union_type = 0x17;19 end: u64,
20pub const TAG_unspecified_parameters = 0x18;20};
21pub const TAG_variant = 0x19;21
22pub const TAG_common_block = 0x1a;22const Func = struct {
23pub const TAG_common_inclusion = 0x1b;23 pc_range: ?PcRange,
24pub const TAG_inheritance = 0x1c;24 name: ?[]const u8,
25pub const TAG_inlined_subroutine = 0x1d;25};
26pub const TAG_module = 0x1e;26
27pub const TAG_ptr_to_member_type = 0x1f;27const CompileUnit = struct {
28pub const TAG_set_type = 0x20;28 version: u16,
29pub const TAG_subrange_type = 0x21;29 is_64: bool,
30pub const TAG_with_stmt = 0x22;30 die: *Die,
31pub const TAG_access_declaration = 0x23;31 pc_range: ?PcRange,
32pub const TAG_base_type = 0x24;32};
33pub const TAG_catch_block = 0x25;33
34pub const TAG_const_type = 0x26;34const AbbrevTable = ArrayList(AbbrevTableEntry);
35pub const TAG_constant = 0x27;35
36pub const TAG_enumerator = 0x28;36const AbbrevTableHeader = struct {
37pub const TAG_file_type = 0x29;37 // offset from .debug_abbrev
38pub const TAG_friend = 0x2a;38 offset: u64,
39pub const TAG_namelist = 0x2b;39 table: AbbrevTable,
40pub const TAG_namelist_item = 0x2c;40};
41pub const TAG_packed_type = 0x2d;41
42pub const TAG_subprogram = 0x2e;42const AbbrevTableEntry = struct {
43pub const TAG_template_type_param = 0x2f;43 has_children: bool,
44pub const TAG_template_value_param = 0x30;44 abbrev_code: u64,
45pub const TAG_thrown_type = 0x31;45 tag_id: u64,
46pub const TAG_try_block = 0x32;46 attrs: ArrayList(AbbrevAttr),
47pub const TAG_variant_part = 0x33;47};
48pub const TAG_variable = 0x34;48
49pub const TAG_volatile_type = 0x35;49const AbbrevAttr = struct {
5050 attr_id: u64,
51// DWARF 351 form_id: u64,
52pub const TAG_dwarf_procedure = 0x36;52};
53pub const TAG_restrict_type = 0x37;53
54pub const TAG_interface_type = 0x38;54const FormValue = union(enum) {
55pub const TAG_namespace = 0x39;55 Address: u64,
56pub const TAG_imported_module = 0x3a;56 Block: []u8,
57pub const TAG_unspecified_type = 0x3b;57 Const: Constant,
58pub const TAG_partial_unit = 0x3c;58 ExprLoc: []u8,
59pub const TAG_imported_unit = 0x3d;59 Flag: bool,
60pub const TAG_condition = 0x3f;60 SecOffset: u64,
61pub const TAG_shared_type = 0x40;61 Ref: u64,
6262 RefAddr: u64,
63// DWARF 463 String: []const u8,
64pub const TAG_type_unit = 0x41;64 StrPtr: u64,
65pub const TAG_rvalue_reference_type = 0x42;65};
66pub const TAG_template_alias = 0x43;66
6767const Constant = struct {
68pub const TAG_lo_user = 0x4080;68 payload: u64,
69pub const TAG_hi_user = 0xffff;69 signed: bool,
7070
71// SGI/MIPS Extensions.71 fn asUnsignedLe(self: *const Constant) !u64 {
72pub const DW_TAG_MIPS_loop = 0x4081;72 if (self.signed) return error.InvalidDebugInfo;
7373 return self.payload;
74// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .74 }
75pub const TAG_HP_array_descriptor = 0x4090;75};
76pub const TAG_HP_Bliss_field = 0x4091;76
77pub const TAG_HP_Bliss_field_set = 0x4092;77const Die = struct {
7878 tag_id: u64,
79// GNU extensions.79 has_children: bool,
80pub const TAG_format_label = 0x4101; // For FORTRAN 77 and Fortran 90.80 attrs: ArrayList(Attr),
81pub const TAG_function_template = 0x4102; // For C++.81
82pub const TAG_class_template = 0x4103; //For C++.82 const Attr = struct {
83pub const TAG_GNU_BINCL = 0x4104;83 id: u64,
84pub const TAG_GNU_EINCL = 0x4105;84 value: FormValue,
8585 };
86// Template template parameter.86
87// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .87 fn getAttr(self: *const Die, id: u64) ?*const FormValue {
88pub const TAG_GNU_template_template_param = 0x4106;88 for (self.attrs.toSliceConst()) |*attr| {
8989 if (attr.id == id) return &attr.value;
90// Template parameter pack extension = specified at90 }
91// http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates91 return null;
92// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags92 }
93// are properly part of DWARF 5.93
94pub const TAG_GNU_template_parameter_pack = 0x4107;94 fn getAttrAddr(self: *const Die, id: u64) !u64 {
95pub const TAG_GNU_formal_parameter_pack = 0x4108;95 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
96// The GNU call site extension = specified at96 return switch (form_value.*) {
97// http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .97 FormValue.Address => |value| value,
98// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags98 else => error.InvalidDebugInfo,
99// are properly part of DWARF 5.99 };
100pub const TAG_GNU_call_site = 0x4109;100 }
101pub const TAG_GNU_call_site_parameter = 0x410a;101
102// Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf.102 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
103pub const TAG_upc_shared_type = 0x8765;103 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
104pub const TAG_upc_strict_type = 0x8766;104 return switch (form_value.*) {
105pub const TAG_upc_relaxed_type = 0x8767;105 FormValue.Const => |value| value.asUnsignedLe(),
106// PGI (STMicroelectronics; extensions. No documentation available.106 FormValue.SecOffset => |value| value,
107pub const TAG_PGI_kanji_type = 0xA000;107 else => error.InvalidDebugInfo,
108pub const TAG_PGI_interface_block = 0xA020;108 };
109109 }
110pub const FORM_addr = 0x01;110
111pub const FORM_block2 = 0x03;111 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
112pub const FORM_block4 = 0x04;112 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
113pub const FORM_data2 = 0x05;113 return switch (form_value.*) {
114pub const FORM_data4 = 0x06;114 FormValue.Const => |value| value.asUnsignedLe(),
115pub const FORM_data8 = 0x07;115 else => error.InvalidDebugInfo,
116pub const FORM_string = 0x08;116 };
117pub const FORM_block = 0x09;117 }
118pub const FORM_block1 = 0x0a;118
119pub const FORM_data1 = 0x0b;119 fn getAttrRef(self: *const Die, id: u64) !u64 {
120pub const FORM_flag = 0x0c;120 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
121pub const FORM_sdata = 0x0d;121 return switch (form_value.*) {
122pub const FORM_strp = 0x0e;122 FormValue.Ref => |value| value,
123pub const FORM_udata = 0x0f;123 else => error.InvalidDebugInfo,
124pub const FORM_ref_addr = 0x10;124 };
125pub const FORM_ref1 = 0x11;125 }
126pub const FORM_ref2 = 0x12;126
127pub const FORM_ref4 = 0x13;127 fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]const u8 {
128pub const FORM_ref8 = 0x14;128 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
129pub const FORM_ref_udata = 0x15;129 return switch (form_value.*) {
130pub const FORM_indirect = 0x16;130 FormValue.String => |value| value,
131pub const FORM_sec_offset = 0x17;131 FormValue.StrPtr => |offset| di.getString(offset),
132pub const FORM_exprloc = 0x18;132 else => error.InvalidDebugInfo,
133pub const FORM_flag_present = 0x19;133 };
134pub const FORM_ref_sig8 = 0x20;134 }
135135};
136// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.136
137pub const FORM_GNU_addr_index = 0x1f01;137const FileEntry = struct {
138pub const FORM_GNU_str_index = 0x1f02;138 file_name: []const u8,
139139 dir_index: usize,
140// Extensions for DWZ multifile.140 mtime: usize,
141// See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open .141 len_bytes: usize,
142pub const FORM_GNU_ref_alt = 0x1f20;142};
143pub const FORM_GNU_strp_alt = 0x1f21;143
144144const LineNumberProgram = struct {
145pub const AT_sibling = 0x01;145 address: usize,
146pub const AT_location = 0x02;146 file: usize,
147pub const AT_name = 0x03;147 line: i64,
148pub const AT_ordering = 0x09;148 column: u64,
149pub const AT_subscr_data = 0x0a;149 is_stmt: bool,
150pub const AT_byte_size = 0x0b;150 basic_block: bool,
151pub const AT_bit_offset = 0x0c;151 end_sequence: bool,
152pub const AT_bit_size = 0x0d;152
153pub const AT_element_list = 0x0f;153 default_is_stmt: bool,
154pub const AT_stmt_list = 0x10;154 target_address: usize,
155pub const AT_low_pc = 0x11;155 include_dirs: []const []const u8,
156pub const AT_high_pc = 0x12;156 file_entries: *ArrayList(FileEntry),
157pub const AT_language = 0x13;157
158pub const AT_member = 0x14;158 prev_address: usize,
159pub const AT_discr = 0x15;159 prev_file: usize,
160pub const AT_discr_value = 0x16;160 prev_line: i64,
161pub const AT_visibility = 0x17;161 prev_column: u64,
162pub const AT_import = 0x18;162 prev_is_stmt: bool,
163pub const AT_string_length = 0x19;163 prev_basic_block: bool,
164pub const AT_common_reference = 0x1a;164 prev_end_sequence: bool,
165pub const AT_comp_dir = 0x1b;165
166pub const AT_const_value = 0x1c;166 // Reset the state machine following the DWARF specification
167pub const AT_containing_type = 0x1d;167 pub fn reset(self: *LineNumberProgram) void {
168pub const AT_default_value = 0x1e;168 self.address = 0;
169pub const AT_inline = 0x20;169 self.file = 1;
170pub const AT_is_optional = 0x21;170 self.line = 1;
171pub const AT_lower_bound = 0x22;171 self.column = 0;
172pub const AT_producer = 0x25;172 self.is_stmt = self.default_is_stmt;
173pub const AT_prototyped = 0x27;173 self.basic_block = false;
174pub const AT_return_addr = 0x2a;174 self.end_sequence = false;
175pub const AT_start_scope = 0x2c;175 // Invalidate all the remaining fields
176pub const AT_bit_stride = 0x2e;176 self.prev_address = 0;
177pub const AT_upper_bound = 0x2f;177 self.prev_file = undefined;
178pub const AT_abstract_origin = 0x31;178 self.prev_line = undefined;
179pub const AT_accessibility = 0x32;179 self.prev_column = undefined;
180pub const AT_address_class = 0x33;180 self.prev_is_stmt = undefined;
181pub const AT_artificial = 0x34;181 self.prev_basic_block = undefined;
182pub const AT_base_types = 0x35;182 self.prev_end_sequence = undefined;
183pub const AT_calling_convention = 0x36;183 }
184pub const AT_count = 0x37;184
185pub const AT_data_member_location = 0x38;185 pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
186pub const AT_decl_column = 0x39;186 return LineNumberProgram{
187pub const AT_decl_file = 0x3a;187 .address = 0,
188pub const AT_decl_line = 0x3b;188 .file = 1,
189pub const AT_declaration = 0x3c;189 .line = 1,
190pub const AT_discr_list = 0x3d;190 .column = 0,
191pub const AT_encoding = 0x3e;191 .is_stmt = is_stmt,
192pub const AT_external = 0x3f;192 .basic_block = false,
193pub const AT_frame_base = 0x40;193 .end_sequence = false,
194pub const AT_friend = 0x41;194 .include_dirs = include_dirs,
195pub const AT_identifier_case = 0x42;195 .file_entries = file_entries,
196pub const AT_macro_info = 0x43;196 .default_is_stmt = is_stmt,
197pub const AT_namelist_items = 0x44;197 .target_address = target_address,
198pub const AT_priority = 0x45;198 .prev_address = 0,
199pub const AT_segment = 0x46;199 .prev_file = undefined,
200pub const AT_specification = 0x47;200 .prev_line = undefined,
201pub const AT_static_link = 0x48;201 .prev_column = undefined,
202pub const AT_type = 0x49;202 .prev_is_stmt = undefined,
203pub const AT_use_location = 0x4a;203 .prev_basic_block = undefined,
204pub const AT_variable_parameter = 0x4b;204 .prev_end_sequence = undefined,
205pub const AT_virtuality = 0x4c;205 };
206pub const AT_vtable_elem_location = 0x4d;206 }
207207
208// DWARF 3 values.208 pub fn checkLineMatch(self: *LineNumberProgram) !?debug.LineInfo {
209pub const AT_allocated = 0x4e;209 if (self.target_address >= self.prev_address and self.target_address < self.address) {
210pub const AT_associated = 0x4f;210 const file_entry = if (self.prev_file == 0) {
211pub const AT_data_location = 0x50;211 return error.MissingDebugInfo;
212pub const AT_byte_stride = 0x51;212 } else if (self.prev_file - 1 >= self.file_entries.len) {
213pub const AT_entry_pc = 0x52;213 return error.InvalidDebugInfo;
214pub const AT_use_UTF8 = 0x53;214 } else
215pub const AT_extension = 0x54;215 &self.file_entries.items[self.prev_file - 1];
216pub const AT_ranges = 0x55;216
217pub const AT_trampoline = 0x56;217 const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
218pub const AT_call_column = 0x57;218 return error.InvalidDebugInfo;
219pub const AT_call_file = 0x58;219 } else
220pub const AT_call_line = 0x59;220 self.include_dirs[file_entry.dir_index];
221pub const AT_description = 0x5a;221 const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name });
222pub const AT_binary_scale = 0x5b;222 errdefer self.file_entries.allocator.free(file_name);
223pub const AT_decimal_scale = 0x5c;223 return debug.LineInfo{
224pub const AT_small = 0x5d;224 .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
225pub const AT_decimal_sign = 0x5e;225 .column = self.prev_column,
226pub const AT_digit_count = 0x5f;226 .file_name = file_name,
227pub const AT_picture_string = 0x60;227 .allocator = self.file_entries.allocator,
228pub const AT_mutable = 0x61;228 };
229pub const AT_threads_scaled = 0x62;229 }
230pub const AT_explicit = 0x63;230
231pub const AT_object_pointer = 0x64;231 self.prev_address = self.address;
232pub const AT_endianity = 0x65;232 self.prev_file = self.file;
233pub const AT_elemental = 0x66;233 self.prev_line = self.line;
234pub const AT_pure = 0x67;234 self.prev_column = self.column;
235pub const AT_recursive = 0x68;235 self.prev_is_stmt = self.is_stmt;
236236 self.prev_basic_block = self.basic_block;
237// DWARF 4.237 self.prev_end_sequence = self.end_sequence;
238pub const AT_signature = 0x69;238 return null;
239pub const AT_main_subprogram = 0x6a;239 }
240pub const AT_data_bit_offset = 0x6b;240};
241pub const AT_const_expr = 0x6c;241
242pub const AT_enum_class = 0x6d;242fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
243pub const AT_linkage_name = 0x6e;243 const first_32_bits = try in_stream.readIntLittle(u32);
244244 is_64.* = (first_32_bits == 0xffffffff);
245// DWARF 5245 if (is_64.*) {
246pub const AT_alignment = 0x88;246 return in_stream.readIntLittle(u64);
247247 } else {
248pub const AT_lo_user = 0x2000; // Implementation-defined range start.248 if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
249pub const AT_hi_user = 0x3fff; // Implementation-defined range end.249 // TODO this cast should not be needed
250250 return @as(u64, first_32_bits);
251// SGI/MIPS extensions.251 }
252pub const AT_MIPS_fde = 0x2001;252}
253pub const AT_MIPS_loop_begin = 0x2002;253
254pub const AT_MIPS_tail_loop_begin = 0x2003;254// TODO the noasyncs here are workarounds
255pub const AT_MIPS_epilog_begin = 0x2004;255fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
256pub const AT_MIPS_loop_unroll_factor = 0x2005;256 const buf = try allocator.alloc(u8, size);
257pub const AT_MIPS_software_pipeline_depth = 0x2006;257 errdefer allocator.free(buf);
258pub const AT_MIPS_linkage_name = 0x2007;258 if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile;
259pub const AT_MIPS_stride = 0x2008;259 return buf;
260pub const AT_MIPS_abstract_name = 0x2009;260}
261pub const AT_MIPS_clone_origin = 0x200a;261
262pub const AT_MIPS_has_inlines = 0x200b;262fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
263263 const buf = try readAllocBytes(allocator, in_stream, size);
264// HP extensions.264 return FormValue{ .Block = buf };
265pub const AT_HP_block_index = 0x2000;265}
266pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde.266
267pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll.267// TODO the noasyncs here are workarounds
268pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride.268fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
269pub const AT_HP_actuals_stmt_list = 0x2010;269 const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size);
270pub const AT_HP_proc_per_section = 0x2011;270 return parseFormValueBlockLen(allocator, in_stream, block_len);
271pub const AT_HP_raw_data_ptr = 0x2012;271}
272pub const AT_HP_pass_by_reference = 0x2013;272
273pub const AT_HP_opt_level = 0x2014;273fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {
274pub const AT_HP_prof_version_id = 0x2015;274 // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
275pub const AT_HP_opt_flags = 0x2016;275 // `noasync` should be removed from all the function calls once it is fixed.
276pub const AT_HP_cold_region_low_pc = 0x2017;276 return FormValue{
277pub const AT_HP_cold_region_high_pc = 0x2018;277 .Const = Constant{
278pub const AT_HP_all_variables_modifiable = 0x2019;278 .signed = signed,
279pub const AT_HP_linkage_name = 0x201a;279 .payload = switch (size) {
280pub const AT_HP_prof_flags = 0x201b; // In comp unit of procs_info for -g.280 1 => try noasync in_stream.readIntLittle(u8),
281pub const AT_HP_unit_name = 0x201f;281 2 => try noasync in_stream.readIntLittle(u16),
282pub const AT_HP_unit_size = 0x2020;282 4 => try noasync in_stream.readIntLittle(u32),
283pub const AT_HP_widened_byte_size = 0x2021;283 8 => try noasync in_stream.readIntLittle(u64),
284pub const AT_HP_definition_points = 0x2022;284 -1 => blk: {
285pub const AT_HP_default_location = 0x2023;285 if (signed) {
286pub const AT_HP_is_result_param = 0x2029;286 const x = try noasync leb.readILEB128(i64, in_stream);
287287 break :blk @bitCast(u64, x);
288// GNU extensions.288 } else {
289pub const AT_sf_names = 0x2101;289 const x = try noasync leb.readULEB128(u64, in_stream);
290pub const AT_src_info = 0x2102;290 break :blk x;
291pub const AT_mac_info = 0x2103;291 }
292pub const AT_src_coords = 0x2104;292 },
293pub const AT_body_begin = 0x2105;293 else => @compileError("Invalid size"),
294pub const AT_body_end = 0x2106;294 },
295pub const AT_GNU_vector = 0x2107;295 },
296// Thread-safety annotations.296 };
297// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation .297}
298pub const AT_GNU_guarded_by = 0x2108;298
299pub const AT_GNU_pt_guarded_by = 0x2109;299// TODO the noasyncs here are workarounds
300pub const AT_GNU_guarded = 0x210a;300fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
301pub const AT_GNU_pt_guarded = 0x210b;301 return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32));
302pub const AT_GNU_locks_excluded = 0x210c;302}
303pub const AT_GNU_exclusive_locks_required = 0x210d;303
304pub const AT_GNU_shared_locks_required = 0x210e;304// TODO the noasyncs here are workarounds
305// One-definition rule violation detection.305fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
306// See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo .306 if (@sizeOf(usize) == 4) {
307pub const AT_GNU_odr_signature = 0x210f;307 // TODO this cast should not be needed
308// Template template argument name.308 return @as(u64, try noasync in_stream.readIntLittle(u32));
309// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .309 } else if (@sizeOf(usize) == 8) {
310pub const AT_GNU_template_name = 0x2110;310 return noasync in_stream.readIntLittle(u64);
311// The GNU call site extension.311 } else {
312// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .312 unreachable;
313pub const AT_GNU_call_site_value = 0x2111;313 }
314pub const AT_GNU_call_site_data_value = 0x2112;314}
315pub const AT_GNU_call_site_target = 0x2113;315
316pub const AT_GNU_call_site_target_clobbered = 0x2114;316// TODO the noasyncs here are workarounds
317pub const AT_GNU_tail_call = 0x2115;317fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
318pub const AT_GNU_all_tail_call_sites = 0x2116;318 return FormValue{
319pub const AT_GNU_all_call_sites = 0x2117;319 .Ref = switch (size) {
320pub const AT_GNU_all_source_call_sites = 0x2118;320 1 => try noasync in_stream.readIntLittle(u8),
321// Section offset into .debug_macro section.321 2 => try noasync in_stream.readIntLittle(u16),
322pub const AT_GNU_macros = 0x2119;322 4 => try noasync in_stream.readIntLittle(u32),
323// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.323 8 => try noasync in_stream.readIntLittle(u64),
324pub const AT_GNU_dwo_name = 0x2130;324 -1 => try noasync leb.readULEB128(u64, in_stream),
325pub const AT_GNU_dwo_id = 0x2131;325 else => unreachable,
326pub const AT_GNU_ranges_base = 0x2132;326 },
327pub const AT_GNU_addr_base = 0x2133;327 };
328pub const AT_GNU_pubnames = 0x2134;328}
329pub const AT_GNU_pubtypes = 0x2135;329
330// VMS extensions.330// TODO the noasyncs here are workarounds
331pub const AT_VMS_rtnbeg_pd_address = 0x2201;331fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
332// GNAT extensions.332 return switch (form_id) {
333// GNAT descriptive type.333 FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
334// See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type .334 FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
335pub const AT_use_GNAT_descriptive_type = 0x2301;335 FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
336pub const AT_GNAT_descriptive_type = 0x2302;336 FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
337// UPC extension.337 FORM_block => x: {
338pub const AT_upc_threads_scaled = 0x3210;338 const block_len = try noasync leb.readULEB128(usize, in_stream);
339// PGI (STMicroelectronics) extensions.339 return parseFormValueBlockLen(allocator, in_stream, block_len);
340pub const AT_PGI_lbase = 0x3a00;340 },
341pub const AT_PGI_soffset = 0x3a01;341 FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
342pub const AT_PGI_lstride = 0x3a02;342 FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
343343 FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
344pub const OP_addr = 0x03;344 FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
345pub const OP_deref = 0x06;345 FORM_udata, FORM_sdata => {
346pub const OP_const1u = 0x08;346 const signed = form_id == FORM_sdata;
347pub const OP_const1s = 0x09;347 return parseFormValueConstant(allocator, in_stream, signed, -1);
348pub const OP_const2u = 0x0a;348 },
349pub const OP_const2s = 0x0b;349 FORM_exprloc => {
350pub const OP_const4u = 0x0c;350 const size = try noasync leb.readULEB128(usize, in_stream);
351pub const OP_const4s = 0x0d;351 const buf = try readAllocBytes(allocator, in_stream, size);
352pub const OP_const8u = 0x0e;352 return FormValue{ .ExprLoc = buf };
353pub const OP_const8s = 0x0f;353 },
354pub const OP_constu = 0x10;354 FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 },
355pub const OP_consts = 0x11;355 FORM_flag_present => FormValue{ .Flag = true },
356pub const OP_dup = 0x12;356 FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
357pub const OP_drop = 0x13;357
358pub const OP_over = 0x14;358 FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
359pub const OP_pick = 0x15;359 FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
360pub const OP_swap = 0x16;360 FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
361pub const OP_rot = 0x17;361 FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
362pub const OP_xderef = 0x18;362 FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
363pub const OP_abs = 0x19;363
364pub const OP_and = 0x1a;364 FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
365pub const OP_div = 0x1b;365 FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) },
366pub const OP_minus = 0x1c;366
367pub const OP_mod = 0x1d;367 FORM_string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },
368pub const OP_mul = 0x1e;368 FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
369pub const OP_neg = 0x1f;369 FORM_indirect => {
370pub const OP_not = 0x20;370 const child_form_id = try noasync leb.readULEB128(u64, in_stream);
371pub const OP_or = 0x21;371 const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64));
372pub const OP_plus = 0x22;372 var frame = try allocator.create(F);
373pub const OP_plus_uconst = 0x23;373 defer allocator.destroy(frame);
374pub const OP_shl = 0x24;374 return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64);
375pub const OP_shr = 0x25;375 },
376pub const OP_shra = 0x26;376 else => error.InvalidDebugInfo,
377pub const OP_xor = 0x27;377 };
378pub const OP_bra = 0x28;378}
379pub const OP_eq = 0x29;379
380pub const OP_ge = 0x2a;380fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
381pub const OP_gt = 0x2b;381 for (abbrev_table.toSliceConst()) |*table_entry| {
382pub const OP_le = 0x2c;382 if (table_entry.abbrev_code == abbrev_code) return table_entry;
383pub const OP_lt = 0x2d;383 }
384pub const OP_ne = 0x2e;384 return null;
385pub const OP_skip = 0x2f;385}
386pub const OP_lit0 = 0x30;386
387pub const OP_lit1 = 0x31;387pub const DwarfInfo = struct {
388pub const OP_lit2 = 0x32;388 endian: builtin.Endian,
389pub const OP_lit3 = 0x33;389 // No memory is owned by the DwarfInfo
390pub const OP_lit4 = 0x34;390 debug_info: []const u8,
391pub const OP_lit5 = 0x35;391 debug_abbrev: []const u8,
392pub const OP_lit6 = 0x36;392 debug_str: []const u8,
393pub const OP_lit7 = 0x37;393 debug_line: []const u8,
394pub const OP_lit8 = 0x38;394 debug_ranges: ?[]const u8,
395pub const OP_lit9 = 0x39;395 // Filled later by the initializer
396pub const OP_lit10 = 0x3a;396 abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined,
397pub const OP_lit11 = 0x3b;397 compile_unit_list: ArrayList(CompileUnit) = undefined,
398pub const OP_lit12 = 0x3c;398 func_list: ArrayList(Func) = undefined,
399pub const OP_lit13 = 0x3d;399
400pub const OP_lit14 = 0x3e;400 pub fn allocator(self: DwarfInfo) *mem.Allocator {
401pub const OP_lit15 = 0x3f;401 return self.abbrev_table_list.allocator;
402pub const OP_lit16 = 0x40;402 }
403pub const OP_lit17 = 0x41;403
404pub const OP_lit18 = 0x42;404 fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
405pub const OP_lit19 = 0x43;405 for (di.func_list.toSliceConst()) |*func| {
406pub const OP_lit20 = 0x44;406 if (func.pc_range) |range| {
407pub const OP_lit21 = 0x45;407 if (address >= range.start and address < range.end) {
408pub const OP_lit22 = 0x46;408 return func.name;
409pub const OP_lit23 = 0x47;409 }
410pub const OP_lit24 = 0x48;410 }
411pub const OP_lit25 = 0x49;411 }
412pub const OP_lit26 = 0x4a;412
413pub const OP_lit27 = 0x4b;413 return null;
414pub const OP_lit28 = 0x4c;414 }
415pub const OP_lit29 = 0x4d;415
416pub const OP_lit30 = 0x4e;416 fn scanAllFunctions(di: *DwarfInfo) !void {
417pub const OP_lit31 = 0x4f;417 var s = io.SliceSeekableInStream.init(di.debug_info);
418pub const OP_reg0 = 0x50;418 var this_unit_offset: u64 = 0;
419pub const OP_reg1 = 0x51;419
420pub const OP_reg2 = 0x52;420 while (true) {
421pub const OP_reg3 = 0x53;421 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
422pub const OP_reg4 = 0x54;422 error.EndOfStream => return,
423pub const OP_reg5 = 0x55;423 else => return err,
424pub const OP_reg6 = 0x56;424 };
425pub const OP_reg7 = 0x57;425
426pub const OP_reg8 = 0x58;426 var is_64: bool = undefined;
427pub const OP_reg9 = 0x59;427 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
428pub const OP_reg10 = 0x5a;428 if (unit_length == 0) return;
429pub const OP_reg11 = 0x5b;429 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
430pub const OP_reg12 = 0x5c;430
431pub const OP_reg13 = 0x5d;431 const version = try s.stream.readInt(u16, di.endian);
432pub const OP_reg14 = 0x5e;432 if (version < 2 or version > 5) return error.InvalidDebugInfo;
433pub const OP_reg15 = 0x5f;433
434pub const OP_reg16 = 0x60;434 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
435pub const OP_reg17 = 0x61;435
436pub const OP_reg18 = 0x62;436 const address_size = try s.stream.readByte();
437pub const OP_reg19 = 0x63;437 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
438pub const OP_reg20 = 0x64;438
439pub const OP_reg21 = 0x65;439 const compile_unit_pos = try s.seekable_stream.getPos();
440pub const OP_reg22 = 0x66;440 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
441pub const OP_reg23 = 0x67;441
442pub const OP_reg24 = 0x68;442 try s.seekable_stream.seekTo(compile_unit_pos);
443pub const OP_reg25 = 0x69;443
444pub const OP_reg26 = 0x6a;444 const next_unit_pos = this_unit_offset + next_offset;
445pub const OP_reg27 = 0x6b;445
446pub const OP_reg28 = 0x6c;446 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
447pub const OP_reg29 = 0x6d;447 const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
448pub const OP_reg30 = 0x6e;448 const after_die_offset = try s.seekable_stream.getPos();
449pub const OP_reg31 = 0x6f;449
450pub const OP_breg0 = 0x70;450 switch (die_obj.tag_id) {
451pub const OP_breg1 = 0x71;451 TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {
452pub const OP_breg2 = 0x72;452 const fn_name = x: {
453pub const OP_breg3 = 0x73;453 var depth: i32 = 3;
454pub const OP_breg4 = 0x74;454 var this_die_obj = die_obj;
455pub const OP_breg5 = 0x75;455 // Prenvent endless loops
456pub const OP_breg6 = 0x76;456 while (depth > 0) : (depth -= 1) {
457pub const OP_breg7 = 0x77;457 if (this_die_obj.getAttr(AT_name)) |_| {
458pub const OP_breg8 = 0x78;458 const name = try this_die_obj.getAttrString(di, AT_name);
459pub const OP_breg9 = 0x79;459 break :x name;
460pub const OP_breg10 = 0x7a;460 } else if (this_die_obj.getAttr(AT_abstract_origin)) |ref| {
461pub const OP_breg11 = 0x7b;461 // Follow the DIE it points to and repeat
462pub const OP_breg12 = 0x7c;462 const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);
463pub const OP_breg13 = 0x7d;463 if (ref_offset > next_offset) return error.InvalidDebugInfo;
464pub const OP_breg14 = 0x7e;464 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
465pub const OP_breg15 = 0x7f;465 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
466pub const OP_breg16 = 0x80;466 } else if (this_die_obj.getAttr(AT_specification)) |ref| {
467pub const OP_breg17 = 0x81;467 // Follow the DIE it points to and repeat
468pub const OP_breg18 = 0x82;468 const ref_offset = try this_die_obj.getAttrRef(AT_specification);
469pub const OP_breg19 = 0x83;469 if (ref_offset > next_offset) return error.InvalidDebugInfo;
470pub const OP_breg20 = 0x84;470 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
471pub const OP_breg21 = 0x85;471 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
472pub const OP_breg22 = 0x86;472 } else {
473pub const OP_breg23 = 0x87;473 break :x null;
474pub const OP_breg24 = 0x88;474 }
475pub const OP_breg25 = 0x89;475 }
476pub const OP_breg26 = 0x8a;476
477pub const OP_breg27 = 0x8b;477 break :x null;
478pub const OP_breg28 = 0x8c;478 };
479pub const OP_breg29 = 0x8d;479
480pub const OP_breg30 = 0x8e;480 const pc_range = x: {
481pub const OP_breg31 = 0x8f;481 if (die_obj.getAttrAddr(AT_low_pc)) |low_pc| {
482pub const OP_regx = 0x90;482 if (die_obj.getAttr(AT_high_pc)) |high_pc_value| {
483pub const OP_fbreg = 0x91;483 const pc_end = switch (high_pc_value.*) {
484pub const OP_bregx = 0x92;484 FormValue.Address => |value| value,
485pub const OP_piece = 0x93;485 FormValue.Const => |value| b: {
486pub const OP_deref_size = 0x94;486 const offset = try value.asUnsignedLe();
487pub const OP_xderef_size = 0x95;487 break :b (low_pc + offset);
488pub const OP_nop = 0x96;488 },
489489 else => return error.InvalidDebugInfo,
490// DWARF 3 extensions.490 };
491pub const OP_push_object_address = 0x97;491 break :x PcRange{
492pub const OP_call2 = 0x98;492 .start = low_pc,
493pub const OP_call4 = 0x99;493 .end = pc_end,
494pub const OP_call_ref = 0x9a;494 };
495pub const OP_form_tls_address = 0x9b;495 } else {
496pub const OP_call_frame_cfa = 0x9c;496 break :x null;
497pub const OP_bit_piece = 0x9d;497 }
498498 } else |err| {
499// DWARF 4 extensions.499 if (err != error.MissingDebugInfo) return err;
500pub const OP_implicit_value = 0x9e;500 break :x null;
501pub const OP_stack_value = 0x9f;501 }
502502 };
503pub const OP_lo_user = 0xe0; // Implementation-defined range start.503
504pub const OP_hi_user = 0xff; // Implementation-defined range end.504 try di.func_list.append(Func{
505505 .name = fn_name,
506// GNU extensions.506 .pc_range = pc_range,
507pub const OP_GNU_push_tls_address = 0xe0;507 });
508// The following is for marking variables that are uninitialized.508 },
509pub const OP_GNU_uninit = 0xf0;509 else => {},
510pub const OP_GNU_encoded_addr = 0xf1;510 }
511// The GNU implicit pointer extension.511
512// See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open .512 try s.seekable_stream.seekTo(after_die_offset);
513pub const OP_GNU_implicit_pointer = 0xf2;513 }
514// The GNU entry value extension.514
515// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open .515 this_unit_offset += next_offset;
516pub const OP_GNU_entry_value = 0xf3;516 }
517// The GNU typed stack extension.517 }
518// See http://www.dwarfstd.org/doc/040408.1.html .518
519pub const OP_GNU_const_type = 0xf4;519 fn scanAllCompileUnits(di: *DwarfInfo) !void {
520pub const OP_GNU_regval_type = 0xf5;520 var s = io.SliceSeekableInStream.init(di.debug_info);
521pub const OP_GNU_deref_type = 0xf6;521 var this_unit_offset: u64 = 0;
522pub const OP_GNU_convert = 0xf7;522
523pub const OP_GNU_reinterpret = 0xf9;523 while (true) {
524// The GNU parameter ref extension.524 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
525pub const OP_GNU_parameter_ref = 0xfa;525 error.EndOfStream => return,
526// Extension for Fission. See http://gcc.gnu.org/wiki/DebugFission.526 else => return err,
527pub const OP_GNU_addr_index = 0xfb;527 };
528pub const OP_GNU_const_index = 0xfc;528
529// HP extensions.529 var is_64: bool = undefined;
530pub const OP_HP_unknown = 0xe0; // Ouch, the same as GNU_push_tls_address.530 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
531pub const OP_HP_is_value = 0xe1;531 if (unit_length == 0) return;
532pub const OP_HP_fltconst4 = 0xe2;532 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
533pub const OP_HP_fltconst8 = 0xe3;533
534pub const OP_HP_mod_range = 0xe4;534 const version = try s.stream.readInt(u16, di.endian);
535pub const OP_HP_unmod_range = 0xe5;535 if (version < 2 or version > 5) return error.InvalidDebugInfo;
536pub const OP_HP_tls = 0xe6;536
537// PGI (STMicroelectronics) extensions.537 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
538pub const OP_PGI_omp_thread_num = 0xf8;538
539539 const address_size = try s.stream.readByte();
540pub const ATE_void = 0x0;540 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
541pub const ATE_address = 0x1;541
542pub const ATE_boolean = 0x2;542 const compile_unit_pos = try s.seekable_stream.getPos();
543pub const ATE_complex_float = 0x3;543 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
544pub const ATE_float = 0x4;544
545pub const ATE_signed = 0x5;545 try s.seekable_stream.seekTo(compile_unit_pos);
546pub const ATE_signed_char = 0x6;546
547pub const ATE_unsigned = 0x7;547 const compile_unit_die = try di.allocator().create(Die);
548pub const ATE_unsigned_char = 0x8;548 compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
549549
550// DWARF 3.550 if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;
551pub const ATE_imaginary_float = 0x9;551
552pub const ATE_packed_decimal = 0xa;552 const pc_range = x: {
553pub const ATE_numeric_string = 0xb;553 if (compile_unit_die.getAttrAddr(AT_low_pc)) |low_pc| {
554pub const ATE_edited = 0xc;554 if (compile_unit_die.getAttr(AT_high_pc)) |high_pc_value| {
555pub const ATE_signed_fixed = 0xd;555 const pc_end = switch (high_pc_value.*) {
556pub const ATE_unsigned_fixed = 0xe;556 FormValue.Address => |value| value,
557pub const ATE_decimal_float = 0xf;557 FormValue.Const => |value| b: {
558558 const offset = try value.asUnsignedLe();
559// DWARF 4.559 break :b (low_pc + offset);
560pub const ATE_UTF = 0x10;560 },
561561 else => return error.InvalidDebugInfo,
562pub const ATE_lo_user = 0x80;562 };
563pub const ATE_hi_user = 0xff;563 break :x PcRange{
564564 .start = low_pc,
565// HP extensions.565 .end = pc_end,
566pub const ATE_HP_float80 = 0x80; // Floating-point (80 bit).566 };
567pub const ATE_HP_complex_float80 = 0x81; // Complex floating-point (80 bit).567 } else {
568pub const ATE_HP_float128 = 0x82; // Floating-point (128 bit).568 break :x null;
569pub const ATE_HP_complex_float128 = 0x83; // Complex fp (128 bit).569 }
570pub const ATE_HP_floathpintel = 0x84; // Floating-point (82 bit IA64).570 } else |err| {
571pub const ATE_HP_imaginary_float80 = 0x85;571 if (err != error.MissingDebugInfo) return err;
572pub const ATE_HP_imaginary_float128 = 0x86;572 break :x null;
573pub const ATE_HP_VAX_float = 0x88; // F or G floating.573 }
574pub const ATE_HP_VAX_float_d = 0x89; // D floating.574 };
575pub const ATE_HP_packed_decimal = 0x8a; // Cobol.575
576pub const ATE_HP_zoned_decimal = 0x8b; // Cobol.576 try di.compile_unit_list.append(CompileUnit{
577pub const ATE_HP_edited = 0x8c; // Cobol.577 .version = version,
578pub const ATE_HP_signed_fixed = 0x8d; // Cobol.578 .is_64 = is_64,
579pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol.579 .pc_range = pc_range,
580pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex.580 .die = compile_unit_die,
581pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex.581 });
582582
583pub const CFA_advance_loc = 0x40;583 this_unit_offset += next_offset;
584pub const CFA_offset = 0x80;584 }
585pub const CFA_restore = 0xc0;585 }
586pub const CFA_nop = 0x00;586
587pub const CFA_set_loc = 0x01;587 fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
588pub const CFA_advance_loc1 = 0x02;588 for (di.compile_unit_list.toSlice()) |*compile_unit| {
589pub const CFA_advance_loc2 = 0x03;589 if (compile_unit.pc_range) |range| {
590pub const CFA_advance_loc4 = 0x04;590 if (target_address >= range.start and target_address < range.end) return compile_unit;
591pub const CFA_offset_extended = 0x05;591 }
592pub const CFA_restore_extended = 0x06;592 if (di.debug_ranges) |debug_ranges| {
593pub const CFA_undefined = 0x07;593 if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {
594pub const CFA_same_value = 0x08;594 var s = io.SliceSeekableInStream.init(debug_ranges);
595pub const CFA_register = 0x09;595
596pub const CFA_remember_state = 0x0a;596 // All the addresses in the list are relative to the value
597pub const CFA_restore_state = 0x0b;597 // specified by DW_AT_low_pc or to some other value encoded
598pub const CFA_def_cfa = 0x0c;598 // in the list itself.
599pub const CFA_def_cfa_register = 0x0d;599 // If no starting value is specified use zero.
600pub const CFA_def_cfa_offset = 0x0e;600 var base_address = compile_unit.die.getAttrAddr(AT_low_pc) catch |err| switch (err) {
601601 error.MissingDebugInfo => 0,
602// DWARF 3.602 else => return err,
603pub const CFA_def_cfa_expression = 0x0f;603 };
604pub const CFA_expression = 0x10;604
605pub const CFA_offset_extended_sf = 0x11;605 try s.seekable_stream.seekTo(ranges_offset);
606pub const CFA_def_cfa_sf = 0x12;606
607pub const CFA_def_cfa_offset_sf = 0x13;607 while (true) {
608pub const CFA_val_offset = 0x14;608 const begin_addr = try s.stream.readIntLittle(usize);
609pub const CFA_val_offset_sf = 0x15;609 const end_addr = try s.stream.readIntLittle(usize);
610pub const CFA_val_expression = 0x16;610 if (begin_addr == 0 and end_addr == 0) {
611611 break;
612pub const CFA_lo_user = 0x1c;612 }
613pub const CFA_hi_user = 0x3f;613 // This entry selects a new value for the base address
614614 if (begin_addr == math.maxInt(usize)) {
615// SGI/MIPS specific.615 base_address = end_addr;
616pub const CFA_MIPS_advance_loc8 = 0x1d;616 continue;
617617 }
618// GNU extensions.618 if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
619pub const CFA_GNU_window_save = 0x2d;619 return compile_unit;
620pub const CFA_GNU_args_size = 0x2e;620 }
621pub const CFA_GNU_negative_offset_extended = 0x2f;621 }
622622 } else |err| {
623pub const CHILDREN_no = 0x00;623 if (err != error.MissingDebugInfo) return err;
624pub const CHILDREN_yes = 0x01;624 continue;
625625 }
626pub const LNS_extended_op = 0x00;626 }
627pub const LNS_copy = 0x01;627 }
628pub const LNS_advance_pc = 0x02;628 return error.MissingDebugInfo;
629pub const LNS_advance_line = 0x03;629 }
630pub const LNS_set_file = 0x04;630
631pub const LNS_set_column = 0x05;631 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
632pub const LNS_negate_stmt = 0x06;632 /// seeks in the stream and parses it.
633pub const LNS_set_basic_block = 0x07;633 fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
634pub const LNS_const_add_pc = 0x08;634 for (di.abbrev_table_list.toSlice()) |*header| {
635pub const LNS_fixed_advance_pc = 0x09;635 if (header.offset == abbrev_offset) {
636pub const LNS_set_prologue_end = 0x0a;636 return &header.table;
637pub const LNS_set_epilogue_begin = 0x0b;637 }
638pub const LNS_set_isa = 0x0c;638 }
639639 try di.abbrev_table_list.append(AbbrevTableHeader{
640pub const LNE_end_sequence = 0x01;640 .offset = abbrev_offset,
641pub const LNE_set_address = 0x02;641 .table = try di.parseAbbrevTable(abbrev_offset),
642pub const LNE_define_file = 0x03;642 });
643pub const LNE_set_discriminator = 0x04;643 return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
644pub const LNE_lo_user = 0x80;644 }
645pub const LNE_hi_user = 0xff;645
646646 fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
647pub const LANG_C89 = 0x0001;647 var s = io.SliceSeekableInStream.init(di.debug_abbrev);
648pub const LANG_C = 0x0002;648
649pub const LANG_Ada83 = 0x0003;649 try s.seekable_stream.seekTo(offset);
650pub const LANG_C_plus_plus = 0x0004;650 var result = AbbrevTable.init(di.allocator());
651pub const LANG_Cobol74 = 0x0005;651 errdefer result.deinit();
652pub const LANG_Cobol85 = 0x0006;652 while (true) {
653pub const LANG_Fortran77 = 0x0007;653 const abbrev_code = try leb.readULEB128(u64, &s.stream);
654pub const LANG_Fortran90 = 0x0008;654 if (abbrev_code == 0) return result;
655pub const LANG_Pascal83 = 0x0009;655 try result.append(AbbrevTableEntry{
656pub const LANG_Modula2 = 0x000a;656 .abbrev_code = abbrev_code,
657pub const LANG_Java = 0x000b;657 .tag_id = try leb.readULEB128(u64, &s.stream),
658pub const LANG_C99 = 0x000c;658 .has_children = (try s.stream.readByte()) == CHILDREN_yes,
659pub const LANG_Ada95 = 0x000d;659 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
660pub const LANG_Fortran95 = 0x000e;660 });
661pub const LANG_PLI = 0x000f;661 const attrs = &result.items[result.len - 1].attrs;
662pub const LANG_ObjC = 0x0010;662
663pub const LANG_ObjC_plus_plus = 0x0011;663 while (true) {
664pub const LANG_UPC = 0x0012;664 const attr_id = try leb.readULEB128(u64, &s.stream);
665pub const LANG_D = 0x0013;665 const form_id = try leb.readULEB128(u64, &s.stream);
666pub const LANG_Python = 0x0014;666 if (attr_id == 0 and form_id == 0) break;
667pub const LANG_Go = 0x0016;667 try attrs.append(AbbrevAttr{
668pub const LANG_C_plus_plus_11 = 0x001a;668 .attr_id = attr_id,
669pub const LANG_Rust = 0x001c;669 .form_id = form_id,
670pub const LANG_C11 = 0x001d;670 });
671pub const LANG_C_plus_plus_14 = 0x0021;671 }
672pub const LANG_Fortran03 = 0x0022;672 }
673pub const LANG_Fortran08 = 0x0023;673 }
674pub const LANG_lo_user = 0x8000;674
675pub const LANG_hi_user = 0xffff;675 fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
676pub const LANG_Mips_Assembler = 0x8001;676 const abbrev_code = try leb.readULEB128(u64, in_stream);
677pub const LANG_Upc = 0x8765;677 if (abbrev_code == 0) return null;
678pub const LANG_HP_Bliss = 0x8003;678 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
679pub const LANG_HP_Basic91 = 0x8004;679
680pub const LANG_HP_Pascal91 = 0x8005;680 var result = Die{
681pub const LANG_HP_IMacro = 0x8006;681 .tag_id = table_entry.tag_id,
682pub const LANG_HP_Assembler = 0x8007;682 .has_children = table_entry.has_children,
683 .attrs = ArrayList(Die.Attr).init(di.allocator()),
684 };
685 try result.attrs.resize(table_entry.attrs.len);
686 for (table_entry.attrs.toSliceConst()) |attr, i| {
687 result.attrs.items[i] = Die.Attr{
688 .id = attr.attr_id,
689 .value = try parseFormValue(di.allocator(), in_stream, attr.form_id, is_64),
690 };
691 }
692 return result;
693 }
694
695 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !debug.LineInfo {
696 var s = io.SliceSeekableInStream.init(di.debug_line);
697
698 const compile_unit_cwd = try compile_unit.die.getAttrString(di, AT_comp_dir);
699 const line_info_offset = try compile_unit.die.getAttrSecOffset(AT_stmt_list);
700
701 try s.seekable_stream.seekTo(line_info_offset);
702
703 var is_64: bool = undefined;
704 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
705 if (unit_length == 0) {
706 return error.MissingDebugInfo;
707 }
708 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
709
710 const version = try s.stream.readInt(u16, di.endian);
711 // TODO support 3 and 5
712 if (version != 2 and version != 4) return error.InvalidDebugInfo;
713
714 const prologue_length = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
715 const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
716
717 const minimum_instruction_length = try s.stream.readByte();
718 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
719
720 if (version >= 4) {
721 // maximum_operations_per_instruction
722 _ = try s.stream.readByte();
723 }
724
725 const default_is_stmt = (try s.stream.readByte()) != 0;
726 const line_base = try s.stream.readByteSigned();
727
728 const line_range = try s.stream.readByte();
729 if (line_range == 0) return error.InvalidDebugInfo;
730
731 const opcode_base = try s.stream.readByte();
732
733 const standard_opcode_lengths = try di.allocator().alloc(u8, opcode_base - 1);
734 defer di.allocator().free(standard_opcode_lengths);
735
736 {
737 var i: usize = 0;
738 while (i < opcode_base - 1) : (i += 1) {
739 standard_opcode_lengths[i] = try s.stream.readByte();
740 }
741 }
742
743 var include_directories = ArrayList([]const u8).init(di.allocator());
744 try include_directories.append(compile_unit_cwd);
745 while (true) {
746 const dir = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
747 if (dir.len == 0) break;
748 try include_directories.append(dir);
749 }
750
751 var file_entries = ArrayList(FileEntry).init(di.allocator());
752 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
753
754 while (true) {
755 const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
756 if (file_name.len == 0) break;
757 const dir_index = try leb.readULEB128(usize, &s.stream);
758 const mtime = try leb.readULEB128(usize, &s.stream);
759 const len_bytes = try leb.readULEB128(usize, &s.stream);
760 try file_entries.append(FileEntry{
761 .file_name = file_name,
762 .dir_index = dir_index,
763 .mtime = mtime,
764 .len_bytes = len_bytes,
765 });
766 }
767
768 try s.seekable_stream.seekTo(prog_start_offset);
769
770 const next_unit_pos = line_info_offset + next_offset;
771
772 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
773 const opcode = try s.stream.readByte();
774
775 if (opcode == LNS_extended_op) {
776 const op_size = try leb.readULEB128(u64, &s.stream);
777 if (op_size < 1) return error.InvalidDebugInfo;
778 var sub_op = try s.stream.readByte();
779 switch (sub_op) {
780 LNE_end_sequence => {
781 prog.end_sequence = true;
782 if (try prog.checkLineMatch()) |info| return info;
783 prog.reset();
784 },
785 LNE_set_address => {
786 const addr = try s.stream.readInt(usize, di.endian);
787 prog.address = addr;
788 },
789 LNE_define_file => {
790 const file_name = try s.stream.readUntilDelimiterAlloc(di.allocator(), 0, math.maxInt(usize));
791 const dir_index = try leb.readULEB128(usize, &s.stream);
792 const mtime = try leb.readULEB128(usize, &s.stream);
793 const len_bytes = try leb.readULEB128(usize, &s.stream);
794 try file_entries.append(FileEntry{
795 .file_name = file_name,
796 .dir_index = dir_index,
797 .mtime = mtime,
798 .len_bytes = len_bytes,
799 });
800 },
801 else => {
802 const fwd_amt = math.cast(isize, op_size - 1) catch return error.InvalidDebugInfo;
803 try s.seekable_stream.seekBy(fwd_amt);
804 },
805 }
806 } else if (opcode >= opcode_base) {
807 // special opcodes
808 const adjusted_opcode = opcode - opcode_base;
809 const inc_addr = minimum_instruction_length * (adjusted_opcode / line_range);
810 const inc_line = @as(i32, line_base) + @as(i32, adjusted_opcode % line_range);
811 prog.line += inc_line;
812 prog.address += inc_addr;
813 if (try prog.checkLineMatch()) |info| return info;
814 prog.basic_block = false;
815 } else {
816 switch (opcode) {
817 LNS_copy => {
818 if (try prog.checkLineMatch()) |info| return info;
819 prog.basic_block = false;
820 },
821 LNS_advance_pc => {
822 const arg = try leb.readULEB128(usize, &s.stream);
823 prog.address += arg * minimum_instruction_length;
824 },
825 LNS_advance_line => {
826 const arg = try leb.readILEB128(i64, &s.stream);
827 prog.line += arg;
828 },
829 LNS_set_file => {
830 const arg = try leb.readULEB128(usize, &s.stream);
831 prog.file = arg;
832 },
833 LNS_set_column => {
834 const arg = try leb.readULEB128(u64, &s.stream);
835 prog.column = arg;
836 },
837 LNS_negate_stmt => {
838 prog.is_stmt = !prog.is_stmt;
839 },
840 LNS_set_basic_block => {
841 prog.basic_block = true;
842 },
843 LNS_const_add_pc => {
844 const inc_addr = minimum_instruction_length * ((255 - opcode_base) / line_range);
845 prog.address += inc_addr;
846 },
847 LNS_fixed_advance_pc => {
848 const arg = try s.stream.readInt(u16, di.endian);
849 prog.address += arg;
850 },
851 LNS_set_prologue_end => {},
852 else => {
853 if (opcode - 1 >= standard_opcode_lengths.len) return error.InvalidDebugInfo;
854 const len_bytes = standard_opcode_lengths[opcode - 1];
855 try s.seekable_stream.seekBy(len_bytes);
856 },
857 }
858 }
859 }
860
861 return error.MissingDebugInfo;
862 }
863
864 fn getString(di: *DwarfInfo, offset: u64) ![]const u8 {
865 if (offset > di.debug_str.len)
866 return error.InvalidDebugInfo;
867 const casted_offset = math.cast(usize, offset) catch
868 return error.InvalidDebugInfo;
869
870 // Valid strings always have a terminating zero byte
871 if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {
872 return di.debug_str[casted_offset..last];
873 }
874
875 return error.InvalidDebugInfo;
876 }
877};
878
879/// Initialize DWARF info. The caller has the responsibility to initialize most
880/// the DwarfInfo fields before calling. These fields can be left undefined:
881/// * abbrev_table_list
882/// * compile_unit_list
883pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: *mem.Allocator) !void {
884 di.abbrev_table_list = ArrayList(AbbrevTableHeader).init(allocator);
885 di.compile_unit_list = ArrayList(CompileUnit).init(allocator);
886 di.func_list = ArrayList(Func).init(allocator);
887 try di.scanAllFunctions();
888 try di.scanAllCompileUnits();
889}
lib/std/dwarf_bits.zig created+682
...@@ -0,0 +1,682 @@
1pub const TAG_padding = 0x00;
2pub const TAG_array_type = 0x01;
3pub const TAG_class_type = 0x02;
4pub const TAG_entry_point = 0x03;
5pub const TAG_enumeration_type = 0x04;
6pub const TAG_formal_parameter = 0x05;
7pub const TAG_imported_declaration = 0x08;
8pub const TAG_label = 0x0a;
9pub const TAG_lexical_block = 0x0b;
10pub const TAG_member = 0x0d;
11pub const TAG_pointer_type = 0x0f;
12pub const TAG_reference_type = 0x10;
13pub const TAG_compile_unit = 0x11;
14pub const TAG_string_type = 0x12;
15pub const TAG_structure_type = 0x13;
16pub const TAG_subroutine = 0x14;
17pub const TAG_subroutine_type = 0x15;
18pub const TAG_typedef = 0x16;
19pub const TAG_union_type = 0x17;
20pub const TAG_unspecified_parameters = 0x18;
21pub const TAG_variant = 0x19;
22pub const TAG_common_block = 0x1a;
23pub const TAG_common_inclusion = 0x1b;
24pub const TAG_inheritance = 0x1c;
25pub const TAG_inlined_subroutine = 0x1d;
26pub const TAG_module = 0x1e;
27pub const TAG_ptr_to_member_type = 0x1f;
28pub const TAG_set_type = 0x20;
29pub const TAG_subrange_type = 0x21;
30pub const TAG_with_stmt = 0x22;
31pub const TAG_access_declaration = 0x23;
32pub const TAG_base_type = 0x24;
33pub const TAG_catch_block = 0x25;
34pub const TAG_const_type = 0x26;
35pub const TAG_constant = 0x27;
36pub const TAG_enumerator = 0x28;
37pub const TAG_file_type = 0x29;
38pub const TAG_friend = 0x2a;
39pub const TAG_namelist = 0x2b;
40pub const TAG_namelist_item = 0x2c;
41pub const TAG_packed_type = 0x2d;
42pub const TAG_subprogram = 0x2e;
43pub const TAG_template_type_param = 0x2f;
44pub const TAG_template_value_param = 0x30;
45pub const TAG_thrown_type = 0x31;
46pub const TAG_try_block = 0x32;
47pub const TAG_variant_part = 0x33;
48pub const TAG_variable = 0x34;
49pub const TAG_volatile_type = 0x35;
50
51// DWARF 3
52pub const TAG_dwarf_procedure = 0x36;
53pub const TAG_restrict_type = 0x37;
54pub const TAG_interface_type = 0x38;
55pub const TAG_namespace = 0x39;
56pub const TAG_imported_module = 0x3a;
57pub const TAG_unspecified_type = 0x3b;
58pub const TAG_partial_unit = 0x3c;
59pub const TAG_imported_unit = 0x3d;
60pub const TAG_condition = 0x3f;
61pub const TAG_shared_type = 0x40;
62
63// DWARF 4
64pub const TAG_type_unit = 0x41;
65pub const TAG_rvalue_reference_type = 0x42;
66pub const TAG_template_alias = 0x43;
67
68pub const TAG_lo_user = 0x4080;
69pub const TAG_hi_user = 0xffff;
70
71// SGI/MIPS Extensions.
72pub const DW_TAG_MIPS_loop = 0x4081;
73
74// HP extensions. See: ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz .
75pub const TAG_HP_array_descriptor = 0x4090;
76pub const TAG_HP_Bliss_field = 0x4091;
77pub const TAG_HP_Bliss_field_set = 0x4092;
78
79// GNU extensions.
80pub const TAG_format_label = 0x4101; // For FORTRAN 77 and Fortran 90.
81pub const TAG_function_template = 0x4102; // For C++.
82pub const TAG_class_template = 0x4103; //For C++.
83pub const TAG_GNU_BINCL = 0x4104;
84pub const TAG_GNU_EINCL = 0x4105;
85
86// Template template parameter.
87// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .
88pub const TAG_GNU_template_template_param = 0x4106;
89
90// Template parameter pack extension = specified at
91// http://wiki.dwarfstd.org/index.php?title=C%2B%2B0x:_Variadic_templates
92// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
93// are properly part of DWARF 5.
94pub const TAG_GNU_template_parameter_pack = 0x4107;
95pub const TAG_GNU_formal_parameter_pack = 0x4108;
96// The GNU call site extension = specified at
97// http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .
98// The values of these two TAGS are in the DW_TAG_GNU_* space until the tags
99// are properly part of DWARF 5.
100pub const TAG_GNU_call_site = 0x4109;
101pub const TAG_GNU_call_site_parameter = 0x410a;
102// Extensions for UPC. See: http://dwarfstd.org/doc/DWARF4.pdf.
103pub const TAG_upc_shared_type = 0x8765;
104pub const TAG_upc_strict_type = 0x8766;
105pub const TAG_upc_relaxed_type = 0x8767;
106// PGI (STMicroelectronics; extensions. No documentation available.
107pub const TAG_PGI_kanji_type = 0xA000;
108pub const TAG_PGI_interface_block = 0xA020;
109
110pub const FORM_addr = 0x01;
111pub const FORM_block2 = 0x03;
112pub const FORM_block4 = 0x04;
113pub const FORM_data2 = 0x05;
114pub const FORM_data4 = 0x06;
115pub const FORM_data8 = 0x07;
116pub const FORM_string = 0x08;
117pub const FORM_block = 0x09;
118pub const FORM_block1 = 0x0a;
119pub const FORM_data1 = 0x0b;
120pub const FORM_flag = 0x0c;
121pub const FORM_sdata = 0x0d;
122pub const FORM_strp = 0x0e;
123pub const FORM_udata = 0x0f;
124pub const FORM_ref_addr = 0x10;
125pub const FORM_ref1 = 0x11;
126pub const FORM_ref2 = 0x12;
127pub const FORM_ref4 = 0x13;
128pub const FORM_ref8 = 0x14;
129pub const FORM_ref_udata = 0x15;
130pub const FORM_indirect = 0x16;
131pub const FORM_sec_offset = 0x17;
132pub const FORM_exprloc = 0x18;
133pub const FORM_flag_present = 0x19;
134pub const FORM_ref_sig8 = 0x20;
135
136// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
137pub const FORM_GNU_addr_index = 0x1f01;
138pub const FORM_GNU_str_index = 0x1f02;
139
140// Extensions for DWZ multifile.
141// See http://www.dwarfstd.org/ShowIssue.php?issue=120604.1&type=open .
142pub const FORM_GNU_ref_alt = 0x1f20;
143pub const FORM_GNU_strp_alt = 0x1f21;
144
145pub const AT_sibling = 0x01;
146pub const AT_location = 0x02;
147pub const AT_name = 0x03;
148pub const AT_ordering = 0x09;
149pub const AT_subscr_data = 0x0a;
150pub const AT_byte_size = 0x0b;
151pub const AT_bit_offset = 0x0c;
152pub const AT_bit_size = 0x0d;
153pub const AT_element_list = 0x0f;
154pub const AT_stmt_list = 0x10;
155pub const AT_low_pc = 0x11;
156pub const AT_high_pc = 0x12;
157pub const AT_language = 0x13;
158pub const AT_member = 0x14;
159pub const AT_discr = 0x15;
160pub const AT_discr_value = 0x16;
161pub const AT_visibility = 0x17;
162pub const AT_import = 0x18;
163pub const AT_string_length = 0x19;
164pub const AT_common_reference = 0x1a;
165pub const AT_comp_dir = 0x1b;
166pub const AT_const_value = 0x1c;
167pub const AT_containing_type = 0x1d;
168pub const AT_default_value = 0x1e;
169pub const AT_inline = 0x20;
170pub const AT_is_optional = 0x21;
171pub const AT_lower_bound = 0x22;
172pub const AT_producer = 0x25;
173pub const AT_prototyped = 0x27;
174pub const AT_return_addr = 0x2a;
175pub const AT_start_scope = 0x2c;
176pub const AT_bit_stride = 0x2e;
177pub const AT_upper_bound = 0x2f;
178pub const AT_abstract_origin = 0x31;
179pub const AT_accessibility = 0x32;
180pub const AT_address_class = 0x33;
181pub const AT_artificial = 0x34;
182pub const AT_base_types = 0x35;
183pub const AT_calling_convention = 0x36;
184pub const AT_count = 0x37;
185pub const AT_data_member_location = 0x38;
186pub const AT_decl_column = 0x39;
187pub const AT_decl_file = 0x3a;
188pub const AT_decl_line = 0x3b;
189pub const AT_declaration = 0x3c;
190pub const AT_discr_list = 0x3d;
191pub const AT_encoding = 0x3e;
192pub const AT_external = 0x3f;
193pub const AT_frame_base = 0x40;
194pub const AT_friend = 0x41;
195pub const AT_identifier_case = 0x42;
196pub const AT_macro_info = 0x43;
197pub const AT_namelist_items = 0x44;
198pub const AT_priority = 0x45;
199pub const AT_segment = 0x46;
200pub const AT_specification = 0x47;
201pub const AT_static_link = 0x48;
202pub const AT_type = 0x49;
203pub const AT_use_location = 0x4a;
204pub const AT_variable_parameter = 0x4b;
205pub const AT_virtuality = 0x4c;
206pub const AT_vtable_elem_location = 0x4d;
207
208// DWARF 3 values.
209pub const AT_allocated = 0x4e;
210pub const AT_associated = 0x4f;
211pub const AT_data_location = 0x50;
212pub const AT_byte_stride = 0x51;
213pub const AT_entry_pc = 0x52;
214pub const AT_use_UTF8 = 0x53;
215pub const AT_extension = 0x54;
216pub const AT_ranges = 0x55;
217pub const AT_trampoline = 0x56;
218pub const AT_call_column = 0x57;
219pub const AT_call_file = 0x58;
220pub const AT_call_line = 0x59;
221pub const AT_description = 0x5a;
222pub const AT_binary_scale = 0x5b;
223pub const AT_decimal_scale = 0x5c;
224pub const AT_small = 0x5d;
225pub const AT_decimal_sign = 0x5e;
226pub const AT_digit_count = 0x5f;
227pub const AT_picture_string = 0x60;
228pub const AT_mutable = 0x61;
229pub const AT_threads_scaled = 0x62;
230pub const AT_explicit = 0x63;
231pub const AT_object_pointer = 0x64;
232pub const AT_endianity = 0x65;
233pub const AT_elemental = 0x66;
234pub const AT_pure = 0x67;
235pub const AT_recursive = 0x68;
236
237// DWARF 4.
238pub const AT_signature = 0x69;
239pub const AT_main_subprogram = 0x6a;
240pub const AT_data_bit_offset = 0x6b;
241pub const AT_const_expr = 0x6c;
242pub const AT_enum_class = 0x6d;
243pub const AT_linkage_name = 0x6e;
244
245// DWARF 5
246pub const AT_alignment = 0x88;
247
248pub const AT_lo_user = 0x2000; // Implementation-defined range start.
249pub const AT_hi_user = 0x3fff; // Implementation-defined range end.
250
251// SGI/MIPS extensions.
252pub const AT_MIPS_fde = 0x2001;
253pub const AT_MIPS_loop_begin = 0x2002;
254pub const AT_MIPS_tail_loop_begin = 0x2003;
255pub const AT_MIPS_epilog_begin = 0x2004;
256pub const AT_MIPS_loop_unroll_factor = 0x2005;
257pub const AT_MIPS_software_pipeline_depth = 0x2006;
258pub const AT_MIPS_linkage_name = 0x2007;
259pub const AT_MIPS_stride = 0x2008;
260pub const AT_MIPS_abstract_name = 0x2009;
261pub const AT_MIPS_clone_origin = 0x200a;
262pub const AT_MIPS_has_inlines = 0x200b;
263
264// HP extensions.
265pub const AT_HP_block_index = 0x2000;
266pub const AT_HP_unmodifiable = 0x2001; // Same as DW_AT_MIPS_fde.
267pub const AT_HP_prologue = 0x2005; // Same as DW_AT_MIPS_loop_unroll.
268pub const AT_HP_epilogue = 0x2008; // Same as DW_AT_MIPS_stride.
269pub const AT_HP_actuals_stmt_list = 0x2010;
270pub const AT_HP_proc_per_section = 0x2011;
271pub const AT_HP_raw_data_ptr = 0x2012;
272pub const AT_HP_pass_by_reference = 0x2013;
273pub const AT_HP_opt_level = 0x2014;
274pub const AT_HP_prof_version_id = 0x2015;
275pub const AT_HP_opt_flags = 0x2016;
276pub const AT_HP_cold_region_low_pc = 0x2017;
277pub const AT_HP_cold_region_high_pc = 0x2018;
278pub const AT_HP_all_variables_modifiable = 0x2019;
279pub const AT_HP_linkage_name = 0x201a;
280pub const AT_HP_prof_flags = 0x201b; // In comp unit of procs_info for -g.
281pub const AT_HP_unit_name = 0x201f;
282pub const AT_HP_unit_size = 0x2020;
283pub const AT_HP_widened_byte_size = 0x2021;
284pub const AT_HP_definition_points = 0x2022;
285pub const AT_HP_default_location = 0x2023;
286pub const AT_HP_is_result_param = 0x2029;
287
288// GNU extensions.
289pub const AT_sf_names = 0x2101;
290pub const AT_src_info = 0x2102;
291pub const AT_mac_info = 0x2103;
292pub const AT_src_coords = 0x2104;
293pub const AT_body_begin = 0x2105;
294pub const AT_body_end = 0x2106;
295pub const AT_GNU_vector = 0x2107;
296// Thread-safety annotations.
297// See http://gcc.gnu.org/wiki/ThreadSafetyAnnotation .
298pub const AT_GNU_guarded_by = 0x2108;
299pub const AT_GNU_pt_guarded_by = 0x2109;
300pub const AT_GNU_guarded = 0x210a;
301pub const AT_GNU_pt_guarded = 0x210b;
302pub const AT_GNU_locks_excluded = 0x210c;
303pub const AT_GNU_exclusive_locks_required = 0x210d;
304pub const AT_GNU_shared_locks_required = 0x210e;
305// One-definition rule violation detection.
306// See http://gcc.gnu.org/wiki/DwarfSeparateTypeInfo .
307pub const AT_GNU_odr_signature = 0x210f;
308// Template template argument name.
309// See http://gcc.gnu.org/wiki/TemplateParmsDwarf .
310pub const AT_GNU_template_name = 0x2110;
311// The GNU call site extension.
312// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.2&type=open .
313pub const AT_GNU_call_site_value = 0x2111;
314pub const AT_GNU_call_site_data_value = 0x2112;
315pub const AT_GNU_call_site_target = 0x2113;
316pub const AT_GNU_call_site_target_clobbered = 0x2114;
317pub const AT_GNU_tail_call = 0x2115;
318pub const AT_GNU_all_tail_call_sites = 0x2116;
319pub const AT_GNU_all_call_sites = 0x2117;
320pub const AT_GNU_all_source_call_sites = 0x2118;
321// Section offset into .debug_macro section.
322pub const AT_GNU_macros = 0x2119;
323// Extensions for Fission. See http://gcc.gnu.org/wiki/DebugFission.
324pub const AT_GNU_dwo_name = 0x2130;
325pub const AT_GNU_dwo_id = 0x2131;
326pub const AT_GNU_ranges_base = 0x2132;
327pub const AT_GNU_addr_base = 0x2133;
328pub const AT_GNU_pubnames = 0x2134;
329pub const AT_GNU_pubtypes = 0x2135;
330// VMS extensions.
331pub const AT_VMS_rtnbeg_pd_address = 0x2201;
332// GNAT extensions.
333// GNAT descriptive type.
334// See http://gcc.gnu.org/wiki/DW_AT_GNAT_descriptive_type .
335pub const AT_use_GNAT_descriptive_type = 0x2301;
336pub const AT_GNAT_descriptive_type = 0x2302;
337// UPC extension.
338pub const AT_upc_threads_scaled = 0x3210;
339// PGI (STMicroelectronics) extensions.
340pub const AT_PGI_lbase = 0x3a00;
341pub const AT_PGI_soffset = 0x3a01;
342pub const AT_PGI_lstride = 0x3a02;
343
344pub const OP_addr = 0x03;
345pub const OP_deref = 0x06;
346pub const OP_const1u = 0x08;
347pub const OP_const1s = 0x09;
348pub const OP_const2u = 0x0a;
349pub const OP_const2s = 0x0b;
350pub const OP_const4u = 0x0c;
351pub const OP_const4s = 0x0d;
352pub const OP_const8u = 0x0e;
353pub const OP_const8s = 0x0f;
354pub const OP_constu = 0x10;
355pub const OP_consts = 0x11;
356pub const OP_dup = 0x12;
357pub const OP_drop = 0x13;
358pub const OP_over = 0x14;
359pub const OP_pick = 0x15;
360pub const OP_swap = 0x16;
361pub const OP_rot = 0x17;
362pub const OP_xderef = 0x18;
363pub const OP_abs = 0x19;
364pub const OP_and = 0x1a;
365pub const OP_div = 0x1b;
366pub const OP_minus = 0x1c;
367pub const OP_mod = 0x1d;
368pub const OP_mul = 0x1e;
369pub const OP_neg = 0x1f;
370pub const OP_not = 0x20;
371pub const OP_or = 0x21;
372pub const OP_plus = 0x22;
373pub const OP_plus_uconst = 0x23;
374pub const OP_shl = 0x24;
375pub const OP_shr = 0x25;
376pub const OP_shra = 0x26;
377pub const OP_xor = 0x27;
378pub const OP_bra = 0x28;
379pub const OP_eq = 0x29;
380pub const OP_ge = 0x2a;
381pub const OP_gt = 0x2b;
382pub const OP_le = 0x2c;
383pub const OP_lt = 0x2d;
384pub const OP_ne = 0x2e;
385pub const OP_skip = 0x2f;
386pub const OP_lit0 = 0x30;
387pub const OP_lit1 = 0x31;
388pub const OP_lit2 = 0x32;
389pub const OP_lit3 = 0x33;
390pub const OP_lit4 = 0x34;
391pub const OP_lit5 = 0x35;
392pub const OP_lit6 = 0x36;
393pub const OP_lit7 = 0x37;
394pub const OP_lit8 = 0x38;
395pub const OP_lit9 = 0x39;
396pub const OP_lit10 = 0x3a;
397pub const OP_lit11 = 0x3b;
398pub const OP_lit12 = 0x3c;
399pub const OP_lit13 = 0x3d;
400pub const OP_lit14 = 0x3e;
401pub const OP_lit15 = 0x3f;
402pub const OP_lit16 = 0x40;
403pub const OP_lit17 = 0x41;
404pub const OP_lit18 = 0x42;
405pub const OP_lit19 = 0x43;
406pub const OP_lit20 = 0x44;
407pub const OP_lit21 = 0x45;
408pub const OP_lit22 = 0x46;
409pub const OP_lit23 = 0x47;
410pub const OP_lit24 = 0x48;
411pub const OP_lit25 = 0x49;
412pub const OP_lit26 = 0x4a;
413pub const OP_lit27 = 0x4b;
414pub const OP_lit28 = 0x4c;
415pub const OP_lit29 = 0x4d;
416pub const OP_lit30 = 0x4e;
417pub const OP_lit31 = 0x4f;
418pub const OP_reg0 = 0x50;
419pub const OP_reg1 = 0x51;
420pub const OP_reg2 = 0x52;
421pub const OP_reg3 = 0x53;
422pub const OP_reg4 = 0x54;
423pub const OP_reg5 = 0x55;
424pub const OP_reg6 = 0x56;
425pub const OP_reg7 = 0x57;
426pub const OP_reg8 = 0x58;
427pub const OP_reg9 = 0x59;
428pub const OP_reg10 = 0x5a;
429pub const OP_reg11 = 0x5b;
430pub const OP_reg12 = 0x5c;
431pub const OP_reg13 = 0x5d;
432pub const OP_reg14 = 0x5e;
433pub const OP_reg15 = 0x5f;
434pub const OP_reg16 = 0x60;
435pub const OP_reg17 = 0x61;
436pub const OP_reg18 = 0x62;
437pub const OP_reg19 = 0x63;
438pub const OP_reg20 = 0x64;
439pub const OP_reg21 = 0x65;
440pub const OP_reg22 = 0x66;
441pub const OP_reg23 = 0x67;
442pub const OP_reg24 = 0x68;
443pub const OP_reg25 = 0x69;
444pub const OP_reg26 = 0x6a;
445pub const OP_reg27 = 0x6b;
446pub const OP_reg28 = 0x6c;
447pub const OP_reg29 = 0x6d;
448pub const OP_reg30 = 0x6e;
449pub const OP_reg31 = 0x6f;
450pub const OP_breg0 = 0x70;
451pub const OP_breg1 = 0x71;
452pub const OP_breg2 = 0x72;
453pub const OP_breg3 = 0x73;
454pub const OP_breg4 = 0x74;
455pub const OP_breg5 = 0x75;
456pub const OP_breg6 = 0x76;
457pub const OP_breg7 = 0x77;
458pub const OP_breg8 = 0x78;
459pub const OP_breg9 = 0x79;
460pub const OP_breg10 = 0x7a;
461pub const OP_breg11 = 0x7b;
462pub const OP_breg12 = 0x7c;
463pub const OP_breg13 = 0x7d;
464pub const OP_breg14 = 0x7e;
465pub const OP_breg15 = 0x7f;
466pub const OP_breg16 = 0x80;
467pub const OP_breg17 = 0x81;
468pub const OP_breg18 = 0x82;
469pub const OP_breg19 = 0x83;
470pub const OP_breg20 = 0x84;
471pub const OP_breg21 = 0x85;
472pub const OP_breg22 = 0x86;
473pub const OP_breg23 = 0x87;
474pub const OP_breg24 = 0x88;
475pub const OP_breg25 = 0x89;
476pub const OP_breg26 = 0x8a;
477pub const OP_breg27 = 0x8b;
478pub const OP_breg28 = 0x8c;
479pub const OP_breg29 = 0x8d;
480pub const OP_breg30 = 0x8e;
481pub const OP_breg31 = 0x8f;
482pub const OP_regx = 0x90;
483pub const OP_fbreg = 0x91;
484pub const OP_bregx = 0x92;
485pub const OP_piece = 0x93;
486pub const OP_deref_size = 0x94;
487pub const OP_xderef_size = 0x95;
488pub const OP_nop = 0x96;
489
490// DWARF 3 extensions.
491pub const OP_push_object_address = 0x97;
492pub const OP_call2 = 0x98;
493pub const OP_call4 = 0x99;
494pub const OP_call_ref = 0x9a;
495pub const OP_form_tls_address = 0x9b;
496pub const OP_call_frame_cfa = 0x9c;
497pub const OP_bit_piece = 0x9d;
498
499// DWARF 4 extensions.
500pub const OP_implicit_value = 0x9e;
501pub const OP_stack_value = 0x9f;
502
503pub const OP_lo_user = 0xe0; // Implementation-defined range start.
504pub const OP_hi_user = 0xff; // Implementation-defined range end.
505
506// GNU extensions.
507pub const OP_GNU_push_tls_address = 0xe0;
508// The following is for marking variables that are uninitialized.
509pub const OP_GNU_uninit = 0xf0;
510pub const OP_GNU_encoded_addr = 0xf1;
511// The GNU implicit pointer extension.
512// See http://www.dwarfstd.org/ShowIssue.php?issue=100831.1&type=open .
513pub const OP_GNU_implicit_pointer = 0xf2;
514// The GNU entry value extension.
515// See http://www.dwarfstd.org/ShowIssue.php?issue=100909.1&type=open .
516pub const OP_GNU_entry_value = 0xf3;
517// The GNU typed stack extension.
518// See http://www.dwarfstd.org/doc/040408.1.html .
519pub const OP_GNU_const_type = 0xf4;
520pub const OP_GNU_regval_type = 0xf5;
521pub const OP_GNU_deref_type = 0xf6;
522pub const OP_GNU_convert = 0xf7;
523pub const OP_GNU_reinterpret = 0xf9;
524// The GNU parameter ref extension.
525pub const OP_GNU_parameter_ref = 0xfa;
526// Extension for Fission. See http://gcc.gnu.org/wiki/DebugFission.
527pub const OP_GNU_addr_index = 0xfb;
528pub const OP_GNU_const_index = 0xfc;
529// HP extensions.
530pub const OP_HP_unknown = 0xe0; // Ouch, the same as GNU_push_tls_address.
531pub const OP_HP_is_value = 0xe1;
532pub const OP_HP_fltconst4 = 0xe2;
533pub const OP_HP_fltconst8 = 0xe3;
534pub const OP_HP_mod_range = 0xe4;
535pub const OP_HP_unmod_range = 0xe5;
536pub const OP_HP_tls = 0xe6;
537// PGI (STMicroelectronics) extensions.
538pub const OP_PGI_omp_thread_num = 0xf8;
539
540pub const ATE_void = 0x0;
541pub const ATE_address = 0x1;
542pub const ATE_boolean = 0x2;
543pub const ATE_complex_float = 0x3;
544pub const ATE_float = 0x4;
545pub const ATE_signed = 0x5;
546pub const ATE_signed_char = 0x6;
547pub const ATE_unsigned = 0x7;
548pub const ATE_unsigned_char = 0x8;
549
550// DWARF 3.
551pub const ATE_imaginary_float = 0x9;
552pub const ATE_packed_decimal = 0xa;
553pub const ATE_numeric_string = 0xb;
554pub const ATE_edited = 0xc;
555pub const ATE_signed_fixed = 0xd;
556pub const ATE_unsigned_fixed = 0xe;
557pub const ATE_decimal_float = 0xf;
558
559// DWARF 4.
560pub const ATE_UTF = 0x10;
561
562pub const ATE_lo_user = 0x80;
563pub const ATE_hi_user = 0xff;
564
565// HP extensions.
566pub const ATE_HP_float80 = 0x80; // Floating-point (80 bit).
567pub const ATE_HP_complex_float80 = 0x81; // Complex floating-point (80 bit).
568pub const ATE_HP_float128 = 0x82; // Floating-point (128 bit).
569pub const ATE_HP_complex_float128 = 0x83; // Complex fp (128 bit).
570pub const ATE_HP_floathpintel = 0x84; // Floating-point (82 bit IA64).
571pub const ATE_HP_imaginary_float80 = 0x85;
572pub const ATE_HP_imaginary_float128 = 0x86;
573pub const ATE_HP_VAX_float = 0x88; // F or G floating.
574pub const ATE_HP_VAX_float_d = 0x89; // D floating.
575pub const ATE_HP_packed_decimal = 0x8a; // Cobol.
576pub const ATE_HP_zoned_decimal = 0x8b; // Cobol.
577pub const ATE_HP_edited = 0x8c; // Cobol.
578pub const ATE_HP_signed_fixed = 0x8d; // Cobol.
579pub const ATE_HP_unsigned_fixed = 0x8e; // Cobol.
580pub const ATE_HP_VAX_complex_float = 0x8f; // F or G floating complex.
581pub const ATE_HP_VAX_complex_float_d = 0x90; // D floating complex.
582
583pub const CFA_advance_loc = 0x40;
584pub const CFA_offset = 0x80;
585pub const CFA_restore = 0xc0;
586pub const CFA_nop = 0x00;
587pub const CFA_set_loc = 0x01;
588pub const CFA_advance_loc1 = 0x02;
589pub const CFA_advance_loc2 = 0x03;
590pub const CFA_advance_loc4 = 0x04;
591pub const CFA_offset_extended = 0x05;
592pub const CFA_restore_extended = 0x06;
593pub const CFA_undefined = 0x07;
594pub const CFA_same_value = 0x08;
595pub const CFA_register = 0x09;
596pub const CFA_remember_state = 0x0a;
597pub const CFA_restore_state = 0x0b;
598pub const CFA_def_cfa = 0x0c;
599pub const CFA_def_cfa_register = 0x0d;
600pub const CFA_def_cfa_offset = 0x0e;
601
602// DWARF 3.
603pub const CFA_def_cfa_expression = 0x0f;
604pub const CFA_expression = 0x10;
605pub const CFA_offset_extended_sf = 0x11;
606pub const CFA_def_cfa_sf = 0x12;
607pub const CFA_def_cfa_offset_sf = 0x13;
608pub const CFA_val_offset = 0x14;
609pub const CFA_val_offset_sf = 0x15;
610pub const CFA_val_expression = 0x16;
611
612pub const CFA_lo_user = 0x1c;
613pub const CFA_hi_user = 0x3f;
614
615// SGI/MIPS specific.
616pub const CFA_MIPS_advance_loc8 = 0x1d;
617
618// GNU extensions.
619pub const CFA_GNU_window_save = 0x2d;
620pub const CFA_GNU_args_size = 0x2e;
621pub const CFA_GNU_negative_offset_extended = 0x2f;
622
623pub const CHILDREN_no = 0x00;
624pub const CHILDREN_yes = 0x01;
625
626pub const LNS_extended_op = 0x00;
627pub const LNS_copy = 0x01;
628pub const LNS_advance_pc = 0x02;
629pub const LNS_advance_line = 0x03;
630pub const LNS_set_file = 0x04;
631pub const LNS_set_column = 0x05;
632pub const LNS_negate_stmt = 0x06;
633pub const LNS_set_basic_block = 0x07;
634pub const LNS_const_add_pc = 0x08;
635pub const LNS_fixed_advance_pc = 0x09;
636pub const LNS_set_prologue_end = 0x0a;
637pub const LNS_set_epilogue_begin = 0x0b;
638pub const LNS_set_isa = 0x0c;
639
640pub const LNE_end_sequence = 0x01;
641pub const LNE_set_address = 0x02;
642pub const LNE_define_file = 0x03;
643pub const LNE_set_discriminator = 0x04;
644pub const LNE_lo_user = 0x80;
645pub const LNE_hi_user = 0xff;
646
647pub const LANG_C89 = 0x0001;
648pub const LANG_C = 0x0002;
649pub const LANG_Ada83 = 0x0003;
650pub const LANG_C_plus_plus = 0x0004;
651pub const LANG_Cobol74 = 0x0005;
652pub const LANG_Cobol85 = 0x0006;
653pub const LANG_Fortran77 = 0x0007;
654pub const LANG_Fortran90 = 0x0008;
655pub const LANG_Pascal83 = 0x0009;
656pub const LANG_Modula2 = 0x000a;
657pub const LANG_Java = 0x000b;
658pub const LANG_C99 = 0x000c;
659pub const LANG_Ada95 = 0x000d;
660pub const LANG_Fortran95 = 0x000e;
661pub const LANG_PLI = 0x000f;
662pub const LANG_ObjC = 0x0010;
663pub const LANG_ObjC_plus_plus = 0x0011;
664pub const LANG_UPC = 0x0012;
665pub const LANG_D = 0x0013;
666pub const LANG_Python = 0x0014;
667pub const LANG_Go = 0x0016;
668pub const LANG_C_plus_plus_11 = 0x001a;
669pub const LANG_Rust = 0x001c;
670pub const LANG_C11 = 0x001d;
671pub const LANG_C_plus_plus_14 = 0x0021;
672pub const LANG_Fortran03 = 0x0022;
673pub const LANG_Fortran08 = 0x0023;
674pub const LANG_lo_user = 0x8000;
675pub const LANG_hi_user = 0xffff;
676pub const LANG_Mips_Assembler = 0x8001;
677pub const LANG_Upc = 0x8765;
678pub const LANG_HP_Bliss = 0x8003;
679pub const LANG_HP_Basic91 = 0x8004;
680pub const LANG_HP_Pascal91 = 0x8005;
681pub const LANG_HP_IMacro = 0x8006;
682pub const LANG_HP_Assembler = 0x8007;
lib/std/macho.zig+11
...@@ -24,6 +24,17 @@ pub const load_command = extern struct {...@@ -24,6 +24,17 @@ pub const load_command = extern struct {
24 cmdsize: u32,24 cmdsize: u32,
25};25};
2626
27pub const uuid_command = extern struct {
28 /// LC_UUID
29 cmd: u32,
30
31 /// sizeof(struct uuid_command)
32 cmdsize: u32,
33
34 /// the 128-bit uuid
35 uuid: [16]u8,
36};
37
27/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD38/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
28/// "stab" style symbol table information as described in the header files39/// "stab" style symbol table information as described in the header files
29/// <nlist.h> and <stab.h>.40/// <nlist.h> and <stab.h>.
test/stack_traces.zig+19-18
...@@ -3,6 +3,7 @@ const std = @import("std");...@@ -3,6 +3,7 @@ const std = @import("std");
3const os = std.os;3const os = std.os;
4const tests = @import("tests.zig");4const tests = @import("tests.zig");
55
6// zig fmt: off
6pub fn addCases(cases: *tests.StackTracesContext) void {7pub fn addCases(cases: *tests.StackTracesContext) void {
7 const source_return =8 const source_return =
8 \\const std = @import("std");9 \\const std = @import("std");
...@@ -41,7 +42,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -41,7 +42,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
41 \\ try foo();42 \\ try foo();
42 \\}43 \\}
43 ;44 ;
44 // zig fmt: off45
45 switch (builtin.os) {46 switch (builtin.os) {
46 .freebsd => {47 .freebsd => {
47 cases.addCase(48 cases.addCase(
...@@ -264,14 +265,14 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -264,14 +265,14 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
264 [_][]const u8{265 [_][]const u8{
265 // debug266 // debug
266 \\error: TheSkyIsFalling267 \\error: TheSkyIsFalling
267 \\source.zig:4:5: [address] in _main.0 (test.o)268 \\source.zig:4:5: [address] in main (test)
268 \\ return error.TheSkyIsFalling;269 \\ return error.TheSkyIsFalling;
269 \\ ^270 \\ ^
270 \\271 \\
271 ,272 ,
272 // release-safe273 // release-safe
273 \\error: TheSkyIsFalling274 \\error: TheSkyIsFalling
274 \\source.zig:4:5: [address] in _main (test.o)275 \\source.zig:4:5: [address] in std.start.main (test)
275 \\ return error.TheSkyIsFalling;276 \\ return error.TheSkyIsFalling;
276 \\ ^277 \\ ^
277 \\278 \\
...@@ -291,20 +292,20 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -291,20 +292,20 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
291 [_][]const u8{292 [_][]const u8{
292 // debug293 // debug
293 \\error: TheSkyIsFalling294 \\error: TheSkyIsFalling
294 \\source.zig:4:5: [address] in _foo (test.o)295 \\source.zig:4:5: [address] in foo (test)
295 \\ return error.TheSkyIsFalling;296 \\ return error.TheSkyIsFalling;
296 \\ ^297 \\ ^
297 \\source.zig:8:5: [address] in _main.0 (test.o)298 \\source.zig:8:5: [address] in main (test)
298 \\ try foo();299 \\ try foo();
299 \\ ^300 \\ ^
300 \\301 \\
301 ,302 ,
302 // release-safe303 // release-safe
303 \\error: TheSkyIsFalling304 \\error: TheSkyIsFalling
304 \\source.zig:4:5: [address] in _main (test.o)305 \\source.zig:4:5: [address] in std.start.main (test)
305 \\ return error.TheSkyIsFalling;306 \\ return error.TheSkyIsFalling;
306 \\ ^307 \\ ^
307 \\source.zig:8:5: [address] in _main (test.o)308 \\source.zig:8:5: [address] in std.start.main (test)
308 \\ try foo();309 \\ try foo();
309 \\ ^310 \\ ^
310 \\311 \\
...@@ -324,32 +325,32 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -324,32 +325,32 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
324 [_][]const u8{325 [_][]const u8{
325 // debug326 // debug
326 \\error: TheSkyIsFalling327 \\error: TheSkyIsFalling
327 \\source.zig:12:5: [address] in _make_error (test.o)328 \\source.zig:12:5: [address] in make_error (test)
328 \\ return error.TheSkyIsFalling;329 \\ return error.TheSkyIsFalling;
329 \\ ^330 \\ ^
330 \\source.zig:8:5: [address] in _bar (test.o)331 \\source.zig:8:5: [address] in bar (test)
331 \\ return make_error();332 \\ return make_error();
332 \\ ^333 \\ ^
333 \\source.zig:4:5: [address] in _foo (test.o)334 \\source.zig:4:5: [address] in foo (test)
334 \\ try bar();335 \\ try bar();
335 \\ ^336 \\ ^
336 \\source.zig:16:5: [address] in _main.0 (test.o)337 \\source.zig:16:5: [address] in main (test)
337 \\ try foo();338 \\ try foo();
338 \\ ^339 \\ ^
339 \\340 \\
340 ,341 ,
341 // release-safe342 // release-safe
342 \\error: TheSkyIsFalling343 \\error: TheSkyIsFalling
343 \\source.zig:12:5: [address] in _main (test.o)344 \\source.zig:12:5: [address] in std.start.main (test)
344 \\ return error.TheSkyIsFalling;345 \\ return error.TheSkyIsFalling;
345 \\ ^346 \\ ^
346 \\source.zig:8:5: [address] in _main (test.o)347 \\source.zig:8:5: [address] in std.start.main (test)
347 \\ return make_error();348 \\ return make_error();
348 \\ ^349 \\ ^
349 \\source.zig:4:5: [address] in _main (test.o)350 \\source.zig:4:5: [address] in std.start.main (test)
350 \\ try bar();351 \\ try bar();
351 \\ ^352 \\ ^
352 \\source.zig:16:5: [address] in _main (test.o)353 \\source.zig:16:5: [address] in std.start.main (test)
353 \\ try foo();354 \\ try foo();
354 \\ ^355 \\ ^
355 \\356 \\
...@@ -393,7 +394,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -393,7 +394,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
393 source_try_return,394 source_try_return,
394 [_][]const u8{395 [_][]const u8{
395 // debug396 // debug
396 \\error: TheSkyIsFalling397 \\error: TheSkyIsFalling
397 \\source.zig:4:5: [address] in foo (test.obj)398 \\source.zig:4:5: [address] in foo (test.obj)
398 \\ return error.TheSkyIsFalling;399 \\ return error.TheSkyIsFalling;
399 \\ ^400 \\ ^
...@@ -419,7 +420,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -419,7 +420,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
419 source_try_try_return_return,420 source_try_try_return_return,
420 [_][]const u8{421 [_][]const u8{
421 // debug422 // debug
422 \\error: TheSkyIsFalling423 \\error: TheSkyIsFalling
423 \\source.zig:12:5: [address] in make_error (test.obj)424 \\source.zig:12:5: [address] in make_error (test.obj)
424 \\ return error.TheSkyIsFalling;425 \\ return error.TheSkyIsFalling;
425 \\ ^426 \\ ^
...@@ -449,5 +450,5 @@ pub fn addCases(cases: *tests.StackTracesContext) void {...@@ -449,5 +450,5 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
449 },450 },
450 else => {},451 else => {},
451 }452 }
452 // zig fmt: off
453}453}
454// zig fmt: off