From 98b2395899aa328f5097c976c1ab3cee30d70cb3 Mon Sep 17 00:00:00 2001
From: Alex Franchuk
Date: Fri, 24 Feb 2023 16:11:08 -0500
Subject: [PATCH 1/8] Write a MemoryInfoListStream on linux.
This makes it easier for consumers to read the minidumps, rather than
needing to parse linux-specific information.
Closes #8.
---
Cargo.toml | 6 +-
src/bin/test.rs | 6 +-
src/linux/errors.rs | 29 +-
src/linux/maps_reader.rs | 498 ++++++++----------
src/linux/minidump_writer.rs | 8 +-
src/linux/ptrace_dumper.rs | 59 +--
src/linux/sections.rs | 1 +
src/linux/sections/mappings.rs | 4 +-
src/linux/sections/memory_info_list_stream.rs | 66 +++
src/minidump_format.rs | 3 +-
tests/linux_minidump_writer.rs | 2 +-
11 files changed, 347 insertions(+), 335 deletions(-)
create mode 100644 src/linux/sections/memory_info_list_stream.rs
diff --git a/Cargo.toml b/Cargo.toml
index ee85b34f..f25d135c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,9 @@ byteorder = "1.3.2"
cfg-if = "1.0"
crash-context = "0.5"
memoffset = "0.8"
-minidump-common = "0.15"
+#minidump-common = "0.15"
+# XXX temporary for development
+minidump-common = { path = "../rust-minidump/minidump-common" }
scroll = "0.11"
tempfile = "3.1.0"
thiserror = "1.0.21"
@@ -30,6 +32,8 @@ nix = { version = "0.26", default-features = false, features = [
"ptrace",
"user",
] }
+# Used for parsing procfs info.
+procfs = "0.15.1"
[target.'cfg(target_os = "macos")'.dependencies]
# Binds some additional mac specifics not in libc
diff --git a/src/bin/test.rs b/src/bin/test.rs
index accbc998..a94086c6 100644
--- a/src/bin/test.rs
+++ b/src/bin/test.rs
@@ -109,7 +109,7 @@ mod linux {
let dumper = PtraceDumper::new(getppid().as_raw())?;
let mut mapping_count = 0;
for map in &dumper.mappings {
- if map.name == Some(path.clone()) {
+ if map.name == Some(path.clone().into()) {
mapping_count += 1;
// This mapping should encompass the entire original mapped
// range.
@@ -127,7 +127,7 @@ mod linux {
let mut dumper = PtraceDumper::new(ppid)?;
let mut found_linux_gate = false;
for mut mapping in dumper.mappings.clone() {
- if mapping.name.as_deref() == Some(LINUX_GATE_LIBRARY_NAME) {
+ if mapping.name == Some(LINUX_GATE_LIBRARY_NAME.into()) {
found_linux_gate = true;
dumper.suspend_threads()?;
let id = PtraceDumper::elf_identifier_for_mapping(&mut mapping, ppid)?;
@@ -148,7 +148,7 @@ mod linux {
test!(linux_gate_loc != 0, "linux_gate_loc == 0")?;
let mut found_linux_gate = false;
for mapping in &dumper.mappings {
- if mapping.name.as_deref() == Some(LINUX_GATE_LIBRARY_NAME) {
+ if mapping.name == Some(LINUX_GATE_LIBRARY_NAME.into()) {
found_linux_gate = true;
test!(
linux_gate_loc == mapping.start_address.try_into()?,
diff --git a/src/linux/errors.rs b/src/linux/errors.rs
index e423389f..d7b1c586 100644
--- a/src/linux/errors.rs
+++ b/src/linux/errors.rs
@@ -3,6 +3,7 @@ use crate::maps_reader::MappingInfo;
use crate::mem_writer::MemoryWriterError;
use crate::thread_info::Pid;
use goblin;
+use std::ffi::OsString;
use thiserror::Error;
#[derive(Debug, Error)]
@@ -28,20 +29,22 @@ pub enum MapsReaderError {
LinuxGateNotConvertable(#[from] std::num::TryFromIntError),
// get_mmap()
- #[error("Not safe to open mapping {0}")]
- NotSafeToOpenMapping(String),
+ #[error("Not safe to open mapping {}", .0.to_string_lossy())]
+ NotSafeToOpenMapping(OsString),
#[error("IO Error")]
FileError(#[from] std::io::Error),
#[error("Mmapped file empty or not an ELF file")]
MmapSanityCheckFailed,
- #[error("Symlink does not match ({0} vs. {1}")]
+ #[error("Symlink does not match ({0} vs. {1})")]
SymlinkError(std::path::PathBuf, std::path::PathBuf),
- // handle_deleted_file_in_mapping()
+ // fixup_deleted_file()
#[error("Couldn't parse as ELF file")]
ELFParsingFailed(#[from] goblin::error::Error),
- #[error("No soname found (filename: {0}")]
- NoSoName(String),
+ #[error("An anonymous mapping has no associated file")]
+ AnonymousMapping,
+ #[error("No soname found (filename: {})", .0.to_string_lossy())]
+ NoSoName(OsString),
}
#[derive(Debug, Error)]
@@ -114,8 +117,8 @@ pub enum DumperError {
ELFParsingFailed(#[from] goblin::error::Error),
#[error("No build-id found")]
NoBuildIDFound,
- #[error("Not safe to open mapping: {0}")]
- NotSafeToOpenMapping(String),
+ #[error("Not safe to open mapping: {}", .0.to_string_lossy())]
+ NotSafeToOpenMapping(OsString),
#[error("Failed integer conversion")]
TryFromIntError(#[from] std::num::TryFromIntError),
#[error("Maps reader error")]
@@ -144,6 +147,14 @@ pub enum SectionMappingsError {
GetEffectivePathError(MappingInfo, #[source] MapsReaderError),
}
+#[derive(Debug, Error)]
+pub enum SectionMemInfoListError {
+ #[error("Failed to write to memory")]
+ MemoryWriterError(#[from] MemoryWriterError),
+ #[error("Failed to read from procfs")]
+ ProcfsError(#[from] procfs::ProcError),
+}
+
#[derive(Debug, Error)]
pub enum SectionMemListError {
#[error("Failed to write to memory")]
@@ -210,6 +221,8 @@ pub enum WriterError {
SectionMemListError(#[from] SectionMemListError),
#[error("Failed when writing section SystemInfo")]
SectionSystemInfoError(#[from] SectionSystemInfoError),
+ #[error("Failed when writing section MemoryInfoList")]
+ SectionMemoryInfoListError(#[from] SectionMemInfoListError),
#[error("Failed when writing section ThreadList")]
SectionThreadListError(#[from] SectionThreadListError),
#[error("Failed when writing section ThreadNameList")]
diff --git a/src/linux/maps_reader.rs b/src/linux/maps_reader.rs
index 8050387a..44267229 100644
--- a/src/linux/maps_reader.rs
+++ b/src/linux/maps_reader.rs
@@ -4,11 +4,13 @@ use crate::thread_info::Pid;
use byteorder::{NativeEndian, ReadBytesExt};
use goblin::elf;
use memmap2::{Mmap, MmapOptions};
+use procfs::process::{MMPermissions, MMapPath, MemoryMaps};
+use std::ffi::{OsStr, OsString};
+use std::os::unix::ffi::OsStrExt;
use std::{fs::File, mem::size_of, path::PathBuf};
pub const LINUX_GATE_LIBRARY_NAME: &str = "linux-gate.so";
-pub const DELETED_SUFFIX: &str = " (deleted)";
-pub const RESERVED_FLAGS: &str = "---p";
+pub const DELETED_SUFFIX: &[u8] = b" (deleted)";
type Result = std::result::Result;
@@ -36,7 +38,7 @@ pub struct MappingInfo {
pub system_mapping_info: SystemMappingInfo,
pub offset: usize, // offset into the backed file.
pub executable: bool, // true if the mapping has the execute bit set.
- pub name: Option,
+ pub name: Option,
// pub elf_obj: Option,
}
@@ -55,133 +57,91 @@ pub enum MappingInfoParsingResult {
Success(MappingInfo),
}
-fn is_mapping_a_path(pathname: Option<&str>) -> bool {
+fn is_mapping_a_path(pathname: Option<&OsStr>) -> bool {
match pathname {
- Some(x) => x.contains('/'),
+ Some(x) => x.as_bytes().contains(&b'/'),
None => false,
}
}
impl MappingInfo {
- pub fn parse_from_line(
- line: &str,
- linux_gate_loc: AuxvType,
- last_mapping: Option<&mut MappingInfo>,
- ) -> Result {
- let mut last_whitespace = false;
-
- // There is no `line.splitn_whitespace(6)`, so we have to do it somewhat manually
- // Split at the first whitespace, trim of the rest.
- let mut splits = line
- .trim()
- .splitn(6, |c: char| {
- if c.is_whitespace() {
- if last_whitespace {
- return false;
- }
- last_whitespace = true;
- true
- } else {
- last_whitespace = false;
- false
- }
- })
- .map(str::trim);
-
- let address = splits
- .next()
- .ok_or(MapsReaderError::MapEntryMalformed("address"))?;
- let perms = splits
- .next()
- .ok_or(MapsReaderError::MapEntryMalformed("permissions"))?;
- let mut offset = usize::from_str_radix(
- splits
- .next()
- .ok_or(MapsReaderError::MapEntryMalformed("offset"))?,
- 16,
- )?;
- let _dev = splits
- .next()
- .ok_or(MapsReaderError::MapEntryMalformed("dev"))?;
- let _inode = splits
- .next()
- .ok_or(MapsReaderError::MapEntryMalformed("inode"))?;
- let mut pathname = splits.next(); // Optional
-
- // Due to our ugly `splitn_whitespace()` hack from above, we might have
- // only trailing whitespaces as the name, so we it might still be "Some()"
- if let Some(x) = pathname {
- if x.is_empty() {
- pathname = None;
- }
- }
-
- let mut addresses = address.split('-');
- let start_address = usize::from_str_radix(addresses.next().unwrap(), 16)?;
- let end_address = usize::from_str_radix(addresses.next().unwrap(), 16)?;
-
- let executable = perms.contains('x');
-
- // Only copy name if the name is a valid path name, or if
- // it's the VDSO image.
- let is_path = is_mapping_a_path(pathname);
+ pub fn aggregate(memory_maps: MemoryMaps, linux_gate_loc: AuxvType) -> Result> {
+ let mut infos = Vec::::new();
+
+ for mm in memory_maps.memory_maps {
+ let start_address: usize = mm.address.0.try_into()?;
+ let end_address: usize = mm.address.1.try_into()?;
+ let mut offset: usize = mm.offset.try_into()?;
+
+ let executable = mm.perms.contains(MMPermissions::EXECUTE);
+
+ let mut pathname: Option = match mm.pathname {
+ MMapPath::Path(p) => Some(p.into()),
+ MMapPath::Heap => Some("[heap]".into()),
+ MMapPath::Stack => Some("[stack]".into()),
+ MMapPath::TStack(i) => Some(format!("[stack:{i}]").into()),
+ MMapPath::Vdso => Some("[vdso]".into()),
+ MMapPath::Vvar => Some("[vvar]".into()),
+ MMapPath::Vsyscall => Some("[vsyscall]".into()),
+ MMapPath::Rollup => Some("[rollup]".into()),
+ MMapPath::Vsys(i) => Some(format!("/SYSV{i:x}").into()),
+ MMapPath::Other(n) => Some(format!("[{n}]").into()),
+ MMapPath::Anonymous => None,
+ };
- if !is_path && linux_gate_loc != 0 && start_address == linux_gate_loc.try_into()? {
- pathname = Some(LINUX_GATE_LIBRARY_NAME);
- offset = 0;
- }
+ let is_path = is_mapping_a_path(pathname.as_deref());
- match (pathname, last_mapping) {
- (Some(_name), Some(module)) => {
- // Merge adjacent mappings into one module, assuming they're a single
- // library mapped by the dynamic linker.
- if (start_address == module.start_address + module.size)
- && (pathname == module.name.as_deref())
- {
- module.system_mapping_info.end_address = end_address;
- module.size = end_address - module.start_address;
- module.executable |= executable;
- return Ok(MappingInfoParsingResult::SkipLine);
- }
+ if !is_path && linux_gate_loc != 0 && start_address == linux_gate_loc.try_into()? {
+ pathname = Some(LINUX_GATE_LIBRARY_NAME.into());
+ offset = 0;
}
- (None, Some(module)) => {
- // Also merge mappings that result from address ranges that the
- // linker reserved but which a loaded library did not use. These
- // appear as an anonymous private mapping with no access flags set
- // and which directly follow an executable mapping.
- let module_end_address = module.start_address + module.size;
- if (start_address == module_end_address)
- && module.executable
- && is_mapping_a_path(module.name.as_deref())
- && (offset == 0 || offset == module_end_address)
- && perms == RESERVED_FLAGS
- {
- module.size = end_address - module.start_address;
- return Ok(MappingInfoParsingResult::SkipLine);
+
+ // Merge adjacent mappings into one module, assuming they're a single
+ // library mapped by the dynamic linker.
+ if let Some(module) = infos.last_mut() {
+ if pathname.is_some() {
+ if (start_address == module.start_address + module.size)
+ && (pathname == module.name)
+ {
+ module.system_mapping_info.end_address = end_address;
+ module.size = end_address - module.start_address;
+ module.executable |= executable;
+ continue;
+ }
+ } else {
+ // Also merge mappings that result from address ranges that the
+ // linker reserved but which a loaded library did not use. These
+ // appear as an anonymous private mapping with no access flags set
+ // and which directly follow an executable mapping.
+ let module_end_address = module.start_address + module.size;
+ if (start_address == module_end_address)
+ && module.executable
+ && is_mapping_a_path(module.name.as_deref())
+ && (offset == 0 || offset == module_end_address)
+ && mm.perms == MMPermissions::PRIVATE
+ {
+ module.size = end_address - module.start_address;
+ continue;
+ }
}
}
- _ => (),
- }
- let name = pathname.map(ToOwned::to_owned);
-
- let info = MappingInfo {
- start_address,
- size: end_address - start_address,
- system_mapping_info: SystemMappingInfo {
+ infos.push(MappingInfo {
start_address,
- end_address,
- },
- offset,
- executable,
- name,
- // elf_obj,
- };
-
- Ok(MappingInfoParsingResult::Success(info))
+ size: end_address - start_address,
+ system_mapping_info: SystemMappingInfo {
+ start_address,
+ end_address,
+ },
+ offset,
+ executable,
+ name: pathname,
+ });
+ }
+ Ok(infos)
}
- pub fn get_mmap(name: &Option, offset: usize) -> Result {
+ pub fn get_mmap(name: &Option, offset: usize) -> Result {
if !MappingInfo::is_mapped_file_safe_to_open(name) {
return Err(MapsReaderError::NotSafeToOpenMapping(
name.clone().unwrap_or_default(),
@@ -203,12 +163,25 @@ impl MappingInfo {
Ok(mapped_file)
}
- pub fn handle_deleted_file_in_mapping(path: &str, pid: Pid) -> Result {
+ /// Check whether the mapping refers to a deleted file, and if so try to find the file
+ /// elsewhere and return that path.
+ ///
+ /// Currently this only supports fixing a deleted file that was the main exe of the given
+ /// `pid`.
+ ///
+ /// Returns a tuple, where the first element is the file path (which is possibly different than
+ /// `self.name`), and the second element is the original file path if a different path was
+ /// used. If no mapping name exists, returns an error.
+ pub fn fixup_deleted_file(&self, pid: Pid) -> Result<(OsString, Option<&OsStr>)> {
// Check for ' (deleted)' in |path|.
// |path| has to be at least as long as "/x (deleted)".
- if !path.ends_with(DELETED_SUFFIX) {
- return Ok(path.to_string());
- }
+ let Some(path) = &self.name else {
+ return Err(MapsReaderError::AnonymousMapping);
+ };
+
+ let Some(old_path) = path.as_bytes().strip_suffix(DELETED_SUFFIX) else {
+ return Ok((path.clone(), None));
+ };
// Check |path| against the /proc/pid/exe 'symlink'.
let exe_link = format!("/proc/{}/exe", pid);
@@ -218,7 +191,7 @@ impl MappingInfo {
// if (!GetMappingAbsolutePath(new_mapping, new_path))
// return false;
- if link_path != PathBuf::from(path) {
+ if &link_path != path {
return Err(MapsReaderError::SymlinkError(
PathBuf::from(path),
link_path,
@@ -233,7 +206,7 @@ impl MappingInfo {
// return Err("".into());
// }
// }
- Ok(exe_link)
+ Ok((exe_link.into(), Some(OsStr::from_bytes(old_path))))
}
pub fn stack_has_pointer_to_mapping(&self, stack_copy: &[u8], sp_offset: usize) -> bool {
@@ -269,13 +242,13 @@ impl MappingInfo {
false
}
- pub fn is_mapped_file_safe_to_open(name: &Option) -> bool {
+ pub fn is_mapped_file_safe_to_open(name: &Option) -> bool {
// It is unsafe to attempt to open a mapped file that lives under /dev,
// because the semantics of the open may be driver-specific so we'd risk
// hanging the crash dumper. And a file in /dev/ almost certainly has no
// ELF file identifier anyways.
if let Some(name) = name {
- if name.starts_with("/dev/") {
+ if name.as_bytes().starts_with(b"/dev/") {
return false;
}
}
@@ -291,13 +264,13 @@ impl MappingInfo {
let elf_obj = elf::Elf::parse(&mapped_file)?;
let soname = elf_obj.soname.ok_or_else(|| {
- MapsReaderError::NoSoName(self.name.clone().unwrap_or_else(|| "None".to_string()))
+ MapsReaderError::NoSoName(self.name.clone().unwrap_or_else(|| "None".into()))
})?;
Ok(soname.to_string())
}
- pub fn get_mapping_effective_name_and_path(&self) -> Result<(String, String)> {
- let mut file_path = self.name.clone().unwrap_or_default();
+ pub fn get_mapping_effective_path_and_name(&self) -> Result<(PathBuf, String)> {
+ let mut file_path = PathBuf::from(self.name.clone().unwrap_or_default());
// Tools such as minidump_stackwalk use the name of the module to look up
// symbols produced by dump_syms. dump_syms will prefer to use a module's
@@ -310,8 +283,10 @@ impl MappingInfo {
} else {
// file_path := /path/to/libname.so
// file_name := libname.so
- // SAFETY: The unwrap is safe as rsplit always returns at least one item
- let file_name = file_path.rsplit('/').next().unwrap().to_owned();
+ let file_name = file_path
+ .file_name()
+ .map(|s| s.to_string_lossy().into_owned())
+ .unwrap_or_default();
return Ok((file_path, file_name));
};
@@ -322,16 +297,10 @@ impl MappingInfo {
// In this case, we append the file_name to the mapped archive path:
// file_name := libname.so
// file_path := /path/to/ARCHIVE.APK/libname.so
- file_path = format!("{}/{}", file_path, file_name);
+ file_path.push(&file_name);
} else {
// Otherwise, replace the basename with the SONAME.
- let split: Vec<_> = file_path.rsplitn(2, '/').collect();
- if split.len() == 2 {
- // NOTE: rsplitn reverses the order, so the remainder is the last item
- file_path = format!("{}/{}", split[1], file_name);
- } else {
- file_path = file_name.clone();
- }
+ file_path.set_file_name(&file_name);
}
Ok((file_path, file_name))
@@ -372,77 +341,72 @@ impl MappingInfo {
mod tests {
use super::*;
- fn get_lines_and_loc() -> (Vec<&'static str>, u64) {
- (vec![
-"5597483fc000-5597483fe000 r--p 00000000 00:31 4750073 /usr/bin/cat",
-"5597483fe000-559748402000 r-xp 00002000 00:31 4750073 /usr/bin/cat",
-"559748402000-559748404000 r--p 00006000 00:31 4750073 /usr/bin/cat",
-"559748404000-559748405000 r--p 00007000 00:31 4750073 /usr/bin/cat",
-"559748405000-559748406000 rw-p 00008000 00:31 4750073 /usr/bin/cat",
-"559749b0e000-559749b2f000 rw-p 00000000 00:00 0 [heap]",
-"7efd968d3000-7efd968f5000 rw-p 00000000 00:00 0",
-"7efd968f5000-7efd9694a000 r--p 00000000 00:31 5004638 /usr/lib/locale/en_US.utf8/LC_CTYPE",
-"7efd9694a000-7efd96bc2000 r--p 00000000 00:31 5004373 /usr/lib/locale/en_US.utf8/LC_COLLATE",
-"7efd96bc2000-7efd96bc4000 rw-p 00000000 00:00 0",
-"7efd96bc4000-7efd96bea000 r--p 00000000 00:31 4996104 /lib64/libc-2.32.so",
-"7efd96bea000-7efd96d39000 r-xp 00026000 00:31 4996104 /lib64/libc-2.32.so",
-"7efd96d39000-7efd96d85000 r--p 00175000 00:31 4996104 /lib64/libc-2.32.so",
-"7efd96d85000-7efd96d86000 ---p 001c1000 00:31 4996104 /lib64/libc-2.32.so",
-"7efd96d86000-7efd96d89000 r--p 001c1000 00:31 4996104 /lib64/libc-2.32.so",
-"7efd96d89000-7efd96d8c000 rw-p 001c4000 00:31 4996104 /lib64/libc-2.32.so",
-"7efd96d8c000-7efd96d92000 ---p 00000000 00:00 0",
-"7efd96da0000-7efd96da1000 r--p 00000000 00:31 5004379 /usr/lib/locale/en_US.utf8/LC_NUMERIC",
-"7efd96da1000-7efd96da2000 r--p 00000000 00:31 5004382 /usr/lib/locale/en_US.utf8/LC_TIME",
-"7efd96da2000-7efd96da3000 r--p 00000000 00:31 5004377 /usr/lib/locale/en_US.utf8/LC_MONETARY",
-"7efd96da3000-7efd96da4000 r--p 00000000 00:31 5004376 /usr/lib/locale/en_US.utf8/LC_MESSAGES/SYS_LC_MESSAGES",
-"7efd96da4000-7efd96da5000 r--p 00000000 00:31 5004380 /usr/lib/locale/en_US.utf8/LC_PAPER",
-"7efd96da5000-7efd96da6000 r--p 00000000 00:31 5004378 /usr/lib/locale/en_US.utf8/LC_NAME",
-"7efd96da6000-7efd96da7000 r--p 00000000 00:31 5004372 /usr/lib/locale/en_US.utf8/LC_ADDRESS",
-"7efd96da7000-7efd96da8000 r--p 00000000 00:31 5004381 /usr/lib/locale/en_US.utf8/LC_TELEPHONE",
-"7efd96da8000-7efd96da9000 r--p 00000000 00:31 5004375 /usr/lib/locale/en_US.utf8/LC_MEASUREMENT",
-"7efd96da9000-7efd96db0000 r--s 00000000 00:31 5004639 /usr/lib64/gconv/gconv-modules.cache",
-"7efd96db0000-7efd96db1000 r--p 00000000 00:31 5004374 /usr/lib/locale/en_US.utf8/LC_IDENTIFICATION",
-"7efd96db1000-7efd96db2000 r--p 00000000 00:31 4996100 /lib64/ld-2.32.so",
-"7efd96db2000-7efd96dd3000 r-xp 00001000 00:31 4996100 /lib64/ld-2.32.so",
-"7efd96dd3000-7efd96ddc000 r--p 00022000 00:31 4996100 /lib64/ld-2.32.so",
-"7efd96ddc000-7efd96ddd000 r--p 0002a000 00:31 4996100 /lib64/ld-2.32.so",
-"7efd96ddd000-7efd96ddf000 rw-p 0002b000 00:31 4996100 /lib64/ld-2.32.so",
-"7ffc6dfda000-7ffc6dffb000 rw-p 00000000 00:00 0 [stack]",
-"7ffc6e0f3000-7ffc6e0f7000 r--p 00000000 00:00 0 [vvar]",
-"7ffc6e0f7000-7ffc6e0f9000 r-xp 00000000 00:00 0 [vdso]",
-"ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]"
- ], 0x7ffc6e0f7000)
+ fn get_mappings_for(map: &str, linux_gate_loc: u64) -> Vec {
+ MappingInfo::aggregate(
+ MemoryMaps::from_reader(map.as_bytes()).expect("failed to read mapping info"),
+ linux_gate_loc,
+ )
+ .unwrap_or_default()
}
+ const LINES: &str = "\
+5597483fc000-5597483fe000 r--p 00000000 00:31 4750073 /usr/bin/cat
+5597483fe000-559748402000 r-xp 00002000 00:31 4750073 /usr/bin/cat
+559748402000-559748404000 r--p 00006000 00:31 4750073 /usr/bin/cat
+559748404000-559748405000 r--p 00007000 00:31 4750073 /usr/bin/cat
+559748405000-559748406000 rw-p 00008000 00:31 4750073 /usr/bin/cat
+559749b0e000-559749b2f000 rw-p 00000000 00:00 0 [heap]
+7efd968d3000-7efd968f5000 rw-p 00000000 00:00 0
+7efd968f5000-7efd9694a000 r--p 00000000 00:31 5004638 /usr/lib/locale/en_US.utf8/LC_CTYPE
+7efd9694a000-7efd96bc2000 r--p 00000000 00:31 5004373 /usr/lib/locale/en_US.utf8/LC_COLLATE
+7efd96bc2000-7efd96bc4000 rw-p 00000000 00:00 0
+7efd96bc4000-7efd96bea000 r--p 00000000 00:31 4996104 /lib64/libc-2.32.so
+7efd96bea000-7efd96d39000 r-xp 00026000 00:31 4996104 /lib64/libc-2.32.so
+7efd96d39000-7efd96d85000 r--p 00175000 00:31 4996104 /lib64/libc-2.32.so
+7efd96d85000-7efd96d86000 ---p 001c1000 00:31 4996104 /lib64/libc-2.32.so
+7efd96d86000-7efd96d89000 r--p 001c1000 00:31 4996104 /lib64/libc-2.32.so
+7efd96d89000-7efd96d8c000 rw-p 001c4000 00:31 4996104 /lib64/libc-2.32.so
+7efd96d8c000-7efd96d92000 ---p 00000000 00:00 0
+7efd96da0000-7efd96da1000 r--p 00000000 00:31 5004379 /usr/lib/locale/en_US.utf8/LC_NUMERIC
+7efd96da1000-7efd96da2000 r--p 00000000 00:31 5004382 /usr/lib/locale/en_US.utf8/LC_TIME
+7efd96da2000-7efd96da3000 r--p 00000000 00:31 5004377 /usr/lib/locale/en_US.utf8/LC_MONETARY
+7efd96da3000-7efd96da4000 r--p 00000000 00:31 5004376 /usr/lib/locale/en_US.utf8/LC_MESSAGES/SYS_LC_MESSAGES
+7efd96da4000-7efd96da5000 r--p 00000000 00:31 5004380 /usr/lib/locale/en_US.utf8/LC_PAPER
+7efd96da5000-7efd96da6000 r--p 00000000 00:31 5004378 /usr/lib/locale/en_US.utf8/LC_NAME
+7efd96da6000-7efd96da7000 r--p 00000000 00:31 5004372 /usr/lib/locale/en_US.utf8/LC_ADDRESS
+7efd96da7000-7efd96da8000 r--p 00000000 00:31 5004381 /usr/lib/locale/en_US.utf8/LC_TELEPHONE
+7efd96da8000-7efd96da9000 r--p 00000000 00:31 5004375 /usr/lib/locale/en_US.utf8/LC_MEASUREMENT
+7efd96da9000-7efd96db0000 r--s 00000000 00:31 5004639 /usr/lib64/gconv/gconv-modules.cache
+7efd96db0000-7efd96db1000 r--p 00000000 00:31 5004374 /usr/lib/locale/en_US.utf8/LC_IDENTIFICATION
+7efd96db1000-7efd96db2000 r--p 00000000 00:31 4996100 /lib64/ld-2.32.so
+7efd96db2000-7efd96dd3000 r-xp 00001000 00:31 4996100 /lib64/ld-2.32.so
+7efd96dd3000-7efd96ddc000 r--p 00022000 00:31 4996100 /lib64/ld-2.32.so
+7efd96ddc000-7efd96ddd000 r--p 0002a000 00:31 4996100 /lib64/ld-2.32.so
+7efd96ddd000-7efd96ddf000 rw-p 0002b000 00:31 4996100 /lib64/ld-2.32.so
+7ffc6dfda000-7ffc6dffb000 rw-p 00000000 00:00 0 [stack]
+7ffc6e0f3000-7ffc6e0f7000 r--p 00000000 00:00 0 [vvar]
+7ffc6e0f7000-7ffc6e0f9000 r-xp 00000000 00:00 0 [vdso]
+ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall]";
+ const LINUX_GATE_LOC: u64 = 0x7ffc6e0f7000;
+
fn get_all_mappings() -> Vec {
- let mut mappings: Vec = Vec::new();
- let (lines, linux_gate_loc) = get_lines_and_loc();
- // Only /usr/bin/cat and [heap]
- for line in lines {
- match MappingInfo::parse_from_line(line, linux_gate_loc, mappings.last_mut())
- .expect("failed to read mapping info")
- {
- MappingInfoParsingResult::Success(map) => mappings.push(map),
- MappingInfoParsingResult::SkipLine => continue,
- }
- }
- assert_eq!(mappings.len(), 23);
- mappings
+ get_mappings_for(LINES, LINUX_GATE_LOC)
}
#[test]
fn test_merged() {
- let mut mappings: Vec = Vec::new();
- let (lines, linux_gate_loc) = get_lines_and_loc();
// Only /usr/bin/cat and [heap]
- for line in lines[0..=6].iter() {
- match MappingInfo::parse_from_line(line, linux_gate_loc, mappings.last_mut())
- .expect("failed to read mapping info")
- {
- MappingInfoParsingResult::Success(map) => mappings.push(map),
- MappingInfoParsingResult::SkipLine => continue,
- }
- }
+ let mappings = get_mappings_for(
+ "\
+5597483fc000-5597483fe000 r--p 00000000 00:31 4750073 /usr/bin/cat
+5597483fe000-559748402000 r-xp 00002000 00:31 4750073 /usr/bin/cat
+559748402000-559748404000 r--p 00006000 00:31 4750073 /usr/bin/cat
+559748404000-559748405000 r--p 00007000 00:31 4750073 /usr/bin/cat
+559748405000-559748406000 rw-p 00008000 00:31 4750073 /usr/bin/cat
+559749b0e000-559749b2f000 rw-p 00000000 00:00 0 [heap]
+7efd968d3000-7efd968f5000 rw-p 00000000 00:00 0 ",
+ 0x7ffc6e0f7000,
+ );
assert_eq!(mappings.len(), 3);
let cat_map = MappingInfo {
@@ -454,7 +418,7 @@ mod tests {
},
offset: 0,
executable: true,
- name: Some("/usr/bin/cat".to_string()),
+ name: Some("/usr/bin/cat".into()),
};
assert_eq!(mappings[0], cat_map);
@@ -468,7 +432,7 @@ mod tests {
},
offset: 0,
executable: false,
- name: Some("[heap]".to_string()),
+ name: Some("[heap]".into()),
};
assert_eq!(mappings[1], heap_map);
@@ -501,7 +465,7 @@ mod tests {
},
offset: 0,
executable: true,
- name: Some("linux-gate.so".to_string()),
+ name: Some("linux-gate.so".into()),
};
assert_eq!(mappings[21], gate_map);
@@ -511,35 +475,35 @@ mod tests {
fn test_reading_all() {
let mappings = get_all_mappings();
- let found_items = vec![
- Some("/usr/bin/cat".to_string()),
- Some("[heap]".to_string()),
+ let found_items: Vec