-
Notifications
You must be signed in to change notification settings - Fork 109
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
Changes from 6 commits
a33221b
2d1e95f
7a2c897
502b365
7470bb1
f879d80
7bdfa0b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -269,6 +269,67 @@ 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 = BaseAddresses::default() | ||
/// # .set_cfi(address_of_cfi_section_in_memory) | ||
/// # .set_text(address_of_text_section_in_memory) | ||
/// # .set_data(address_of_data_section_in_memory); | ||
/// 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)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible for this to be encoded as |
||
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.clone(), &mut input)?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unneeded clone. |
||
let entry = match entry { | ||
Some(CieOrFde::Fde(fde)) => Ok(fde.parse(cb)?), | ||
Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), | ||
None => Err(Error::NoUnwindInfoForAddress) | ||
}?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Style nit: I think I prefer returns in the match arms instead. |
||
if entry.contains(address) { | ||
Ok(entry) | ||
} else { | ||
Err(Error::NoUnwindInfoForAddress) | ||
} | ||
} | ||
} | ||
|
||
/// `EhFrame` contains the frame unwinding information needed during exception | ||
|
@@ -5528,6 +5589,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(§ion, 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(§ion, 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); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simpler to
let bases = unimplemented!();
?