wasmtime/runtime/vm/sys/unix/
vm.rs1use crate::runtime::vm::sys::DecommitBehavior;
2use rustix::fd::AsRawFd;
3use rustix::mm::{mmap_anonymous, mprotect, MapFlags, MprotectFlags, ProtFlags};
4use std::fs::File;
5use std::io;
6#[cfg(feature = "std")]
7use std::sync::Arc;
8
9pub unsafe fn expose_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
10 mprotect(ptr.cast(), len, MprotectFlags::READ | MprotectFlags::WRITE)?;
11 Ok(())
12}
13
14pub unsafe fn hide_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
15 mprotect(ptr.cast(), len, MprotectFlags::empty())?;
16 Ok(())
17}
18
19pub unsafe fn erase_existing_mapping(ptr: *mut u8, len: usize) -> io::Result<()> {
20 let ret = mmap_anonymous(
21 ptr.cast(),
22 len,
23 ProtFlags::empty(),
24 MapFlags::PRIVATE | super::mmap::MMAP_NORESERVE_FLAG | MapFlags::FIXED,
25 )?;
26 assert_eq!(ptr, ret.cast());
27 Ok(())
28}
29
30#[cfg(feature = "pooling-allocator")]
31pub unsafe fn commit_pages(_addr: *mut u8, _len: usize) -> io::Result<()> {
32 Ok(())
35}
36
37#[cfg(feature = "pooling-allocator")]
38pub unsafe fn decommit_pages(addr: *mut u8, len: usize) -> io::Result<()> {
39 if len == 0 {
40 return Ok(());
41 }
42
43 unsafe {
44 cfg_if::cfg_if! {
45 if #[cfg(target_os = "linux")] {
46 use rustix::mm::{madvise, Advice};
47
48 madvise(addr as _, len, Advice::LinuxDontNeed)?;
51 } else {
52 mmap_anonymous(
57 addr as _,
58 len,
59 ProtFlags::READ | ProtFlags::WRITE,
60 MapFlags::PRIVATE | super::mmap::MMAP_NORESERVE_FLAG | MapFlags::FIXED,
61 )?;
62 }
63 }
64 }
65
66 Ok(())
67}
68
69pub fn get_page_size() -> usize {
70 unsafe { libc::sysconf(libc::_SC_PAGESIZE).try_into().unwrap() }
71}
72
73pub fn decommit_behavior() -> DecommitBehavior {
74 if cfg!(target_os = "linux") {
75 DecommitBehavior::RestoreOriginalMapping
76 } else {
77 DecommitBehavior::Zero
78 }
79}
80
81#[derive(Debug)]
82pub enum MemoryImageSource {
83 #[cfg(feature = "std")]
84 Mmap(Arc<File>),
85 #[cfg(target_os = "linux")]
86 Memfd(memfd::Memfd),
87}
88
89impl MemoryImageSource {
90 #[cfg(feature = "std")]
91 pub fn from_file(file: &Arc<File>) -> Option<MemoryImageSource> {
92 Some(MemoryImageSource::Mmap(file.clone()))
93 }
94
95 #[cfg(not(target_os = "linux"))]
96 pub fn from_data(_data: &[u8]) -> io::Result<Option<MemoryImageSource>> {
97 Ok(None)
98 }
99
100 #[cfg(target_os = "linux")]
101 pub fn from_data(data: &[u8]) -> anyhow::Result<Option<MemoryImageSource>> {
102 use std::io::{ErrorKind, Write};
107
108 let memfd = match memfd::MemfdOptions::new()
111 .allow_sealing(true)
112 .create("wasm-memory-image")
113 {
114 Ok(memfd) => memfd,
115 Err(memfd::Error::Create(err)) if err.kind() == ErrorKind::Unsupported => {
119 return Ok(None)
120 }
121 Err(e) => return Err(e.into()),
122 };
123 memfd.as_file().write_all(data)?;
124
125 memfd.add_seals(&[
141 memfd::FileSeal::SealGrow,
142 memfd::FileSeal::SealShrink,
143 memfd::FileSeal::SealWrite,
144 memfd::FileSeal::SealSeal,
145 ])?;
146
147 Ok(Some(MemoryImageSource::Memfd(memfd)))
148 }
149
150 pub(super) fn as_file(&self) -> &File {
151 match *self {
152 #[cfg(feature = "std")]
153 MemoryImageSource::Mmap(ref file) => file,
154 #[cfg(target_os = "linux")]
155 MemoryImageSource::Memfd(ref memfd) => memfd.as_file(),
156 }
157 }
158
159 pub unsafe fn remap_as_zeros_at(&self, base: *mut u8, len: usize) -> io::Result<()> {
160 let ptr = mmap_anonymous(
161 base.cast(),
162 len,
163 ProtFlags::READ | ProtFlags::WRITE,
164 MapFlags::PRIVATE | super::mmap::MMAP_NORESERVE_FLAG | MapFlags::FIXED,
165 )?;
166 assert_eq!(base, ptr.cast());
167 Ok(())
168 }
169}
170
171impl PartialEq for MemoryImageSource {
172 fn eq(&self, other: &MemoryImageSource) -> bool {
173 self.as_file().as_raw_fd() == other.as_file().as_raw_fd()
174 }
175}