Path: blob/main/crates/fiber/src/stackswitch/aarch64.rs
1692 views
// A WORD OF CAUTION1//2// This entire file basically needs to be kept in sync with itself. It's not3// really possible to modify just one bit of this file without understanding4// all the other bits. Documentation tries to reference various bits here and5// there but try to make sure to read over everything before tweaking things!6//7// Also at this time this file is heavily based off the x86_64 file, so you'll8// probably want to read that one as well.9//10// Finally, control flow integrity hardening has been applied to the code using11// the Pointer Authentication (PAuth) and Branch Target Identification (BTI)12// technologies from the Arm instruction set architecture:13// * All callable functions start with either the `BTI c` or `PACIASP`/`PACIBSP`14// instructions15// * Return addresses are signed and authenticated using the stack pointer16// value as a modifier (similarly to the salt in a HMAC operation); the17// `DW_CFA_AARCH64_negate_ra_state` DWARF operation (aliased with the18// `.cfi_window_save` assembler directive) informs an unwinder about this1920use super::wasmtime_fiber_start;21use wasmtime_asm_macros::asm_func;2223cfg_if::cfg_if! {24if #[cfg(target_vendor = "apple")] {25macro_rules! paci1716 { () => ("pacib1716\n"); }26macro_rules! pacisp { () => ("pacibsp\n"); }27macro_rules! autisp { () => ("autibsp\n"); }28macro_rules! sym_adrp { ($s:tt) => (concat!($s, "@PAGE")); }29macro_rules! sym_add { ($s:tt) => (concat!($s, "@PAGEOFF")); }30} else {31macro_rules! paci1716 { () => ("pacia1716\n"); }32macro_rules! pacisp { () => ("paciasp\n"); }33macro_rules! autisp { () => ("autiasp\n"); }34macro_rules! sym_adrp { ($s:tt) => (concat!($s, "")); }35macro_rules! sym_add { ($s:tt) => (concat!(":lo12:", $s)); }36}37}3839// fn(top_of_stack(%x0): *mut u8)40asm_func!(41wasmtime_versioned_export_macros::versioned_stringify_ident!(wasmtime_fiber_switch),42concat!(43"44.cfi_startproc45",46pacisp!(),47"48.cfi_window_save49// Save all callee-saved registers on the stack since we're50// assuming they're clobbered as a result of the stack switch.51stp x29, x30, [sp, -16]!52stp x20, x19, [sp, -16]!53stp x22, x21, [sp, -16]!54stp x24, x23, [sp, -16]!55stp x26, x25, [sp, -16]!56stp x28, x27, [sp, -16]!57stp d9, d8, [sp, -16]!58stp d11, d10, [sp, -16]!59stp d13, d12, [sp, -16]!60stp d15, d14, [sp, -16]!6162// Load our previously saved stack pointer to resume to, and save63// off our current stack pointer on where to come back to64// eventually.65ldr x8, [x0, -0x10]66mov x9, sp67str x9, [x0, -0x10]6869// Switch to the new stack and restore all our callee-saved70// registers after the switch and return to our new stack.71mov sp, x872ldp d15, d14, [sp], 1673ldp d13, d12, [sp], 1674ldp d11, d10, [sp], 1675ldp d9, d8, [sp], 1676ldp x28, x27, [sp], 1677ldp x26, x25, [sp], 1678ldp x24, x23, [sp], 1679ldp x22, x21, [sp], 1680ldp x20, x19, [sp], 1681ldp x29, x30, [sp], 1682",83autisp!(),84"85.cfi_window_save86ret87.cfi_endproc88",89),90);9192// fn(93// top_of_stack(%x0): *mut u8,94// entry_point(%x1): extern fn(*mut u8, *mut u8),95// entry_arg0(%x2): *mut u8,96// )97// We set up the newly initialized fiber, so that it resumes execution98// from wasmtime_fiber_start(). As a result, we need a signed address99// of this function, so there are 2 requirements:100// * The fiber stack pointer value that is used by the signing operation101// must match the value when the pointer is authenticated inside102// wasmtime_fiber_switch(), otherwise the latter would fault103// * We would like to use an instruction that is executed as a no-op by104// processors that do not support PAuth, so that the code is105// backward-compatible and there is no duplication; `PACIA1716` is a106// suitable one, which has the following operand register107// conventions:108// * X17 contains the pointer value to sign109// * X16 contains the modifier value110//111// TODO: Use the PACGA instruction to authenticate the saved register112// state, which avoids creating signed pointers to113// wasmtime_fiber_start(), and provides wider coverage.114#[rustfmt::skip]115asm_func!(116wasmtime_versioned_export_macros::versioned_stringify_ident!(wasmtime_fiber_init),117concat!(118"119.cfi_startproc120hint #34 // bti c121sub x16, x0, #16122adrp x17, ", sym_adrp!("{fiber}"), "123add x17, x17, ", sym_add!("{fiber}"), "124",125paci1716!(),126"127str x17, [x16, -0x8] // x17 => lr128str x0, [x16, -0x18] // x0 => x19129stp x2, x1, [x0, -0x38] // x1 => x20, x2 => x21130131// `wasmtime_fiber_switch` has an 0xa0 byte stack, and we add 0x10 more for132// the original reserved 16 bytes.133add x8, x0, -0xb0134str x8, [x0, -0x10]135ret136.cfi_endproc137",138),139fiber = sym wasmtime_fiber_start,140);141142// See the x86_64 file for more commentary on what these CFI directives are143// doing. Like over there note that the relative offsets to registers here144// match the frame layout in `wasmtime_fiber_switch`.145asm_func!(146wasmtime_versioned_export_macros::versioned_stringify_ident!(wasmtime_fiber_start),147"148.cfi_startproc simple149.cfi_def_cfa_offset 0150.cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \1515, /* the byte length of this expression */ \1520x6f, /* DW_OP_reg31(%sp) */ \1530x06, /* DW_OP_deref */ \1540x23, 0xa0, 0x1 /* DW_OP_plus_uconst 0xa0 */155.cfi_rel_offset x29, -0x10156.cfi_rel_offset x30, -0x08157.cfi_window_save158.cfi_rel_offset x19, -0x18159.cfi_rel_offset x20, -0x20160.cfi_rel_offset x21, -0x28161.cfi_rel_offset x22, -0x30162.cfi_rel_offset x23, -0x38163.cfi_rel_offset x24, -0x40164.cfi_rel_offset x25, -0x48165.cfi_rel_offset x26, -0x50166.cfi_rel_offset x27, -0x58167168// Load our two arguments from the stack, where x1 is our start169// procedure and x0 is its first argument. This also blows away the170// stack space used by those two arguments.171mov x0, x21172mov x1, x19173174// ... and then we call the function! Note that this is a function call175// so our frame stays on the stack to backtrace through.176blr x20177// Unreachable, here for safety. This should help catch unexpected178// behaviors. Use a noticeable payload so one can grep for it in the179// codebase.180brk 0xf1b3181.cfi_endproc182",183);184185186