1 use crate::auxv_reader::AuxvType;
2 use crate::errors::MapsReaderError;
3 use crate::thread_info::Pid;
4 use byteorder::{NativeEndian, ReadBytesExt};
5 use goblin::elf;
6 use memmap2::{Mmap, MmapOptions};
7 use std::convert::TryInto;
8 use std::fs::File;
9 use std::mem::size_of;
10 use std::path::PathBuf;
11
12 pub const LINUX_GATE_LIBRARY_NAME: &str = "linux-gate.so";
13 pub const DELETED_SUFFIX: &str = " (deleted)";
14 pub const RESERVED_FLAGS: &str = "---p";
15
16 type Result<T> = std::result::Result<T, MapsReaderError>;
17
18 #[derive(Debug, PartialEq, Clone)]
19 pub struct SystemMappingInfo {
20 pub start_address: usize,
21 pub end_address: usize,
22 }
23
24 // One of these is produced for each mapping in the process (i.e. line in
25 // /proc/$x/maps).
26 #[derive(Debug, PartialEq, Clone)]
27 pub struct MappingInfo {
28 // On Android, relocation packing can mean that the reported start
29 // address of the mapping must be adjusted by a bias in order to
30 // compensate for the compression of the relocation section. The
31 // following two members hold (after LateInit) the adjusted mapping
32 // range. See crbug.com/606972 for more information.
33 pub start_address: usize,
34 pub size: usize,
35 // When Android relocation packing causes |start_addr| and |size| to
36 // be modified with a load bias, we need to remember the unbiased
37 // address range. The following structure holds the original mapping
38 // address range as reported by the operating system.
39 pub system_mapping_info: SystemMappingInfo,
40 pub offset: usize, // offset into the backed file.
41 pub executable: bool, // true if the mapping has the execute bit set.
42 pub name: Option<String>,
43 // pub elf_obj: Option<elf::Elf>,
44 }
45
46 #[derive(Debug)]
47 pub struct MappingEntry {
48 pub mapping: MappingInfo,
49 pub identifier: Vec<u8>,
50 }
51
52 // A list of <MappingInfo, GUID>
53 pub type MappingList = Vec<MappingEntry>;
54
55 #[derive(Debug)]
56 pub enum MappingInfoParsingResult {
57 SkipLine,
58 Success(MappingInfo),
59 }
60
is_mapping_a_path(pathname: Option<&str>) -> bool61 fn is_mapping_a_path(pathname: Option<&str>) -> bool {
62 match pathname {
63 Some(x) => x.contains('/'),
64 None => false,
65 }
66 }
67
68 impl MappingInfo {
parse_from_line( line: &str, linux_gate_loc: AuxvType, last_mapping: Option<&mut MappingInfo>, ) -> Result<MappingInfoParsingResult>69 pub fn parse_from_line(
70 line: &str,
71 linux_gate_loc: AuxvType,
72 last_mapping: Option<&mut MappingInfo>,
73 ) -> Result<MappingInfoParsingResult> {
74 let mut last_whitespace = false;
75
76 // There is no `line.splitn_whitespace(6)`, so we have to do it somewhat manually
77 // Split at the first whitespace, trim of the rest.
78 let mut splits = line
79 .trim()
80 .splitn(6, |c: char| {
81 if c.is_whitespace() {
82 if last_whitespace {
83 return false;
84 }
85 last_whitespace = true;
86 true
87 } else {
88 last_whitespace = false;
89 false
90 }
91 })
92 .map(str::trim);
93
94 let address = splits
95 .next()
96 .ok_or(MapsReaderError::MapEntryMalformed("address"))?;
97 let perms = splits
98 .next()
99 .ok_or(MapsReaderError::MapEntryMalformed("permissions"))?;
100 let mut offset = usize::from_str_radix(
101 splits
102 .next()
103 .ok_or(MapsReaderError::MapEntryMalformed("offset"))?,
104 16,
105 )?;
106 let _dev = splits
107 .next()
108 .ok_or(MapsReaderError::MapEntryMalformed("dev"))?;
109 let _inode = splits
110 .next()
111 .ok_or(MapsReaderError::MapEntryMalformed("inode"))?;
112 let mut pathname = splits.next(); // Optional
113
114 // Due to our ugly `splitn_whitespace()` hack from above, we might have
115 // only trailing whitespaces as the name, so we it might still be "Some()"
116 if let Some(x) = pathname {
117 if x.is_empty() {
118 pathname = None;
119 }
120 }
121
122 let mut addresses = address.split('-');
123 let start_address = usize::from_str_radix(addresses.next().unwrap(), 16)?;
124 let end_address = usize::from_str_radix(addresses.next().unwrap(), 16)?;
125
126 let executable = perms.contains('x');
127
128 // Only copy name if the name is a valid path name, or if
129 // it's the VDSO image.
130 let is_path = is_mapping_a_path(pathname);
131
132 if !is_path && linux_gate_loc != 0 && start_address == linux_gate_loc.try_into()? {
133 pathname = Some(LINUX_GATE_LIBRARY_NAME);
134 offset = 0;
135 }
136
137 match (pathname, last_mapping) {
138 (Some(_name), Some(module)) => {
139 // Merge adjacent mappings into one module, assuming they're a single
140 // library mapped by the dynamic linker.
141 if (start_address == module.start_address + module.size)
142 && (pathname == module.name.as_deref())
143 {
144 module.system_mapping_info.end_address = end_address;
145 module.size = end_address - module.start_address;
146 module.executable |= executable;
147 return Ok(MappingInfoParsingResult::SkipLine);
148 }
149 }
150 (None, Some(module)) => {
151 // Also merge mappings that result from address ranges that the
152 // linker reserved but which a loaded library did not use. These
153 // appear as an anonymous private mapping with no access flags set
154 // and which directly follow an executable mapping.
155 let module_end_address = module.start_address + module.size;
156 if (start_address == module_end_address)
157 && module.executable
158 && is_mapping_a_path(module.name.as_deref())
159 && (offset == 0 || offset == module_end_address)
160 && perms == RESERVED_FLAGS
161 {
162 module.size = end_address - module.start_address;
163 return Ok(MappingInfoParsingResult::SkipLine);
164 }
165 }
166 _ => (),
167 }
168
169 let name = pathname.map(ToOwned::to_owned);
170
171 let info = MappingInfo {
172 start_address,
173 size: end_address - start_address,
174 system_mapping_info: SystemMappingInfo {
175 start_address,
176 end_address,
177 },
178 offset,
179 executable,
180 name,
181 // elf_obj,
182 };
183
184 Ok(MappingInfoParsingResult::Success(info))
185 }
186
get_mmap(name: &Option<String>, offset: usize) -> Result<Mmap>187 pub fn get_mmap(name: &Option<String>, offset: usize) -> Result<Mmap> {
188 if !MappingInfo::is_mapped_file_safe_to_open(&name) {
189 return Err(MapsReaderError::NotSafeToOpenMapping(
190 name.clone().unwrap_or_default(),
191 ));
192 }
193
194 // Not doing this as root_prefix is always "" at the moment
195 // if (!dumper.GetMappingAbsolutePath(mapping, filename))
196 let filename = name.clone().unwrap_or_default();
197 let mapped_file = unsafe {
198 MmapOptions::new()
199 .offset(offset.try_into()?) // try_into() to work for both 32 and 64 bit
200 .map(&File::open(filename)?)?
201 };
202
203 if mapped_file.is_empty() || mapped_file.len() < elf::header::SELFMAG {
204 return Err(MapsReaderError::MmapSanityCheckFailed);
205 }
206 Ok(mapped_file)
207 }
208
handle_deleted_file_in_mapping(path: &str, pid: Pid) -> Result<String>209 pub fn handle_deleted_file_in_mapping(path: &str, pid: Pid) -> Result<String> {
210 // Check for ' (deleted)' in |path|.
211 // |path| has to be at least as long as "/x (deleted)".
212 if !path.ends_with(DELETED_SUFFIX) {
213 return Ok(path.to_string());
214 }
215
216 // Check |path| against the /proc/pid/exe 'symlink'.
217 let exe_link = format!("/proc/{}/exe", pid);
218 let link_path = std::fs::read_link(&exe_link)?;
219
220 // This is a no-op for now (until we want to support root_prefix for chroot-envs)
221 // if (!GetMappingAbsolutePath(new_mapping, new_path))
222 // return false;
223
224 if link_path != PathBuf::from(path) {
225 return Err(MapsReaderError::SymlinkError(
226 PathBuf::from(path),
227 link_path,
228 ));
229 }
230
231 // Check to see if someone actually named their executable 'foo (deleted)'.
232
233 // This makes currently no sense, as exe_link == new_path
234 // if let (Some(exe_stat), Some(new_path_stat)) = (nix::stat::stat(exe_link), nix::stat::stat(new_path)) {
235 // if exe_stat.st_dev == new_path_stat.st_dev && exe_stat.st_ino == new_path_stat.st_ino {
236 // return Err("".into());
237 // }
238 // }
239 Ok(exe_link)
240 }
241
stack_has_pointer_to_mapping(&self, stack_copy: &[u8], sp_offset: usize) -> bool242 pub fn stack_has_pointer_to_mapping(&self, stack_copy: &[u8], sp_offset: usize) -> bool {
243 // Loop over all stack words that would have been on the stack in
244 // the target process (i.e. are word aligned, and at addresses >=
245 // the stack pointer). Regardless of the alignment of |stack_copy|,
246 // the memory starting at |stack_copy| + |offset| represents an
247 // aligned word in the target process.
248 let low_addr = self.system_mapping_info.start_address;
249 let high_addr = self.system_mapping_info.end_address;
250 let mut offset = (sp_offset + size_of::<usize>() - 1) & !(size_of::<usize>() - 1);
251 while offset <= stack_copy.len() - size_of::<usize>() {
252 let addr = match std::mem::size_of::<usize>() {
253 4 => stack_copy[offset..]
254 .as_ref()
255 .read_u32::<NativeEndian>()
256 .map(|u| u as usize),
257 8 => stack_copy[offset..]
258 .as_ref()
259 .read_u64::<NativeEndian>()
260 .map(|u| u as usize),
261 x => panic!("Unexpected type width: {}", x),
262 };
263 if let Ok(addr) = addr {
264 if low_addr <= addr && addr <= high_addr {
265 return true;
266 }
267 offset += size_of::<usize>();
268 } else {
269 break;
270 }
271 }
272 false
273 }
274
is_mapped_file_safe_to_open(name: &Option<String>) -> bool275 pub fn is_mapped_file_safe_to_open(name: &Option<String>) -> bool {
276 // It is unsafe to attempt to open a mapped file that lives under /dev,
277 // because the semantics of the open may be driver-specific so we'd risk
278 // hanging the crash dumper. And a file in /dev/ almost certainly has no
279 // ELF file identifier anyways.
280 if let Some(name) = name {
281 if name.starts_with("/dev/") {
282 return false;
283 }
284 }
285 true
286 }
287
elf_file_so_name(&self) -> Result<String>288 fn elf_file_so_name(&self) -> Result<String> {
289 // Find the shared object name (SONAME) by examining the ELF information
290 // for |mapping|. If the SONAME is found copy it into the passed buffer
291 // |soname| and return true. The size of the buffer is |soname_size|.
292 let mapped_file = MappingInfo::get_mmap(&self.name, self.offset)?;
293
294 let elf_obj = elf::Elf::parse(&mapped_file)?;
295
296 let soname = elf_obj.soname.ok_or_else(|| {
297 MapsReaderError::NoSoName(self.name.clone().unwrap_or_else(|| "None".to_string()))
298 })?;
299 Ok(soname.to_string())
300 }
301
get_mapping_effective_name_and_path(&self) -> Result<(String, String)>302 pub fn get_mapping_effective_name_and_path(&self) -> Result<(String, String)> {
303 let mut file_path = self.name.clone().unwrap_or_default();
304 let file_name;
305
306 // Tools such as minidump_stackwalk use the name of the module to look up
307 // symbols produced by dump_syms. dump_syms will prefer to use a module's
308 // DT_SONAME as the module name, if one exists, and will fall back to the
309 // filesystem name of the module.
310
311 // Just use the filesystem name if no SONAME is present.
312 let file_name = match self.elf_file_so_name() {
313 Ok(name) => name,
314 Err(_) => {
315 // file_path := /path/to/libname.so
316 // file_name := libname.so
317 let split: Vec<_> = file_path.rsplitn(2, '/').collect();
318 file_name = split.first().unwrap().to_string();
319 return Ok((file_path, file_name));
320 }
321 };
322
323 if self.executable && self.offset != 0 {
324 // If an executable is mapped from a non-zero offset, this is likely because
325 // the executable was loaded directly from inside an archive file (e.g., an
326 // apk on Android).
327 // In this case, we append the file_name to the mapped archive path:
328 // file_name := libname.so
329 // file_path := /path/to/ARCHIVE.APK/libname.so
330 file_path = format!("{}/{}", file_path, file_name);
331 } else {
332 // Otherwise, replace the basename with the SONAME.
333 let split: Vec<_> = file_path.rsplitn(2, '/').collect();
334 if split.len() == 2 {
335 // NOTE: rsplitn reverses the order, so the remainder is the last item
336 file_path = format!("{}/{}", split[1], file_name);
337 } else {
338 file_path = file_name.clone();
339 }
340 }
341
342 Ok((file_path, file_name))
343 }
344
is_contained_in(&self, user_mapping_list: &MappingList) -> bool345 pub fn is_contained_in(&self, user_mapping_list: &MappingList) -> bool {
346 for user in user_mapping_list {
347 // Ignore any mappings that are wholly contained within
348 // mappings in the mapping_info_ list.
349 if self.start_address >= user.mapping.start_address
350 && (self.start_address + self.size)
351 <= (user.mapping.start_address + user.mapping.size)
352 {
353 return true;
354 }
355 }
356 false
357 }
358
is_interesting(&self) -> bool359 pub fn is_interesting(&self) -> bool {
360 // only want modules with filenames.
361 self.name.is_some() &&
362 // Only want to include one mapping per shared lib.
363 // Avoid filtering executable mappings.
364 (self.offset == 0 || self.executable) &&
365 // big enough to get a signature for.
366 self.size >= 4096
367 }
368
contains_address(&self, address: usize) -> bool369 pub fn contains_address(&self, address: usize) -> bool {
370 self.system_mapping_info.start_address <= address
371 && address < self.system_mapping_info.end_address
372 }
373 }
374
375 #[cfg(test)]
376 #[cfg(target_pointer_width = "64")] // All addresses are 64 bit and I'm currently too lazy to adjust it to work for both
377 mod tests {
378 use super::*;
379
get_lines_and_loc() -> (Vec<&'static str>, u64)380 fn get_lines_and_loc() -> (Vec<&'static str>, u64) {
381 (vec![
382 "5597483fc000-5597483fe000 r--p 00000000 00:31 4750073 /usr/bin/cat",
383 "5597483fe000-559748402000 r-xp 00002000 00:31 4750073 /usr/bin/cat",
384 "559748402000-559748404000 r--p 00006000 00:31 4750073 /usr/bin/cat",
385 "559748404000-559748405000 r--p 00007000 00:31 4750073 /usr/bin/cat",
386 "559748405000-559748406000 rw-p 00008000 00:31 4750073 /usr/bin/cat",
387 "559749b0e000-559749b2f000 rw-p 00000000 00:00 0 [heap]",
388 "7efd968d3000-7efd968f5000 rw-p 00000000 00:00 0",
389 "7efd968f5000-7efd9694a000 r--p 00000000 00:31 5004638 /usr/lib/locale/en_US.utf8/LC_CTYPE",
390 "7efd9694a000-7efd96bc2000 r--p 00000000 00:31 5004373 /usr/lib/locale/en_US.utf8/LC_COLLATE",
391 "7efd96bc2000-7efd96bc4000 rw-p 00000000 00:00 0",
392 "7efd96bc4000-7efd96bea000 r--p 00000000 00:31 4996104 /lib64/libc-2.32.so",
393 "7efd96bea000-7efd96d39000 r-xp 00026000 00:31 4996104 /lib64/libc-2.32.so",
394 "7efd96d39000-7efd96d85000 r--p 00175000 00:31 4996104 /lib64/libc-2.32.so",
395 "7efd96d85000-7efd96d86000 ---p 001c1000 00:31 4996104 /lib64/libc-2.32.so",
396 "7efd96d86000-7efd96d89000 r--p 001c1000 00:31 4996104 /lib64/libc-2.32.so",
397 "7efd96d89000-7efd96d8c000 rw-p 001c4000 00:31 4996104 /lib64/libc-2.32.so",
398 "7efd96d8c000-7efd96d92000 ---p 00000000 00:00 0",
399 "7efd96da0000-7efd96da1000 r--p 00000000 00:31 5004379 /usr/lib/locale/en_US.utf8/LC_NUMERIC",
400 "7efd96da1000-7efd96da2000 r--p 00000000 00:31 5004382 /usr/lib/locale/en_US.utf8/LC_TIME",
401 "7efd96da2000-7efd96da3000 r--p 00000000 00:31 5004377 /usr/lib/locale/en_US.utf8/LC_MONETARY",
402 "7efd96da3000-7efd96da4000 r--p 00000000 00:31 5004376 /usr/lib/locale/en_US.utf8/LC_MESSAGES/SYS_LC_MESSAGES",
403 "7efd96da4000-7efd96da5000 r--p 00000000 00:31 5004380 /usr/lib/locale/en_US.utf8/LC_PAPER",
404 "7efd96da5000-7efd96da6000 r--p 00000000 00:31 5004378 /usr/lib/locale/en_US.utf8/LC_NAME",
405 "7efd96da6000-7efd96da7000 r--p 00000000 00:31 5004372 /usr/lib/locale/en_US.utf8/LC_ADDRESS",
406 "7efd96da7000-7efd96da8000 r--p 00000000 00:31 5004381 /usr/lib/locale/en_US.utf8/LC_TELEPHONE",
407 "7efd96da8000-7efd96da9000 r--p 00000000 00:31 5004375 /usr/lib/locale/en_US.utf8/LC_MEASUREMENT",
408 "7efd96da9000-7efd96db0000 r--s 00000000 00:31 5004639 /usr/lib64/gconv/gconv-modules.cache",
409 "7efd96db0000-7efd96db1000 r--p 00000000 00:31 5004374 /usr/lib/locale/en_US.utf8/LC_IDENTIFICATION",
410 "7efd96db1000-7efd96db2000 r--p 00000000 00:31 4996100 /lib64/ld-2.32.so",
411 "7efd96db2000-7efd96dd3000 r-xp 00001000 00:31 4996100 /lib64/ld-2.32.so",
412 "7efd96dd3000-7efd96ddc000 r--p 00022000 00:31 4996100 /lib64/ld-2.32.so",
413 "7efd96ddc000-7efd96ddd000 r--p 0002a000 00:31 4996100 /lib64/ld-2.32.so",
414 "7efd96ddd000-7efd96ddf000 rw-p 0002b000 00:31 4996100 /lib64/ld-2.32.so",
415 "7ffc6dfda000-7ffc6dffb000 rw-p 00000000 00:00 0 [stack]",
416 "7ffc6e0f3000-7ffc6e0f7000 r--p 00000000 00:00 0 [vvar]",
417 "7ffc6e0f7000-7ffc6e0f9000 r-xp 00000000 00:00 0 [vdso]",
418 "ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]"
419 ], 0x7ffc6e0f7000)
420 }
421
get_all_mappings() -> Vec<MappingInfo>422 fn get_all_mappings() -> Vec<MappingInfo> {
423 let mut mappings: Vec<MappingInfo> = Vec::new();
424 let (lines, linux_gate_loc) = get_lines_and_loc();
425 // Only /usr/bin/cat and [heap]
426 for line in lines {
427 match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
428 Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
429 Ok(MappingInfoParsingResult::SkipLine) => continue,
430 Err(_) => assert!(false),
431 }
432 }
433 assert_eq!(mappings.len(), 23);
434 mappings
435 }
436
437 #[test]
test_merged()438 fn test_merged() {
439 let mut mappings: Vec<MappingInfo> = Vec::new();
440 let (lines, linux_gate_loc) = get_lines_and_loc();
441 // Only /usr/bin/cat and [heap]
442 for line in lines[0..=6].iter() {
443 match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
444 Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
445 Ok(MappingInfoParsingResult::SkipLine) => continue,
446 Err(_) => assert!(false),
447 }
448 }
449
450 assert_eq!(mappings.len(), 3);
451 let cat_map = MappingInfo {
452 start_address: 0x5597483fc000,
453 size: 40960,
454 system_mapping_info: SystemMappingInfo {
455 start_address: 0x5597483fc000,
456 end_address: 0x559748406000,
457 },
458 offset: 0,
459 executable: true,
460 name: Some("/usr/bin/cat".to_string()),
461 };
462
463 assert_eq!(mappings[0], cat_map);
464
465 let heap_map = MappingInfo {
466 start_address: 0x559749b0e000,
467 size: 135168,
468 system_mapping_info: SystemMappingInfo {
469 start_address: 0x559749b0e000,
470 end_address: 0x559749b2f000,
471 },
472 offset: 0,
473 executable: false,
474 name: Some("[heap]".to_string()),
475 };
476
477 assert_eq!(mappings[1], heap_map);
478
479 let empty_map = MappingInfo {
480 start_address: 0x7efd968d3000,
481 size: 139264,
482 system_mapping_info: SystemMappingInfo {
483 start_address: 0x7efd968d3000,
484 end_address: 0x7efd968f5000,
485 },
486 offset: 0,
487 executable: false,
488 name: None,
489 };
490
491 assert_eq!(mappings[2], empty_map);
492 }
493
494 #[test]
test_linux_gate_parsing()495 fn test_linux_gate_parsing() {
496 let mappings = get_all_mappings();
497
498 let gate_map = MappingInfo {
499 start_address: 0x7ffc6e0f7000,
500 size: 8192,
501 system_mapping_info: SystemMappingInfo {
502 start_address: 0x7ffc6e0f7000,
503 end_address: 0x7ffc6e0f9000,
504 },
505 offset: 0,
506 executable: true,
507 name: Some("linux-gate.so".to_string()),
508 };
509
510 assert_eq!(mappings[21], gate_map);
511 }
512
513 #[test]
test_reading_all()514 fn test_reading_all() {
515 let mappings = get_all_mappings();
516
517 let found_items = vec![
518 Some("/usr/bin/cat".to_string()),
519 Some("[heap]".to_string()),
520 None,
521 Some("/usr/lib/locale/en_US.utf8/LC_CTYPE".to_string()),
522 Some("/usr/lib/locale/en_US.utf8/LC_COLLATE".to_string()),
523 None,
524 Some("/lib64/libc-2.32.so".to_string()),
525 // The original shows a None here, but this is an address ranges that the
526 // linker reserved but which a loaded library did not use. These
527 // appear as an anonymous private mapping with no access flags set
528 // and which directly follow an executable mapping.
529 Some("/usr/lib/locale/en_US.utf8/LC_NUMERIC".to_string()),
530 Some("/usr/lib/locale/en_US.utf8/LC_TIME".to_string()),
531 Some("/usr/lib/locale/en_US.utf8/LC_MONETARY".to_string()),
532 Some("/usr/lib/locale/en_US.utf8/LC_MESSAGES/SYS_LC_MESSAGES".to_string()),
533 Some("/usr/lib/locale/en_US.utf8/LC_PAPER".to_string()),
534 Some("/usr/lib/locale/en_US.utf8/LC_NAME".to_string()),
535 Some("/usr/lib/locale/en_US.utf8/LC_ADDRESS".to_string()),
536 Some("/usr/lib/locale/en_US.utf8/LC_TELEPHONE".to_string()),
537 Some("/usr/lib/locale/en_US.utf8/LC_MEASUREMENT".to_string()),
538 Some("/usr/lib64/gconv/gconv-modules.cache".to_string()),
539 Some("/usr/lib/locale/en_US.utf8/LC_IDENTIFICATION".to_string()),
540 Some("/lib64/ld-2.32.so".to_string()),
541 Some("[stack]".to_string()),
542 Some("[vvar]".to_string()),
543 // This is rewritten from [vdso] to linux-gate.so
544 Some("linux-gate.so".to_string()),
545 Some("[vsyscall]".to_string()),
546 ];
547
548 assert_eq!(
549 mappings.iter().map(|x| x.name.clone()).collect::<Vec<_>>(),
550 found_items
551 );
552 }
553
554 #[test]
test_merged_reserved_mappings()555 fn test_merged_reserved_mappings() {
556 let mappings = get_all_mappings();
557
558 let gate_map = MappingInfo {
559 start_address: 0x7efd96bc4000,
560 size: 1892352, // Merged the anonymous area after in this mapping, so its bigger..
561 system_mapping_info: SystemMappingInfo {
562 start_address: 0x7efd96bc4000,
563 end_address: 0x7efd96d8c000, // ..but this is not visible here
564 },
565 offset: 0,
566 executable: true,
567 name: Some("/lib64/libc-2.32.so".to_string()),
568 };
569
570 assert_eq!(mappings[6], gate_map);
571 }
572
573 #[test]
test_get_mapping_effective_name()574 fn test_get_mapping_effective_name() {
575 let lines = vec![
576 "7f0b97b6f000-7f0b97b70000 r--p 00000000 00:3e 27136458 /home/martin/Documents/mozilla/devel/mozilla-central/obj/widget/gtk/mozgtk/gtk3/libmozgtk.so",
577 "7f0b97b70000-7f0b97b71000 r-xp 00000000 00:3e 27136458 /home/martin/Documents/mozilla/devel/mozilla-central/obj/widget/gtk/mozgtk/gtk3/libmozgtk.so",
578 "7f0b97b71000-7f0b97b73000 r--p 00000000 00:3e 27136458 /home/martin/Documents/mozilla/devel/mozilla-central/obj/widget/gtk/mozgtk/gtk3/libmozgtk.so",
579 "7f0b97b73000-7f0b97b74000 rw-p 00001000 00:3e 27136458 /home/martin/Documents/mozilla/devel/mozilla-central/obj/widget/gtk/mozgtk/gtk3/libmozgtk.so",
580 ];
581 let linux_gate_loc = 0x7ffe091bf000;
582 let mut mappings: Vec<MappingInfo> = Vec::new();
583 for line in lines {
584 match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
585 Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
586 Ok(MappingInfoParsingResult::SkipLine) => continue,
587 Err(_) => assert!(false),
588 }
589 }
590 assert_eq!(mappings.len(), 1);
591
592 let (file_path, file_name) = mappings[0]
593 .get_mapping_effective_name_and_path()
594 .expect("Couldn't get effective name for mapping");
595 assert_eq!(file_name, "libmozgtk.so");
596 assert_eq!(file_path, "/home/martin/Documents/mozilla/devel/mozilla-central/obj/widget/gtk/mozgtk/gtk3/libmozgtk.so");
597 }
598
599 #[test]
test_whitespaces_in_maps()600 fn test_whitespaces_in_maps() {
601 let lines = vec![
602 " 7f0b97b6f000-7f0b97b70000 r--p 00000000 00:3e 27136458 libmozgtk.so",
603 "7f0b97b70000-7f0b97b71000 r-xp 00000000 00:3e 27136458 libmozgtk.so ",
604 "7f0b97b71000-7f0b97b73000 r--p 00000000 00:3e 27136458\t\t\tlibmozgtk.so",
605 ];
606 let linux_gate_loc = 0x7ffe091bf000;
607 let mut mappings: Vec<MappingInfo> = Vec::new();
608 for line in lines {
609 match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
610 Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
611 Ok(MappingInfoParsingResult::SkipLine) => continue,
612 Err(x) => panic!("{:?}", x),
613 }
614 }
615 assert_eq!(mappings.len(), 1);
616
617 let expected_map = MappingInfo {
618 start_address: 0x7f0b97b6f000,
619 size: 16384,
620 system_mapping_info: SystemMappingInfo {
621 start_address: 0x7f0b97b6f000,
622 end_address: 0x7f0b97b73000,
623 },
624 offset: 0,
625 executable: true,
626 name: Some("libmozgtk.so".to_string()),
627 };
628
629 assert_eq!(expected_map, mappings[0]);
630 }
631
632 #[test]
test_whitespaces_in_name()633 fn test_whitespaces_in_name() {
634 let lines = vec![
635 "10000000-20000000 r--p 00000000 00:3e 27136458 libmoz gtk.so",
636 "20000000-30000000 r--p 00000000 00:3e 27136458 libmozgtk.so (deleted)",
637 "30000000-40000000 r--p 00000000 00:3e 27136458 \"libmoz gtk.so (deleted)\"",
638 "30000000-40000000 r--p 00000000 00:3e 27136458 ",
639 ];
640 let linux_gate_loc = 0x7ffe091bf000;
641 let mut mappings: Vec<MappingInfo> = Vec::new();
642 for line in lines {
643 match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
644 Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
645 Ok(MappingInfoParsingResult::SkipLine) => continue,
646 Err(_) => assert!(false),
647 }
648 }
649 assert_eq!(mappings.len(), 4);
650 assert_eq!(mappings[0].name, Some("libmoz gtk.so".to_string()));
651 assert_eq!(mappings[1].name, Some("libmozgtk.so (deleted)".to_string()));
652 assert_eq!(
653 mappings[2].name,
654 Some("\"libmoz gtk.so (deleted)\"".to_string())
655 );
656 assert_eq!(mappings[3].name, None);
657 }
658 }
659