mirror of
https://github.com/fluencelabs/wasmer
synced 2024-12-13 06:15:33 +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:
parent
57bfa9b0a4
commit
caf2205936
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1213,6 +1213,7 @@ dependencies = [
|
||||
"inkwell 0.1.0 (git+https://github.com/TheDan64/inkwell?branch=llvm7-0)",
|
||||
"lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.49 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -12,6 +12,7 @@ hashbrown = "0.1.8"
|
||||
smallvec = "0.6.8"
|
||||
goblin = "0.0.20"
|
||||
libc = "0.2.49"
|
||||
nix = "0.13.0"
|
||||
capstone = "0.5.0"
|
||||
|
||||
[build-dependencies]
|
||||
|
@ -115,7 +115,7 @@ public:
|
||||
}
|
||||
|
||||
uint32_t type_id, value_num;
|
||||
uint64_t values[];
|
||||
uint64_t values[1];
|
||||
};
|
||||
|
||||
struct WasmModule {
|
||||
@ -140,7 +140,7 @@ extern "C" {
|
||||
return RESULT_OK;
|
||||
}
|
||||
|
||||
void throw_trap(WasmTrap::Type ty) {
|
||||
[[noreturn]] void throw_trap(WasmTrap::Type ty) {
|
||||
throw WasmTrap(ty);
|
||||
}
|
||||
|
||||
|
@ -243,6 +243,10 @@ impl LLVMBackend {
|
||||
)
|
||||
};
|
||||
|
||||
unsafe {
|
||||
crate::platform::install_signal_handler();
|
||||
}
|
||||
|
||||
if res != LLVMResult::OK {
|
||||
panic!("failed to load object")
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -4,9 +4,12 @@ use wabt::wat2wasm;
|
||||
|
||||
static WAT: &'static str = r#"
|
||||
(module
|
||||
(memory 1)
|
||||
(type (;0;) (func (param i32) (result i32)))
|
||||
(func (;0;) (type 0) (param i32) (result i32)
|
||||
unreachable)
|
||||
i32.const 0x20000
|
||||
i32.load
|
||||
)
|
||||
(export "select_trap_l" (func 0))
|
||||
)
|
||||
"#;
|
||||
|
Loading…
Reference in New Issue
Block a user