authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-07-02 13:27:40-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-07-02 13:41:16-04:00
log1a1598c58cf506217355c6b8eec84c0e9a3a6d2e
tree2ec9f7e299e4e9d22459e305fd69eef64523a3a8
parent0dd2e93e4cb28205823e840d5fdb0fccce9cc2e7
signaturelock-open Commit is signed but in an unrecognized format.

stack traces on segfault by default for linux-x86_64

closes #2355

6 files changed, 252 insertions(+), 8 deletions(-)

std/debug.zig+67
......@@ -99,6 +99,32 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
9999 };
100100}
101101
102/// Tries to print the stack trace starting from the supplied base pointer to stderr,
103/// unbuffered, and ignores any error returned.
104/// TODO multithreaded awareness
105pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
106 const stderr = getStderrStream() catch return;
107 if (builtin.strip_debug_info) {
108 stderr.print("Unable to dump stack trace: debug info stripped\n") catch return;
109 return;
110 }
111 const debug_info = getSelfDebugInfo() catch |err| {
112 stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
113 return;
114 };
115 const tty_color = wantTtyColor();
116 printSourceAtAddress(debug_info, stderr, ip, tty_color) catch return;
117 const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*;
118 printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_color) catch return;
119 var it = StackIterator{
120 .first_addr = null,
121 .fp = bp,
122 };
123 while (it.next()) |return_address| {
124 printSourceAtAddress(debug_info, stderr, return_address - 1, tty_color) catch return;
125 }
126}
127
102128/// Returns a slice with the same pointer as addresses, with a potentially smaller len.
103129/// On Windows, when first_address is not null, we ask for at least 32 stack frames,
104130/// and then try to find the first address. If addresses.len is more than 32, we
......@@ -2291,3 +2317,44 @@ fn getDebugInfoAllocator() *mem.Allocator {
22912317 debug_info_allocator = &debug_info_arena_allocator.allocator;
22922318 return &debug_info_arena_allocator.allocator;
22932319}
2320
2321/// Whether or not the current target can print useful debug information when a segfault occurs.
2322pub const have_segfault_handling_support = builtin.arch == .x86_64 and builtin.os == .linux;
2323
2324/// Attaches a global SIGSEGV handler which calls @panic("segmentation fault");
2325pub fn attachSegfaultHandler() void {
2326 if (!have_segfault_handling_support) {
2327 @compileError("segfault handler not supported for this target");
2328 }
2329 var act = os.Sigaction{
2330 .sigaction = handleSegfault,
2331 .mask = os.empty_sigset,
2332 .flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND),
2333 };
2334
2335 os.sigaction(os.SIGSEGV, &act, null);
2336}
2337
2338extern fn handleSegfault(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn {
2339 // Reset to the default handler so that if a segfault happens in this handler it will crash
2340 // the process. Also when this handler returns, the original instruction will be repeated
2341 // and the resulting segfault will crash the process rather than continually dump stack traces.
2342 var act = os.Sigaction{
2343 .sigaction = os.SIG_DFL,
2344 .mask = os.empty_sigset,
2345 .flags = 0,
2346 };
2347 os.sigaction(os.SIGSEGV, &act, null);
2348
2349 const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
2350 const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]);
2351 const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]);
2352 const addr = @ptrToInt(info.fields.sigfault.addr);
2353 std.debug.warn("Segmentation fault at address 0x{x}\n", addr);
2354 dumpStackTraceFromBase(bp, ip);
2355
2356 // We cannot allow the signal handler to return because when it runs the original instruction
2357 // again, the memory may be mapped and undefined behavior would occur rather than repeating
2358 // the segfault. So we simply abort here.
2359 os.abort();
2360}
std/os.zig+10
......@@ -2529,6 +2529,16 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
25292529 }
25302530}
25312531
2532/// Examine and change a signal action.
2533pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void {
2534 switch (errno(system.sigaction(sig, act, oact))) {
2535 0 => return,
2536 EFAULT => unreachable,
2537 EINVAL => unreachable,
2538 else => unreachable,
2539 }
2540}
2541
25322542test "" {
25332543 _ = @import("os/darwin.zig");
25342544 _ = @import("os/freebsd.zig");
std/os/bits/linux.zig+9-6
......@@ -12,6 +12,8 @@ pub usingnamespace switch (builtin.arch) {
1212
1313pub const pid_t = i32;
1414pub const fd_t = i32;
15pub const uid_t = i32;
16pub const clock_t = isize;
1517
1618pub const PATH_MAX = 4096;
1719pub const IOV_MAX = 1024;
......@@ -712,22 +714,23 @@ pub const all_mask = [_]u32{ 0xffffffff, 0xffffffff };
712714pub const app_mask = [_]u32{ 0xfffffffc, 0x7fffffff };
713715
714716pub const k_sigaction = extern struct {
715 handler: extern fn (i32) void,
717 sigaction: ?extern fn (i32, *siginfo_t, *c_void) void,
716718 flags: usize,
717719 restorer: extern fn () void,
718720 mask: [2]u32,
719721};
720722
721723/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
722pub const Sigaction = struct {
723 handler: extern fn (i32) void,
724pub const Sigaction = extern struct {
725 sigaction: ?extern fn (i32, *siginfo_t, *c_void) void,
724726 mask: sigset_t,
725727 flags: u32,
728 restorer: ?extern fn () void = null,
726729};
727730
728pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize));
729pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0);
730pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1);
731pub const SIG_ERR = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, maxInt(usize));
732pub const SIG_DFL = @intToPtr(?extern fn (i32, *siginfo_t, *c_void) void, 0);
733pub const SIG_IGN = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, 1);
731734pub const empty_sigset = [_]usize{0} ** sigset_t.len;
732735
733736pub const in_port_t = u16;
std/os/bits/linux/x86_64.zig+155
......@@ -1,5 +1,10 @@
11// x86-64-specific declarations that are intended to be imported into the POSIX namespace.
22const std = @import("../../../std.zig");
3const pid_t = linux.pid_t;
4const uid_t = linux.uid_t;
5const clock_t = linux.clock_t;
6const stack_t = linux.stack_t;
7const sigset_t = linux.sigset_t;
38
49const linux = std.os.linux;
510const sockaddr = linux.sockaddr;
......@@ -407,6 +412,30 @@ pub const ARCH_SET_FS = 0x1002;
407412pub const ARCH_GET_FS = 0x1003;
408413pub const ARCH_GET_GS = 0x1004;
409414
415pub const REG_R8 = 0;
416pub const REG_R9 = 1;
417pub const REG_R10 = 2;
418pub const REG_R11 = 3;
419pub const REG_R12 = 4;
420pub const REG_R13 = 5;
421pub const REG_R14 = 6;
422pub const REG_R15 = 7;
423pub const REG_RDI = 8;
424pub const REG_RSI = 9;
425pub const REG_RBP = 10;
426pub const REG_RBX = 11;
427pub const REG_RDX = 12;
428pub const REG_RAX = 13;
429pub const REG_RCX = 14;
430pub const REG_RSP = 15;
431pub const REG_RIP = 16;
432pub const REG_EFL = 17;
433pub const REG_CSGSFS = 18;
434pub const REG_ERR = 19;
435pub const REG_TRAPNO = 20;
436pub const REG_OLDMASK = 21;
437pub const REG_CR2 = 22;
438
410439pub const msghdr = extern struct {
411440 msg_name: ?*sockaddr,
412441 msg_namelen: socklen_t,
......@@ -468,3 +497,129 @@ pub const timezone = extern struct {
468497};
469498
470499pub const Elf_Symndx = u32;
500
501pub const sigval = extern union {
502 int: i32,
503 ptr: *c_void,
504};
505
506pub const siginfo_t = extern struct {
507 signo: i32,
508 errno: i32,
509 code: i32,
510 fields: extern union {
511 pad: [128 - 2 * @sizeOf(c_int) - @sizeOf(c_long)]u8,
512 common: extern struct {
513 first: extern union {
514 piduid: extern struct {
515 pid: pid_t,
516 uid: uid_t,
517 },
518 timer: extern struct {
519 timerid: i32,
520 overrun: i32,
521 },
522 },
523 second: extern union {
524 value: sigval,
525 sigchld: extern struct {
526 status: i32,
527 utime: clock_t,
528 stime: clock_t,
529 },
530 },
531 },
532 sigfault: extern struct {
533 addr: *c_void,
534 addr_lsb: i16,
535 first: extern union {
536 addr_bnd: extern struct {
537 lower: *c_void,
538 upper: *c_void,
539 },
540 pkey: u32,
541 },
542 },
543 sigpoll: extern struct {
544 band: isize,
545 fd: i32,
546 },
547 sigsys: extern struct {
548 call_addr: *c_void,
549 syscall: i32,
550 arch: u32,
551 },
552 },
553};
554
555pub const greg_t = usize;
556pub const gregset_t = [23]greg_t;
557pub const fpstate = extern struct {
558 cwd: u16,
559 swd: u16,
560 ftw: u16,
561 fop: u16,
562 rip: usize,
563 rdp: usize,
564 mxcsr: u32,
565 mxcr_mask: u32,
566 st: [8]extern struct {
567 significand: [4]u16,
568 exponent: u16,
569 padding: [3]u16 = undefined,
570 },
571 xmm: [16]extern struct {
572 element: [4]u32,
573 },
574 padding: [24]u32 = undefined,
575};
576pub const fpregset_t = *fpstate;
577pub const sigcontext = extern struct {
578 r8: usize,
579 r9: usize,
580 r10: usize,
581 r11: usize,
582 r12: usize,
583 r13: usize,
584 r14: usize,
585 r15: usize,
586
587 rdi: usize,
588 rsi: usize,
589 rbp: usize,
590 rbx: usize,
591 rdx: usize,
592 rax: usize,
593 rcx: usize,
594 rsp: usize,
595 rip: usize,
596 eflags: usize,
597
598 cs: u16,
599 gs: u16,
600 fs: u16,
601 pad0: u16 = undefined,
602
603 err: usize,
604 trapno: usize,
605 oldmask: usize,
606 cr2: usize,
607
608 fpstate: *fpstate,
609 reserved1: [8]usize = undefined,
610};
611
612pub const mcontext_t = extern struct {
613 gregs: gregset_t,
614 fpregs: fpregset_t,
615 reserved1: [8]usize = undefined,
616};
617
618pub const ucontext_t = extern struct {
619 flags: usize,
620 link: *ucontext_t,
621 stack: stack_t,
622 mcontext: mcontext_t,
623 sigmask: sigset_t,
624 fpregs_mem: [64]usize,
625};
std/os/linux.zig+2-2
......@@ -542,7 +542,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
542542 assert(sig != SIGKILL);
543543 assert(sig != SIGSTOP);
544544 var ksa = k_sigaction{
545 .handler = act.handler,
545 .sigaction = act.sigaction,
546546 .flags = act.flags | SA_RESTORER,
547547 .mask = undefined,
548548 .restorer = @ptrCast(extern fn () void, restore_rt),
......@@ -555,7 +555,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
555555 return result;
556556 }
557557 if (oact) |old| {
558 old.handler = ksa_old.handler;
558 old.sigaction = ksa_old.sigaction;
559559 old.flags = @truncate(u32, ksa_old.flags);
560560 @memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask)));
561561 }
std/special/start.zig+9
......@@ -99,6 +99,15 @@ fn posixCallMainAndExit() noreturn {
9999inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
100100 std.os.argv = argv[0..argc];
101101 std.os.environ = envp;
102
103 const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
104 root.enable_segfault_handler
105 else
106 std.debug.runtime_safety and std.debug.have_segfault_handling_support;
107 if (enable_segfault_handler) {
108 std.debug.attachSegfaultHandler();
109 }
110
102111 return callMain();
103112}
104113