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 {
148148 link: ?*ucontext_t,
149149 mcsize: u64,
150150 mcontext: *mcontext_t,
151 __mcontext_data: mcontext_t,
151152};
152153
153154pub const mcontext_t = extern struct {
154155 es: arch_bits.exception_state,
155156 ss: arch_bits.thread_state,
157 fs: arch_bits.float_state,
156158};
157159
158160extern "c" fn __error() *c_int;
lib/std/c/darwin/x86_64.zig+23
......@@ -31,6 +31,29 @@ pub const thread_state = extern struct {
3131 gs: u64,
3232};
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
3457pub const THREAD_STATE = 4;
3558pub 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 {
444444 return true;
445445 }
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;
448451}
449452
450453pub const UnwindError = if (have_ucontext)
......@@ -553,6 +556,7 @@ pub const StackIterator = struct {
553556 if (native_os == .freestanding) return true;
554557
555558 const aligned_address = address & ~@as(usize, @intCast((mem.page_size - 1)));
559 if (aligned_address == 0) return false;
556560 const aligned_memory = @as([*]align(mem.page_size) u8, @ptrFromInt(aligned_address))[0..mem.page_size];
557561
558562 if (native_os != .windows) {
......@@ -815,11 +819,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz
815819pub fn printUnwindError(debug_info: *DebugInfo, out_stream: anytype, address: usize, err: UnwindError, tty_config: io.tty.Config) !void {
816820 const module_name = debug_info.getModuleNameForAddress(address) orelse "???";
817821 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 });
823823 try tty_config.setColor(out_stream, .reset);
824824}
825825
......@@ -1309,6 +1309,7 @@ fn readMachODebugInfo(allocator: mem.Allocator, macho_file: File) !ModuleDebugIn
13091309
13101310 return ModuleDebugInfo{
13111311 .base_address = undefined,
1312 .vmaddr_slide = undefined,
13121313 .mapped_memory = mapped_mem,
13131314 .ofiles = ModuleDebugInfo.OFileTable.init(allocator),
13141315 .symbols = symbols,
......@@ -1514,11 +1515,10 @@ pub const DebugInfo = struct {
15141515
15151516 var i: u32 = 0;
15161517 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
15211518 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
15231523 var it = macho.LoadCommandIterator{
15241524 .ncmds = header.ncmds,
......@@ -1527,14 +1527,16 @@ pub const DebugInfo = struct {
15271527 @ptrFromInt(@intFromPtr(header) + @sizeOf(macho.mach_header_64)),
15281528 )[0..header.sizeofcmds]),
15291529 };
1530
15301531 while (it.next()) |cmd| switch (cmd.cmd()) {
15311532 .SEGMENT_64 => {
15321533 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;
15341537 const seg_start = segment_cmd.vmaddr;
15351538 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) {
15381540 if (self.address_map.get(base_address)) |obj_di| {
15391541 return obj_di;
15401542 }
......@@ -1551,6 +1553,7 @@ pub const DebugInfo = struct {
15511553 };
15521554 obj_di.* = try readMachODebugInfo(self.allocator, macho_file);
15531555 obj_di.base_address = base_address;
1556 obj_di.vmaddr_slide = vmaddr_slide;
15541557
15551558 try self.address_map.putNoClobber(base_address, obj_di);
15561559
......@@ -1808,6 +1811,7 @@ pub const DebugInfo = struct {
18081811pub const ModuleDebugInfo = switch (native_os) {
18091812 .macos, .ios, .watchos, .tvos => struct {
18101813 base_address: usize,
1814 vmaddr_slide: usize,
18111815 mapped_memory: []align(mem.page_size) const u8,
18121816 symbols: []const MachoSymbol,
18131817 strings: [:0]const u8,
......@@ -1972,7 +1976,7 @@ pub const ModuleDebugInfo = switch (native_os) {
19721976 } {
19731977 nosuspend {
19741978 // 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
19771981 // Find the .o file where this symbol is defined
19781982 const symbol = machoSearchSymbols(self.symbols, relocated_address) orelse return .{
lib/std/dwarf.zig+30-5
......@@ -1705,28 +1705,53 @@ pub const DwarfInfo = struct {
17051705
17061706 if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
17071707
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 };
17101721
1722 var update_tail: ?*RegisterUpdate = null;
17111723 var has_next_ip = false;
17121724 for (context.vm.rowColumns(row.*)) |column| {
17131725 if (column.register) |register| {
1714 const dest = try abi.regBytes(&next_ucontext, register, context.reg_ctx);
17151726 if (register == cie.return_address_register) {
17161727 has_next_ip = column.rule != .undefined;
17171728 }
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
17191741 try column.resolveValue(
17201742 context,
17211743 compile_unit,
17221744 &context.ucontext,
17231745 context.reg_ctx,
1724 dest,
1746 new_value,
17251747 );
17261748 }
17271749 }
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
17311756 if (has_next_ip) {
17321757 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
6868 var m = &ucontext_ptr.mcontext;
6969
7070 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,
103106 },
104107 .x86_64 => switch (builtin.os.tag) {
105108 .linux, .netbsd, .solaris => switch (reg_number) {