mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-30 12:07:40 +00:00
77 lines
3.3 KiB
Rust
77 lines
3.3 KiB
Rust
![]() |
//! Signal handler for rustc
|
||
|
//! Primarily used to extract a backtrace from stack overflow
|
||
|
|
||
|
use std::alloc::{alloc, Layout};
|
||
|
use std::{mem, ptr};
|
||
|
|
||
|
extern "C" {
|
||
|
fn backtrace_symbols_fd(buffer: *const *mut libc::c_void, size: libc::c_int, fd: libc::c_int);
|
||
|
}
|
||
|
|
||
|
/// Signal handler installed for SIGSEGV
|
||
|
extern "C" fn print_stack_trace(_: libc::c_int) {
|
||
|
const MAX_FRAMES: usize = 256;
|
||
|
// Reserve data segment so we don't have to malloc in a signal handler, which might fail
|
||
|
// in incredibly undesirable and unexpected ways due to e.g. the allocator deadlocking
|
||
|
static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] = [ptr::null_mut(); MAX_FRAMES];
|
||
|
unsafe {
|
||
|
// Collect return addresses
|
||
|
let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
|
||
|
if depth == 0 {
|
||
|
return;
|
||
|
}
|
||
|
// Just a stack trace is cryptic. Explain what we're doing.
|
||
|
write_raw_err("error: rustc interrupted by SIGSEGV, printing stack trace:\n\n");
|
||
|
// Elaborate return addrs into symbols and write them directly to stderr
|
||
|
backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, libc::STDERR_FILENO);
|
||
|
if depth > 22 {
|
||
|
// We probably just scrolled that "we got SIGSEGV" message off the terminal
|
||
|
write_raw_err("\nerror: stack trace dumped due to SIGSEGV, possible stack overflow");
|
||
|
};
|
||
|
write_raw_err("\nerror: please report a bug to https://github.com/rust-lang/rust\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Write without locking stderr.
|
||
|
///
|
||
|
/// Only acceptable because everything will end soon anyways.
|
||
|
fn write_raw_err(input: &str) {
|
||
|
// We do not care how many bytes we actually get out. SIGSEGV comes for our head.
|
||
|
// Splash stderr with letters of our own blood to warn our friends about the monster.
|
||
|
let _ = unsafe { libc::write(libc::STDERR_FILENO, input.as_ptr().cast(), input.len()) };
|
||
|
}
|
||
|
|
||
|
/// When SIGSEGV is delivered to the process, print a stack trace and then exit.
|
||
|
pub(super) fn install() {
|
||
|
unsafe {
|
||
|
let alt_stack_size: usize = min_sigstack_size() + 64 * 1024;
|
||
|
let mut alt_stack: libc::stack_t = mem::zeroed();
|
||
|
alt_stack.ss_sp = alloc(Layout::from_size_align(alt_stack_size, 1).unwrap()).cast();
|
||
|
alt_stack.ss_size = alt_stack_size;
|
||
|
libc::sigaltstack(&alt_stack, ptr::null_mut());
|
||
|
|
||
|
let mut sa: libc::sigaction = mem::zeroed();
|
||
|
sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
|
||
|
sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
|
||
|
libc::sigemptyset(&mut sa.sa_mask);
|
||
|
libc::sigaction(libc::SIGSEGV, &sa, ptr::null_mut());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// Modern kernels on modern hardware can have dynamic signal stack sizes.
|
||
|
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||
|
fn min_sigstack_size() -> usize {
|
||
|
const AT_MINSIGSTKSZ: core::ffi::c_ulong = 51;
|
||
|
let dynamic_sigstksz = unsafe { libc::getauxval(AT_MINSIGSTKSZ) };
|
||
|
// If getauxval couldn't find the entry, it returns 0,
|
||
|
// so take the higher of the "constant" and auxval.
|
||
|
// This transparently supports older kernels which don't provide AT_MINSIGSTKSZ
|
||
|
libc::MINSIGSTKSZ.max(dynamic_sigstksz as _)
|
||
|
}
|
||
|
|
||
|
/// Not all OS support hardware where this is needed.
|
||
|
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||
|
fn min_sigstack_size() -> usize {
|
||
|
libc::MINSIGSTKSZ
|
||
|
}
|