use libc::{c_void, siginfo_t}; use nix::sys::signal::{ sigaction, SaFlags, SigAction, SigHandler, SigSet, Signal, SIGBUS, SIGFPE, SIGILL, SIGSEGV, }; /// `__register_frame` and `__deregister_frame` on macos take a single fde as an /// argument, so we need to parse the fde table here. /// /// This is a pretty direct port of llvm's fde handling code: /// https://llvm.org/doxygen/RTDyldMemoryManager_8cpp_source.html. #[cfg(target_os = "macos")] pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { unsafe fn process_fde(entry: *mut u8, visitor: extern "C" fn(*mut u8)) -> *mut u8 { let mut p = entry; let length = (p as *const u32).read_unaligned(); p = p.add(4); let offset = (p as *const u32).read_unaligned(); if offset != 0 { visitor(entry); } p.add(length as usize) } let mut p = addr; let end = p.add(size); loop { if p >= end { break; } p = process_fde(p, visitor); } } #[cfg(not(target_os = "macos"))] pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut u8)) { visitor(addr); } extern "C" { fn throw_trap(ty: i32) -> !; } pub unsafe fn install_signal_handler() { let sa = SigAction::new( SigHandler::SigAction(signal_trap_handler), SaFlags::SA_ONSTACK | SaFlags::SA_SIGINFO, SigSet::empty(), ); sigaction(SIGFPE, &sa).unwrap(); sigaction(SIGILL, &sa).unwrap(); sigaction(SIGSEGV, &sa).unwrap(); sigaction(SIGBUS, &sa).unwrap(); } extern "C" fn signal_trap_handler( signum: ::nix::libc::c_int, siginfo: *mut siginfo_t, ucontext: *mut c_void, ) { unsafe { /// By setting the instruction pointer of the interrupted context /// to `throw_trap` and the register of the first argument /// to the trap ID, we can approximate throwing an exception /// from a signal handler. set_context_to_throw(ucontext); } } #[cfg(all(target_os = "linux", target_arch = "x86_64"))] unsafe fn set_context_to_throw(ucontext: *mut c_void) { use libc::{ucontext_t, RDI, RIP}; let ucontext = ucontext as *mut ucontext_t; (*ucontext).uc_mcontext.gregs[RIP as usize] = throw_trap as u64; (*ucontext).uc_mcontext.gregs[RDI as usize] = 2; // `MemoryOutOfBounds` variant. } #[cfg(all(target_os = "macos", target_arch = "x86_64"))] unsafe fn set_context_to_throw(ucontext: *mut c_void) { #[allow(dead_code)] #[repr(C)] struct ucontext_t { uc_onstack: u32, uc_sigmask: u32, uc_stack: libc::stack_t, uc_link: *const ucontext_t, uc_mcsize: u64, uc_mcontext: *mut mcontext_t, } #[repr(C)] struct exception_state { trapno: u16, cpu: u16, err: u32, faultvaddr: u64, } #[repr(C)] struct regs { rax: u64, rbx: u64, rcx: u64, rdx: u64, rdi: u64, rsi: u64, rbp: u64, rsp: u64, r8: u64, r9: u64, r10: u64, r11: u64, r12: u64, r13: u64, r14: u64, r15: u64, rip: u64, rflags: u64, cs: u64, fs: u64, gs: u64, } #[allow(dead_code)] #[repr(C)] struct mcontext_t { es: exception_state, ss: regs, // ... } let ucontext = ucontext as *mut ucontext_t; (*(*ucontext).uc_mcontext).ss.rip = throw_trap as u64; (*(*ucontext).uc_mcontext).ss.rdi = 2; // `MemoryOutOfBounds` variant. } #[cfg(not(any( all(target_os = "macos", target_arch = "x86_64"), all(target_os = "linux", target_arch = "x86_64"), )))] compile_error!("This crate doesn't yet support compiling on operating systems other than linux and macos and architectures other than x86_64");