Commit afd1f7ed47

John Schmidt <john.schmidt.h@gmail.com>
2022-01-17 17:46:48
Implement segfault handler for macOS x86_64
1 parent 2ac7aef
Changed files (3)
lib/std/c/darwin.zig
@@ -478,6 +478,51 @@ pub const SIG = struct {
     pub const USR2 = 31;
 };
 
+pub const ucontext_t = extern struct {
+    onstack: c_int,
+    sigmask: sigset_t,
+    stack: stack_t,
+    link: ?*ucontext_t,
+    mcsize: u64,
+    mcontext: *mcontext_t,
+};
+
+pub const exception_state = extern struct {
+    trapno: u16,
+    cpu: u16,
+    err: u32,
+    faultvaddr: u64,
+};
+
+pub const thread_state = extern struct {
+    rax: u64,
+    rbx: u64,
+    rcx: u64,
+    rdx: u64,
+    rdi: u64,
+    rsi: u64,
+    rbp: u64,
+    rsp: u64,
+    r8: u64,
+    r9: u64,
+    r10: u64,
+    r11: u64,
+    r12: u64,
+    r13: u64,
+    r14: u64,
+    r15: u64,
+    rip: u64,
+    rflags: u64,
+    cs: u64,
+    fs: u64,
+    gs: u64,
+};
+
+pub const mcontext_t = extern struct {
+    es: exception_state,
+    ss: thread_state,
+};
+
 pub const siginfo_t = extern struct {
     signo: c_int,
     errno: c_int,
lib/std/debug.zig
@@ -1574,6 +1574,7 @@ fn getDebugInfoAllocator() mem.Allocator {
 /// Whether or not the current target can print useful debug information when a segfault occurs.
 pub const have_segfault_handling_support = switch (native_os) {
     .linux, .netbsd, .solaris => true,
+    .macos => native_arch == .x86_64,
     .windows => true,
     .freebsd, .openbsd => @hasDecl(os.system, "ucontext_t"),
     else => false,
@@ -1601,7 +1602,7 @@ pub fn attachSegfaultHandler() void {
         return;
     }
     var act = os.Sigaction{
-        .handler = .{ .sigaction = handleSegfaultLinux },
+        .handler = .{ .sigaction = handleSegfaultPosix },
         .mask = os.empty_sigset,
         .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND),
     };
@@ -1629,7 +1630,7 @@ fn resetSegfaultHandler() void {
     os.sigaction(os.SIG.BUS, &act, null);
 }
 
-fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
+fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
     // Reset to the default handler so that if a segfault happens in this handler it will crash
     // the process. Also when this handler returns, the original instruction will be repeated
     // and the resulting segfault will crash the process rather than continually dump stack traces.
@@ -1637,7 +1638,7 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
 
     const addr = switch (native_os) {
         .linux => @ptrToInt(info.fields.sigfault.addr),
-        .freebsd => @ptrToInt(info.addr),
+        .freebsd, .macos => @ptrToInt(info.addr),
         .netbsd => @ptrToInt(info.info.reason.fault.addr),
         .openbsd => @ptrToInt(info.data.fault.addr),
         .solaris => @ptrToInt(info.reason.fault.addr),
@@ -1668,12 +1669,14 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
                 .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]),
                 .freebsd => @intCast(usize, ctx.mcontext.rip),
                 .openbsd => @intCast(usize, ctx.sc_rip),
+                .macos => @intCast(usize, ctx.mcontext.ss.rip),
                 else => unreachable,
             };
             const bp = switch (native_os) {
                 .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]),
                 .openbsd => @intCast(usize, ctx.sc_rbp),
                 .freebsd => @intCast(usize, ctx.mcontext.rbp),
+                .macos => @intCast(usize, ctx.mcontext.ss.rbp),
                 else => unreachable,
             };
             dumpStackTraceFromBase(bp, ip);
src/crash_report.zig
@@ -169,7 +169,7 @@ pub fn attachSegfaultHandler() void {
         return;
     }
     var act = os.Sigaction{
-        .handler = .{ .sigaction = handleSegfaultLinux },
+        .handler = .{ .sigaction = handleSegfaultPosix },
         .mask = os.empty_sigset,
         .flags = (os.SA.SIGINFO | os.SA.RESTART | os.SA.RESETHAND),
     };
@@ -179,17 +179,17 @@ pub fn attachSegfaultHandler() void {
     os.sigaction(os.SIG.BUS, &act, null);
 }
 
-fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
+fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const anyopaque) callconv(.C) noreturn {
     // TODO: use alarm() here to prevent infinite loops
     PanicSwitch.preDispatch();
 
     const addr = switch (builtin.os.tag) {
         .linux => @ptrToInt(info.fields.sigfault.addr),
-        .freebsd => @ptrToInt(info.addr),
+        .freebsd, .macos => @ptrToInt(info.addr),
         .netbsd => @ptrToInt(info.info.reason.fault.addr),
         .openbsd => @ptrToInt(info.data.fault.addr),
         .solaris => @ptrToInt(info.reason.fault.addr),
-        else => @compileError("TODO implement handleSegfaultLinux for new linux OS"),
+        else => @compileError("TODO implement handleSegfaultPosix for new POSIX OS"),
     };
 
     var err_buffer: [128]u8 = undefined;
@@ -213,12 +213,14 @@ fn handleSegfaultLinux(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
                 .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RIP]),
                 .freebsd => @intCast(usize, ctx.mcontext.rip),
                 .openbsd => @intCast(usize, ctx.sc_rip),
+                .macos => @intCast(usize, ctx.mcontext.ss.rip),
                 else => unreachable,
             };
             const bp = switch (builtin.os.tag) {
                 .linux, .netbsd, .solaris => @intCast(usize, ctx.mcontext.gregs[os.REG.RBP]),
                 .openbsd => @intCast(usize, ctx.sc_rbp),
                 .freebsd => @intCast(usize, ctx.mcontext.rbp),
+                .macos => @intCast(usize, ctx.mcontext.ss.rbp),
                 else => unreachable,
             };
             break :ctx StackContext{ .exception = .{ .bp = bp, .ip = ip } };