authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-05-15 01:52:53-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:13-04:00
logb449d98a935a20429874d8eb379d9cc0e49c5fcd
tree8d483a4e3e8ccdfcc90ad9e0c7f70c642f9bff38
parent69399fbb82ea74fce4fb6bbfec5ab2cbfa435c1a

- rework StackIterator to optionally use debug_info to unwind the stack

- add abi routines for getting register values - unwding is working!

4 files changed, 513 insertions(+), 122 deletions(-)

lib/std/debug.zig+187-80
...@@ -135,8 +135,9 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {...@@ -135,8 +135,9 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
135135
136/// Tries to print the stack trace starting from the supplied base pointer to stderr,136/// Tries to print the stack trace starting from the supplied base pointer to stderr,
137/// unbuffered, and ignores any error returned.137/// unbuffered, and ignores any error returned.
138/// `context` is either *const os.ucontext_t on posix, or the result of CONTEXT.getRegs() on Windows.
138/// TODO multithreaded awareness139/// TODO multithreaded awareness
139pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {140pub fn dumpStackTraceFromBase(context: anytype) void {
140 nosuspend {141 nosuspend {
141 if (comptime builtin.target.isWasm()) {142 if (comptime builtin.target.isWasm()) {
142 if (native_os == .wasi) {143 if (native_os == .wasi) {
...@@ -156,12 +157,15 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {...@@ -156,12 +157,15 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
156 };157 };
157 const tty_config = io.tty.detectConfig(io.getStdErr());158 const tty_config = io.tty.detectConfig(io.getStdErr());
158 if (native_os == .windows) {159 if (native_os == .windows) {
159 writeCurrentStackTraceWindows(stderr, debug_info, tty_config, ip) catch return;160 writeCurrentStackTraceWindows(stderr, debug_info, tty_config, context.ip) catch return;
160 return;161 return;
161 }162 }
162163
163 printSourceAtAddress(debug_info, stderr, ip, tty_config) catch return;164 var it = StackIterator.initWithContext(null, debug_info, context) catch return;
164 var it = StackIterator.init(null, bp);165
166 // TODO: Should `it.dwarf_context.pc` be `it.getIp()`? (but then the non-dwarf case has to store ip)
167 printSourceAtAddress(debug_info, stderr, it.dwarf_context.pc, tty_config) catch return;
168
165 while (it.next()) |return_address| {169 while (it.next()) |return_address| {
166 // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS,170 // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS,
167 // therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid171 // therefore, we do a check for `return_address == 0` before subtracting 1 from it to avoid
...@@ -206,6 +210,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT...@@ -206,6 +210,7 @@ pub fn captureStackTrace(first_address: ?usize, stack_trace: *std.builtin.StackT
206 }210 }
207 stack_trace.index = slice.len;211 stack_trace.index = slice.len;
208 } else {212 } else {
213 // TODO: This should use the dwarf unwinder if it's available
209 var it = StackIterator.init(first_address, null);214 var it = StackIterator.init(first_address, null);
210 for (stack_trace.instruction_addresses, 0..) |*addr, i| {215 for (stack_trace.instruction_addresses, 0..) |*addr, i| {
211 addr.* = it.next() orelse {216 addr.* = it.next() orelse {
...@@ -405,6 +410,11 @@ pub const StackIterator = struct {...@@ -405,6 +410,11 @@ pub const StackIterator = struct {
405 // Last known value of the frame pointer register.410 // Last known value of the frame pointer register.
406 fp: usize,411 fp: usize,
407412
413 // When DebugInfo and a register context is available, this iterator can unwind
414 // stacks with frames that don't use a frame pointer (ie. -fomit-frame-pointer).
415 debug_info: ?*DebugInfo,
416 dwarf_context: if (@hasDecl(os, "ucontext_t")) DW.UnwindContext else void = undefined,
417
408 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {418 pub fn init(first_address: ?usize, fp: ?usize) StackIterator {
409 if (native_arch == .sparc64) {419 if (native_arch == .sparc64) {
410 // Flush all the register windows on stack.420 // Flush all the register windows on stack.
...@@ -416,9 +426,17 @@ pub const StackIterator = struct {...@@ -416,9 +426,17 @@ pub const StackIterator = struct {
416 return StackIterator{426 return StackIterator{
417 .first_address = first_address,427 .first_address = first_address,
418 .fp = fp orelse @frameAddress(),428 .fp = fp orelse @frameAddress(),
429 .debug_info = null,
419 };430 };
420 }431 }
421432
433 pub fn initWithContext(first_address: ?usize, debug_info: *DebugInfo, context: *const os.ucontext_t) !StackIterator {
434 var iterator = init(first_address, null);
435 iterator.debug_info = debug_info;
436 iterator.dwarf_context = try DW.UnwindContext.init(context);
437 return iterator;
438 }
439
422 // Offset of the saved BP wrt the frame pointer.440 // Offset of the saved BP wrt the frame pointer.
423 const fp_offset = if (native_arch.isRISCV())441 const fp_offset = if (native_arch.isRISCV())
424 // On RISC-V the frame pointer points to the top of the saved register442 // On RISC-V the frame pointer points to the top of the saved register
...@@ -500,7 +518,28 @@ pub const StackIterator = struct {...@@ -500,7 +518,28 @@ pub const StackIterator = struct {
500 }518 }
501 }519 }
502520
521 fn next_dwarf(self: *StackIterator) !void {
522 const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
523 if (module.getDwarfInfo()) |di| {
524 try di.unwindFrame(self.debug_info.?.allocator, &self.dwarf_context, module.base_address);
525 } else return error.MissingDebugInfo;
526 }
527
503 fn next_internal(self: *StackIterator) ?usize {528 fn next_internal(self: *StackIterator) ?usize {
529 if (self.debug_info != null) {
530 if (self.next_dwarf()) |_| {
531 return self.dwarf_context.pc;
532 } else |err| {
533 // Fall back to fp unwinding on the first failure,
534 // as the register context won't be updated
535 self.fp = self.dwarf_context.getFp() catch 0;
536 self.debug_info = null;
537
538 // TODO: Remove
539 print("\ndwarf unwind error {}, placing fp at 0x{x}\n\n", .{err, self.fp});
540 }
541 }
542
504 const fp = if (comptime native_arch.isSPARC())543 const fp = if (comptime native_arch.isSPARC())
505 // On SPARC the offset is positive. (!)544 // On SPARC the offset is positive. (!)
506 math.add(usize, self.fp, fp_offset) catch return null545 math.add(usize, self.fp, fp_offset) catch return null
...@@ -540,6 +579,8 @@ pub fn writeCurrentStackTrace(...@@ -540,6 +579,8 @@ pub fn writeCurrentStackTrace(
540 if (native_os == .windows) {579 if (native_os == .windows) {
541 return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);580 return writeCurrentStackTraceWindows(out_stream, debug_info, tty_config, start_addr);
542 }581 }
582
583 // TODO: Capture a context and use initWithContext
543 var it = StackIterator.init(start_addr, null);584 var it = StackIterator.init(start_addr, null);
544 while (it.next()) |return_address| {585 while (it.next()) |return_address| {
545 // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS,586 // On arm64 macOS, the address of the last frame is 0x0 rather than 0x1 as on x86_64 macOS,
...@@ -800,12 +841,14 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe...@@ -800,12 +841,14 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe
800 // This coff file has embedded DWARF debug info841 // This coff file has embedded DWARF debug info
801 _ = sec;842 _ = sec;
802843
803 const num_sections = std.enums.directEnumArrayLen(DW.DwarfSection, 0);844 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
804 var sections: [num_sections]?[]const u8 = [_]?[]const u8{null} ** num_sections;845 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
805 errdefer for (sections) |section| if (section) |s| allocator.free(s);
806846
807 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {847 inline for (@typeInfo(DW.DwarfSection).Enum.fields, 0..) |section, i| {
808 sections[i] = try coff_obj.getSectionDataAlloc("." ++ section.name, allocator);848 sections[i] = .{
849 .data = try coff_obj.getSectionDataAlloc("." ++ section.name, allocator),
850 .owned = true,
851 };
809 }852 }
810853
811 var dwarf = DW.DwarfInfo{854 var dwarf = DW.DwarfInfo{
...@@ -813,7 +856,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe...@@ -813,7 +856,7 @@ fn readCoffDebugInfo(allocator: mem.Allocator, coff_bytes: []const u8) !ModuleDe
813 .sections = sections,856 .sections = sections,
814 };857 };
815858
816 try DW.openDwarfDebugInfo(&dwarf, allocator);859 try DW.openDwarfDebugInfo(&dwarf, allocator, coff_bytes);
817 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };860 di.debug_data = PdbOrDwarf{ .dwarf = dwarf };
818 return di;861 return di;
819 }862 }
...@@ -854,6 +897,8 @@ pub fn readElfDebugInfo(...@@ -854,6 +897,8 @@ pub fn readElfDebugInfo(
854 elf_filename: ?[]const u8,897 elf_filename: ?[]const u8,
855 build_id: ?[]const u8,898 build_id: ?[]const u8,
856 expected_crc: ?u32,899 expected_crc: ?u32,
900 parent_sections: *DW.DwarfInfo.SectionArray,
901 parent_mapped_mem: ?[]align(mem.page_size) const u8,
857) !ModuleDebugInfo {902) !ModuleDebugInfo {
858 nosuspend {903 nosuspend {
859904
...@@ -891,10 +936,20 @@ pub fn readElfDebugInfo(...@@ -891,10 +936,20 @@ pub fn readElfDebugInfo(
891 @ptrCast(@alignCast(&mapped_mem[shoff])),936 @ptrCast(@alignCast(&mapped_mem[shoff])),
892 )[0..hdr.e_shnum];937 )[0..hdr.e_shnum];
893938
894 const num_sections = std.enums.directEnumArrayLen(DW.DwarfSection, 0);939 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
895 var sections: [num_sections]?[]const u8 = [_]?[]const u8{null} ** num_sections;940
896 var owned_sections: [num_sections][]const u8 = [_][]const u8{&.{}} ** num_sections;941 // Take ownership over any owned sections from the parent scope
897 errdefer for (owned_sections) |section| allocator.free(section);942 for (parent_sections, &sections) |*parent, *section| {
943 if (parent.*) |*p| {
944 section.* = p.*;
945 p.owned = false;
946 }
947 }
948
949 errdefer for (sections) |section| if (section) |s| if (s.owned) allocator.free(s.data);
950
951 // TODO: This function should take a ptr to GNU_EH_FRAME (which is .eh_frame_hdr) from the ELF headers
952 // and prefil sections[.eh_frame_hdr]
898953
899 var separate_debug_filename: ?[]const u8 = null;954 var separate_debug_filename: ?[]const u8 = null;
900 var separate_debug_crc: ?u32 = null;955 var separate_debug_crc: ?u32 = null;
...@@ -920,7 +975,7 @@ pub fn readElfDebugInfo(...@@ -920,7 +975,7 @@ pub fn readElfDebugInfo(
920 if (section_index == null) continue;975 if (section_index == null) continue;
921976
922 const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);977 const section_bytes = try chopSlice(mapped_mem, shdr.sh_offset, shdr.sh_size);
923 if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) {978 sections[section_index.?] = if ((shdr.sh_flags & elf.SHF_COMPRESSED) > 0) blk: {
924 var section_stream = io.fixedBufferStream(section_bytes);979 var section_stream = io.fixedBufferStream(section_bytes);
925 var section_reader = section_stream.reader();980 var section_reader = section_stream.reader();
926 const chdr = section_reader.readStruct(elf.Chdr) catch continue;981 const chdr = section_reader.readStruct(elf.Chdr) catch continue;
...@@ -937,11 +992,14 @@ pub fn readElfDebugInfo(...@@ -937,11 +992,14 @@ pub fn readElfDebugInfo(
937 const read = zlib_stream.reader().readAll(decompressed_section) catch continue;992 const read = zlib_stream.reader().readAll(decompressed_section) catch continue;
938 assert(read == decompressed_section.len);993 assert(read == decompressed_section.len);
939994
940 sections[section_index.?] = decompressed_section;995 break :blk .{
941 owned_sections[section_index.?] = decompressed_section;996 .data = decompressed_section,
942 } else {997 .owned = true,
943 sections[section_index.?] = section_bytes;998 };
944 }999 } else .{
1000 .data = section_bytes,
1001 .owned = false,
1002 };
945 }1003 }
9461004
947 const missing_debug_info =1005 const missing_debug_info =
...@@ -953,6 +1011,12 @@ pub fn readElfDebugInfo(...@@ -953,6 +1011,12 @@ pub fn readElfDebugInfo(
953 // Attempt to load debug info from an external file1011 // Attempt to load debug info from an external file
954 // See: https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html1012 // See: https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
955 if (missing_debug_info) {1013 if (missing_debug_info) {
1014
1015 // Only allow one level of debug info nesting
1016 if (parent_mapped_mem) |_| {
1017 return error.MissingDebugInfo;
1018 }
1019
956 const global_debug_directories = [_][]const u8{1020 const global_debug_directories = [_][]const u8{
957 "/usr/lib/debug",1021 "/usr/lib/debug",
958 };1022 };
...@@ -977,8 +1041,9 @@ pub fn readElfDebugInfo(...@@ -977,8 +1041,9 @@ pub fn readElfDebugInfo(
977 // TODO: joinBuf would be ideal (with a fs.MAX_PATH_BYTES buffer)1041 // TODO: joinBuf would be ideal (with a fs.MAX_PATH_BYTES buffer)
978 const path = try fs.path.join(allocator, &.{ global_directory, ".build-id", &id_prefix_buf, filename });1042 const path = try fs.path.join(allocator, &.{ global_directory, ".build-id", &id_prefix_buf, filename });
979 defer allocator.free(path);1043 defer allocator.free(path);
1044 // TODO: Remove
980 std.debug.print(" Loading external debug info from {s}\n", .{path});1045 std.debug.print(" Loading external debug info from {s}\n", .{path});
981 return readElfDebugInfo(allocator, path, null, separate_debug_crc) catch continue;1046 return readElfDebugInfo(allocator, path, null, separate_debug_crc, &sections, mapped_mem) catch continue;
982 }1047 }
983 }1048 }
9841049
...@@ -987,14 +1052,14 @@ pub fn readElfDebugInfo(...@@ -987,14 +1052,14 @@ pub fn readElfDebugInfo(
987 if (elf_filename != null and mem.eql(u8, elf_filename.?, separate_filename)) return error.MissingDebugInfo;1052 if (elf_filename != null and mem.eql(u8, elf_filename.?, separate_filename)) return error.MissingDebugInfo;
9881053
989 // <cwd>/<gnu_debuglink>1054 // <cwd>/<gnu_debuglink>
990 if (readElfDebugInfo(allocator, separate_filename, null, separate_debug_crc)) |debug_info| return debug_info else |_| {}1055 if (readElfDebugInfo(allocator, separate_filename, null, separate_debug_crc, &sections, mapped_mem)) |debug_info| return debug_info else |_| {}
9911056
992 // <cwd>/.debug/<gnu_debuglink>1057 // <cwd>/.debug/<gnu_debuglink>
993 {1058 {
994 const path = try fs.path.join(allocator, &.{ ".debug", separate_filename });1059 const path = try fs.path.join(allocator, &.{ ".debug", separate_filename });
995 defer allocator.free(path);1060 defer allocator.free(path);
9961061
997 if (readElfDebugInfo(allocator, path, null, separate_debug_crc)) |debug_info| return debug_info else |_| {}1062 if (readElfDebugInfo(allocator, path, null, separate_debug_crc, &sections, mapped_mem)) |debug_info| return debug_info else |_| {}
998 }1063 }
9991064
1000 var cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;1065 var cwd_buf: [fs.MAX_PATH_BYTES]u8 = undefined;
...@@ -1004,7 +1069,7 @@ pub fn readElfDebugInfo(...@@ -1004,7 +1069,7 @@ pub fn readElfDebugInfo(
1004 for (global_debug_directories) |global_directory| {1069 for (global_debug_directories) |global_directory| {
1005 const path = try fs.path.join(allocator, &.{ global_directory, cwd_path, separate_filename });1070 const path = try fs.path.join(allocator, &.{ global_directory, cwd_path, separate_filename });
1006 defer allocator.free(path);1071 defer allocator.free(path);
1007 if (readElfDebugInfo(allocator, path, null, separate_debug_crc)) |debug_info| return debug_info else |_| {}1072 if (readElfDebugInfo(allocator, path, null, separate_debug_crc, &sections, mapped_mem)) |debug_info| return debug_info else |_| {}
1008 }1073 }
1009 }1074 }
10101075
...@@ -1016,13 +1081,13 @@ pub fn readElfDebugInfo(...@@ -1016,13 +1081,13 @@ pub fn readElfDebugInfo(
1016 .sections = sections,1081 .sections = sections,
1017 };1082 };
10181083
1019 try DW.openDwarfDebugInfo(&di, allocator);1084 try DW.openDwarfDebugInfo(&di, allocator, parent_mapped_mem orelse mapped_mem);
10201085
1021 return ModuleDebugInfo{1086 return ModuleDebugInfo{
1022 .base_address = undefined,1087 .base_address = undefined,
1023 .dwarf = di,1088 .dwarf = di,
1024 .mapped_memory = mapped_mem,1089 .mapped_memory = parent_mapped_mem orelse mapped_mem,
1025 .owned_sections = owned_sections,1090 .external_mapped_memory = if (parent_mapped_mem != null) mapped_mem else null,
1026 };1091 };
1027 }1092 }
1028}1093}
...@@ -1426,7 +1491,8 @@ pub const DebugInfo = struct {...@@ -1426,7 +1491,8 @@ pub const DebugInfo = struct {
1426 for (phdrs) |*phdr| {1491 for (phdrs) |*phdr| {
1427 if (phdr.p_type != elf.PT_LOAD) continue;1492 if (phdr.p_type != elf.PT_LOAD) continue;
14281493
1429 const seg_start = info.dlpi_addr + phdr.p_vaddr;1494 // Overflowing addition is used to handle the case of VSDOs having a p_vaddr = 0xffffffffff700000
1495 const seg_start = info.dlpi_addr +% phdr.p_vaddr;
1430 const seg_end = seg_start + phdr.p_memsz;1496 const seg_end = seg_start + phdr.p_memsz;
1431 if (context.address >= seg_start and context.address < seg_end) {1497 if (context.address >= seg_start and context.address < seg_end) {
1432 // Android libc uses NULL instead of an empty string to mark the1498 // Android libc uses NULL instead of an empty string to mark the
...@@ -1437,6 +1503,8 @@ pub const DebugInfo = struct {...@@ -1437,6 +1503,8 @@ pub const DebugInfo = struct {
1437 }1503 }
1438 } else return;1504 } else return;
14391505
1506 // TODO: Look for the GNU_EH_FRAME section and pass it to readElfDebugInfo
1507
1440 for (info.dlpi_phdr[0..info.dlpi_phnum]) |phdr| {1508 for (info.dlpi_phdr[0..info.dlpi_phnum]) |phdr| {
1441 if (phdr.p_type != elf.PT_NOTE) continue;1509 if (phdr.p_type != elf.PT_NOTE) continue;
14421510
...@@ -1447,7 +1515,7 @@ pub const DebugInfo = struct {...@@ -1447,7 +1515,7 @@ pub const DebugInfo = struct {
1447 const note_type = mem.readIntSliceNative(u32, note_bytes[8..12]);1515 const note_type = mem.readIntSliceNative(u32, note_bytes[8..12]);
1448 if (note_type != elf.NT_GNU_BUILD_ID) continue;1516 if (note_type != elf.NT_GNU_BUILD_ID) continue;
1449 if (!mem.eql(u8, "GNU\x00", note_bytes[12..16])) continue;1517 if (!mem.eql(u8, "GNU\x00", note_bytes[12..16])) continue;
1450 context.build_id = note_bytes[16 .. 16 + desc_size];1518 context.build_id = note_bytes[16..][0..desc_size];
1451 }1519 }
14521520
1453 // Stop the iteration1521 // Stop the iteration
...@@ -1466,7 +1534,10 @@ pub const DebugInfo = struct {...@@ -1466,7 +1534,10 @@ pub const DebugInfo = struct {
1466 const obj_di = try self.allocator.create(ModuleDebugInfo);1534 const obj_di = try self.allocator.create(ModuleDebugInfo);
1467 errdefer self.allocator.destroy(obj_di);1535 errdefer self.allocator.destroy(obj_di);
14681536
1469 obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.name.len > 0) ctx.name else null, ctx.build_id, null);1537 var sections: DW.DwarfInfo.SectionArray = DW.DwarfInfo.null_section_array;
1538 // TODO: If GNU_EH_FRAME was found, set it in sections
1539
1540 obj_di.* = try readElfDebugInfo(self.allocator, if (ctx.name.len > 0) ctx.name else null, ctx.build_id, null, &sections, null);
1470 obj_di.base_address = ctx.base_address;1541 obj_di.base_address = ctx.base_address;
14711542
1472 try self.address_map.putNoClobber(ctx.base_address, obj_di);1543 try self.address_map.putNoClobber(ctx.base_address, obj_di);
...@@ -1491,6 +1562,7 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1491,6 +1562,7 @@ pub const ModuleDebugInfo = switch (native_os) {
1491 .macos, .ios, .watchos, .tvos => struct {1562 .macos, .ios, .watchos, .tvos => struct {
1492 base_address: usize,1563 base_address: usize,
1493 mapped_memory: []align(mem.page_size) const u8,1564 mapped_memory: []align(mem.page_size) const u8,
1565 external_mapped_memory: ?[]align(mem.page_size) const u8,
1494 symbols: []const MachoSymbol,1566 symbols: []const MachoSymbol,
1495 strings: [:0]const u8,1567 strings: [:0]const u8,
1496 ofiles: OFileTable,1568 ofiles: OFileTable,
...@@ -1511,6 +1583,7 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1511,6 +1583,7 @@ pub const ModuleDebugInfo = switch (native_os) {
1511 self.ofiles.deinit();1583 self.ofiles.deinit();
1512 allocator.free(self.symbols);1584 allocator.free(self.symbols);
1513 os.munmap(self.mapped_memory);1585 os.munmap(self.mapped_memory);
1586 if (self.external_mapped_memory) |m| os.munmap(m);
1514 }1587 }
15151588
1516 fn loadOFile(self: *@This(), allocator: mem.Allocator, o_file_path: []const u8) !OFileInfo {1589 fn loadOFile(self: *@This(), allocator: mem.Allocator, o_file_path: []const u8) !OFileInfo {
...@@ -1723,6 +1796,12 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1723,6 +1796,12 @@ pub const ModuleDebugInfo = switch (native_os) {
1723 unreachable;1796 unreachable;
1724 }1797 }
1725 }1798 }
1799
1800 pub fn getDwarfInfo(self: *@This()) ?*const DW.DwarfInfo {
1801 // TODO: Implement
1802 _ = self;
1803 return null;
1804 }
1726 },1805 },
1727 .uefi, .windows => struct {1806 .uefi, .windows => struct {
1728 base_address: usize,1807 base_address: usize,
...@@ -1803,19 +1882,24 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1803,19 +1882,24 @@ pub const ModuleDebugInfo = switch (native_os) {
1803 .line_info = opt_line_info,1882 .line_info = opt_line_info,
1804 };1883 };
1805 }1884 }
1885
1886 pub fn getDwarfInfo(self: *@This()) ?*const DW.DwarfInfo {
1887 return switch (self.debug_data) {
1888 .dwarf => |*dwarf| dwarf,
1889 else => null,
1890 };
1891 }
1806 },1892 },
1807 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris => struct {1893 .linux, .netbsd, .freebsd, .dragonfly, .openbsd, .haiku, .solaris => struct {
1808 base_address: usize,1894 base_address: usize,
1809 dwarf: DW.DwarfInfo,1895 dwarf: DW.DwarfInfo,
1810 mapped_memory: []align(mem.page_size) const u8,1896 mapped_memory: []align(mem.page_size) const u8,
1811 owned_sections: [num_sections][]const u8 = [_][]const u8{&.{}} ** num_sections,1897 external_mapped_memory: ?[]align(mem.page_size) const u8,
1812
1813 const num_sections = 14;
18141898
1815 fn deinit(self: *@This(), allocator: mem.Allocator) void {1899 fn deinit(self: *@This(), allocator: mem.Allocator) void {
1816 self.dwarf.deinit(allocator);1900 self.dwarf.deinit(allocator);
1817 for (self.owned_sections) |section| allocator.free(section);
1818 os.munmap(self.mapped_memory);1901 os.munmap(self.mapped_memory);
1902 if (self.external_mapped_memory) |m| os.munmap(m);
1819 }1903 }
18201904
1821 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {1905 pub fn getSymbolAtAddress(self: *@This(), allocator: mem.Allocator, address: usize) !SymbolInfo {
...@@ -1823,6 +1907,10 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1823,6 +1907,10 @@ pub const ModuleDebugInfo = switch (native_os) {
1823 const relocated_address = address - self.base_address;1907 const relocated_address = address - self.base_address;
1824 return getSymbolFromDwarf(allocator, relocated_address, &self.dwarf);1908 return getSymbolFromDwarf(allocator, relocated_address, &self.dwarf);
1825 }1909 }
1910
1911 pub fn getDwarfInfo(self: *@This()) ?*const DW.DwarfInfo {
1912 return &self.dwarf;
1913 }
1826 },1914 },
1827 .wasi => struct {1915 .wasi => struct {
1828 fn deinit(self: *@This(), allocator: mem.Allocator) void {1916 fn deinit(self: *@This(), allocator: mem.Allocator) void {
...@@ -1836,6 +1924,11 @@ pub const ModuleDebugInfo = switch (native_os) {...@@ -1836,6 +1924,11 @@ pub const ModuleDebugInfo = switch (native_os) {
1836 _ = address;1924 _ = address;
1837 return SymbolInfo{};1925 return SymbolInfo{};
1838 }1926 }
1927
1928 pub fn getDwarfInfo(self: *@This()) ?*const DW.DwarfInfo {
1929 _ = self;
1930 return null;
1931 }
1839 },1932 },
1840 else => DW.DwarfInfo,1933 else => DW.DwarfInfo,
1841};1934};
...@@ -1992,55 +2085,69 @@ fn dumpSegfaultInfoPosix(sig: i32, addr: usize, ctx_ptr: ?*const anyopaque) void...@@ -1992,55 +2085,69 @@ fn dumpSegfaultInfoPosix(sig: i32, addr: usize, ctx_ptr: ?*const anyopaque) void
1992 } catch os.abort();2085 } catch os.abort();
19932086
1994 switch (native_arch) {2087 switch (native_arch) {
1995 .x86 => {2088 .x86,
1996 const ctx: *const os.ucontext_t = @ptrCast(@alignCast(ctx_ptr));2089 .x86_64,
1997 const ip = @as(usize, @intCast(ctx.mcontext.gregs[os.REG.EIP]));2090 .arm,
1998 const bp = @as(usize, @intCast(ctx.mcontext.gregs[os.REG.EBP]));2091 .aarch64,
1999 dumpStackTraceFromBase(bp, ip);2092 => {
2000 },2093 const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
2001 .x86_64 => {2094 dumpStackTraceFromBase(ctx);
2002 const ctx: *const os.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
2003 const ip = switch (native_os) {
2004 .linux, .netbsd, .solaris => @as(usize, @intCast(ctx.mcontext.gregs[os.REG.RIP])),
2005 .freebsd => @as(usize, @intCast(ctx.mcontext.rip)),
2006 .openbsd => @as(usize, @intCast(ctx.sc_rip)),
2007 .macos => @as(usize, @intCast(ctx.mcontext.ss.rip)),
2008 else => unreachable,
2009 };
2010 const bp = switch (native_os) {
2011 .linux, .netbsd, .solaris => @as(usize, @intCast(ctx.mcontext.gregs[os.REG.RBP])),
2012 .openbsd => @as(usize, @intCast(ctx.sc_rbp)),
2013 .freebsd => @as(usize, @intCast(ctx.mcontext.rbp)),
2014 .macos => @as(usize, @intCast(ctx.mcontext.ss.rbp)),
2015 else => unreachable,
2016 };
2017 dumpStackTraceFromBase(bp, ip);
2018 },
2019 .arm => {
2020 const ctx: *const os.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
2021 const ip = @as(usize, @intCast(ctx.mcontext.arm_pc));
2022 const bp = @as(usize, @intCast(ctx.mcontext.arm_fp));
2023 dumpStackTraceFromBase(bp, ip);
2024 },
2025 .aarch64 => {
2026 const ctx: *const os.ucontext_t = @ptrCast(@alignCast(ctx_ptr));
2027 const ip = switch (native_os) {
2028 .macos => @as(usize, @intCast(ctx.mcontext.ss.pc)),
2029 .netbsd => @as(usize, @intCast(ctx.mcontext.gregs[os.REG.PC])),
2030 .freebsd => @as(usize, @intCast(ctx.mcontext.gpregs.elr)),
2031 else => @as(usize, @intCast(ctx.mcontext.pc)),
2032 };
2033 // x29 is the ABI-designated frame pointer
2034 const bp = switch (native_os) {
2035 .macos => @as(usize, @intCast(ctx.mcontext.ss.fp)),
2036 .netbsd => @as(usize, @intCast(ctx.mcontext.gregs[os.REG.FP])),
2037 .freebsd => @as(usize, @intCast(ctx.mcontext.gpregs.x[os.REG.FP])),
2038 else => @as(usize, @intCast(ctx.mcontext.regs[29])),
2039 };
2040 dumpStackTraceFromBase(bp, ip);
2041 },2095 },
2042 else => {},2096 else => {},
2043 }2097 }
2098
2099 // TODO: Move this logic to dwarf.abi.regBytes
2100
2101 // switch (native_arch) {
2102 // .x86 => {
2103 // const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
2104 // const ip = @intCast(usize, ctx.mcontext.gregs[os.REG.EIP]) ;
2105 // const bp = @intCast(usize, ctx.mcontext.gregs[os.REG.EBP]);
2106 // dumpStackTraceFromBase(bp, ip);
2107 // },
2108 // .x86_64 => {
2109 // const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
2110 // const ip = switch (native_os) {
2111 // .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]),
2112 // .freebsd => @intCast(usize, ctx.mcontext.rip),
2113 // .openbsd => @intCast(usize, ctx.sc_rip),
2114 // .macos => @intCast(usize, ctx.mcontext.ss.rip),
2115 // else => unreachable,
2116 // };
2117 // const bp = switch (native_os) {
2118 // .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]),
2119 // .openbsd => @intCast(usize, ctx.sc_rbp),
2120 // .freebsd => @intCast(usize, ctx.mcontext.rbp),
2121 // .macos => @intCast(usize, ctx.mcontext.ss.rbp),
2122 // else => unreachable,
2123 // };
2124 // dumpStackTraceFromBase(bp, ip);
2125 // },
2126 // .arm => {
2127 // const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
2128 // const ip = @intCast(usize, ctx.mcontext.arm_pc);
2129 // const bp = @intCast(usize, ctx.mcontext.arm_fp);
2130 // dumpStackTraceFromBase(bp, ip);
2131 // },
2132 // .aarch64 => {
2133 // const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
2134 // const ip = switch (native_os) {
2135 // .macos => @intCast(usize, ctx.mcontext.ss.pc),
2136 // .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.PC]),
2137 // .freebsd => @intCast(usize, ctx.mcontext.gpregs.elr),
2138 // else => @intCast(usize, ctx.mcontext.pc),
2139 // };
2140 // // x29 is the ABI-designated frame pointer
2141 // const bp = switch (native_os) {
2142 // .macos => @intCast(usize, ctx.mcontext.ss.fp),
2143 // .netbsd => @intCast(usize, ctx.mcontext.gregs[os.REG.FP]),
2144 // .freebsd => @intCast(usize, ctx.mcontext.gpregs.x[os.REG.FP]),
2145 // else => @intCast(usize, ctx.mcontext.regs[29]),
2146 // };
2147 // dumpStackTraceFromBase(bp, ip);
2148 // },
2149 // else => {},
2150 // }
2044}2151}
20452152
2046fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long {2153fn handleSegfaultWindows(info: *windows.EXCEPTION_POINTERS) callconv(windows.WINAPI) c_long {
...@@ -2105,7 +2212,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[...@@ -2105,7 +2212,7 @@ fn dumpSegfaultInfoWindows(info: *windows.EXCEPTION_POINTERS, msg: u8, label: ?[
2105 else => unreachable,2212 else => unreachable,
2106 } catch os.abort();2213 } catch os.abort();
21072214
2108 dumpStackTraceFromBase(regs.bp, regs.ip);2215 dumpStackTraceFromBase(regs);
2109}2216}
21102217
2111pub fn dumpStackPointerAddr(prefix: []const u8) void {2218pub fn dumpStackPointerAddr(prefix: []const u8) void {
lib/std/dwarf.zig+130-17
...@@ -3,6 +3,7 @@ const std = @import("std.zig");...@@ -3,6 +3,7 @@ const std = @import("std.zig");
3const debug = std.debug;3const debug = std.debug;
4const fs = std.fs;4const fs = std.fs;
5const io = std.io;5const io = std.io;
6const os = std.os;
6const mem = std.mem;7const mem = std.mem;
7const math = std.math;8const math = std.math;
8const leb = @import("leb128.zig");9const leb = @import("leb128.zig");
...@@ -664,10 +665,17 @@ pub const DwarfSection = enum {...@@ -664,10 +665,17 @@ pub const DwarfSection = enum {
664};665};
665666
666pub const DwarfInfo = struct {667pub const DwarfInfo = struct {
667 endian: std.builtin.Endian,668 pub const Section = struct {
669 data: []const u8,
670 owned: bool,
671 };
672
673 const num_sections = std.enums.directEnumArrayLen(DwarfSection, 0);
674 pub const SectionArray = [num_sections]?Section;
675 pub const null_section_array = [_]?Section{null} ** num_sections;
668676
669 // No section memory is owned by the DwarfInfo677 endian: std.builtin.Endian,
670 sections: [std.enums.directEnumArrayLen(DwarfSection, 0)]?[]const u8,678 sections: SectionArray,
671679
672 // Filled later by the initializer680 // Filled later by the initializer
673 abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},681 abbrev_table_list: std.ArrayListUnmanaged(AbbrevTableHeader) = .{},
...@@ -679,10 +687,13 @@ pub const DwarfInfo = struct {...@@ -679,10 +687,13 @@ pub const DwarfInfo = struct {
679 fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{},687 fde_list: std.ArrayListUnmanaged(FrameDescriptionEntry) = .{},
680688
681 pub fn section(di: DwarfInfo, dwarf_section: DwarfSection) ?[]const u8 {689 pub fn section(di: DwarfInfo, dwarf_section: DwarfSection) ?[]const u8 {
682 return di.sections[@enumToInt(dwarf_section)];690 return if (di.sections[@enumToInt(dwarf_section)]) |s| s.data else null;
683 }691 }
684692
685 pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void {693 pub fn deinit(di: *DwarfInfo, allocator: mem.Allocator) void {
694 for (di.sections) |s| {
695 if (s.owned) allocator.free(s.data);
696 }
686 for (di.abbrev_table_list.items) |*abbrev| {697 for (di.abbrev_table_list.items) |*abbrev| {
687 abbrev.deinit();698 abbrev.deinit();
688 }699 }
...@@ -696,6 +707,8 @@ pub const DwarfInfo = struct {...@@ -696,6 +707,8 @@ pub const DwarfInfo = struct {
696 func.deinit(allocator);707 func.deinit(allocator);
697 }708 }
698 di.func_list.deinit(allocator);709 di.func_list.deinit(allocator);
710 di.cie_map.deinit(allocator);
711 di.fde_list.deinit(allocator);
699 }712 }
700713
701 pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {714 pub fn getSymbolName(di: *DwarfInfo, address: u64) ?[]const u8 {
...@@ -1443,7 +1456,6 @@ pub const DwarfInfo = struct {...@@ -1443,7 +1456,6 @@ pub const DwarfInfo = struct {
1443 return getStringGeneric(di.section(.debug_line_str), offset);1456 return getStringGeneric(di.section(.debug_line_str), offset);
1444 }1457 }
14451458
1446
1447 fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 {1459 fn readDebugAddr(di: DwarfInfo, compile_unit: CompileUnit, index: u64) !u64 {
1448 const debug_addr = di.section(.debug_addr) orelse return badDwarf();1460 const debug_addr = di.section(.debug_addr) orelse return badDwarf();
14491461
...@@ -1470,12 +1482,13 @@ pub const DwarfInfo = struct {...@@ -1470,12 +1482,13 @@ pub const DwarfInfo = struct {
1470 };1482 };
1471 }1483 }
14721484
1473 pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {1485 pub fn scanAllUnwindInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void {
1474 var has_eh_frame_hdr = false;1486 var has_eh_frame_hdr = false;
1475 if (di.section(.eh_frame)) |eh_frame_hdr| {1487 if (di.section(.eh_frame_hdr)) |eh_frame_hdr| {
1476 has_eh_frame_hdr = true;1488 has_eh_frame_hdr = true;
14771489
1478 // TODO: Parse this section1490 // TODO: Parse this section to get the lookup table, and skip loading the entire section
1491
1479 _ = eh_frame_hdr;1492 _ = eh_frame_hdr;
1480 }1493 }
14811494
...@@ -1494,16 +1507,14 @@ pub const DwarfInfo = struct {...@@ -1494,16 +1507,14 @@ pub const DwarfInfo = struct {
1494 }1507 }
14951508
1496 const id_len = @as(u8, if (is_64) 8 else 4);1509 const id_len = @as(u8, if (is_64) 8 else 4);
1510 const id = if (is_64) try reader.readInt(u64, di.endian) else try reader.readInt(u32, di.endian);
1497 const entry_bytes = eh_frame[stream.pos..][0 .. length - id_len];1511 const entry_bytes = eh_frame[stream.pos..][0 .. length - id_len];
1498 const id = try reader.readInt(u32, di.endian);
1499
1500 // TODO: Get section_offset here (pass in from headers)
15011512
1502 if (id == 0) {1513 if (id == 0) {
1503 const cie = try CommonInformationEntry.parse(1514 const cie = try CommonInformationEntry.parse(
1504 entry_bytes,1515 entry_bytes,
1505 @ptrToInt(eh_frame.ptr),1516 @ptrToInt(eh_frame.ptr),
1506 0,1517 @ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr),
1507 true,1518 true,
1508 length_offset,1519 length_offset,
1509 @sizeOf(usize),1520 @sizeOf(usize),
...@@ -1511,12 +1522,12 @@ pub const DwarfInfo = struct {...@@ -1511,12 +1522,12 @@ pub const DwarfInfo = struct {
1511 );1522 );
1512 try di.cie_map.put(allocator, length_offset, cie);1523 try di.cie_map.put(allocator, length_offset, cie);
1513 } else {1524 } else {
1514 const cie_offset = stream.pos - 4 - id;1525 const cie_offset = stream.pos - id_len - id;
1515 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();1526 const cie = di.cie_map.get(cie_offset) orelse return badDwarf();
1516 const fde = try FrameDescriptionEntry.parse(1527 const fde = try FrameDescriptionEntry.parse(
1517 entry_bytes,1528 entry_bytes,
1518 @ptrToInt(eh_frame.ptr),1529 @ptrToInt(eh_frame.ptr),
1519 0,1530 @ptrToInt(eh_frame.ptr) - @ptrToInt(binary_mem.ptr),
1520 true,1531 true,
1521 cie,1532 cie,
1522 @sizeOf(usize),1533 @sizeOf(usize),
...@@ -1524,6 +1535,8 @@ pub const DwarfInfo = struct {...@@ -1524,6 +1535,8 @@ pub const DwarfInfo = struct {
1524 );1535 );
1525 try di.fde_list.append(allocator, fde);1536 try di.fde_list.append(allocator, fde);
1526 }1537 }
1538
1539 stream.pos += entry_bytes.len;
1527 }1540 }
15281541
1529 // TODO: Avoiding sorting if has_eh_frame_hdr exists1542 // TODO: Avoiding sorting if has_eh_frame_hdr exists
...@@ -1536,16 +1549,116 @@ pub const DwarfInfo = struct {...@@ -1536,16 +1549,116 @@ pub const DwarfInfo = struct {
1536 }1549 }
1537 }1550 }
15381551
1552 pub fn unwindFrame(di: *const DwarfInfo, allocator: mem.Allocator, context: *UnwindContext, module_base_address: usize) !void {
1553 if (context.pc == 0) return;
1554
1555 // TODO: Handle signal frame (ie. use_prev_instr in libunwind)
1556 // TOOD: Use eh_frame_hdr to accelerate the search if available
1557 //const eh_frame_hdr = di.section(.eh_frame_hdr) orelse return error.MissingDebugInfo;
1558
1559 // Find the FDE
1560 const unmapped_pc = context.pc - module_base_address;
1561 const index = std.sort.binarySearch(FrameDescriptionEntry, unmapped_pc, di.fde_list.items, {}, struct {
1562 pub fn compareFn(_: void, pc: usize, mid_item: FrameDescriptionEntry) std.math.Order {
1563 if (pc < mid_item.pc_begin) {
1564 return .lt;
1565 } else {
1566 const range_end = mid_item.pc_begin + mid_item.pc_range;
1567 if (pc < range_end) {
1568 return .eq;
1569 }
1570
1571 return .gt;
1572 }
1573 }
1574 }.compareFn);
1575
1576 const fde = if (index) |i| &di.fde_list.items[i] else return error.MissingFDE;
1577 const cie = di.cie_map.getPtr(fde.cie_length_offset) orelse return error.MissingCIE;
1578
1579 // const prev_cfa = context.cfa;
1580 // const prev_pc = context.pc;
1581
1582 // TODO: Cache this on self so we can re-use the allocations?
1583 var vm = call_frame.VirtualMachine{};
1584 defer vm.deinit(allocator);
1585
1586 const row = try vm.runToNative(allocator, unmapped_pc, cie.*, fde.*);
1587 context.cfa = switch (row.cfa.rule) {
1588 .val_offset => |offset| blk: {
1589 const register = row.cfa.register orelse return error.InvalidCFARule;
1590 const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register));
1591
1592 // TODO: Check isValidMemory?
1593 break :blk try call_frame.applyOffset(value, offset);
1594 },
1595 .expression => |expression| {
1596
1597 // TODO: Evaluate expression
1598 _ = expression;
1599 return error.UnimplementedTODO;
1600
1601 },
1602 else => return error.InvalidCFARule,
1603 };
1604
1605 // Update the context with the unwound values
1606 // TODO: Need old cfa and pc?
1607
1608 var next_ucontext = context.ucontext;
1609
1610 var has_next_ip = false;
1611 for (vm.rowColumns(row)) |column| {
1612 if (column.register) |register| {
1613 const dest = try abi.regBytes(&next_ucontext, register);
1614 if (register == cie.return_address_register) {
1615 has_next_ip = column.rule != .undefined;
1616 }
1617
1618 try column.resolveValue(context.*, dest);
1619 }
1620 }
1621
1622 context.ucontext = next_ucontext;
1623
1624 if (has_next_ip) {
1625 context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, @enumToInt(abi.Register.ip)));
1626 } else {
1627 context.pc = 0;
1628 }
1629
1630 mem.writeIntSliceNative(usize, try abi.regBytes(&context.ucontext, @enumToInt(abi.Register.sp)), context.cfa.?);
1631 }
1632};
1633
1634pub const UnwindContext = struct {
1635 cfa: ?usize,
1636 pc: usize,
1637 ucontext: os.ucontext_t,
1638
1639 pub fn init(ucontext: *const os.ucontext_t) !UnwindContext {
1640 const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, @enumToInt(abi.Register.ip)));
1641 return .{
1642 .cfa = null,
1643 .pc = pc,
1644 .ucontext = ucontext.*,
1645 };
1646 }
1647
1648 pub fn getFp(self: *const UnwindContext) !usize {
1649 return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, @enumToInt(abi.Register.fp)));
1650 }
1539};1651};
15401652
1541/// Initialize DWARF info. The caller has the responsibility to initialize most1653/// Initialize DWARF info. The caller has the responsibility to initialize most
1542/// the DwarfInfo fields before calling.1654/// the DwarfInfo fields before calling. `binary_mem` is the raw bytes of the
1543pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator) !void {1655/// main binary file (not the secondary debug info file).
1656pub fn openDwarfDebugInfo(di: *DwarfInfo, allocator: mem.Allocator, binary_mem: []const u8) !void {
1544 try di.scanAllFunctions(allocator);1657 try di.scanAllFunctions(allocator);
1545 try di.scanAllCompileUnits(allocator);1658 try di.scanAllCompileUnits(allocator);
15461659
1547 // Unwind info is not required1660 // Unwind info is not required
1548 di.scanAllUnwindInfo(allocator) catch {};1661 di.scanAllUnwindInfo(allocator, binary_mem) catch {};
1549}1662}
15501663
1551/// This function is to make it handy to comment out the return and make it1664/// This function is to make it handy to comment out the return and make it
lib/std/dwarf/abi.zig+106
...@@ -1,4 +1,110 @@...@@ -1,4 +1,110 @@
1const builtin = @import("builtin");
1const std = @import("../std.zig");2const std = @import("../std.zig");
3const os = std.os;
4const mem = std.mem;
5
6/// Maps register names to their DWARF register number.
7/// `bp`, `ip`, and `sp` are provided as aliases.
8pub const Register = switch (builtin.cpu.arch) {
9 .x86 => {
10
11 //pub const ip = Register.eip;
12 //pub const sp = Register.
13 },
14 .x86_64 => enum(u8) {
15 rax,
16 rdx,
17 rcx,
18 rbx,
19 rsi,
20 rdi,
21 rbp,
22 rsp,
23 r8,
24 r9,
25 r10,
26 r11,
27 r12,
28 r13,
29 r14,
30 r15,
31 rip,
32 xmm0,
33 xmm1,
34 xmm2,
35 xmm3,
36 xmm4,
37 xmm5,
38 xmm6,
39 xmm7,
40 xmm8,
41 xmm9,
42 xmm10,
43 xmm11,
44 xmm12,
45 xmm13,
46 xmm14,
47 xmm15,
48
49 pub const fp = Register.rbp;
50 pub const ip = Register.rip;
51 pub const sp = Register.rsp;
52 },
53 else => enum {},
54};
55
56fn RegBytesReturnType(comptime ContextPtrType: type) type {
57 const info = @typeInfo(ContextPtrType);
58 if (info != .Pointer or info.Pointer.child != os.ucontext_t) {
59 @compileError("Expected a pointer to ucontext_t, got " ++ @typeName(@TypeOf(ContextPtrType)));
60 }
61
62 return if (info.Pointer.is_const) return []const u8 else []u8;
63}
64
65/// Returns a slice containing the backing storage for `reg_number`
66pub fn regBytes(ucontext_ptr: anytype, reg_number: u8) !RegBytesReturnType(@TypeOf(ucontext_ptr)) {
67 var m = &ucontext_ptr.mcontext;
68
69 return switch (builtin.cpu.arch) {
70 .x86_64 => switch (builtin.os.tag) {
71 .linux, .netbsd, .solaris => switch (reg_number) {
72 0 => mem.asBytes(&m.gregs[os.REG.RAX]),
73 1 => mem.asBytes(&m.gregs[os.REG.RDX]),
74 2 => mem.asBytes(&m.gregs[os.REG.RCX]),
75 3 => mem.asBytes(&m.gregs[os.REG.RBX]),
76 4 => mem.asBytes(&m.gregs[os.REG.RSI]),
77 5 => mem.asBytes(&m.gregs[os.REG.RDI]),
78 6 => mem.asBytes(&m.gregs[os.REG.RBP]),
79 7 => mem.asBytes(&m.gregs[os.REG.RSP]),
80 8 => mem.asBytes(&m.gregs[os.REG.R8]),
81 9 => mem.asBytes(&m.gregs[os.REG.R9]),
82 10 => mem.asBytes(&m.gregs[os.REG.R10]),
83 11 => mem.asBytes(&m.gregs[os.REG.R11]),
84 12 => mem.asBytes(&m.gregs[os.REG.R12]),
85 13 => mem.asBytes(&m.gregs[os.REG.R13]),
86 14 => mem.asBytes(&m.gregs[os.REG.R14]),
87 15 => mem.asBytes(&m.gregs[os.REG.R15]),
88 16 => mem.asBytes(&m.gregs[os.REG.RIP]),
89 17...32 => |i| mem.asBytes(&m.fpregs.xmm[i - 17]),
90 else => error.InvalidRegister,
91 },
92 //.freebsd => @intCast(usize, ctx.mcontext.rip),
93 //.openbsd => @intCast(usize, ctx.sc_rip),
94 //.macos => @intCast(usize, ctx.mcontext.ss.rip),
95 else => error.UnimplementedOs,
96 },
97 else => error.UnimplementedArch,
98 };
99}
100
101/// Returns the ABI-defined default value this register has in the unwinding table
102/// before running any of the CIE instructions.
103pub fn getRegDefaultValue(reg_number: u8, out: []u8) void {
104 // TODO: Implement any ABI-specific rules for the default value for registers
105 _ = reg_number;
106 @memset(out, undefined);
107}
2108
3fn writeUnknownReg(writer: anytype, reg_number: u8) !void {109fn writeUnknownReg(writer: anytype, reg_number: u8) !void {
4 try writer.print("reg{}", .{reg_number});110 try writer.print("reg{}", .{reg_number});
lib/std/dwarf/call_frame.zig+90-25
...@@ -1,5 +1,6 @@...@@ -1,5 +1,6 @@
1const builtin = @import("builtin");1const builtin = @import("builtin");
2const std = @import("../std.zig");2const std = @import("../std.zig");
3const mem = std.mem;
3const debug = std.debug;4const debug = std.debug;
4const leb = @import("../leb128.zig");5const leb = @import("../leb128.zig");
5const abi = @import("abi.zig");6const abi = @import("abi.zig");
...@@ -216,10 +217,19 @@ pub const Instruction = union(Opcode) {...@@ -216,10 +217,19 @@ pub const Instruction = union(Opcode) {
216 }217 }
217};218};
218219
220/// Since register rules are applied (usually) during a panic,
221/// checked addition / subtraction is used so that we can return
222/// an error and fall back to FP-based unwinding.
223pub fn applyOffset(base: usize, offset: i64) !usize {
224 return if (offset >= 0)
225 try std.math.add(usize, base, @intCast(usize, offset))
226 else
227 try std.math.sub(usize, base, @intCast(usize, -offset));
228}
229
219/// This is a virtual machine that runs DWARF call frame instructions.230/// This is a virtual machine that runs DWARF call frame instructions.
220/// See section 6.4.1 of the DWARF5 specification.
221pub const VirtualMachine = struct {231pub const VirtualMachine = struct {
222232 /// See section 6.4.1 of the DWARF5 specification for details on each
223 const RegisterRule = union(enum) {233 const RegisterRule = union(enum) {
224 // The spec says that the default rule for each column is the undefined rule.234 // The spec says that the default rule for each column is the undefined rule.
225 // However, it also allows ABI / compiler authors to specify alternate defaults, so235 // However, it also allows ABI / compiler authors to specify alternate defaults, so
...@@ -254,20 +264,63 @@ pub const VirtualMachine = struct {...@@ -254,20 +264,63 @@ pub const VirtualMachine = struct {
254 offset: u64 = 0,264 offset: u64 = 0,
255265
256 /// Special-case column that defines the CFA (Canonical Frame Address) rule.266 /// Special-case column that defines the CFA (Canonical Frame Address) rule.
257 /// The register field of this column defines the register that CFA is derived267 /// The register field of this column defines the register that CFA is derived from.
258 /// from, while other columns define register rules in terms of the CFA.
259 cfa: Column = .{},268 cfa: Column = .{},
269
270 /// The register fields in these columns define the register the rule applies to.
260 columns: ColumnRange = .{},271 columns: ColumnRange = .{},
261272
262 /// Indicates that the next write to any column in this row needs to copy273 /// Indicates that the next write to any column in this row needs to copy
263 /// the backing column storage first.274 /// the backing column storage first, as it may be referenced by previous rows.
264 copy_on_write: bool = false,275 copy_on_write: bool = false,
265 };276 };
266277
267 pub const Column = struct {278 pub const Column = struct {
268 /// Register can only null in the case of the CFA column
269 register: ?u8 = null,279 register: ?u8 = null,
270 rule: RegisterRule = .{ .default = {} },280 rule: RegisterRule = .{ .default = {} },
281
282 /// Resolves the register rule and places the result into `out` (see dwarf.abi.regBytes)
283 pub fn resolveValue(self: Column, context: dwarf.UnwindContext, out: []u8) !void {
284 switch (self.rule) {
285 .default => {
286 const register = self.register orelse return error.InvalidRegister;
287 abi.getRegDefaultValue(register, out);
288 },
289 .undefined => {
290 @memset(out, undefined);
291 },
292 .same_value => {},
293 .offset => |offset| {
294 if (context.cfa) |cfa| {
295 const ptr = @intToPtr(*const usize, try applyOffset(cfa, offset));
296
297 // TODO: context.isValidMemory(ptr)
298 mem.writeIntSliceNative(usize, out, ptr.*);
299 } else return error.InvalidCFA;
300 },
301 .val_offset => |offset| {
302 if (context.cfa) |cfa| {
303 mem.writeIntSliceNative(usize, out, try applyOffset(cfa, offset));
304 } else return error.InvalidCFA;
305 },
306 .register => |register| {
307 const src = try abi.regBytes(&context.ucontext, register);
308 if (src.len != out.len) return error.RegisterTypeMismatch;
309 @memcpy(out, try abi.regBytes(&context.ucontext, register));
310 },
311 .expression => |expression| {
312 // TODO
313 _ = expression;
314 unreachable;
315 },
316 .val_expression => |expression| {
317 // TODO
318 _ = expression;
319 unreachable;
320 },
321 .architectural => return error.UnimplementedRule,
322 }
323 }
271 };324 };
272325
273 const ColumnRange = struct {326 const ColumnRange = struct {
...@@ -294,7 +347,7 @@ pub const VirtualMachine = struct {...@@ -294,7 +347,7 @@ pub const VirtualMachine = struct {
294 return self.columns.items[row.columns.start..][0..row.columns.len];347 return self.columns.items[row.columns.start..][0..row.columns.len];
295 }348 }
296349
297 /// Either retrieves or adds a column for `register` (non-CFA) in the current row350 /// Either retrieves or adds a column for `register` (non-CFA) in the current row.
298 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {351 fn getOrAddColumn(self: *VirtualMachine, allocator: std.mem.Allocator, register: u8) !*Column {
299 for (self.rowColumns(self.current_row)) |*c| {352 for (self.rowColumns(self.current_row)) |*c| {
300 if (c.register == register) return c;353 if (c.register == register) return c;
...@@ -315,7 +368,7 @@ pub const VirtualMachine = struct {...@@ -315,7 +368,7 @@ pub const VirtualMachine = struct {
315368
316 /// Runs the CIE instructions, then the FDE instructions. Execution halts369 /// Runs the CIE instructions, then the FDE instructions. Execution halts
317 /// once the row that corresponds to `pc` is known, and it is returned.370 /// once the row that corresponds to `pc` is known, and it is returned.
318 pub fn unwindTo(371 pub fn runTo(
319 self: *VirtualMachine,372 self: *VirtualMachine,
320 allocator: std.mem.Allocator,373 allocator: std.mem.Allocator,
321 pc: u64,374 pc: u64,
...@@ -328,12 +381,15 @@ pub const VirtualMachine = struct {...@@ -328,12 +381,15 @@ pub const VirtualMachine = struct {
328 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;381 if (pc < fde.pc_begin or pc >= fde.pc_begin + fde.pc_range) return error.AddressOutOfRange;
329382
330 var prev_row: Row = self.current_row;383 var prev_row: Row = self.current_row;
331 const streams = .{384
332 std.io.fixedBufferStream(cie.initial_instructions),385 var cie_stream = std.io.fixedBufferStream(cie.initial_instructions);
333 std.io.fixedBufferStream(fde.instructions),386 var fde_stream = std.io.fixedBufferStream(fde.instructions);
387 var streams = [_]*std.io.FixedBufferStream([]const u8){
388 &cie_stream,
389 &fde_stream,
334 };390 };
335391
336 outer: for (streams, 0..) |*stream, i| {392 outer: for (&streams, 0..) |stream, i| {
337 while (stream.pos < stream.buffer.len) {393 while (stream.pos < stream.buffer.len) {
338 const instruction = try dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);394 const instruction = try dwarf.call_frame.Instruction.read(stream, addr_size_bytes, endian);
339 prev_row = try self.step(allocator, cie, i == 0, instruction);395 prev_row = try self.step(allocator, cie, i == 0, instruction);
...@@ -346,14 +402,14 @@ pub const VirtualMachine = struct {...@@ -346,14 +402,14 @@ pub const VirtualMachine = struct {
346 return prev_row;402 return prev_row;
347 }403 }
348404
349 pub fn unwindToNative(405 pub fn runToNative(
350 self: *VirtualMachine,406 self: *VirtualMachine,
351 allocator: std.mem.Allocator,407 allocator: std.mem.Allocator,
352 pc: u64,408 pc: u64,
353 cie: dwarf.CommonInformationEntry,409 cie: dwarf.CommonInformationEntry,
354 fde: dwarf.FrameDescriptionEntry,410 fde: dwarf.FrameDescriptionEntry,
355 ) void {411 ) !Row {
356 self.stepTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian());412 return self.runTo(allocator, pc, cie, fde, @sizeOf(usize), builtin.target.cpu.arch.endian());
357 }413 }
358414
359 fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void {415 fn resolveCopyOnWrite(self: *VirtualMachine, allocator: std.mem.Allocator) !void {
...@@ -451,30 +507,30 @@ pub const VirtualMachine = struct {...@@ -451,30 +507,30 @@ pub const VirtualMachine = struct {
451 try self.resolveCopyOnWrite(allocator);507 try self.resolveCopyOnWrite(allocator);
452 self.current_row.cfa = .{508 self.current_row.cfa = .{
453 .register = i.operands.register,509 .register = i.operands.register,
454 .rule = .{ .offset = @intCast(i64, i.operands.offset) },510 .rule = .{ .val_offset = @intCast(i64, i.operands.offset) },
455 };511 };
456 },512 },
457 .def_cfa_sf => |i| {513 .def_cfa_sf => |i| {
458 try self.resolveCopyOnWrite(allocator);514 try self.resolveCopyOnWrite(allocator);
459 self.current_row.cfa = .{515 self.current_row.cfa = .{
460 .register = i.operands.register,516 .register = i.operands.register,
461 .rule = .{ .offset = i.operands.offset * cie.data_alignment_factor },517 .rule = .{ .val_offset = i.operands.offset * cie.data_alignment_factor },
462 };518 };
463 },519 },
464 .def_cfa_register => |i| {520 .def_cfa_register => |i| {
465 try self.resolveCopyOnWrite(allocator);521 try self.resolveCopyOnWrite(allocator);
466 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .offset) return error.InvalidOperation;522 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
467 self.current_row.cfa.register = i.operands.register;523 self.current_row.cfa.register = i.operands.register;
468 },524 },
469 .def_cfa_offset => |i| {525 .def_cfa_offset => |i| {
470 try self.resolveCopyOnWrite(allocator);526 try self.resolveCopyOnWrite(allocator);
471 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .offset) return error.InvalidOperation;527 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
472 self.current_row.cfa.rule = .{ .offset = @intCast(i64, i.operands.offset) };528 self.current_row.cfa.rule = .{ .val_offset = @intCast(i64, i.operands.offset) };
473 },529 },
474 .def_cfa_offset_sf => |i| {530 .def_cfa_offset_sf => |i| {
475 try self.resolveCopyOnWrite(allocator);531 try self.resolveCopyOnWrite(allocator);
476 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .offset) return error.InvalidOperation;532 if (self.current_row.cfa.register == null or self.current_row.cfa.rule != .val_offset) return error.InvalidOperation;
477 self.current_row.cfa.rule = .{ .offset = i.operands.offset * cie.data_alignment_factor };533 self.current_row.cfa.rule = .{ .val_offset = i.operands.offset * cie.data_alignment_factor };
478 },534 },
479 .def_cfa_expression => |i| {535 .def_cfa_expression => |i| {
480 try self.resolveCopyOnWrite(allocator);536 try self.resolveCopyOnWrite(allocator);
...@@ -490,9 +546,18 @@ pub const VirtualMachine = struct {...@@ -490,9 +546,18 @@ pub const VirtualMachine = struct {
490 .expression = i.operands.block,546 .expression = i.operands.block,
491 };547 };
492 },548 },
493 .val_offset => {},549 .val_offset => {
494 .val_offset_sf => {},550 // TODO: Implement
495 .val_expression => {},551 unreachable;
552 },
553 .val_offset_sf => {
554 // TODO: Implement
555 unreachable;
556 },
557 .val_expression => {
558 // TODO: Implement
559 unreachable;
560 },
496 }561 }
497562
498 return prev_row;563 return prev_row;