authorgravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-03 14:31:29-04:00
committergravatar for kcbanner@gmail.comCasey Banner <kcbanner@gmail.com> 2023-07-20 22:58:14-04:00
log412cd789bf38a8fb6126803b8eab601b700e5a9e
treec9a5560c06129d260a9014c41606246863f6038e
parentb85f84061aebb6c61ab9ca42d8147e8b76154818

debug: fixup base address calculations for macho

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 type

5 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};
152153
153pub const mcontext_t = extern struct {154pub 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};
157159
158extern "c" fn __error() *c_int;160extern "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};
3333
34const stmm_reg = [16]u8;
35const xmm_reg = [16]u8;
36pub 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
34pub const THREAD_STATE = 4;57pub const THREAD_STATE = 4;
35pub const THREAD_STATE_COUNT: c.mach_msg_type_number_t = @sizeOf(thread_state) / @sizeOf(c_int);58pub const THREAD_STATE_COUNT: c.mach_msg_type_number_t = @sizeOf(thread_state) / @sizeOf(c_int);
3659
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 }
446446
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}
449452
450pub const UnwindError = if (have_ucontext)453pub 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;
554557
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];
557561
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
815pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {819pub 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}
825825
...@@ -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
13091309
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 {
15141515
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);
15221522
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;
15361539 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;
15541557
1555 try self.address_map.putNoClobber(base_address, obj_di);1558 try self.address_map.putNoClobber(base_address, obj_di);
15561559
...@@ -1808,6 +1811,7 @@ pub const DebugInfo = struct {...@@ -1808,6 +1811,7 @@ pub const DebugInfo = struct {
1808pub const ModuleDebugInfo = switch (native_os) {1811pub 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 object1978 // 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;
19761980
1977 // Find the .o file where this symbol is defined1981 // 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 {
17051705
1706 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;1706 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
17071707
1708 // Update the context with the previous frame's values1708 // 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 };
17101721
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 }
17181729
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 }
17281750
1729 context.ucontext = next_ucontext;1751 while (update_tail) |tail| {
1752 @memcpy(tail.old_value, tail.new_value);
1753 update_tail = tail.prev;
1754 }
17301755
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;
6969
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 else80 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 else85 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.fpregs99 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),
100 // TODO: Map TRAPNO, ERR, UESP100 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
101 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs101 // 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) {