wasmtime_runtime/
mmap.rs

1//! Low-level abstraction for allocating and managing zero-filled pages
2//! of memory.
3
4use crate::sys::mmap;
5use anyhow::{Context, Result};
6use std::fs::File;
7use std::ops::Range;
8use std::path::Path;
9use std::sync::Arc;
10
11/// A simple struct consisting of a page-aligned pointer to page-aligned
12/// and initially-zeroed memory and a length.
13#[derive(Debug)]
14pub struct Mmap {
15    sys: mmap::Mmap,
16    file: Option<Arc<File>>,
17}
18
19impl Mmap {
20    /// Create a new `Mmap` pointing to at least `size` bytes of page-aligned
21    /// accessible memory.
22    pub fn with_at_least(size: usize) -> Result<Self> {
23        let page_size = crate::page_size();
24        let rounded_size = (size + (page_size - 1)) & !(page_size - 1);
25        Self::accessible_reserved(rounded_size, rounded_size)
26    }
27
28    /// Creates a new `Mmap` by opening the file located at `path` and mapping
29    /// it into memory.
30    ///
31    /// The memory is mapped in read-only mode for the entire file. If portions
32    /// of the file need to be modified then the `region` crate can be use to
33    /// alter permissions of each page.
34    ///
35    /// The memory mapping and the length of the file within the mapping are
36    /// returned.
37    pub fn from_file(path: &Path) -> Result<Self> {
38        let (sys, file) = mmap::Mmap::from_file(path)?;
39        Ok(Mmap {
40            sys,
41            file: Some(Arc::new(file)),
42        })
43    }
44
45    /// Create a new `Mmap` pointing to `accessible_size` bytes of page-aligned
46    /// accessible memory, within a reserved mapping of `mapping_size` bytes.
47    /// `accessible_size` and `mapping_size` must be native page-size multiples.
48    ///
49    /// # Panics
50    ///
51    /// This function will panic if `accessible_size` is greater than
52    /// `mapping_size` or if either of them are not page-aligned.
53    pub fn accessible_reserved(accessible_size: usize, mapping_size: usize) -> Result<Self> {
54        let page_size = crate::page_size();
55        assert!(accessible_size <= mapping_size);
56        assert_eq!(mapping_size & (page_size - 1), 0);
57        assert_eq!(accessible_size & (page_size - 1), 0);
58
59        if mapping_size == 0 {
60            Ok(Mmap {
61                sys: mmap::Mmap::new_empty(),
62                file: None,
63            })
64        } else if accessible_size == mapping_size {
65            Ok(Mmap {
66                sys: mmap::Mmap::new(mapping_size)
67                    .context(format!("mmap failed to allocate {mapping_size:#x} bytes"))?,
68                file: None,
69            })
70        } else {
71            let mut result = Mmap {
72                sys: mmap::Mmap::reserve(mapping_size)
73                    .context(format!("mmap failed to reserve {mapping_size:#x} bytes"))?,
74                file: None,
75            };
76            if accessible_size > 0 {
77                result.make_accessible(0, accessible_size).context(format!(
78                    "mmap failed to allocate {accessible_size:#x} bytes"
79                ))?;
80            }
81            Ok(result)
82        }
83    }
84
85    /// Make the memory starting at `start` and extending for `len` bytes
86    /// accessible. `start` and `len` must be native page-size multiples and
87    /// describe a range within `self`'s reserved memory.
88    ///
89    /// # Panics
90    ///
91    /// This function will panic if `start` or `len` is not page aligned or if
92    /// either are outside the bounds of this mapping.
93    pub fn make_accessible(&mut self, start: usize, len: usize) -> Result<()> {
94        let page_size = crate::page_size();
95        assert_eq!(start & (page_size - 1), 0);
96        assert_eq!(len & (page_size - 1), 0);
97        assert!(len <= self.len());
98        assert!(start <= self.len() - len);
99
100        self.sys.make_accessible(start, len)
101    }
102
103    /// Return the allocated memory as a slice of u8.
104    ///
105    /// # Safety
106    ///
107    /// The caller must ensure that the range of bytes is accessible to the
108    /// program and additionally has previously been initialized.
109    ///
110    /// # Panics
111    ///
112    /// Panics of the `range` provided is outside of the limits of this mmap.
113    #[inline]
114    pub unsafe fn slice(&self, range: Range<usize>) -> &[u8] {
115        assert!(range.start <= range.end);
116        assert!(range.end <= self.len());
117        std::slice::from_raw_parts(self.as_ptr().add(range.start), range.end - range.start)
118    }
119
120    /// Return the allocated memory as a mutable slice of u8.
121    ///
122    /// # Safety
123    ///
124    /// The caller must ensure that the range of bytes is accessible to the
125    /// program and additionally has previously been initialized.
126    ///
127    /// # Panics
128    ///
129    /// Panics of the `range` provided is outside of the limits of this mmap.
130    pub unsafe fn slice_mut(&mut self, range: Range<usize>) -> &mut [u8] {
131        assert!(range.start <= range.end);
132        assert!(range.end <= self.len());
133        std::slice::from_raw_parts_mut(self.as_mut_ptr().add(range.start), range.end - range.start)
134    }
135
136    /// Return the allocated memory as a pointer to u8.
137    #[inline]
138    pub fn as_ptr(&self) -> *const u8 {
139        self.sys.as_ptr()
140    }
141
142    /// Return the allocated memory as a mutable pointer to u8.
143    #[inline]
144    pub fn as_mut_ptr(&mut self) -> *mut u8 {
145        self.sys.as_mut_ptr()
146    }
147
148    /// Return the length of the allocated memory.
149    ///
150    /// This is the byte length of this entire mapping which includes both
151    /// addressible and non-addressible memory.
152    #[inline]
153    pub fn len(&self) -> usize {
154        self.sys.len()
155    }
156
157    /// Return whether any memory has been allocated or reserved.
158    pub fn is_empty(&self) -> bool {
159        self.len() == 0
160    }
161
162    /// Makes the specified `range` within this `Mmap` to be read/execute.
163    ///
164    /// # Unsafety
165    ///
166    /// This method is unsafe as it's generally not valid to simply make memory
167    /// executable, so it's up to the caller to ensure that everything is in
168    /// order and this doesn't overlap with other memory that should only be
169    /// read or only read/write.
170    ///
171    /// # Panics
172    ///
173    /// Panics of `range` is out-of-bounds or not page-aligned.
174    pub unsafe fn make_executable(
175        &self,
176        range: Range<usize>,
177        enable_branch_protection: bool,
178    ) -> Result<()> {
179        assert!(range.start <= self.len());
180        assert!(range.end <= self.len());
181        assert!(range.start <= range.end);
182        assert!(
183            range.start % crate::page_size() == 0,
184            "changing of protections isn't page-aligned",
185        );
186        self.sys
187            .make_executable(range, enable_branch_protection)
188            .context("failed to make memory executable")
189    }
190
191    /// Makes the specified `range` within this `Mmap` to be readonly.
192    pub unsafe fn make_readonly(&self, range: Range<usize>) -> Result<()> {
193        assert!(range.start <= self.len());
194        assert!(range.end <= self.len());
195        assert!(range.start <= range.end);
196        assert!(
197            range.start % crate::page_size() == 0,
198            "changing of protections isn't page-aligned",
199        );
200        self.sys
201            .make_readonly(range)
202            .context("failed to make memory readonly")
203    }
204
205    /// Returns the underlying file that this mmap is mapping, if present.
206    pub fn original_file(&self) -> Option<&Arc<File>> {
207        self.file.as_ref()
208    }
209}
210
211fn _assert() {
212    fn _assert_send_sync<T: Send + Sync>() {}
213    _assert_send_sync::<Mmap>();
214}