1 | extern crate libc; |
2 | |
3 | use std::fs::File; |
4 | use std::os::unix::io::{AsRawFd, RawFd}; |
5 | use 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 | ))] |
12 | const 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 | )))] |
19 | const MAP_STACK: libc::c_int = 0; |
20 | |
21 | #[cfg (any(target_os = "linux" , target_os = "android" ))] |
22 | const MAP_POPULATE: libc::c_int = libc::MAP_POPULATE; |
23 | |
24 | #[cfg (not(any(target_os = "linux" , target_os = "android" )))] |
25 | const MAP_POPULATE: libc::c_int = 0; |
26 | |
27 | pub struct MmapInner { |
28 | ptr: *mut libc::c_void, |
29 | len: usize, |
30 | } |
31 | |
32 | impl 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 | |
214 | impl 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 | |
230 | unsafe impl Sync for MmapInner {} |
231 | unsafe impl Send for MmapInner {} |
232 | |
233 | fn page_size() -> usize { |
234 | unsafe { libc::sysconf(name:libc::_SC_PAGESIZE) as usize } |
235 | } |
236 | |