memmap/
unix.rs

1extern crate libc;
2
3use std::fs::File;
4use std::os::unix::io::{AsRawFd, RawFd};
5use std::{io, ptr};
6
7#[cfg(any(
8    all(target_os = "linux", not(target_arch = "mips")),
9    target_os = "freebsd",
10    target_os = "android"
11))]
12const MAP_STACK: libc::c_int = libc::MAP_STACK;
13
14#[cfg(not(any(
15    all(target_os = "linux", not(target_arch = "mips")),
16    target_os = "freebsd",
17    target_os = "android"
18)))]
19const MAP_STACK: libc::c_int = 0;
20
21pub struct MmapInner {
22    ptr: *mut libc::c_void,
23    len: usize,
24}
25
26impl MmapInner {
27    /// Creates a new `MmapInner`.
28    ///
29    /// This is a thin wrapper around the `mmap` sytem call.
30    fn new(
31        len: usize,
32        prot: libc::c_int,
33        flags: libc::c_int,
34        file: RawFd,
35        offset: u64,
36    ) -> io::Result<MmapInner> {
37        let alignment = offset % page_size() as u64;
38        let aligned_offset = offset - alignment;
39        let aligned_len = len + alignment as usize;
40        if aligned_len == 0 {
41            // Normally the OS would catch this, but it segfaults under QEMU.
42            return Err(io::Error::new(
43                io::ErrorKind::InvalidInput,
44                "memory map must have a non-zero length",
45            ));
46        }
47
48        unsafe {
49            let ptr = libc::mmap(
50                ptr::null_mut(),
51                aligned_len as libc::size_t,
52                prot,
53                flags,
54                file,
55                aligned_offset as libc::off_t,
56            );
57
58            if ptr == libc::MAP_FAILED {
59                Err(io::Error::last_os_error())
60            } else {
61                Ok(MmapInner {
62                    ptr: ptr.offset(alignment as isize),
63                    len: len,
64                })
65            }
66        }
67    }
68
69    pub fn map(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> {
70        MmapInner::new(
71            len,
72            libc::PROT_READ,
73            libc::MAP_SHARED,
74            file.as_raw_fd(),
75            offset,
76        )
77    }
78
79    pub fn map_exec(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> {
80        MmapInner::new(
81            len,
82            libc::PROT_READ | libc::PROT_EXEC,
83            libc::MAP_SHARED,
84            file.as_raw_fd(),
85            offset,
86        )
87    }
88
89    pub fn map_mut(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> {
90        MmapInner::new(
91            len,
92            libc::PROT_READ | libc::PROT_WRITE,
93            libc::MAP_SHARED,
94            file.as_raw_fd(),
95            offset,
96        )
97    }
98
99    pub fn map_copy(len: usize, file: &File, offset: u64) -> io::Result<MmapInner> {
100        MmapInner::new(
101            len,
102            libc::PROT_READ | libc::PROT_WRITE,
103            libc::MAP_PRIVATE,
104            file.as_raw_fd(),
105            offset,
106        )
107    }
108
109    /// Open an anonymous memory map.
110    pub fn map_anon(len: usize, stack: bool) -> io::Result<MmapInner> {
111        let stack = if stack { MAP_STACK } else { 0 };
112        MmapInner::new(
113            len,
114            libc::PROT_READ | libc::PROT_WRITE,
115            libc::MAP_SHARED | libc::MAP_ANON | stack,
116            -1,
117            0,
118        )
119    }
120
121    pub fn flush(&self, offset: usize, len: usize) -> io::Result<()> {
122        let alignment = (self.ptr as usize + offset) % page_size();
123        let offset = offset as isize - alignment as isize;
124        let len = len + alignment;
125        let result =
126            unsafe { libc::msync(self.ptr.offset(offset), len as libc::size_t, libc::MS_SYNC) };
127        if result == 0 {
128            Ok(())
129        } else {
130            Err(io::Error::last_os_error())
131        }
132    }
133
134    pub fn flush_async(&self, offset: usize, len: usize) -> io::Result<()> {
135        let alignment = offset % page_size();
136        let aligned_offset = offset - alignment;
137        let aligned_len = len + alignment;
138        let result = unsafe {
139            libc::msync(
140                self.ptr.offset(aligned_offset as isize),
141                aligned_len as libc::size_t,
142                libc::MS_ASYNC,
143            )
144        };
145        if result == 0 {
146            Ok(())
147        } else {
148            Err(io::Error::last_os_error())
149        }
150    }
151
152    fn mprotect(&mut self, prot: libc::c_int) -> io::Result<()> {
153        unsafe {
154            let alignment = self.ptr as usize % page_size();
155            let ptr = self.ptr.offset(-(alignment as isize));
156            let len = self.len + alignment;
157            if libc::mprotect(ptr, len, prot) == 0 {
158                Ok(())
159            } else {
160                Err(io::Error::last_os_error())
161            }
162        }
163    }
164
165    pub fn make_read_only(&mut self) -> io::Result<()> {
166        self.mprotect(libc::PROT_READ)
167    }
168
169    pub fn make_exec(&mut self) -> io::Result<()> {
170        self.mprotect(libc::PROT_READ | libc::PROT_EXEC)
171    }
172
173    pub fn make_mut(&mut self) -> io::Result<()> {
174        self.mprotect(libc::PROT_READ | libc::PROT_WRITE)
175    }
176
177    #[inline]
178    pub fn ptr(&self) -> *const u8 {
179        self.ptr as *const u8
180    }
181
182    #[inline]
183    pub fn mut_ptr(&mut self) -> *mut u8 {
184        self.ptr as *mut u8
185    }
186
187    #[inline]
188    pub fn len(&self) -> usize {
189        self.len
190    }
191}
192
193impl Drop for MmapInner {
194    fn drop(&mut self) {
195        let alignment = self.ptr as usize % page_size();
196        unsafe {
197            assert!(
198                libc::munmap(
199                    self.ptr.offset(-(alignment as isize)),
200                    (self.len + alignment) as libc::size_t
201                ) == 0,
202                "unable to unmap mmap: {}",
203                io::Error::last_os_error()
204            );
205        }
206    }
207}
208
209unsafe impl Sync for MmapInner {}
210unsafe impl Send for MmapInner {}
211
212fn page_size() -> usize {
213    unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
214}