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