From a33221b7fc732e05739c6691c31a896f800622bf Mon Sep 17 00:00:00 2001 From: roblabla Date: Tue, 31 Jul 2018 21:47:31 +0200 Subject: [PATCH 1/7] Add EhFrameHdr::lookup_and_parse function --- src/cfi.rs | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/cfi.rs b/src/cfi.rs index be5c21e4f..e0e31d509 100644 --- a/src/cfi.rs +++ b/src/cfi.rs @@ -269,6 +269,37 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { &mut reader, ) } + + /// Returns a parsed FDE for the given address, or NoUnwindInfoForAddress + /// if there are none. + pub fn lookup_and_parse(&self, address: u64, bases: &BaseAddresses, frame: EhFrame) -> Result, R, R::Offset>> { + let fdeptr = self.lookup(address, bases)?; + let fdeptr = match fdeptr { + Pointer::Direct(x) => x, + _ => unreachable!(), + }; + + let eh_frame_ptr = match self.hdr.eh_frame_ptr() { + Pointer::Direct(x) => x, + _ => unreachable!(), + }; + + // 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)?; + let target_fde = match entry { + Some(CieOrFde::Fde(fde)) => Some(fde.parse(|offset| frame.cie_from_offset(bases, offset))?), + Some(CieOrFde::Cie(_)) => unimplemented!(), + None => None + }; + match target_fde { + Some(ref fde) if fde.contains(address) => Ok((*fde).clone()), + _ => Err(Error::NoUnwindInfoForAddress) + } + } } /// `EhFrame` contains the frame unwinding information needed during exception From 2d1e95fc335edb7d53933090154b9d782d1b3735 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 1 Aug 2018 01:52:13 +0200 Subject: [PATCH 2/7] Proper error handling, allow caching of CIE --- src/cfi.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/cfi.rs b/src/cfi.rs index e0e31d509..efa33bb06 100644 --- a/src/cfi.rs +++ b/src/cfi.rs @@ -272,16 +272,22 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { /// Returns a parsed FDE for the given address, or NoUnwindInfoForAddress /// if there are none. - pub fn lookup_and_parse(&self, address: u64, bases: &BaseAddresses, frame: EhFrame) -> Result, R, R::Offset>> { + /// + /// You must provide a function get its associated CIE. See PartialFrameDescriptionEntry::parse + /// for more information. + pub fn lookup_and_parse(&self, address: u64, bases: &BaseAddresses, frame: EhFrame, cb: F) -> Result, R, R::Offset>> + where + F: FnMut(EhFrameOffset) -> Result, R, R::Offset>> + { let fdeptr = self.lookup(address, bases)?; let fdeptr = match fdeptr { Pointer::Direct(x) => x, - _ => unreachable!(), + _ => return Err(Error::UnsupportedPointerEncoding), }; let eh_frame_ptr = match self.hdr.eh_frame_ptr() { Pointer::Direct(x) => x, - _ => unreachable!(), + _ => return Err(Error::UnsupportedPointerEncoding), }; // Calculate the offset in the EhFrame section @@ -290,14 +296,10 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { input.skip(offset)?; let entry = parse_cfi_entry(bases, frame.clone(), &mut input)?; - let target_fde = match entry { - Some(CieOrFde::Fde(fde)) => Some(fde.parse(|offset| frame.cie_from_offset(bases, offset))?), - Some(CieOrFde::Cie(_)) => unimplemented!(), - None => None - }; - match target_fde { - Some(ref fde) if fde.contains(address) => Ok((*fde).clone()), - _ => Err(Error::NoUnwindInfoForAddress) + match entry { + Some(CieOrFde::Fde(fde)) => Ok(fde.parse(cb)?), + Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), + None => Err(Error::NoUnwindInfoForAddress) } } } From 7a2c897bb2e92b28cb5328fe1cfd463342d1552f Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 1 Aug 2018 01:55:04 +0200 Subject: [PATCH 3/7] Add back the check to make sure the FDE contains the address --- src/cfi.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cfi.rs b/src/cfi.rs index efa33bb06..219abd568 100644 --- a/src/cfi.rs +++ b/src/cfi.rs @@ -296,10 +296,15 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { input.skip(offset)?; let entry = parse_cfi_entry(bases, frame.clone(), &mut input)?; - match entry { + let entry = match entry { Some(CieOrFde::Fde(fde)) => Ok(fde.parse(cb)?), Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), None => Err(Error::NoUnwindInfoForAddress) + }?; + if entry.contains(address) { + Ok(entry) + } else { + Err(Error::NoUnwindInfoForAddress) } } } From 502b36573833aa0b3434517a2a59a07928032860 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 1 Aug 2018 03:32:30 +0200 Subject: [PATCH 4/7] Simple example --- src/cfi.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/cfi.rs b/src/cfi.rs index 219abd568..57c65d09b 100644 --- a/src/cfi.rs +++ b/src/cfi.rs @@ -275,6 +275,29 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { /// /// 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> = unreachable!(); + /// # let eh_frame_hdr: ParsedEhFrameHdr> = 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(&self, address: u64, bases: &BaseAddresses, frame: EhFrame, cb: F) -> Result, R, R::Offset>> where F: FnMut(EhFrameOffset) -> Result, R, R::Offset>> From 7470bb11e52433844df284e8825fd00dec7d121f Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 1 Aug 2018 03:40:19 +0200 Subject: [PATCH 5/7] Add unit test --- src/cfi.rs | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/cfi.rs b/src/cfi.rs index 57c65d09b..eaed62df7 100644 --- a/src/cfi.rs +++ b/src/cfi.rs @@ -5589,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); From f879d804c801d06c4ca0b059bad2d6c552acb627 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 1 Aug 2018 03:41:12 +0200 Subject: [PATCH 6/7] fixup! Proper error handling, allow caching of CIE --- src/parser.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index bdbae702b..60ec3e2e5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -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. @@ -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" From 7bdfa0b926bb868e5fdf4b7873632b753e2a36e3 Mon Sep 17 00:00:00 2001 From: roblabla Date: Wed, 1 Aug 2018 14:25:30 +0200 Subject: [PATCH 7/7] Implement style nits --- src/cfi.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/cfi.rs b/src/cfi.rs index eaed62df7..a980aabdd 100644 --- a/src/cfi.rs +++ b/src/cfi.rs @@ -288,10 +288,7 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { /// # 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 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))?; @@ -318,12 +315,12 @@ impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { let mut input = &mut frame.section().clone(); input.skip(offset)?; - let entry = parse_cfi_entry(bases, frame.clone(), &mut input)?; + let entry = parse_cfi_entry(bases, frame, &mut input)?; let entry = match entry { - Some(CieOrFde::Fde(fde)) => Ok(fde.parse(cb)?), - Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), - None => Err(Error::NoUnwindInfoForAddress) - }?; + 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 {