Skip to content

Enable stack traces on segfault in stage1 on linux #2355

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6701,6 +6701,10 @@ add_custom_command(
DEPENDS
"${CMAKE_SOURCE_DIR}/src-self-hosted/stage1.zig"
"${CMAKE_SOURCE_DIR}/src-self-hosted/translate_c.zig"
"${CMAKE_SOURCE_DIR}/src-self-hosted/segv_handler/handler.zig"
"${CMAKE_SOURCE_DIR}/src-self-hosted/segv_handler/c.zig"
"${CMAKE_SOURCE_DIR}/src-self-hosted/segv_handler/x86_64-linux.zig"
"${CMAKE_SOURCE_DIR}/src-self-hosted/segv_handler/i386-linux.zig"
"${CMAKE_SOURCE_DIR}/build.zig"
)
add_custom_target(userland_target DEPENDS "${LIBUSERLAND}")
Expand Down
45 changes: 45 additions & 0 deletions src-self-hosted/segv_handler/c.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
const arch = switch (@import("builtin").arch) {
.x86_64 => @import("x86_64-linux.zig"),
.i386 => @import("i386-linux.zig"),
else => struct {},
};

pub const SIGSEGV = 11;
pub const SA_SIGINFO = (1 << 2); // 4
pub const SA_RESTART = (1 << 28); // 268435456
pub const SA_NODEFER = (1 << 30); // 1073741824
pub const REG_BP = arch.REG_BP;
pub const REG_SP = arch.REG_SP;
pub const REG_IP = arch.REG_IP;

pub const siginfo_t = arch.siginfo_t;
pub const sigset_t = arch.sigset_t;

pub const sigaction_t = extern struct {
sa_handler: extern union {
sa_handler: ?extern fn(c_int) void,
sa_sigaction: ?extern fn(c_int, ?*siginfo_t, ?*c_void) void,
},
sa_mask: sigset_t,
sa_flags: c_int,
sa_restorer: ?extern fn() void,
};

const stack_t = extern struct {
ss_sp: ?*c_void,
ss_flags: c_int,
ss_size: usize,
};

pub const ucontext_t = extern struct {
uc_flags: c_ulong,
uc_link: [*c]ucontext_t,
uc_stack: stack_t,
uc_mcontext: arch.mcontext_t,
uc_sigmask: sigset_t,
__fpregs_mem: arch._libc_fpstate,
__ssp: [4]arch.__ssp_type,
};

pub extern fn sigemptyset(set: *sigset_t) c_int;
pub extern fn sigaction(signum: c_int, noalias act: ?*const sigaction_t, noalias oldact: ?*sigaction_t) c_int;
65 changes: 65 additions & 0 deletions src-self-hosted/segv_handler/handler.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
const std = @import("std");
const c = @import("c.zig");

pub fn attach() void {
var act: c.sigaction_t = c.sigaction_t {
.sa_handler = undefined,
.sa_mask = undefined,
.sa_flags = (c.SA_SIGINFO | c.SA_RESTART | c.SA_NODEFER),
.sa_restorer = null,
};

act.sa_handler.sa_sigaction = handler;
_ = c.sigemptyset(&act.sa_mask);

_ = c.sigaction(c.SIGSEGV, &act, null);
}

var segfaulted: u8 = 0;

