1use crate::{backend::RunnableModule, module::ModuleInfo, types::Type, types::Value, vm::Ctx};
3#[cfg(unix)]
4use libc::{
5 mmap, mprotect, munmap, MAP_ANON, MAP_NORESERVE, MAP_PRIVATE, PROT_EXEC, PROT_READ, PROT_WRITE,
6};
7use std::{
8 fmt::Debug,
9 ops::{Deref, DerefMut},
10};
11
12pub trait Loader {
14 type Instance: Instance;
16 type Error: Debug;
18
19 fn load(
21 &self,
22 rm: &dyn RunnableModule,
23 module: &ModuleInfo,
24 ctx: &Ctx,
25 ) -> Result<Self::Instance, Self::Error>;
26}
27
28pub trait Instance {
30 type Error: Debug;
32 fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, Self::Error>;
34 fn read_memory(&mut self, _offset: u32, _len: u32) -> Result<Vec<u8>, Self::Error> {
36 unimplemented!("Instance::read_memory")
37 }
38 fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> {
40 unimplemented!("Instance::write_memory")
41 }
42}
43
44pub struct LocalLoader;
46
47impl Loader for LocalLoader {
48 type Instance = LocalInstance;
49 type Error = String;
50
51 fn load(
52 &self,
53 rm: &dyn RunnableModule,
54 _module: &ModuleInfo,
55 _ctx: &Ctx,
56 ) -> Result<Self::Instance, Self::Error> {
57 let code = rm.get_code().unwrap();
58 let mut code_mem = CodeMemory::new(code.len());
59 code_mem[..code.len()].copy_from_slice(code);
60 code_mem.make_executable();
61
62 Ok(LocalInstance {
63 code: code_mem,
64 offsets: rm.get_offsets().unwrap(),
65 })
66 }
67}
68
69pub struct LocalInstance {
71 code: CodeMemory,
72 offsets: Vec<usize>,
73}
74
75impl Instance for LocalInstance {
76 type Error = String;
77 fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, Self::Error> {
78 let mut args_u64: Vec<u64> = Vec::new();
79 for arg in args {
80 if arg.ty() == Type::V128 {
81 let bytes = arg.to_u128().to_le_bytes();
82 let mut lo = [0u8; 8];
83 lo.clone_from_slice(&bytes[0..8]);
84 args_u64.push(u64::from_le_bytes(lo));
85 let mut hi = [0u8; 8];
86 hi.clone_from_slice(&bytes[8..16]);
87 args_u64.push(u64::from_le_bytes(hi));
88 } else {
89 args_u64.push(arg.to_u128() as u64);
90 }
91 }
92 let offset = self.offsets[id];
93 let addr: *const u8 = unsafe { self.code.as_ptr().add(offset) };
94 use std::mem::transmute;
95 Ok(unsafe {
96 match args_u64.len() {
97 0 => (transmute::<_, extern "C" fn() -> u128>(addr))(),
98 1 => (transmute::<_, extern "C" fn(u64) -> u128>(addr))(args_u64[0]),
99 2 => (transmute::<_, extern "C" fn(u64, u64) -> u128>(addr))(
100 args_u64[0],
101 args_u64[1],
102 ),
103 3 => (transmute::<_, extern "C" fn(u64, u64, u64) -> u128>(addr))(
104 args_u64[0],
105 args_u64[1],
106 args_u64[2],
107 ),
108 4 => (transmute::<_, extern "C" fn(u64, u64, u64, u64) -> u128>(addr))(
109 args_u64[0],
110 args_u64[1],
111 args_u64[2],
112 args_u64[3],
113 ),
114 5 => (transmute::<_, extern "C" fn(u64, u64, u64, u64, u64) -> u128>(addr))(
115 args_u64[0],
116 args_u64[1],
117 args_u64[2],
118 args_u64[3],
119 args_u64[4],
120 ),
121 _ => return Err("too many arguments".into()),
122 }
123 })
124 }
125}
126
127pub struct CodeMemory {
129 ptr: *mut u8,
130 size: usize,
131}
132
133unsafe impl Send for CodeMemory {}
134unsafe impl Sync for CodeMemory {}
135
136#[cfg(not(unix))]
137impl CodeMemory {
138 pub fn new(_size: usize) -> CodeMemory {
140 unimplemented!("CodeMemory::new");
141 }
142
143 pub fn make_executable(&self) {
145 unimplemented!("CodeMemory::make_executable");
146 }
147
148 pub fn make_writable(&self) {
150 unimplemented!("CodeMemory::make_writable");
151 }
152}
153
154#[cfg(unix)]
155impl CodeMemory {
156 pub fn new(size: usize) -> CodeMemory {
158 if size == 0 {
159 return CodeMemory {
160 ptr: std::ptr::null_mut(),
161 size: 0,
162 };
163 }
164
165 fn round_up_to_page_size(size: usize) -> usize {
166 (size + (4096 - 1)) & !(4096 - 1)
167 }
168 let size = round_up_to_page_size(size);
169 let ptr = unsafe {
170 mmap(
171 std::ptr::null_mut(),
172 size,
173 PROT_READ | PROT_WRITE,
174 MAP_PRIVATE | MAP_ANON | MAP_NORESERVE,
175 -1,
176 0,
177 )
178 };
179 if ptr as isize == -1 {
180 panic!("cannot allocate code memory");
181 }
182 CodeMemory {
183 ptr: ptr as _,
184 size: size,
185 }
186 }
187
188 pub fn make_executable(&self) {
190 if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_EXEC) } != 0 {
191 panic!("cannot set code memory to executable");
192 }
193 }
194
195 pub fn make_writable(&self) {
197 if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE) } != 0 {
198 panic!("cannot set code memory to writable");
199 }
200 }
201
202 pub fn make_writable_executable(&self) {
206 if unsafe { mprotect(self.ptr as _, self.size, PROT_READ | PROT_WRITE | PROT_EXEC) } != 0 {
207 panic!("cannot set code memory to writable and executable");
208 }
209 }
210
211 pub fn get_backing_ptr(&self) -> *mut u8 {
213 self.ptr
214 }
215}
216
217#[cfg(unix)]
218impl Drop for CodeMemory {
219 fn drop(&mut self) {
220 unsafe {
221 munmap(self.ptr as _, self.size);
222 }
223 }
224}
225
226impl Deref for CodeMemory {
227 type Target = [u8];
228 fn deref(&self) -> &[u8] {
229 unsafe { std::slice::from_raw_parts(self.ptr, self.size) }
230 }
231}
232
233impl DerefMut for CodeMemory {
234 fn deref_mut(&mut self) -> &mut [u8] {
235 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size) }
236 }
237}