Skip to content

Commit

Permalink
Switch gimli dependency to philipc's branch.
Browse files Browse the repository at this point in the history
This is the branch for gimli-rs/gimli#703 .
  • Loading branch information
mstange committed Mar 13, 2024
1 parent 07ce0f4 commit bb30867
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 60 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repository = "https://github.com/mstange/framehop/"
exclude = ["/.github", "/.vscode", "/tests", "/fixtures", "/big-fixtures"]

[dependencies]
gimli = "0.28.1"
gimli = { git = "https://github.com/philipc/gimli", branch = "issue-701" }
object = { version = "0.32", optional = true }
thiserror = "1.0.56"
macho-unwind-info = "0.4.0"
Expand Down
30 changes: 15 additions & 15 deletions src/aarch64/dwarf.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use gimli::{
AArch64, CfaRule, Encoding, EvaluationStorage, Reader, Register, RegisterRule,
UnwindContextStorage, UnwindTableRow,
AArch64, CfaRule, Encoding, EvaluationStorage, Reader, ReaderOffset, Register, RegisterRule, UnwindContextStorage, UnwindSection, UnwindTableRow
};

use super::{arch::ArchAarch64, unwind_rule::UnwindRuleAarch64, unwindregs::UnwindRegsAarch64};
Expand All @@ -25,7 +24,8 @@ impl DwarfUnwindRegs for UnwindRegsAarch64 {

impl DwarfUnwinding for ArchAarch64 {
fn unwind_frame<F, R, S>(
unwind_info: &UnwindTableRow<R, S>,
section: &impl UnwindSection<R>,
unwind_info: &UnwindTableRow<R::Offset, S>,
encoding: Encoding,
regs: &mut Self::UnwindRegs,
is_first_frame: bool,
Expand All @@ -34,7 +34,7 @@ impl DwarfUnwinding for ArchAarch64 {
where
F: FnMut(u64) -> Result<u64, ()>,
R: Reader,
S: UnwindContextStorage<R> + EvaluationStorage<R>,
S: UnwindContextStorage<R::Offset> + EvaluationStorage<R>,
{
let cfa_rule = unwind_info.cfa();
let fp_rule = unwind_info.register(AArch64::X29);
Expand All @@ -48,7 +48,7 @@ impl DwarfUnwinding for ArchAarch64 {
}
}

let cfa = eval_cfa_rule::<R, _, S>(cfa_rule, encoding, regs)
let cfa = eval_cfa_rule::<R, _, S>(section, cfa_rule, encoding, regs)
.ok_or(DwarfUnwinderError::CouldNotRecoverCfa)?;

let lr = regs.lr();
Expand All @@ -59,18 +59,18 @@ impl DwarfUnwinding for ArchAarch64 {
if cfa <= sp {
return Err(DwarfUnwinderError::StackPointerMovedBackwards);
}
let fp = eval_register_rule::<R, F, _, S>(fp_rule, cfa, encoding, fp, regs, read_stack)
let fp = eval_register_rule::<R, F, _, S>(section, fp_rule, cfa, encoding, fp, regs, read_stack)
.ok_or(DwarfUnwinderError::CouldNotRecoverFramePointer)?;
let lr = eval_register_rule::<R, F, _, S>(lr_rule, cfa, encoding, lr, regs, read_stack)
let lr = eval_register_rule::<R, F, _, S>(section, lr_rule, cfa, encoding, lr, regs, read_stack)
.ok_or(DwarfUnwinderError::CouldNotRecoverReturnAddress)?;
(fp, lr)
} else {
// For the first frame, be more lenient when encountering errors.
// TODO: Find evidence of what this gives us. I think on macOS the prologue often has Unknown register rules
// and we only encounter prologues for the first frame.
let fp = eval_register_rule::<R, F, _, S>(fp_rule, cfa, encoding, fp, regs, read_stack)
let fp = eval_register_rule::<R, F, _, S>(section, fp_rule, cfa, encoding, fp, regs, read_stack)
.unwrap_or(fp);
let lr = eval_register_rule::<R, F, _, S>(lr_rule, cfa, encoding, lr, regs, read_stack)
let lr = eval_register_rule::<R, F, _, S>(section, lr_rule, cfa, encoding, lr, regs, read_stack)
.unwrap_or(lr);
(fp, lr)
};
Expand All @@ -87,8 +87,8 @@ impl DwarfUnwinding for ArchAarch64 {
}
}

fn register_rule_to_cfa_offset<R: gimli::Reader>(
rule: &RegisterRule<R>,
fn register_rule_to_cfa_offset<RO: ReaderOffset>(
rule: &RegisterRule<RO>,
) -> Result<Option<i64>, ConversionError> {
match *rule {
RegisterRule::Undefined | RegisterRule::SameValue => Ok(None),
Expand All @@ -97,10 +97,10 @@ fn register_rule_to_cfa_offset<R: gimli::Reader>(
}
}

fn translate_into_unwind_rule<R: gimli::Reader>(
cfa_rule: &CfaRule<R>,
fp_rule: &RegisterRule<R>,
lr_rule: &RegisterRule<R>,
fn translate_into_unwind_rule<RO: ReaderOffset>(
cfa_rule: &CfaRule<RO>,
fp_rule: &RegisterRule<RO>,
lr_rule: &RegisterRule<RO>,
) -> Result<UnwindRuleAarch64, ConversionError> {
match cfa_rule {
CfaRule::RegisterAndOffset { register, offset } => match *register {
Expand Down
6 changes: 3 additions & 3 deletions src/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ pub use crate::rule_cache::CacheStats;
/// A trait which lets you opt into allocation-free unwinding. The two implementations of
/// this trait are [`MustNotAllocateDuringUnwind`] and [`MayAllocateDuringUnwind`].
pub trait AllocationPolicy<D: Deref<Target = [u8]>> {
type GimliStorage: gimli::UnwindContextStorage<ArcDataReader<D>>
type GimliStorage: gimli::UnwindContextStorage<usize>
+ gimli::EvaluationStorage<ArcDataReader<D>>;
}

Expand All @@ -27,7 +27,7 @@ pub struct MustNotAllocateDuringUnwind;
#[doc(hidden)]
pub struct StoreOnStack;

impl<R: gimli::Reader> gimli::UnwindContextStorage<R> for StoreOnStack {
impl<R: gimli::ReaderOffset> gimli::UnwindContextStorage<R> for StoreOnStack {
type Rules = [(gimli::Register, gimli::RegisterRule<R>); 192];
type Stack = [gimli::UnwindTableRow<R, Self>; 4];
}
Expand Down Expand Up @@ -64,7 +64,7 @@ pub struct Cache<
R: UnwindRule,
P: AllocationPolicy<D> = MayAllocateDuringUnwind,
> {
pub(crate) gimli_unwind_context: Box<gimli::UnwindContext<ArcDataReader<D>, P::GimliStorage>>,
pub(crate) gimli_unwind_context: Box<gimli::UnwindContext<usize, P::GimliStorage>>,
pub(crate) rule_cache: RuleCache<R>,
}

Expand Down
82 changes: 60 additions & 22 deletions src/dwarf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ pub enum ConversionError {

pub trait DwarfUnwinding: Arch {
fn unwind_frame<F, R, S>(
unwind_info: &UnwindTableRow<R, S>,
section: &impl UnwindSection<R>,
unwind_info: &UnwindTableRow<R::Offset, S>,
encoding: Encoding,
regs: &mut Self::UnwindRegs,
is_first_frame: bool,
Expand All @@ -64,7 +65,7 @@ pub trait DwarfUnwinding: Arch {
where
F: FnMut(u64) -> Result<u64, ()>,
R: Reader,
S: UnwindContextStorage<R> + EvaluationStorage<R>;
S: UnwindContextStorage<R::Offset> + EvaluationStorage<R>;

fn rule_if_uncovered_by_fde() -> Self::UnwindRule;
}
Expand All @@ -74,24 +75,33 @@ pub enum UnwindSectionType {
DebugFrame,
}

pub struct DwarfUnwinder<'a, R: Reader, A: DwarfUnwinding + ?Sized, S: UnwindContextStorage<R>> {
pub struct DwarfUnwinder<
'a,
R: Reader,
A: DwarfUnwinding + ?Sized,
S: UnwindContextStorage<R::Offset>,
> {
unwind_section_data: R,
unwind_section_type: UnwindSectionType,
eh_frame_hdr: Option<ParsedEhFrameHdr<EndianSlice<'a, R::Endian>>>,
unwind_context: &'a mut UnwindContext<R, S>,
unwind_context: &'a mut UnwindContext<R::Offset, S>,
base_svma: u64,
bases: BaseAddresses,
_arch: PhantomData<A>,
}

impl<'a, R: Reader, A: DwarfUnwinding, S: UnwindContextStorage<R> + EvaluationStorage<R>>
DwarfUnwinder<'a, R, A, S>
impl<
'a,
R: Reader,
A: DwarfUnwinding,
S: UnwindContextStorage<R::Offset> + EvaluationStorage<R>,
> DwarfUnwinder<'a, R, A, S>
{
pub fn new(
unwind_section_data: R,
unwind_section_type: UnwindSectionType,
eh_frame_hdr_data: Option<&'a [u8]>,
unwind_context: &'a mut UnwindContext<R, S>,
unwind_context: &'a mut UnwindContext<R::Offset, S>,
bases: BaseAddresses,
base_svma: u64,
) -> Self {
Expand Down Expand Up @@ -138,31 +148,50 @@ impl<'a, R: Reader, A: DwarfUnwinding, S: UnwindContextStorage<R> + EvaluationSt
{
let lookup_svma = self.base_svma + rel_lookup_address as u64;
let unwind_section_data = self.unwind_section_data.clone();
let unwind_info = match self.unwind_section_type {
match self.unwind_section_type {
UnwindSectionType::EhFrame => {
let mut eh_frame = EhFrame::from(unwind_section_data);
eh_frame.set_address_size(8);
self.unwind_info_for_fde(eh_frame, lookup_svma, fde_offset)
let unwind_info = self.unwind_info_for_fde(&eh_frame, lookup_svma, fde_offset);
if let Err(DwarfUnwinderError::UnwindInfoForAddressFailed(_)) = unwind_info {
return Ok(UnwindResult::ExecRule(A::rule_if_uncovered_by_fde()));
}
let (unwind_info, encoding) = unwind_info?;
A::unwind_frame::<F, R, S>(
&eh_frame,
unwind_info,
encoding,
regs,
is_first_frame,
read_stack,
)
}
UnwindSectionType::DebugFrame => {
let mut debug_frame = DebugFrame::from(unwind_section_data);
debug_frame.set_address_size(8);
self.unwind_info_for_fde(debug_frame, lookup_svma, fde_offset)
let unwind_info = self.unwind_info_for_fde(&debug_frame, lookup_svma, fde_offset);
if let Err(DwarfUnwinderError::UnwindInfoForAddressFailed(_)) = unwind_info {
return Ok(UnwindResult::ExecRule(A::rule_if_uncovered_by_fde()));
}
let (unwind_info, encoding) = unwind_info?;
A::unwind_frame::<F, R, S>(
&debug_frame,
unwind_info,
encoding,
regs,
is_first_frame,
read_stack,
)
}
};
if let Err(DwarfUnwinderError::UnwindInfoForAddressFailed(_)) = unwind_info {
return Ok(UnwindResult::ExecRule(A::rule_if_uncovered_by_fde()));
}
let (unwind_info, encoding) = unwind_info?;
A::unwind_frame::<F, R, S>(unwind_info, encoding, regs, is_first_frame, read_stack)
}

fn unwind_info_for_fde<US: UnwindSection<R>>(
&mut self,
unwind_section: US,
unwind_section: &US,
lookup_svma: u64,
fde_offset: u32,
) -> Result<(&UnwindTableRow<R, S>, Encoding), DwarfUnwinderError> {
) -> Result<(&UnwindTableRow<R::Offset, S>, Encoding), DwarfUnwinderError> {
let fde = unwind_section.fde_from_offset(
&self.bases,
US::Offset::from(R::Offset::from_u32(fde_offset)),
Expand All @@ -172,7 +201,7 @@ impl<'a, R: Reader, A: DwarfUnwinding, S: UnwindContextStorage<R> + EvaluationSt
let encoding = fde.cie().encoding();
let unwind_info: &UnwindTableRow<_, _> = fde
.unwind_info_for_address(
&unwind_section,
unwind_section,
&self.bases,
self.unwind_context,
lookup_svma,
Expand Down Expand Up @@ -320,7 +349,8 @@ pub trait DwarfUnwindRegs {
}

pub fn eval_cfa_rule<R: Reader, UR: DwarfUnwindRegs, S: EvaluationStorage<R>>(
rule: &CfaRule<R>,
section: &impl UnwindSection<R>,
rule: &CfaRule<R::Offset>,
encoding: Encoding,
regs: &UR,
) -> Option<u64> {
Expand All @@ -329,7 +359,10 @@ pub fn eval_cfa_rule<R: Reader, UR: DwarfUnwindRegs, S: EvaluationStorage<R>>(
let val = regs.get(*register)?;
u64::try_from(i64::try_from(val).ok()?.checked_add(*offset)?).ok()
}
CfaRule::Expression(expr) => eval_expr::<R, UR, S>(expr.clone(), encoding, regs),
CfaRule::Expression(expr) => {
let expr = expr.get(section).ok()?;
eval_expr::<R, UR, S>(expr, encoding, regs)
}
}
}

Expand Down Expand Up @@ -359,7 +392,8 @@ fn eval_expr<R: Reader, UR: DwarfUnwindRegs, S: EvaluationStorage<R>>(
}

pub fn eval_register_rule<R, F, UR, S>(
rule: RegisterRule<R>,
section: &impl UnwindSection<R>,
rule: RegisterRule<R::Offset>,
cfa: u64,
encoding: Encoding,
val: u64,
Expand All @@ -385,10 +419,14 @@ where
}
RegisterRule::Register(register) => regs.get(register),
RegisterRule::Expression(expr) => {
let expr = expr.get(section).ok()?;
let val = eval_expr::<R, UR, S>(expr, encoding, regs)?;
read_stack(val).ok()
}
RegisterRule::ValExpression(expr) => eval_expr::<R, UR, S>(expr, encoding, regs),
RegisterRule::ValExpression(expr) => {
let expr = expr.get(section).ok()?;
eval_expr::<R, UR, S>(expr, encoding, regs)
}
RegisterRule::Architectural => {
// Unimplemented
// TODO: Find out what the architectural rules for x86_64 and for aarch64 are, if any.
Expand Down
42 changes: 23 additions & 19 deletions src/x86_64/dwarf.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use gimli::{
CfaRule, Encoding, EvaluationStorage, Reader, Register, RegisterRule, UnwindContextStorage,
UnwindTableRow, X86_64,
CfaRule, Encoding, EvaluationStorage, Reader, ReaderOffset, Register, RegisterRule,
UnwindContextStorage, UnwindSection, UnwindTableRow, X86_64,
};

use super::{arch::ArchX86_64, unwind_rule::UnwindRuleX86_64, unwindregs::UnwindRegsX86_64};
Expand All @@ -23,7 +23,8 @@ impl DwarfUnwindRegs for UnwindRegsX86_64 {

impl DwarfUnwinding for ArchX86_64 {
fn unwind_frame<F, R, S>(
unwind_info: &UnwindTableRow<R, S>,
section: &impl UnwindSection<R>,
unwind_info: &UnwindTableRow<R::Offset, S>,
encoding: Encoding,
regs: &mut Self::UnwindRegs,
is_first_frame: bool,
Expand All @@ -32,7 +33,7 @@ impl DwarfUnwinding for ArchX86_64 {
where
F: FnMut(u64) -> Result<u64, ()>,
R: Reader,
S: UnwindContextStorage<R> + EvaluationStorage<R>,
S: UnwindContextStorage<R::Offset> + EvaluationStorage<R>,
{
let cfa_rule = unwind_info.cfa();
let bp_rule = unwind_info.register(X86_64::RBP);
Expand All @@ -46,22 +47,25 @@ impl DwarfUnwinding for ArchX86_64 {
}
}

let cfa = eval_cfa_rule::<R, _, S>(cfa_rule, encoding, regs)
let cfa = eval_cfa_rule::<R, _, S>(section, cfa_rule, encoding, regs)
.ok_or(DwarfUnwinderError::CouldNotRecoverCfa)?;

let ip = regs.ip();
let bp = regs.bp();
let sp = regs.sp();

let new_bp = eval_register_rule::<R, F, _, S>(bp_rule, cfa, encoding, bp, regs, read_stack)
.unwrap_or(bp);
let new_bp =
eval_register_rule::<R, F, _, S>(section, bp_rule, cfa, encoding, bp, regs, read_stack)
.unwrap_or(bp);

let return_address =
match eval_register_rule::<R, F, _, S>(ra_rule, cfa, encoding, ip, regs, read_stack) {
Some(ra) => ra,
None => read_stack(cfa - 8)
.map_err(|_| DwarfUnwinderError::CouldNotRecoverReturnAddress)?,
};
let return_address = match eval_register_rule::<R, F, _, S>(
section, ra_rule, cfa, encoding, ip, regs, read_stack,
) {
Some(ra) => ra,
None => {
read_stack(cfa - 8).map_err(|_| DwarfUnwinderError::CouldNotRecoverReturnAddress)?
}
};

if cfa == sp && return_address == ip {
return Err(DwarfUnwinderError::DidNotAdvance);
Expand All @@ -82,8 +86,8 @@ impl DwarfUnwinding for ArchX86_64 {
}
}

fn register_rule_to_cfa_offset<R: gimli::Reader>(
rule: &RegisterRule<R>,
fn register_rule_to_cfa_offset<RO: ReaderOffset>(
rule: &RegisterRule<RO>,
) -> Result<Option<i64>, ConversionError> {
match *rule {
RegisterRule::Undefined | RegisterRule::SameValue => Ok(None),
Expand All @@ -92,10 +96,10 @@ fn register_rule_to_cfa_offset<R: gimli::Reader>(
}
}

fn translate_into_unwind_rule<R: gimli::Reader>(
cfa_rule: &CfaRule<R>,
bp_rule: &RegisterRule<R>,
ra_rule: &RegisterRule<R>,
fn translate_into_unwind_rule<RO: ReaderOffset>(
cfa_rule: &CfaRule<RO>,
bp_rule: &RegisterRule<RO>,
ra_rule: &RegisterRule<RO>,
) -> Result<UnwindRuleX86_64, ConversionError> {
match ra_rule {
RegisterRule::Undefined => {
Expand Down

0 comments on commit bb30867

Please sign in to comment.