Turns out we can just throw an exception from the signal handler

without any fancy tricks.

This has been tested on macos, hopefully it works on linux too.
This commit is contained in:
Lachlan Sneff 2019-03-03 02:15:53 -08:00
parent 9cfda6800f
commit e362b56b07

View File

@ -61,81 +61,11 @@ extern "C" fn signal_trap_handler(
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);
/// Apparently, we can unwind from arbitary instructions, as long
/// as we don't need to catch the exception inside the function that
/// was interrupted.
///
/// This works on macos, not sure about linux.
throw_trap(2);
}
}
#[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");