use core::arch::{asm, global_asm};
use super::{allocate_obj_on_stack, push};
use crate::coroutine::adjusted_stack_base;
use crate::stack::{Stack, StackPointer, StackTebFields};
use crate::unwind::{
asm_may_unwind_root, asm_may_unwind_yield, InitialFunc, StackCallFunc, TrapHandler,
};
use crate::util::EncodedValue;
pub const STACK_ALIGNMENT: usize = 16;
pub const PARENT_STACK_OFFSET: usize = 0;
pub const PARENT_LINK_OFFSET: usize = 32;
pub type StackWord = u64;
global_asm!(
".balign 16",
asm_function_begin!("stack_init_trampoline"),
".seh_proc stack_init_trampoline",
"push rbp",
"mov [rsi - 32], rsp",
"sub rsi, 32",
"lea rsp, [rdx + 8]",
"mov qword ptr gs:[0x0], 0xffffffffffffffff",
"pop qword ptr gs:[0x8]", "pop qword ptr gs:[0x10]", "pop qword ptr gs:[0x1478]", "pop qword ptr gs:[0x1748]", "mov rbp, rsi",
".seh_stackalloc 40",
".seh_pushreg rbx",
".seh_stackalloc 8",
".seh_pushreg rbp",
".seh_savereg rsp, 0",
".seh_setframe rbp, 0",
".seh_endprologue",
"mov rdx, rsp",
"lea rcx, [rip + 0f]",
"push rcx",
"jmp [rsi + 8]",
"0:",
asm_function_alt_entry!("stack_init_trampoline_return"),
"int3",
".seh_endproc",
asm_function_end!("stack_init_trampoline"),
);
global_asm!(
".balign 16",
asm_function_begin!("stack_call_trampoline"),
".seh_proc stack_call_trampoline",
"push rbp",
".seh_pushreg rbp",
"push qword ptr gs:[0x1748]", ".seh_stackalloc 8",
"push qword ptr gs:[0x1478]", ".seh_stackalloc 8",
"push qword ptr gs:[0x10]", ".seh_stackalloc 8",
"push qword ptr gs:[0x8]", ".seh_stackalloc 8",
"push qword ptr gs:[0x0]", ".seh_stackalloc 8",
"mov rbp, rsp",
".seh_setframe rbp, 0",
".seh_endprologue",
"mov rsp, rsi",
"mov qword ptr gs:[0x0], 0xffffffffffffffff",
"mov gs:[0x8], r8", "mov gs:[0x10], r9", "mov gs:[0x1478], r10", "mov gs:[0x1748], r11", "call rdx",
"push qword ptr gs:[0x10]", "push qword ptr gs:[0x1748]", "mov rsp, rbp",
"pop qword ptr gs:[0x0]", "pop qword ptr gs:[0x8]", "pop qword ptr gs:[0x10]", "pop qword ptr gs:[0x1478]", "pop qword ptr gs:[0x1748]", "pop rbp",
"ret",
".seh_endproc",
asm_function_end!("stack_call_trampoline"),
);
extern "C" {
fn stack_init_trampoline(arg: EncodedValue, stack_base: StackPointer, stack_ptr: StackPointer);
static stack_init_trampoline_return: [u8; 0];
#[allow(dead_code)]
fn stack_call_trampoline(arg: *mut u8, sp: StackPointer, f: StackCallFunc);
}
#[inline]
pub unsafe fn init_stack<T>(stack: &impl Stack, func: InitialFunc<T>, obj: T) -> StackPointer {
let mut sp = adjusted_stack_base(stack).get();
push(&mut sp, None);
push(&mut sp, None);
push(&mut sp, Some(func as StackWord));
push(&mut sp, None);
allocate_obj_on_stack(&mut sp, 32, obj);
let teb = stack.teb_fields();
push(&mut sp, Some(teb.GuaranteedStackBytes as StackWord));
push(&mut sp, Some(teb.DeallocationStack as StackWord));
push(&mut sp, Some(teb.StackLimit as StackWord));
push(&mut sp, Some(teb.StackBase as StackWord));
debug_assert_eq!(sp % STACK_ALIGNMENT, 0);
push(
&mut sp,
Some(stack_init_trampoline as *const () as StackWord),
);
StackPointer::new_unchecked(sp)
}
#[inline]
pub unsafe fn switch_and_link(
arg: EncodedValue,
sp: StackPointer,
stack_base: StackPointer,
) -> (EncodedValue, Option<StackPointer>) {
let (ret_val, ret_sp);
asm_may_unwind_root!(
"lea rax, [rip + 2f]",
"push rax",
"push qword ptr gs:[0x1748]", "push qword ptr gs:[0x1478]", "push qword ptr gs:[0x10]", "push qword ptr gs:[0x8]", "push qword ptr gs:[0x0]",
"push rbx",
"call [rdx]",
"2:",
"pop rbx",
"pop qword ptr gs:[0x0]", "pop qword ptr gs:[0x8]", "pop qword ptr gs:[0x10]", "pop qword ptr gs:[0x1478]", "pop qword ptr gs:[0x1748]",
"add rsp, 8",
inlateout("rdi") arg => ret_val,
lateout("rsi") ret_sp,
in("rsi") stack_base.get() as u64,
in("rdx") sp.get() as u64,
lateout("r12") _, lateout("r13") _, lateout("r14") _, lateout("r15") _,
clobber_abi("sysv64"),
);
(ret_val, StackPointer::new(ret_sp))
}
#[inline(always)]
pub unsafe fn switch_yield(arg: EncodedValue, parent_link: *mut StackPointer) -> EncodedValue {
let ret_val;
asm_may_unwind_yield!(
"push qword ptr gs:[0x1748]", "push qword ptr gs:[0x1478]", "push qword ptr gs:[0x10]", "push qword ptr gs:[0x8]", "push qword ptr gs:[0x0]",
"push rbp",
"push rbx",
"lea rax, [rip + 2f]",
"push rax",
"mov rsi, rsp",
"mov rsp, [rdx]",
"pop rbp",
"ret",
"2:",
"push rbp",
"mov [rsi - 32], rsp",
"lea rsp, [rdx + 8]",
"pop rbx",
"pop rbp",
"pop qword ptr gs:[0x0]", "pop qword ptr gs:[0x8]", "pop qword ptr gs:[0x10]", "pop qword ptr gs:[0x1478]", "pop qword ptr gs:[0x1748]",
inlateout("rdi") arg => ret_val,
in("rdx") parent_link as u64,
lateout("r12") _, lateout("r13") _, lateout("r14") _, lateout("r15") _,
clobber_abi("sysv64"),
);
ret_val
}
#[inline(always)]
pub unsafe fn switch_and_reset(arg: EncodedValue, parent_link: *mut StackPointer) -> ! {
asm!(
"mov rax, gs:[0x10]", "mov [rdx + 24], rax",
"mov rax, gs:[0x1748]", "mov [rdx + 16], rax",
"mov rsp, [rdx]",
"pop rbp",
"ret",
in("rdx") parent_link as u64,
in("rdi") arg,
in("rsi") 0,
options(noreturn),
);
}
#[inline]
#[cfg(feature = "asm-unwind")]
pub unsafe fn switch_and_throw(
forced_unwind: crate::unwind::ForcedUnwind,
sp: StackPointer,
stack_base: StackPointer,
) -> (EncodedValue, Option<StackPointer>) {
extern "sysv64-unwind" fn throw(forced_unwind: crate::unwind::ForcedUnwind) -> ! {
extern crate std;
use std::boxed::Box;
std::panic::resume_unwind(Box::new(forced_unwind));
}
let (ret_val, ret_sp);
asm_may_unwind_root!(
"lea rax, [rip + 2f]",
"push rax",
"push qword ptr gs:[0x1748]", "push qword ptr gs:[0x1478]", "push qword ptr gs:[0x10]", "push qword ptr gs:[0x8]", "push qword ptr gs:[0x0]", "push rbx",
"push rax",
"push rbp",
"mov [rsi - 32], rsp",
"mov rsp, rdx",
"pop rax",
"pop rbx",
"pop rbp",
"pop qword ptr gs:[0x0]", "pop qword ptr gs:[0x8]", "pop qword ptr gs:[0x10]", "pop qword ptr gs:[0x1478]", "pop qword ptr gs:[0x1748]",
"push rax",
"jmp {throw}",
"2:",
"pop rbx",
"pop qword ptr gs:[0x0]", "pop qword ptr gs:[0x8]", "pop qword ptr gs:[0x10]", "pop qword ptr gs:[0x1478]", "pop qword ptr gs:[0x1748]", "add rsp, 8",
throw = sym throw,
in("rdi") forced_unwind.0.get(),
lateout("rdi") ret_val,
lateout("rsi") ret_sp,
in("rsi") stack_base.get() as u64,
in("rdx") sp.get() as u64,
lateout("r12") _, lateout("r13") _, lateout("r14") _, lateout("r15") _,
clobber_abi("sysv64"),
);
(ret_val, StackPointer::new(ret_sp))
}
#[inline]
pub unsafe fn drop_initial_obj(
stack_base: StackPointer,
stack_ptr: StackPointer,
drop_fn: unsafe fn(ptr: *mut u8),
) {
let ptr = (stack_ptr.get() as *mut u8).add(40);
drop_fn(ptr);
let base = stack_base.get() as *mut StackWord;
let stack = stack_ptr.get() as *const StackWord;
*base.sub(1) = *stack.add(2); *base.sub(2) = *stack.add(4); }
#[inline]
pub unsafe fn reset_teb_fields_from_suspended(stack_base: StackPointer, stack_ptr: StackPointer) {
let base = stack_base.get() as *mut StackWord;
let stack = stack_ptr.get() as *const StackWord;
*base.sub(1) = *stack.add(5); *base.sub(2) = *stack.add(7); }
#[inline]
pub unsafe fn update_stack_teb_fields(stack: &mut impl Stack) {
let base = adjusted_stack_base(stack).get() as *const StackWord;
let stack_limit = *base.sub(1) as usize;
let guaranteed_stack_bytes = *base.sub(2) as usize;
stack.update_teb_fields(stack_limit, guaranteed_stack_bytes);
}
#[inline]
pub unsafe fn read_parent_stack_teb_fields(stack_ptr: StackPointer) -> StackTebFields {
let stack_ptr = stack_ptr.get() as *const StackWord;
StackTebFields {
StackBase: *stack_ptr.add(4) as usize,
StackLimit: *stack_ptr.add(5) as usize,
DeallocationStack: *stack_ptr.add(6) as usize,
GuaranteedStackBytes: *stack_ptr.add(7) as usize,
}
}
#[inline]
pub unsafe fn update_parent_stack_teb_fields(
stack_ptr: StackPointer,
stack_limit: usize,
guaranteed_stack_bytes: usize,
) {
let stack_ptr = stack_ptr.get() as *mut StackWord;
*stack_ptr.add(5) = stack_limit as StackWord;
*stack_ptr.add(7) = guaranteed_stack_bytes as StackWord;
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug)]
pub struct TrapHandlerRegs {
pub rip: u64,
pub rsp: u64,
pub rbp: u64,
pub rdi: u64,
pub rsi: u64,
}
pub unsafe fn setup_trap_trampoline<T>(
stack_base: StackPointer,
val: T,
handler: TrapHandler<T>,
) -> TrapHandlerRegs {
let parent_link = stack_base.get() - PARENT_LINK_OFFSET;
let mut sp = parent_link;
allocate_obj_on_stack(&mut sp, 32, val);
let val_ptr = sp;
push(
&mut sp,
Some(stack_init_trampoline_return.as_ptr() as StackWord),
);
TrapHandlerRegs {
rip: handler as u64,
rsp: sp as u64,
rdi: val_ptr as u64,
rsi: parent_link as u64,
rbp: parent_link as u64,
}
}
#[inline]
pub unsafe fn on_stack<S: Stack>(arg: *mut u8, stack: S, f: StackCallFunc) {
let stack = scopeguard::guard(stack, |mut stack| {
let base = adjusted_stack_base(&stack).get() as *const u64;
let stack_limit = *base.sub(1) as usize;
let guaranteed_stack_bytes = *base.sub(2) as usize;
stack.update_teb_fields(stack_limit, guaranteed_stack_bytes);
});
let teb_fields = stack.teb_fields();
asm_may_unwind_root!(
concat!("call ", asm_mangle!("stack_call_trampoline")),
in("rdi") arg,
in("rsi") adjusted_stack_base(&*stack).get(),
in("rdx") f,
in("r8") teb_fields.StackBase,
in("r9") teb_fields.StackLimit,
in("r10") teb_fields.DeallocationStack,
in("r11") teb_fields.GuaranteedStackBytes,
clobber_abi("sysv64"),
);
}