use crate::{JmpBuf, JmpBufFields, JmpBufStruct};
use crate::{SigJmpBuf, SigJmpBufFields, SigJmpBufStruct};
use libc::c_int;
use std::mem::MaybeUninit;
#[cfg(target_arch = "x86_64")]
macro_rules! maybesig_setjmp_asm {
($setjmp:ident, $jbuf_ptr:ident, $ifsig_savemask:ident, $closure_env_ptr:ident, $c2r:ident, $ret:ident) => {
std::arch::asm!(
"mov rdi, r12", "call {tmp}", "test eax, eax", "jne 1f", "mov rdi, r12", "mov rsi, r13", "call r14" , "1:", tmp = in(reg) $setjmp,
in("rsi") $ifsig_savemask,
in("r12") $jbuf_ptr,
in("r13") $closure_env_ptr,
in("r14") $c2r,
out("rax") $ret,
clobber_abi("sysv64"), out("rdi") _, );
}
}
#[cfg(target_arch = "aarch64")]
macro_rules! maybesig_setjmp_asm {
($sigsetjmp:ident, $jbuf_ptr:ident, $ifsig_savemask:ident, $closure_env_ptr:ident, $c2r:ident, $ret:ident) => {
std::arch::asm!(
"mov x0, x21", "blr {tmp}", "cbnz w0, 1f", "mov x0, x21", "mov x1, x20", "blr x22", "1:", tmp = in(reg) $sigsetjmp,
in("x1") $ifsig_savemask, out("x0") $ret, in("x20") $closure_env_ptr,
in("x21") $jbuf_ptr,
in("x22") $c2r,
clobber_abi("C"), );
}
}
#[inline(never)] pub fn call_with_setjmp<F>(mut callback: F) -> c_int
where
F: for<'a> FnOnce(&'a JmpBufFields) -> c_int,
{
extern "C" fn call_from_c_to_rust<F>(jbuf: JmpBuf, closure_env_ptr: *mut F) -> c_int
where
F: for<'a> FnOnce(&'a JmpBufFields) -> c_int,
{
unsafe { (closure_env_ptr.read())(&*jbuf) }
}
unsafe {
let mut jbuf = MaybeUninit::<JmpBufStruct>::zeroed().assume_init();
let ret: c_int;
let jbuf_ptr = std::ptr::addr_of_mut!(jbuf);
let closure_env_ptr = std::ptr::addr_of_mut!(callback);
std::mem::forget(callback);
extern "C" {
#[cfg_attr(target_os = "macos", link_name = "_setjmp")]
#[cfg_attr(target_os = "linux", link_name = "_setjmp")]
fn find_your_targets_setjmp(env: JmpBuf) -> c_int;
}
let setjmp = find_your_targets_setjmp;
let c2r = call_from_c_to_rust::<F>;
let unused_savemask: c_int = 0;
maybesig_setjmp_asm!(setjmp, jbuf_ptr, unused_savemask, closure_env_ptr, c2r, ret);
ret
}
}
#[inline(never)] pub fn call_with_sigsetjmp<F>(savemask: bool, mut callback: F) -> c_int
where
F: for<'a> FnOnce(&'a SigJmpBufFields) -> c_int,
{
extern "C" fn call_from_c_to_rust<F>(jbuf: SigJmpBuf, closure_env_ptr: *mut F) -> c_int
where
F: for<'a> FnOnce(&'a SigJmpBufFields) -> c_int,
{
unsafe { (closure_env_ptr.read())(&*jbuf) }
}
let savemask: c_int = if savemask { 1 } else { 0 };
unsafe {
let mut jbuf = MaybeUninit::<SigJmpBufStruct>::zeroed().assume_init();
let ret: c_int;
let jbuf_ptr = std::ptr::addr_of_mut!(jbuf);
let closure_env_ptr = std::ptr::addr_of_mut!(callback);
std::mem::forget(callback);
extern "C" {
#[cfg_attr(target_os = "macos", link_name = "sigsetjmp")]
#[cfg_attr(target_os = "linux", link_name = "__sigsetjmp")]
fn find_your_targets_sigsetjmp(env: SigJmpBuf, savemask: c_int) -> c_int;
}
let sigsetjmp = find_your_targets_sigsetjmp;
let c2r = call_from_c_to_rust::<F>;
maybesig_setjmp_asm!(sigsetjmp, jbuf_ptr, savemask, closure_env_ptr, c2r, ret);
ret
}
}