1abcd6accSChris Fallin // A WORD OF CAUTION
2abcd6accSChris Fallin //
3abcd6accSChris Fallin // This entire file basically needs to be kept in sync with itself. It's not
4abcd6accSChris Fallin // really possible to modify just one bit of this file without understanding
5abcd6accSChris Fallin // all the other bits. Documentation tries to reference various bits here and
6abcd6accSChris Fallin // there but try to make sure to read over everything before tweaking things!
7abcd6accSChris Fallin //
8abcd6accSChris Fallin // Also at this time this file is heavily based off the x86_64 file, so you'll
9abcd6accSChris Fallin // probably want to read that one as well.
10abcd6accSChris Fallin //
11abcd6accSChris Fallin // Finally, control flow integrity hardening has been applied to the code using
12abcd6accSChris Fallin // the Pointer Authentication (PAuth) and Branch Target Identification (BTI)
13abcd6accSChris Fallin // technologies from the Arm instruction set architecture:
14abcd6accSChris Fallin // * All callable functions start with either the `BTI c` or `PACIASP`/`PACIBSP`
15abcd6accSChris Fallin // instructions
16abcd6accSChris Fallin // * Return addresses are signed and authenticated using the stack pointer
17abcd6accSChris Fallin // value as a modifier (similarly to the salt in a HMAC operation); the
18abcd6accSChris Fallin // `DW_CFA_AARCH64_negate_ra_state` DWARF operation (aliased with the
19abcd6accSChris Fallin // `.cfi_window_save` assembler directive) informs an unwinder about this
20abcd6accSChris Fallin
213e9eca8bSAlex Crichton use core::arch::naked_asm;
22abcd6accSChris Fallin
23abcd6accSChris Fallin cfg_if::cfg_if! {
24fc7ef8d6SAlex Crichton if #[cfg(target_vendor = "apple")] {
25abcd6accSChris Fallin macro_rules! paci1716 { () => ("pacib1716\n"); }
26abcd6accSChris Fallin macro_rules! pacisp { () => ("pacibsp\n"); }
27abcd6accSChris Fallin macro_rules! autisp { () => ("autibsp\n"); }
28abcd6accSChris Fallin } else {
29abcd6accSChris Fallin macro_rules! paci1716 { () => ("pacia1716\n"); }
30abcd6accSChris Fallin macro_rules! pacisp { () => ("paciasp\n"); }
31abcd6accSChris Fallin macro_rules! autisp { () => ("autiasp\n"); }
32abcd6accSChris Fallin }
33abcd6accSChris Fallin }
34abcd6accSChris Fallin
351700302cSAlex Crichton #[inline(never)] // FIXME(rust-lang/rust#148307)
wasmtime_fiber_switch(top_of_stack: *mut u8)361700302cSAlex Crichton pub(crate) unsafe extern "C" fn wasmtime_fiber_switch(top_of_stack: *mut u8) {
371700302cSAlex Crichton unsafe { wasmtime_fiber_switch_(top_of_stack) }
381700302cSAlex Crichton }
391700302cSAlex Crichton
403e9eca8bSAlex Crichton #[unsafe(naked)]
wasmtime_fiber_switch_(top_of_stack: *mut u8 )411700302cSAlex Crichton unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8 /* x0 */) {
423e9eca8bSAlex Crichton naked_asm!(concat!(
43abcd6accSChris Fallin "
44abcd6accSChris Fallin .cfi_startproc
45abcd6accSChris Fallin ",
46abcd6accSChris Fallin pacisp!(),
47abcd6accSChris Fallin "
48abcd6accSChris Fallin .cfi_window_save
49abcd6accSChris Fallin // Save all callee-saved registers on the stack since we're
50abcd6accSChris Fallin // assuming they're clobbered as a result of the stack switch.
51abcd6accSChris Fallin stp x29, x30, [sp, -16]!
5265879713SAlex Crichton stp x27, x28, [sp, -16]!
5365879713SAlex Crichton stp x25, x26, [sp, -16]!
5465879713SAlex Crichton stp x23, x24, [sp, -16]!
5565879713SAlex Crichton stp x21, x22, [sp, -16]!
5665879713SAlex Crichton stp x19, x20, [sp, -16]!
5765879713SAlex Crichton stp d14, d15, [sp, -16]!
5865879713SAlex Crichton stp d12, d13, [sp, -16]!
5965879713SAlex Crichton stp d10, d11, [sp, -16]!
6065879713SAlex Crichton stp d8, d9, [sp, -16]!
61abcd6accSChris Fallin
62abcd6accSChris Fallin // Load our previously saved stack pointer to resume to, and save
63abcd6accSChris Fallin // off our current stack pointer on where to come back to
64abcd6accSChris Fallin // eventually.
65abcd6accSChris Fallin ldr x8, [x0, -0x10]
66abcd6accSChris Fallin mov x9, sp
67abcd6accSChris Fallin str x9, [x0, -0x10]
68abcd6accSChris Fallin
69abcd6accSChris Fallin // Switch to the new stack and restore all our callee-saved
70abcd6accSChris Fallin // registers after the switch and return to our new stack.
71abcd6accSChris Fallin mov sp, x8
7265879713SAlex Crichton ldp d8, d9, [sp], 16
7365879713SAlex Crichton ldp d10, d11, [sp], 16
7465879713SAlex Crichton ldp d12, d13, [sp], 16
7565879713SAlex Crichton ldp d14, d15, [sp], 16
7665879713SAlex Crichton
7765879713SAlex Crichton ldp x19, x20, [sp], 16
7865879713SAlex Crichton ldp x21, x22, [sp], 16
7965879713SAlex Crichton ldp x23, x24, [sp], 16
8065879713SAlex Crichton ldp x25, x26, [sp], 16
8165879713SAlex Crichton ldp x27, x28, [sp], 16
82abcd6accSChris Fallin ldp x29, x30, [sp], 16
83abcd6accSChris Fallin ",
84abcd6accSChris Fallin autisp!(),
85abcd6accSChris Fallin "
86abcd6accSChris Fallin .cfi_window_save
87abcd6accSChris Fallin ret
88abcd6accSChris Fallin .cfi_endproc
89abcd6accSChris Fallin ",
903e9eca8bSAlex Crichton ));
913e9eca8bSAlex Crichton }
92abcd6accSChris Fallin
wasmtime_fiber_init( top_of_stack: *mut u8, entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, )9365879713SAlex Crichton pub(crate) unsafe fn wasmtime_fiber_init(
9465879713SAlex Crichton top_of_stack: *mut u8,
95*f3156fe0SAlex Crichton entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8,
9665879713SAlex Crichton entry_arg0: *mut u8, // x2
9765879713SAlex Crichton ) {
9865879713SAlex Crichton #[repr(C)]
9965879713SAlex Crichton #[derive(Default)]
10065879713SAlex Crichton struct InitialStack {
10165879713SAlex Crichton d8: u64,
10265879713SAlex Crichton d9: u64,
10365879713SAlex Crichton d10: u64,
10465879713SAlex Crichton d11: u64,
10565879713SAlex Crichton d12: u64,
10665879713SAlex Crichton d13: u64,
10765879713SAlex Crichton d14: u64,
10865879713SAlex Crichton d15: u64,
10965879713SAlex Crichton
11065879713SAlex Crichton x19: *mut u8,
11165879713SAlex Crichton x20: *mut u8,
11265879713SAlex Crichton x21: *mut u8,
11365879713SAlex Crichton x22: *mut u8,
11465879713SAlex Crichton x23: *mut u8,
11565879713SAlex Crichton x24: *mut u8,
11665879713SAlex Crichton x25: *mut u8,
11765879713SAlex Crichton x26: *mut u8,
11865879713SAlex Crichton x27: *mut u8,
11965879713SAlex Crichton x28: *mut u8,
12065879713SAlex Crichton
12165879713SAlex Crichton fp: *mut u8,
12265879713SAlex Crichton lr: *mut u8,
12365879713SAlex Crichton
12465879713SAlex Crichton // unix.rs reserved space
12565879713SAlex Crichton last_sp: *mut u8,
12665879713SAlex Crichton run_result: *mut u8,
12765879713SAlex Crichton }
12865879713SAlex Crichton
12965879713SAlex Crichton unsafe {
13065879713SAlex Crichton let initial_stack = top_of_stack.cast::<InitialStack>().sub(1);
13165879713SAlex Crichton initial_stack.write(InitialStack {
13265879713SAlex Crichton x19: top_of_stack,
13365879713SAlex Crichton x20: entry_point as *mut u8,
13465879713SAlex Crichton x21: entry_arg0,
135*f3156fe0SAlex Crichton x22: wasmtime_fiber_switch_ as *mut u8,
13665879713SAlex Crichton
13765879713SAlex Crichton // We set up the newly initialized fiber, so that it resumes
13865879713SAlex Crichton // execution from wasmtime_fiber_start(). As a result, we need a
13965879713SAlex Crichton // signed address of this function because `wasmtime_fiber_switch`
14065879713SAlex Crichton // ends with a `auti{a,b}sp` instruction. There are 2 requirements:
14165879713SAlex Crichton // * We would like to use an instruction that is executed as a no-op
14265879713SAlex Crichton // by processors that do not support PAuth, so that the code is
14365879713SAlex Crichton // backward-compatible and there is no duplication; `PACIA1716` is
14465879713SAlex Crichton // a suitable one.
14565879713SAlex Crichton // * The fiber stack pointer value that is used by the signing
14665879713SAlex Crichton // operation must match the value when the pointer is
14765879713SAlex Crichton // authenticated inside wasmtime_fiber_switch(), which is 16 bytes
14865879713SAlex Crichton // below the `top_of_stack` which will be `sp` at the time of the
14965879713SAlex Crichton // `auti{a,b}sp`.
150abcd6accSChris Fallin //
151abcd6accSChris Fallin // TODO: Use the PACGA instruction to authenticate the saved register
152abcd6accSChris Fallin // state, which avoids creating signed pointers to
153abcd6accSChris Fallin // wasmtime_fiber_start(), and provides wider coverage.
15465879713SAlex Crichton lr: paci1716(wasmtime_fiber_start as *mut u8, top_of_stack.sub(16)),
155abcd6accSChris Fallin
15665879713SAlex Crichton last_sp: initial_stack.cast(),
15765879713SAlex Crichton ..InitialStack::default()
15865879713SAlex Crichton });
15965879713SAlex Crichton }
16065879713SAlex Crichton }
16165879713SAlex Crichton
16265879713SAlex Crichton /// Signs `r17` with the value in `r16` using either `paci{a,b}1716` depending
16365879713SAlex Crichton /// on the platform.
paci1716(mut r17: *mut u8, r16: *mut u8) -> *mut u816465879713SAlex Crichton fn paci1716(mut r17: *mut u8, r16: *mut u8) -> *mut u8 {
16565879713SAlex Crichton unsafe {
16665879713SAlex Crichton core::arch::asm!(
16765879713SAlex Crichton paci1716!(),
16865879713SAlex Crichton inout("x17") r17,
16965879713SAlex Crichton in("x16") r16,
170abcd6accSChris Fallin );
17165879713SAlex Crichton r17
17265879713SAlex Crichton }
1733e9eca8bSAlex Crichton }
174abcd6accSChris Fallin
175abcd6accSChris Fallin // See the x86_64 file for more commentary on what these CFI directives are
176abcd6accSChris Fallin // doing. Like over there note that the relative offsets to registers here
177abcd6accSChris Fallin // match the frame layout in `wasmtime_fiber_switch`.
1783e9eca8bSAlex Crichton #[unsafe(naked)]
wasmtime_fiber_start() -> !1793e9eca8bSAlex Crichton unsafe extern "C" fn wasmtime_fiber_start() -> ! {
1803e9eca8bSAlex Crichton naked_asm!(
181abcd6accSChris Fallin "
182abcd6accSChris Fallin .cfi_startproc simple
183abcd6accSChris Fallin .cfi_def_cfa_offset 0
184abcd6accSChris Fallin .cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \
185abcd6accSChris Fallin 5, /* the byte length of this expression */ \
186abcd6accSChris Fallin 0x6f, /* DW_OP_reg31(%sp) */ \
187abcd6accSChris Fallin 0x06, /* DW_OP_deref */ \
188abcd6accSChris Fallin 0x23, 0xa0, 0x1 /* DW_OP_plus_uconst 0xa0 */
189abcd6accSChris Fallin .cfi_rel_offset x30, -0x08
19065879713SAlex Crichton .cfi_rel_offset x29, -0x10
191abcd6accSChris Fallin .cfi_window_save
19265879713SAlex Crichton .cfi_rel_offset x28, -0x18
19365879713SAlex Crichton .cfi_rel_offset x27, -0x20
19465879713SAlex Crichton .cfi_rel_offset x26, -0x28
19565879713SAlex Crichton .cfi_rel_offset x25, -0x30
19665879713SAlex Crichton .cfi_rel_offset x24, -0x38
19765879713SAlex Crichton .cfi_rel_offset x23, -0x40
19865879713SAlex Crichton .cfi_rel_offset x22, -0x48
19965879713SAlex Crichton .cfi_rel_offset x21, -0x50
20065879713SAlex Crichton .cfi_rel_offset x20, -0x58
20165879713SAlex Crichton .cfi_rel_offset x19, -0x60
202abcd6accSChris Fallin
203abcd6accSChris Fallin // Load our two arguments from the stack, where x1 is our start
204abcd6accSChris Fallin // procedure and x0 is its first argument. This also blows away the
205abcd6accSChris Fallin // stack space used by those two arguments.
206abcd6accSChris Fallin mov x0, x21
207abcd6accSChris Fallin mov x1, x19
208abcd6accSChris Fallin
209abcd6accSChris Fallin // ... and then we call the function! Note that this is a function call
210abcd6accSChris Fallin // so our frame stays on the stack to backtrace through.
211abcd6accSChris Fallin blr x20
212*f3156fe0SAlex Crichton
213*f3156fe0SAlex Crichton // The entry function returns where to switch to as the final switch, so
214*f3156fe0SAlex Crichton // that's performed here in inline assembly.
215*f3156fe0SAlex Crichton blr x22
216*f3156fe0SAlex Crichton
217abcd6accSChris Fallin // Unreachable, here for safety. This should help catch unexpected
218abcd6accSChris Fallin // behaviors. Use a noticeable payload so one can grep for it in the
219abcd6accSChris Fallin // codebase.
220abcd6accSChris Fallin brk 0xf1b3
221abcd6accSChris Fallin .cfi_endproc
222abcd6accSChris Fallin ",
223abcd6accSChris Fallin );
2243e9eca8bSAlex Crichton }
225