#![feature(new_uninit)]
#[cfg(not(target_os = "linux"))]
compile_error!("libprocmem only supported for Linux targets");
use std::fs::File;
use std::io::{Seek, SeekFrom, Read};
use std::mem::MaybeUninit;
pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
OpenMem(usize, std::io::Error),
ReadMem(usize, std::io::Error),
ReadMaps(usize, std::io::Error),
InvalidString(usize, std::string::FromUtf8Error),
ParseMaps(usize),
}
impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Error::OpenMem(pid, e) =>
write!(f, "Failed to open memory for pid {pid}: {e}")?,
Error::ReadMem(pid, e) =>
write!(f, "Failed to read memory for pid {pid}: {e}")?,
Error::ReadMaps(pid, e) =>
write!(f, "Failed to read maps for pid {pid}: {e}")?,
Error::InvalidString(pid, e) =>
write!(f, "Failed to convert bytes to UTF-8 data \
for pid {pid}: {e}")?,
Error::ParseMaps(pid) =>
write!(f, "Failed to parse maps for pid {pid}")?,
}
Ok(())
}
}
pub struct Memory {
pid: usize,
mem: File,
}
pub struct Mapping {
pub base: u64,
pub end: u64,
pub r: bool,
pub w: bool,
pub x: bool,
}
impl Memory {
pub fn pid(pid: usize) -> Result<Self> {
let mem = File::open(format!("/proc/{}/mem", pid))
.map_err(|x| Error::OpenMem(pid, x))?;
Ok(Self {
pid,
mem,
})
}
pub fn read_nul_string(&mut self, addr: u64) -> Result<String> {
let mut bytes = Vec::new();
for addr in addr.. {
let byte = self.read::<u8>(addr)?;
if byte == 0 {
break;
}
bytes.push(byte);
}
Ok(String::from_utf8(bytes).map_err(|x| {
Error::InvalidString(self.pid, x)
})?)
}
pub fn query_address_space(&mut self) -> Result<Vec<Mapping>> {
let mut ret = Vec::new();
let maps = std::fs::read_to_string(
format!("/proc/{}/maps", self.pid))
.map_err(|x| Error::ReadMaps(self.pid, x))?;
for line in maps.lines() {
let mut spl = line.split(" ");
let addr_range = spl.next()
.ok_or(Error::ParseMaps(self.pid))?;
let perms = spl.next()
.ok_or(Error::ParseMaps(self.pid))?;
let mut spl = addr_range.split("-");
let base = u64::from_str_radix(spl.next()
.ok_or(Error::ParseMaps(self.pid))?, 16)
.map_err(|_| Error::ParseMaps(self.pid))?;
let end = u64::from_str_radix(spl.next()
.ok_or(Error::ParseMaps(self.pid))?, 16)
.map_err(|_| Error::ParseMaps(self.pid))?;
let r = perms.get(0..1) == Some("r");
let w = perms.get(1..2) == Some("w");
let x = perms.get(2..3) == Some("x");
ret.push(Mapping { base, end, r, w, x });
}
Ok(ret)
}
pub fn read<T: Pod>(&mut self, addr: u64) -> Result<T> {
let mut ret: MaybeUninit<T> = MaybeUninit::uninit();
self.mem.seek(SeekFrom::Start(addr))
.map_err(|x| Error::ReadMem(self.pid, x))?;
let ptr = unsafe {
core::slice::from_raw_parts_mut(
ret.as_mut_ptr() as *mut u8, core::mem::size_of_val(&ret))
};
self.mem.read_exact(ptr)
.map_err(|x| Error::ReadMem(self.pid, x))?;
Ok(unsafe { ret.assume_init() })
}
pub fn read_slice<T: Pod>(&mut self, addr: u64, elements: usize)
-> Result<Box<[T]>> {
let mut ret: Box<[MaybeUninit<T>]> = Box::new_uninit_slice(elements);
self.mem.seek(SeekFrom::Start(addr))
.map_err(|x| Error::ReadMem(self.pid, x))?;
let ptr = unsafe {
core::slice::from_raw_parts_mut(
ret.as_mut_ptr() as *mut u8, core::mem::size_of_val(&*ret))
};
self.mem.read_exact(ptr)
.map_err(|x| Error::ReadMem(self.pid, x))?;
Ok(unsafe { ret.assume_init() })
}
}
pub unsafe trait Pod: Copy + Sync + Send + 'static {}
unsafe impl Pod for i8 {}
unsafe impl Pod for i16 {}
unsafe impl Pod for i32 {}
unsafe impl Pod for i64 {}
unsafe impl Pod for i128 {}
unsafe impl Pod for isize {}
unsafe impl Pod for u8 {}
unsafe impl Pod for u16 {}
unsafe impl Pod for u32 {}
unsafe impl Pod for u64 {}
unsafe impl Pod for u128 {}
unsafe impl Pod for usize {}
unsafe impl Pod for f32 {}
unsafe impl Pod for f64 {}
unsafe impl<const N: usize> Pod for [i8; N] {}
unsafe impl<const N: usize> Pod for [i16; N] {}
unsafe impl<const N: usize> Pod for [i32; N] {}
unsafe impl<const N: usize> Pod for [i64; N] {}
unsafe impl<const N: usize> Pod for [i128; N] {}
unsafe impl<const N: usize> Pod for [isize; N] {}
unsafe impl<const N: usize> Pod for [u8; N] {}
unsafe impl<const N: usize> Pod for [u16; N] {}
unsafe impl<const N: usize> Pod for [u32; N] {}
unsafe impl<const N: usize> Pod for [u64; N] {}
unsafe impl<const N: usize> Pod for [u128; N] {}
unsafe impl<const N: usize> Pod for [usize; N] {}
unsafe impl<const N: usize> Pod for [f32; N] {}
unsafe impl<const N: usize> Pod for [f64; N] {}