wasmer_runtime_core_fl/
loader.rs

1//! The loader module functions are used to load an instance.
2use 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
12/// The loader trait represents the functions used to load an instance.
13pub trait Loader {
14    /// The type of `Instance` for the loader.
15    type Instance: Instance;
16    /// The error type returned by the loader.
17    type Error: Debug;
18
19    /// Loads the given module and context into an instance.
20    fn load(
21        &self,
22        rm: &dyn RunnableModule,
23        module: &ModuleInfo,
24        ctx: &Ctx,
25    ) -> Result<Self::Instance, Self::Error>;
26}
27
28/// This trait represents an instance used by the loader.
29pub trait Instance {
30    /// The error type returned by this instance.
31    type Error: Debug;
32    /// Call a function by id with the given args.
33    fn call(&mut self, id: usize, args: &[Value]) -> Result<u128, Self::Error>;
34    /// Read memory at the given offset and length.
35    fn read_memory(&mut self, _offset: u32, _len: u32) -> Result<Vec<u8>, Self::Error> {
36        unimplemented!("Instance::read_memory")
37    }
38    /// Write memory at the given offset and length.
39    fn write_memory(&mut self, _offset: u32, _len: u32, _buf: &[u8]) -> Result<(), Self::Error> {
40        unimplemented!("Instance::write_memory")
41    }
42}
43
44/// A local implementation for `Loader`.
45pub 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
69/// A local instance.
70pub 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
127/// A pointer to code in memory.
128pub 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    /// Creates a new code memory with the given size.
139    pub fn new(_size: usize) -> CodeMemory {
140        unimplemented!("CodeMemory::new");
141    }
142
143    /// Makes this code memory executable and not writable.
144    pub fn make_executable(&self) {
145        unimplemented!("CodeMemory::make_executable");
146    }
147
148    /// Makes this code memory writable and not executable.
149    pub fn make_writable(&self) {
150        unimplemented!("CodeMemory::make_writable");
151    }
152}
153
154#[cfg(unix)]
155impl CodeMemory {
156    /// Creates a new code memory with the given size.
157    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    /// Makes this code memory executable and not writable.
189    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    /// Makes this code memory writable and not executable.
196    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    /// Makes this code memory both writable and executable.
203    ///
204    /// Avoid using this if a combination `make_executable` and `make_writable` can be used.
205    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    /// Returns the backing pointer of this code memory.
212    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}