1 // A WORD OF CAUTION 2 // 3 // This entire file basically needs to be kept in sync with itself. It's not 4 // really possible to modify just one bit of this file without understanding 5 // all the other bits. Documentation tries to reference various bits here and 6 // there but try to make sure to read over everything before tweaking things! 7 // 8 // This file is modeled after x86_64.rs and comments are not copied over. For 9 // reference be sure to review the other file. Note that the pointer size is 10 // different so the reserved space at the top of the stack is 8 bytes, not 16 11 // bytes. Still two pointers though. 12 13 use core::arch::naked_asm; 14 15 #[inline(never)] // FIXME(rust-lang/rust#148307) 16 pub(crate) unsafe extern "C" fn wasmtime_fiber_switch(top_of_stack: *mut u8) { 17 unsafe { wasmtime_fiber_switch_(top_of_stack) } 18 } 19 20 #[unsafe(naked)] 21 unsafe extern "C" fn wasmtime_fiber_switch_(top_of_stack: *mut u8) { 22 naked_asm!( 23 " 24 // Load our stack-to-use 25 mov eax, 0x4[esp] 26 mov ecx, -0x8[eax] 27 28 // Save callee-saved registers 29 push ebp 30 push ebx 31 push esi 32 push edi 33 34 // Save our current stack and jump to the stack-to-use 35 mov -0x8[eax], esp 36 mov esp, ecx 37 38 // Restore callee-saved registers 39 pop edi 40 pop esi 41 pop ebx 42 pop ebp 43 ret 44 ", 45 ) 46 } 47 48 pub(crate) unsafe fn wasmtime_fiber_init( 49 top_of_stack: *mut u8, 50 entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, 51 entry_arg0: *mut u8, 52 ) { 53 // Our stack from top-to-bottom looks like: 54 // 55 // * 8 bytes of reserved space per unix.rs (two-pointers space) 56 // * 8 bytes of arguments (two arguments wasmtime_fiber_start forwards) 57 // * 4 bytes of return address 58 // * 16 bytes of saved registers 59 // 60 // Note that after the return address the stack is conveniently 16-byte 61 // aligned as required, so we just leave the arguments on the stack in 62 // `wasmtime_fiber_start` and immediately do the call. 63 #[repr(C)] 64 #[derive(Default)] 65 struct InitialStack { 66 // state that will get resumed into from a `wasmtime_fiber_switch` 67 // starting up this fiber. 68 edi: *mut u8, 69 esi: *mut u8, 70 ebx: *mut u8, 71 ebp: *mut u8, 72 return_address: *mut u8, 73 74 // two arguments to `entry_point` 75 arg1: *mut u8, 76 arg2: *mut u8, 77 78 // unix.rs reserved space 79 last_sp: *mut u8, 80 run_result: *mut u8, 81 } 82 83 unsafe { 84 let initial_stack = top_of_stack.cast::<InitialStack>().sub(1); 85 initial_stack.write(InitialStack { 86 ebp: entry_point as *mut u8, 87 esi: wasmtime_fiber_switch_ as *mut u8, 88 return_address: wasmtime_fiber_start as *mut u8, 89 arg1: entry_arg0, 90 arg2: top_of_stack, 91 last_sp: initial_stack.cast(), 92 ..InitialStack::default() 93 }); 94 } 95 } 96 97 #[unsafe(naked)] 98 unsafe extern "C" fn wasmtime_fiber_start() -> ! { 99 naked_asm!( 100 " 101 .cfi_startproc simple 102 .cfi_def_cfa_offset 0 103 .cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \ 104 5, /* the byte length of this expression */ \ 105 0x74, 0x08, /* DW_OP_breg4 (%esp) + 8 */ \ 106 0x06, /* DW_OP_deref */ \ 107 0x23, 0x14 /* DW_OP_plus_uconst 0x14 */ 108 109 .cfi_rel_offset eip, -4 110 .cfi_rel_offset ebp, -8 111 .cfi_rel_offset ebx, -12 112 .cfi_rel_offset esi, -16 113 .cfi_rel_offset edi, -20 114 115 // Our arguments and stack alignment are all prepped by 116 // `wasmtime_fiber_init`. After calling `entry_point` clean up the 117 // stack arguments. 118 call ebp 119 pop edi 120 pop edi 121 122 // Call `wasmtime_fiber_switch_`, pushing its argument onto the stack. 123 push eax 124 call esi 125 ud2 126 .cfi_endproc 127 ", 128 ); 129 } 130