extern fn handler(sig: c_int, info: ?*const c.siginfo_t, ctx_ptr: ?*const c_void) noreturn {
const ctx = @ptrCast(?*const c.ucontext_t, @alignCast(@alignOf(*c.ucontext_t), ctx_ptr));

const ip = @intCast(usize, ctx.?.uc_mcontext.gregs[c.REG_IP]);
const bp = @intCast(usize, ctx.?.uc_mcontext.gregs[c.REG_BP]);
const sp = @intCast(usize, ctx.?.uc_mcontext.gregs[c.REG_SP]);
const addr = @ptrToInt(info.?._si_fields._sigfault.si_addr);

std.debug.warn(
\\Received SIGSEGV at instruction 0x{x} (addr=0x{x})
\\Frame address: 0x{x}
\\Stack address: 0x{x}
\\
, ip, addr, bp, sp);

if (@atomicRmw(u8, &segfaulted, .Xchg, 1, .SeqCst) == 1) {
// Segfaulted while handling sigsegv.
std.os.abort();
}

// Using some tricks we can link stacks
// and safely unwind from here

// a call to panic needs a big stack
var buf: [2560]usize = undefined;
const newStack = @sliceToBytes(buf[0..]);
@newStackCall(newStack, entryPanic, bp, ip);
}

inline fn entryPanic(bp: usize, ip: usize) noreturn {
@noInlineCall(segvPanic, bp, ip);
}

fn segvPanic(bp: usize, ip: usize) noreturn {
// Does this ensure that %rbp is pushed onto the stack
// and %rsp is moved to %rbp?
const frame_addr = @frameAddress();

// replace frame pointer
@intToPtr(*usize, frame_addr).* = bp;

// replace instruction pointer
@intToPtr(*usize, frame_addr + @sizeOf(usize)).* = ip;

@panic(""); // Segmentation fault
}
96 changes: 96 additions & 0 deletions src-self-hosted/segv_handler/i386-linux.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
pub const REG_BP = 6; // ebp
pub const REG_SP = 7; // esp
pub const REG_IP = 14; // eip

const clock_t = c_long;
const uid_t = c_uint;
const pid_t = c_int;

const sigval_t = extern union {
sival_int: c_int,
sival_ptr: ?*c_void,
};

pub const sigset_t = extern struct {
__val: [32]c_ulong,
};

pub const siginfo_t = extern struct {
si_signo: c_int,
si_errno: c_int,
si_code: c_int,
_si_fields: extern union {
_pad: [29]c_int,
_kill: extern struct {
si_pid: pid_t,
si_uid: uid_t,
},
_timer: extern struct {
si_tid: c_int,
si_overrun: c_int,
si_sigval: sigval_t,
},
_rt: extern struct {
si_pid: pid_t,
si_uid: uid_t,
si_sigval: sigval_t,
},
_sigchld: extern struct {
si_pid: pid_t,
si_uid: uid_t,
si_status: c_int,
si_utime: clock_t,
si_stime: clock_t,
},
_sigfault: extern struct {
si_addr: ?*c_void,
si_addr_lsb: c_short,
_bounds: extern union {
_addr_bnd: extern struct {
_lower: ?*c_void,
_upper: ?*c_void,
},
_pkey: c_uint,
},
},
_sigpoll: extern struct {
si_band: c_long,
si_fd: c_int,
},
_sigsys: extern struct {
_call_addr: ?*c_void,
_syscall: c_int,
_arch: c_uint,
},
},
};

const _libc_fpreg = extern struct {
significand: [4]c_ushort,
exponent: c_ushort,
};

pub const _libc_fpstate = extern struct {
cw: c_ulong,
sw: c_ulong,
tag: c_ulong,
ipoff: c_ulong,
cssel: c_ulong,
dataoff: c_ulong,
datasel: c_ulong,
_st: [8]_libc_fpreg,
status: c_ulong,
};

const greg_t = c_int;
const gregset_t = [19]greg_t;
const fpregset_t = [*c]_libc_fpstate;

pub const mcontext_t = extern struct {
gregs: gregset_t,
fpregs: fpregset_t,
oldmask: c_ulong,
cr2: c_ulong,
};

pub const __ssp_type = c_ulong;
103 changes: 103 additions & 0 deletions src-self-hosted/segv_handler/x86_64-linux.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
pub const REG_BP = 10; // rbp
pub const REG_SP = 15; // rsp
pub const REG_IP = 16; // rip

