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

Add EhFrameHdr::lookup_and_parse function #316

Merged
merged 7 commits into from
Aug 1, 2018
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
149 changes: 149 additions & 0 deletions src/cfi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,64 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> {
&mut reader,
)
}

/// Returns a parsed FDE for the given address, or NoUnwindInfoForAddress
/// if there are none.
///
/// You must provide a function get its associated CIE. See PartialFrameDescriptionEntry::parse
/// for more information.
///
/// # Example
///
/// ```
/// # use gimli::{BaseAddresses, EhFrame, ParsedEhFrameHdr, EndianRcSlice, NativeEndian, Error, UnwindSection};
/// # fn foo() -> Result<(), Error> {
/// # let eh_frame: EhFrame<EndianRcSlice<NativeEndian>> = unreachable!();
/// # let eh_frame_hdr: ParsedEhFrameHdr<EndianRcSlice<NativeEndian>> = unimplemented!();
/// # let addr = 0;
/// # let address_of_cfi_section_in_memory = unimplemented!();
/// # let address_of_text_section_in_memory = unimplemented!();
/// # let address_of_data_section_in_memory = unimplemented!();
/// # let address_of_the_start_of_current_func = unimplemented!();
/// # let bases = unimplemented!();
/// let table = eh_frame_hdr.table().unwrap();
/// let fde = table.lookup_and_parse(addr, &bases, eh_frame.clone(),
/// |offset| eh_frame.cie_from_offset(&bases, offset))?;
/// # Ok(())
/// # }
/// ```
pub fn lookup_and_parse<F>(&self, address: u64, bases: &BaseAddresses, frame: EhFrame<R>, cb: F) -> Result<FrameDescriptionEntry<EhFrame<R>, R, R::Offset>>
where
F: FnMut(EhFrameOffset<R::Offset>) -> Result<CommonInformationEntry<EhFrame<R>, R, R::Offset>>
{
let fdeptr = self.lookup(address, bases)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for this to be encoded as DW_EH_PE_pcrel? If so, we'll get this wrong, since bases.cfi will be for the eh_frame section.

let fdeptr = match fdeptr {
Pointer::Direct(x) => x,
_ => return Err(Error::UnsupportedPointerEncoding),
};

let eh_frame_ptr = match self.hdr.eh_frame_ptr() {
Pointer::Direct(x) => x,
_ => return Err(Error::UnsupportedPointerEncoding),
};

// Calculate the offset in the EhFrame section
let offset = R::Offset::from_u64(fdeptr - eh_frame_ptr)?;
let mut input = &mut frame.section().clone();
input.skip(offset)?;

let entry = parse_cfi_entry(bases, frame, &mut input)?;
let entry = match entry {
Some(CieOrFde::Fde(fde)) => fde.parse(cb)?,
Some(CieOrFde::Cie(_)) => return Err(Error::NotFdePointer),
None => return Err(Error::NoUnwindInfoForAddress)
};
if entry.contains(address) {
Ok(entry)
} else {
Err(Error::NoUnwindInfoForAddress)
}
}
}

/// `EhFrame` contains the frame unwinding information needed during exception
Expand Down Expand Up @@ -5528,6 +5586,97 @@ mod tests {
assert_eq!(table.lookup(100000, &bases), Ok(Pointer::Direct(2)));
}

#[test]
fn test_eh_frame_lookup_parse_good() {
// First, setup eh_frame
// Write the CIE first so that its length gets set before we clone it
// into the FDE.
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;

let start_of_cie = Label::new();
let end_of_cie = Label::new();

let section = Section::with_endian(Endian::Little)
.append_repeated(0, 16)
.mark(&start_of_cie)
.cie(Endian::Little, None, &mut cie)
.mark(&end_of_cie);

let mut fde1 = EhFrameFde {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 9,
address_range: 4,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let mut fde2 = EhFrameFde {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 20,
address_range: 8,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};

let start_of_fde1 = Label::new();
let start_of_fde2 = Label::new();

let section = section
// +4 for the FDE length before the CIE offset.
.mark(&start_of_fde1)
.fde(Endian::Little, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde1)
.mark(&start_of_fde2)
.fde(Endian::Little, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde2);

section.start().set_const(0);
let section = section.get_contents().unwrap();
let section = EndianSlice::new(&section, LittleEndian);
let eh_frame = EhFrame::new(section.into(), LittleEndian);

// Setup eh_frame_hdr
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0x0b)
.L8(0x03)
.L8(0x0b)
.L32(0x12345)
.L32(2)
.L32(10)
.L32(0x12345 + start_of_fde1.value().unwrap() as u32)
.L32(20)
.L32(0x12345 + start_of_fde2.value().unwrap() as u32);

let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let eh_frame_hdr = EhFrameHdr::new(&section, LittleEndian).parse(&bases, 8);
assert!(eh_frame_hdr.is_ok());
let eh_frame_hdr = eh_frame_hdr.unwrap();

let table = eh_frame_hdr.table();
assert!(table.is_some());
let table = table.unwrap();

let bases = Default::default();

let f = |_offset| Ok(cie.clone());
assert_eq!(table.lookup_and_parse(9, &bases, eh_frame.clone(), f), Ok(fde1.clone()));
assert_eq!(table.lookup_and_parse(10, &bases, eh_frame.clone(), f), Ok(fde1.clone()));
assert_eq!(table.lookup_and_parse(11, &bases, eh_frame.clone(), f), Ok(fde1));
assert_eq!(table.lookup_and_parse(19, &bases, eh_frame.clone(), f), Err(Error::NoUnwindInfoForAddress));
assert_eq!(table.lookup_and_parse(20, &bases, eh_frame.clone(), f), Ok(fde2.clone()));
assert_eq!(table.lookup_and_parse(21, &bases, eh_frame.clone(), f), Ok(fde2));
assert_eq!(table.lookup_and_parse(100000, &bases, eh_frame.clone(), f), Err(Error::NoUnwindInfoForAddress));
}

#[test]
fn test_eh_frame_stops_at_zero_length() {
let section = Section::with_endian(Endian::Little).L32(0);
Expand Down
3 changes: 3 additions & 0 deletions src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ pub enum Error {
NotCieId,
/// Expected to find a pointer to a CIE, but found the CIE ID instead.
NotCiePointer,
/// Expected to find a pointer to an FDE, but found a CIE instead.
NotFdePointer,
/// Invalid branch target for a DW_OP_bra or DW_OP_skip.
BadBranchTarget(u64),
/// DW_OP_push_object_address used but no address passed in.
Expand Down Expand Up @@ -214,6 +216,7 @@ impl Error {
Error::BadUtf8 => "Found an invalid UTF-8 string.",
Error::NotCieId => "Expected to find the CIE ID, but found something else.",
Error::NotCiePointer => "Expected to find a CIE pointer, but found the CIE ID instead.",
Error::NotFdePointer => "Expected to find an FDE pointer, but found a CIE pointer instead.",
Error::BadBranchTarget(_) => "Invalid branch target in DWARF expression",
Error::InvalidPushObjectAddress => {
"DW_OP_push_object_address used but no object address given"
Expand Down