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 #[unsafe(naked)] 16 pub(crate) unsafe extern "C" fn wasmtime_fiber_switch(top_of_stack: *mut u8) { 17 naked_asm!( 18 " 19 // Load our stack-to-use 20 mov eax, 0x4[esp] 21 mov ecx, -0x8[eax] 22 23 // Save callee-saved registers 24 push ebp 25 push ebx 26 push esi 27 push edi 28 29 // Save our current stack and jump to the stack-to-use 30 mov -0x8[eax], esp 31 mov esp, ecx 32 33 // Restore callee-saved registers 34 pop edi 35 pop esi 36 pop ebx 37 pop ebp 38 ret 39 ", 40 ) 41 } 42 43 pub(crate) unsafe fn wasmtime_fiber_init( 44 top_of_stack: *mut u8, 45 entry_point: extern "C" fn(*mut u8, *mut u8), 46 entry_arg0: *mut u8, 47 ) { 48 // Our stack from top-to-bottom looks like: 49 // 50 // * 8 bytes of reserved space per unix.rs (two-pointers space) 51 // * 8 bytes of arguments (two arguments wasmtime_fiber_start forwards) 52 // * 4 bytes of return address 53 // * 16 bytes of saved registers 54 // 55 // Note that after the return address the stack is conveniently 16-byte 56 // aligned as required, so we just leave the arguments on the stack in 57 // `wasmtime_fiber_start` and immediately do the call. 58 #[repr(C)] 59 #[derive(Default)] 60 struct InitialStack { 61 // state that will get resumed into from a `wasmtime_fiber_switch` 62 // starting up this fiber. 63 edi: *mut u8, 64 esi: *mut u8, 65 ebx: *mut u8, 66 ebp: *mut u8, 67 return_address: *mut u8, 68 69 // two arguments to `entry_point` 70 arg1: *mut u8, 71 arg2: *mut u8, 72 73 // unix.rs reserved space 74 last_sp: *mut u8, 75 run_result: *mut u8, 76 } 77 78 unsafe { 79 let initial_stack = top_of_stack.cast::<InitialStack>().sub(1); 80 initial_stack.write(InitialStack { 81 ebp: entry_point as *mut u8, 82 return_address: wasmtime_fiber_start as *mut u8, 83 arg1: entry_arg0, 84 arg2: top_of_stack, 85 last_sp: initial_stack.cast(), 86 ..InitialStack::default() 87 }); 88 } 89 } 90 91 #[unsafe(naked)] 92 unsafe extern "C" fn wasmtime_fiber_start() -> ! { 93 naked_asm!( 94 " 95 .cfi_startproc simple 96 .cfi_def_cfa_offset 0 97 .cfi_escape 0x0f, /* DW_CFA_def_cfa_expression */ \ 98 5, /* the byte length of this expression */ \ 99 0x74, 0x08, /* DW_OP_breg4 (%esp) + 8 */ \ 100 0x06, /* DW_OP_deref */ \ 101 0x23, 0x14 /* DW_OP_plus_uconst 0x14 */ 102 103 .cfi_rel_offset eip, -4 104 .cfi_rel_offset ebp, -8 105 .cfi_rel_offset ebx, -12 106 .cfi_rel_offset esi, -16 107 .cfi_rel_offset edi, -20 108 109 // Our arguments and stack alignment are all prepped by 110 // `wasmtime_fiber_init`. 111 call ebp 112 ud2 113 .cfi_endproc 114 ", 115 ); 116 } 117