iptr_edge_analyzer/memory_reader/
libxdc.rs1use std::{fs::File, io::Read, path::Path};
5
6use super::ReadMemory;
7use memmap2::Mmap;
8use thiserror::Error;
9
10const PAGE_CACHE_ADDR_LINE_SIZE: u64 = 8;
11const PAGE_SIZE: usize = 0x1000;
12
13pub struct LibxdcMemoryReader {
16 pages: Mmap,
17 page_maps: Vec<(u64, usize)>,
18}
19
20#[derive(Debug, Error)]
23pub enum LibxdcMemoryReaderCreateError {
24 #[error("Failed to open page dump file")]
26 InvalidPageDumpFile(#[source] std::io::Error),
27 #[error("Failed to open page addr file")]
29 InvalidPageAddrFile(#[source] std::io::Error),
30 #[error("Size of page address file is not consistent with page dump file")]
32 InconsistentLength,
33}
34
35impl LibxdcMemoryReader {
36 pub fn new(page_dump: &Path, page_addr: &Path) -> Result<Self, LibxdcMemoryReaderCreateError> {
38 let page_dump_file =
39 File::open(page_dump).map_err(LibxdcMemoryReaderCreateError::InvalidPageDumpFile)?;
40 let mut page_addr_file =
41 File::open(page_addr).map_err(LibxdcMemoryReaderCreateError::InvalidPageAddrFile)?;
42 let page_addr_file_len = page_addr_file
43 .metadata()
44 .map_err(LibxdcMemoryReaderCreateError::InvalidPageAddrFile)?
45 .len();
46 let num_pages = page_addr_file_len / PAGE_CACHE_ADDR_LINE_SIZE;
47 let page_dump_file_len = page_dump_file
48 .metadata()
49 .map_err(LibxdcMemoryReaderCreateError::InvalidPageDumpFile)?
50 .len();
51 if num_pages * PAGE_SIZE as u64 != page_dump_file_len {
52 return Err(LibxdcMemoryReaderCreateError::InconsistentLength);
53 }
54
55 let mut page_maps = Vec::with_capacity(num_pages as usize);
56 let pages = unsafe {
57 Mmap::map(&page_dump_file)
58 .map_err(LibxdcMemoryReaderCreateError::InvalidPageDumpFile)?
59 };
60 let mut addr_buf = [0u8; 8];
61 let mut offset = 0;
62 while page_addr_file.read_exact(&mut addr_buf).is_ok() {
63 let addr = u64::from_le_bytes(addr_buf);
64 page_maps.push((addr & 0xFFFF_FFFF_FFFF_F000, offset));
65 offset += PAGE_SIZE;
66 }
67 page_maps.sort_by_key(|(addr, _)| *addr);
68
69 Ok(Self { pages, page_maps })
70 }
71}
72
73#[derive(Debug, Error)]
76pub enum LibxdcMemoryReaderError {
77 #[error("Queried area {0:#x} is not included in page.addr file")]
79 NotIncluded(u64),
80}
81
82impl ReadMemory for LibxdcMemoryReader {
83 type Error = LibxdcMemoryReaderError;
84
85 fn at_decode_begin(&mut self) -> std::result::Result<(), Self::Error> {
86 Ok(())
87 }
88
89 #[expect(clippy::cast_possible_truncation)]
90 fn read_memory<T>(
91 &mut self,
92 address: u64,
93 size: usize,
94 callback: impl FnOnce(&[u8]) -> T,
95 ) -> std::result::Result<T, Self::Error> {
96 let pos = match self
97 .page_maps
98 .binary_search_by_key(&address, |(addr, _)| *addr)
99 {
100 Ok(pos) => pos,
101 Err(pos) => {
102 if pos == 0 {
103 return Err(LibxdcMemoryReaderError::NotIncluded(address));
104 }
105 pos - 1
106 }
107 };
108 debug_assert!(pos < self.page_maps.len(), "Unexpected pos out of bounds!");
110 let (page_addr, offset) = unsafe { self.page_maps.get_unchecked(pos) };
111 let page_content_start = *offset;
112 let start_offset = address - page_addr;
113 let read_size = std::cmp::min(size, PAGE_SIZE.saturating_sub(start_offset as usize));
114 if read_size == 0 {
115 return Err(LibxdcMemoryReaderError::NotIncluded(address));
117 }
118 let content_start = page_content_start + start_offset as usize;
119 let Some(mem) = self
120 .pages
121 .get(content_start..(content_start.saturating_add(read_size)))
122 else {
123 return Err(LibxdcMemoryReaderError::NotIncluded(
124 address.saturating_add(read_size as u64) - 1,
125 ));
126 };
127 Ok(callback(mem))
128 }
129}