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 {
3838 checksum_offset: ?usize,
3939};
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
4153/// Tries to write to stderr, unbuffered, and ignores any error returned.
4254/// Does not append a newline.
4355var stderr_file: File = undefined;
......@@ -378,175 +390,6 @@ pub fn writeCurrentStackTraceWindows(
378390 }
379391}
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
550393pub const TTY = struct {
551394 pub const Color = enum {
552395 Red,
......@@ -618,7 +461,7 @@ pub const TTY = struct {
618461};
619462
620463/// TODO resources https://github.com/ziglang/zig/issues/4353
621fn populateModule(di: *DebugInfo, mod: *Module) !void {
464fn populateModule(di: *ModuleDebugInfo, mod: *Module) !void {
622465 if (mod.populated)
623466 return;
624467 const allocator = getDebugInfoAllocator();
......@@ -650,7 +493,7 @@ fn populateModule(di: *DebugInfo, mod: *Module) !void {
650493 sect_offset += @sizeOf(pdb.DebugSubsectionHeader);
651494
652495 switch (subsect_hdr.Kind) {
653 pdb.DebugSubsectionKind.FileChecksums => {
496 .FileChecksums => {
654497 mod.checksum_offset = sect_offset;
655498 break;
656499 },
......@@ -682,41 +525,37 @@ fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const Mach
682525 return null;
683526}
684527
685fn printSourceAtAddressMacOs(di: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
686 const base_addr = process.getBaseAddress();
687 const adjusted_addr = 0x100000000 + (address - base_addr);
688
689 const symbol = machoSearchSymbols(di.symbols, adjusted_addr) orelse {
690 return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFileAnyOs);
691 };
692
693 const symbol_name = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + symbol.nlist.n_strx));
694 const compile_unit_name = if (symbol.ofile) |ofile| blk: {
695 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
696 break :blk fs.path.basename(ofile_path);
697 } else "???";
698
699 const line_info = getLineNumberInfoMacOs(di, symbol.*, adjusted_addr) catch |err| switch (err) {
700 error.MissingDebugInfo, error.InvalidDebugInfo => null,
528/// TODO resources https://github.com/ziglang/zig/issues/4353
529pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: var, address: usize, tty_config: TTY.Config) !void {
530 const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
531 error.MissingDebugInfo, error.InvalidDebugInfo => {
532 return printLineInfo(
533 out_stream,
534 null,
535 address,
536 "???",
537 "???",
538 tty_config,
539 printLineFromFileAnyOs,
540 );
541 },
701542 else => return err,
702543 };
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(
706549 out_stream,
707 line_info,
550 symbol_info.line_info,
708551 address,
709 symbol_name,
710 compile_unit_name,
552 symbol_info.symbol_name,
553 symbol_info.compile_unit_name,
711554 tty_config,
712555 printLineFromFileAnyOs,
713556 );
714557}
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
720559fn printLineInfo(
721560 out_stream: var,
722561 line_info: ?LineInfo,
......@@ -772,29 +611,32 @@ pub const OpenSelfDebugInfoError = error{
772611/// TODO resources https://github.com/ziglang/zig/issues/4353
773612/// TODO once https://github.com/ziglang/zig/issues/3157 is fully implemented,
774613/// 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 {
776615 if (builtin.strip_debug_info)
777616 return error.MissingDebugInfo;
778617 if (@hasDecl(root, "os") and @hasDecl(root.os, "debug") and @hasDecl(root.os.debug, "openSelfDebugInfo")) {
779618 return noasync root.os.debug.openSelfDebugInfo(allocator);
780619 }
781 if (builtin.os == .windows) {
782 return noasync openSelfDebugInfoWindows(allocator);
783 }
784 if (comptime std.Target.current.isDarwin()) {
785 return noasync openSelfDebugInfoMacOs(allocator);
620 switch (builtin.os) {
621 .linux,
622 .freebsd,
623 .macosx,
624 .windows,
625 => return DebugInfo.init(allocator),
626 else => @compileError("openSelfDebugInfo unsupported for this platform"),
786627 }
787 return noasync openSelfDebugInfoPosix(allocator);
788628}
789629
790fn openSelfDebugInfoWindows(allocator: *mem.Allocator) !DebugInfo {
791 const self_file = try fs.openSelfExe();
792 defer self_file.close();
630/// TODO resources https://github.com/ziglang/zig/issues/4353
631fn openCoffDebugInfo(allocator: *mem.Allocator, coff_file_path: [:0]const u16) !ModuleDebugInfo {
632 const coff_file = try std.fs.openFileAbsoluteW(coff_file_path.ptr, .{});
633 errdefer coff_file.close();
793634
794635 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,
798640 .coff = coff_obj,
799641 .pdb = undefined,
800642 .sect_contribs = undefined,
......@@ -958,36 +800,21 @@ fn readSparseBitVector(stream: var, allocator: *mem.Allocator) ![]usize {
958800 return list.toOwnedSlice();
959801}
960802
961fn findDwarfSectionFromElf(elf_file: *elf.Elf, name: []const u8) !?DwarfInfo.Section {
962 const elf_header = (try elf_file.findSection(name)) orelse return null;
963 return DwarfInfo.Section{
964 .offset = elf_header.sh_offset,
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();
803fn chopSlice(ptr: []const u8, offset: u64, size: u64) ![]const u8 {
804 const start = try math.cast(usize, offset);
805 const end = start + try math.cast(usize, size);
806 return ptr[start..end];
979807}
980808
981809/// TODO resources https://github.com/ziglang/zig/issues/4353
982pub fn openElfDebugInfo(
983 allocator: *mem.Allocator,
984 data: []u8,
985) !DwarfInfo {
986 var seekable_stream = io.SliceSeekableInStream.init(data);
810pub fn openElfDebugInfo(allocator: *mem.Allocator, elf_file_path: []const u8) !ModuleDebugInfo {
811 const mapped_mem = try mapWholeFile(elf_file_path);
812
813 var seekable_stream = io.SliceSeekableInStream.init(mapped_mem);
987814 var efile = try elf.Elf.openStream(
988815 allocator,
989 @ptrCast(*DwarfSeekableStream, &seekable_stream.seekable_stream),
990 @ptrCast(*DwarfInStream, &seekable_stream.stream),
816 @ptrCast(*DW.DwarfSeekableStream, &seekable_stream.seekable_stream),
817 @ptrCast(*DW.DwarfInStream, &seekable_stream.stream),
991818 );
992819 defer efile.close();
993820
......@@ -1001,66 +828,57 @@ pub fn openElfDebugInfo(
1001828 return error.MissingDebugInfo;
1002829 const opt_debug_ranges = try efile.findSection(".debug_ranges");
1003830
1004 var di = DwarfInfo{
831 var di = DW.DwarfInfo{
1005832 .endian = efile.endian,
1006 .debug_info = (data[@intCast(usize, debug_info.sh_offset)..@intCast(usize, 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)]),
1008 .debug_str = (data[@intCast(usize, debug_str.sh_offset)..@intCast(usize, 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)]),
833 .debug_info = try chopSlice(mapped_mem, debug_info.sh_offset, debug_info.sh_size),
834 .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.sh_offset, debug_abbrev.sh_size),
835 .debug_str = try chopSlice(mapped_mem, debug_str.sh_offset, debug_str.sh_size),
836 .debug_line = try chopSlice(mapped_mem, debug_line.sh_offset, debug_line.sh_size),
1010837 .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)
1012839 else
1013840 null,
1014841 };
1015842
1016 try openDwarfDebugInfo(&di, allocator);
1017 return di;
843 try DW.openDwarfDebugInfo(&di, allocator);
844
845 return ModuleDebugInfo{
846 .base_address = undefined,
847 .dwarf = di,
848 .mapped_memory = mapped_mem,
849 };
1018850}
1019851
1020852/// TODO resources https://github.com/ziglang/zig/issues/4353
1021fn openSelfDebugInfoPosix(allocator: *mem.Allocator) !DwarfInfo {
1022 var exe_file = try fs.openSelfExe();
1023 errdefer exe_file.close();
853fn openMachODebugInfo(allocator: *mem.Allocator, macho_file_path: []const u8) !ModuleDebugInfo {
854 const mapped_mem = try mapWholeFile(macho_file_path);
1024855
1025 const exe_len = math.cast(usize, try exe_file.getEndPos()) catch
1026 return error.DebugInfoTooLarge;
1027 const exe_mmap = try os.mmap(
1028 null,
1029 exe_len,
1030 os.PROT_READ,
1031 os.MAP_SHARED,
1032 exe_file.handle,
1033 0,
856 const hdr = @ptrCast(
857 *const macho.mach_header_64,
858 @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
1034859 );
1035 errdefer os.munmap(exe_mmap);
1036
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);
860 if (hdr.magic != macho.MH_MAGIC_64)
861 return error.InvalidDebugInfo;
1044862
1045 const hdr_base = @ptrCast([*]u8, hdr);
863 const hdr_base = @ptrCast([*]const u8, hdr);
1046864 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
1047865 var ncmd: u32 = hdr.ncmds;
1048866 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);
1050868 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),
1052870 else => {},
1053871 }
1054872 ptr = @alignCast(@alignOf(std.macho.load_command), ptr + lc.cmdsize);
1055873 } else {
1056874 return error.MissingDebugInfo;
1057875 };
1058 const syms = @ptrCast([*]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];
876 const syms = @ptrCast([*]const macho.nlist_64, @alignCast(@alignOf(macho.nlist_64), hdr_base + symtab.symoff))[0..symtab.nsyms];
877 const strings = @ptrCast([*]const u8, hdr_base + symtab.stroff)[0..symtab.strsize :0];
1060878
1061879 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;
1064882 var reloc: u64 = 0;
1065883 var symbol_index: usize = 0;
1066884 var last_len: u64 = 0;
......@@ -1108,8 +926,10 @@ fn openSelfDebugInfoMacOs(allocator: *mem.Allocator) !DebugInfo {
1108926 // This sort is so that we can binary search later.
1109927 std.sort.sort(MachoSymbol, symbols, MachoSymbol.addressLessThan);
1110928
1111 return DebugInfo{
1112 .ofiles = DebugInfo.OFileTable.init(allocator),
929 return ModuleDebugInfo{
930 .base_address = undefined,
931 .mapped_memory = mapped_mem,
932 .ofiles = ModuleDebugInfo.OFileTable.init(allocator),
1113933 .symbols = symbols,
1114934 .strings = strings,
1115935 };
......@@ -1148,8 +968,8 @@ fn printLineFromFileAnyOs(out_stream: var, line_info: LineInfo) !void {
1148968}
1149969
1150970const MachoSymbol = struct {
1151 nlist: *macho.nlist_64,
1152 ofile: ?*macho.nlist_64,
971 nlist: *const macho.nlist_64,
972 ofile: ?*const macho.nlist_64,
1153973 reloc: u64,
1154974
1155975 /// Returns the address from the macho file
......@@ -1162,1057 +982,614 @@ const MachoSymbol = struct {
1162982 }
1163983};
1164984
1165pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
1166pub const DwarfInStream = io.InStream(anyerror);
1167
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 }
985fn mapWholeFile(path: []const u8) ![]const u8 {
986 const file = try fs.openFileAbsolute(path, .{});
987 defer file.close();
1184988
1185 /// This function works in freestanding mode.
1186 /// fn printLineFromFile(out_stream: var, line_info: LineInfo) !void
1187 pub fn printSourceAtAddress(
1188 self: *DwarfInfo,
1189 out_stream: var,
1190 address: usize,
1191 tty_config: TTY.Config,
1192 comptime printLineFromFile: var,
1193 ) !void {
1194 const compile_unit = self.findCompileUnit(address) catch {
1195 return printLineInfo(out_stream, null, address, "???", "???", tty_config, printLineFromFile);
1196 };
989 const file_len = try math.cast(usize, try file.getEndPos());
990 const mapped_mem = try os.mmap(
991 null,
992 file_len,
993 os.PROT_READ,
994 os.MAP_SHARED,
995 file.handle,
996 0,
997 );
998 errdefer os.munmap(mapped_mem);
1197999
1198 const compile_unit_name = try compile_unit.die.getAttrString(self, DW.AT_name);
1199 const symbol_name = self.getSymbolName(address) orelse "???";
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 }
1000 return mapped_mem;
1001}
12161002
1217 fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
1218 for (di.func_list.toSliceConst()) |*func| {
1219 if (func.pc_range) |range| {
1220 if (address >= range.start and address < range.end) {
1221 return func.name;
1222 }
1223 }
1224 }
1003pub const DebugInfo = struct {
1004 allocator: *mem.Allocator,
1005 address_map: std.AutoHashMap(usize, *ModuleDebugInfo),
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 };
12271012 }
12281013
1229 fn scanAllFunctions(di: *DwarfInfo) !void {
1230 var s = io.SliceSeekableInStream.init(di.debug_info);
1231 var this_unit_offset: u64 = 0;
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 }
1014 pub fn deinit(self: *DebugInfo) void {
1015 // TODO: resources https://github.com/ziglang/zig/issues/4353
1016 self.address_map.deinit();
13301017 }
13311018
1332 fn scanAllCompileUnits(di: *DwarfInfo) !void {
1333 var s = io.SliceSeekableInStream.init(di.debug_info);
1334 var this_unit_offset: u64 = 0;
1335
1336 while (true) {
1337 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
1338 error.EndOfStream => return,
1339 else => return err,
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));
1019 pub fn getModuleForAddress(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1020 if (comptime std.Target.current.isDarwin())
1021 return self.lookupModuleDyld(address)
1022 else if (builtin.os == .windows)
1023 return self.lookupModuleWin32(address)
1024 else
1025 return self.lookupModuleDl(address);
1026 }
13461027
1347 const version = try s.stream.readInt(u16, di.endian);
1348 if (version < 2 or version > 5) return error.InvalidDebugInfo;
1028 fn lookupModuleDyld(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
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();
1353 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
1035 if (address < base_address) continue;
13541036
1355 const compile_unit_pos = try s.seekable_stream.getPos();
1356 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
1037 const header = std.c._dyld_get_image_header(i) orelse continue;
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);
1361 compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
1050 const segment_cmd = @ptrCast(
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: {
1366 if (compile_unit_die.getAttrAddr(DW.AT_low_pc)) |low_pc| {
1367 if (compile_unit_die.getAttr(DW.AT_high_pc)) |high_pc_value| {
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;
1059 if (rebased_address >= seg_start and rebased_address < seg_end) {
1060 if (self.address_map.getValue(base_address)) |obj_di| {
1061 return obj_di;
13821062 }
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;
1397 }
1398 }
1064 const obj_di = try self.allocator.create(ModuleDebugInfo);
1065 errdefer self.allocator.destroy(obj_di);
13991066
1400 fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
1401 for (di.compile_unit_list.toSlice()) |*compile_unit| {
1402 if (compile_unit.pc_range) |range| {
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,
1067 const macho_path = mem.toSliceConst(u8, std.c._dyld_get_image_name(i));
1068 obj_di.* = openMachODebugInfo(self.allocator, macho_path) catch |err| switch (err) {
1069 error.FileNotFound => return error.MissingDebugInfo,
14151070 else => return err,
14161071 };
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) {
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;
1076 return obj_di;
14381077 }
14391078 }
14401079 }
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{
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;
1081 return error.MissingDebugInfo;
15061082 }
15071083
1508 fn getLineNumberInfo(di: *DwarfInfo, compile_unit: CompileUnit, target_address: usize) !LineInfo {
1509 var s = io.SliceSeekableInStream.init(di.debug_line);
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);
1084 fn lookupModuleWin32(self: *DebugInfo, address: usize) !*ModuleDebugInfo {
1085 const process_handle = windows.kernel32.GetCurrentProcess();
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;
1517 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
1518 if (unit_length == 0) {
1098 const needed_modules = bytes_needed / @sizeOf(windows.HMODULE);
1099
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)
15191109 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);
1524 // TODO support 3 and 5
1525 if (version != 2 and version != 4) return error.InvalidDebugInfo;
1111 // There's an unavoidable TOCTOU problem here, the module list may have
1112 // changed between the two EnumProcessModules call.
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);
1528 const prog_start_offset = (try s.seekable_stream.getPos()) + prologue_length;
1127 const seg_start = @ptrToInt(info.lpBaseOfDll);
1128 const seg_end = seg_start + info.SizeOfImage;
15291129
1530 const minimum_instruction_length = try s.stream.readByte();
1531 if (minimum_instruction_length == 0) return error.InvalidDebugInfo;
1130 if (address >= seg_start and address < seg_end) {
1131 if (self.address_map.getValue(seg_start)) |obj_di| {
1132 return obj_di;
1133 }
15321134
1533 if (version >= 4) {
1534 // maximum_operations_per_instruction
1535 _ = try s.stream.readByte();
1135 var name_buffer: [windows.PATH_MAX_WIDE + 4:0]u16 = undefined;
1136 // openFileAbsoluteW requires the prefix to be present
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 }
15361159 }
15371160
1538 const default_is_stmt = (try s.stream.readByte()) != 0;
1539 const line_base = try s.stream.readByteSigned();
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();
1161 return error.MissingDebugInfo;
1162 }
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 {
1549 var i: usize = 0;
1550 while (i < opcode_base - 1) : (i += 1) {
1551 standard_opcode_lengths[i] = try s.stream.readByte();
1180 const phdrs = info.dlpi_phdr[0..info.dlpi_phnum];
1181 for (phdrs) |*phdr| {
1182 if (phdr.p_type != elf.PT_LOAD) continue;
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 }
15521199 }
1200 }.callback)) {
1201 return error.MissingDebugInfo;
1202 } else |err| switch (err) {
1203 error.Found => {},
1204 else => return error.MissingDebugInfo,
15531205 }
15541206
1555 var include_directories = ArrayList([]u8).init(di.allocator());
1556 try include_directories.append(compile_unit_cwd);
1557 while (true) {
1558 const dir = try readStringRaw(di.allocator(), &s.stream);
1559 if (dir.len == 0) break;
1560 try include_directories.append(dir);
1207 if (self.address_map.getValue(ctx.base_address)) |obj_di| {
1208 return obj_di;
15611209 }
15621210
1563 var file_entries = ArrayList(FileEntry).init(di.allocator());
1564 var prog = LineNumberProgram.init(default_is_stmt, include_directories.toSliceConst(), &file_entries, target_address);
1565
1566 while (true) {
1567 const file_name = try readStringRaw(di.allocator(), &s.stream);
1568 if (file_name.len == 0) break;
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 }
1211 const elf_path = if (ctx.name.len > 0)
1212 ctx.name
1213 else blk: {
1214 var buf: [fs.MAX_PATH_BYTES]u8 = undefined;
1215 break :blk try fs.selfExePath(&buf);
1216 };
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) {
1585 const opcode = try s.stream.readByte();
1227 try self.address_map.putNoClobber(ctx.base_address, obj_di);
15861228
1587 if (opcode == DW.LNS_extended_op) {
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;
1229 return obj_di;
16741230 }
1231};
16751232
1676 fn getString(di: *DwarfInfo, offset: u64) ![]u8 {
1677 if (offset > di.debug_str.len)
1678 return error.InvalidDebugInfo;
1679 const casted_offset = math.cast(usize, offset) catch
1680 return error.InvalidDebugInfo;
1233const SymbolInfo = struct {
1234 symbol_name: []const u8 = "???",
1235 compile_unit_name: []const u8 = "???",
1236 line_info: ?LineInfo = null,
16811237
1682 // Valid strings always have a terminating zero byte
1683 if (mem.indexOfScalarPos(u8, di.debug_str, casted_offset, 0)) |last| {
1684 return di.debug_str[casted_offset..last];
1238 fn deinit(self: @This()) void {
1239 if (self.line_info) |li| {
1240 li.deinit();
16851241 }
1686
1687 return error.InvalidDebugInfo;
16881242 }
16891243};
16901244
1691pub const DebugInfo = switch (builtin.os) {
1245pub const ModuleDebugInfo = switch (builtin.os) {
16921246 .macosx, .ios, .watchos, .tvos => struct {
1247 base_address: usize,
1248 mapped_memory: []const u8,
16931249 symbols: []const MachoSymbol,
1694 strings: []const u8,
1250 strings: [:0]const u8,
16951251 ofiles: OFileTable,
16961252
1697 const OFileTable = std.HashMap(
1698 *macho.nlist_64,
1699 DwarfInfo,
1700 std.hash_map.getHashPtrAddrFn(*macho.nlist_64),
1701 std.hash_map.getTrivialEqlFn(*macho.nlist_64),
1702 );
1253 const OFileTable = std.StringHashMap(DW.DwarfInfo);
17031254
1704 pub fn allocator(self: DebugInfo) *mem.Allocator {
1255 pub fn allocator(self: @This()) *mem.Allocator {
17051256 return self.ofiles.allocator;
17061257 }
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 {
1732 // offset from .debug_abbrev
1733 offset: u64,
1734 table: AbbrevTable,
1735};
1736
1737const AbbrevTableEntry = struct {
1738 has_children: bool,
1739 abbrev_code: u64,
1740 tag_id: u64,
1741 attrs: ArrayList(AbbrevAttr),
1742};
1262 const hdr = @ptrCast(
1263 *const macho.mach_header_64,
1264 @alignCast(@alignOf(macho.mach_header_64), mapped_mem.ptr),
1265 );
1266 if (hdr.magic != std.macho.MH_MAGIC_64)
1267 return error.InvalidDebugInfo;
17431268
1744const AbbrevAttr = struct {
1745 attr_id: u64,
1746 form_id: u64,
1747};
1269 const hdr_base = @ptrCast([*]const u8, hdr);
1270 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
1271 var ncmd: u32 = hdr.ncmds;
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) {
1750 Address: u64,
1751 Block: []u8,
1752 Const: Constant,
1753 ExprLoc: []u8,
1754 Flag: bool,
1755 SecOffset: u64,
1756 Ref: u64,
1757 RefAddr: u64,
1758 String: []u8,
1759 StrPtr: u64,
1760};
1288 var opt_debug_line: ?*const macho.section_64 = null;
1289 var opt_debug_info: ?*const macho.section_64 = null;
1290 var opt_debug_abbrev: ?*const macho.section_64 = null;
1291 var opt_debug_str: ?*const macho.section_64 = null;
1292 var opt_debug_ranges: ?*const macho.section_64 = null;
1293
1294 const sections = @ptrCast(
1295 [*]const macho.section_64,
1296 @alignCast(@alignOf(macho.section_64), ptr + @sizeOf(std.macho.segment_command_64)),
1297 )[0..segcmd.nsects];
1298 for (sections) |*sect| {
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 {
1763 payload: u64,
1764 signed: bool,
1319 const debug_line = opt_debug_line orelse
1320 return error.MissingDebugInfo;
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 {
1767 if (self.signed) return error.InvalidDebugInfo;
1768 return self.payload;
1769 }
1770};
1328 var di = DW.DwarfInfo{
1329 .endian = .Little,
1330 .debug_info = try chopSlice(mapped_mem, debug_info.offset, debug_info.size),
1331 .debug_abbrev = try chopSlice(mapped_mem, debug_abbrev.offset, debug_abbrev.size),
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 {
1773 tag_id: u64,
1774 has_children: bool,
1775 attrs: ArrayList(Attr),
1340 try DW.openDwarfDebugInfo(&di, self.allocator());
17761341
1777 const Attr = struct {
1778 id: u64,
1779 value: FormValue,
1780 };
1342 // Add the debug info to the cache
1343 try self.ofiles.putNoClobber(o_file_path, di);
17811344
1782 fn getAttr(self: *const Die, id: u64) ?*const FormValue {
1783 for (self.attrs.toSliceConst()) |*attr| {
1784 if (attr.id == id) return &attr.value;
1345 return di;
17851346 }
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 {
1798 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
1799 return switch (form_value.*) {
1800 FormValue.Const => |value| value.asUnsignedLe(),
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};
1348 fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
1349 // Translate the VA into an address into this object
1350 const relocated_address = address - self.base_address;
1351 assert(relocated_address >= 0x100000000);
18311352
1832const FileEntry = struct {
1833 file_name: []const u8,
1834 dir_index: usize,
1835 mtime: usize,
1836 len_bytes: usize,
1837};
1353 // Find the .o file where this symbol is defined
1354 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse
1355 return SymbolInfo{};
18381356
1839pub const LineInfo = struct {
1840 line: u64,
1841 column: u64,
1842 file_name: []const u8,
1843 allocator: ?*mem.Allocator,
1357 // XXX: Return the symbol name
1358 if (symbol.ofile == null)
1359 return SymbolInfo{};
18441360
1845 fn deinit(self: LineInfo) void {
1846 const allocator = self.allocator orelse return;
1847 allocator.free(self.file_name);
1848 }
1849};
1361 assert(symbol.ofile.?.n_strx < self.strings.len);
1362 const o_file_path = mem.toSliceConst(u8, self.strings.ptr + symbol.ofile.?.n_strx);
18501363
1851const LineNumberProgram = struct {
1852 address: usize,
1853 file: usize,
1854 line: i64,
1855 column: u64,
1856 is_stmt: bool,
1857 basic_block: bool,
1858 end_sequence: bool,
1859
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 }
1364 // Check if its debug infos are already in the cache
1365 var o_file_di = self.ofiles.getValue(o_file_path) orelse
1366 (self.loadOFile(o_file_path) catch |err| switch (err) {
1367 error.MissingDebugInfo, error.InvalidDebugInfo => {
1368 // XXX: Return the symbol name
1369 return SymbolInfo{};
1370 },
1371 else => return err,
1372 });
18911373
1892 pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
1893 return LineNumberProgram{
1894 .address = 0,
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 }
1374 // Translate again the address, this time into an address inside the
1375 // .o file
1376 const relocated_address_o = relocated_address - symbol.reloc;
19141377
1915 pub fn checkLineMatch(self: *LineNumberProgram) !?LineInfo {
1916 if (self.target_address >= self.prev_address and self.target_address < self.address) {
1917 const file_entry = if (self.prev_file == 0) {
1918 return error.MissingDebugInfo;
1919 } else if (self.prev_file - 1 >= self.file_entries.len) {
1920 return error.InvalidDebugInfo;
1921 } else
1922 &self.file_entries.items[self.prev_file - 1];
1378 if (o_file_di.findCompileUnit(relocated_address_o)) |compile_unit| {
1379 return SymbolInfo{
1380 .symbol_name = o_file_di.getSymbolName(relocated_address_o) orelse "???",
1381 .compile_unit_name = compile_unit.die.getAttrString(&o_file_di, DW.AT_name) catch |err| switch (err) {
1382 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1383 else => return err,
1384 },
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) {
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 };
1397 unreachable;
19361398 }
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;
1939 self.prev_file = self.file;
1940 self.prev_line = self.line;
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};
1407 pub fn allocator(self: @This()) *mem.Allocator {
1408 return self.coff.allocator;
1409 }
19481410
1949// TODO the noasyncs here are workarounds
1950fn readStringRaw(allocator: *mem.Allocator, in_stream: var) ![]u8 {
1951 var buf = ArrayList(u8).init(allocator);
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}
1411 fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
1412 // Translate the VA into an address into this object
1413 const relocated_address = address - self.base_address;
19591414
1960// TODO the noasyncs here are workarounds
1961fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
1962 const buf = try allocator.alloc(u8, size);
1963 errdefer allocator.free(buf);
1964 if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile;
1965 return buf;
1966}
1415 var coff_section: *coff.Section = undefined;
1416 const mod_index = for (self.sect_contribs) |sect_contrib| {
1417 if (sect_contrib.Section > self.coff.sections.len) continue;
1418 // Remember that SectionContribEntry.Section is 1-based.
1419 coff_section = &self.coff.sections.toSlice()[sect_contrib.Section - 1];
19671420
1968fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
1969 const buf = try readAllocBytes(allocator, in_stream, size);
1970 return FormValue{ .Block = buf };
1971}
1421 const vaddr_start = coff_section.header.virtual_address + sect_contrib.Offset;
1422 const vaddr_end = vaddr_start + sect_contrib.Size;
1423 if (relocated_address >= vaddr_start and relocated_address < vaddr_end) {
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 workarounds
1974fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
1975 const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size);
1976 return parseFormValueBlockLen(allocator, in_stream, block_len);
1977}
1431 const mod = &self.modules[mod_index];
1432 try populateModule(self, mod);
1433 const obj_basename = fs.path.basename(mod.obj_file_name);
1434
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 {
1980 // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
1981 // `noasync` should be removed from all the function calls once it is fixed.
1982 return FormValue{
1983 .Const = Constant{
1984 .signed = signed,
1985 .payload = switch (size) {
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;
1536 // Checking that we are not reading garbage after the (possibly) multiple block fragments.
1537 if (line_index != subsection_end_index) {
1538 return error.InvalidDebugInfo;
1539 }
1540 }
1541 },
1542 else => {},
19971543 }
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 {
2087 for (abbrev_table.toSliceConst()) |*table_entry| {
2088 if (table_entry.abbrev_code == abbrev_code) return table_entry;
2089 }
2090 return null;
2091}
1545 if (sect_offset > subsect_info.len)
1546 return error.InvalidDebugInfo;
1547 } else {
1548 break :subsections null;
1549 }
1550 };
20921551
2093/// TODO resources https://github.com/ziglang/zig/issues/4353
2094fn getLineNumberInfoMacOs(di: *DebugInfo, symbol: MachoSymbol, address: usize) !LineInfo {
2095 const ofile = symbol.ofile orelse return error.MissingDebugInfo;
2096 const gop = try di.ofiles.getOrPut(ofile);
2097 const dwarf_info = if (gop.found_existing) &gop.kv.value else blk: {
2098 errdefer _ = di.ofiles.remove(ofile);
2099 const ofile_path = mem.toSliceConst(u8, @ptrCast([*:0]const u8, di.strings.ptr + ofile.n_strx));
2100
2101 var exe_file = try std.fs.openFileAbsoluteC(ofile_path, .{});
2102 errdefer exe_file.close();
2103
2104 const exe_len = math.cast(usize, try exe_file.getEndPos()) catch
2105 return error.DebugInfoTooLarge;
2106 const exe_mmap = try os.mmap(
2107 null,
2108 exe_len,
2109 os.PROT_READ,
2110 os.MAP_SHARED,
2111 exe_file.handle,
2112 0,
2113 );
2114 errdefer os.munmap(exe_mmap);
2115
2116 const hdr = @ptrCast(
2117 *const macho.mach_header_64,
2118 @alignCast(@alignOf(macho.mach_header_64), exe_mmap.ptr),
2119 );
2120 if (hdr.magic != std.macho.MH_MAGIC_64) return error.InvalidDebugInfo;
2121
2122 const hdr_base = @ptrCast([*]const u8, hdr);
2123 var ptr = hdr_base + @sizeOf(macho.mach_header_64);
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 );
1552 return SymbolInfo{
1553 .symbol_name = symbol_name,
1554 .compile_unit_name = obj_basename,
1555 .line_info = opt_line_info,
1556 };
1557 }
1558 },
1559 .linux, .freebsd => struct {
1560 base_address: usize,
1561 dwarf: DW.DwarfInfo,
1562 mapped_memory: []const u8,
1563
1564 fn getSymbolAtAddress(self: *@This(), address: usize) !SymbolInfo {
1565 // Translate the VA into an address into this object
1566 const relocated_address = address - self.base_address;
1567
1568 if (self.dwarf.findCompileUnit(relocated_address)) |compile_unit| {
1569 return SymbolInfo{
1570 .symbol_name = self.dwarf.getSymbolName(relocated_address) orelse "???",
1571 .compile_unit_name = compile_unit.die.getAttrString(&self.dwarf, DW.AT_name) catch |err| switch (err) {
1572 error.MissingDebugInfo, error.InvalidDebugInfo => "???",
1573 else => return err,
1574 },
1575 .line_info = self.dwarf.getLineNumberInfo(compile_unit.*, relocated_address) catch |err| switch (err) {
1576 error.MissingDebugInfo, error.InvalidDebugInfo => null,
1577 else => return err,
1578 },
1579 };
1580 } else |err| switch (err) {
1581 error.MissingDebugInfo, error.InvalidDebugInfo => {
1582 return SymbolInfo{};
21331583 },
2134 else => {},
1584 else => return err,
21351585 }
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;
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 }
1587 unreachable;
21671588 }
2168
2169 var debug_line = opt_debug_line orelse
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,
1589 },
1590 else => DW.DwarfInfo,
22021591};
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
22161593/// TODO multithreaded awareness
22171594var debug_info_allocator: ?*mem.Allocator = null;
22181595var debug_info_arena_allocator: std.heap.ArenaAllocator = undefined;
lib/std/dwarf.zig+889-682
......@@ -1,682 +1,889 @@
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;
1const std = @import("std.zig");
2const builtin = @import("builtin");
3const debug = std.debug;
4const fs = std.fs;
5const io = std.io;
6const mem = std.mem;
7const math = std.math;
8const leb = @import("debug/leb128.zig");
9
10const ArrayList = std.ArrayList;
11
12usingnamespace @import("dwarf_bits.zig");
13
14pub const DwarfSeekableStream = io.SeekableStream(anyerror, anyerror);
15pub const DwarfInStream = io.InStream(anyerror);
16
17const PcRange = struct {
18 start: u64,
19 end: u64,
20};
21
22const Func = struct {
23 pc_range: ?PcRange,
24 name: ?[]const u8,
25};
26
27const CompileUnit = struct {
28 version: u16,
29 is_64: bool,
30 die: *Die,
31 pc_range: ?PcRange,
32};
33
34const AbbrevTable = ArrayList(AbbrevTableEntry);
35
36const AbbrevTableHeader = struct {
37 // offset from .debug_abbrev
38 offset: u64,
39 table: AbbrevTable,
40};
41
42const AbbrevTableEntry = struct {
43 has_children: bool,
44 abbrev_code: u64,
45 tag_id: u64,
46 attrs: ArrayList(AbbrevAttr),
47};
48
49const AbbrevAttr = struct {
50 attr_id: u64,
51 form_id: u64,
52};
53
54const FormValue = union(enum) {
55 Address: u64,
56 Block: []u8,
57 Const: Constant,
58 ExprLoc: []u8,
59 Flag: bool,
60 SecOffset: u64,
61 Ref: u64,
62 RefAddr: u64,
63 String: []const u8,
64 StrPtr: u64,
65};
66
67const Constant = struct {
68 payload: u64,
69 signed: bool,
70
71 fn asUnsignedLe(self: *const Constant) !u64 {
72 if (self.signed) return error.InvalidDebugInfo;
73 return self.payload;
74 }
75};
76
77const Die = struct {
78 tag_id: u64,
79 has_children: bool,
80 attrs: ArrayList(Attr),
81
82 const Attr = struct {
83 id: u64,
84 value: FormValue,
85 };
86
87 fn getAttr(self: *const Die, id: u64) ?*const FormValue {
88 for (self.attrs.toSliceConst()) |*attr| {
89 if (attr.id == id) return &attr.value;
90 }
91 return null;
92 }
93
94 fn getAttrAddr(self: *const Die, id: u64) !u64 {
95 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
96 return switch (form_value.*) {
97 FormValue.Address => |value| value,
98 else => error.InvalidDebugInfo,
99 };
100 }
101
102 fn getAttrSecOffset(self: *const Die, id: u64) !u64 {
103 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
104 return switch (form_value.*) {
105 FormValue.Const => |value| value.asUnsignedLe(),
106 FormValue.SecOffset => |value| value,
107 else => error.InvalidDebugInfo,
108 };
109 }
110
111 fn getAttrUnsignedLe(self: *const Die, id: u64) !u64 {
112 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
113 return switch (form_value.*) {
114 FormValue.Const => |value| value.asUnsignedLe(),
115 else => error.InvalidDebugInfo,
116 };
117 }
118
119 fn getAttrRef(self: *const Die, id: u64) !u64 {
120 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
121 return switch (form_value.*) {
122 FormValue.Ref => |value| value,
123 else => error.InvalidDebugInfo,
124 };
125 }
126
127 fn getAttrString(self: *const Die, di: *DwarfInfo, id: u64) ![]const u8 {
128 const form_value = self.getAttr(id) orelse return error.MissingDebugInfo;
129 return switch (form_value.*) {
130 FormValue.String => |value| value,
131 FormValue.StrPtr => |offset| di.getString(offset),
132 else => error.InvalidDebugInfo,
133 };
134 }
135};
136
137const FileEntry = struct {
138 file_name: []const u8,
139 dir_index: usize,
140 mtime: usize,
141 len_bytes: usize,
142};
143
144const LineNumberProgram = struct {
145 address: usize,
146 file: usize,
147 line: i64,
148 column: u64,
149 is_stmt: bool,
150 basic_block: bool,
151 end_sequence: bool,
152
153 default_is_stmt: bool,
154 target_address: usize,
155 include_dirs: []const []const u8,
156 file_entries: *ArrayList(FileEntry),
157
158 prev_address: usize,
159 prev_file: usize,
160 prev_line: i64,
161 prev_column: u64,
162 prev_is_stmt: bool,
163 prev_basic_block: bool,
164 prev_end_sequence: bool,
165
166 // Reset the state machine following the DWARF specification
167 pub fn reset(self: *LineNumberProgram) void {
168 self.address = 0;
169 self.file = 1;
170 self.line = 1;
171 self.column = 0;
172 self.is_stmt = self.default_is_stmt;
173 self.basic_block = false;
174 self.end_sequence = false;
175 // Invalidate all the remaining fields
176 self.prev_address = 0;
177 self.prev_file = undefined;
178 self.prev_line = undefined;
179 self.prev_column = undefined;
180 self.prev_is_stmt = undefined;
181 self.prev_basic_block = undefined;
182 self.prev_end_sequence = undefined;
183 }
184
185 pub fn init(is_stmt: bool, include_dirs: []const []const u8, file_entries: *ArrayList(FileEntry), target_address: usize) LineNumberProgram {
186 return LineNumberProgram{
187 .address = 0,
188 .file = 1,
189 .line = 1,
190 .column = 0,
191 .is_stmt = is_stmt,
192 .basic_block = false,
193 .end_sequence = false,
194 .include_dirs = include_dirs,
195 .file_entries = file_entries,
196 .default_is_stmt = is_stmt,
197 .target_address = target_address,
198 .prev_address = 0,
199 .prev_file = undefined,
200 .prev_line = undefined,
201 .prev_column = undefined,
202 .prev_is_stmt = undefined,
203 .prev_basic_block = undefined,
204 .prev_end_sequence = undefined,
205 };
206 }
207
208 pub fn checkLineMatch(self: *LineNumberProgram) !?debug.LineInfo {
209 if (self.target_address >= self.prev_address and self.target_address < self.address) {
210 const file_entry = if (self.prev_file == 0) {
211 return error.MissingDebugInfo;
212 } else if (self.prev_file - 1 >= self.file_entries.len) {
213 return error.InvalidDebugInfo;
214 } else
215 &self.file_entries.items[self.prev_file - 1];
216
217 const dir_name = if (file_entry.dir_index >= self.include_dirs.len) {
218 return error.InvalidDebugInfo;
219 } else
220 self.include_dirs[file_entry.dir_index];
221 const file_name = try fs.path.join(self.file_entries.allocator, &[_][]const u8{ dir_name, file_entry.file_name });
222 errdefer self.file_entries.allocator.free(file_name);
223 return debug.LineInfo{
224 .line = if (self.prev_line >= 0) @intCast(u64, self.prev_line) else 0,
225 .column = self.prev_column,
226 .file_name = file_name,
227 .allocator = self.file_entries.allocator,
228 };
229 }
230
231 self.prev_address = self.address;
232 self.prev_file = self.file;
233 self.prev_line = self.line;
234 self.prev_column = self.column;
235 self.prev_is_stmt = self.is_stmt;
236 self.prev_basic_block = self.basic_block;
237 self.prev_end_sequence = self.end_sequence;
238 return null;
239 }
240};
241
242fn readInitialLength(comptime E: type, in_stream: *io.InStream(E), is_64: *bool) !u64 {
243 const first_32_bits = try in_stream.readIntLittle(u32);
244 is_64.* = (first_32_bits == 0xffffffff);
245 if (is_64.*) {
246 return in_stream.readIntLittle(u64);
247 } else {
248 if (first_32_bits >= 0xfffffff0) return error.InvalidDebugInfo;
249 // TODO this cast should not be needed
250 return @as(u64, first_32_bits);
251 }
252}
253
254// TODO the noasyncs here are workarounds
255fn readAllocBytes(allocator: *mem.Allocator, in_stream: var, size: usize) ![]u8 {
256 const buf = try allocator.alloc(u8, size);
257 errdefer allocator.free(buf);
258 if ((try noasync in_stream.read(buf)) < size) return error.EndOfFile;
259 return buf;
260}
261
262fn parseFormValueBlockLen(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
263 const buf = try readAllocBytes(allocator, in_stream, size);
264 return FormValue{ .Block = buf };
265}
266
267// TODO the noasyncs here are workarounds
268fn parseFormValueBlock(allocator: *mem.Allocator, in_stream: var, size: usize) !FormValue {
269 const block_len = try noasync in_stream.readVarInt(usize, builtin.Endian.Little, size);
270 return parseFormValueBlockLen(allocator, in_stream, block_len);
271}
272
273fn parseFormValueConstant(allocator: *mem.Allocator, in_stream: var, signed: bool, comptime size: i32) !FormValue {
274 // TODO: Please forgive me, I've worked around zig not properly spilling some intermediate values here.
275 // `noasync` should be removed from all the function calls once it is fixed.
276 return FormValue{
277 .Const = Constant{
278 .signed = signed,
279 .payload = switch (size) {
280 1 => try noasync in_stream.readIntLittle(u8),
281 2 => try noasync in_stream.readIntLittle(u16),
282 4 => try noasync in_stream.readIntLittle(u32),
283 8 => try noasync in_stream.readIntLittle(u64),
284 -1 => blk: {
285 if (signed) {
286 const x = try noasync leb.readILEB128(i64, in_stream);
287 break :blk @bitCast(u64, x);
288 } else {
289 const x = try noasync leb.readULEB128(u64, in_stream);
290 break :blk x;
291 }
292 },
293 else => @compileError("Invalid size"),
294 },
295 },
296 };
297}
298
299// TODO the noasyncs here are workarounds
300fn parseFormValueDwarfOffsetSize(in_stream: var, is_64: bool) !u64 {
301 return if (is_64) try noasync in_stream.readIntLittle(u64) else @as(u64, try noasync in_stream.readIntLittle(u32));
302}
303
304// TODO the noasyncs here are workarounds
305fn parseFormValueTargetAddrSize(in_stream: var) !u64 {
306 if (@sizeOf(usize) == 4) {
307 // TODO this cast should not be needed
308 return @as(u64, try noasync in_stream.readIntLittle(u32));
309 } else if (@sizeOf(usize) == 8) {
310 return noasync in_stream.readIntLittle(u64);
311 } else {
312 unreachable;
313 }
314}
315
316// TODO the noasyncs here are workarounds
317fn parseFormValueRef(allocator: *mem.Allocator, in_stream: var, size: i32) !FormValue {
318 return FormValue{
319 .Ref = switch (size) {
320 1 => try noasync in_stream.readIntLittle(u8),
321 2 => try noasync in_stream.readIntLittle(u16),
322 4 => try noasync in_stream.readIntLittle(u32),
323 8 => try noasync in_stream.readIntLittle(u64),
324 -1 => try noasync leb.readULEB128(u64, in_stream),
325 else => unreachable,
326 },
327 };
328}
329
330// TODO the noasyncs here are workarounds
331fn parseFormValue(allocator: *mem.Allocator, in_stream: var, form_id: u64, is_64: bool) anyerror!FormValue {
332 return switch (form_id) {
333 FORM_addr => FormValue{ .Address = try parseFormValueTargetAddrSize(in_stream) },
334 FORM_block1 => parseFormValueBlock(allocator, in_stream, 1),
335 FORM_block2 => parseFormValueBlock(allocator, in_stream, 2),
336 FORM_block4 => parseFormValueBlock(allocator, in_stream, 4),
337 FORM_block => x: {
338 const block_len = try noasync leb.readULEB128(usize, in_stream);
339 return parseFormValueBlockLen(allocator, in_stream, block_len);
340 },
341 FORM_data1 => parseFormValueConstant(allocator, in_stream, false, 1),
342 FORM_data2 => parseFormValueConstant(allocator, in_stream, false, 2),
343 FORM_data4 => parseFormValueConstant(allocator, in_stream, false, 4),
344 FORM_data8 => parseFormValueConstant(allocator, in_stream, false, 8),
345 FORM_udata, FORM_sdata => {
346 const signed = form_id == FORM_sdata;
347 return parseFormValueConstant(allocator, in_stream, signed, -1);
348 },
349 FORM_exprloc => {
350 const size = try noasync leb.readULEB128(usize, in_stream);
351 const buf = try readAllocBytes(allocator, in_stream, size);
352 return FormValue{ .ExprLoc = buf };
353 },
354 FORM_flag => FormValue{ .Flag = (try noasync in_stream.readByte()) != 0 },
355 FORM_flag_present => FormValue{ .Flag = true },
356 FORM_sec_offset => FormValue{ .SecOffset = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
357
358 FORM_ref1 => parseFormValueRef(allocator, in_stream, 1),
359 FORM_ref2 => parseFormValueRef(allocator, in_stream, 2),
360 FORM_ref4 => parseFormValueRef(allocator, in_stream, 4),
361 FORM_ref8 => parseFormValueRef(allocator, in_stream, 8),
362 FORM_ref_udata => parseFormValueRef(allocator, in_stream, -1),
363
364 FORM_ref_addr => FormValue{ .RefAddr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
365 FORM_ref_sig8 => FormValue{ .Ref = try noasync in_stream.readIntLittle(u64) },
366
367 FORM_string => FormValue{ .String = try in_stream.readUntilDelimiterAlloc(allocator, 0, math.maxInt(usize)) },
368 FORM_strp => FormValue{ .StrPtr = try parseFormValueDwarfOffsetSize(in_stream, is_64) },
369 FORM_indirect => {
370 const child_form_id = try noasync leb.readULEB128(u64, in_stream);
371 const F = @TypeOf(async parseFormValue(allocator, in_stream, child_form_id, is_64));
372 var frame = try allocator.create(F);
373 defer allocator.destroy(frame);
374 return await @asyncCall(frame, {}, parseFormValue, allocator, in_stream, child_form_id, is_64);
375 },
376 else => error.InvalidDebugInfo,
377 };
378}
379
380fn getAbbrevTableEntry(abbrev_table: *const AbbrevTable, abbrev_code: u64) ?*const AbbrevTableEntry {
381 for (abbrev_table.toSliceConst()) |*table_entry| {
382 if (table_entry.abbrev_code == abbrev_code) return table_entry;
383 }
384 return null;
385}
386
387pub const DwarfInfo = struct {
388 endian: builtin.Endian,
389 // No memory is owned by the DwarfInfo
390 debug_info: []const u8,
391 debug_abbrev: []const u8,
392 debug_str: []const u8,
393 debug_line: []const u8,
394 debug_ranges: ?[]const u8,
395 // Filled later by the initializer
396 abbrev_table_list: ArrayList(AbbrevTableHeader) = undefined,
397 compile_unit_list: ArrayList(CompileUnit) = undefined,
398 func_list: ArrayList(Func) = undefined,
399
400 pub fn allocator(self: DwarfInfo) *mem.Allocator {
401 return self.abbrev_table_list.allocator;
402 }
403
404 fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
405 for (di.func_list.toSliceConst()) |*func| {
406 if (func.pc_range) |range| {
407 if (address >= range.start and address < range.end) {
408 return func.name;
409 }
410 }
411 }
412
413 return null;
414 }
415
416 fn scanAllFunctions(di: *DwarfInfo) !void {
417 var s = io.SliceSeekableInStream.init(di.debug_info);
418 var this_unit_offset: u64 = 0;
419
420 while (true) {
421 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
422 error.EndOfStream => return,
423 else => return err,
424 };
425
426 var is_64: bool = undefined;
427 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
428 if (unit_length == 0) return;
429 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
430
431 const version = try s.stream.readInt(u16, di.endian);
432 if (version < 2 or version > 5) return error.InvalidDebugInfo;
433
434 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
435
436 const address_size = try s.stream.readByte();
437 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
438
439 const compile_unit_pos = try s.seekable_stream.getPos();
440 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
441
442 try s.seekable_stream.seekTo(compile_unit_pos);
443
444 const next_unit_pos = this_unit_offset + next_offset;
445
446 while ((try s.seekable_stream.getPos()) < next_unit_pos) {
447 const die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse continue;
448 const after_die_offset = try s.seekable_stream.getPos();
449
450 switch (die_obj.tag_id) {
451 TAG_subprogram, TAG_inlined_subroutine, TAG_subroutine, TAG_entry_point => {
452 const fn_name = x: {
453 var depth: i32 = 3;
454 var this_die_obj = die_obj;
455 // Prenvent endless loops
456 while (depth > 0) : (depth -= 1) {
457 if (this_die_obj.getAttr(AT_name)) |_| {
458 const name = try this_die_obj.getAttrString(di, AT_name);
459 break :x name;
460 } else if (this_die_obj.getAttr(AT_abstract_origin)) |ref| {
461 // Follow the DIE it points to and repeat
462 const ref_offset = try this_die_obj.getAttrRef(AT_abstract_origin);
463 if (ref_offset > next_offset) return error.InvalidDebugInfo;
464 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
465 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
466 } else if (this_die_obj.getAttr(AT_specification)) |ref| {
467 // Follow the DIE it points to and repeat
468 const ref_offset = try this_die_obj.getAttrRef(AT_specification);
469 if (ref_offset > next_offset) return error.InvalidDebugInfo;
470 try s.seekable_stream.seekTo(this_unit_offset + ref_offset);
471 this_die_obj = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
472 } else {
473 break :x null;
474 }
475 }
476
477 break :x null;
478 };
479
480 const pc_range = x: {
481 if (die_obj.getAttrAddr(AT_low_pc)) |low_pc| {
482 if (die_obj.getAttr(AT_high_pc)) |high_pc_value| {
483 const pc_end = switch (high_pc_value.*) {
484 FormValue.Address => |value| value,
485 FormValue.Const => |value| b: {
486 const offset = try value.asUnsignedLe();
487 break :b (low_pc + offset);
488 },
489 else => return error.InvalidDebugInfo,
490 };
491 break :x PcRange{
492 .start = low_pc,
493 .end = pc_end,
494 };
495 } else {
496 break :x null;
497 }
498 } else |err| {
499 if (err != error.MissingDebugInfo) return err;
500 break :x null;
501 }
502 };
503
504 try di.func_list.append(Func{
505 .name = fn_name,
506 .pc_range = pc_range,
507 });
508 },
509 else => {},
510 }
511
512 try s.seekable_stream.seekTo(after_die_offset);
513 }
514
515 this_unit_offset += next_offset;
516 }
517 }
518
519 fn scanAllCompileUnits(di: *DwarfInfo) !void {
520 var s = io.SliceSeekableInStream.init(di.debug_info);
521 var this_unit_offset: u64 = 0;
522
523 while (true) {
524 s.seekable_stream.seekTo(this_unit_offset) catch |err| switch (err) {
525 error.EndOfStream => return,
526 else => return err,
527 };
528
529 var is_64: bool = undefined;
530 const unit_length = try readInitialLength(@TypeOf(s.stream.readFn).ReturnType.ErrorSet, &s.stream, &is_64);
531 if (unit_length == 0) return;
532 const next_offset = unit_length + (if (is_64) @as(usize, 12) else @as(usize, 4));
533
534 const version = try s.stream.readInt(u16, di.endian);
535 if (version < 2 or version > 5) return error.InvalidDebugInfo;
536
537 const debug_abbrev_offset = if (is_64) try s.stream.readInt(u64, di.endian) else try s.stream.readInt(u32, di.endian);
538
539 const address_size = try s.stream.readByte();
540 if (address_size != @sizeOf(usize)) return error.InvalidDebugInfo;
541
542 const compile_unit_pos = try s.seekable_stream.getPos();
543 const abbrev_table = try di.getAbbrevTable(debug_abbrev_offset);
544
545 try s.seekable_stream.seekTo(compile_unit_pos);
546
547 const compile_unit_die = try di.allocator().create(Die);
548 compile_unit_die.* = (try di.parseDie(&s.stream, abbrev_table, is_64)) orelse return error.InvalidDebugInfo;
549
550 if (compile_unit_die.tag_id != TAG_compile_unit) return error.InvalidDebugInfo;
551
552 const pc_range = x: {
553 if (compile_unit_die.getAttrAddr(AT_low_pc)) |low_pc| {
554 if (compile_unit_die.getAttr(AT_high_pc)) |high_pc_value| {
555 const pc_end = switch (high_pc_value.*) {
556 FormValue.Address => |value| value,
557 FormValue.Const => |value| b: {
558 const offset = try value.asUnsignedLe();
559 break :b (low_pc + offset);
560 },
561 else => return error.InvalidDebugInfo,
562 };
563 break :x PcRange{
564 .start = low_pc,
565 .end = pc_end,
566 };
567 } else {
568 break :x null;
569 }
570 } else |err| {
571 if (err != error.MissingDebugInfo) return err;
572 break :x null;
573 }
574 };
575
576 try di.compile_unit_list.append(CompileUnit{
577 .version = version,
578 .is_64 = is_64,
579 .pc_range = pc_range,
580 .die = compile_unit_die,
581 });
582
583 this_unit_offset += next_offset;
584 }
585 }
586
587 fn findCompileUnit(di: *DwarfInfo, target_address: u64) !*const CompileUnit {
588 for (di.compile_unit_list.toSlice()) |*compile_unit| {
589 if (compile_unit.pc_range) |range| {
590 if (target_address >= range.start and target_address < range.end) return compile_unit;
591 }
592 if (di.debug_ranges) |debug_ranges| {
593 if (compile_unit.die.getAttrSecOffset(AT_ranges)) |ranges_offset| {
594 var s = io.SliceSeekableInStream.init(debug_ranges);
595
596 // All the addresses in the list are relative to the value
597 // specified by DW_AT_low_pc or to some other value encoded
598 // in the list itself.
599 // If no starting value is specified use zero.
600 var base_address = compile_unit.die.getAttrAddr(AT_low_pc) catch |err| switch (err) {
601 error.MissingDebugInfo => 0,
602 else => return err,
603 };
604
605 try s.seekable_stream.seekTo(ranges_offset);
606
607 while (true) {
608 const begin_addr = try s.stream.readIntLittle(usize);
609 const end_addr = try s.stream.readIntLittle(usize);
610 if (begin_addr == 0 and end_addr == 0) {
611 break;
612 }
613 // This entry selects a new value for the base address
614 if (begin_addr == math.maxInt(usize)) {
615 base_address = end_addr;
616 continue;
617 }
618 if (target_address >= base_address + begin_addr and target_address < base_address + end_addr) {
619 return compile_unit;
620 }
621 }
622 } else |err| {
623 if (err != error.MissingDebugInfo) return err;
624 continue;
625 }
626 }
627 }
628 return error.MissingDebugInfo;
629 }
630
631 /// Gets an already existing AbbrevTable given the abbrev_offset, or if not found,
632 /// seeks in the stream and parses it.
633 fn getAbbrevTable(di: *DwarfInfo, abbrev_offset: u64) !*const AbbrevTable {
634 for (di.abbrev_table_list.toSlice()) |*header| {
635 if (header.offset == abbrev_offset) {
636 return &header.table;
637 }
638 }
639 try di.abbrev_table_list.append(AbbrevTableHeader{
640 .offset = abbrev_offset,
641 .table = try di.parseAbbrevTable(abbrev_offset),
642 });
643 return &di.abbrev_table_list.items[di.abbrev_table_list.len - 1].table;
644 }
645
646 fn parseAbbrevTable(di: *DwarfInfo, offset: u64) !AbbrevTable {
647 var s = io.SliceSeekableInStream.init(di.debug_abbrev);
648
649 try s.seekable_stream.seekTo(offset);
650 var result = AbbrevTable.init(di.allocator());
651 errdefer result.deinit();
652 while (true) {
653 const abbrev_code = try leb.readULEB128(u64, &s.stream);
654 if (abbrev_code == 0) return result;
655 try result.append(AbbrevTableEntry{
656 .abbrev_code = abbrev_code,
657 .tag_id = try leb.readULEB128(u64, &s.stream),
658 .has_children = (try s.stream.readByte()) == CHILDREN_yes,
659 .attrs = ArrayList(AbbrevAttr).init(di.allocator()),
660 });
661 const attrs = &result.items[result.len - 1].attrs;
662
663 while (true) {
664 const attr_id = try leb.readULEB128(u64, &s.stream);
665 const form_id = try leb.readULEB128(u64, &s.stream);
666 if (attr_id == 0 and form_id == 0) break;
667 try attrs.append(AbbrevAttr{
668 .attr_id = attr_id,
669 .form_id = form_id,
670 });
671 }
672 }
673 }
674
675 fn parseDie(di: *DwarfInfo, in_stream: var, abbrev_table: *const AbbrevTable, is_64: bool) !?Die {
676 const abbrev_code = try leb.readULEB128(u64, in_stream);
677 if (abbrev_code == 0) return null;
678 const table_entry = getAbbrevTableEntry(abbrev_table, abbrev_code) orelse return error.InvalidDebugInfo;
679
680 var result = Die{
681 .tag_id = table_entry.tag_id,
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 {
2424 cmdsize: u32,
2525};
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
2738/// The symtab_command contains the offsets and sizes of the link-edit 4.3BSD
2839/// "stab" style symbol table information as described in the header files
2940/// <nlist.h> and <stab.h>.
test/stack_traces.zig+19-18
......@@ -3,6 +3,7 @@ const std = @import("std");
33const os = std.os;
44const tests = @import("tests.zig");
55
6// zig fmt: off
67pub fn addCases(cases: *tests.StackTracesContext) void {
78 const source_return =
89 \\const std = @import("std");
......@@ -41,7 +42,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
4142 \\ try foo();
4243 \\}
4344 ;
44 // zig fmt: off
45
4546 switch (builtin.os) {
4647 .freebsd => {
4748 cases.addCase(
......@@ -264,14 +265,14 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
264265 [_][]const u8{
265266 // debug
266267 \\error: TheSkyIsFalling
267 \\source.zig:4:5: [address] in _main.0 (test.o)
268 \\source.zig:4:5: [address] in main (test)
268269 \\ return error.TheSkyIsFalling;
269270 \\ ^
270271 \\
271272 ,
272273 // release-safe
273274 \\error: TheSkyIsFalling
274 \\source.zig:4:5: [address] in _main (test.o)
275 \\source.zig:4:5: [address] in std.start.main (test)
275276 \\ return error.TheSkyIsFalling;
276277 \\ ^
277278 \\
......@@ -291,20 +292,20 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
291292 [_][]const u8{
292293 // debug
293294 \\error: TheSkyIsFalling
294 \\source.zig:4:5: [address] in _foo (test.o)
295 \\source.zig:4:5: [address] in foo (test)
295296 \\ return error.TheSkyIsFalling;
296297 \\ ^
297 \\source.zig:8:5: [address] in _main.0 (test.o)
298 \\source.zig:8:5: [address] in main (test)
298299 \\ try foo();
299300 \\ ^
300301 \\
301302 ,
302303 // release-safe
303304 \\error: TheSkyIsFalling
304 \\source.zig:4:5: [address] in _main (test.o)
305 \\source.zig:4:5: [address] in std.start.main (test)
305306 \\ return error.TheSkyIsFalling;
306307 \\ ^
307 \\source.zig:8:5: [address] in _main (test.o)
308 \\source.zig:8:5: [address] in std.start.main (test)
308309 \\ try foo();
309310 \\ ^
310311 \\
......@@ -324,32 +325,32 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
324325 [_][]const u8{
325326 // debug
326327 \\error: TheSkyIsFalling
327 \\source.zig:12:5: [address] in _make_error (test.o)
328 \\source.zig:12:5: [address] in make_error (test)
328329 \\ return error.TheSkyIsFalling;
329330 \\ ^
330 \\source.zig:8:5: [address] in _bar (test.o)
331 \\source.zig:8:5: [address] in bar (test)
331332 \\ return make_error();
332333 \\ ^
333 \\source.zig:4:5: [address] in _foo (test.o)
334 \\source.zig:4:5: [address] in foo (test)
334335 \\ try bar();
335336 \\ ^
336 \\source.zig:16:5: [address] in _main.0 (test.o)
337 \\source.zig:16:5: [address] in main (test)
337338 \\ try foo();
338339 \\ ^
339340 \\
340341 ,
341342 // release-safe
342343 \\error: TheSkyIsFalling
343 \\source.zig:12:5: [address] in _main (test.o)
344 \\source.zig:12:5: [address] in std.start.main (test)
344345 \\ return error.TheSkyIsFalling;
345346 \\ ^
346 \\source.zig:8:5: [address] in _main (test.o)
347 \\source.zig:8:5: [address] in std.start.main (test)
347348 \\ return make_error();
348349 \\ ^
349 \\source.zig:4:5: [address] in _main (test.o)
350 \\source.zig:4:5: [address] in std.start.main (test)
350351 \\ try bar();
351352 \\ ^
352 \\source.zig:16:5: [address] in _main (test.o)
353 \\source.zig:16:5: [address] in std.start.main (test)
353354 \\ try foo();
354355 \\ ^
355356 \\
......@@ -393,7 +394,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
393394 source_try_return,
394395 [_][]const u8{
395396 // debug
396 \\error: TheSkyIsFalling
397 \\error: TheSkyIsFalling
397398 \\source.zig:4:5: [address] in foo (test.obj)
398399 \\ return error.TheSkyIsFalling;
399400 \\ ^
......@@ -419,7 +420,7 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
419420 source_try_try_return_return,
420421 [_][]const u8{
421422 // debug
422 \\error: TheSkyIsFalling
423 \\error: TheSkyIsFalling
423424 \\source.zig:12:5: [address] in make_error (test.obj)
424425 \\ return error.TheSkyIsFalling;
425426 \\ ^
......@@ -449,5 +450,5 @@ pub fn addCases(cases: *tests.StackTracesContext) void {
449450 },
450451 else => {},
451452 }
452 // zig fmt: off
453453}
454// zig fmt: off