소스 검색

Implement frame unwinder

Gary Guo 3 년 전
부모
커밋
dd94e27b1f
2개의 변경된 파일143개의 추가작업 그리고 0개의 파일을 삭제
  1. 142 0
      src/frame.rs
  2. 1 0
      src/lib.rs

+ 142 - 0
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<StaticSlice>,
+}
+
+impl Frame {
+    pub fn from_context(ctx: &Context) -> Result<Option<Self>, 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<StaticSlice>,
+    ) -> Result<usize, gimli::Error> {
+        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<Context, gimli::Error> {
+        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<usize> {
+        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 _
+    }
+}

+ 1 - 0
src/lib.rs

@@ -5,4 +5,5 @@
 
 mod arch;
 mod find_fde;
+mod frame;
 mod util;