mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-16 18:31:23 +00:00
Add a signal handler for macos and linux.
Implementation Notes: - To avoid setjmp, longjmp, and the mess that those create, we instead set the interrupting context of the signal handler to return into the `throw_trap` routine. To my surprise, this actually works. The stack ends up getting unwound normally and the memory-oob error is caught by the trampoline.
This commit is contained in:
@ -1,3 +1,8 @@
|
||||
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.
|
||||
///
|
||||
@ -33,3 +38,104 @@ pub unsafe fn visit_fde(addr: *mut u8, size: usize, visitor: extern "C" fn(*mut
|
||||
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,
|
||||
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");
|
||||
|
Reference in New Issue
Block a user