use alloc::vec::Vec;
use core::{
cell::UnsafeCell,
fmt::{self, Display, Formatter},
mem, ptr,
ptr::{addr_of_mut, write_volatile},
sync::atomic::{compiler_fence, Ordering},
};
#[cfg(feature = "std")]
use std::ffi::CString;
#[cfg(target_arch = "arm")]
pub use libc::c_ulong;
#[cfg(feature = "std")]
use nix::errno::{errno, Errno};
#[cfg(target_arch = "arm")]
#[derive(Debug)]
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct mcontext_t {
pub trap_no: c_ulong,
pub error_code: c_ulong,
pub oldmask: c_ulong,
pub arm_r0: c_ulong,
pub arm_r1: c_ulong,
pub arm_r2: c_ulong,
pub arm_r3: c_ulong,
pub arm_r4: c_ulong,
pub arm_r5: c_ulong,
pub arm_r6: c_ulong,
pub arm_r7: c_ulong,
pub arm_r8: c_ulong,
pub arm_r9: c_ulong,
pub arm_r10: c_ulong,
pub arm_fp: c_ulong,
pub arm_ip: c_ulong,
pub arm_sp: c_ulong,
pub arm_lr: c_ulong,
pub arm_pc: c_ulong,
pub arm_cpsr: c_ulong,
pub fault_address: c_ulong,
}
#[cfg(all(target_os = "linux", target_arch = "arm"))]
#[derive(Debug)]
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct ucontext_t {
pub uc_flags: u32,
pub uc_link: *mut ucontext_t,
pub uc_stack: stack_t,
pub uc_mcontext: mcontext_t,
pub uc_sigmask: libc::sigset_t,
}
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
#[derive(Debug)]
#[repr(C)]
pub struct arm_exception_state64 {
pub __far: u64,
pub __esr: u32,
pub __exception: u32,
}
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
#[derive(Debug)]
#[repr(C)]
pub struct arm_thread_state64 {
pub __x: [u64; 29],
pub __fp: u64,
pub __lr: u64,
pub __sp: u64,
pub __pc: u64,
pub __cpsr: u32,
pub __pad: u32,
}
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
#[derive(Debug)]
#[allow(non_camel_case_types)]
#[repr(C, align(16))]
pub struct arm_neon_state64 {
pub opaque: [u8; (32 * 16) + (2 * mem::size_of::<u32>())],
}
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
#[allow(non_camel_case_types)]
#[derive(Debug)]
#[repr(C)]
pub struct mcontext64 {
pub __es: arm_exception_state64,
pub __ss: arm_thread_state64,
pub __ns: arm_neon_state64,
}
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
#[derive(Debug)]
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct sigaltstack {
pub ss_sp: *mut c_void,
pub ss_size: libc::size_t,
pub ss_flags: c_int,
}
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
#[derive(Debug)]
#[allow(non_camel_case_types)]
#[repr(C)]
pub struct ucontext_t {
pub uc_onstack: c_int,
pub uc_sigmask: u32,
pub uc_stack: sigaltstack,
pub uc_link: *mut c_void,
pub uc_mcsize: ssize_t,
pub uc_mcontext: *mut mcontext64,
pub mcontext_data: mcontext64,
}
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
use libc::ssize_t;
#[cfg(not(any(
all(target_os = "linux", target_arch = "arm"),
all(target_vendor = "apple", target_arch = "aarch64")
)))]
pub use libc::ucontext_t;
use libc::{
c_int, malloc, sigaction, sigaddset, sigaltstack, sigemptyset, stack_t, SA_NODEFER, SA_ONSTACK,
SA_SIGINFO, SIGABRT, SIGALRM, SIGBUS, SIGFPE, SIGHUP, SIGILL, SIGINT, SIGKILL, SIGPIPE,
SIGQUIT, SIGSEGV, SIGTERM, SIGTRAP, SIGUSR2,
};
pub use libc::{c_void, siginfo_t};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::Error;
extern "C" {
fn getcontext(ucp: *mut ucontext_t) -> c_int;
}
#[derive(Debug, IntoPrimitive, TryFromPrimitive, Clone, Copy)]
#[repr(i32)]
pub enum Signal {
SigAbort = SIGABRT,
SigBus = SIGBUS,
SigFloatingPointException = SIGFPE,
SigIllegalInstruction = SIGILL,
SigPipe = SIGPIPE,
SigSegmentationFault = SIGSEGV,
SigUser2 = SIGUSR2,
SigAlarm = SIGALRM,
SigHangUp = SIGHUP,
SigKill = SIGKILL,
SigQuit = SIGQUIT,
SigTerm = SIGTERM,
SigInterrupt = SIGINT,
SigTrap = SIGTRAP,
}
pub static CRASH_SIGNALS: &[Signal] = &[
Signal::SigAbort,
Signal::SigBus,
Signal::SigFloatingPointException,
Signal::SigIllegalInstruction,
Signal::SigPipe,
Signal::SigSegmentationFault,
];
impl PartialEq for Signal {
fn eq(&self, other: &Self) -> bool {
*self as i32 == *other as i32
}
}
impl Eq for Signal {}
unsafe impl Sync for Signal {}
impl Display for Signal {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
match self {
Signal::SigAbort => write!(f, "SIGABRT")?,
Signal::SigBus => write!(f, "SIGBUS")?,
Signal::SigFloatingPointException => write!(f, "SIGFPE")?,
Signal::SigIllegalInstruction => write!(f, "SIGILL")?,
Signal::SigPipe => write!(f, "SIGPIPE")?,
Signal::SigSegmentationFault => write!(f, "SIGSEGV")?,
Signal::SigUser2 => write!(f, "SIGUSR2")?,
Signal::SigAlarm => write!(f, "SIGALRM")?,
Signal::SigHangUp => write!(f, "SIGHUP")?,
Signal::SigKill => write!(f, "SIGKILL")?,
Signal::SigQuit => write!(f, "SIGQUIT")?,
Signal::SigTerm => write!(f, "SIGTERM")?,
Signal::SigInterrupt => write!(f, "SIGINT")?,
Signal::SigTrap => write!(f, "SIGTRAP")?,
};
Ok(())
}
}
pub trait Handler {
fn handle(&mut self, signal: Signal, info: siginfo_t, _context: &mut ucontext_t);
fn signals(&self) -> Vec<Signal>;
}
struct HandlerHolder {
handler: UnsafeCell<*mut dyn Handler>,
}
unsafe impl Send for HandlerHolder {}
const SIGNAL_STACK_SIZE: usize = 2 << 22;
static mut SIGNAL_STACK_PTR: *mut c_void = ptr::null_mut();
static mut SIGNAL_HANDLERS: [Option<HandlerHolder>; 32] = [
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None,
];
unsafe fn handle_signal(sig: c_int, info: siginfo_t, void: *mut c_void) {
let signal = &Signal::try_from(sig).unwrap();
let handler = {
match &SIGNAL_HANDLERS[*signal as usize] {
Some(handler_holder) => &mut **handler_holder.handler.get(),
None => return,
}
};
handler.handle(*signal, info, &mut *(void as *mut ucontext_t));
}
pub unsafe fn setup_signal_handler<T: 'static + Handler>(handler: &mut T) -> Result<(), Error> {
if SIGNAL_STACK_PTR.is_null() {
SIGNAL_STACK_PTR = malloc(SIGNAL_STACK_SIZE);
assert!(
!SIGNAL_STACK_PTR.is_null(),
"Failed to allocate signal stack with {} bytes!",
SIGNAL_STACK_SIZE
);
}
let mut ss: stack_t = mem::zeroed();
ss.ss_size = SIGNAL_STACK_SIZE;
ss.ss_sp = SIGNAL_STACK_PTR;
sigaltstack(addr_of_mut!(ss), ptr::null_mut() as _);
let mut sa: sigaction = mem::zeroed();
sigemptyset(addr_of_mut!(sa.sa_mask));
sigaddset(addr_of_mut!(sa.sa_mask), SIGALRM);
sa.sa_flags = SA_NODEFER | SA_SIGINFO | SA_ONSTACK;
sa.sa_sigaction = handle_signal as usize;
let signals = handler.signals();
for sig in signals {
write_volatile(
&mut SIGNAL_HANDLERS[sig as usize],
Some(HandlerHolder {
handler: UnsafeCell::new(handler as *mut dyn Handler),
}),
);
if sigaction(sig as i32, addr_of_mut!(sa), ptr::null_mut()) < 0 {
#[cfg(feature = "std")]
{
let err_str = CString::new(format!("Failed to setup {sig} handler")).unwrap();
libc::perror(err_str.as_ptr());
}
return Err(Error::unknown(format!("Could not set up {sig} handler")));
}
}
compiler_fence(Ordering::SeqCst);
Ok(())
}
#[cfg(unix)]
#[allow(clippy::inline_always)] #[inline(always)]
pub fn ucontext() -> Result<ucontext_t, Error> {
let mut ucontext = unsafe { mem::zeroed() };
if cfg!(not(target_os = "openbsd")) {
if unsafe { getcontext(&mut ucontext) } == 0 {
Ok(ucontext)
} else {
#[cfg(not(feature = "std"))]
unsafe {
libc::perror(b"Failed to get ucontext\n".as_ptr() as _);
};
#[cfg(not(feature = "std"))]
return Err(Error::unknown("Failed to get ucontex"));
#[cfg(feature = "std")]
Err(Error::unknown(format!(
"Failed to get ucontext: {:?}",
Errno::from_i32(errno())
)))
}
} else {
Ok(ucontext)
}
}