]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
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 | ||
cdc7bbd5 XL |
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 | ||
6a06907d XL |
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), | |
cdc7bbd5 | 69 | len, |
6a06907d XL |
70 | }) |
71 | } | |
72 | } | |
73 | } | |
74 | ||
cdc7bbd5 XL |
75 | pub fn map(len: usize, file: &File, offset: u64, populate: bool) -> io::Result<MmapInner> { |
76 | let populate = if populate { MAP_POPULATE } else { 0 }; | |
6a06907d XL |
77 | MmapInner::new( |
78 | len, | |
79 | libc::PROT_READ, | |
cdc7bbd5 | 80 | libc::MAP_SHARED | populate, |
6a06907d XL |
81 | file.as_raw_fd(), |
82 | offset, | |
83 | ) | |
84 | } | |
85 | ||
cdc7bbd5 XL |
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 }; | |
6a06907d XL |
88 | MmapInner::new( |
89 | len, | |
90 | libc::PROT_READ | libc::PROT_EXEC, | |
cdc7bbd5 | 91 | libc::MAP_SHARED | populate, |
6a06907d XL |
92 | file.as_raw_fd(), |
93 | offset, | |
94 | ) | |
95 | } | |
96 | ||
cdc7bbd5 XL |
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 }; | |
6a06907d XL |
99 | MmapInner::new( |
100 | len, | |
101 | libc::PROT_READ | libc::PROT_WRITE, | |
cdc7bbd5 | 102 | libc::MAP_SHARED | populate, |
6a06907d XL |
103 | file.as_raw_fd(), |
104 | offset, | |
105 | ) | |
106 | } | |
107 | ||
cdc7bbd5 XL |
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 }; | |
6a06907d XL |
110 | MmapInner::new( |
111 | len, | |
112 | libc::PROT_READ | libc::PROT_WRITE, | |
cdc7bbd5 | 113 | libc::MAP_PRIVATE | populate, |
6a06907d XL |
114 | file.as_raw_fd(), |
115 | offset, | |
116 | ) | |
117 | } | |
118 | ||
cdc7bbd5 XL |
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 }; | |
6a06907d XL |
126 | MmapInner::new( |
127 | len, | |
128 | libc::PROT_READ, | |
cdc7bbd5 | 129 | libc::MAP_PRIVATE | populate, |
6a06907d XL |
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<()> { | |
cdc7bbd5 XL |
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) }; | |
6a06907d XL |
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 = 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(libc::_SC_PAGESIZE) as usize } | |
235 | } |