use std::fmt;
use bitflags::bitflags;
use crate::error::{PageMapError, Result};
#[derive(Debug, Default, Clone, Copy)]
pub struct MemoryRegion {
pub(crate) start: u64,
pub(crate) end: u64,
}
impl MemoryRegion {
#[inline(always)]
pub fn start_address(&self) -> u64 {
self.start
}
#[inline(always)]
pub fn last_address(&self) -> u64 {
self.end - 1
}
#[inline(always)]
pub fn size(&self) -> u64 {
self.end - self.start
}
#[inline(always)]
pub fn contains(&self, addr: u64) -> bool {
addr >= self.start && addr < self.end
}
}
impl std::convert::From<(u64, u64)> for MemoryRegion {
fn from(r: (u64, u64)) -> Self {
debug_assert!(r.0 < r.1); MemoryRegion {
start: r.0,
end: r.1,
}
}
}
impl std::str::FromStr for MemoryRegion {
type Err = PageMapError;
fn from_str(s: &str) -> Result<Self> {
let r: Vec<_> = s
.splitn(2, '-')
.map(|addr| u64::from_str_radix(addr, 16).map_err(PageMapError::ParseMemoryRegion))
.collect::<Result<_>>()?;
Ok((r[0], r[1]).into())
}
}
impl fmt::Display for MemoryRegion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"0x{:016x}-0x{:016x} ({:5}K)",
self.start,
self.end,
self.size() / 1024,
)
}
}
bitflags! {
#[derive(Default)]
pub struct PagePermissions: u8 {
const READ = 1 << 0;
const WRITE = 1 << 1;
const EXECUTE = 1 << 2;
const SHARED = 1 << 3;
}
}
impl std::str::FromStr for PagePermissions {
type Err = PageMapError;
fn from_str(s: &str) -> Result<Self> {
if s.len() != 4 {
return Err(PageMapError::ParsePagePermissions(s.into()));
}
let mut ret: PagePermissions = Default::default();
for c in s.bytes() {
ret |= match c {
b'r' => Self::READ,
b'w' => Self::WRITE,
b'x' => Self::EXECUTE,
b's' => Self::SHARED,
b'p' | b'-' => Self::empty(),
_ => return Err(PageMapError::ParsePagePermissions(s.into())),
}
}
Ok(ret)
}
}
impl fmt::Display for PagePermissions {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut ret = "---p".to_owned();
if self.contains(Self::READ) {
ret.replace_range(0..1, "r");
}
if self.contains(Self::WRITE) {
ret.replace_range(1..2, "w");
}
if self.contains(Self::EXECUTE) {
ret.replace_range(2..3, "x");
}
if self.contains(Self::SHARED) {
ret.replace_range(3..4, "s");
}
write!(f, "{}", ret)
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct DeviceNumbers {
major: u16, minor: u32, }
impl DeviceNumbers {
#[inline(always)]
pub fn major(&self) -> u16 {
self.major
}
#[inline(always)]
pub fn minor(&self) -> u32 {
self.minor
}
}
impl std::convert::From<(u32, u32)> for DeviceNumbers {
fn from(p: (u32, u32)) -> Self {
debug_assert!(p.0 < 1 << 12); debug_assert!(p.1 < 1 << 20); DeviceNumbers {
major: p.0 as u16,
minor: p.1,
}
}
}
impl std::str::FromStr for DeviceNumbers {
type Err = PageMapError;
fn from_str(s: &str) -> Result<Self> {
let p: Vec<_> = s
.splitn(2, ':')
.map(|addr| {
u32::from_str_radix(addr, 16)
.map_err(|_| PageMapError::ParseDeviceNumbers(s.into()))
})
.collect::<Result<_>>()?;
Ok((p[0], p[1]).into())
}
}
impl fmt::Display for DeviceNumbers {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.major, self.minor)
}
}
#[derive(Debug, Default, Clone)]
pub struct MapsEntry {
pub(crate) region: MemoryRegion,
perms: PagePermissions,
offset: u64,
dev: DeviceNumbers,
inode: u64,
pathname: Option<String>,
}
impl MapsEntry {
#[inline(always)]
pub fn memory_region(&self) -> MemoryRegion {
self.region
}
#[inline(always)]
pub fn permissions(&self) -> PagePermissions {
self.perms
}
#[inline(always)]
pub fn offset(&self) -> u64 {
self.offset
}
#[inline(always)]
pub fn device_numbers(&self) -> DeviceNumbers {
self.dev
}
#[inline(always)]
pub fn inode(&self) -> u64 {
self.inode
}
pub fn path(&self) -> Option<&str> {
self.pathname.as_ref().map(|p| p.as_ref())
}
}
impl std::str::FromStr for MapsEntry {
type Err = PageMapError;
fn from_str(s: &str) -> Result<Self> {
let s: Vec<_> = s.split_ascii_whitespace().collect();
Ok(MapsEntry {
region: s[0].parse()?,
perms: s[1].parse()?,
offset: u64::from_str_radix(s[2], 16)?,
dev: s[3].parse()?,
inode: s[4].parse()?,
pathname: s.get(5).map(|&p| p.to_owned()),
})
}
}
impl fmt::Display for MapsEntry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{} {} {:20} {} {} {:?}",
self.region, self.perms, self.offset, self.dev, self.inode, self.pathname
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_perms() {
let perms = vec![
"---s", "---p", "r--s", "r--p", "rw-s", "-w-s", "-w-p", "--xs", "--xp", "rw-p", "r-xp",
];
for p in perms {
eprint!("{:#?} --> ", p);
let pp = p.parse::<PagePermissions>().unwrap();
eprintln!("{:?}", pp);
assert_eq!(format!("{}", pp), p);
}
}
#[test]
fn test_maps_entry() -> std::result::Result<(), PageMapError> {
let start: u64 = u64::from_str_radix("7f368bc85000", 16).unwrap();
eprintln!("start = {:#?}", start);
let maps_entries = vec![
"7f368bc85000-7f368bca7000 r--s 00000000 fe:00 400910 /usr/share/zsh/functions/Completion/Base.zwc",
"7f368bcaf000-7f368bcb3000 rw-p 00000000 00:00 0",
"7f368bcc2000-7f368bcc3000 ---p 0000f000 fe:00 13377416 /usr/lib/x86_64-linux-gnu/zsh/5.7.1/zsh/complist.so",
"7ffcec729000-7ffcec784000 rw-p 00000000 00:00 0 [stack]",
"7ffcec7d1000-7ffcec7d3000 r-xp 00000000 00:00 0 [vdso]",
];
for line in maps_entries {
eprintln!("line = {:#?}", line);
let entry = line.parse::<MapsEntry>()?;
eprintln!("entry = {:#?}", entry);
eprintln!("pretty entry = {}\n", entry);
}
Ok(())
}
}