mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
parent
0dd2e93e4c
commit
1a1598c58c
@ -99,6 +99,32 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
|
||||
};
|
||||
}
|
||||
|
||||
/// Tries to print the stack trace starting from the supplied base pointer to stderr,
|
||||
/// unbuffered, and ignores any error returned.
|
||||
/// TODO multithreaded awareness
|
||||
pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
|
||||
const stderr = getStderrStream() catch return;
|
||||
if (builtin.strip_debug_info) {
|
||||
stderr.print("Unable to dump stack trace: debug info stripped\n") catch return;
|
||||
return;
|
||||
}
|
||||
const debug_info = getSelfDebugInfo() catch |err| {
|
||||
stderr.print("Unable to dump stack trace: Unable to open debug info: {}\n", @errorName(err)) catch return;
|
||||
return;
|
||||
};
|
||||
const tty_color = wantTtyColor();
|
||||
printSourceAtAddress(debug_info, stderr, ip, tty_color) catch return;
|
||||
const first_return_address = @intToPtr(*const usize, bp + @sizeOf(usize)).*;
|
||||
printSourceAtAddress(debug_info, stderr, first_return_address - 1, tty_color) catch return;
|
||||
var it = StackIterator{
|
||||
.first_addr = null,
|
||||
.fp = bp,
|
||||
};
|
||||
while (it.next()) |return_address| {
|
||||
printSourceAtAddress(debug_info, stderr, return_address - 1, tty_color) catch return;
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a slice with the same pointer as addresses, with a potentially smaller len.
|
||||
/// On Windows, when first_address is not null, we ask for at least 32 stack frames,
|
||||
/// and then try to find the first address. If addresses.len is more than 32, we
|
||||
@ -2291,3 +2317,44 @@ fn getDebugInfoAllocator() *mem.Allocator {
|
||||
debug_info_allocator = &debug_info_arena_allocator.allocator;
|
||||
return &debug_info_arena_allocator.allocator;
|
||||
}
|
||||
|
||||
/// Whether or not the current target can print useful debug information when a segfault occurs.
|
||||
pub const have_segfault_handling_support = builtin.arch == .x86_64 and builtin.os == .linux;
|
||||
|
||||
/// Attaches a global SIGSEGV handler which calls @panic("segmentation fault");
|
||||
pub fn attachSegfaultHandler() void {
|
||||
if (!have_segfault_handling_support) {
|
||||
@compileError("segfault handler not supported for this target");
|
||||
}
|
||||
var act = os.Sigaction{
|
||||
.sigaction = handleSegfault,
|
||||
.mask = os.empty_sigset,
|
||||
.flags = (os.SA_SIGINFO | os.SA_RESTART | os.SA_RESETHAND),
|
||||
};
|
||||
|
||||
os.sigaction(os.SIGSEGV, &act, null);
|
||||
}
|
||||
|
||||
extern fn handleSegfault(sig: i32, info: *const os.siginfo_t, ctx_ptr: *const c_void) noreturn {
|
||||
// Reset to the default handler so that if a segfault happens in this handler it will crash
|
||||
// the process. Also when this handler returns, the original instruction will be repeated
|
||||
// and the resulting segfault will crash the process rather than continually dump stack traces.
|
||||
var act = os.Sigaction{
|
||||
.sigaction = os.SIG_DFL,
|
||||
.mask = os.empty_sigset,
|
||||
.flags = 0,
|
||||
};
|
||||
os.sigaction(os.SIGSEGV, &act, null);
|
||||
|
||||
const ctx = @ptrCast(*const os.ucontext_t, @alignCast(@alignOf(os.ucontext_t), ctx_ptr));
|
||||
const ip = @intCast(usize, ctx.mcontext.gregs[os.REG_RIP]);
|
||||
const bp = @intCast(usize, ctx.mcontext.gregs[os.REG_RBP]);
|
||||
const addr = @ptrToInt(info.fields.sigfault.addr);
|
||||
std.debug.warn("Segmentation fault at address 0x{x}\n", addr);
|
||||
dumpStackTraceFromBase(bp, ip);
|
||||
|
||||
// We cannot allow the signal handler to return because when it runs the original instruction
|
||||
// again, the memory may be mapped and undefined behavior would occur rather than repeating
|
||||
// the segfault. So we simply abort here.
|
||||
os.abort();
|
||||
}
|
||||
|
||||
10
std/os.zig
10
std/os.zig
@ -2529,6 +2529,16 @@ pub fn sigaltstack(ss: ?*stack_t, old_ss: ?*stack_t) SigaltstackError!void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Examine and change a signal action.
|
||||
pub fn sigaction(sig: u6, act: *const Sigaction, oact: ?*Sigaction) void {
|
||||
switch (errno(system.sigaction(sig, act, oact))) {
|
||||
0 => return,
|
||||
EFAULT => unreachable,
|
||||
EINVAL => unreachable,
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
test "" {
|
||||
_ = @import("os/darwin.zig");
|
||||
_ = @import("os/freebsd.zig");
|
||||
|
||||
@ -12,6 +12,8 @@ pub usingnamespace switch (builtin.arch) {
|
||||
|
||||
pub const pid_t = i32;
|
||||
pub const fd_t = i32;
|
||||
pub const uid_t = i32;
|
||||
pub const clock_t = isize;
|
||||
|
||||
pub const PATH_MAX = 4096;
|
||||
pub const IOV_MAX = 1024;
|
||||
@ -712,22 +714,23 @@ pub const all_mask = [_]u32{ 0xffffffff, 0xffffffff };
|
||||
pub const app_mask = [_]u32{ 0xfffffffc, 0x7fffffff };
|
||||
|
||||
pub const k_sigaction = extern struct {
|
||||
handler: extern fn (i32) void,
|
||||
sigaction: ?extern fn (i32, *siginfo_t, *c_void) void,
|
||||
flags: usize,
|
||||
restorer: extern fn () void,
|
||||
mask: [2]u32,
|
||||
};
|
||||
|
||||
/// Renamed from `sigaction` to `Sigaction` to avoid conflict with the syscall.
|
||||
pub const Sigaction = struct {
|
||||
handler: extern fn (i32) void,
|
||||
pub const Sigaction = extern struct {
|
||||
sigaction: ?extern fn (i32, *siginfo_t, *c_void) void,
|
||||
mask: sigset_t,
|
||||
flags: u32,
|
||||
restorer: ?extern fn () void = null,
|
||||
};
|
||||
|
||||
pub const SIG_ERR = @intToPtr(extern fn (i32) void, maxInt(usize));
|
||||
pub const SIG_DFL = @intToPtr(extern fn (i32) void, 0);
|
||||
pub const SIG_IGN = @intToPtr(extern fn (i32) void, 1);
|
||||
pub const SIG_ERR = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, maxInt(usize));
|
||||
pub const SIG_DFL = @intToPtr(?extern fn (i32, *siginfo_t, *c_void) void, 0);
|
||||
pub const SIG_IGN = @intToPtr(extern fn (i32, *siginfo_t, *c_void) void, 1);
|
||||
pub const empty_sigset = [_]usize{0} ** sigset_t.len;
|
||||
|
||||
pub const in_port_t = u16;
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
// x86-64-specific declarations that are intended to be imported into the POSIX namespace.
|
||||
const std = @import("../../../std.zig");
|
||||
const pid_t = linux.pid_t;
|
||||
const uid_t = linux.uid_t;
|
||||
const clock_t = linux.clock_t;
|
||||
const stack_t = linux.stack_t;
|
||||
const sigset_t = linux.sigset_t;
|
||||
|
||||
const linux = std.os.linux;
|
||||
const sockaddr = linux.sockaddr;
|
||||
@ -407,6 +412,30 @@ pub const ARCH_SET_FS = 0x1002;
|
||||
pub const ARCH_GET_FS = 0x1003;
|
||||
pub const ARCH_GET_GS = 0x1004;
|
||||
|
||||
pub const REG_R8 = 0;
|
||||
pub const REG_R9 = 1;
|
||||
pub const REG_R10 = 2;
|
||||
pub const REG_R11 = 3;
|
||||
pub const REG_R12 = 4;
|
||||
pub const REG_R13 = 5;
|
||||
pub const REG_R14 = 6;
|
||||
pub const REG_R15 = 7;
|
||||
pub const REG_RDI = 8;
|
||||
pub const REG_RSI = 9;
|
||||
pub const REG_RBP = 10;
|
||||
pub const REG_RBX = 11;
|
||||
pub const REG_RDX = 12;
|
||||
pub const REG_RAX = 13;
|
||||
pub const REG_RCX = 14;
|
||||
pub const REG_RSP = 15;
|
||||
pub const REG_RIP = 16;
|
||||
pub const REG_EFL = 17;
|
||||
pub const REG_CSGSFS = 18;
|
||||
pub const REG_ERR = 19;
|
||||
pub const REG_TRAPNO = 20;
|
||||
pub const REG_OLDMASK = 21;
|
||||
pub const REG_CR2 = 22;
|
||||
|
||||
pub const msghdr = extern struct {
|
||||
msg_name: ?*sockaddr,
|
||||
msg_namelen: socklen_t,
|
||||
@ -468,3 +497,129 @@ pub const timezone = extern struct {
|
||||
};
|
||||
|
||||
pub const Elf_Symndx = u32;
|
||||
|
||||
pub const sigval = extern union {
|
||||
int: i32,
|
||||
ptr: *c_void,
|
||||
};
|
||||
|
||||
pub const siginfo_t = extern struct {
|
||||
signo: i32,
|
||||
errno: i32,
|
||||
code: i32,
|
||||
fields: extern union {
|
||||
pad: [128 - 2 * @sizeOf(c_int) - @sizeOf(c_long)]u8,
|
||||
common: extern struct {
|
||||
first: extern union {
|
||||
piduid: extern struct {
|
||||
pid: pid_t,
|
||||
uid: uid_t,
|
||||
},
|
||||
timer: extern struct {
|
||||
timerid: i32,
|
||||
overrun: i32,
|
||||
},
|
||||
},
|
||||
second: extern union {
|
||||
value: sigval,
|
||||
sigchld: extern struct {
|
||||
status: i32,
|
||||
utime: clock_t,
|
||||
stime: clock_t,
|
||||
},
|
||||
},
|
||||
},
|
||||
sigfault: extern struct {
|
||||
addr: *c_void,
|
||||
addr_lsb: i16,
|
||||
first: extern union {
|
||||
addr_bnd: extern struct {
|
||||
lower: *c_void,
|
||||
upper: *c_void,
|
||||
},
|
||||
pkey: u32,
|
||||
},
|
||||
},
|
||||
sigpoll: extern struct {
|
||||
band: isize,
|
||||
fd: i32,
|
||||
},
|
||||
sigsys: extern struct {
|
||||
call_addr: *c_void,
|
||||
syscall: i32,
|
||||
arch: u32,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
pub const greg_t = usize;
|
||||
pub const gregset_t = [23]greg_t;
|
||||
pub const fpstate = extern struct {
|
||||
cwd: u16,
|
||||
swd: u16,
|
||||
ftw: u16,
|
||||
fop: u16,
|
||||
rip: usize,
|
||||
rdp: usize,
|
||||
mxcsr: u32,
|
||||
mxcr_mask: u32,
|
||||
st: [8]extern struct {
|
||||
significand: [4]u16,
|
||||
exponent: u16,
|
||||
padding: [3]u16 = undefined,
|
||||
},
|
||||
xmm: [16]extern struct {
|
||||
element: [4]u32,
|
||||
},
|
||||
padding: [24]u32 = undefined,
|
||||
};
|
||||
pub const fpregset_t = *fpstate;
|
||||
pub const sigcontext = extern struct {
|
||||
r8: usize,
|
||||
r9: usize,
|
||||
r10: usize,
|
||||
r11: usize,
|
||||
r12: usize,
|
||||
r13: usize,
|
||||
r14: usize,
|
||||
r15: usize,
|
||||
|
||||
rdi: usize,
|
||||
rsi: usize,
|
||||
rbp: usize,
|
||||
rbx: usize,
|
||||
rdx: usize,
|
||||
rax: usize,
|
||||
rcx: usize,
|
||||
rsp: usize,
|
||||
rip: usize,
|
||||
eflags: usize,
|
||||
|
||||
cs: u16,
|
||||
gs: u16,
|
||||
fs: u16,
|
||||
pad0: u16 = undefined,
|
||||
|
||||
err: usize,
|
||||
trapno: usize,
|
||||
oldmask: usize,
|
||||
cr2: usize,
|
||||
|
||||
fpstate: *fpstate,
|
||||
reserved1: [8]usize = undefined,
|
||||
};
|
||||
|
||||
pub const mcontext_t = extern struct {
|
||||
gregs: gregset_t,
|
||||
fpregs: fpregset_t,
|
||||
reserved1: [8]usize = undefined,
|
||||
};
|
||||
|
||||
pub const ucontext_t = extern struct {
|
||||
flags: usize,
|
||||
link: *ucontext_t,
|
||||
stack: stack_t,
|
||||
mcontext: mcontext_t,
|
||||
sigmask: sigset_t,
|
||||
fpregs_mem: [64]usize,
|
||||
};
|
||||
|
||||
@ -542,7 +542,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
|
||||
assert(sig != SIGKILL);
|
||||
assert(sig != SIGSTOP);
|
||||
var ksa = k_sigaction{
|
||||
.handler = act.handler,
|
||||
.sigaction = act.sigaction,
|
||||
.flags = act.flags | SA_RESTORER,
|
||||
.mask = undefined,
|
||||
.restorer = @ptrCast(extern fn () void, restore_rt),
|
||||
@ -555,7 +555,7 @@ pub fn sigaction(sig: u6, noalias act: *const Sigaction, noalias oact: ?*Sigacti
|
||||
return result;
|
||||
}
|
||||
if (oact) |old| {
|
||||
old.handler = ksa_old.handler;
|
||||
old.sigaction = ksa_old.sigaction;
|
||||
old.flags = @truncate(u32, ksa_old.flags);
|
||||
@memcpy(@ptrCast([*]u8, &old.mask), @ptrCast([*]const u8, &ksa_old.mask), @sizeOf(@typeOf(ksa_old.mask)));
|
||||
}
|
||||
|
||||
@ -99,6 +99,15 @@ fn posixCallMainAndExit() noreturn {
|
||||
inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
|
||||
std.os.argv = argv[0..argc];
|
||||
std.os.environ = envp;
|
||||
|
||||
const enable_segfault_handler: bool = if (@hasDecl(root, "enable_segfault_handler"))
|
||||
root.enable_segfault_handler
|
||||
else
|
||||
std.debug.runtime_safety and std.debug.have_segfault_handling_support;
|
||||
if (enable_segfault_handler) {
|
||||
std.debug.attachSegfaultHandler();
|
||||
}
|
||||
|
||||
return callMain();
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user