From 4ed312ad0d88719c7b32f502fb7b58af506f04da Mon Sep 17 00:00:00 2001 From: Rocknest Date: Wed, 24 Apr 2019 22:22:39 +0300 Subject: [PATCH] Handle SIGSEGV and print stack traces --- CMakeLists.txt | 4 + src-self-hosted/segv_handler/c.zig | 45 ++++++++ src-self-hosted/segv_handler/handler.zig | 65 +++++++++++ src-self-hosted/segv_handler/i386-linux.zig | 96 ++++++++++++++++ src-self-hosted/segv_handler/x86_64-linux.zig | 103 ++++++++++++++++++ src-self-hosted/stage1.zig | 7 ++ src/main.cpp | 2 + src/userland.cpp | 4 + src/userland.h | 2 + 9 files changed, 328 insertions(+) create mode 100644 src-self-hosted/segv_handler/c.zig create mode 100644 src-self-hosted/segv_handler/handler.zig create mode 100644 src-self-hosted/segv_handler/i386-linux.zig create mode 100644 src-self-hosted/segv_handler/x86_64-linux.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 5acf0d8c751d..7504dc884447 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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}") diff --git a/src-self-hosted/segv_handler/c.zig b/src-self-hosted/segv_handler/c.zig new file mode 100644 index 000000000000..a0d41961408a --- /dev/null +++ b/src-self-hosted/segv_handler/c.zig @@ -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; diff --git a/src-self-hosted/segv_handler/handler.zig b/src-self-hosted/segv_handler/handler.zig new file mode 100644 index 000000000000..b49947a38351 --- /dev/null +++ b/src-self-hosted/segv_handler/handler.zig @@ -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 +} diff --git a/src-self-hosted/segv_handler/i386-linux.zig b/src-self-hosted/segv_handler/i386-linux.zig new file mode 100644 index 000000000000..568f57995580 --- /dev/null +++ b/src-self-hosted/segv_handler/i386-linux.zig @@ -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; diff --git a/src-self-hosted/segv_handler/x86_64-linux.zig b/src-self-hosted/segv_handler/x86_64-linux.zig new file mode 100644 index 000000000000..c8b5bc11ecf6 --- /dev/null +++ b/src-self-hosted/segv_handler/x86_64-linux.zig @@ -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; diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index f87c8a0e70fa..4e20fb211f0e 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -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; diff --git a/src/main.cpp b/src/main.cpp index 03cf3aad68e7..44cd22e29599 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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; diff --git a/src/userland.cpp b/src/userland.cpp index 6c56bceaa0f4..13434bbda637 100644 --- a/src/userland.cpp +++ b/src/userland.cpp @@ -6,6 +6,10 @@ #include #include +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)); diff --git a/src/userland.h b/src/userland.h index a01bcc62c3dc..9e63e2ee3440 100644 --- a/src/userland.h +++ b/src/userland.h @@ -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);