1extern crate libc;
26
27#[cfg(target_os = "macos")]
28extern crate anyhow;
29#[cfg(target_os = "macos")]
30extern crate libproc;
31#[cfg(target_os = "macos")]
32extern crate mach2;
33#[cfg(windows)]
34extern crate winapi;
35
36#[cfg(target_os = "macos")]
37pub mod mac_maps;
38#[cfg(target_os = "macos")]
39pub use mac_maps::{get_process_maps, MapRange, Pid};
40
41#[cfg(any(target_os = "linux", target_os = "android"))]
42pub mod linux_maps;
43#[cfg(any(target_os = "linux", target_os = "android"))]
44pub use linux_maps::{get_process_maps, MapRange, Pid};
45
46#[cfg(windows)]
47pub mod win_maps;
48#[cfg(windows)]
49pub use win_maps::{get_process_maps, MapRange, Pid};
50
51#[cfg(target_os = "freebsd")]
52pub mod freebsd_maps;
53#[cfg(target_os = "freebsd")]
54pub use freebsd_maps::{get_process_maps, MapRange, Pid};
55
56trait MapRangeImpl {
61 fn size(&self) -> usize;
63 fn start(&self) -> usize;
65 fn filename(&self) -> Option<&std::path::Path>;
67 fn is_exec(&self) -> bool;
69 fn is_write(&self) -> bool;
71 fn is_read(&self) -> bool;
73}
74
75impl MapRange {
76 #[inline]
78 pub fn size(&self) -> usize {
79 MapRangeImpl::size(self)
80 }
81 #[inline]
83 pub fn start(&self) -> usize {
84 MapRangeImpl::start(self)
85 }
86 #[inline]
88 pub fn filename(&self) -> Option<&std::path::Path> {
89 MapRangeImpl::filename(self)
90 }
91 #[inline]
93 pub fn is_exec(&self) -> bool {
94 MapRangeImpl::is_exec(self)
95 }
96 #[inline]
98 pub fn is_write(&self) -> bool {
99 MapRangeImpl::is_write(self)
100 }
101 #[inline]
103 pub fn is_read(&self) -> bool {
104 MapRangeImpl::is_read(self)
105 }
106}
107
108fn map_contain_addr(map: &MapRange, addr: usize) -> bool {
109 let start = map.start();
110 (addr >= start) && (addr < (start + map.size()))
111}
112
113pub fn maps_contain_addr(addr: usize, maps: &[MapRange]) -> bool {
116 maps.iter().any(|map| map_contain_addr(map, addr))
117}
118
119pub fn maps_contain_addr_range(mut addr: usize, mut size: usize, maps: &[MapRange]) -> bool {
122 if size == 0 || addr.checked_add(size).is_none() {
123 return false;
124 }
125
126 while size > 0 {
127 match maps.iter().find(|map| map_contain_addr(map, addr)) {
128 None => return false,
129 Some(map) => {
130 let end = map.start() + map.size();
131 if addr + size <= end {
132 return true;
133 } else {
134 size -= end - addr;
135 addr = end;
136 }
137 }
138 }
139 }
140
141 true
142}
143
144#[cfg(test)]
145mod tests {
146 use crate::get_process_maps;
147 use crate::Pid;
148
149 #[cfg(not(target_os = "windows"))]
150 fn test_process_path() -> Option<std::path::PathBuf> {
151 std::env::current_exe().ok().and_then(|p| {
152 p.parent().map(|p| {
153 p.with_file_name("test")
154 .with_extension(std::env::consts::EXE_EXTENSION)
155 })
156 })
157 }
158
159 #[cfg(not(target_os = "freebsd"))]
160 #[test]
161 fn test_map_from_test_binary_present() -> () {
162 let maps = get_process_maps(std::process::id() as Pid).unwrap();
163
164 let region = maps.iter().find(|map| {
165 if let Some(filename) = map.filename() {
166 filename.to_string_lossy().contains("proc_maps")
167 } else {
168 false
169 }
170 });
171
172 assert!(
173 region.is_some(),
174 "We should have a map for the current test process"
175 );
176 }
177
178 #[cfg(not(target_os = "windows"))]
179 #[test]
180 fn test_map_from_invoked_binary_present() -> () {
181 let path = test_process_path().unwrap();
182 if !path.exists() {
183 println!("Skipping test because the 'test' binary hasn't been built");
184 return;
185 }
186
187 let mut have_expected_map = false;
188 for _ in 1..10 {
190 let mut child = std::process::Command::new(&path)
191 .spawn()
192 .expect("failed to execute test process");
193
194 let maps = get_process_maps(child.id() as Pid).unwrap();
195
196 child.kill().expect("failed to kill test process");
197
198 let region = maps.iter().find(|map| {
199 if let Some(filename) = map.filename() {
200 filename.to_string_lossy().contains("/test")
201 } else {
202 false
203 }
204 });
205
206 if region.is_some() {
207 have_expected_map = true;
208 break;
209 } else {
210 std::thread::sleep(std::time::Duration::from_millis(100));
211 }
212 }
213
214 assert!(
215 have_expected_map,
216 "We should have a map from the binary we invoked!"
217 );
218 }
219}