Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split UnwindSection::unwind_info_for_address #396

Merged
merged 3 commits into from
Mar 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 10 additions & 25 deletions benches/bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ mod cfi {

use gimli::{
BaseAddresses, CieOrFde, EhFrame, FrameDescriptionEntry, LittleEndian,
UninitializedUnwindContext, UnwindSection, UnwindTable,
UninitializedUnwindContext, UnwindSection,
};

#[bench]
Expand Down Expand Up @@ -629,18 +629,13 @@ mod cfi {
let fde = partial
.parse(|offset| eh_frame.cie_from_offset(&bases, offset))
.expect("Should be able to get CIE for FED");

let context = ctx
.initialize(fde.cie())
let mut table = fde
.rows(&mut ctx)
.expect("Should be able to initialize ctx");

while let Some(row) =
table.next_row().expect("Should get next unwind table row")
{
let mut table = UnwindTable::new(context, &fde);
while let Some(row) =
table.next_row().expect("Should get next unwind table row")
{
test::black_box(row);
}
test::black_box(row);
}
}
};
Expand Down Expand Up @@ -712,11 +707,7 @@ mod cfi {

b.iter(|| {
let mut ctx = UninitializedUnwindContext::new();
let context = ctx
.initialize(fde.cie())
.expect("Should initialize the ctx OK");

let mut table = UnwindTable::new(context, &fde);
let mut table = fde.rows(&mut ctx).expect("Should initialize the ctx OK");
while let Some(row) = table.next_row().expect("Should get next unwind table row") {
test::black_box(row);
}
Expand All @@ -732,15 +723,9 @@ mod cfi {
let mut ctx = UninitializedUnwindContext::new();

b.iter(|| {
let context = ctx
.initialize(fde.cie())
.expect("Should be able to initialize ctx");

{
let mut table = UnwindTable::new(context, &fde);
while let Some(row) = table.next_row().expect("Should get next unwind table row") {
test::black_box(row);
}
let mut table = fde.rows(&mut ctx).expect("Should initialize the ctx OK");
while let Some(row) = table.next_row().expect("Should get next unwind table row") {
test::black_box(row);
}
});
}
Expand Down
110 changes: 71 additions & 39 deletions src/read/cfi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -544,10 +544,37 @@ pub trait UnwindSection<R: Reader>: Clone + Debug + _UnwindSectionPrivate<R> {
}
}

/// Find the `FrameDescriptionEntry` for the given address.
///
/// If found, the FDE is returned. If not found,
/// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned.
/// If parsing fails, the error is returned.
///
/// Note: this iterates over all FDEs. If available, it is possible
/// to do a binary search with `EhFrameHdr::lookup_and_parse` instead.
fn fde_for_address(
&self,
bases: &BaseAddresses,
address: u64,
) -> Result<FrameDescriptionEntry<Self, R>> {
let mut entries = self.entries(bases);
while let Some(entry) = entries.next()? {
match entry {
CieOrFde::Cie(_) => {}
CieOrFde::Fde(partial) => {
let fde = partial.parse(|offset| self.cie_from_offset(bases, offset))?;
if fde.contains(address) {
return Ok(fde);
}
}
}
}
Err(Error::NoUnwindInfoForAddress)
}

/// Find the frame unwind information for the given address.
///
/// If found, the unwind information is returned along with the reset
/// context in the form `Ok((unwind_info, context))`. If not found,
/// If found, the unwind information is returned. If not found,
/// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or
/// CFI evaluation fails, the error is returned.
///
Expand Down Expand Up @@ -585,35 +612,15 @@ pub trait UnwindSection<R: Reader>: Clone + Debug + _UnwindSectionPrivate<R> {
/// # unreachable!()
/// # }
/// ```
#[allow(clippy::type_complexity)]
#[inline]
fn unwind_info_for_address<'bases>(
&self,
bases: &'bases BaseAddresses,
ctx: &mut UninitializedUnwindContext<Self, R>,
address: u64,
) -> Result<UnwindTableRow<R>> {
let mut entries = self.entries(bases);
let fde = loop {
match entries.next()? {
None => return Err(Error::NoUnwindInfoForAddress),
Some(CieOrFde::Cie(_)) => {}
Some(CieOrFde::Fde(partial)) => {
let fde = partial.parse(|offset| self.cie_from_offset(bases, offset))?;
if fde.contains(address) {
break fde;
}
}
}
};

let ctx = ctx.initialize(fde.cie())?;
let mut table = UnwindTable::new(ctx, &fde);
while let Some(row) = table.next_row()? {
if row.contains(address) {
return Ok(row.clone());
}
}
Err(Error::NoUnwindInfoForAddress)
let fde = self.fde_for_address(bases, address)?;
fde.unwind_info_for_address(ctx, address)
}
}

Expand Down Expand Up @@ -1592,6 +1599,35 @@ where
Ok((initial_address, address_range))
}
}

