wasmtime/runtime/vm/
mmap_vec.rs1#[cfg(not(has_virtual_memory))]
2use crate::error::OutOfMemory;
3use crate::prelude::*;
4use crate::runtime::vm::send_sync_ptr::SendSyncPtr;
5#[cfg(has_virtual_memory)]
6use crate::runtime::vm::{Mmap, mmap::UnalignedLength};
7#[cfg(not(has_virtual_memory))]
8use alloc::alloc::Layout;
9use alloc::sync::Arc;
10use core::ops::{Deref, Range};
11use core::ptr::NonNull;
12#[cfg(feature = "std")]
13use std::fs::File;
14
15pub enum MmapVec {
36 #[doc(hidden)]
37 #[cfg(not(has_virtual_memory))]
38 Alloc {
39 base: SendSyncPtr<u8>,
40 layout: Layout,
41 },
42 #[doc(hidden)]
43 ExternallyOwned { memory: SendSyncPtr<[u8]> },
44 #[doc(hidden)]
45 #[cfg(has_virtual_memory)]
46 Mmap {
47 mmap: Mmap<UnalignedLength>,
48 len: usize,
49 },
50}
51
52impl MmapVec {
53 #[cfg(has_virtual_memory)]
59 fn new_mmap<M>(mmap: M, len: usize) -> MmapVec
60 where
61 M: Into<Mmap<UnalignedLength>>,
62 {
63 let mmap = mmap.into();
64 assert!(len <= mmap.len());
65 MmapVec::Mmap { mmap, len }
66 }
67
68 #[cfg(not(has_virtual_memory))]
69 fn new_alloc(len: usize, alignment: usize) -> Result<MmapVec, OutOfMemory> {
70 let layout = Layout::from_size_align(len, alignment)
71 .expect("Invalid size or alignment for MmapVec allocation");
72 match NonNull::new(unsafe { alloc::alloc::alloc_zeroed(layout.clone()) }) {
73 Some(ptr) => {
74 let base = SendSyncPtr::new(ptr);
75 Ok(MmapVec::Alloc { base, layout })
76 }
77 None => return Err(OutOfMemory::new(layout.size())),
78 }
79 }
80
81 fn new_externally_owned(memory: NonNull<[u8]>) -> MmapVec {
82 let memory = SendSyncPtr::new(memory);
83 MmapVec::ExternallyOwned { memory }
84 }
85
86 pub fn with_capacity_and_alignment(size: usize, alignment: usize) -> Result<MmapVec> {
93 #[cfg(has_virtual_memory)]
94 {
95 assert!(alignment <= crate::runtime::vm::host_page_size());
96 return Ok(MmapVec::new_mmap(Mmap::with_at_least(size)?, size));
97 }
98 #[cfg(not(has_virtual_memory))]
99 {
100 return Ok(MmapVec::new_alloc(size, alignment)?);
101 }
102 }
103
104 pub fn from_slice(slice: &[u8]) -> Result<MmapVec> {
110 MmapVec::from_slice_with_alignment(slice, 1)
111 }
112
113 pub unsafe fn from_raw(memory: NonNull<[u8]>) -> Result<MmapVec> {
129 Ok(MmapVec::new_externally_owned(memory))
130 }
131
132 pub fn from_slice_with_alignment(slice: &[u8], align: usize) -> Result<MmapVec> {
143 let mut result = MmapVec::with_capacity_and_alignment(slice.len(), align)?;
144 unsafe {
147 result.as_mut_slice().copy_from_slice(slice);
148 }
149 Ok(result)
150 }
151
152 pub fn supports_virtual_memory(&self) -> bool {
158 match self {
159 #[cfg(has_virtual_memory)]
160 MmapVec::Mmap { .. } => true,
161 MmapVec::ExternallyOwned { .. } => false,
162 #[cfg(not(has_virtual_memory))]
163 MmapVec::Alloc { .. } => false,
164 }
165 }
166
167 pub fn is_always_readonly(&self) -> bool {
174 match self {
175 #[cfg(has_virtual_memory)]
176 MmapVec::Mmap { .. } => false,
177 MmapVec::ExternallyOwned { .. } => true,
178 #[cfg(not(has_virtual_memory))]
179 MmapVec::Alloc { .. } => false,
180 }
181 }
182
183 #[cfg(feature = "std")]
193 pub fn from_file(file: File) -> Result<MmapVec> {
194 let file = Arc::new(file);
195 let mmap = Mmap::from_file(Arc::clone(&file))
196 .with_context(move || format!("failed to create mmap for file {file:?}"))?;
197 let len = mmap.len();
198 Ok(MmapVec::new_mmap(mmap, len))
199 }
200
201 #[cfg(has_virtual_memory)]
203 pub unsafe fn make_executable(
204 &self,
205 range: Range<usize>,
206 enable_branch_protection: bool,
207 ) -> Result<()> {
208 let (mmap, len) = match self {
209 MmapVec::Mmap { mmap, len } => (mmap, *len),
210 MmapVec::ExternallyOwned { .. } => {
211 bail!("Unable to make externally owned memory executable");
212 }
213 };
214 assert!(range.start <= range.end);
215 assert!(range.end <= len);
216 unsafe { mmap.make_executable(range.start..range.end, enable_branch_protection) }
217 }
218
219 #[cfg(has_virtual_memory)]
221 pub unsafe fn make_readonly(&self, range: Range<usize>) -> Result<()> {
222 let (mmap, len) = match self {
223 MmapVec::Mmap { mmap, len } => (mmap, *len),
224 MmapVec::ExternallyOwned { .. } => {
225 bail!("Unable to make externally owned memory readonly");
226 }
227 };
228 assert!(range.start <= range.end);
229 assert!(range.end <= len);
230 unsafe { mmap.make_readonly(range.start..range.end) }
231 }
232
233 #[cfg(has_virtual_memory)]
236 pub unsafe fn make_readwrite(&self, range: Range<usize>) -> Result<()> {
237 let (mmap, len) = match self {
238 MmapVec::Mmap { mmap, len } => (mmap, *len),
239 MmapVec::ExternallyOwned { .. } => {
240 bail!("Unable to make externally owned memory read-write");
241 }
242 };
243 assert!(range.start <= range.end);
244 assert!(range.end <= len);
245 unsafe { mmap.make_readwrite(range.start..range.end) }
246 }
247
248 #[cfg(feature = "std")]
250 pub fn original_file(&self) -> Option<&Arc<File>> {
251 match self {
252 #[cfg(not(has_virtual_memory))]
253 MmapVec::Alloc { .. } => None,
254 MmapVec::ExternallyOwned { .. } => None,
255 #[cfg(has_virtual_memory)]
256 MmapVec::Mmap { mmap, .. } => mmap.original_file(),
257 }
258 }
259
260 pub fn image_range(&self) -> Range<*const u8> {
263 let base = self.as_ptr();
264 let len = self.len();
265 base..base.wrapping_add(len)
266 }
267
268 pub unsafe fn as_mut_slice(&mut self) -> &mut [u8] {
280 match self {
281 #[cfg(not(has_virtual_memory))]
282 MmapVec::Alloc { base, layout } => unsafe {
283 core::slice::from_raw_parts_mut(base.as_mut(), layout.size())
284 },
285 MmapVec::ExternallyOwned { .. } => {
286 panic!("Mutating externally owned memory is prohibited");
287 }
288 #[cfg(has_virtual_memory)]
289 MmapVec::Mmap { mmap, len } => unsafe { mmap.slice_mut(0..*len) },
290 }
291 }
292
293 #[cfg(feature = "debug")]
296 pub(crate) fn deep_clone(&self) -> Result<MmapVec> {
297 match self {
298 #[cfg(not(has_virtual_memory))]
299 MmapVec::Alloc { layout, .. } => {
300 MmapVec::from_slice_with_alignment(&self[..], layout.align())
301 }
302 MmapVec::ExternallyOwned { .. } => {
303 crate::bail!("Cannot clone an externally-owned code memory.");
304 }
305 #[cfg(has_virtual_memory)]
306 #[allow(
307 unused_variables,
308 reason = "`mmap` and `len` only used with `std` feature"
309 )]
310 MmapVec::Mmap { mmap, len } => {
311 #[cfg(feature = "std")]
312 if let Some(original_file) = mmap.original_file() {
313 let mmap = Mmap::from_file(original_file.clone())?;
314 return Ok(MmapVec::Mmap { mmap, len: *len });
315 }
316 MmapVec::from_slice_with_alignment(&self[..], crate::runtime::vm::host_page_size())
317 }
318 }
319 }
320}
321
322impl Deref for MmapVec {
323 type Target = [u8];
324
325 #[inline]
326 fn deref(&self) -> &[u8] {
327 match self {
328 #[cfg(not(has_virtual_memory))]
329 MmapVec::Alloc { base, layout } => unsafe {
330 core::slice::from_raw_parts(base.as_ptr(), layout.size())
331 },
332 MmapVec::ExternallyOwned { memory } => unsafe { memory.as_ref() },
333 #[cfg(has_virtual_memory)]
334 MmapVec::Mmap { mmap, len } => {
335 unsafe { mmap.slice(0..*len) }
338 }
339 }
340 }
341}
342
343impl Drop for MmapVec {
344 fn drop(&mut self) {
345 match self {
346 #[cfg(not(has_virtual_memory))]
347 MmapVec::Alloc { base, layout, .. } => unsafe {
348 alloc::alloc::dealloc(base.as_mut(), layout.clone());
349 },
350 MmapVec::ExternallyOwned { .. } => {
351 }
353 #[cfg(has_virtual_memory)]
354 MmapVec::Mmap { .. } => {
355 }
357 }
358 }
359}
360
361#[cfg(test)]
362mod tests {
363 use super::MmapVec;
364
365 #[test]
366 fn smoke() {
367 let mut mmap = MmapVec::with_capacity_and_alignment(10, 1).unwrap();
368 assert_eq!(mmap.len(), 10);
369 assert_eq!(&mmap[..], &[0; 10]);
370
371 unsafe {
372 mmap.as_mut_slice()[0] = 1;
373 mmap.as_mut_slice()[2] = 3;
374 }
375 assert!(mmap.get(10).is_none());
376 assert_eq!(mmap[0], 1);
377 assert_eq!(mmap[2], 3);
378 }
379
380 #[test]
381 fn alignment() {
382 let mmap = MmapVec::with_capacity_and_alignment(10, 4096).unwrap();
383 let raw_ptr = &mmap[0] as *const _ as usize;
384 assert_eq!(raw_ptr & (4096 - 1), 0);
385 }
386}