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)
wasmtime_fiber_switch(top_of_stack: *mut u8)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)]
wasmtime_fiber_switch_(top_of_stack: *mut u8)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
wasmtime_fiber_init( top_of_stack: *mut u8, entry_point: extern "C" fn(*mut u8, *mut u8) -> *mut u8, entry_arg0: *mut u8, )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)]
wasmtime_fiber_start() -> !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