mmtk/util/os/imp/unix_like/
unix_common.rs

1use 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}