use crate::prelude::*;
use crate::runtime::vm::traphandlers::{TrapRegisters, TrapTest, tls};
use std::cell::Cell;
use std::ffi::c_void;
use std::io;
use windows_sys::Win32::Foundation::*;
use windows_sys::Win32::System::Diagnostics::Debug::*;
pub type SignalHandler = Box<dyn Fn(*mut EXCEPTION_POINTERS) -> bool + Send + Sync>;
pub struct TrapHandler {
exception_handler: *mut c_void,
continue_handler: *mut c_void,
}
unsafe impl Send for TrapHandler {}
unsafe impl Sync for TrapHandler {}
impl TrapHandler {
pub unsafe fn new(_macos_use_mach_ports: bool) -> TrapHandler {
let exception_handler = unsafe { AddVectoredExceptionHandler(1, Some(exception_handler)) };
if exception_handler.is_null() {
panic!(
"failed to add exception handler: {}",
io::Error::last_os_error()
);
}
let continue_handler = unsafe { AddVectoredContinueHandler(1, Some(continue_handler)) };
if continue_handler.is_null() {
panic!(
"failed to add continue handler: {}",
io::Error::last_os_error()
);
}
TrapHandler {
exception_handler,
continue_handler,
}
}
pub fn validate_config(&self, _macos_use_mach_ports: bool) {}
}
impl Drop for TrapHandler {
fn drop(&mut self) {
unsafe {
let rc = RemoveVectoredExceptionHandler(self.exception_handler);
if rc == 0 {
eprintln!(
"failed to remove exception handler: {}",
io::Error::last_os_error()
);
libc::abort();
}
let rc = RemoveVectoredContinueHandler(self.continue_handler);
if rc == 0 {
eprintln!(
"failed to remove continue handler: {}",
io::Error::last_os_error()
);
libc::abort();
}
}
}
}
std::thread_local! {
static LAST_EXCEPTION_PC: Cell<usize> = const { Cell::new(0) };
}
#[allow(
clippy::cast_possible_truncation,
reason = "too fiddly to handle and wouldn't help much anyway"
)]
unsafe extern "system" fn exception_handler(exception_info: *mut EXCEPTION_POINTERS) -> i32 {
let exception_info = unsafe { exception_info.as_mut().unwrap() };
let record = unsafe { &*exception_info.ExceptionRecord };
if record.ExceptionCode != EXCEPTION_ACCESS_VIOLATION
&& record.ExceptionCode != EXCEPTION_ILLEGAL_INSTRUCTION
&& record.ExceptionCode != EXCEPTION_INT_DIVIDE_BY_ZERO
&& record.ExceptionCode != EXCEPTION_INT_OVERFLOW
{
return EXCEPTION_CONTINUE_SEARCH;
}
tls::with(|info| {
let info = match info {
Some(info) => info,
None => return EXCEPTION_CONTINUE_SEARCH,
};
let context = unsafe { exception_info.ContextRecord.as_ref().unwrap() };
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
let regs = TrapRegisters {
pc: context.Rip as usize,
fp: context.Rbp as usize,
};
} else if #[cfg(target_arch = "aarch64")] {
let regs = TrapRegisters {
pc: context.Pc as usize,
fp: unsafe { context.Anonymous.Anonymous.Fp as usize },
};
} else {
compile_error!("unsupported platform");
}
}
let faulting_addr = if record.ExceptionCode == EXCEPTION_ACCESS_VIOLATION {
assert!(record.NumberParameters >= 2);
Some(record.ExceptionInformation[1])
} else {
None
};
match info.test_if_trap(regs, faulting_addr, |handler| handler(exception_info)) {
TrapTest::NotWasm => EXCEPTION_CONTINUE_SEARCH,
TrapTest::HandledByEmbedder => EXCEPTION_CONTINUE_EXECUTION,
TrapTest::Trap(handler) => {
let context = unsafe { exception_info.ContextRecord.as_mut().unwrap() };
LAST_EXCEPTION_PC.with(|s| s.set(handler.pc));
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
context.Rip = handler.pc as _;
context.Rbp = handler.fp as _;
context.Rsp = handler.sp as _;
context.Rax = 0;
context.Rdx = 0;
} else if #[cfg(target_arch = "aarch64")] {
context.Pc = handler.pc as _;
context.Sp = handler.sp as _;
context.Anonymous.Anonymous.Fp = handler.fp as _;
context.Anonymous.Anonymous.X0 = 0;
context.Anonymous.Anonymous.X1 = 0;
} else {
compile_error!("unsupported platform");
}
}
EXCEPTION_CONTINUE_EXECUTION
}
}
})
}
unsafe extern "system" fn continue_handler(exception_info: *mut EXCEPTION_POINTERS) -> i32 {
let context = unsafe { &(*(*exception_info).ContextRecord) };
let last_exception_pc = LAST_EXCEPTION_PC.with(|s| s.replace(0));
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
let context_pc = context.Rip as usize;
} else if #[cfg(target_arch = "aarch64")] {
let context_pc = context.Pc as usize;
} else {
compile_error!("unsupported platform");
}
}
if last_exception_pc == context_pc {
EXCEPTION_CONTINUE_EXECUTION
} else {
EXCEPTION_CONTINUE_SEARCH
}
}