use core::arch::{asm, global_asm};
use core::sync::atomic::{AtomicUsize, Ordering};
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, cfi_reset_args_size_yield, InitialFunc,
StackCallFunc, TrapHandler,
};
use crate::util::EncodedValue;
pub const STACK_ALIGNMENT: usize = 4;
pub const PARENT_STACK_OFFSET: usize = 0;
pub const PARENT_LINK_OFFSET: usize = 16;
pub type StackWord = u32;
cfg_if::cfg_if! {
if #[cfg(target_env = "gnu")] {
macro_rules! dwarf {
($asm:expr) => { $asm }
}
} else {
macro_rules! dwarf {
($asm:expr) => { "" }
}
}
}
global_asm!(
".balign 16",
asm_function_begin!("stack_init_trampoline"),
dwarf!(".cfi_startproc"),
dwarf!(".cfi_signal_frame"),
"push ebp",
"mov ebp, esp",
"mov [edx - 16], esp",
"sub edx, 16",
"lea esp, [eax + 4]",
"pop dword ptr fs:[0x0]", "pop dword ptr fs:[0x4]", "pop dword ptr fs:[0x8]", "pop dword ptr fs:[0xe0c]", "pop dword ptr fs:[0xf78]", "mov ebp, edx",
dwarf!(".cfi_escape 0x0f, 5, 0x75, 0x00, 0x06, 0x23, 0x20"),
dwarf!(".cfi_offset esi, -24"),
dwarf!(".cfi_offset eip, -28"),
dwarf!(".cfi_offset ebp, -32"),
"call 2f",
"2:",
"jmp [edx + 8]",
asm_function_alt_entry!("stack_init_trampoline_return"),
"int3",
dwarf!(".cfi_endproc"),
asm_function_end!("stack_init_trampoline"),
);
global_asm!(
".balign 16",
asm_function_begin!("stack_call_trampoline"),
dwarf!(".cfi_startproc"),
dwarf!(".cfi_signal_frame"),
"push ebp",
"mov ebp, esp",
dwarf!(".cfi_def_cfa ebp, 8"),
dwarf!(".cfi_offset ebp, -8"),
"push dword ptr fs:[0xf78]", "push dword ptr fs:[0xe0c]", "push dword ptr fs:[0x8]", "push dword ptr fs:[0x4]", "push dword ptr fs:[0x0]", "mov esp, edx",
"pop dword ptr fs:[0x4]", "pop dword ptr fs:[0x8]", "pop dword ptr fs:[0xe0c]", "pop dword ptr fs:[0xf78]", "mov fs:[0x0], esp",
"call eax",
"push dword ptr fs:[0x8]", "push dword ptr fs:[0xf78]", "lea esp, [ebp - 20]",
"pop dword ptr fs:[0x0]", "pop dword ptr fs:[0x4]", "pop dword ptr fs:[0x8]", "pop dword ptr fs:[0xe0c]", "pop dword ptr fs:[0xf78]", "pop ebp",
"ret",
dwarf!(".cfi_endproc"),
asm_function_end!("stack_call_trampoline"),
);
global_asm!(
".balign 16",
asm_function_begin!("trap_handler_trampoline"),
"mov fs:[0x0], eax",
"jmp ebx",
asm_function_end!("trap_handler_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);
fn trap_handler_trampoline();
}
const EXCEPTION_LIST_END: usize = !0;
fn ntdll_final_exception_handler() -> usize {
static CACHED: AtomicUsize = AtomicUsize::new(0);
let cached = CACHED.load(Ordering::Relaxed);
if cached != 0 {
return cached;
}
let mut exception_list: *mut [usize; 2];
unsafe {
asm!("mov {}, fs:[0x0]", out(reg) exception_list, options(nostack, readonly, preserves_flags));
}
let mut exception_handler = 0;
while exception_list as usize != EXCEPTION_LIST_END {
let record = unsafe { *exception_list };
exception_list = record[0] as *mut _;
exception_handler = record[1];
}
if exception_handler == 0 {
panic!("FinalExceptionHandler not found");
}
exception_handler
}
#[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, Some(func as StackWord));
push(
&mut sp,
Some(stack_init_trampoline_return.as_ptr() as StackWord),
);
push(&mut sp, None);
push(&mut sp, Some(ntdll_final_exception_handler() as StackWord));
push(&mut sp, Some(EXCEPTION_LIST_END as StackWord));
let initial_seh_handler = sp;
allocate_obj_on_stack(&mut sp, 24, obj);
let initial_obj = sp;
push(&mut sp, Some(initial_obj as StackWord));
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));
push(&mut sp, Some(initial_seh_handler as StackWord));
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!(
"push dword ptr fs:[0xf78]", "push dword ptr fs:[0xe0c]", "push dword ptr fs:[0x8]", "push dword ptr fs:[0x4]", "push dword ptr fs:[0x0]",
"push esi",
"call [eax]",
"pop esi",
"pop dword ptr fs:[0x0]", "pop dword ptr fs:[0x4]", "pop dword ptr fs:[0x8]", "pop dword ptr fs:[0xe0c]", "pop dword ptr fs:[0xf78]",
inlateout("ecx") arg => ret_val,
lateout("edx") ret_sp,
in("edx") stack_base.get(),
in("eax") sp.get(),
lateout("ebx") _, lateout("edi") _,
clobber_abi("C"),
);
(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 dword ptr fs:[0xf78]", "push dword ptr fs:[0xe0c]", "push dword ptr fs:[0x8]", "push dword ptr fs:[0x4]", "push dword ptr fs:[0x0]",
"push ebp",
"push esi",
"call 2f",
"2:",
".equ .Loffset_yield, 3f - 2b",
"add dword ptr [esp], offset .Loffset_yield",
"mov edx, esp",
"mov esp, [eax]",
"pop ebp",
cfi_reset_args_size_yield!(),
"ret",
"3:",
"push ebp",
"mov [edx - 16], esp",
"lea esp, [eax + 4]",
"pop esi",
"pop ebp",
"pop dword ptr fs:[0x0]", "pop dword ptr fs:[0x4]", "pop dword ptr fs:[0x8]", "pop dword ptr fs:[0xe0c]", "pop dword ptr fs:[0xf78]",
inlateout("ecx") arg => ret_val,
in("eax") parent_link,
lateout("ebx") _, lateout("edi") _,
clobber_abi("C"),
);
ret_val
}
#[inline(always)]
pub unsafe fn switch_and_reset(arg: EncodedValue, parent_link: *mut StackPointer) -> ! {
asm!(
"mov eax, fs:[0x8]", "mov [edi + 12], eax",
"mov eax, fs:[0xf78]", "mov [edi + 8], eax",
"mov esp, [edi]",
"pop ebp",
"ret",
in("edi") parent_link,
in("ecx") arg,
in("edx") 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 "fastcall-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!(
"mov ebx, FS:[0x18]",
"push dword ptr fs:[0xf78]", "push dword ptr fs:[0xe0c]", "push dword ptr fs:[0x8]", "push dword ptr fs:[0x4]", "push dword ptr fs:[0x0]", "push esi",
"call 2f",
"2:",
".equ .Loffset_throw, 3f - 2b",
"add dword ptr [esp], offset .Loffset_throw",
"push ebp",
"mov [edx - 16], esp",
"mov esp, eax",
"pop eax",
"pop esi",
"pop ebp",
"pop dword ptr fs:[0x0]", "pop dword ptr fs:[0x4]", "pop dword ptr fs:[0x8]", "pop dword ptr fs:[0xe0c]", "pop dword ptr fs:[0xf78]",
"push eax",
"jmp {throw}",
"3:",
"pop esi",
"pop dword ptr fs:[0x0]", "pop dword ptr fs:[0x4]", "pop dword ptr fs:[0x8]", "pop dword ptr fs:[0xe0c]", "pop dword ptr fs:[0xf78]",
throw = sym throw,
in("ecx") forced_unwind.0.get(),
lateout("ecx") ret_val,
lateout("edx") ret_sp,
in("edx") stack_base.get(),
in("eax") sp.get(),
lateout("ebx") _, lateout("edi") _,
clobber_abi("C"),
);
(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(28);
drop_fn(ptr);
let base = stack_base.get() as *mut StackWord;
let stack = stack_ptr.get() as *const StackWord;
*base.sub(1) = *stack.add(3); *base.sub(2) = *stack.add(5); }
#[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 eip: u32,
pub esp: u32,
pub ebp: u32,
pub eax: u32,
pub ebx: u32,
pub ecx: u32,
pub edx: u32,
}
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 - 8;
allocate_obj_on_stack(&mut sp, 24, val);
let val_ptr = sp;
push(
&mut sp,
Some(stack_init_trampoline_return.as_ptr() as StackWord),
);
let initial_seh_handler = parent_link - 8;
TrapHandlerRegs {
eip: trap_handler_trampoline as u32,
esp: sp as u32,
eax: initial_seh_handler as u32,
ebx: handler as u32,
ecx: val_ptr as u32,
edx: parent_link as u32,
ebp: parent_link as u32,
}
}
#[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 usize;
let stack_limit = *base.sub(4);
let guaranteed_stack_bytes = *base.sub(5);
stack.update_teb_fields(stack_limit, guaranteed_stack_bytes);
});
let mut sp = adjusted_stack_base(&*stack).get();
push(&mut sp, None);
push(&mut sp, Some(ntdll_final_exception_handler() as StackWord));
push(&mut sp, Some(EXCEPTION_LIST_END as StackWord));
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));
asm_may_unwind_root!(
concat!("call ", asm_mangle!("stack_call_trampoline")),
in("ecx") arg,
in("edx") sp,
in("eax") f,
clobber_abi("fastcall"),
);
}