use gimli::{ BaseAddresses, CfaRule, Expression, Register, RegisterRule, UnwindContext, UnwindTableRow, }; #[cfg(feature = "dwarf-expr")] use gimli::{Evaluation, EvaluationResult, Location, Value}; use super::arch::*; use super::find_fde::{self, FDEFinder, FDESearchResult}; use crate::abi::PersonalityRoutine; use crate::arch::*; use crate::util::*; struct StoreOnStack; // gimli's MSRV doesn't allow const generics, so we need to pick a supported array size. const fn next_value(x: usize) -> usize { let supported = [0, 1, 2, 3, 4, 8, 16, 32, 64, 128]; let mut i = 0; while i < supported.len() { if supported[i] >= x { return supported[i]; } i += 1; } 192 } impl gimli::UnwindContextStorage for StoreOnStack { type Rules = [(Register, RegisterRule); next_value(MAX_REG_RULES)]; type Stack = [UnwindTableRow; 2]; } #[cfg(feature = "dwarf-expr")] impl gimli::EvaluationStorage for StoreOnStack { type Stack = [Value; 64]; type ExpressionStack = [(R, R); 0]; type Result = [gimli::Piece; 1]; } #[derive(Debug)] pub struct Frame { fde_result: FDESearchResult, row: UnwindTableRow, } impl Frame { pub fn from_context(ctx: &Context, signal: bool) -> Result, gimli::Error> { let mut ra = ctx[Arch::RA]; // Reached end of stack if ra == 0 { return Ok(None); } // RA points to the *next* instruction, so move it back 1 byte for the call instruction. if !signal { ra -= 1; } let fde_result = match find_fde::get_finder().find_fde(ra as _) { Some(v) => v, None => return Ok(None), }; let mut unwinder = UnwindContext::<_, StoreOnStack>::new_in(); let row = fde_result .fde .unwind_info_for_address( &fde_result.eh_frame, &fde_result.bases, &mut unwinder, ra as _, )? .clone(); Ok(Some(Self { fde_result, row })) } #[cfg(feature = "dwarf-expr")] fn evaluate_expression( &self, ctx: &Context, expr: Expression, ) -> Result { let mut eval = Evaluation::<_, StoreOnStack>::new_in(expr.0, self.fde_result.fde.cie().encoding()); let mut result = eval.evaluate()?; loop { match result { EvaluationResult::Complete => break, EvaluationResult::RequiresMemory { address, .. } => { let value = unsafe { (address as usize as *const usize).read_unaligned() }; result = eval.resume_with_memory(Value::Generic(value as _))?; } EvaluationResult::RequiresRegister { register, .. } => { let value = ctx[register]; result = eval.resume_with_register(Value::Generic(value as _))?; } EvaluationResult::RequiresRelocatedAddress(address) => { let value = unsafe { (address as usize as *const usize).read_unaligned() }; result = eval.resume_with_memory(Value::Generic(value as _))?; } _ => unreachable!(), } } Ok( match eval .as_result() .last() .ok_or(gimli::Error::PopWithEmptyStack)? .location { Location::Address { address } => address as usize, _ => unreachable!(), }, ) } #[cfg(not(feature = "dwarf-expr"))] fn evaluate_expression( &self, _ctx: &Context, _expr: Expression, ) -> Result { Err(gimli::Error::UnsupportedEvaluation) } pub fn adjust_stack_for_args(&self, ctx: &mut Context) { let size = self.row.saved_args_size(); ctx[Arch::SP] = ctx[Arch::SP].wrapping_add(size as usize); } pub fn unwind(&self, ctx: &Context) -> Result { let row = &self.row; let mut new_ctx = ctx.clone(); let cfa = match *row.cfa() { CfaRule::RegisterAndOffset { register, offset } => { ctx[register].wrapping_add(offset as usize) } CfaRule::Expression(expr) => self.evaluate_expression(ctx, expr)?, }; new_ctx[Arch::SP] = cfa as _; new_ctx[Arch::RA] = 0; #[warn(non_exhaustive_omitted_patterns)] for (reg, rule) in row.registers() { let value = match *rule { RegisterRule::Undefined | RegisterRule::SameValue => ctx[*reg], RegisterRule::Offset(offset) => unsafe { *((cfa.wrapping_add(offset as usize)) as *const usize) }, RegisterRule::ValOffset(offset) => cfa.wrapping_add(offset as usize), RegisterRule::Register(r) => ctx[r], RegisterRule::Expression(expr) => { let addr = self.evaluate_expression(ctx, expr)?; unsafe { *(addr as *const usize) } } RegisterRule::ValExpression(expr) => self.evaluate_expression(ctx, expr)?, RegisterRule::Architectural => unreachable!(), RegisterRule::Constant(value) => value as usize, _ => unreachable!(), }; new_ctx[*reg] = value; } Ok(new_ctx) } pub fn bases(&self) -> &BaseAddresses { &self.fde_result.bases } pub fn personality(&self) -> Option { self.fde_result .fde .personality() .map(|x| unsafe { deref_pointer(x) }) .map(|x| unsafe { core::mem::transmute(x) }) } pub fn lsda(&self) -> usize { self.fde_result .fde .lsda() .map(|x| unsafe { deref_pointer(x) }) .unwrap_or(0) } pub fn initial_address(&self) -> usize { self.fde_result.fde.initial_address() as _ } pub fn is_signal_trampoline(&self) -> bool { self.fde_result.fde.is_signal_trampoline() } }