Skip to content

Commit

Permalink
Use EndianSlice, remove ArcData.
Browse files Browse the repository at this point in the history
The new gimli version stores offsets instead of readers
in the evaluation cache, which means that our readers
no longer have to take Arcs to the module data.
  • Loading branch information
mstange committed Apr 11, 2024
1 parent 4bd7ecb commit b1969dc
Show file tree
Hide file tree
Showing 12 changed files with 151 additions and 168 deletions.
19 changes: 12 additions & 7 deletions src/aarch64/cache.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
use core::ops::Deref;

use super::unwind_rule::*;
use crate::cache::*;

/// The unwinder cache type for [`UnwinderAarch64`](super::UnwinderAarch64).
pub struct CacheAarch64<D: Deref<Target = [u8]>, P: AllocationPolicy<D> = MayAllocateDuringUnwind>(
pub Cache<D, UnwindRuleAarch64, P>,
pub struct CacheAarch64<P: AllocationPolicy = MayAllocateDuringUnwind>(
pub Cache<UnwindRuleAarch64, P>,
);

impl<D: Deref<Target = [u8]>, P: AllocationPolicy<D>> CacheAarch64<D, P> {
impl CacheAarch64<MayAllocateDuringUnwind> {
/// Create a new cache.
pub fn new() -> Self {
Self(Cache::new())
}
}

impl<P: AllocationPolicy> CacheAarch64<P> {
/// Create a new cache.
pub fn new_in() -> Self {
Self(Cache::new())
}

/// Returns a snapshot of the cache usage statistics.
pub fn stats(&self) -> CacheStats {
self.0.rule_cache.stats()
}
}

impl<D: Deref<Target = [u8]>, P: AllocationPolicy<D>> Default for CacheAarch64<D, P> {
impl<P: AllocationPolicy> Default for CacheAarch64<P> {
fn default() -> Self {
Self::new()
Self::new_in()
}
}
36 changes: 23 additions & 13 deletions src/aarch64/dwarf.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use gimli::{
AArch64, CfaRule, Encoding, EvaluationStorage, Reader, ReaderOffset, Register, RegisterRule, UnwindContextStorage, UnwindSection, UnwindTableRow
AArch64, CfaRule, Encoding, EvaluationStorage, Reader, ReaderOffset, Register, RegisterRule,
UnwindContextStorage, UnwindSection, UnwindTableRow,
};

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

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

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

let lr = regs.lr();
Expand All @@ -59,19 +61,27 @@ impl DwarfUnwinding for ArchAarch64 {
if cfa <= sp {
return Err(DwarfUnwinderError::StackPointerMovedBackwards);
}
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>(section, lr_rule, cfa, encoding, lr, regs, read_stack)
.ok_or(DwarfUnwinderError::CouldNotRecoverReturnAddress)?;
let fp = eval_register_rule::<R, F, _, ES>(
section, fp_rule, cfa, encoding, fp, regs, read_stack,
)
.ok_or(DwarfUnwinderError::CouldNotRecoverFramePointer)?;
let lr = eval_register_rule::<R, F, _, ES>(
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>(section, fp_rule, cfa, encoding, fp, regs, read_stack)
.unwrap_or(fp);
let lr = eval_register_rule::<R, F, _, S>(section, lr_rule, cfa, encoding, lr, regs, read_stack)
.unwrap_or(lr);
let fp = eval_register_rule::<R, F, _, ES>(
section, fp_rule, cfa, encoding, fp, regs, read_stack,
)
.unwrap_or(fp);
let lr = eval_register_rule::<R, F, _, ES>(
section, lr_rule, cfa, encoding, lr, regs, read_stack,
)
.unwrap_or(lr);
(fp, lr)
};

Expand Down
17 changes: 8 additions & 9 deletions src/aarch64/unwinder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,26 @@ use super::{ArchAarch64, CacheAarch64, UnwindRegsAarch64};
///
/// - `D`: The type for unwind section data in the modules. See [`Module`].
/// - `P`: The [`AllocationPolicy`].
pub struct UnwinderAarch64<
D: Deref<Target = [u8]>,
P: AllocationPolicy<D> = MayAllocateDuringUnwind,
>(UnwinderInternal<D, ArchAarch64, P>);
pub struct UnwinderAarch64<D: Deref<Target = [u8]>, P: AllocationPolicy = MayAllocateDuringUnwind>(
UnwinderInternal<D, ArchAarch64, P>,
);

impl<D: Deref<Target = [u8]>, P: AllocationPolicy<D>> Default for UnwinderAarch64<D, P> {
impl<D: Deref<Target = [u8]>, P: AllocationPolicy> Default for UnwinderAarch64<D, P> {
fn default() -> Self {
Self::new()
}
}

impl<D: Deref<Target = [u8]>, P: AllocationPolicy<D>> UnwinderAarch64<D, P> {
impl<D: Deref<Target = [u8]>, P: AllocationPolicy> UnwinderAarch64<D, P> {
/// Create an unwinder for a process.
pub fn new() -> Self {
Self(UnwinderInternal::new())
}
}

impl<D: Deref<Target = [u8]>, P: AllocationPolicy<D>> Unwinder for UnwinderAarch64<D, P> {
impl<D: Deref<Target = [u8]>, P: AllocationPolicy> Unwinder for UnwinderAarch64<D, P> {
type UnwindRegs = UnwindRegsAarch64;
type Cache = CacheAarch64<D, P>;
type Cache = CacheAarch64<P>;
type Module = Module<D>;

fn add_module(&mut self, module: Module<D>) {
Expand All @@ -52,7 +51,7 @@ impl<D: Deref<Target = [u8]>, P: AllocationPolicy<D>> Unwinder for UnwinderAarch
&self,
address: FrameAddress,
regs: &mut UnwindRegsAarch64,
cache: &mut CacheAarch64<D, P>,
cache: &mut CacheAarch64<P>,
read_stack: &mut F,
) -> Result<Option<u64>, Error>
where
Expand Down
30 changes: 0 additions & 30 deletions src/arcdata.rs

This file was deleted.

33 changes: 14 additions & 19 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
use core::ops::Deref;

use alloc::boxed::Box;

use crate::{rule_cache::RuleCache, unwind_rule::UnwindRule};

use super::arcdata::ArcDataReader;

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<usize>
+ gimli::EvaluationStorage<ArcDataReader<D>>;
pub trait AllocationPolicy {
type GimliUnwindContextStorage<R: gimli::ReaderOffset>: gimli::UnwindContextStorage<R>;
type GimliEvaluationStorage<R: gimli::Reader>: gimli::EvaluationStorage<R>;
}

/// Require allocation-free unwinding. This is one of the two [`AllocationPolicy`]
Expand Down Expand Up @@ -40,8 +36,9 @@ impl<R: gimli::Reader> gimli::EvaluationStorage<R> for StoreOnStack {
type Result = [gimli::Piece<R>; 1];
}

impl<D: Deref<Target = [u8]>> AllocationPolicy<D> for MustNotAllocateDuringUnwind {
type GimliStorage = StoreOnStack;
impl AllocationPolicy for MustNotAllocateDuringUnwind {
type GimliUnwindContextStorage<R: gimli::ReaderOffset> = StoreOnStack;
type GimliEvaluationStorage<R: gimli::Reader> = StoreOnStack;
}

/// Allow allocation during unwinding. This is one of the two [`AllocationPolicy`]
Expand All @@ -50,8 +47,9 @@ impl<D: Deref<Target = [u8]>> AllocationPolicy<D> for MustNotAllocateDuringUnwin
/// This is the preferred policy because it saves memory and places no limitations on
/// DWARF CFI evaluation.
pub struct MayAllocateDuringUnwind;
impl<D: Deref<Target = [u8]>> AllocationPolicy<D> for MayAllocateDuringUnwind {
type GimliStorage = gimli::StoreOnHeap;
impl AllocationPolicy for MayAllocateDuringUnwind {
type GimliUnwindContextStorage<R: gimli::ReaderOffset> = gimli::StoreOnHeap;
type GimliEvaluationStorage<R: gimli::Reader> = gimli::StoreOnHeap;
}

/// The unwinder cache. This needs to be created upfront before unwinding. During
Expand All @@ -61,16 +59,13 @@ impl<D: Deref<Target = [u8]>> AllocationPolicy<D> for MayAllocateDuringUnwind {
///
/// The cache stores unwind rules for addresses it has seen before, and it stores the
/// unwind context which gimli needs for DWARF CFI evaluation.
pub struct Cache<
D: Deref<Target = [u8]>,
R: UnwindRule,
P: AllocationPolicy<D> = MayAllocateDuringUnwind,
> {
pub(crate) gimli_unwind_context: Box<gimli::UnwindContext<usize, P::GimliStorage>>,
pub struct Cache<R: UnwindRule, P: AllocationPolicy = MayAllocateDuringUnwind> {
pub(crate) gimli_unwind_context:
Box<gimli::UnwindContext<usize, P::GimliUnwindContextStorage<usize>>>,
pub(crate) rule_cache: RuleCache<R>,
}

impl<D: Deref<Target = [u8]>, R: UnwindRule, P: AllocationPolicy<D>> Cache<D, R, P> {
impl<R: UnwindRule, P: AllocationPolicy> Cache<R, P> {
pub fn new() -> Self {
Self {
gimli_unwind_context: Box::new(gimli::UnwindContext::new_in()),
Expand All @@ -79,7 +74,7 @@ impl<D: Deref<Target = [u8]>, R: UnwindRule, P: AllocationPolicy<D>> Cache<D, R,
}
}

impl<D: Deref<Target = [u8]>, R: UnwindRule, P: AllocationPolicy<D>> Default for Cache<D, R, P> {
impl<R: UnwindRule, P: AllocationPolicy> Default for Cache<R, P> {
fn default() -> Self {
Self::new()
}
Expand Down
30 changes: 14 additions & 16 deletions src/dwarf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ pub enum ConversionError {
}

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

fn rule_if_uncovered_by_fde() -> Self::UnwindRule;
}
Expand All @@ -82,29 +83,25 @@ pub struct DwarfUnwinder<
'a,
R: Reader,
A: DwarfUnwinding + ?Sized,
S: UnwindContextStorage<R::Offset>,
UCS: 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::Offset, S>,
unwind_context: &'a mut UnwindContext<R::Offset, UCS>,
base_svma: u64,
bases: BaseAddresses,
_arch: PhantomData<A>,
}

impl<
'a,
R: Reader,
A: DwarfUnwinding,
S: UnwindContextStorage<R::Offset> + EvaluationStorage<R>,
> DwarfUnwinder<'a, R, A, S>
impl<'a, R: Reader, A: DwarfUnwinding, UCS: UnwindContextStorage<R::Offset>>
DwarfUnwinder<'a, R, A, UCS>
{
pub fn new(
unwind_section_data: R,
unwind_section_type: UnwindSectionType,
eh_frame_hdr_data: Option<&'a [u8]>,
unwind_context: &'a mut UnwindContext<R::Offset, S>,
unwind_context: &'a mut UnwindContext<R::Offset, UCS>,
bases: BaseAddresses,
base_svma: u64,
) -> Self {
Expand Down Expand Up @@ -138,7 +135,7 @@ impl<
fde_offset.0.into_u64().try_into().ok()
}

pub fn unwind_frame_with_fde<F>(
pub fn unwind_frame_with_fde<F, ES>(
&mut self,
regs: &mut A::UnwindRegs,
is_first_frame: bool,
Expand All @@ -148,6 +145,7 @@ impl<
) -> Result<UnwindResult<A::UnwindRule>, DwarfUnwinderError>
where
F: FnMut(u64) -> Result<u64, ()>,
ES: EvaluationStorage<R>,
{
let lookup_svma = self.base_svma + rel_lookup_address as u64;
let unwind_section_data = self.unwind_section_data.clone();
Expand All @@ -160,7 +158,7 @@ impl<
return Ok(UnwindResult::ExecRule(A::rule_if_uncovered_by_fde()));
}
let (unwind_info, encoding) = unwind_info?;
A::unwind_frame::<F, R, S>(
A::unwind_frame::<F, R, UCS, ES>(
&eh_frame,
unwind_info,
encoding,
Expand All @@ -177,7 +175,7 @@ impl<
return Ok(UnwindResult::ExecRule(A::rule_if_uncovered_by_fde()));
}
let (unwind_info, encoding) = unwind_info?;
A::unwind_frame::<F, R, S>(
A::unwind_frame::<F, R, UCS, ES>(
&debug_frame,
unwind_info,
encoding,
Expand All @@ -194,7 +192,7 @@ impl<
unwind_section: &US,
lookup_svma: u64,
fde_offset: u32,
) -> Result<(&UnwindTableRow<R::Offset, S>, Encoding), DwarfUnwinderError> {
) -> Result<(&UnwindTableRow<R::Offset, UCS>, Encoding), DwarfUnwinderError> {
let fde = unwind_section.fde_from_offset(
&self.bases,
US::Offset::from(R::Offset::from_u32(fde_offset)),
Expand Down
5 changes: 2 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,6 @@
extern crate alloc;

mod add_signed;
mod arcdata;
mod arch;
mod cache;
mod code_address;
Expand Down Expand Up @@ -148,7 +147,7 @@ pub use unwinder::{

/// The unwinder cache for the native CPU architecture.
#[cfg(target_arch = "aarch64")]
pub type CacheNative<D, P> = aarch64::CacheAarch64<D, P>;
pub type CacheNative<P> = aarch64::CacheAarch64<P>;
/// The unwind registers type for the native CPU architecture.
#[cfg(target_arch = "aarch64")]
pub type UnwindRegsNative = aarch64::UnwindRegsAarch64;
Expand All @@ -158,7 +157,7 @@ pub type UnwinderNative<D, P> = aarch64::UnwinderAarch64<D, P>;

/// The unwinder cache for the native CPU architecture.
#[cfg(target_arch = "x86_64")]
pub type CacheNative<D, P> = x86_64::CacheX86_64<D, P>;
pub type CacheNative<P> = x86_64::CacheX86_64<P>;
/// The unwind registers type for the native CPU architecture.
#[cfg(target_arch = "x86_64")]
pub type UnwindRegsNative = x86_64::UnwindRegsX86_64;
Expand Down
Loading

0 comments on commit b1969dc

Please sign in to comment.