Skip to main content

iptr_edge_analyzer/memory_reader/
libxdc.rs

1//! This module contains a memory reader that re-construct memory content
2//! from addr and dump files used in libxdc experiments.
3
4use 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
13/// Memory reader that re-construct memory content from addr and dump files
14/// used in libxdc experiments
15pub struct LibxdcMemoryReader {
16    pages: Mmap,
17    page_maps: Vec<(u64, usize)>,
18}
19
20/// Error type for [`LibxdcMemoryReader`], only used in
21/// [`LibxdcMemoryReader::new`].
22#[derive(Debug, Error)]
23pub enum LibxdcMemoryReaderCreateError {
24    /// Failed to open page dump file
25    #[error("Failed to open page dump file")]
26    InvalidPageDumpFile(#[source] std::io::Error),
27    /// Failed to open page addr file
28    #[error("Failed to open page addr file")]
29    InvalidPageAddrFile(#[source] std::io::Error),
30    /// Size of page address file is not consistent with page dump file
31    #[error("Size of page address file is not consistent with page dump file")]
32    InconsistentLength,
33}
34
35impl LibxdcMemoryReader {
36    /// Create a [`LibxdcMemoryReader`] from the page dump and page addr files.
37    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/// Error type for [`LibxdcMemoryReader`] in the
74/// implementation of [`ReadMemory`]
75#[derive(Debug, Error)]
76pub enum LibxdcMemoryReaderError {
77    /// The queried address is not included
78    #[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        // SAFETY: pos is generated by binary search, no possibility to out of bounds
109        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            // This includes cases where address - page_addr > PAGE_SIZE
116            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}