| 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,11 +148,13 @@ pub const ucontext_t = extern struct { |
| 148 | link: ?*ucontext_t, | 148 | link: ?*ucontext_t, |
| 149 | mcsize: u64, | 149 | mcsize: u64, |
| 150 | mcontext: *mcontext_t, | 150 | mcontext: *mcontext_t, |
| 151 | __mcontext_data: mcontext_t, | ||
| 151 | }; | 152 | }; |
| 152 | 153 | ||
| 153 | pub const mcontext_t = extern struct { | 154 | pub const mcontext_t = extern struct { |
| 154 | es: arch_bits.exception_state, | 155 | es: arch_bits.exception_state, |
| 155 | ss: arch_bits.thread_state, | 156 | ss: arch_bits.thread_state, |
| 157 | fs: arch_bits.float_state, | ||
| 156 | }; | 158 | }; |
| 157 | 159 | ||
| 158 | extern "c" fn __error() *c_int; | 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,6 +31,29 @@ pub const thread_state = extern struct { |
| 31 | gs: u64, | 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 | pub const THREAD_STATE = 4; | 57 | pub const THREAD_STATE = 4; |
| 35 | pub const THREAD_STATE_COUNT: c.mach_msg_type_number_t = @sizeOf(thread_state) / @sizeOf(c_int); | 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,7 +444,10 @@ pub inline fn getContext(context: *StackTraceContext) bool { |
| 444 | return true; | 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 | pub const UnwindError = if (have_ucontext) | 453 | pub const UnwindError = if (have_ucontext) |
| ... | @@ -553,6 +556,7 @@ pub const StackIterator = struct { | ... | @@ -553,6 +556,7 @@ pub const StackIterator = struct { |
| 553 | if (native_os == .freestanding) return true; | 556 | if (native_os == .freestanding) return true; |
| 554 | 557 | ||
| 555 | const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1))); | 558 | const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1))); |
| 559 | if (aligned_address == 0) return false; | ||
| 556 | const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size]; | 560 | const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size]; |
| 557 | 561 | ||
| 558 | if (native_os != .windows) { | 562 | if (native_os != .windows) { |
| ... | @@ -815,11 +819,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz | ... | @@ -815,11 +819,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz |
| 815 | pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { | 819 | pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void { |
| 816 | const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; | 820 | const module_name = debug_info.getModuleNameForAddress(address) orelse "???"; |
| 817 | try tty_config.setColor(out_stream, .dim); | 821 | try tty_config.setColor(out_stream, .dim); |
| 818 | if (err != error.MissingDebugInfo) { | 822 | try out_stream.print("Unwind information for {s} was not available ({}), trace may be incomplete\n\n", .{ module_name, err }); |
| 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 | } | ||
| 823 | try tty_config.setColor(out_stream, .reset); | 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,6 +1309,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn |
| 1309 | 1309 | ||
| 1310 | return ModuleDebugInfo{ | 1310 | return ModuleDebugInfo{ |
| 1311 | .base_address = undefined, | 1311 | .base_address = undefined, |
| 1312 | .vmaddr_slide = undefined, | ||
| 1312 | .mapped_memory = mapped_mem, | 1313 | .mapped_memory = mapped_mem, |
| 1313 | .ofiles = ModuleDebugInfo.OFileTable.init(allocator), | 1314 | .ofiles = ModuleDebugInfo.OFileTable.init(allocator), |
| 1314 | .symbols = symbols, | 1315 | .symbols = symbols, |
| ... | @@ -1514,11 +1515,10 @@ pub const DebugInfo = struct { | ... | @@ -1514,11 +1515,10 @@ pub const DebugInfo = struct { |
| 1514 | 1515 | ||
| 1515 | var i: u32 = 0; | 1516 | var i: u32 = 0; |
| 1516 | while (i < image_count) : (i += 1) { | 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 | const header = std.c._dyld_get_image_header(i) orelse continue; | 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 | var it = macho.LoadCommandIterator{ | 1523 | var it = macho.LoadCommandIterator{ |
| 1524 | .ncmds = header.ncmds, | 1524 | .ncmds = header.ncmds, |
| ... | @@ -1527,14 +1527,16 @@ pub const DebugInfo = struct { | ... | @@ -1527,14 +1527,16 @@ pub const DebugInfo = struct { |
| 1527 | @ptrFromInt(@intFromPtr(header) + @sizeOf(macho.mach_header_64)), | 1527 | @ptrFromInt(@intFromPtr(header) + @sizeOf(macho.mach_header_64)), |
| 1528 | )[0..header.sizeofcmds]), | 1528 | )[0..header.sizeofcmds]), |
| 1529 | }; | 1529 | }; |
| 1530 | |||
| 1530 | while (it.next()) |cmd| switch (cmd.cmd()) { | 1531 | while (it.next()) |cmd| switch (cmd.cmd()) { |
| 1531 | .SEGMENT_64 => { | 1532 | .SEGMENT_64 => { |
| 1532 | const segment_cmd = cmd.cast(macho.segment_command_64).?; | 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 | const seg_start = segment_cmd.vmaddr; | 1537 | const seg_start = segment_cmd.vmaddr; |
| 1535 | const seg_end = seg_start + segment_cmd.vmsize; | 1538 | const seg_end = seg_start + segment_cmd.vmsize; |
| 1536 | 1539 | if (original_address >= seg_start and original_address < seg_end) { | |
| 1537 | if (rebased_address >= seg_start and rebased_address < seg_end) { | ||
| 1538 | if (self.address_map.get(base_address)) |obj_di| { | 1540 | if (self.address_map.get(base_address)) |obj_di| { |
| 1539 | return obj_di; | 1541 | return obj_di; |
| 1540 | } | 1542 | } |
| ... | @@ -1551,6 +1553,7 @@ pub const DebugInfo = struct { | ... | @@ -1551,6 +1553,7 @@ pub const DebugInfo = struct { |
| 1551 | }; | 1553 | }; |
| 1552 | obj_di.* = try readMachODebugInfo(self.allocator, macho_file); | 1554 | obj_di.* = try readMachODebugInfo(self.allocator, macho_file); |
| 1553 | obj_di.base_address = base_address; | 1555 | obj_di.base_address = base_address; |
| 1556 | obj_di.vmaddr_slide = vmaddr_slide; | ||
| 1554 | 1557 | ||
| 1555 | try self.address_map.putNoClobber(base_address, obj_di); | 1558 | try self.address_map.putNoClobber(base_address, obj_di); |
| 1556 | 1559 | ||
| ... | @@ -1808,6 +1811,7 @@ pub const DebugInfo = struct { | ... | @@ -1808,6 +1811,7 @@ pub const DebugInfo = struct { |
| 1808 | pub const ModuleDebugInfo = switch (native_os) { | 1811 | pub const ModuleDebugInfo = switch (native_os) { |
| 1809 | .macos, .ios, .watchos, .tvos => struct { | 1812 | .macos, .ios, .watchos, .tvos => struct { |
| 1810 | base_address: usize, | 1813 | base_address: usize, |
| 1814 | vmaddr_slide: usize, | ||
| 1811 | mapped_memory: []align(mem.page_size) const u8, | 1815 | mapped_memory: []align(mem.page_size) const u8, |
| 1812 | symbols: []const MachoSymbol, | 1816 | symbols: []const MachoSymbol, |
| 1813 | strings: [:0]const u8, | 1817 | strings: [:0]const u8, |
| ... | @@ -1972,7 +1976,7 @@ pub const ModuleDebugInfo = switch (native_os) { | ... | @@ -1972,7 +1976,7 @@ pub const ModuleDebugInfo = switch (native_os) { |
| 1972 | } { | 1976 | } { |
| 1973 | nosuspend { | 1977 | nosuspend { |
| 1974 | // Translate the VA into an address into this object | 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 | // Find the .o file where this symbol is defined | 1981 | // Find the .o file where this symbol is defined |
| 1978 | const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{ | 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,28 +1705,53 @@ pub const DwarfInfo = struct { |
| 1705 | 1705 | ||
| 1706 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; | 1706 | if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA; |
| 1707 | 1707 | ||
| 1708 | // Update the context with the previous frame's values | 1708 | // Buffering the modifications is done because copying the ucontext is not portable, |
| 1709 | var next_ucontext = context.ucontext; | 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 | var has_next_ip = false; | 1723 | var has_next_ip = false; |
| 1712 | for (context.vm.rowColumns(row.*)) |column| { | 1724 | for (context.vm.rowColumns(row.*)) |column| { |
| 1713 | if (column.register) |register| { | 1725 | if (column.register) |register| { |
| 1714 | const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx); | ||
| 1715 | if (register == cie.return_address_register) { | 1726 | if (register == cie.return_address_register) { |
| 1716 | has_next_ip = column.rule != .undefined; | 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 | try column.resolveValue( | 1741 | try column.resolveValue( |
| 1720 | context, | 1742 | context, |
| 1721 | compile_unit, | 1743 | compile_unit, |
| 1722 | &context.ucontext, | 1744 | &context.ucontext, |
| 1723 | context.reg_ctx, | 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 | if (has_next_ip) { | 1756 | if (has_next_ip) { |
| 1732 | context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, comptime abi.ipRegNum(), context.reg_ctx)); | 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,38 +68,41 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext |
| 68 | var m = &ucontext_ptr.mcontext; | 68 | var m = &ucontext_ptr.mcontext; |
| 69 | 69 | ||
| 70 | return switch (builtin.cpu.arch) { | 70 | return switch (builtin.cpu.arch) { |
| 71 | .x86 => switch (reg_number) { | 71 | .x86 => switch (builtin.os.tag) { |
| 72 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]), | 72 | .linux, .netbsd, .solaris => switch (reg_number) { |
| 73 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), | 73 | 0 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EAX]), |
| 74 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), | 74 | 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]), |
| 75 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), | 75 | 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]), |
| 76 | 4...5 => if (reg_ctx) |r| bytes: { | 76 | 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]), |
| 77 | if (reg_number == 4) { | 77 | 4...5 => if (reg_ctx) |r| bytes: { |
| 78 | break :bytes if (r.eh_frame and r.is_macho) | 78 | if (reg_number == 4) { |
| 79 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]) | 79 | break :bytes if (r.eh_frame and r.is_macho) |
| 80 | else | 80 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]) |
| 81 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]); | 81 | else |
| 82 | } else { | 82 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]); |
| 83 | break :bytes if (r.eh_frame and r.is_macho) | 83 | } else { |
| 84 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]) | 84 | break :bytes if (r.eh_frame and r.is_macho) |
| 85 | else | 85 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESP]) |
| 86 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]); | 86 | else |
| 87 | } | 87 | mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP]); |
| 88 | } else error.RegisterContextRequired, | 88 | } |
| 89 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]), | 89 | } else error.RegisterContextRequired, |
| 90 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]), | 90 | 6 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ESI]), |
| 91 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]), | 91 | 7 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDI]), |
| 92 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]), | 92 | 8 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EIP]), |
| 93 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]), | 93 | 9 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EFL]), |
| 94 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]), | 94 | 10 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.CS]), |
| 95 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]), | 95 | 11 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.SS]), |
| 96 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]), | 96 | 12 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.DS]), |
| 97 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), | 97 | 13 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ES]), |
| 98 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), | 98 | 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]), |
| 99 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs | 99 | 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]), |
| 100 | // TODO: Map TRAPNO, ERR, UESP | 100 | 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs |
| 101 | 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs | 101 | // TODO: Map TRAPNO, ERR, UESP |
| 102 | else => error.InvalidRegister, | 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 | .x86_64 => switch (builtin.os.tag) { | 107 | .x86_64 => switch (builtin.os.tag) { |
| 105 | .linux, .netbsd, .solaris => switch (reg_number) { | 108 | .linux, .netbsd, .solaris => switch (reg_number) { |