From dd94e27b1f6c7194ce0458fb22d7c989572d5586 Mon Sep 17 00:00:00 2001 From: Gary Guo Date: Wed, 25 Aug 2021 21:37:41 +0100 Subject: [PATCH] Implement frame unwinder --- src/frame.rs | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 143 insertions(+) create mode 100644 src/frame.rs diff --git a/src/frame.rs b/src/frame.rs new file mode 100644 index 0000000..40e1473 --- /dev/null +++ b/src/frame.rs @@ -0,0 +1,142 @@ +use gimli::{ + BaseAddresses, CfaRule, EvaluationResult, Expression, Location, RegisterRule, + UninitializedUnwindContext, UnwindTableRow, Value, +}; + +use crate::arch::*; +use crate::find_fde::{self, FDEFinder, FDESearchResult}; +use crate::util::*; + +#[derive(Debug)] +pub struct Frame { + fde_result: FDESearchResult, + row: UnwindTableRow, +} + +impl Frame { + pub fn from_context(ctx: &Context) -> 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. + ra -= 1; + + let fde_result = match find_fde::get_finder().find_fde(ra as _) { + Some(v) => v, + None => return Ok(None), + }; + let mut unwinder = UninitializedUnwindContext::new(); + 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 })) + } + + fn evaluate_expression( + &self, + ctx: &Context, + expr: Expression, + ) -> Result { + let mut eval = expr.evaluation(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 + .result() + .pop() + .ok_or(gimli::Error::PopWithEmptyStack)? + .location + { + Location::Address { address } => address as usize, + _ => unreachable!(), + }, + ) + } + + 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; + + 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!(), + }; + 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) }) + } + + 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 _ + } +} diff --git a/src/lib.rs b/src/lib.rs index e793755..b4d4125 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,4 +5,5 @@ mod arch; mod find_fde; +mod frame; mod util;