Commit 1a1598c58c
std/os/bits/linux/x86_64.zig
@@ -1,5 +1,10 @@
// x86-64-specific declarations that are intended to be imported into the POSIX namespace.
const std = @import("../../../std.zig");
+const pid_t = linux.pid_t;
+const uid_t = linux.uid_t;
+const clock_t = linux.clock_t;
+const stack_t = linux.stack_t;
+const sigset_t = linux.sigset_t;
const linux = std.os.linux;
const sockaddr = linux.sockaddr;
@@ -407,6 +412,30 @@ pub const ARCH_SET_FS = 0x1002;
pub const ARCH_GET_FS = 0x1003;
pub const ARCH_GET_GS = 0x1004;
+pub const REG_R8 = 0;
+pub const REG_R9 = 1;
+pub const REG_R10 = 2;
+pub const REG_R11 = 3;
+pub const REG_R12 = 4;
+pub const REG_R13 = 5;
+pub const REG_R14 = 6;
+pub const REG_R15 = 7;
+pub const REG_RDI = 8;
+pub const REG_RSI = 9;
+pub const REG_RBP = 10;
+pub const REG_RBX = 11;
+pub const REG_RDX = 12;
+pub const REG_RAX = 13;
+pub const REG_RCX = 14;
+pub const REG_RSP = 15;
+pub const REG_RIP = 16;
+pub const REG_EFL = 17;
+pub const REG_CSGSFS = 18;
+pub const REG_ERR = 19;
+pub const REG_TRAPNO = 20;
+pub const REG_OLDMASK = 21;
+pub const REG_CR2 = 22;
+
pub const msghdr = extern struct {
msg_name: ?*sockaddr,
msg_namelen: socklen_t,
@@ -468,3 +497,129 @@ pub const timezone = extern struct {
};
pub const Elf_Symndx = u32;
+
+pub const sigval = extern union {
+ int: i32,
+ ptr: *c_void,
+};
+
+pub const siginfo_t = extern struct {
+ signo: i32,
+ errno: i32,
+ code: i32,
+ fields: extern union {
+ pad: [128 - 2 * @sizeOf(c_int) - @sizeOf(c_long)]u8,
+ common: extern struct {
+ first: extern union {
+ piduid: extern struct {
+ pid: pid_t,
+ uid: uid_t,
+ },
+ timer: extern struct {
+ timerid: i32,
+ overrun: i32,
+ },
+ },
+ second: extern union {
+ value: sigval,
+ sigchld: extern struct {
+ status: i32,
+ utime: clock_t,
+ stime: clock_t,
+ },
+ },
+ },
+ sigfault: extern struct {
+ addr: *c_void,
+ addr_lsb: i16,
+ first: extern union {
+ addr_bnd: extern struct {
+ lower: *c_void,
+ upper: *c_void,
+ },
+ pkey: u32,
+ },
+ },
+ sigpoll: extern struct {
+ band: isize,
+ fd: i32,
+ },
+ sigsys: extern struct {
+ call_addr: *c_void,
+ syscall: i32,
+ arch: u32,
+ },
+ },
+};
+
+pub const greg_t = usize;
+pub const gregset_t = [23]greg_t;
+pub const fpstate = extern struct {
+ cwd: u16,
+ swd: u16,
+ ftw: u16,
+ fop: u16,
+ rip: usize,
+ rdp: usize,
+ mxcsr: u32,
+ mxcr_mask: u32,
+ st: [8]extern struct {
+ significand: [4]u16,
+ exponent: u16,
+ padding: [3]u16 = undefined,
+ },
+ xmm: [16]extern struct {
+ element: [4]u32,
+ },
+ padding: [24]u32 = undefined,
+};
+pub const fpregset_t = *fpstate;
+pub const sigcontext = extern struct {
+ r8: usize,
+ r9: usize,
+ r10: usize,
+ r11: usize,
+ r12: usize,
+ r13: usize,
+ r14: usize,
+ r15: usize,
+
+ rdi: usize,
+ rsi: usize,
+ rbp: usize,
+ rbx: usize,
+ rdx: usize,
+ rax: usize,
+ rcx: usize,
+ rsp: usize,
+ rip: usize,
+ eflags: usize,
+
+ cs: u16,
+ gs: u16,
+ fs: u16,
+ pad0: u16 = undefined,
+
+ err: usize,
+ trapno: usize,
+ oldmask: usize,
+ cr2: usize,
+
+ fpstate: *fpstate,
+ reserved1: [8]usize = undefined,
+};
+
+pub const mcontext_t = extern struct {
+ gregs: gregset_t,
+ fpregs: fpregset_t,
+ reserved1: [8]usize = undefined,
+};
+
+pub const ucontext_t = extern struct {
+ flags: usize,
+ link: *ucontext_t,
+ stack: stack_t,
+ mcontext: mcontext_t,
+ sigmask: sigset_t,
+ fpregs_mem: [64]usize,
+};
std/os/bits/linux.zig
@@ -12,6 +12,8 @@ pub usingnamespace switch (builtin.arch) {
pub const pid_t = i32;
pub const fd_t = i32;
+pub const uid_t = i32;
+pub const clock_t = isize;
pub const PATH_MAX = 4096;
pub const IOV_MAX = 1024;
@@ -712,22 +714,23 @@ pub const all_mask = [_]u32{ 0xffffffff, 0xffffffff };
pub const app_mask = [_]u32{ 0xfffffffc, 0x7fffffff };
pub const k_sigaction = extern struct {
- handler: extern fn (i32) void,
+ sigaction: ?extern fn (i32, *siginfo_t, *c_void) void,
flags: usize,
restorer: extern fn () void,
mask: [2]u32,
};
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
-pub const Sigaction = struct {
- handler: extern fn (i32) void,
+pub const Sigaction = extern struct {
+ sigaction: ?extern fn (i32, *siginfo_t, *c_void) void,
mask: sigset_t,
flags: u32,
+ restorer: ?extern fn () void = null,
};
-pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize));
-pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0);
-pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1);
+pub const SIG_ERR = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, maxInt(usize));
+pub const SIG_DFL = @intToPtr(?extern fn (i32, *siginfo_t, *c_void) void, 0);
+pub const SIG_IGN = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, 1);
pub const empty_sigset = [_]usize{0} ** sigset_t.len;
pub const in_port_t = u16;
std/os/linux.zig
@@ -542,7 +542,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
assert(sig != SIGKILL);
assert(sig != SIGSTOP);
var ksa = k_sigaction{
- .handler = act.handler,
+ .sigaction = act.sigaction,
.flags = act.flags | SA_RESTORER,
.mask = undefined,
.restorer = @ptrCast(extern fn () void, restore_rt),
@@ -555,7 +555,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
return result;
}
if (oact) |old| {
- old.handler = ksa_old.handler;
+ old.sigaction = ksa_old.sigaction;
old.flags = @truncate(u32, ksa_old.flags);
@memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask)));
}
std/special/start.zig
@@ -99,6 +99,15 @@ fn posixCallMainAndExit() noreturn {
inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
std.os.argv = argv[0..argc];
std.os.environ = envp;
+
+ const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
+ root.enable_segfault_handler
+ else
+ std.debug.runtime_safety and std.debug.have_segfault_handling_support;
+ if (enable_segfault_handler) {
+ std.debug.attachSegfaultHandler();
+ }
+
return callMain();
}
std/debug.zig
@@ -99,6 +99,32 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
};
}
+/// 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(bp: usize, ip: usize) void {
+ const stderr = getStderrStream() catch return;
+ if (builtin.strip_debug_info) {
+ stderr.print("Unable to dump stack trace: debug info stripped\n") catch return;
+ return;
+ }
+ const debug_info = getSelfDebugInfo() catch |err| {
+ stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
+ return;
+ };
+ const tty_color = wantTtyColor();
+ printSourceAtAddress(debug_info, stderr, ip, tty_color) catch return;
+ const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*;
+ printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_color) catch return;
+ var it = StackIterator{
+ .first_addr = null,
+ .fp = bp,
+ };
+ while (it.next()) |return_address| {
+ printSourceAtAddress(debug_info, stderr, return_address - 1, tty_color) catch return;
+ }
+}
+
/// Returns a slice with the same pointer as addresses, with a potentially smaller len.
/// On Windows, when first_address is not null, we ask for at least 32 stack frames,
/// and then try to find the first address. If addresses.len is more than 32, we
@@ -2291,3 +2317,44 @@ fn getDebugInfoAllocator() *mem.Allocator {
debug_info_allocator = &debug_info_arena_allocator.allocator;
return &debug_info_arena_allocator.allocator;
}
+
+/// Whether or not the current target can print useful debug information when a segfault occurs.
+pub const have_segfault_handling_support = builtin.arch == .x86_64 and builtin.os == .linux;
+
+/// Attaches a global SIGSEGV handler which calls @panic("segmentation fault");
+pub fn attachSegfaultHandler() void {
+ if (!have_segfault_handling_support) {
+ @compileError("segfault handler not supported for this target");
+ }
+ var act = os.Sigaction{
+ .sigaction = handleSegfault,
+ .mask = os.empty_sigset,
+ .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND),
+ };
+
+ os.sigaction(os.SIGSEGV, &act, null);
+}
+
+extern fn handleSegfault(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) 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.
+ var act = os.Sigaction{
+ .sigaction = os.SIG_DFL,
+ .mask = os.empty_sigset,
+ .flags = 0,
+ };
+ os.sigaction(os.SIGSEGV, &act, null);
+
+ const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
+ const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]);
+ const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]);
+ const addr = @ptrToInt(info.fields.sigfault.addr);
+ std.debug.warn("Segmentation fault at address 0x{x}\n", addr);
+ dumpStackTraceFromBase(bp, ip);
+
+ // We cannot allow the signal handler to return because when it runs the original instruction
+ // again, the memory may be mapped and undefined behavior would occur rather than repeating
+ // the segfault. So we simply abort here.
+ os.abort();
+}
std/os.zig
@@ -2529,6 +2529,16 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
}
}
+/// Examine and change a signal action.
+pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void {
+ switch (errno(system.sigaction(sig, act, oact))) {
+ 0 => return,
+ EFAULT => unreachable,
+ EINVAL => unreachable,
+ else => unreachable,
+ }
+}
+
test "" {
_ = @import("os/darwin.zig");
_ = @import("os/freebsd.zig");