blob: 75f74d4dc00cf73d8217896a2c0cf1d2a8b675b4 [file] [log] [blame] [edit]
use gimli::{
BaseAddresses, CfaRule, Register, RegisterRule, UnwindContext, UnwindExpression, 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<O: gimli::ReaderOffset> gimli::UnwindContextStorage<O> for StoreOnStack {
type Rules = [(Register, RegisterRule<O>); next_value(MAX_REG_RULES)];
type Stack = [UnwindTableRow<O, Self>; 2];
}
#[cfg(feature = "dwarf-expr")]
impl<R: gimli::Reader> gimli::EvaluationStorage<R> for StoreOnStack {
type Stack = [Value; 64];
type ExpressionStack = [(R, R); 0];
type Result = [gimli::Piece<R>; 1];
}
#[derive(Debug)]
pub struct Frame {
fde_result: FDESearchResult,
row: UnwindTableRow<usize, StoreOnStack>,
}
impl Frame {
pub fn from_context(ctx: &Context, signal: bool) -> 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.
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: UnwindExpression<usize>,
) -> Result<usize, gimli::Error> {
let expr = expr.get(&self.fde_result.eh_frame).unwrap();
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: UnwindExpression<usize>,
) -> Result<usize, gimli::Error> {
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<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;
#[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<PersonalityRoutine> {
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()
}
}