mmtk/util/os/imp/unix_like/
unix_common.rs1use crate::util::address::Address;
2use crate::util::constants::BYTES_IN_PAGE;
3use crate::util::conversions::raw_align_up;
4use crate::util::os::*;
5use std::io::Result;
6
7impl MmapProtection {
8 fn get_native_flags(&self) -> i32 {
9 use libc::{PROT_EXEC, PROT_NONE, PROT_READ, PROT_WRITE};
10 match self {
11 Self::ReadWrite => PROT_READ | PROT_WRITE,
12 Self::ReadWriteExec => PROT_READ | PROT_WRITE | PROT_EXEC,
13 Self::NoAccess => PROT_NONE,
14 }
15 }
16}
17
18pub fn mmap(
19 start: Address,
20 size: usize,
21 strategy: MmapStrategy,
22 annotation: &MmapAnnotation<'_>,
23) -> MmapResult<Address> {
24 let ptr = start.to_mut_ptr();
25 let prot = strategy.prot.get_native_flags();
26 let flags = strategy.get_posix_mmap_flags(true);
27 wrap_libc_call(
28 &|| unsafe { libc::mmap(start.to_mut_ptr(), size, prot, flags, -1, 0) },
29 ptr,
30 )
31 .map_err(|e| MmapError::new(start, size, annotation, e))?;
32 Ok(start)
33}
34
35pub fn mmap_anywhere(
36 size: usize,
37 align: usize,
38 strategy: MmapStrategy,
39 annotation: &MmapAnnotation<'_>,
40) -> MmapResult<Address> {
41 mmap_aligned(Address::ZERO, size, align, strategy, annotation)
42}
43
44pub fn mmap_preferred(
45 start: Address,
46 size: usize,
47 align: usize,
48 strategy: MmapStrategy,
49 annotation: &MmapAnnotation<'_>,
50) -> MmapResult<Address> {
51 mmap_aligned(start, size, align, strategy, annotation)
52}
53
54fn mmap_aligned(
55 preferred_start: Address,
56 size: usize,
57 align: usize,
58 strategy: MmapStrategy,
59 annotation: &MmapAnnotation<'_>,
60) -> MmapResult<Address> {
61 debug_assert!(align.is_power_of_two());
62 debug_assert!(align % BYTES_IN_PAGE == 0);
63 debug_assert!(size % BYTES_IN_PAGE == 0);
64
65 let aligned_size = raw_align_up(size, align);
66 let alloc_size = aligned_size + align;
67 let prot = strategy.prot.get_native_flags();
68 let flags = strategy.get_posix_mmap_flags(false);
69
70 let ptr = unsafe { libc::mmap(preferred_start.to_mut_ptr(), alloc_size, prot, flags, -1, 0) };
71 if ptr == libc::MAP_FAILED {
72 return Err(MmapError::new(
73 preferred_start,
74 alloc_size,
75 annotation,
76 std::io::Error::last_os_error(),
77 ));
78 }
79
80 let start = Address::from_mut_ptr(ptr);
81 let aligned_start = start.align_up(align);
82
83 let leading_unaligned_size = aligned_start - start;
84 let trailing_unaligned_size = alloc_size - leading_unaligned_size - size;
85
86 if leading_unaligned_size > 0 {
87 debug_assert!(leading_unaligned_size % BYTES_IN_PAGE == 0);
88 munmap(start, leading_unaligned_size)
89 .map_err(|e| MmapError::new(start, leading_unaligned_size, annotation, e))?;
90 }
91
92 if trailing_unaligned_size > 0 {
93 debug_assert!(trailing_unaligned_size % BYTES_IN_PAGE == 0);
94 let trailing_start = aligned_start + size;
95 munmap(trailing_start, trailing_unaligned_size)
96 .map_err(|e| MmapError::new(trailing_start, trailing_unaligned_size, annotation, e))?;
97 }
98
99 Ok(aligned_start)
100}
101
102pub fn is_mmap_oom(os_errno: i32) -> bool {
103 os_errno == libc::ENOMEM
104}
105
106pub fn munmap(start: Address, size: usize) -> Result<()> {
107 wrap_libc_call(&|| unsafe { libc::munmap(start.to_mut_ptr(), size) }, 0)
108}
109
110pub fn mprotect(start: Address, size: usize, prot: MmapProtection) -> Result<()> {
111 wrap_libc_call(
112 &|| unsafe { libc::mprotect(start.to_mut_ptr(), size, prot.get_native_flags()) },
113 0,
114 )
115}
116
117pub type ProcessIDType = libc::pid_t;
118pub type ThreadIDType = libc::pthread_t;
119
120pub fn get_process_id() -> Result<ProcessIDType> {
121 Ok(unsafe { libc::getpid() })
122}
123
124pub fn get_thread_id() -> Result<ThreadIDType> {
125 Ok(unsafe { libc::pthread_self() })
126}
127
128pub fn wrap_libc_call<T: PartialEq>(f: &dyn Fn() -> T, expect: T) -> Result<()> {
129 let ret = f();
130 if ret == expect {
131 Ok(())
132 } else {
133 Err(std::io::Error::last_os_error())
134 }
135}
136
137#[cfg(all(test, target_os = "linux"))]
138mod tests {
139 use super::*;
140 use crate::util::heap::layout::vm_layout::BYTES_IN_CHUNK;
141 use crate::util::test_util::{serial_test, with_cleanup};
142 use std::io::ErrorKind;
143
144 fn assert_mapping_state(start: Address, size: usize, expect_mapped: bool) {
145 let annotation = MmapAnnotation::Misc {
146 name: "mmap_anywhere_test",
147 };
148 match mmap(
149 start,
150 size,
151 MmapStrategy::QUARANTINE.replace(false),
152 &annotation,
153 ) {
154 Ok(_) => {
155 let _ = munmap(start, size);
156 assert!(
157 !expect_mapped,
158 "{start} of size {size} should still be mapped"
159 );
160 }
161 Err(e) => {
162 assert_eq!(e.error.kind(), ErrorKind::AlreadyExists);
163 assert!(expect_mapped, "{start} of size {size} should be unmapped");
164 }
165 }
166 }
167
168 #[test]
169 fn mmap_anywhere_unmaps_alignment_padding() {
170 serial_test(|| {
171 let size = BYTES_IN_CHUNK + BYTES_IN_PAGE;
172 let start = mmap_anywhere(
173 size,
174 BYTES_IN_CHUNK,
175 MmapStrategy::QUARANTINE,
176 mmap_anno_test!(),
177 )
178 .unwrap();
179
180 with_cleanup(
181 || {
182 assert!(start.is_aligned_to(BYTES_IN_CHUNK));
183 assert_mapping_state(start + size - BYTES_IN_PAGE, BYTES_IN_PAGE, true);
184 assert_mapping_state(start + size, BYTES_IN_PAGE, false);
185 },
186 || {
187 let _ = munmap(start, size);
188 },
189 );
190 });
191 }
192}