Zig 0.17.0-dev (Split by item)

This is an example of documentation generated by ZigDoc, an alternative to Zig's built-in Auto Doc feature. See also examples in other modes/formats. The project being documented here (as the example) is the Zig library itself.

handleSegfaultPosix

debug.handleSegfaultPosix
fn handleSegfaultPosix(sig: posix.SIG, info: *const posix.siginfo_t, ctx_ptr: ?*anyopaque) callconv(.c) noreturn

File

lib/std/debug.zig:1589

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) {
            // x86_64 doesn't have a full 64-bit virtual address space.
            // 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()) {
        // It's unclear to me whether this is a QEMU bug or also real kernel behavior, but in the
        // 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);
}