use crate::{arch::Arch, unwind_result::UnwindResult}; use core::ops::Range; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum PeUnwinderError { MissingUnwindInfoData(u32), MissingInstructionData(u32), MissingStackData(Option), UnwindInfoParseError, Aarch64Unsupported, } impl core::fmt::Display for PeUnwinderError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::MissingUnwindInfoData(rva) => { write!(f, "failed to read unwind info memory at RVA {rva:x}") } Self::MissingInstructionData(rva) => { write!(f, "failed to read instruction memory at RVA {rva:x}") } Self::MissingStackData(addr) => { write!(f, "failed to read stack")?; if let Some(addr) = addr { write!(f, " at address {addr:x}")?; } Ok(()) } Self::UnwindInfoParseError => write!(f, "failed to parse UnwindInfo"), Self::Aarch64Unsupported => write!(f, "AArch64 is not yet supported"), } } } #[cfg(feature = "std")] impl std::error::Error for PeUnwinderError {} /// Data and the related RVA range within the binary. /// /// This is only used by PE unwinding. /// /// Type arguments: /// - `D`: The type for unwind section data. This allows carrying owned data on the /// module, e.g. `Vec`. But it could also be a wrapper around mapped memory from /// a file or a different process, for example. It just needs to provide a slice of /// bytes via its `Deref` implementation. pub struct DataAtRvaRange { pub data: D, pub rva_range: Range, } pub struct PeSections<'a, D> { pub pdata: &'a D, pub rdata: Option<&'a DataAtRvaRange>, pub xdata: Option<&'a DataAtRvaRange>, pub text: Option<&'a DataAtRvaRange>, } impl<'a, D> PeSections<'a, D> where D: core::ops::Deref, { pub fn unwind_info_memory_at_rva(&self, rva: u32) -> Result<&'a [u8], PeUnwinderError> { [&self.rdata, &self.xdata] .into_iter() .find_map(|o| o.and_then(|m| memory_at_rva(m, rva))) .ok_or(PeUnwinderError::MissingUnwindInfoData(rva)) } pub fn text_memory_at_rva(&self, rva: u32) -> Result<&'a [u8], PeUnwinderError> { self.text .and_then(|m| memory_at_rva(m, rva)) .ok_or(PeUnwinderError::MissingInstructionData(rva)) } } fn memory_at_rva>( DataAtRvaRange { data, rva_range }: &DataAtRvaRange, address: u32, ) -> Option<&[u8]> { if rva_range.contains(&address) { let offset = address - rva_range.start; Some(&data[(offset as usize)..]) } else { None } } pub trait PeUnwinding: Arch { fn unwind_frame( sections: PeSections, address: u32, regs: &mut Self::UnwindRegs, is_first_frame: bool, read_stack: &mut F, ) -> Result, PeUnwinderError> where F: FnMut(u64) -> Result, D: core::ops::Deref; }