feature. See also
. The project being documented here (as the example) is the Zig library itself.
debug.handleSegfaultPosix
fn handleSegfaultPosix(sig: posix.SIG, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) noreturn
File
Code
fn handleSegfaultPosix(sig: posix.SIG, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) noreturn {
if (use_trap_panic) @trap();
const addr: ?usize, const name: []const u8 = info: {
if (native_os == .linux and native_arch == .x86_64) {
// Addresses outside of that address space are non-canonical
// and the CPU won't provide the faulting address to us.
// This happens when accessing memory addresses such as 0xaaaaaaaaaaaaaaaa
// but can also happen when no addressable memory is involved;
// for example when reading/writing model-specific registers
// by executing `rdmsr` or `wrmsr` in user-space (unprivileged mode).
const SI_KERNEL = 0x80;
if (sig == .SEGV and info.code == SI_KERNEL) {
break :info .{ null, "General protection exception" };
}
}
const addr: usize = switch (native_os) {
.serenity,
.dragonfly,
.freebsd,
.driverkit,
.ios,
.maccatalyst,
.macos,
.tvos,
.visionos,
.watchos,
.haiku,
=> @intFromPtr(info.addr),
.linux,
=> @intFromPtr(info.fields.sigfault.addr),
.netbsd,
=> @intFromPtr(info.info.reason.fault.addr),
.openbsd,
=> @intFromPtr(info.data.fault.addr),
.illumos,
=> @intFromPtr(info.reason.fault.addr),
else => comptime unreachable,
};
const name = switch (sig) {
.SEGV => "Segmentation fault",
.ILL => "Illegal instruction",
.BUS => "Bus error",
.FPE => "Arithmetic exception",
else => unreachable,
};
break :info .{ addr, name };
};
const opt_cpu_context: ?cpu_context.Native = cpu_context.fromPosixSignalContext(ctx_ptr);
if (native_arch.isSPARC()) {
// former, I observed that the most recent register window wasn't getting spilled on the
// stack as expected when a signal arrived. A `flushw` from the signal handler does not
// appear to be sufficient either. On the other hand, when doing a synchronous stack trace
// and using `flushw`, this all appears to work as expected. So, *probably* a QEMU bug, but
// someone with real SPARC hardware should verify.
//
// In any case, the register save area exists specifically so that register windows can be
// spilled asynchronously. This means that it should be perfectly fine for us to manually do
// so here.
const ctx = opt_cpu_context.?;
@as(*[16]usize, @ptrFromInt(ctx.o[6] + StackIterator.stack_bias)).* = ctx.l ++ ctx.i;
}
handleSegfault(addr, name, if (opt_cpu_context) |*ctx| ctx else null);
}