wasmtime/runtime/vm/sys/unix/
mmap.rs1use crate::prelude::*;
2use crate::runtime::vm::sys::vm::MemoryImageSource;
3use crate::runtime::vm::{HostAlignedByteCount, SendSyncPtr};
4use rustix::mm::{mprotect, MprotectFlags};
5use std::ops::Range;
6use std::ptr::{self, NonNull};
7#[cfg(feature = "std")]
8use std::{fs::File, path::Path};
9
10#[cfg(feature = "std")]
12pub fn open_file_for_mmap(path: &Path) -> Result<File> {
13 File::open(path).context("failed to open file")
14}
15
16#[derive(Debug)]
17pub struct Mmap {
18 memory: SendSyncPtr<[u8]>,
19}
20
21cfg_if::cfg_if! {
22 if #[cfg(any(target_os = "illumos", target_os = "linux"))] {
23 pub(super) const MMAP_NORESERVE_FLAG: rustix::mm::MapFlags =
32 rustix::mm::MapFlags::NORESERVE;
33 } else {
34 pub(super) const MMAP_NORESERVE_FLAG: rustix::mm::MapFlags = rustix::mm::MapFlags::empty();
35 }
36}
37
38impl Mmap {
39 pub fn new_empty() -> Mmap {
40 Mmap {
41 memory: crate::vm::sys::empty_mmap(),
42 }
43 }
44
45 pub fn new(size: HostAlignedByteCount) -> Result<Self> {
46 let ptr = unsafe {
47 rustix::mm::mmap_anonymous(
48 ptr::null_mut(),
49 size.byte_count(),
50 rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
51 rustix::mm::MapFlags::PRIVATE | MMAP_NORESERVE_FLAG,
52 )?
53 };
54 let memory = std::ptr::slice_from_raw_parts_mut(ptr.cast(), size.byte_count());
55 let memory = SendSyncPtr::new(NonNull::new(memory).unwrap());
56 Ok(Mmap { memory })
57 }
58
59 pub fn reserve(size: HostAlignedByteCount) -> Result<Self> {
60 let ptr = unsafe {
61 rustix::mm::mmap_anonymous(
62 ptr::null_mut(),
63 size.byte_count(),
64 rustix::mm::ProtFlags::empty(),
65 rustix::mm::MapFlags::PRIVATE | MMAP_NORESERVE_FLAG,
77 )?
78 };
79
80 let memory = std::ptr::slice_from_raw_parts_mut(ptr.cast(), size.byte_count());
81 let memory = SendSyncPtr::new(NonNull::new(memory).unwrap());
82 Ok(Mmap { memory })
83 }
84
85 #[cfg(feature = "std")]
86 pub fn from_file(file: &File) -> Result<Self> {
87 let len = file
88 .metadata()
89 .context("failed to get file metadata")?
90 .len();
91 let len = usize::try_from(len).map_err(|_| anyhow::anyhow!("file too large to map"))?;
92 let ptr = unsafe {
93 rustix::mm::mmap(
94 ptr::null_mut(),
95 len,
96 rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
97 rustix::mm::MapFlags::PRIVATE,
98 &file,
99 0,
100 )
101 .context(format!("mmap failed to allocate {len:#x} bytes"))?
102 };
103 let memory = std::ptr::slice_from_raw_parts_mut(ptr.cast(), len);
104 let memory = SendSyncPtr::new(NonNull::new(memory).unwrap());
105
106 Ok(Mmap { memory })
107 }
108
109 pub unsafe fn make_accessible(
110 &self,
111 start: HostAlignedByteCount,
112 len: HostAlignedByteCount,
113 ) -> Result<()> {
114 let ptr = self.memory.as_ptr();
115 unsafe {
116 mprotect(
117 ptr.byte_add(start.byte_count()).cast(),
118 len.byte_count(),
119 MprotectFlags::READ | MprotectFlags::WRITE,
120 )?;
121 }
122
123 Ok(())
124 }
125
126 #[inline]
127 pub fn as_send_sync_ptr(&self) -> SendSyncPtr<u8> {
128 self.memory.cast()
129 }
130
131 #[inline]
132 pub fn len(&self) -> usize {
133 self.memory.as_ptr().len()
137 }
138
139 pub unsafe fn make_executable(
140 &self,
141 range: Range<usize>,
142 enable_branch_protection: bool,
143 ) -> Result<()> {
144 let base = self.memory.as_ptr().byte_add(range.start).cast();
145 let len = range.end - range.start;
146
147 let flags = MprotectFlags::READ | MprotectFlags::EXEC;
148 let flags = if enable_branch_protection {
149 #[cfg(all(target_arch = "aarch64", target_os = "linux"))]
150 if std::arch::is_aarch64_feature_detected!("bti") {
151 MprotectFlags::from_bits_retain(flags.bits() | 0x10)
152 } else {
153 flags
154 }
155
156 #[cfg(not(all(target_arch = "aarch64", target_os = "linux")))]
157 flags
158 } else {
159 flags
160 };
161
162 mprotect(base, len, flags)?;
163
164 Ok(())
165 }
166
167 pub unsafe fn make_readonly(&self, range: Range<usize>) -> Result<()> {
168 let base = self.memory.as_ptr().byte_add(range.start).cast();
169 let len = range.end - range.start;
170
171 mprotect(base, len, MprotectFlags::READ)?;
172
173 Ok(())
174 }
175
176 pub unsafe fn map_image_at(
177 &self,
178 image_source: &MemoryImageSource,
179 source_offset: u64,
180 memory_offset: HostAlignedByteCount,
181 memory_len: HostAlignedByteCount,
182 ) -> Result<()> {
183 unsafe {
184 let map_base = self.memory.as_ptr().byte_add(memory_offset.byte_count());
185 let ptr = rustix::mm::mmap(
186 map_base.cast(),
187 memory_len.byte_count(),
188 rustix::mm::ProtFlags::READ | rustix::mm::ProtFlags::WRITE,
189 rustix::mm::MapFlags::PRIVATE | rustix::mm::MapFlags::FIXED,
190 image_source.as_file(),
191 source_offset,
192 )?;
193 assert_eq!(map_base.cast(), ptr);
194 };
195 Ok(())
196 }
197}
198
199impl Drop for Mmap {
200 fn drop(&mut self) {
201 unsafe {
202 let ptr = self.memory.as_ptr().cast();
203 let len = self.memory.as_ptr().len();
204 if len == 0 {
205 return;
206 }
207 rustix::mm::munmap(ptr, len).expect("munmap failed");
208 }
209 }
210}