Commit 5f72c6508d

kcbanner <kcbanner@gmail.com>
2023-07-07 04:02:47
debug: rename StackTraceContext to ThreadContext dwarf: use ThreadContext instead of os.ucontext_t dwarf: add regBytes impl for windows dwarf: fixup expression types for non-native
1 parent 8547c42
lib/std/dwarf/abi.zig
@@ -3,11 +3,6 @@ const std = @import("../std.zig");
 const os = std.os;
 const mem = std.mem;
 
-pub const RegisterContext = struct {
-    eh_frame: bool,
-    is_macho: bool,
-};
-
 pub fn isSupportedArch(arch: std.Target.Cpu.Arch) bool {
     return switch (arch) {
         .x86,
@@ -29,10 +24,10 @@ pub fn ipRegNum() u8 {
     };
 }
 
-pub fn fpRegNum(reg_ctx: RegisterContext) u8 {
+pub fn fpRegNum(reg_context: RegisterContext) u8 {
     return switch (builtin.cpu.arch) {
         // GCC on OS X did the opposite of ELF for these registers (only in .eh_frame), and that is now the convention for MachO
-        .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 4 else 5,
+        .x86 => if (reg_context.eh_frame and reg_context.is_macho) 4 else 5,
         .x86_64 => 6,
         .arm => 11,
         .aarch64 => 29,
@@ -40,9 +35,9 @@ pub fn fpRegNum(reg_ctx: RegisterContext) u8 {
     };
 }
 
-pub fn spRegNum(reg_ctx: RegisterContext) u8 {
+pub fn spRegNum(reg_context: RegisterContext) u8 {
     return switch (builtin.cpu.arch) {
-        .x86 => if (reg_ctx.eh_frame and reg_ctx.is_macho) 5 else 4,
+        .x86 => if (reg_context.eh_frame and reg_context.is_macho) 5 else 4,
         .x86_64 => 7,
         .arm => 13,
         .aarch64 => 31,
@@ -52,21 +47,76 @@ pub fn spRegNum(reg_ctx: RegisterContext) u8 {
 
 fn RegBytesReturnType(comptime ContextPtrType: type) type {
     const info = @typeInfo(ContextPtrType);
-    if (info != .Pointer or info.Pointer.child != os.ucontext_t) {
-        @compileError("Expected a pointer to ucontext_t, got " ++ @typeName(@TypeOf(ContextPtrType)));
+    if (info != .Pointer or info.Pointer.child != std.debug.ThreadContext) {
+        @compileError("Expected a pointer to std.debug.ThreadContext, got " ++ @typeName(@TypeOf(ContextPtrType)));
     }
 
     return if (info.Pointer.is_const) return []const u8 else []u8;
 }
 
+pub const RegisterContext = struct {
+    eh_frame: bool,
+    is_macho: bool,
+};
+
 /// Returns a slice containing the backing storage for `reg_number`.
 ///
-/// `reg_ctx` describes in what context the register number is used, as it can have different
+/// `reg_context` describes in what context the register number is used, as it can have different
 /// meanings depending on the DWARF container. It is only required when getting the stack or
 /// frame pointer register on some architectures.
-pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext) !RegBytesReturnType(@TypeOf(ucontext_ptr)) {
-    var m = &ucontext_ptr.mcontext;
+pub fn regBytes(thread_context_ptr: anytype, reg_number: u8, reg_context: ?RegisterContext) !RegBytesReturnType(@TypeOf(thread_context_ptr)) {
+    if (builtin.os.tag == .windows) {
+        return switch (builtin.cpu.arch) {
+            .x86 => switch (reg_number) {
+                0 => mem.asBytes(&thread_context_ptr.Eax),
+                1 => mem.asBytes(&thread_context_ptr.Ecx),
+                2 => mem.asBytes(&thread_context_ptr.Edx),
+                3 => mem.asBytes(&thread_context_ptr.Ebx),
+                4 => mem.asBytes(&thread_context_ptr.Esp),
+                5 => mem.asBytes(&thread_context_ptr.Ebp),
+                6 => mem.asBytes(&thread_context_ptr.Esi),
+                7 => mem.asBytes(&thread_context_ptr.Edi),
+                8 => mem.asBytes(&thread_context_ptr.Eip),
+                9 => mem.asBytes(&thread_context_ptr.EFlags),
+                10 => mem.asBytes(&thread_context_ptr.SegCs),
+                11 => mem.asBytes(&thread_context_ptr.SegSs),
+                12 => mem.asBytes(&thread_context_ptr.SegDs),
+                13 => mem.asBytes(&thread_context_ptr.SegEs),
+                14 => mem.asBytes(&thread_context_ptr.SegFs),
+                15 => mem.asBytes(&thread_context_ptr.SegGs),
+                else => error.InvalidRegister,
+            },
+            .x86_64 => switch (reg_number) {
+                0 => mem.asBytes(&thread_context_ptr.Rax),
+                1 => mem.asBytes(&thread_context_ptr.Rdx),
+                2 => mem.asBytes(&thread_context_ptr.Rcx),
+                3 => mem.asBytes(&thread_context_ptr.Rbx),
+                4 => mem.asBytes(&thread_context_ptr.Rsi),
+                5 => mem.asBytes(&thread_context_ptr.Rdi),
+                6 => mem.asBytes(&thread_context_ptr.Rbp),
+                7 => mem.asBytes(&thread_context_ptr.Rsp),
+                8 => mem.asBytes(&thread_context_ptr.R8),
+                9 => mem.asBytes(&thread_context_ptr.R9),
+                10 => mem.asBytes(&thread_context_ptr.R10),
+                11 => mem.asBytes(&thread_context_ptr.R11),
+                12 => mem.asBytes(&thread_context_ptr.R12),
+                13 => mem.asBytes(&thread_context_ptr.R13),
+                14 => mem.asBytes(&thread_context_ptr.R14),
+                15 => mem.asBytes(&thread_context_ptr.R15),
+                16 => mem.asBytes(&thread_context_ptr.Rip),
+                else => error.InvalidRegister,
+            },
+            .aarch64 => switch (reg_number) {
+                0...30 => mem.asBytes(&thread_context_ptr.DUMMYUNIONNAME.X[reg_number]),
+                31 => mem.asBytes(&thread_context_ptr.Sp),
+                32 => mem.asBytes(&thread_context_ptr.Pc),
+            },
+            else => error.UnimplementedArch,
+        };
+    }
 
+    const ucontext_ptr = thread_context_ptr;
+    var m = &ucontext_ptr.mcontext;
     return switch (builtin.cpu.arch) {
         .x86 => switch (builtin.os.tag) {
             .linux, .netbsd, .solaris => switch (reg_number) {
@@ -74,7 +124,7 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
                 1 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.ECX]),
                 2 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EDX]),
                 3 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBX]),
-                4...5 => if (reg_ctx) |r| bytes: {
+                4...5 => if (reg_context) |r| bytes: {
                     if (reg_number == 4) {
                         break :bytes if (r.eh_frame and r.is_macho)
                             mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.EBP])
@@ -98,7 +148,6 @@ pub fn regBytes(ucontext_ptr: anytype, reg_number: u8, reg_ctx: ?RegisterContext
                 14 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.FS]),
                 15 => mem.asBytes(&ucontext_ptr.mcontext.gregs[os.REG.GS]),
                 16...23 => error.InvalidRegister, // TODO: Support loading ST0-ST7 from mcontext.fpregs
-                // TODO: Map TRAPNO, ERR, UESP
                 32...39 => error.InvalidRegister, // TODO: Support loading XMM0-XMM7 from mcontext.fpregs
                 else => error.InvalidRegister,
             },
lib/std/dwarf/call_frame.zig
@@ -2,10 +2,10 @@ const builtin = @import("builtin");
 const std = @import("../std.zig");
 const mem = std.mem;
 const debug = std.debug;
-const leb = @import("../leb128.zig");
-const abi = @import("abi.zig");
-const dwarf = @import("../dwarf.zig");
-const expressions = @import("expressions.zig");
+const leb = std.leb;
+const dwarf = std.dwarf;
+const abi = dwarf.abi;
+const expressions = dwarf.expressions;
 const assert = std.debug.assert;
 
 const Opcode = enum(u8) {
@@ -315,9 +315,9 @@ pub const VirtualMachine = struct {
                     } else return error.InvalidCFA;
                 },
                 .register => |register| {
-                    const src = try abi.regBytes(&context.ucontext, register, context.reg_ctx);
+                    const src = try abi.regBytes(&context.thread_context, register, context.reg_context);
                     if (src.len != out.len) return error.RegisterTypeMismatch;
-                    @memcpy(out, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
+                    @memcpy(out, try abi.regBytes(&context.thread_context, register, context.reg_context));
                 },
                 .expression => |expression| {
                     context.stack_machine.reset();
lib/std/dwarf/expressions.zig
@@ -1,8 +1,8 @@
 const std = @import("std");
 const builtin = @import("builtin");
 const OP = @import("OP.zig");
-const leb = @import("../leb128.zig");
-const dwarf = @import("../dwarf.zig");
+const leb = std.leb;
+const dwarf = std.dwarf;
 const abi = dwarf.abi;
 const mem = std.mem;
 const assert = std.debug.assert;
@@ -17,12 +17,12 @@ pub const ExpressionContext = struct {
     /// The compilation unit this expression relates to, if any
     compile_unit: ?*const dwarf.CompileUnit = null,
 
-    /// Register context
-    ucontext: ?*std.os.ucontext_t,
-    reg_ctx: ?abi.RegisterContext,
+    /// Thread context
+    thread_context: ?*std.debug.ThreadContext = null,
+    reg_context: ?abi.RegisterContext = null,
 
     /// Call frame address, if in a CFI context
-    cfa: ?usize,
+    cfa: ?usize = null,
 };
 
 pub const ExpressionOptions = struct {
@@ -344,13 +344,13 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 OP.breg0...OP.breg31,
                 OP.bregx,
                 => {
-                    if (context.ucontext == null) return error.IncompleteExpressionContext;
+                    if (context.thread_context == null) return error.IncompleteExpressionContext;
 
                     const base_register = (try readOperand(stream, opcode)).?.base_register;
                     var value: i64 = @intCast(mem.readIntSliceNative(usize, try abi.regBytes(
-                        context.ucontext.?,
+                        context.thread_context.?,
                         base_register.base_register,
-                        context.reg_ctx,
+                        context.reg_context,
                     )));
                     value += base_register.offset;
                     try self.stack.append(allocator, .{ .generic = @intCast(value) });
@@ -358,9 +358,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 OP.regval_type => {
                     const register_type = (try readOperand(stream, opcode)).?.register_type;
                     const value = mem.readIntSliceNative(usize, try abi.regBytes(
-                        context.ucontext.?,
+                        context.thread_context.?,
                         register_type.register,
-                        context.reg_ctx,
+                        context.reg_context,
                     ));
                     try self.stack.append(allocator, .{
                         .regval_type = .{
@@ -464,7 +464,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 // 2.5.1.4: Arithmetic and Logical Operations
                 OP.abs => {
                     if (self.stack.items.len == 0) return error.InvalidExpression;
-                    const value: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+                    const value: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
                     self.stack.items[self.stack.items.len - 1] = .{
                         .generic = std.math.absCast(value),
                     };
@@ -478,10 +478,10 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 },
                 OP.div => {
                     if (self.stack.items.len < 2) return error.InvalidExpression;
-                    const a: isize = @bitCast(try self.stack.pop().asIntegral());
-                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+                    const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
+                    const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
                     self.stack.items[self.stack.items.len - 1] = .{
-                        .generic = @bitCast(try std.math.divTrunc(isize, b, a)),
+                        .generic = @bitCast(try std.math.divTrunc(addr_type_signed, b, a)),
                     };
                 },
                 OP.minus => {
@@ -493,16 +493,16 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 },
                 OP.mod => {
                     if (self.stack.items.len < 2) return error.InvalidExpression;
-                    const a: isize = @bitCast(try self.stack.pop().asIntegral());
-                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+                    const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
+                    const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
                     self.stack.items[self.stack.items.len - 1] = .{
                         .generic = @bitCast(@mod(b, a)),
                     };
                 },
                 OP.mul => {
                     if (self.stack.items.len < 2) return error.InvalidExpression;
-                    const a: isize = @bitCast(try self.stack.pop().asIntegral());
-                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+                    const a: addr_type_signed = @bitCast(try self.stack.pop().asIntegral());
+                    const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
                     self.stack.items[self.stack.items.len - 1] = .{
                         .generic = @bitCast(@mulWithOverflow(a, b)[0]),
                     };
@@ -512,7 +512,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     self.stack.items[self.stack.items.len - 1] = .{
                         .generic = @bitCast(
                             try std.math.negate(
-                                @as(isize, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
+                                @as(addr_type_signed, @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral())),
                             ),
                         ),
                     };
@@ -563,9 +563,9 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                 OP.shra => {
                     if (self.stack.items.len < 2) return error.InvalidExpression;
                     const a = try self.stack.pop().asIntegral();
-                    const b: isize = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
+                    const b: addr_type_signed = @bitCast(try self.stack.items[self.stack.items.len - 1].asIntegral());
                     self.stack.items[self.stack.items.len - 1] = .{
-                        .generic = @bitCast(std.math.shr(isize, b, a)),
+                        .generic = @bitCast(std.math.shr(addr_type_signed, b, a)),
                     };
                 },
                 OP.xor => {
@@ -589,8 +589,8 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     const b = self.stack.items[self.stack.items.len - 1];
 
                     if (a == .generic and b == .generic) {
-                        const a_int: isize = @bitCast(a.asIntegral() catch unreachable);
-                        const b_int: isize = @bitCast(b.asIntegral() catch unreachable);
+                        const a_int: addr_type_signed = @bitCast(a.asIntegral() catch unreachable);
+                        const b_int: addr_type_signed = @bitCast(b.asIntegral() catch unreachable);
                         const result = @intFromBool(switch (opcode) {
                             OP.le => b_int < a_int,
                             OP.ge => b_int >= a_int,
@@ -617,7 +617,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
                     if (condition) {
                         const new_pos = std.math.cast(
                             usize,
-                            try std.math.add(isize, @as(isize, @intCast(stream.pos)), branch_offset),
+                            try std.math.add(addr_type_signed, @as(addr_type_signed, @intCast(stream.pos)), branch_offset),
                         ) orelse return error.InvalidExpression;
 
                         if (new_pos < 0 or new_pos >= stream.buffer.len) return error.InvalidExpression;
@@ -710,7 +710,7 @@ pub fn StackMachine(comptime options: ExpressionOptions) type {
     };
 }
 
-pub fn Writer(options: ExpressionOptions) type {
+pub fn Builder(comptime options: ExpressionOptions) type {
     const addr_type = switch (options.addr_size) {
         2 => u16,
         4 => u32,
@@ -959,10 +959,33 @@ fn opcodeValidInCFA(opcode: u8) bool {
     };
 }
 
+const testing = std.testing;
 test "DWARF expressions" {
     const allocator = std.testing.allocator;
 
     const options = ExpressionOptions{};
-    const stack_machine = StackMachine(options){};
+    var stack_machine = StackMachine(options){};
     defer stack_machine.deinit(allocator);
+
+    const b = Builder(options);
+
+    var program = std.ArrayList(u8).init(allocator);
+    defer program.deinit();
+
+    const writer = program.writer();
+
+    // Literals
+    {
+        const context = ExpressionContext{};
+        for (0..32) |i| {
+            try b.writeLiteral(writer, @intCast(i));
+        }
+
+        _ = try stack_machine.run(program.items, allocator, context, 0);
+
+        for (0..32) |i| {
+            const expected = 31 - i;
+            try testing.expectEqual(expected, stack_machine.stack.popOrNull().?.generic);
+        }
+    }
 }
lib/std/debug.zig
@@ -133,7 +133,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
     }
 }
 
-pub const StackTraceContext = blk: {
+pub const ThreadContext = blk: {
     if (native_os == .windows) {
         break :blk std.os.windows.CONTEXT;
     } else if (have_ucontext) {
@@ -146,7 +146,7 @@ pub const StackTraceContext = blk: {
 /// Tries to print the stack trace starting from the supplied base pointer to stderr,
 /// unbuffered, and ignores any error returned.
 /// TODO multithreaded awareness
-pub fn dumpStackTraceFromBase(context: *const StackTraceContext) void {
+pub fn dumpStackTraceFromBase(context: *const ThreadContext) void {
     nosuspend {
         if (comptime builtin.target.isWasm()) {
             if (native_os == .wasi) {
@@ -437,7 +437,7 @@ pub const have_ucontext = @hasDecl(os.system, "ucontext_t") and
     else => true,
 });
 
-pub inline fn getContext(context: *StackTraceContext) bool {
+pub inline fn getContext(context: *ThreadContext) bool {
     if (native_os == .windows) {
         context.* = std.mem.zeroes(windows.CONTEXT);
         windows.ntdll.RtlCaptureContext(context);
@@ -606,8 +606,8 @@ pub const StackIterator = struct {
     fn next_dwarf(self: *StackIterator) !usize {
         const module = try self.debug_info.?.getModuleForAddress(self.dwarf_context.pc);
         if (try module.getDwarfInfoForAddress(self.debug_info.?.allocator, self.dwarf_context.pc)) |di| {
-            self.dwarf_context.reg_ctx.eh_frame = true;
-            self.dwarf_context.reg_ctx.is_macho = di.is_macho;
+            self.dwarf_context.reg_context.eh_frame = true;
+            self.dwarf_context.reg_context.is_macho = di.is_macho;
             return di.unwindFrame(&self.dwarf_context, module.base_address);
         } else return error.MissingDebugInfo;
     }
@@ -663,7 +663,7 @@ pub fn writeCurrentStackTrace(
     tty_config: io.tty.Config,
     start_addr: ?usize,
 ) !void {
-    var context: StackTraceContext = undefined;
+    var context: ThreadContext = undefined;
     const has_context = getContext(&context);
     if (native_os == .windows) {
         return writeStackTraceWindows(out_stream, debug_info, tty_config, &context, start_addr);
lib/std/dwarf.zig
@@ -146,11 +146,11 @@ pub const CC = enum(u8) {
     pass_by_reference = 0x4,
     pass_by_value = 0x5,
 
-    lo_user = 0x40,
-    hi_user = 0xff,
-
     GNU_renesas_sh = 0x40,
     GNU_borland_fastcall_i386 = 0x41,
+
+    pub const lo_user = 0x40;
+    pub const hi_user = 0xff;
 };
 
 pub const Format = enum { @"32", @"64" };
@@ -1676,13 +1676,13 @@ pub const DwarfInfo = struct {
         var expression_context = .{
             .isValidMemory = context.isValidMemory,
             .compile_unit = di.findCompileUnit(fde.pc_begin) catch null,
-            .ucontext = &context.ucontext,
-            .reg_ctx = context.reg_ctx,
+            .thread_context = &context.thread_context,
+            .reg_context = context.reg_context,
             .cfa = context.cfa,
         };
 
         context.vm.reset();
-        context.reg_ctx.eh_frame = cie.version != 4;
+        context.reg_context.eh_frame = cie.version != 4;
 
         _ = try context.vm.runToNative(context.allocator, mapped_pc, cie, fde);
         const row = &context.vm.current_row;
@@ -1690,7 +1690,7 @@ pub const DwarfInfo = struct {
         context.cfa = switch (row.cfa.rule) {
             .val_offset => |offset| blk: {
                 const register = row.cfa.register orelse return error.InvalidCFARule;
-                const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, register, context.reg_ctx));
+                const value = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, register, context.reg_context));
                 break :blk try call_frame.applyOffset(value, offset);
             },
             .expression => |expression| blk: {
@@ -1711,14 +1711,14 @@ pub const DwarfInfo = struct {
         if (!context.isValidMemory(context.cfa.?)) return error.InvalidCFA;
         expression_context.cfa = context.cfa;
 
-        // Buffering the modifications is done because copying the ucontext is not portable,
+        // Buffering the modifications is done because copying the thread context is not portable,
         // some implementations (ie. darwin) use internal pointers to the mcontext.
         var arena = std.heap.ArenaAllocator.init(context.allocator);
         defer arena.deinit();
         const update_allocator = arena.allocator();
 
         const RegisterUpdate = struct {
-            // Backed by ucontext
+            // Backed by thread_context
             old_value: []u8,
             // Backed by arena
             new_value: []const u8,
@@ -1733,7 +1733,7 @@ pub const DwarfInfo = struct {
                     has_next_ip = column.rule != .undefined;
                 }
 
-                const old_value = try abi.regBytes(&context.ucontext, register, context.reg_ctx);
+                const old_value = try abi.regBytes(&context.thread_context, register, context.reg_context);
                 const new_value = try update_allocator.alloc(u8, old_value.len);
 
                 const prev = update_tail;
@@ -1758,12 +1758,12 @@ pub const DwarfInfo = struct {
         }
 
         if (has_next_ip) {
-            context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.ucontext, comptime abi.ipRegNum(), context.reg_ctx));
+            context.pc = mem.readIntSliceNative(usize, try abi.regBytes(&context.thread_context, comptime abi.ipRegNum(), context.reg_context));
         } else {
             context.pc = 0;
         }
 
-        mem.writeIntSliceNative(usize, try abi.regBytes(&context.ucontext, abi.spRegNum(context.reg_ctx), context.reg_ctx), context.cfa.?);
+        mem.writeIntSliceNative(usize, try abi.regBytes(&context.thread_context, abi.spRegNum(context.reg_context), context.reg_context), context.cfa.?);
 
         // The call instruction will have pushed the address of the instruction that follows the call as the return address
         // However, this return address may be past the end of the function if the caller was `noreturn`.
@@ -1779,20 +1779,24 @@ pub const UnwindContext = struct {
     allocator: mem.Allocator,
     cfa: ?usize,
     pc: usize,
-    ucontext: os.ucontext_t,
-    reg_ctx: abi.RegisterContext,
+    thread_context: debug.ThreadContext,
+    reg_context: abi.RegisterContext,
     isValidMemory: *const fn (address: usize) bool,
     vm: call_frame.VirtualMachine = .{},
     stack_machine: expressions.StackMachine(.{ .call_frame_context = true }) = .{},
 
-    pub fn init(allocator: mem.Allocator, ucontext: *const os.ucontext_t, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
-        const pc = mem.readIntSliceNative(usize, try abi.regBytes(ucontext, abi.ipRegNum(), null));
+    pub fn init(allocator: mem.Allocator, thread_context: *const debug.ThreadContext, isValidMemory: *const fn (address: usize) bool) !UnwindContext {
+        const pc = mem.readIntSliceNative(usize, try abi.regBytes(thread_context, abi.ipRegNum(), null));
+
+        if (builtin.os.tag == .macos) @compileError("Fix below TODO");
+
         return .{
             .allocator = allocator,
             .cfa = null,
             .pc = pc,
-            .ucontext = ucontext.*,
-            .reg_ctx = undefined,
+            // TODO: This is broken on macos, need a function that knows how to copy the OSs mcontext properly
+            .thread_context = thread_context.*,
+            .reg_context = undefined,
             .isValidMemory = isValidMemory,
         };
     }
@@ -1803,7 +1807,7 @@ pub const UnwindContext = struct {
     }
 
     pub fn getFp(self: *const UnwindContext) !usize {
-        return mem.readIntSliceNative(usize, try abi.regBytes(&self.ucontext, abi.fpRegNum(self.reg_ctx), self.reg_ctx));
+        return mem.readIntSliceNative(usize, try abi.regBytes(&self.thread_context, abi.fpRegNum(self.reg_context), self.reg_context));
     }
 };
 
@@ -2388,3 +2392,7 @@ fn pcRelBase(field_ptr: usize, pc_rel_offset: i64) !usize {
         return math.add(usize, field_ptr, @as(usize, @intCast(pc_rel_offset)));
     }
 }
+
+test {
+    std.testing.refAllDecls(@This());
+}
src/crash_report.zig
@@ -271,7 +271,7 @@ const StackContext = union(enum) {
     current: struct {
         ret_addr: ?usize,
     },
-    exception: *const debug.StackTraceContext,
+    exception: *const debug.ThreadContext,
     not_supported: void,
 
     pub fn dumpStackTrace(ctx: @This()) void {
test/standalone/dwarf_unwinding/shared_lib_unwind.zig
@@ -5,7 +5,7 @@ const testing = std.testing;
 noinline fn frame4(expected: *[4]usize, unwound: *[4]usize) void {
     expected[0] = @returnAddress();
 
-    var context: debug.StackTraceContext = undefined;
+    var context: debug.ThreadContext = undefined;
     testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
 
     var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");
test/standalone/dwarf_unwinding/zig_unwind.zig
@@ -5,7 +5,7 @@ const testing = std.testing;
 noinline fn frame3(expected: *[4]usize, unwound: *[4]usize) void {
     expected[0] = @returnAddress();
 
-    var context: debug.StackTraceContext = undefined;
+    var context: debug.ThreadContext = undefined;
     testing.expect(debug.getContext(&context)) catch @panic("failed to getContext");
 
     var debug_info = debug.getSelfDebugInfo() catch @panic("failed to openSelfDebugInfo");