use std::cell::RefCell;
use std::mem;
use gimli::UnwindContext;
use log::error;
use crate::compiler::{eh_frames, host_eh_frames, unwind_ctx};
use crate::unwind::cfi::{unwind_gimli, unwind_host, Registers};
use crate::unwind::{PanicData, PanicMessage, PanicPayload, RangeVec};
use crate::unwind::signal_stack::sigalt_stack_init;
#[macro_export]
macro_rules! jit_panic(
($pattern:literal $(,$arg:expr)*) => ({
$crate::unwind::PanicMessage::set($crate::unwind::PanicMessage {
data: format!($pattern $(,$arg)*),
});
$crate::unwind::jit_sync_panic()
});
($msg:expr) => ({
$crate::unwind::PanicMessage::set($crate::unwind::PanicMessage {
data: $msg.to_string(),
});
$crate::unwind::jit_sync_panic()
});
() => ({
$crate::unwind::jit_sync_panic()
});
);
pub use jit_panic;
use crate::prelude::HostUnwindInfo;
#[inline]
#[cfg(all(target_arch="x86_64", any(target_os="linux", target_os="macos")))]
pub unsafe fn cause_jit_async_panic() -> ! {
core::arch::asm!("ud2"); panic!() }
static mut PREV_SIGSEGV: libc::sigaction = unsafe { mem::zeroed() };
static mut PREV_SIGBUS: libc::sigaction = unsafe { mem::zeroed() };
static mut PREV_SIGILL: libc::sigaction = unsafe { mem::zeroed() };
static mut PREV_SIGFPE: libc::sigaction = unsafe { mem::zeroed() };
struct TrapHandlerInfo {
sp: usize,
}
pub struct TrapHandler;
impl TrapHandler {
pub unsafe fn new() -> TrapHandler {
sigalt_stack_init(); for_each_handler(|slot, sig| {
let mut handler: libc::sigaction = unsafe { mem::zeroed() };
handler.sa_flags = libc::SA_SIGINFO | libc::SA_NODEFER | libc::SA_ONSTACK;
handler.sa_sigaction = (trap_handler as *const ()).addr();
unsafe {
libc::sigemptyset(&mut handler.sa_mask);
if libc::sigaction(sig, &handler, slot) != 0 {
panic!("unable to install signal handler. Cause: {}", std::io::Error::last_os_error());
}
}
});
TrapHandler
}
}
unsafe fn for_each_handler(mut f: impl FnMut(*mut libc::sigaction, i32)) {
f(&raw mut PREV_SIGSEGV, libc::SIGSEGV);
#[cfg(target_vendor="apple")]
f(&raw mut PREV_SIGBUS, libc::SIGBUS);
#[cfg(target_arch="x86_64")]
f(&raw mut PREV_SIGFPE, libc::SIGFPE);
f(&raw mut PREV_SIGILL, libc::SIGILL);
}
impl Drop for TrapHandler {
fn drop(&mut self) {
unsafe {
for_each_handler(|slot, sig| {
let mut prev: libc::sigaction = mem::zeroed();
if libc::sigaction(sig, slot, &mut prev) != 0 {
error!("unable to reinstall signal handler. Cause: {}", std::io::Error::last_os_error());
std::process::exit(-1);
}
if prev.sa_sigaction != (trap_handler as *const ()).addr() {
error!("wrong signal handler detected. All hope is lost, abandon your posts!");
std::process::exit(-1);
}
})
}
}
}
unsafe extern "C" fn trap_handler(
signum: libc::c_int,
siginfo: *mut libc::siginfo_t,
context: *mut libc::c_void,
) {
let prev = match signum {
libc::SIGSEGV => &raw const PREV_SIGSEGV,
libc::SIGBUS => &raw const PREV_SIGBUS,
libc::SIGFPE => &raw const PREV_SIGFPE,
libc::SIGILL => &raw const PREV_SIGILL,
_ => {
error!("unknown signal!");
std::process::exit(-1);
},
};
let mut regs = Registers::load(context);
let handled = PanicData::set(|data| {
let eh_frames = eh_frames();
let host_eh_frames = host_eh_frames();
if let (
Ok(eh_frames),
Ok(host_eh_frames),
) = (eh_frames.read(), host_eh_frames.read()) {
if backtrace_thread_local(eh_frames.slice(), &*host_eh_frames, &mut regs, data) {
regs.store(context);
true
} else{
false
}
} else {
false
}
}, false);
if handled {
return;
}
unsafe {
delegate_sig(prev, signum, siginfo, context);
}
}
#[inline(never)]
#[no_mangle]
pub fn jit_sync_panic() -> ! {
let mut regs = Registers::steal();
let handled = PanicData::set(|data| {
let eh_frames = eh_frames();
let host_eh_frames = host_eh_frames();
if let (
Ok(eh_frames),
Ok(host_eh_frames),
) = (eh_frames.read(), host_eh_frames.read()) {
if backtrace_thread_local(eh_frames.slice(), &*host_eh_frames, &mut regs, data) {
true
} else{
false
}
} else {
false
}
}, false);
if handled {
unsafe { regs.restore() }
} else {
eprintln!("failed to initialize JIT panic");
std::process::exit(-1);
}
}
fn backtrace_thread_local(
unwind_data: &[u8],
host_frames: &RangeVec<usize, HostUnwindInfo>,
regs: &mut Registers,
data: &mut PanicPayload,
) -> bool {
unwind_ctx(|ctx| {
unsafe { backtrace(unwind_data, host_frames, regs, data, ctx) }
}).unwrap_or(false)
}
unsafe fn backtrace(
unwind_data: &[u8],
host_frames: &RangeVec<usize, HostUnwindInfo>,
regs: &mut Registers,
data: &mut PanicPayload,
context: &mut UnwindContext<usize>,
) -> bool {
let mut jit_frame_c: u32 = 0;
for frame_num in 0..16 {
let current_ip = if frame_num == 0 {
regs.rip
} else {
regs.rip - 1
};
match unwind_gimli(unwind_data, current_ip, regs, context) {
Ok(mut entry) => {
jit_frame_c += 1;
entry.flags = entry.flags.set_jit_frame();
data.backtrace.push(entry);
if regs.rip == 0 {
return false; }
}
Err(gimli::Error::NoUnwindInfoForAddress) if jit_frame_c != 0 => {
data.reached_host = true;
return true;
}
Err(gimli::Error::NoUnwindInfoForAddress) => {
match unwind_host(host_frames, current_ip, regs, context) {
Ok(entry) => {
data.backtrace.push(entry);
if regs.rip == 0 {
return false; }
}
Err(err) => {
return false;
}
}
},
Err(_err) => {
return false;
}
}
}
false
}
pub unsafe fn delegate_sig(
prev: *const libc::sigaction,
signum: libc::c_int,
siginfo: *mut libc::siginfo_t,
ctx: *mut libc::c_void,
) {
unsafe {
let prev = *prev;
if prev.sa_flags & libc::SA_SIGINFO != 0 {
mem::transmute::<usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void)>(prev.sa_sigaction)(signum, siginfo, ctx);
} else if prev.sa_sigaction == libc::SIG_DFL || prev.sa_sigaction == libc::SIG_IGN {
libc::sigaction(signum, &prev as *const _, std::ptr::null_mut());
} else {
mem::transmute::<usize, extern "C" fn(libc::c_int)>(prev.sa_sigaction)(signum);
}
}
}