const clock_t = c_long;
const uid_t = c_uint;
const pid_t = c_int;

const sigval_t = extern union {
sival_int: c_int,
sival_ptr: ?*c_void,
};

pub const sigset_t = extern struct {
__val: [16]c_ulong,
};

pub const siginfo_t = extern struct {
si_signo: c_int,
si_errno: c_int,
si_code: c_int,
__pad0: c_int,
_si_fields: extern union {
_pad: [28]c_int,
_kill: extern struct {
si_pid: pid_t,
si_uid: uid_t,
},
_timer: extern struct {
si_tid: c_int,
si_overrun: c_int,
si_sigval: sigval_t,
},
_rt: extern struct {
si_pid: pid_t,
si_uid: uid_t,
si_sigval: sigval_t,
},
_sigchld: extern struct {
si_pid: pid_t,
si_uid: uid_t,
si_status: c_int,
si_utime: clock_t,
si_stime: clock_t,
},
_sigfault: extern struct {
si_addr: ?*c_void,
si_addr_lsb: c_short,
_bounds: extern union {
_addr_bnd: extern struct {
_lower: ?*c_void,
_upper: ?*c_void,
},
_pkey: c_uint,
},
},
_sigpoll: extern struct {
si_band: c_long,
si_fd: c_int,
},
_sigsys: extern struct {
_call_addr: ?*c_void,
_syscall: c_int,
_arch: c_uint,
},
},
};

const _libc_fpxreg = extern struct {
significand: [4]c_ushort,
exponent: c_ushort,
__glibc_reserved1: [3]c_ushort,
};

const _libc_xmmreg = extern struct {
element: [4]c_uint,
};

pub const _libc_fpstate = extern struct {
cwd: c_ushort,
swd: c_ushort,
ftw: c_ushort,
fop: c_ushort,
rip: c_ulong,
rdp: c_ulong,
mxcsr: c_uint,
mxcr_mask: c_uint,
_st: [8]_libc_fpxreg,
_xmm: [16]_libc_xmmreg,
__glibc_reserved1: [24]c_uint,
};

const greg_t = c_longlong;
const gregset_t = [23]greg_t;
const fpregset_t = [*c]_libc_fpstate;

pub const mcontext_t = extern struct {
gregs: gregset_t,
fpregs: fpregset_t,
__reserved1: [8]c_ulonglong,
};

pub const __ssp_type = c_ulonglong;
7 changes: 7 additions & 0 deletions src-self-hosted/stage1.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ pub const info_zen =
\\
;

export fn stage2_attach_segv_handler() void {
const builtin = @import("builtin");
if (builtin.os == .linux and (builtin.arch == .x86_64 or builtin.arch == .i386)) {
@import("segv_handler/handler.zig").attach();
}
}

export fn stage2_zen(ptr: *[*]const u8, len: *usize) void {
ptr.* = &info_zen;
len.* = info_zen.len;
Expand Down
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ static bool get_cache_opt(CacheOpt opt, bool default_value) {
extern "C" int ZigClang_main(int argc, char **argv);

int main(int argc, char **argv) {
stage2_attach_segv_handler();

char *arg0 = argv[0];
Error err;

Expand Down
4 changes: 4 additions & 0 deletions src/userland.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@
#include <stdlib.h>
#include <string.h>

void stage2_attach_segv_handler(void) {
// do nothing in stage0
}

void stage2_translate_c(void) {
const char *msg = "stage0 called stage2_translate_c";
stage2_panic(msg, strlen(msg));
Expand Down
2 changes: 2 additions & 0 deletions src/userland.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
#define ZIG_USERLAND_EXTERN_C
#endif

ZIG_USERLAND_EXTERN_C void stage2_attach_segv_handler(void);

ZIG_USERLAND_EXTERN_C void stage2_translate_c(void);

ZIG_USERLAND_EXTERN_C void stage2_zen(const char **ptr, size_t *len);
Expand Down