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}