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 pub struct MmapInner {
22     ptr: *mut libc::c_void,
23     len: usize,
24 }
25 
26 impl MmapInner {
27     /// Creates a new `MmapInner`.
28     ///
29     /// This is a thin wrapper around the `mmap` sytem call.
new( len: usize, prot: libc::c_int, flags: libc::c_int, file: RawFd, offset: u64, ) -> io::Result<MmapInner>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 
map(len: usize, file: &File, offset: u64) -> io::Result<MmapInner>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 
map_exec(len: usize, file: &File, offset: u64) -> io::Result<MmapInner>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 
map_mut(len: usize, file: &File, offset: u64) -> io::Result<MmapInner>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 
map_copy(len: usize, file: &File, offset: u64) -> io::Result<MmapInner>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.
map_anon(len: usize, stack: bool) -> io::Result<MmapInner>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 
flush(&self, offset: usize, len: usize) -> io::Result<()>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 
flush_async(&self, offset: usize, len: usize) -> io::Result<()>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 
mprotect(&mut self, prot: libc::c_int) -> io::Result<()>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 
make_read_only(&mut self) -> io::Result<()>165     pub fn make_read_only(&mut self) -> io::Result<()> {
166         self.mprotect(libc::PROT_READ)
167     }
168 
make_exec(&mut self) -> io::Result<()>169     pub fn make_exec(&mut self) -> io::Result<()> {
170         self.mprotect(libc::PROT_READ | libc::PROT_EXEC)
171     }
172 
make_mut(&mut self) -> io::Result<()>173     pub fn make_mut(&mut self) -> io::Result<()> {
174         self.mprotect(libc::PROT_READ | libc::PROT_WRITE)
175     }
176 
177     #[inline]
ptr(&self) -> *const u8178     pub fn ptr(&self) -> *const u8 {
179         self.ptr as *const u8
180     }
181 
182     #[inline]
mut_ptr(&mut self) -> *mut u8183     pub fn mut_ptr(&mut self) -> *mut u8 {
184         self.ptr as *mut u8
185     }
186 
187     #[inline]
len(&self) -> usize188     pub fn len(&self) -> usize {
189         self.len
190     }
191 }
192 
193 impl Drop for MmapInner {
drop(&mut self)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 
209 unsafe impl Sync for MmapInner {}
210 unsafe impl Send for MmapInner {}
211 
page_size() -> usize212 fn page_size() -> usize {
213     unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize }
214 }
215