| author | |
| committer | |
| log | 412cd789bf38a8fb6126803b8eab601b700e5a9e |
| tree | c9a5560c06129d260a9014c41606246863f6038e |
| parent | b85f84061aebb6c61ab9ca42d8147e8b76154818 |
dwarf: fixup x86 register mapping logic
dwarf: change the register context update to update in-place instead of copying
debug: always print the unwind error type5 files changed, 108 insertions(+), 51 deletions(-)
lib/std/c/darwin.zig+2| ... | ... | @@ -148,11 +148,13 @@ pub const ucontext_t = extern struct { |
| 148 | 148 | link: ?*ucontext_t, |
| 149 | 149 | mcsize: u64, |
| 150 | 150 | mcontext: *mcontext_t, |
| 151 | __mcontext_data: mcontext_t, | |
| 151 | 152 | }; |
| 152 | 153 | |
| 153 | 154 | pub const mcontext_t = extern struct { |
| 154 | 155 | es: arch_bits.exception_state, |
| 155 | 156 | ss: arch_bits.thread_state, |
| 157 | fs: arch_bits.float_state, | |
| 156 | 158 | }; |
| 157 | 159 | |
| 158 | 160 | extern "c" fn __error() *c_int; |
lib/std/c/darwin/x86_64.zig+23| ... | ... | @@ -31,6 +31,29 @@ pub const thread_state = extern struct { |
| 31 | 31 | gs: u64, |
| 32 | 32 | }; |
| 33 | 33 | |
| 34 | const stmm_reg = [16]u8; | |
| 35 | const xmm_reg = [16]u8; | |
| 36 | pub const float_state = extern struct { | |
| 37 | reserved: [2]c_int, | |
| 38 | fcw: u16, | |
| 39 | fsw: u16, | |
| 40 | ftw: u8, | |
| 41 | rsrv1: u8, | |
| 42 | fop: u16, | |
| 43 | ip: u32, | |
| 44 | cs: u16, | |
| 45 | rsrv2: u16, | |
| 46 | dp: u32, | |
| 47 | ds: u16, | |
| 48 | rsrv3: u16, | |
| 49 | mxcsr: u32, | |
| 50 | mxcsrmask: u32, | |
| 51 | stmm: [8]stmm_reg, | |
| 52 | xmm: [16]xmm_reg, | |
| 53 | rsrv4: [96]u8, | |
| 54 | reserved1: c_int, | |
| 55 | }; | |
| 56 | ||
| 34 | 57 | pub const THREAD_STATE = 4; |
| 35 | 58 | pub const THREAD_STATE_COUNT: c.mach_msg_type_number_t = @sizeOf(thread_state) / @sizeOf(c_int); |
| 36 | 59 |
lib/std/debug.zig+18-14| ... | ... | @@ -444,7 +444,10 @@ pub inline fn getContext(context: *StackTraceContext) bool { |
| 444 | 444 | return true; |
| 445 | 445 | } |
| 446 | 446 | |
| 447 | return have_getcontext and os.system.getcontext(context) == 0; | |
| 447 | const result = have_getcontext and os.system.getcontext(context) == 0; | |
| 448 | if (native_os == .macos) assert(context.mcsize == @sizeOf(std.c.mcontext_t)); | |
| 449 | ||
| 450 | return result; | |
| 448 | 451 | } |
| 449 | 452 | |
| 450 | 453 | pub const UnwindError = if (have_ucontext) |
| ... | ... | @@ -553,6 +556,7 @@ pub const StackIterator = struct { |
| 553 | 556 | if (native_os == .freestanding) return true; |
| 554 | 557 | |
| 555 | 558 | const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1))); |
| 559 | if (aligned_address == 0) return false; | |
| 556 | 560 | const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size]; |
| 557 | 561 | |
| 558 | 562 | if (native_os != .windows) { |
| ... | ... | @@ -815,11 +819,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz |
| 815 | 819 | pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { |
| 816 | 820 | const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; |
| 817 | 821 | try tty_config.setColor(out_stream, .dim); |
| 818 | if (err != error.MissingDebugInfo) { | |
| 819 | try out_stream.print("Unwind information for {s} was not available ({}), trace may be incomplete\n\n", .{ module_name, err }); | |
| 820 | } else { | |
| 821 | try out_stream.print("Unwind information for {s} was not available, trace may be incomplete\n\n", .{module_name}); | |
| 822 | } | |
| 822 | try out_stream.print("Unwind information for {s} was not available ({}), trace may be incomplete\n\n", .{ module_name, err }); | |
| 823 | 823 | try tty_config.setColor(out_stream, .reset); |
| 824 | 824 | } |
| 825 | 825 | |
| ... | ... | @@ -1309,6 +1309,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn |
| 1309 | 1309 | |
| 1310 | 1310 | return ModuleDebugInfo{ |
| 1311 | 1311 | .base_address = undefined, |
| 1312 | .vmaddr_slide = undefined, | |
| 1312 | 1313 | .mapped_memory = mapped_mem, |
| 1313 | 1314 | .ofiles = ModuleDebugInfo.OFileTable.init(allocator), |
| 1314 | 1315 | .symbols = symbols, |
| ... | ... | @@ -1514,11 +1515,10 @@ pub const DebugInfo = struct { |
| 1514 | 1515 | |
| 1515 | 1516 | var i: u32 = 0; |
| 1516 | 1517 | while (i < image_count) : (i += 1) { |
| 1517 | const base_address = std.c._dyld_get_image_vmaddr_slide(i); | |
| 1518 | ||
| 1519 | if (address < base_address) continue; | |
| 1520 | ||
| 1521 | 1518 | const header = std.c._dyld_get_image_header(i) orelse continue; |
| 1519 | const base_address = @intFromPtr(header); | |
| 1520 | if (address < base_address) continue; | |
| 1521 | const vmaddr_slide = std.c._dyld_get_image_vmaddr_slide(i); | |
| 1522 | 1522 | |
| 1523 | 1523 | var it = macho.LoadCommandIterator{ |
| 1524 | 1524 | .ncmds = header.ncmds, |
| ... | ... | @@ -1527,14 +1527,16 @@ pub const DebugInfo = struct { |
| 1527 | 1527 | @ptrFromInt(@intFromPtr(header) + @sizeOf(macho.mach_header_64)), |
| 1528 | 1528 | )[0..header.sizeofcmds]), |
| 1529 | 1529 | }; |
| 1530 | ||
| 1530 | 1531 | while (it.next()) |cmd| switch (cmd.cmd()) { |
| 1531 | 1532 | .SEGMENT_64 => { |
| 1532 | 1533 | const segment_cmd = cmd.cast(macho.segment_command_64).?; |
| 1533 | const rebased_address = address - base_address; | |
| 1534 | if (!mem.eql(u8, "__TEXT", segment_cmd.segName())) continue; | |
| 1535 | ||
| 1536 | const original_address = address - vmaddr_slide; | |
| 1534 | 1537 | const seg_start = segment_cmd.vmaddr; |
| 1535 | 1538 | const seg_end = seg_start + segment_cmd.vmsize; |
| 1536 | ||
| 1537 | if (rebased_address >= seg_start and rebased_address < seg_end) { | |
| 1539 | if (original_address >= seg_start and original_address < seg_end) { | |
| 1538 | 1540 | if (self.address_map.get(base_address)) |obj_di| { |
| 1539 | 1541 | return obj_di; |
| 1540 | 1542 | } |
| ... | ... | @@ -1551,6 +1553,7 @@ pub const DebugInfo = struct { |
| 1551 | 1553 | }; |
| 1552 | 1554 | obj_di.* = try readMachODebugInfo(self.allocator, macho_file); |
| 1553 | 1555 | obj_di.base_address = base_address; |
| 1556 | obj_di.vmaddr_slide = vmaddr_slide; | |
| 1554 | 1557 | |
| 1555 | 1558 | try self.address_map.putNoClobber(base_address, obj_di); |
| 1556 | 1559 | |
| ... | ... | @@ -1808,6 +1811,7 @@ pub const DebugInfo = struct { |
| 1808 | 1811 | pub const ModuleDebugInfo = switch (native_os) { |
| 1809 | 1812 | .macos, .ios, .watchos, .tvos => struct { |
| 1810 | 1813 | base_address: usize, |
| 1814 | vmaddr_slide: usize, | |
| 1811 | 1815 | mapped_memory: []align(mem.page_size) const u8, |
| 1812 | 1816 | symbols: []const MachoSymbol, |
| 1813 | 1817 | strings: [:0]const u8, |
| ... | ... | @@ -1972,7 +1976,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1972 | 1976 | } { |
| 1973 | 1977 | nosuspend { |
| 1974 | 1978 | // Translate the VA into an address into this object |
| 1975 | const relocated_address = address - self.base_address; | |
| 1979 | const relocated_address = address - self.vmaddr_slide; | |
| 1976 | 1980 | |
| 1977 | 1981 | // Find the .o file where this symbol is defined |
| 1978 | 1982 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{ |
lib/std/dwarf.zig+30-5| ... | ... | @@ -1705,28 +1705,53 @@ pub const DwarfInfo = struct { |
| 1705 | 1705 | |
| 1706 | 1706 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; |
| 1707 | 1707 | |
| 1708 | // Update the context with the previous frame's values | |
| 1709 | var next_ucontext = context.ucontext; | |
| 1708 | // Buffering the modifications is done because copying the ucontext is not portable, | |
| 1709 | // some implementations (ie. darwin) use internal pointers to the mcontext. | |
| 1710 | var arena = std.heap.ArenaAllocator.init(context.allocator); | |
| 1711 | defer arena.deinit(); | |
| 1712 | const update_allocator = arena.allocator(); | |
| 1713 | ||
| 1714 | const RegisterUpdate = struct { | |
| 1715 | // Backed by ucontext | |
| 1716 | old_value: []u8, | |
| 1717 | // Backed by arena | |
| 1718 | new_value: []const u8, | |
| 1719 | prev: ?*@This(), | |
| 1720 | }; | |
| 1710 | 1721 | |
| 1722 | var update_tail: ?*RegisterUpdate = null; | |
| 1711 | 1723 | var has_next_ip = false; |
| 1712 | 1724 | for (context.vm.rowColumns(row.*)) |column| { |
| 1713 | 1725 | if (column.register) |register| { |
| 1714 | const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx); | |
| 1715 | 1726 | if (register == cie.return_address_register) { |
| 1716 | 1727 | has_next_ip = column.rule != .undefined; |
| 1717 | 1728 | } |
| 1718 | 1729 | |
| 1730 | const old_value = try abi.regBytes(&context.ucontext, register, context.reg_ctx); | |
| 1731 | const new_value = try update_allocator.alloc(u8, old_value.len); | |
| 1732 | ||
| 1733 | const prev = update_tail; | |
| 1734 | update_tail = try update_allocator.create(RegisterUpdate); | |
| 1735 | update_tail.?.* = .{ | |
| 1736 | .old_value = old_value, | |
| 1737 | .new_value = new_value, | |
| 1738 | .prev = prev, | |
| 1739 | }; | |
| 1740 | ||
| 1719 | 1741 | try column.resolveValue( |
| 1720 | 1742 | context, |
| 1721 | 1743 | compile_unit, |
| 1722 | 1744 | &context.ucontext, |
| 1723 | 1745 | context.reg_ctx, |
| 1724 | dest, | |
| 1746 | new_value, | |
| 1725 | 1747 | ); |
| 1726 | 1748 | } |
| 1727 | 1749 | } |
| 1728 | 1750 | |
| 1729 | context.ucontext = next_ucontext; | |
| 1751 | while (update_tail) |tail| { | |
| 1752 | @memcpy(tail.old_value, tail.new_value); | |
| 1753 | update_tail = tail.prev; | |
| 1754 | } | |
| 1730 | 1755 | |
| 1731 | 1756 | if (has_next_ip) { |
| 1732 | 1757 | context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, comptime abi.ipRegNum(), context.reg_ctx)); |
lib/std/dwarf/abi.zig+35-32| ... | ... | @@ -68,38 +68,41 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext |
| 68 | 68 | var m = &ucontext_ptr.mcontext; |
| 69 | 69 | |
| 70 | 70 | return switch (builtin.cpu.arch) { |
| 71 | .x86 => switch (reg_number) { | |
| 72 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]), | |
| 73 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), | |
| 74 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), | |
| 75 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), | |
| 76 | 4...5 => if (reg_ctx) |r| bytes: { | |
| 77 | if (reg_number == 4) { | |
| 78 | break :bytes if (r.eh_frame and r.is_macho) | |
| 79 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]) | |
| 80 | else | |
| 81 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]); | |
| 82 | } else { | |
| 83 | break :bytes if (r.eh_frame and r.is_macho) | |
| 84 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]) | |
| 85 | else | |
| 86 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]); | |
| 87 | } | |
| 88 | } else error.RegisterContextRequired, | |
| 89 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]), | |
| 90 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]), | |
| 91 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]), | |
| 92 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]), | |
| 93 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]), | |
| 94 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]), | |
| 95 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]), | |
| 96 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]), | |
| 97 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), | |
| 98 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), | |
| 99 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs | |
| 100 | // TODO: Map TRAPNO, ERR, UESP | |
| 101 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs | |
| 102 | else => error.InvalidRegister, | |
| 71 | .x86 => switch (builtin.os.tag) { | |
| 72 | .linux, .netbsd, .solaris => switch (reg_number) { | |
| 73 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]), | |
| 74 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), | |
| 75 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), | |
| 76 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), | |
| 77 | 4...5 => if (reg_ctx) |r| bytes: { | |
| 78 | if (reg_number == 4) { | |
| 79 | break :bytes if (r.eh_frame and r.is_macho) | |
| 80 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]) | |
| 81 | else | |
| 82 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]); | |
| 83 | } else { | |
| 84 | break :bytes if (r.eh_frame and r.is_macho) | |
| 85 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]) | |
| 86 | else | |
| 87 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]); | |
| 88 | } | |
| 89 | } else error.RegisterContextRequired, | |
| 90 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]), | |
| 91 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]), | |
| 92 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]), | |
| 93 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]), | |
| 94 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]), | |
| 95 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]), | |
| 96 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]), | |
| 97 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]), | |
| 98 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), | |
| 99 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), | |
| 100 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs | |
| 101 | // TODO: Map TRAPNO, ERR, UESP | |
| 102 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs | |
| 103 | else => error.InvalidRegister, | |
| 104 | }, | |
| 105 | else => error.UnimplementedOs, | |
| 103 | 106 | }, |
| 104 | 107 | .x86_64 => switch (builtin.os.tag) { |
| 105 | 108 | .linux, .netbsd, .solaris => switch (reg_number) { |