/// Return the table of unwind information for this FDE.
#[inline]
pub fn rows<'fde, 'ctx>(
&'fde self,
ctx: &'ctx mut UninitializedUnwindContext<Section, R>,
) -> Result<UnwindTable<'fde, 'fde, 'ctx, Section, R>> {
UnwindTable::new(ctx, self)
}

/// Find the frame unwind information for the given address.
///
/// If found, the unwind information is returned along with the reset
/// context in the form `Ok((unwind_info, context))`. If not found,
/// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or
/// CFI evaluation fails, the error is returned.
pub fn unwind_info_for_address(
&self,
ctx: &mut UninitializedUnwindContext<Section, R>,
address: u64,
) -> Result<UnwindTableRow<R>> {
let mut table = self.rows(ctx)?;
while let Some(row) = table.next_row()? {
if row.contains(address) {
return Ok(row.clone());
}
}
Err(Error::NoUnwindInfoForAddress)
}
}

/// # Signal Safe Methods
Expand Down Expand Up @@ -1699,11 +1735,9 @@ where
/// // An uninitialized context.
/// let mut ctx = UninitializedUnwindContext::new();
///
/// // Initialize the context by evaluating the CIE's initial instruction program.
/// let ctx = ctx.initialize(some_fde.cie())?;
///
/// // The initialized context can now be used to generate the unwind table.
/// let mut table = UnwindTable::new(ctx, &some_fde);
/// // Initialize the context by evaluating the CIE's initial instruction program,
/// // and generate the unwind table.
/// let mut table = some_fde.rows(&mut ctx)?;
/// while let Some(row) = table.next_row()? {
/// // Do stuff with each row...
/// # let _ = row;
Expand Down Expand Up @@ -1977,11 +2011,11 @@ where
/// Construct a new `UnwindTable` for the given
/// `FrameDescriptionEntry`'s CFI unwinding program.
pub fn new(
ctx: &'ctx mut UnwindContext<Section, R>,
ctx: &'ctx mut UninitializedUnwindContext<Section, R>,
fde: &'fde FrameDescriptionEntry<Section, R>,
) -> UnwindTable<'fde, 'fde, 'ctx, Section, R> {
assert!(ctx.is_initialized);
Self::new_internal(ctx, fde.cie(), Some(fde))
) -> Result<UnwindTable<'fde, 'fde, 'ctx, Section, R>> {
let ctx = ctx.initialize(fde.cie())?;
Ok(Self::new_internal(ctx, fde.cie(), Some(fde)))
}
}

Expand Down Expand Up @@ -5131,18 +5165,16 @@ mod tests {

let mut ctx = UninitializedUnwindContext::new();
ctx.0.assert_fully_uninitialized();
let ctx = ctx.initialize(&cie).expect("Should run initial program OK");

assert!(ctx.is_initialized);
let mut table = fde.rows(&mut ctx).expect("Should run initial program OK");
assert!(table.ctx.is_initialized);
let expected_initial_rules: RegisterRuleMap<_> = [
(Register(0), RegisterRule::Offset(8)),
(Register(3), RegisterRule::Offset(4)),
]
.into_iter()
.collect();
assert_eq!(ctx.initial_rules, expected_initial_rules);

let mut table = UnwindTable::new(ctx, &fde);
assert_eq!(table.ctx.initial_rules, expected_initial_rules);

{
let row = table.next_row().expect("Should evaluate first row OK");
Expand Down