#![allow(non_snake_case)]
use crate::traphandlers::{tls, wasmtime_longjmp};
use mach::exception_types::*;
use mach::kern_return::*;
use mach::mach_init::*;
use mach::mach_port::*;
use mach::message::*;
use mach::port::*;
use mach::thread_act::*;
use mach::traps::*;
use std::mem;
use std::thread;
mod mach_addons {
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(dead_code)]
use mach::{
exception_types::*, kern_return::*, mach_types::*, message::*, port::*, thread_status::*,
};
use std::mem;
#[repr(C)]
#[derive(Copy, Clone, Debug)]
#[allow(dead_code)]
pub struct NDR_record_t {
mig_vers: libc::c_uchar,
if_vers: libc::c_uchar,
reserved1: libc::c_uchar,
mig_encoding: libc::c_uchar,
int_rep: libc::c_uchar,
char_rep: libc::c_uchar,
float_rep: libc::c_uchar,
reserved32: libc::c_uchar,
}
extern "C" {
pub static NDR_record: NDR_record_t;
}
#[repr(C)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
pub struct __Request__exception_raise_t {
pub Head: mach_msg_header_t,
pub msgh_body: mach_msg_body_t,
pub thread: mach_msg_port_descriptor_t,
pub task: mach_msg_port_descriptor_t,
pub NDR: NDR_record_t,
pub exception: exception_type_t,
pub codeCnt: mach_msg_type_number_t,
pub code: [i64; 2],
}
#[repr(C)]
#[allow(dead_code)]
#[derive(Copy, Clone, Debug)]
pub struct __Reply__exception_raise_t {
pub Head: mach_msg_header_t,
pub NDR: NDR_record_t,
pub RetCode: kern_return_t,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Default, Hash, PartialOrd, PartialEq, Eq, Ord)]
pub struct arm_thread_state64_t {
pub __x: [u64; 29],
pub __fp: u64, pub __lr: u64, pub __sp: u64, pub __pc: u64,
pub __cpsr: u32,
pub __pad: u32,
}
impl arm_thread_state64_t {
pub fn count() -> mach_msg_type_number_t {
(mem::size_of::<Self>() / mem::size_of::<u32>()) as mach_msg_type_number_t
}
}
pub static ARM_THREAD_STATE64: thread_state_flavor_t = 6;
#[cfg(target_arch = "x86_64")]
pub static THREAD_STATE_NONE: thread_state_flavor_t = 13;
#[cfg(target_arch = "aarch64")]
pub static THREAD_STATE_NONE: thread_state_flavor_t = 5;
extern "C" {
pub fn thread_set_state(
target_act: thread_port_t,
flavor: thread_state_flavor_t,
new_state: thread_state_t,
new_stateCnt: mach_msg_type_number_t,
) -> kern_return_t;
pub fn thread_set_exception_ports(
thread: thread_port_t,
exception_mask: exception_mask_t,
new_port: mach_port_t,
behavior: libc::c_uint,
new_flavor: thread_state_flavor_t,
) -> kern_return_t;
}
}
use mach_addons::*;
pub enum Void {}
pub type SignalHandler<'a> = dyn Fn(Void) -> bool + Send + Sync + 'a;
static mut WASMTIME_PORT: mach_port_name_t = MACH_PORT_NULL;
pub unsafe fn platform_init() {
let me = mach_task_self();
let kret = mach_port_allocate(me, MACH_PORT_RIGHT_RECEIVE, &mut WASMTIME_PORT);
assert_eq!(kret, KERN_SUCCESS, "failed to allocate port");
let kret = mach_port_insert_right(me, WASMTIME_PORT, WASMTIME_PORT, MACH_MSG_TYPE_MAKE_SEND);
assert_eq!(kret, KERN_SUCCESS, "failed to insert right");
thread::spawn(|| handler_thread());
}
#[repr(C)]
#[allow(dead_code)]
struct ExceptionRequest {
body: __Request__exception_raise_t,
trailer: mach_msg_trailer_t,
}
unsafe fn handler_thread() {
const EXCEPTION_MSG_ID: mach_msg_id_t = 2405;
loop {
let mut request: ExceptionRequest = mem::zeroed();
let kret = mach_msg(
&mut request.body.Head,
MACH_RCV_MSG,
0,
mem::size_of_val(&request) as u32,
WASMTIME_PORT,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL,
);
if kret != KERN_SUCCESS {
eprintln!("mach_msg failed with {} ({0:x})", kret);
libc::abort();
}
if request.body.Head.msgh_id != EXCEPTION_MSG_ID {
eprintln!("unexpected msg header id {}", request.body.Head.msgh_id);
libc::abort();
}
let reply_code = if handle_exception(&mut request) {
KERN_SUCCESS
} else {
KERN_FAILURE
};
let mut reply: __Reply__exception_raise_t = mem::zeroed();
reply.Head.msgh_bits =
MACH_MSGH_BITS(request.body.Head.msgh_bits & MACH_MSGH_BITS_REMOTE_MASK, 0);
reply.Head.msgh_size = mem::size_of_val(&reply) as u32;
reply.Head.msgh_remote_port = request.body.Head.msgh_remote_port;
reply.Head.msgh_local_port = MACH_PORT_NULL;
reply.Head.msgh_id = request.body.Head.msgh_id + 100;
reply.NDR = NDR_record;
reply.RetCode = reply_code;
mach_msg(
&mut reply.Head,
MACH_SEND_MSG,
mem::size_of_val(&reply) as u32,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL,
);
}
}
unsafe fn handle_exception(request: &mut ExceptionRequest) -> bool {
match request.body.exception as u32 {
EXC_BAD_ACCESS | EXC_BAD_INSTRUCTION => {}
_ => return false,
}
cfg_if::cfg_if! {
if #[cfg(target_arch = "x86_64")] {
use mach::structs::x86_thread_state64_t;
use mach::thread_status::x86_THREAD_STATE64;
type ThreadState = x86_thread_state64_t;
let thread_state_flavor = x86_THREAD_STATE64;
let get_pc_and_fp = |state: &ThreadState| (
state.__rip as *const u8,
state.__rbp as usize,
);
let resume = |state: &mut ThreadState, pc: usize, fp: usize| {
if state.__rsp % 16 == 0 {
state.__rsp -= 8;
*(state.__rsp as *mut u64) = state.__rip;
}
state.__rip = unwind as u64;
state.__rdi = pc as u64;
state.__rsi = fp as u64;
};
let mut thread_state = ThreadState::new();
} else if #[cfg(target_arch = "aarch64")] {
type ThreadState = arm_thread_state64_t;
let thread_state_flavor = ARM_THREAD_STATE64;
let get_pc_and_fp = |state: &ThreadState| (
state.__pc as *const u8,
state.__fp as usize,
);
let resume = |state: &mut ThreadState, pc: usize, fp: usize| {
state.__lr = pc as u64;
state.__x[0] = pc as u64;
state.__x[1] = fp as u64;
state.__pc = unwind as u64;
};
let mut thread_state = mem::zeroed::<ThreadState>();
} else {
compile_error!("unsupported target architecture");
}
}
let origin_thread = request.body.thread.name;
let mut thread_state_count = ThreadState::count();
let kret = thread_get_state(
origin_thread,
thread_state_flavor,
&mut thread_state as *mut ThreadState as *mut u32,
&mut thread_state_count,
);
if kret != KERN_SUCCESS {
return false;
}
let (pc, fp) = get_pc_and_fp(&thread_state);
if !super::IS_WASM_PC(pc as usize) {
return false;
}
resume(&mut thread_state, pc as usize, fp);
let kret = thread_set_state(
origin_thread,
thread_state_flavor,
&mut thread_state as *mut ThreadState as *mut u32,
thread_state_count,
);
kret == KERN_SUCCESS
}
unsafe extern "C" fn unwind(wasm_pc: *const u8, wasm_fp: usize) -> ! {
let jmp_buf = tls::with(|state| {
let state = state.unwrap();
state.set_jit_trap(wasm_pc, wasm_fp);
state.jmp_buf.get()
});
debug_assert!(!jmp_buf.is_null());
wasmtime_longjmp(jmp_buf);
}
#[cold]
pub fn lazy_per_thread_init() {
unsafe {
assert!(WASMTIME_PORT != MACH_PORT_NULL);
let this_thread = mach_thread_self();
let kret = thread_set_exception_ports(
this_thread,
EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION,
WASMTIME_PORT,
EXCEPTION_DEFAULT | MACH_EXCEPTION_CODES,
mach_addons::THREAD_STATE_NONE,
);
mach_port_deallocate(mach_task_self(), this_thread);
assert_eq!(kret, KERN_SUCCESS, "failed to set thread exception port");
}
}