diff options
Diffstat (limited to 'vendor/gimli/src/read/cfi.rs')
-rw-r--r-- | vendor/gimli/src/read/cfi.rs | 7823 |
1 files changed, 0 insertions, 7823 deletions
diff --git a/vendor/gimli/src/read/cfi.rs b/vendor/gimli/src/read/cfi.rs deleted file mode 100644 index d92c8b2..0000000 --- a/vendor/gimli/src/read/cfi.rs +++ /dev/null @@ -1,7823 +0,0 @@ -#[cfg(feature = "read")] -use alloc::boxed::Box; - -use core::cmp::{Ord, Ordering}; -use core::fmt::{self, Debug}; -use core::iter::FromIterator; -use core::mem; -use core::num::Wrapping; - -use super::util::{ArrayLike, ArrayVec}; -use crate::common::{ - DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId, Vendor, -}; -use crate::constants::{self, DwEhPe}; -use crate::endianity::Endianity; -use crate::read::{ - EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section, StoreOnHeap, -}; - -/// `DebugFrame` contains the `.debug_frame` section's frame unwinding -/// information required to unwind to and recover registers from older frames on -/// the stack. For example, this is useful for a debugger that wants to print -/// locals in a backtrace. -/// -/// Most interesting methods are defined in the -/// [`UnwindSection`](trait.UnwindSection.html) trait. -/// -/// ### Differences between `.debug_frame` and `.eh_frame` -/// -/// While the `.debug_frame` section's information has a lot of overlap with the -/// `.eh_frame` section's information, the `.eh_frame` information tends to only -/// encode the subset of information needed for exception handling. Often, only -/// one of `.eh_frame` or `.debug_frame` will be present in an object file. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct DebugFrame<R: Reader> { - section: R, - address_size: u8, - segment_size: u8, - vendor: Vendor, -} - -impl<R: Reader> DebugFrame<R> { - /// Set the size of a target address in bytes. - /// - /// This defaults to the native word size. - /// This is only used if the CIE version is less than 4. - pub fn set_address_size(&mut self, address_size: u8) { - self.address_size = address_size - } - - /// Set the size of a segment selector in bytes. - /// - /// This defaults to 0. - /// This is only used if the CIE version is less than 4. - pub fn set_segment_size(&mut self, segment_size: u8) { - self.segment_size = segment_size - } - - /// Set the vendor extensions to use. - /// - /// This defaults to `Vendor::Default`. - pub fn set_vendor(&mut self, vendor: Vendor) { - self.vendor = vendor; - } -} - -impl<'input, Endian> DebugFrame<EndianSlice<'input, Endian>> -where - Endian: Endianity, -{ - /// Construct a new `DebugFrame` instance from the data in the - /// `.debug_frame` section. - /// - /// It is the caller's responsibility to read the section and present it as - /// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O - /// loader on macOS, etc. - /// - /// ``` - /// use gimli::{DebugFrame, NativeEndian}; - /// - /// // Use with `.debug_frame` - /// # let buf = [0x00, 0x01, 0x02, 0x03]; - /// # let read_debug_frame_section_somehow = || &buf; - /// let debug_frame = DebugFrame::new(read_debug_frame_section_somehow(), NativeEndian); - /// ``` - pub fn new(section: &'input [u8], endian: Endian) -> Self { - Self::from(EndianSlice::new(section, endian)) - } -} - -impl<R: Reader> Section<R> for DebugFrame<R> { - fn id() -> SectionId { - SectionId::DebugFrame - } - - fn reader(&self) -> &R { - &self.section - } -} - -impl<R: Reader> From<R> for DebugFrame<R> { - fn from(section: R) -> Self { - // Default to no segments and native word size. - DebugFrame { - section, - address_size: mem::size_of::<usize>() as u8, - segment_size: 0, - vendor: Vendor::Default, - } - } -} - -/// `EhFrameHdr` contains the information about the `.eh_frame_hdr` section. -/// -/// A pointer to the start of the `.eh_frame` data, and optionally, a binary -/// search table of pointers to the `.eh_frame` records that are found in this section. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct EhFrameHdr<R: Reader>(R); - -/// `ParsedEhFrameHdr` contains the parsed information from the `.eh_frame_hdr` section. -#[derive(Clone, Debug)] -pub struct ParsedEhFrameHdr<R: Reader> { - address_size: u8, - section: R, - - eh_frame_ptr: Pointer, - fde_count: u64, - table_enc: DwEhPe, - table: R, -} - -impl<'input, Endian> EhFrameHdr<EndianSlice<'input, Endian>> -where - Endian: Endianity, -{ - /// Constructs a new `EhFrameHdr` instance from the data in the `.eh_frame_hdr` section. - pub fn new(section: &'input [u8], endian: Endian) -> Self { - Self::from(EndianSlice::new(section, endian)) - } -} - -impl<R: Reader> EhFrameHdr<R> { - /// Parses this `EhFrameHdr` to a `ParsedEhFrameHdr`. - pub fn parse(&self, bases: &BaseAddresses, address_size: u8) -> Result<ParsedEhFrameHdr<R>> { - let mut reader = self.0.clone(); - let version = reader.read_u8()?; - if version != 1 { - return Err(Error::UnknownVersion(u64::from(version))); - } - - let eh_frame_ptr_enc = parse_pointer_encoding(&mut reader)?; - let fde_count_enc = parse_pointer_encoding(&mut reader)?; - let table_enc = parse_pointer_encoding(&mut reader)?; - - let parameters = PointerEncodingParameters { - bases: &bases.eh_frame_hdr, - func_base: None, - address_size, - section: &self.0, - }; - - // Omitting this pointer is not valid (defeats the purpose of .eh_frame_hdr entirely) - if eh_frame_ptr_enc == constants::DW_EH_PE_omit { - return Err(Error::CannotParseOmitPointerEncoding); - } - let eh_frame_ptr = parse_encoded_pointer(eh_frame_ptr_enc, ¶meters, &mut reader)?; - - let fde_count; - if fde_count_enc == constants::DW_EH_PE_omit || table_enc == constants::DW_EH_PE_omit { - fde_count = 0 - } else { - fde_count = parse_encoded_pointer(fde_count_enc, ¶meters, &mut reader)?.direct()?; - } - - Ok(ParsedEhFrameHdr { - address_size, - section: self.0.clone(), - - eh_frame_ptr, - fde_count, - table_enc, - table: reader, - }) - } -} - -impl<R: Reader> Section<R> for EhFrameHdr<R> { - fn id() -> SectionId { - SectionId::EhFrameHdr - } - - fn reader(&self) -> &R { - &self.0 - } -} - -impl<R: Reader> From<R> for EhFrameHdr<R> { - fn from(section: R) -> Self { - EhFrameHdr(section) - } -} - -impl<R: Reader> ParsedEhFrameHdr<R> { - /// Returns the address of the binary's `.eh_frame` section. - pub fn eh_frame_ptr(&self) -> Pointer { - self.eh_frame_ptr - } - - /// Retrieves the CFI binary search table, if there is one. - pub fn table(&self) -> Option<EhHdrTable<R>> { - // There are two big edge cases here: - // * You search the table for an invalid address. As this is just a binary - // search table, we always have to return a valid result for that (unless - // you specify an address that is lower than the first address in the - // table). Since this means that you have to recheck that the FDE contains - // your address anyways, we just return the first FDE even when the address - // is too low. After all, we're just doing a normal binary search. - // * This falls apart when the table is empty - there is no entry we could - // return. We conclude that an empty table is not really a table at all. - if self.fde_count == 0 { - None - } else { - Some(EhHdrTable { hdr: self }) - } - } -} - -/// An iterator for `.eh_frame_hdr` section's binary search table. -/// -/// Each table entry consists of a tuple containing an `initial_location` and `address`. -/// The `initial location` represents the first address that the targeted FDE -/// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. -/// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. -#[derive(Debug)] -pub struct EhHdrTableIter<'a, 'bases, R: Reader> { - hdr: &'a ParsedEhFrameHdr<R>, - table: R, - bases: &'bases BaseAddresses, - remain: u64, -} - -impl<'a, 'bases, R: Reader> EhHdrTableIter<'a, 'bases, R> { - /// Yield the next entry in the `EhHdrTableIter`. - pub fn next(&mut self) -> Result<Option<(Pointer, Pointer)>> { - if self.remain == 0 { - return Ok(None); - } - - let parameters = PointerEncodingParameters { - bases: &self.bases.eh_frame_hdr, - func_base: None, - address_size: self.hdr.address_size, - section: &self.hdr.section, - }; - - self.remain -= 1; - let from = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; - let to = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?; - Ok(Some((from, to))) - } - /// Yield the nth entry in the `EhHdrTableIter` - pub fn nth(&mut self, n: usize) -> Result<Option<(Pointer, Pointer)>> { - use core::convert::TryFrom; - let size = match self.hdr.table_enc.format() { - constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => { - return Err(Error::VariableLengthSearchTable); - } - constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2, - constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4, - constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8, - _ => return Err(Error::UnknownPointerEncoding), - }; - - let row_size = size * 2; - let n = u64::try_from(n).map_err(|_| Error::UnsupportedOffset)?; - self.remain = self.remain.saturating_sub(n); - self.table.skip(R::Offset::from_u64(n * row_size)?)?; - self.next() - } -} - -#[cfg(feature = "fallible-iterator")] -impl<'a, 'bases, R: Reader> fallible_iterator::FallibleIterator for EhHdrTableIter<'a, 'bases, R> { - type Item = (Pointer, Pointer); - type Error = Error; - fn next(&mut self) -> Result<Option<Self::Item>> { - EhHdrTableIter::next(self) - } - - fn size_hint(&self) -> (usize, Option<usize>) { - use core::convert::TryInto; - ( - self.remain.try_into().unwrap_or(0), - self.remain.try_into().ok(), - ) - } - - fn nth(&mut self, n: usize) -> Result<Option<Self::Item>> { - EhHdrTableIter::nth(self, n) - } -} - -/// The CFI binary search table that is an optional part of the `.eh_frame_hdr` section. -#[derive(Debug, Clone)] -pub struct EhHdrTable<'a, R: Reader> { - hdr: &'a ParsedEhFrameHdr<R>, -} - -impl<'a, R: Reader + 'a> EhHdrTable<'a, R> { - /// Return an iterator that can walk the `.eh_frame_hdr` table. - /// - /// Each table entry consists of a tuple containing an `initial_location` and `address`. - /// The `initial location` represents the first address that the targeted FDE - /// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section. - /// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE. - pub fn iter<'bases>(&self, bases: &'bases BaseAddresses) -> EhHdrTableIter<'_, 'bases, R> { - EhHdrTableIter { - hdr: self.hdr, - bases, - remain: self.hdr.fde_count, - table: self.hdr.table.clone(), - } - } - /// *Probably* returns a pointer to the FDE for the given address. - /// - /// This performs a binary search, so if there is no FDE for the given address, - /// this function **will** return a pointer to any other FDE that's close by. - /// - /// To be sure, you **must** call `contains` on the FDE. - pub fn lookup(&self, address: u64, bases: &BaseAddresses) -> Result<Pointer> { - let size = match self.hdr.table_enc.format() { - constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => { - return Err(Error::VariableLengthSearchTable); - } - constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2, - constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4, - constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8, - _ => return Err(Error::UnknownPointerEncoding), - }; - - let row_size = size * 2; - - let mut len = self.hdr.fde_count; - - let mut reader = self.hdr.table.clone(); - - let parameters = PointerEncodingParameters { - bases: &bases.eh_frame_hdr, - func_base: None, - address_size: self.hdr.address_size, - section: &self.hdr.section, - }; - - while len > 1 { - let head = reader.split(R::Offset::from_u64((len / 2) * row_size)?)?; - let tail = reader.clone(); - - let pivot = - parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader)?.direct()?; - - match pivot.cmp(&address) { - Ordering::Equal => { - reader = tail; - break; - } - Ordering::Less => { - reader = tail; - len = len - (len / 2); - } - Ordering::Greater => { - reader = head; - len /= 2; - } - } - } - - reader.skip(R::Offset::from_u64(size)?)?; - - parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader) - } - - /// Convert a `Pointer` to a section offset. - /// - /// This does not support indirect pointers. - pub fn pointer_to_offset(&self, ptr: Pointer) -> Result<EhFrameOffset<R::Offset>> { - let ptr = ptr.direct()?; - let eh_frame_ptr = self.hdr.eh_frame_ptr().direct()?; - - // Calculate the offset in the EhFrame section - R::Offset::from_u64(ptr - eh_frame_ptr).map(EhFrameOffset) - } - - /// Returns a parsed FDE for the given address, or `NoUnwindInfoForAddress` - /// if there are none. - /// - /// You must provide a function to get its associated CIE. See - /// `PartialFrameDescriptionEntry::parse` for more information. - /// - /// # Example - /// - /// ``` - /// # use gimli::{BaseAddresses, EhFrame, ParsedEhFrameHdr, EndianSlice, NativeEndian, Error, UnwindSection}; - /// # fn foo() -> Result<(), Error> { - /// # let eh_frame: EhFrame<EndianSlice<NativeEndian>> = unreachable!(); - /// # let eh_frame_hdr: ParsedEhFrameHdr<EndianSlice<NativeEndian>> = unimplemented!(); - /// # let addr = 0; - /// # let bases = unimplemented!(); - /// let table = eh_frame_hdr.table().unwrap(); - /// let fde = table.fde_for_address(&eh_frame, &bases, addr, EhFrame::cie_from_offset)?; - /// # Ok(()) - /// # } - /// ``` - pub fn fde_for_address<F>( - &self, - frame: &EhFrame<R>, - bases: &BaseAddresses, - address: u64, - get_cie: F, - ) -> Result<FrameDescriptionEntry<R>> - where - F: FnMut( - &EhFrame<R>, - &BaseAddresses, - EhFrameOffset<R::Offset>, - ) -> Result<CommonInformationEntry<R>>, - { - let fdeptr = self.lookup(address, bases)?; - let offset = self.pointer_to_offset(fdeptr)?; - let entry = frame.fde_from_offset(bases, offset, get_cie)?; - if entry.contains(address) { - Ok(entry) - } else { - Err(Error::NoUnwindInfoForAddress) - } - } - - #[inline] - #[doc(hidden)] - #[deprecated(note = "Method renamed to fde_for_address; use that instead.")] - pub fn lookup_and_parse<F>( - &self, - address: u64, - bases: &BaseAddresses, - frame: EhFrame<R>, - get_cie: F, - ) -> Result<FrameDescriptionEntry<R>> - where - F: FnMut( - &EhFrame<R>, - &BaseAddresses, - EhFrameOffset<R::Offset>, - ) -> Result<CommonInformationEntry<R>>, - { - self.fde_for_address(&frame, bases, address, get_cie) - } - - /// Returns the frame unwind information for the given address, - /// or `NoUnwindInfoForAddress` if there are none. - /// - /// You must provide a function to get the associated CIE. See - /// `PartialFrameDescriptionEntry::parse` for more information. - pub fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage<R>>( - &self, - frame: &EhFrame<R>, - bases: &BaseAddresses, - ctx: &'ctx mut UnwindContext<R, A>, - address: u64, - get_cie: F, - ) -> Result<&'ctx UnwindTableRow<R, A>> - where - F: FnMut( - &EhFrame<R>, - &BaseAddresses, - EhFrameOffset<R::Offset>, - ) -> Result<CommonInformationEntry<R>>, - { - let fde = self.fde_for_address(frame, bases, address, get_cie)?; - fde.unwind_info_for_address(frame, bases, ctx, address) - } -} - -/// `EhFrame` contains the frame unwinding information needed during exception -/// handling found in the `.eh_frame` section. -/// -/// Most interesting methods are defined in the -/// [`UnwindSection`](trait.UnwindSection.html) trait. -/// -/// See -/// [`DebugFrame`](./struct.DebugFrame.html#differences-between-debug_frame-and-eh_frame) -/// for some discussion on the differences between `.debug_frame` and -/// `.eh_frame`. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub struct EhFrame<R: Reader> { - section: R, - address_size: u8, - vendor: Vendor, -} - -impl<R: Reader> EhFrame<R> { - /// Set the size of a target address in bytes. - /// - /// This defaults to the native word size. - pub fn set_address_size(&mut self, address_size: u8) { - self.address_size = address_size - } - - /// Set the vendor extensions to use. - /// - /// This defaults to `Vendor::Default`. - pub fn set_vendor(&mut self, vendor: Vendor) { - self.vendor = vendor; - } -} - -impl<'input, Endian> EhFrame<EndianSlice<'input, Endian>> -where - Endian: Endianity, -{ - /// Construct a new `EhFrame` instance from the data in the - /// `.eh_frame` section. - /// - /// It is the caller's responsibility to read the section and present it as - /// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O - /// loader on macOS, etc. - /// - /// ``` - /// use gimli::{EhFrame, EndianSlice, NativeEndian}; - /// - /// // Use with `.eh_frame` - /// # let buf = [0x00, 0x01, 0x02, 0x03]; - /// # let read_eh_frame_section_somehow = || &buf; - /// let eh_frame = EhFrame::new(read_eh_frame_section_somehow(), NativeEndian); - /// ``` - pub fn new(section: &'input [u8], endian: Endian) -> Self { - Self::from(EndianSlice::new(section, endian)) - } -} - -impl<R: Reader> Section<R> for EhFrame<R> { - fn id() -> SectionId { - SectionId::EhFrame - } - - fn reader(&self) -> &R { - &self.section - } -} - -impl<R: Reader> From<R> for EhFrame<R> { - fn from(section: R) -> Self { - // Default to native word size. - EhFrame { - section, - address_size: mem::size_of::<usize>() as u8, - vendor: Vendor::Default, - } - } -} - -// This has to be `pub` to silence a warning (that is deny(..)'d by default) in -// rustc. Eventually, not having this `pub` will become a hard error. -#[doc(hidden)] -#[allow(missing_docs)] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum CieOffsetEncoding { - U32, - U64, -} - -/// An offset into an `UnwindSection`. -// -// Needed to avoid conflicting implementations of `Into<T>`. -pub trait UnwindOffset<T = usize>: Copy + Debug + Eq + From<T> -where - T: ReaderOffset, -{ - /// Convert an `UnwindOffset<T>` into a `T`. - fn into(self) -> T; -} - -impl<T> UnwindOffset<T> for DebugFrameOffset<T> -where - T: ReaderOffset, -{ - #[inline] - fn into(self) -> T { - self.0 - } -} - -impl<T> UnwindOffset<T> for EhFrameOffset<T> -where - T: ReaderOffset, -{ - #[inline] - fn into(self) -> T { - self.0 - } -} - -/// This trait completely encapsulates everything that is different between -/// `.eh_frame` and `.debug_frame`, as well as all the bits that can change -/// between DWARF versions. -#[doc(hidden)] -pub trait _UnwindSectionPrivate<R: Reader> { - /// Get the underlying section data. - fn section(&self) -> &R; - - /// Returns true if the given length value should be considered an - /// end-of-entries sentinel. - fn length_value_is_end_of_entries(length: R::Offset) -> bool; - - /// Return true if the given offset if the CIE sentinel, false otherwise. - fn is_cie(format: Format, id: u64) -> bool; - - /// Return the CIE offset/ID encoding used by this unwind section with the - /// given DWARF format. - fn cie_offset_encoding(format: Format) -> CieOffsetEncoding; - - /// For `.eh_frame`, CIE offsets are relative to the current position. For - /// `.debug_frame`, they are relative to the start of the section. We always - /// internally store them relative to the section, so we handle translating - /// `.eh_frame`'s relative offsets in this method. If the offset calculation - /// underflows, return `None`. - fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<R::Offset>; - - /// Does this version of this unwind section encode address and segment - /// sizes in its CIEs? - fn has_address_and_segment_sizes(version: u8) -> bool; - - /// The address size to use if `has_address_and_segment_sizes` returns false. - fn address_size(&self) -> u8; - - /// The segment size to use if `has_address_and_segment_sizes` returns false. - fn segment_size(&self) -> u8; - - /// The vendor extensions to use. - fn vendor(&self) -> Vendor; -} - -/// A section holding unwind information: either `.debug_frame` or -/// `.eh_frame`. See [`DebugFrame`](./struct.DebugFrame.html) and -/// [`EhFrame`](./struct.EhFrame.html) respectively. -pub trait UnwindSection<R: Reader>: Clone + Debug + _UnwindSectionPrivate<R> { - /// The offset type associated with this CFI section. Either - /// `DebugFrameOffset` or `EhFrameOffset`. - type Offset: UnwindOffset<R::Offset>; - - /// Iterate over the `CommonInformationEntry`s and `FrameDescriptionEntry`s - /// in this `.debug_frame` section. - /// - /// Can be [used with - /// `FallibleIterator`](./index.html#using-with-fallibleiterator). - fn entries<'bases>(&self, bases: &'bases BaseAddresses) -> CfiEntriesIter<'bases, Self, R> { - CfiEntriesIter { - section: self.clone(), - bases, - input: self.section().clone(), - } - } - - /// Parse the `CommonInformationEntry` at the given offset. - fn cie_from_offset( - &self, - bases: &BaseAddresses, - offset: Self::Offset, - ) -> Result<CommonInformationEntry<R>> { - let offset = UnwindOffset::into(offset); - let input = &mut self.section().clone(); - input.skip(offset)?; - CommonInformationEntry::parse(bases, self, input) - } - - /// Parse the `PartialFrameDescriptionEntry` at the given offset. - fn partial_fde_from_offset<'bases>( - &self, - bases: &'bases BaseAddresses, - offset: Self::Offset, - ) -> Result<PartialFrameDescriptionEntry<'bases, Self, R>> { - let offset = UnwindOffset::into(offset); - let input = &mut self.section().clone(); - input.skip(offset)?; - PartialFrameDescriptionEntry::parse_partial(self, bases, input) - } - - /// Parse the `FrameDescriptionEntry` at the given offset. - fn fde_from_offset<F>( - &self, - bases: &BaseAddresses, - offset: Self::Offset, - get_cie: F, - ) -> Result<FrameDescriptionEntry<R>> - where - F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>, - { - let partial = self.partial_fde_from_offset(bases, offset)?; - partial.parse(get_cie) - } - - /// 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. - /// - /// You must provide a function to get its associated CIE. See - /// `PartialFrameDescriptionEntry::parse` for more information. - /// - /// Note: this iterates over all FDEs. If available, it is possible - /// to do a binary search with `EhFrameHdr::fde_for_address` instead. - fn fde_for_address<F>( - &self, - bases: &BaseAddresses, - address: u64, - mut get_cie: F, - ) -> Result<FrameDescriptionEntry<R>> - where - F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>, - { - let mut entries = self.entries(bases); - while let Some(entry) = entries.next()? { - match entry { - CieOrFde::Cie(_) => {} - CieOrFde::Fde(partial) => { - let fde = partial.parse(&mut get_cie)?; - 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. If not found, - /// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or - /// CFI evaluation fails, the error is returned. - /// - /// ``` - /// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindContext, - /// UnwindSection}; - /// - /// # fn foo() -> gimli::Result<()> { - /// # let read_eh_frame_section = || unimplemented!(); - /// // Get the `.eh_frame` section from the object file. Alternatively, - /// // use `EhFrame` with the `.eh_frame` section of the object file. - /// let eh_frame = EhFrame::new(read_eh_frame_section(), NativeEndian); - /// - /// # let get_frame_pc = || unimplemented!(); - /// // Get the address of the PC for a frame you'd like to unwind. - /// let address = get_frame_pc(); - /// - /// // This context is reusable, which cuts down on heap allocations. - /// let ctx = UnwindContext::new(); - /// - /// // Optionally provide base addresses for any relative pointers. If a - /// // base address isn't provided and a pointer is found that is relative to - /// // it, we will return an `Err`. - /// # let address_of_text_section_in_memory = unimplemented!(); - /// # let address_of_got_section_in_memory = unimplemented!(); - /// let bases = BaseAddresses::default() - /// .set_text(address_of_text_section_in_memory) - /// .set_got(address_of_got_section_in_memory); - /// - /// let unwind_info = eh_frame.unwind_info_for_address( - /// &bases, - /// &mut ctx, - /// address, - /// EhFrame::cie_from_offset, - /// )?; - /// - /// # let do_stuff_with = |_| unimplemented!(); - /// do_stuff_with(unwind_info); - /// # let _ = ctx; - /// # unreachable!() - /// # } - /// ``` - #[inline] - fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage<R>>( - &self, - bases: &BaseAddresses, - ctx: &'ctx mut UnwindContext<R, A>, - address: u64, - get_cie: F, - ) -> Result<&'ctx UnwindTableRow<R, A>> - where - F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>, - { - let fde = self.fde_for_address(bases, address, get_cie)?; - fde.unwind_info_for_address(self, bases, ctx, address) - } -} - -impl<R: Reader> _UnwindSectionPrivate<R> for DebugFrame<R> { - fn section(&self) -> &R { - &self.section - } - - fn length_value_is_end_of_entries(_: R::Offset) -> bool { - false - } - - fn is_cie(format: Format, id: u64) -> bool { - match format { - Format::Dwarf32 => id == 0xffff_ffff, - Format::Dwarf64 => id == 0xffff_ffff_ffff_ffff, - } - } - - fn cie_offset_encoding(format: Format) -> CieOffsetEncoding { - match format { - Format::Dwarf32 => CieOffsetEncoding::U32, - Format::Dwarf64 => CieOffsetEncoding::U64, - } - } - - fn resolve_cie_offset(&self, _: R::Offset, offset: R::Offset) -> Option<R::Offset> { - Some(offset) - } - - fn has_address_and_segment_sizes(version: u8) -> bool { - version == 4 - } - - fn address_size(&self) -> u8 { - self.address_size - } - - fn segment_size(&self) -> u8 { - self.segment_size - } - - fn vendor(&self) -> Vendor { - self.vendor - } -} - -impl<R: Reader> UnwindSection<R> for DebugFrame<R> { - type Offset = DebugFrameOffset<R::Offset>; -} - -impl<R: Reader> _UnwindSectionPrivate<R> for EhFrame<R> { - fn section(&self) -> &R { - &self.section - } - - fn length_value_is_end_of_entries(length: R::Offset) -> bool { - length.into_u64() == 0 - } - - fn is_cie(_: Format, id: u64) -> bool { - id == 0 - } - - fn cie_offset_encoding(_format: Format) -> CieOffsetEncoding { - // `.eh_frame` offsets are always 4 bytes, regardless of the DWARF - // format. - CieOffsetEncoding::U32 - } - - fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<R::Offset> { - base.checked_sub(offset) - } - - fn has_address_and_segment_sizes(_version: u8) -> bool { - false - } - - fn address_size(&self) -> u8 { - self.address_size - } - - fn segment_size(&self) -> u8 { - 0 - } - - fn vendor(&self) -> Vendor { - self.vendor - } -} - -impl<R: Reader> UnwindSection<R> for EhFrame<R> { - type Offset = EhFrameOffset<R::Offset>; -} - -/// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers. -/// -/// During CIE/FDE parsing, if a relative pointer is encountered for a base -/// address that is unknown, an Err will be returned. -/// -/// ``` -/// use gimli::BaseAddresses; -/// -/// # fn foo() { -/// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!(); -/// # let address_of_eh_frame_section_in_memory = unimplemented!(); -/// # let address_of_text_section_in_memory = unimplemented!(); -/// # let address_of_got_section_in_memory = unimplemented!(); -/// # let address_of_the_start_of_current_func = unimplemented!(); -/// let bases = BaseAddresses::default() -/// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory) -/// .set_eh_frame(address_of_eh_frame_section_in_memory) -/// .set_text(address_of_text_section_in_memory) -/// .set_got(address_of_got_section_in_memory); -/// # let _ = bases; -/// # } -/// ``` -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct BaseAddresses { - /// The base addresses to use for pointers in the `.eh_frame_hdr` section. - pub eh_frame_hdr: SectionBaseAddresses, - - /// The base addresses to use for pointers in the `.eh_frame` section. - pub eh_frame: SectionBaseAddresses, -} - -/// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers -/// in a particular section. -/// -/// See `BaseAddresses` for methods that are helpful in setting these addresses. -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub struct SectionBaseAddresses { - /// The address of the section containing the pointer. - pub section: Option<u64>, - - /// The base address for text relative pointers. - /// This is generally the address of the `.text` section. - pub text: Option<u64>, - - /// The base address for data relative pointers. - /// - /// For pointers in the `.eh_frame_hdr` section, this is the address - /// of the `.eh_frame_hdr` section - /// - /// For pointers in the `.eh_frame` section, this is generally the - /// global pointer, such as the address of the `.got` section. - pub data: Option<u64>, -} - -impl BaseAddresses { - /// Set the `.eh_frame_hdr` section base address. - #[inline] - pub fn set_eh_frame_hdr(mut self, addr: u64) -> Self { - self.eh_frame_hdr.section = Some(addr); - self.eh_frame_hdr.data = Some(addr); - self - } - - /// Set the `.eh_frame` section base address. - #[inline] - pub fn set_eh_frame(mut self, addr: u64) -> Self { - self.eh_frame.section = Some(addr); - self - } - - /// Set the `.text` section base address. - #[inline] - pub fn set_text(mut self, addr: u64) -> Self { - self.eh_frame_hdr.text = Some(addr); - self.eh_frame.text = Some(addr); - self - } - - /// Set the `.got` section base address. - #[inline] - pub fn set_got(mut self, addr: u64) -> Self { - self.eh_frame.data = Some(addr); - self - } -} - -/// An iterator over CIE and FDE entries in a `.debug_frame` or `.eh_frame` -/// section. -/// -/// Some pointers may be encoded relative to various base addresses. Use the -/// [`BaseAddresses`](./struct.BaseAddresses.html) parameter to provide them. By -/// default, none are provided. If a relative pointer is encountered for a base -/// address that is unknown, an `Err` will be returned and iteration will abort. -/// -/// Can be [used with -/// `FallibleIterator`](./index.html#using-with-fallibleiterator). -/// -/// ``` -/// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindSection}; -/// -/// # fn foo() -> gimli::Result<()> { -/// # let read_eh_frame_somehow = || unimplemented!(); -/// let eh_frame = EhFrame::new(read_eh_frame_somehow(), NativeEndian); -/// -/// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!(); -/// # let address_of_eh_frame_section_in_memory = unimplemented!(); -/// # let address_of_text_section_in_memory = unimplemented!(); -/// # let address_of_got_section_in_memory = unimplemented!(); -/// # let address_of_the_start_of_current_func = unimplemented!(); -/// // Provide base addresses for relative pointers. -/// let bases = BaseAddresses::default() -/// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory) -/// .set_eh_frame(address_of_eh_frame_section_in_memory) -/// .set_text(address_of_text_section_in_memory) -/// .set_got(address_of_got_section_in_memory); -/// -/// let mut entries = eh_frame.entries(&bases); -/// -/// # let do_stuff_with = |_| unimplemented!(); -/// while let Some(entry) = entries.next()? { -/// do_stuff_with(entry) -/// } -/// # unreachable!() -/// # } -/// ``` -#[derive(Clone, Debug)] -pub struct CfiEntriesIter<'bases, Section, R> -where - R: Reader, - Section: UnwindSection<R>, -{ - section: Section, - bases: &'bases BaseAddresses, - input: R, -} - -impl<'bases, Section, R> CfiEntriesIter<'bases, Section, R> -where - R: Reader, - Section: UnwindSection<R>, -{ - /// Advance the iterator to the next entry. - pub fn next(&mut self) -> Result<Option<CieOrFde<'bases, Section, R>>> { - if self.input.is_empty() { - return Ok(None); - } - - match parse_cfi_entry(self.bases, &self.section, &mut self.input) { - Err(e) => { - self.input.empty(); - Err(e) - } - Ok(None) => { - self.input.empty(); - Ok(None) - } - Ok(Some(entry)) => Ok(Some(entry)), - } - } -} - -#[cfg(feature = "fallible-iterator")] -impl<'bases, Section, R> fallible_iterator::FallibleIterator for CfiEntriesIter<'bases, Section, R> -where - R: Reader, - Section: UnwindSection<R>, -{ - type Item = CieOrFde<'bases, Section, R>; - type Error = Error; - - fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { - CfiEntriesIter::next(self) - } -} - -/// Either a `CommonInformationEntry` (CIE) or a `FrameDescriptionEntry` (FDE). -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum CieOrFde<'bases, Section, R> -where - R: Reader, - Section: UnwindSection<R>, -{ - /// This CFI entry is a `CommonInformationEntry`. - Cie(CommonInformationEntry<R>), - /// This CFI entry is a `FrameDescriptionEntry`, however fully parsing it - /// requires parsing its CIE first, so it is left in a partially parsed - /// state. - Fde(PartialFrameDescriptionEntry<'bases, Section, R>), -} - -fn parse_cfi_entry<'bases, Section, R>( - bases: &'bases BaseAddresses, - section: &Section, - input: &mut R, -) -> Result<Option<CieOrFde<'bases, Section, R>>> -where - R: Reader, - Section: UnwindSection<R>, -{ - let (offset, length, format) = loop { - let offset = input.offset_from(section.section()); - let (length, format) = input.read_initial_length()?; - - if Section::length_value_is_end_of_entries(length) { - return Ok(None); - } - - // Hack: skip zero padding inserted by buggy compilers/linkers. - // We require that the padding is a multiple of 32-bits, otherwise - // there is no reliable way to determine when the padding ends. This - // should be okay since CFI entries must be aligned to the address size. - - if length.into_u64() != 0 || format != Format::Dwarf32 { - break (offset, length, format); - } - }; - - let mut rest = input.split(length)?; - let cie_offset_base = rest.offset_from(section.section()); - let cie_id_or_offset = match Section::cie_offset_encoding(format) { - CieOffsetEncoding::U32 => rest.read_u32().map(u64::from)?, - CieOffsetEncoding::U64 => rest.read_u64()?, - }; - - if Section::is_cie(format, cie_id_or_offset) { - let cie = CommonInformationEntry::parse_rest(offset, length, format, bases, section, rest)?; - Ok(Some(CieOrFde::Cie(cie))) - } else { - let cie_offset = R::Offset::from_u64(cie_id_or_offset)?; - let cie_offset = match section.resolve_cie_offset(cie_offset_base, cie_offset) { - None => return Err(Error::OffsetOutOfBounds), - Some(cie_offset) => cie_offset, - }; - - let fde = PartialFrameDescriptionEntry { - offset, - length, - format, - cie_offset: cie_offset.into(), - rest, - section: section.clone(), - bases, - }; - - Ok(Some(CieOrFde::Fde(fde))) - } -} - -/// We support the z-style augmentation [defined by `.eh_frame`][ehframe]. -/// -/// [ehframe]: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html -#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)] -pub struct Augmentation { - /// > A 'L' may be present at any position after the first character of the - /// > string. This character may only be present if 'z' is the first character - /// > of the string. If present, it indicates the presence of one argument in - /// > the Augmentation Data of the CIE, and a corresponding argument in the - /// > Augmentation Data of the FDE. The argument in the Augmentation Data of - /// > the CIE is 1-byte and represents the pointer encoding used for the - /// > argument in the Augmentation Data of the FDE, which is the address of a - /// > language-specific data area (LSDA). The size of the LSDA pointer is - /// > specified by the pointer encoding used. - lsda: Option<constants::DwEhPe>, - - /// > A 'P' may be present at any position after the first character of the - /// > string. This character may only be present if 'z' is the first character - /// > of the string. If present, it indicates the presence of two arguments in - /// > the Augmentation Data of the CIE. The first argument is 1-byte and - /// > represents the pointer encoding used for the second argument, which is - /// > the address of a personality routine handler. The size of the - /// > personality routine pointer is specified by the pointer encoding used. - personality: Option<(constants::DwEhPe, Pointer)>, - - /// > A 'R' may be present at any position after the first character of the - /// > string. This character may only be present if 'z' is the first character - /// > of the string. If present, The Augmentation Data shall include a 1 byte - /// > argument that represents the pointer encoding for the address pointers - /// > used in the FDE. - fde_address_encoding: Option<constants::DwEhPe>, - - /// True if this CIE's FDEs are trampolines for signal handlers. - is_signal_trampoline: bool, -} - -impl Augmentation { - fn parse<Section, R>( - augmentation_str: &mut R, - bases: &BaseAddresses, - address_size: u8, - section: &Section, - input: &mut R, - ) -> Result<Augmentation> - where - R: Reader, - Section: UnwindSection<R>, - { - debug_assert!( - !augmentation_str.is_empty(), - "Augmentation::parse should only be called if we have an augmentation" - ); - - let mut augmentation = Augmentation::default(); - - let mut parsed_first = false; - let mut data = None; - - while !augmentation_str.is_empty() { - let ch = augmentation_str.read_u8()?; - match ch { - b'z' => { - if parsed_first { - return Err(Error::UnknownAugmentation); - } - - let augmentation_length = input.read_uleb128().and_then(R::Offset::from_u64)?; - data = Some(input.split(augmentation_length)?); - } - b'L' => { - let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; - let encoding = parse_pointer_encoding(rest)?; - augmentation.lsda = Some(encoding); - } - b'P' => { - let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; - let encoding = parse_pointer_encoding(rest)?; - let parameters = PointerEncodingParameters { - bases: &bases.eh_frame, - func_base: None, - address_size, - section: section.section(), - }; - - let personality = parse_encoded_pointer(encoding, ¶meters, rest)?; - augmentation.personality = Some((encoding, personality)); - } - b'R' => { - let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?; - let encoding = parse_pointer_encoding(rest)?; - augmentation.fde_address_encoding = Some(encoding); - } - b'S' => augmentation.is_signal_trampoline = true, - _ => return Err(Error::UnknownAugmentation), - } - - parsed_first = true; - } - - Ok(augmentation) - } -} - -/// Parsed augmentation data for a `FrameDescriptEntry`. -#[derive(Clone, Debug, Default, PartialEq, Eq)] -struct AugmentationData { - lsda: Option<Pointer>, -} - -impl AugmentationData { - fn parse<R: Reader>( - augmentation: &Augmentation, - encoding_parameters: &PointerEncodingParameters<R>, - input: &mut R, - ) -> Result<AugmentationData> { - // In theory, we should be iterating over the original augmentation - // string, interpreting each character, and reading the appropriate bits - // out of the augmentation data as we go. However, the only character - // that defines augmentation data in the FDE is the 'L' character, so we - // can just check for its presence directly. - - let aug_data_len = input.read_uleb128().and_then(R::Offset::from_u64)?; - let rest = &mut input.split(aug_data_len)?; - let mut augmentation_data = AugmentationData::default(); - if let Some(encoding) = augmentation.lsda { - let lsda = parse_encoded_pointer(encoding, encoding_parameters, rest)?; - augmentation_data.lsda = Some(lsda); - } - Ok(augmentation_data) - } -} - -/// > A Common Information Entry holds information that is shared among many -/// > Frame Description Entries. There is at least one CIE in every non-empty -/// > `.debug_frame` section. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct CommonInformationEntry<R, Offset = <R as Reader>::Offset> -where - R: Reader<Offset = Offset>, - Offset: ReaderOffset, -{ - /// The offset of this entry from the start of its containing section. - offset: Offset, - - /// > A constant that gives the number of bytes of the CIE structure, not - /// > including the length field itself (see Section 7.2.2). The size of the - /// > length field plus the value of length must be an integral multiple of - /// > the address size. - length: Offset, - - format: Format, - - /// > A version number (see Section 7.23). This number is specific to the - /// > call frame information and is independent of the DWARF version number. - version: u8, - - /// The parsed augmentation, if any. - augmentation: Option<Augmentation>, - - /// > The size of a target address in this CIE and any FDEs that use it, in - /// > bytes. If a compilation unit exists for this frame, its address size - /// > must match the address size here. - address_size: u8, - - /// "The size of a segment selector in this CIE and any FDEs that use it, in - /// bytes." - segment_size: u8, - - /// "A constant that is factored out of all advance location instructions - /// (see Section 6.4.2.1)." - code_alignment_factor: u64, - - /// > A constant that is factored out of certain offset instructions (see - /// > below). The resulting value is (operand * data_alignment_factor). - data_alignment_factor: i64, - - /// > An unsigned LEB128 constant that indicates which column in the rule - /// > table represents the return address of the function. Note that this - /// > column might not correspond to an actual machine register. - return_address_register: Register, - - /// > A sequence of rules that are interpreted to create the initial setting - /// > of each column in the table. - /// - /// > The default rule for all columns before interpretation of the initial - /// > instructions is the undefined rule. However, an ABI authoring body or a - /// > compilation system authoring body may specify an alternate default - /// > value for any or all columns. - /// - /// This is followed by `DW_CFA_nop` padding until the end of `length` bytes - /// in the input. - initial_instructions: R, -} - -impl<R: Reader> CommonInformationEntry<R> { - fn parse<Section: UnwindSection<R>>( - bases: &BaseAddresses, - section: &Section, - input: &mut R, - ) -> Result<CommonInformationEntry<R>> { - match parse_cfi_entry(bases, section, input)? { - Some(CieOrFde::Cie(cie)) => Ok(cie), - Some(CieOrFde::Fde(_)) => Err(Error::NotCieId), - None => Err(Error::NoEntryAtGivenOffset), - } - } - - fn parse_rest<Section: UnwindSection<R>>( - offset: R::Offset, - length: R::Offset, - format: Format, - bases: &BaseAddresses, - section: &Section, - mut rest: R, - ) -> Result<CommonInformationEntry<R>> { - let version = rest.read_u8()?; - - // Version 1 of `.debug_frame` corresponds to DWARF 2, and then for - // DWARF 3 and 4, I think they decided to just match the standard's - // version. - match version { - 1 | 3 | 4 => (), - _ => return Err(Error::UnknownVersion(u64::from(version))), - } - - let mut augmentation_string = rest.read_null_terminated_slice()?; - - let (address_size, segment_size) = if Section::has_address_and_segment_sizes(version) { - let address_size = rest.read_u8()?; - let segment_size = rest.read_u8()?; - (address_size, segment_size) - } else { - (section.address_size(), section.segment_size()) - }; - - let code_alignment_factor = rest.read_uleb128()?; - let data_alignment_factor = rest.read_sleb128()?; - - let return_address_register = if version == 1 { - Register(rest.read_u8()?.into()) - } else { - rest.read_uleb128().and_then(Register::from_u64)? - }; - - let augmentation = if augmentation_string.is_empty() { - None - } else { - Some(Augmentation::parse( - &mut augmentation_string, - bases, - address_size, - section, - &mut rest, - )?) - }; - - let entry = CommonInformationEntry { - offset, - length, - format, - version, - augmentation, - address_size, - segment_size, - code_alignment_factor, - data_alignment_factor, - return_address_register, - initial_instructions: rest, - }; - - Ok(entry) - } -} - -/// # Signal Safe Methods -/// -/// These methods are guaranteed not to allocate, acquire locks, or perform any -/// other signal-unsafe operations. -impl<R: Reader> CommonInformationEntry<R> { - /// Get the offset of this entry from the start of its containing section. - pub fn offset(&self) -> R::Offset { - self.offset - } - - /// Return the encoding parameters for this CIE. - pub fn encoding(&self) -> Encoding { - Encoding { - format: self.format, - version: u16::from(self.version), - address_size: self.address_size, - } - } - - /// The size of addresses (in bytes) in this CIE. - pub fn address_size(&self) -> u8 { - self.address_size - } - - /// Iterate over this CIE's initial instructions. - /// - /// Can be [used with - /// `FallibleIterator`](./index.html#using-with-fallibleiterator). - pub fn instructions<'a, Section>( - &self, - section: &'a Section, - bases: &'a BaseAddresses, - ) -> CallFrameInstructionIter<'a, R> - where - Section: UnwindSection<R>, - { - CallFrameInstructionIter { - input: self.initial_instructions.clone(), - address_encoding: None, - parameters: PointerEncodingParameters { - bases: &bases.eh_frame, - func_base: None, - address_size: self.address_size, - section: section.section(), - }, - vendor: section.vendor(), - } - } - - /// > A constant that gives the number of bytes of the CIE structure, not - /// > including the length field itself (see Section 7.2.2). The size of the - /// > length field plus the value of length must be an integral multiple of - /// > the address size. - pub fn entry_len(&self) -> R::Offset { - self.length - } - - /// > A version number (see Section 7.23). This number is specific to the - /// > call frame information and is independent of the DWARF version number. - pub fn version(&self) -> u8 { - self.version - } - - /// Get the augmentation data, if any exists. - /// - /// The only augmentation understood by `gimli` is that which is defined by - /// `.eh_frame`. - pub fn augmentation(&self) -> Option<&Augmentation> { - self.augmentation.as_ref() - } - - /// True if this CIE's FDEs have a LSDA. - pub fn has_lsda(&self) -> bool { - self.augmentation.map_or(false, |a| a.lsda.is_some()) - } - - /// Return the encoding of the LSDA address for this CIE's FDEs. - pub fn lsda_encoding(&self) -> Option<constants::DwEhPe> { - self.augmentation.and_then(|a| a.lsda) - } - - /// Return the encoding and address of the personality routine handler - /// for this CIE's FDEs. - pub fn personality_with_encoding(&self) -> Option<(constants::DwEhPe, Pointer)> { - self.augmentation.as_ref().and_then(|a| a.personality) - } - - /// Return the address of the personality routine handler - /// for this CIE's FDEs. - pub fn personality(&self) -> Option<Pointer> { - self.augmentation - .as_ref() - .and_then(|a| a.personality) - .map(|(_, p)| p) - } - - /// Return the encoding of the addresses for this CIE's FDEs. - pub fn fde_address_encoding(&self) -> Option<constants::DwEhPe> { - self.augmentation.and_then(|a| a.fde_address_encoding) - } - - /// True if this CIE's FDEs are trampolines for signal handlers. - pub fn is_signal_trampoline(&self) -> bool { - self.augmentation.map_or(false, |a| a.is_signal_trampoline) - } - - /// > A constant that is factored out of all advance location instructions - /// > (see Section 6.4.2.1). - pub fn code_alignment_factor(&self) -> u64 { - self.code_alignment_factor - } - - /// > A constant that is factored out of certain offset instructions (see - /// > below). The resulting value is (operand * data_alignment_factor). - pub fn data_alignment_factor(&self) -> i64 { - self.data_alignment_factor - } - - /// > An unsigned ... constant that indicates which column in the rule - /// > table represents the return address of the function. Note that this - /// > column might not correspond to an actual machine register. - pub fn return_address_register(&self) -> Register { - self.return_address_register - } -} - -/// A partially parsed `FrameDescriptionEntry`. -/// -/// Fully parsing this FDE requires first parsing its CIE. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct PartialFrameDescriptionEntry<'bases, Section, R> -where - R: Reader, - Section: UnwindSection<R>, -{ - offset: R::Offset, - length: R::Offset, - format: Format, - cie_offset: Section::Offset, - rest: R, - section: Section, - bases: &'bases BaseAddresses, -} - -impl<'bases, Section, R> PartialFrameDescriptionEntry<'bases, Section, R> -where - R: Reader, - Section: UnwindSection<R>, -{ - fn parse_partial( - section: &Section, - bases: &'bases BaseAddresses, - input: &mut R, - ) -> Result<PartialFrameDescriptionEntry<'bases, Section, R>> { - match parse_cfi_entry(bases, section, input)? { - Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer), - Some(CieOrFde::Fde(partial)) => Ok(partial), - None => Err(Error::NoEntryAtGivenOffset), - } - } - - /// Fully parse this FDE. - /// - /// You must provide a function get its associated CIE (either by parsing it - /// on demand, or looking it up in some table mapping offsets to CIEs that - /// you've already parsed, etc.) - pub fn parse<F>(&self, get_cie: F) -> Result<FrameDescriptionEntry<R>> - where - F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>, - { - FrameDescriptionEntry::parse_rest( - self.offset, - self.length, - self.format, - self.cie_offset, - self.rest.clone(), - &self.section, - self.bases, - get_cie, - ) - } - - /// Get the offset of this entry from the start of its containing section. - pub fn offset(&self) -> R::Offset { - self.offset - } - - /// Get the offset of this FDE's CIE. - pub fn cie_offset(&self) -> Section::Offset { - self.cie_offset - } - - /// > A constant that gives the number of bytes of the header and - /// > instruction stream for this function, not including the length field - /// > itself (see Section 7.2.2). The size of the length field plus the value - /// > of length must be an integral multiple of the address size. - pub fn entry_len(&self) -> R::Offset { - self.length - } -} - -/// A `FrameDescriptionEntry` is a set of CFA instructions for an address range. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct FrameDescriptionEntry<R, Offset = <R as Reader>::Offset> -where - R: Reader<Offset = Offset>, - Offset: ReaderOffset, -{ - /// The start of this entry within its containing section. - offset: Offset, - - /// > A constant that gives the number of bytes of the header and - /// > instruction stream for this function, not including the length field - /// > itself (see Section 7.2.2). The size of the length field plus the value - /// > of length must be an integral multiple of the address size. - length: Offset, - - format: Format, - - /// "A constant offset into the .debug_frame section that denotes the CIE - /// that is associated with this FDE." - /// - /// This is the CIE at that offset. - cie: CommonInformationEntry<R, Offset>, - - /// > The address of the first location associated with this table entry. If - /// > the segment_size field of this FDE's CIE is non-zero, the initial - /// > location is preceded by a segment selector of the given length. - initial_segment: u64, - initial_address: u64, - - /// "The number of bytes of program instructions described by this entry." - address_range: u64, - - /// The parsed augmentation data, if we have any. - augmentation: Option<AugmentationData>, - - /// "A sequence of table defining instructions that are described below." - /// - /// This is followed by `DW_CFA_nop` padding until `length` bytes of the - /// input are consumed. - instructions: R, -} - -impl<R: Reader> FrameDescriptionEntry<R> { - fn parse_rest<Section, F>( - offset: R::Offset, - length: R::Offset, - format: Format, - cie_pointer: Section::Offset, - mut rest: R, - section: &Section, - bases: &BaseAddresses, - mut get_cie: F, - ) -> Result<FrameDescriptionEntry<R>> - where - Section: UnwindSection<R>, - F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>, - { - let cie = get_cie(section, bases, cie_pointer)?; - - let initial_segment = if cie.segment_size > 0 { - rest.read_address(cie.segment_size)? - } else { - 0 - }; - - let mut parameters = PointerEncodingParameters { - bases: &bases.eh_frame, - func_base: None, - address_size: cie.address_size, - section: section.section(), - }; - - let (initial_address, address_range) = Self::parse_addresses(&mut rest, &cie, ¶meters)?; - parameters.func_base = Some(initial_address); - - let aug_data = if let Some(ref augmentation) = cie.augmentation { - Some(AugmentationData::parse( - augmentation, - ¶meters, - &mut rest, - )?) - } else { - None - }; - - let entry = FrameDescriptionEntry { - offset, - length, - format, - cie, - initial_segment, - initial_address, - address_range, - augmentation: aug_data, - instructions: rest, - }; - - Ok(entry) - } - - fn parse_addresses( - input: &mut R, - cie: &CommonInformationEntry<R>, - parameters: &PointerEncodingParameters<R>, - ) -> Result<(u64, u64)> { - let encoding = cie.augmentation().and_then(|a| a.fde_address_encoding); - if let Some(encoding) = encoding { - let initial_address = parse_encoded_pointer(encoding, parameters, input)?; - - // Ignore indirection. - let initial_address = initial_address.pointer(); - - // Address ranges cannot be relative to anything, so just grab the - // data format bits from the encoding. - let address_range = parse_encoded_pointer(encoding.format(), parameters, input)?; - Ok((initial_address, address_range.pointer())) - } else { - let initial_address = input.read_address(cie.address_size)?; - let address_range = input.read_address(cie.address_size)?; - Ok((initial_address, address_range)) - } - } - - /// Return the table of unwind information for this FDE. - #[inline] - pub fn rows<'a, 'ctx, Section: UnwindSection<R>, A: UnwindContextStorage<R>>( - &self, - section: &'a Section, - bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext<R, A>, - ) -> Result<UnwindTable<'a, 'ctx, R, A>> { - UnwindTable::new(section, bases, 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<'ctx, Section: UnwindSection<R>, A: UnwindContextStorage<R>>( - &self, - section: &Section, - bases: &BaseAddresses, - ctx: &'ctx mut UnwindContext<R, A>, - address: u64, - ) -> Result<&'ctx UnwindTableRow<R, A>> { - let mut table = self.rows(section, bases, ctx)?; - while let Some(row) = table.next_row()? { - if row.contains(address) { - return Ok(table.ctx.row()); - } - } - Err(Error::NoUnwindInfoForAddress) - } -} - -/// # Signal Safe Methods -/// -/// These methods are guaranteed not to allocate, acquire locks, or perform any -/// other signal-unsafe operations. -#[allow(clippy::len_without_is_empty)] -impl<R: Reader> FrameDescriptionEntry<R> { - /// Get the offset of this entry from the start of its containing section. - pub fn offset(&self) -> R::Offset { - self.offset - } - - /// Get a reference to this FDE's CIE. - pub fn cie(&self) -> &CommonInformationEntry<R> { - &self.cie - } - - /// > A constant that gives the number of bytes of the header and - /// > instruction stream for this function, not including the length field - /// > itself (see Section 7.2.2). The size of the length field plus the value - /// > of length must be an integral multiple of the address size. - pub fn entry_len(&self) -> R::Offset { - self.length - } - - /// Iterate over this FDE's instructions. - /// - /// Will not include the CIE's initial instructions, if you want those do - /// `fde.cie().instructions()` first. - /// - /// Can be [used with - /// `FallibleIterator`](./index.html#using-with-fallibleiterator). - pub fn instructions<'a, Section>( - &self, - section: &'a Section, - bases: &'a BaseAddresses, - ) -> CallFrameInstructionIter<'a, R> - where - Section: UnwindSection<R>, - { - CallFrameInstructionIter { - input: self.instructions.clone(), - address_encoding: self.cie.augmentation().and_then(|a| a.fde_address_encoding), - parameters: PointerEncodingParameters { - bases: &bases.eh_frame, - func_base: None, - address_size: self.cie.address_size, - section: section.section(), - }, - vendor: section.vendor(), - } - } - - /// The first address for which this entry has unwind information for. - pub fn initial_address(&self) -> u64 { - self.initial_address - } - - /// The number of bytes of instructions that this entry has unwind - /// information for. - pub fn len(&self) -> u64 { - self.address_range - } - - /// Return `true` if the given address is within this FDE, `false` - /// otherwise. - /// - /// This is equivalent to `entry.initial_address() <= address < - /// entry.initial_address() + entry.len()`. - pub fn contains(&self, address: u64) -> bool { - let start = self.initial_address(); - let end = start + self.len(); - start <= address && address < end - } - - /// The address of this FDE's language-specific data area (LSDA), if it has - /// any. - pub fn lsda(&self) -> Option<Pointer> { - self.augmentation.as_ref().and_then(|a| a.lsda) - } - - /// Return true if this FDE's function is a trampoline for a signal handler. - #[inline] - pub fn is_signal_trampoline(&self) -> bool { - self.cie().is_signal_trampoline() - } - - /// Return the address of the FDE's function's personality routine - /// handler. The personality routine does language-specific clean up when - /// unwinding the stack frames with the intent to not run them again. - #[inline] - pub fn personality(&self) -> Option<Pointer> { - self.cie().personality() - } -} - -/// Specification of what storage should be used for [`UnwindContext`]. -/// -#[cfg_attr( - feature = "read", - doc = " -Normally you would only need to use [`StoreOnHeap`], which places the stack -on the heap using [`Vec`]. This is the default storage type parameter for [`UnwindContext`]. -" -)] -/// -/// If you need to avoid [`UnwindContext`] from allocating memory, e.g. for signal safety, -/// you can provide you own storage specification: -/// ```rust,no_run -/// # use gimli::*; -/// # -/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>) -/// # -> gimli::Result<()> { -/// # let eh_frame: gimli::EhFrame<_> = unreachable!(); -/// # let bases = unimplemented!(); -/// # -/// struct StoreOnStack; -/// -/// impl<R: Reader> UnwindContextStorage<R> for StoreOnStack { -/// type Rules = [(Register, RegisterRule<R>); 192]; -/// type Stack = [UnwindTableRow<R, Self>; 4]; -/// } -/// -/// let mut ctx = UnwindContext::<_, StoreOnStack>::new_in(); -/// -/// // Initialize the context by evaluating the CIE's initial instruction program, -/// // and generate the unwind table. -/// let mut table = some_fde.rows(&eh_frame, &bases, &mut ctx)?; -/// while let Some(row) = table.next_row()? { -/// // Do stuff with each row... -/// # let _ = row; -/// } -/// # unreachable!() -/// # } -/// ``` -pub trait UnwindContextStorage<R: Reader>: Sized { - /// The storage used for register rules in a unwind table row. - /// - /// Note that this is nested within the stack. - type Rules: ArrayLike<Item = (Register, RegisterRule<R>)>; - - /// The storage used for unwind table row stack. - type Stack: ArrayLike<Item = UnwindTableRow<R, Self>>; -} - -#[cfg(feature = "read")] -const MAX_RULES: usize = 192; -#[cfg(feature = "read")] -const MAX_UNWIND_STACK_DEPTH: usize = 4; - -#[cfg(feature = "read")] -impl<R: Reader> UnwindContextStorage<R> for StoreOnHeap { - type Rules = [(Register, RegisterRule<R>); MAX_RULES]; - type Stack = Box<[UnwindTableRow<R, Self>; MAX_UNWIND_STACK_DEPTH]>; -} - -/// Common context needed when evaluating the call frame unwinding information. -/// -/// This structure can be large so it is advisable to place it on the heap. -/// To avoid re-allocating the context multiple times when evaluating multiple -/// CFI programs, it can be reused. -/// -/// ``` -/// use gimli::{UnwindContext, UnwindTable}; -/// -/// # fn foo<'a>(some_fde: gimli::FrameDescriptionEntry<gimli::EndianSlice<'a, gimli::LittleEndian>>) -/// # -> gimli::Result<()> { -/// # let eh_frame: gimli::EhFrame<_> = unreachable!(); -/// # let bases = unimplemented!(); -/// // An uninitialized context. -/// let mut ctx = Box::new(UnwindContext::new()); -/// -/// // Initialize the context by evaluating the CIE's initial instruction program, -/// // and generate the unwind table. -/// let mut table = some_fde.rows(&eh_frame, &bases, &mut ctx)?; -/// while let Some(row) = table.next_row()? { -/// // Do stuff with each row... -/// # let _ = row; -/// } -/// # unreachable!() -/// # } -/// ``` -#[derive(Clone, PartialEq, Eq)] -pub struct UnwindContext<R: Reader, A: UnwindContextStorage<R> = StoreOnHeap> { - // Stack of rows. The last row is the row currently being built by the - // program. There is always at least one row. The vast majority of CFI - // programs will only ever have one row on the stack. - stack: ArrayVec<A::Stack>, - - // If we are evaluating an FDE's instructions, then `is_initialized` will be - // `true`. If `initial_rule` is `Some`, then the initial register rules are either - // all default rules or have just 1 non-default rule, stored in `initial_rule`. - // If it's `None`, `stack[0]` will contain the initial register rules - // described by the CIE's initial instructions. These rules are used by - // `DW_CFA_restore`. Otherwise, when we are currently evaluating a CIE's - // initial instructions, `is_initialized` will be `false` and initial rules - // cannot be read. - initial_rule: Option<(Register, RegisterRule<R>)>, - - is_initialized: bool, -} - -impl<R: Reader, S: UnwindContextStorage<R>> Debug for UnwindContext<R, S> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("UnwindContext") - .field("stack", &self.stack) - .field("initial_rule", &self.initial_rule) - .field("is_initialized", &self.is_initialized) - .finish() - } -} - -impl<R: Reader, A: UnwindContextStorage<R>> Default for UnwindContext<R, A> { - fn default() -> Self { - Self::new_in() - } -} - -#[cfg(feature = "read")] -impl<R: Reader> UnwindContext<R> { - /// Construct a new call frame unwinding context. - pub fn new() -> Self { - Self::new_in() - } -} - -/// # Signal Safe Methods -/// -/// These methods are guaranteed not to allocate, acquire locks, or perform any -/// other signal-unsafe operations, if an non-allocating storage is used. -impl<R: Reader, A: UnwindContextStorage<R>> UnwindContext<R, A> { - /// Construct a new call frame unwinding context. - pub fn new_in() -> Self { - let mut ctx = UnwindContext { - stack: Default::default(), - initial_rule: None, - is_initialized: false, - }; - ctx.reset(); - ctx - } - - /// Run the CIE's initial instructions and initialize this `UnwindContext`. - fn initialize<Section: UnwindSection<R>>( - &mut self, - section: &Section, - bases: &BaseAddresses, - cie: &CommonInformationEntry<R>, - ) -> Result<()> { - // Always reset because previous initialization failure may leave dirty state. - self.reset(); - - let mut table = UnwindTable::new_for_cie(section, bases, self, cie); - while table.next_row()?.is_some() {} - - self.save_initial_rules()?; - Ok(()) - } - - fn reset(&mut self) { - self.stack.clear(); - self.stack.try_push(UnwindTableRow::default()).unwrap(); - debug_assert!(self.stack[0].is_default()); - self.initial_rule = None; - self.is_initialized = false; - } - - fn row(&self) -> &UnwindTableRow<R, A> { - self.stack.last().unwrap() - } - - fn row_mut(&mut self) -> &mut UnwindTableRow<R, A> { - self.stack.last_mut().unwrap() - } - - fn save_initial_rules(&mut self) -> Result<()> { - debug_assert!(!self.is_initialized); - self.initial_rule = match *self.stack.last().unwrap().registers.rules { - // All rules are default (undefined). In this case just synthesize - // an undefined rule. - [] => Some((Register(0), RegisterRule::Undefined)), - [ref rule] => Some(rule.clone()), - _ => { - let rules = self.stack.last().unwrap().clone(); - self.stack - .try_insert(0, rules) - .map_err(|_| Error::StackFull)?; - None - } - }; - self.is_initialized = true; - Ok(()) - } - - fn start_address(&self) -> u64 { - self.row().start_address - } - - fn set_start_address(&mut self, start_address: u64) { - let row = self.row_mut(); - row.start_address = start_address; - } - - fn set_register_rule(&mut self, register: Register, rule: RegisterRule<R>) -> Result<()> { - let row = self.row_mut(); - row.registers.set(register, rule) - } - - /// Returns `None` if we have not completed evaluation of a CIE's initial - /// instructions. - fn get_initial_rule(&self, register: Register) -> Option<RegisterRule<R>> { - if !self.is_initialized { - return None; - } - Some(match self.initial_rule { - None => self.stack[0].registers.get(register), - Some((r, ref rule)) if r == register => rule.clone(), - _ => RegisterRule::Undefined, - }) - } - - fn set_cfa(&mut self, cfa: CfaRule<R>) { - self.row_mut().cfa = cfa; - } - - fn cfa_mut(&mut self) -> &mut CfaRule<R> { - &mut self.row_mut().cfa - } - - fn push_row(&mut self) -> Result<()> { - let new_row = self.row().clone(); - self.stack.try_push(new_row).map_err(|_| Error::StackFull) - } - - fn pop_row(&mut self) -> Result<()> { - let min_size = if self.is_initialized && self.initial_rule.is_none() { - 2 - } else { - 1 - }; - if self.stack.len() <= min_size { - return Err(Error::PopWithEmptyStack); - } - self.stack.pop().unwrap(); - Ok(()) - } -} - -/// The `UnwindTable` iteratively evaluates a `FrameDescriptionEntry`'s -/// `CallFrameInstruction` program, yielding the each row one at a time. -/// -/// > 6.4.1 Structure of Call Frame Information -/// > -/// > DWARF supports virtual unwinding by defining an architecture independent -/// > basis for recording how procedures save and restore registers during their -/// > lifetimes. This basis must be augmented on some machines with specific -/// > information that is defined by an architecture specific ABI authoring -/// > committee, a hardware vendor, or a compiler producer. The body defining a -/// > specific augmentation is referred to below as the “augmenter.” -/// > -/// > Abstractly, this mechanism describes a very large table that has the -/// > following structure: -/// > -/// > <table> -/// > <tr> -/// > <th>LOC</th><th>CFA</th><th>R0</th><th>R1</th><td>...</td><th>RN</th> -/// > </tr> -/// > <tr> -/// > <th>L0</th> <td></td> <td></td> <td></td> <td></td> <td></td> -/// > </tr> -/// > <tr> -/// > <th>L1</th> <td></td> <td></td> <td></td> <td></td> <td></td> -/// > </tr> -/// > <tr> -/// > <td>...</td><td></td> <td></td> <td></td> <td></td> <td></td> -/// > </tr> -/// > <tr> -/// > <th>LN</th> <td></td> <td></td> <td></td> <td></td> <td></td> -/// > </tr> -/// > </table> -/// > -/// > The first column indicates an address for every location that contains code -/// > in a program. (In shared objects, this is an object-relative offset.) The -/// > remaining columns contain virtual unwinding rules that are associated with -/// > the indicated location. -/// > -/// > The CFA column defines the rule which computes the Canonical Frame Address -/// > value; it may be either a register and a signed offset that are added -/// > together, or a DWARF expression that is evaluated. -/// > -/// > The remaining columns are labeled by register number. This includes some -/// > registers that have special designation on some architectures such as the PC -/// > and the stack pointer register. (The actual mapping of registers for a -/// > particular architecture is defined by the augmenter.) The register columns -/// > contain rules that describe whether a given register has been saved and the -/// > rule to find the value for the register in the previous frame. -/// > -/// > ... -/// > -/// > This table would be extremely large if actually constructed as -/// > described. Most of the entries at any point in the table are identical to -/// > the ones above them. The whole table can be represented quite compactly by -/// > recording just the differences starting at the beginning address of each -/// > subroutine in the program. -#[derive(Debug)] -pub struct UnwindTable<'a, 'ctx, R: Reader, A: UnwindContextStorage<R> = StoreOnHeap> { - code_alignment_factor: Wrapping<u64>, - data_alignment_factor: Wrapping<i64>, - next_start_address: u64, - last_end_address: u64, - returned_last_row: bool, - current_row_valid: bool, - instructions: CallFrameInstructionIter<'a, R>, - ctx: &'ctx mut UnwindContext<R, A>, -} - -/// # Signal Safe Methods -/// -/// These methods are guaranteed not to allocate, acquire locks, or perform any -/// other signal-unsafe operations. -impl<'a, 'ctx, R: Reader, A: UnwindContextStorage<R>> UnwindTable<'a, 'ctx, R, A> { - /// Construct a new `UnwindTable` for the given - /// `FrameDescriptionEntry`'s CFI unwinding program. - pub fn new<Section: UnwindSection<R>>( - section: &'a Section, - bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext<R, A>, - fde: &FrameDescriptionEntry<R>, - ) -> Result<Self> { - ctx.initialize(section, bases, fde.cie())?; - Ok(Self::new_for_fde(section, bases, ctx, fde)) - } - - fn new_for_fde<Section: UnwindSection<R>>( - section: &'a Section, - bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext<R, A>, - fde: &FrameDescriptionEntry<R>, - ) -> Self { - assert!(ctx.stack.len() >= 1); - UnwindTable { - code_alignment_factor: Wrapping(fde.cie().code_alignment_factor()), - data_alignment_factor: Wrapping(fde.cie().data_alignment_factor()), - next_start_address: fde.initial_address(), - last_end_address: fde.initial_address().wrapping_add(fde.len()), - returned_last_row: false, - current_row_valid: false, - instructions: fde.instructions(section, bases), - ctx, - } - } - - fn new_for_cie<Section: UnwindSection<R>>( - section: &'a Section, - bases: &'a BaseAddresses, - ctx: &'ctx mut UnwindContext<R, A>, - cie: &CommonInformationEntry<R>, - ) -> Self { - assert!(ctx.stack.len() >= 1); - UnwindTable { - code_alignment_factor: Wrapping(cie.code_alignment_factor()), - data_alignment_factor: Wrapping(cie.data_alignment_factor()), - next_start_address: 0, - last_end_address: 0, - returned_last_row: false, - current_row_valid: false, - instructions: cie.instructions(section, bases), - ctx, - } - } - - /// Evaluate call frame instructions until the next row of the table is - /// completed, and return it. - /// - /// Unfortunately, this cannot be used with `FallibleIterator` because of - /// the restricted lifetime of the yielded item. - pub fn next_row(&mut self) -> Result<Option<&UnwindTableRow<R, A>>> { - assert!(self.ctx.stack.len() >= 1); - self.ctx.set_start_address(self.next_start_address); - self.current_row_valid = false; - - loop { - match self.instructions.next() { - Err(e) => return Err(e), - - Ok(None) => { - if self.returned_last_row { - return Ok(None); - } - - let row = self.ctx.row_mut(); - row.end_address = self.last_end_address; - - self.returned_last_row = true; - self.current_row_valid = true; - return Ok(Some(row)); - } - - Ok(Some(instruction)) => { - if self.evaluate(instruction)? { - self.current_row_valid = true; - return Ok(Some(self.ctx.row())); - } - } - }; - } - } - - /// Returns the current row with the lifetime of the context. - pub fn into_current_row(self) -> Option<&'ctx UnwindTableRow<R, A>> { - if self.current_row_valid { - Some(self.ctx.row()) - } else { - None - } - } - - /// Evaluate one call frame instruction. Return `Ok(true)` if the row is - /// complete, `Ok(false)` otherwise. - fn evaluate(&mut self, instruction: CallFrameInstruction<R>) -> Result<bool> { - use crate::CallFrameInstruction::*; - - match instruction { - // Instructions that complete the current row and advance the - // address for the next row. - SetLoc { address } => { - if address < self.ctx.start_address() { - return Err(Error::InvalidAddressRange); - } - - self.next_start_address = address; - self.ctx.row_mut().end_address = self.next_start_address; - return Ok(true); - } - AdvanceLoc { delta } => { - let delta = Wrapping(u64::from(delta)) * self.code_alignment_factor; - self.next_start_address = (Wrapping(self.ctx.start_address()) + delta).0; - self.ctx.row_mut().end_address = self.next_start_address; - return Ok(true); - } - - // Instructions that modify the CFA. - DefCfa { register, offset } => { - self.ctx.set_cfa(CfaRule::RegisterAndOffset { - register, - offset: offset as i64, - }); - } - DefCfaSf { - register, - factored_offset, - } => { - let data_align = self.data_alignment_factor; - self.ctx.set_cfa(CfaRule::RegisterAndOffset { - register, - offset: (Wrapping(factored_offset) * data_align).0, - }); - } - DefCfaRegister { register } => { - if let CfaRule::RegisterAndOffset { - register: ref mut reg, - .. - } = *self.ctx.cfa_mut() - { - *reg = register; - } else { - return Err(Error::CfiInstructionInInvalidContext); - } - } - DefCfaOffset { offset } => { - if let CfaRule::RegisterAndOffset { - offset: ref mut off, - .. - } = *self.ctx.cfa_mut() - { - *off = offset as i64; - } else { - return Err(Error::CfiInstructionInInvalidContext); - } - } - DefCfaOffsetSf { factored_offset } => { - if let CfaRule::RegisterAndOffset { - offset: ref mut off, - .. - } = *self.ctx.cfa_mut() - { - let data_align = self.data_alignment_factor; - *off = (Wrapping(factored_offset) * data_align).0; - } else { - return Err(Error::CfiInstructionInInvalidContext); - } - } - DefCfaExpression { expression } => { - self.ctx.set_cfa(CfaRule::Expression(expression)); - } - - // Instructions that define register rules. - Undefined { register } => { - self.ctx - .set_register_rule(register, RegisterRule::Undefined)?; - } - SameValue { register } => { - self.ctx - .set_register_rule(register, RegisterRule::SameValue)?; - } - Offset { - register, - factored_offset, - } => { - let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; - self.ctx - .set_register_rule(register, RegisterRule::Offset(offset.0))?; - } - OffsetExtendedSf { - register, - factored_offset, - } => { - let offset = Wrapping(factored_offset) * self.data_alignment_factor; - self.ctx - .set_register_rule(register, RegisterRule::Offset(offset.0))?; - } - ValOffset { - register, - factored_offset, - } => { - let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor; - self.ctx - .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; - } - ValOffsetSf { - register, - factored_offset, - } => { - let offset = Wrapping(factored_offset) * self.data_alignment_factor; - self.ctx - .set_register_rule(register, RegisterRule::ValOffset(offset.0))?; - } - Register { - dest_register, - src_register, - } => { - self.ctx - .set_register_rule(dest_register, RegisterRule::Register(src_register))?; - } - Expression { - register, - expression, - } => { - let expression = RegisterRule::Expression(expression); - self.ctx.set_register_rule(register, expression)?; - } - ValExpression { - register, - expression, - } => { - let expression = RegisterRule::ValExpression(expression); - self.ctx.set_register_rule(register, expression)?; - } - Restore { register } => { - let initial_rule = if let Some(rule) = self.ctx.get_initial_rule(register) { - rule - } else { - // Can't restore the initial rule when we are - // evaluating the initial rules! - return Err(Error::CfiInstructionInInvalidContext); - }; - - self.ctx.set_register_rule(register, initial_rule)?; - } - - // Row push and pop instructions. - RememberState => { - self.ctx.push_row()?; - } - RestoreState => { - // Pop state while preserving current location. - let start_address = self.ctx.start_address(); - self.ctx.pop_row()?; - self.ctx.set_start_address(start_address); - } - - // GNU Extension. Save the size somewhere so the unwinder can use - // it when restoring IP - ArgsSize { size } => { - self.ctx.row_mut().saved_args_size = size; - } - - // AArch64 extension. - NegateRaState => { - let register = crate::AArch64::RA_SIGN_STATE; - let value = match self.ctx.row().register(register) { - RegisterRule::Undefined => 0, - RegisterRule::Constant(value) => value, - _ => return Err(Error::CfiInstructionInInvalidContext), - }; - self.ctx - .set_register_rule(register, RegisterRule::Constant(value ^ 1))?; - } - - // No operation. - Nop => {} - }; - - Ok(false) - } -} - -// We tend to have very few register rules: usually only a couple. Even if we -// have a rule for every register, on x86-64 with SSE and everything we're -// talking about ~100 rules. So rather than keeping the rules in a hash map, or -// a vector indexed by register number (which would lead to filling lots of -// empty entries), we store them as a vec of (register number, register rule) -// pairs. -// -// Additionally, because every register's default rule is implicitly -// `RegisterRule::Undefined`, we never store a register's rule in this vec if it -// is undefined and save a little bit more space and do a little fewer -// comparisons that way. -// -// The maximum number of rules preallocated by libunwind is 97 for AArch64, 128 -// for ARM, and even 188 for MIPS. It is extremely unlikely to encounter this -// many register rules in practice. -// -// See: -// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-x86_64/dwarf-config.h#L36 -// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-aarch64/dwarf-config.h#L32 -// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-arm/dwarf-config.h#L31 -// - https://github.com/libunwind/libunwind/blob/11fd461095ea98f4b3e3a361f5a8a558519363fa/include/tdep-mips/dwarf-config.h#L31 -struct RegisterRuleMap<R: Reader, S: UnwindContextStorage<R> = StoreOnHeap> { - rules: ArrayVec<S::Rules>, -} - -impl<R: Reader, S: UnwindContextStorage<R>> Debug for RegisterRuleMap<R, S> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RegisterRuleMap") - .field("rules", &self.rules) - .finish() - } -} - -impl<R: Reader, S: UnwindContextStorage<R>> Clone for RegisterRuleMap<R, S> { - fn clone(&self) -> Self { - Self { - rules: self.rules.clone(), - } - } -} - -impl<R: Reader, S: UnwindContextStorage<R>> Default for RegisterRuleMap<R, S> { - fn default() -> Self { - RegisterRuleMap { - rules: Default::default(), - } - } -} - -/// # Signal Safe Methods -/// -/// These methods are guaranteed not to allocate, acquire locks, or perform any -/// other signal-unsafe operations. -impl<R: Reader, S: UnwindContextStorage<R>> RegisterRuleMap<R, S> { - fn is_default(&self) -> bool { - self.rules.is_empty() - } - - fn get(&self, register: Register) -> RegisterRule<R> { - self.rules - .iter() - .find(|rule| rule.0 == register) - .map(|r| { - debug_assert!(r.1.is_defined()); - r.1.clone() - }) - .unwrap_or(RegisterRule::Undefined) - } - - fn set(&mut self, register: Register, rule: RegisterRule<R>) -> Result<()> { - if !rule.is_defined() { - let idx = self - .rules - .iter() - .enumerate() - .find(|&(_, r)| r.0 == register) - .map(|(i, _)| i); - if let Some(idx) = idx { - self.rules.swap_remove(idx); - } - return Ok(()); - } - - for &mut (reg, ref mut old_rule) in &mut *self.rules { - debug_assert!(old_rule.is_defined()); - if reg == register { - *old_rule = rule; - return Ok(()); - } - } - - self.rules - .try_push((register, rule)) - .map_err(|_| Error::TooManyRegisterRules) - } - - fn iter(&self) -> RegisterRuleIter<R> { - RegisterRuleIter(self.rules.iter()) - } -} - -impl<'a, R, S: UnwindContextStorage<R>> FromIterator<&'a (Register, RegisterRule<R>)> - for RegisterRuleMap<R, S> -where - R: 'a + Reader, -{ - fn from_iter<T>(iter: T) -> Self - where - T: IntoIterator<Item = &'a (Register, RegisterRule<R>)>, - { - let iter = iter.into_iter(); - let mut rules = RegisterRuleMap::default(); - for &(reg, ref rule) in iter.filter(|r| r.1.is_defined()) { - rules.set(reg, rule.clone()).expect( - "This is only used in tests, impl isn't exposed publicly. - If you trip this, fix your test", - ); - } - rules - } -} - -impl<R, S: UnwindContextStorage<R>> PartialEq for RegisterRuleMap<R, S> -where - R: Reader + PartialEq, -{ - fn eq(&self, rhs: &Self) -> bool { - for &(reg, ref rule) in &*self.rules { - debug_assert!(rule.is_defined()); - if *rule != rhs.get(reg) { - return false; - } - } - - for &(reg, ref rhs_rule) in &*rhs.rules { - debug_assert!(rhs_rule.is_defined()); - if *rhs_rule != self.get(reg) { - return false; - } - } - - true - } -} - -impl<R, S: UnwindContextStorage<R>> Eq for RegisterRuleMap<R, S> where R: Reader + Eq {} - -/// An unordered iterator for register rules. -#[derive(Debug, Clone)] -pub struct RegisterRuleIter<'iter, R>(::core::slice::Iter<'iter, (Register, RegisterRule<R>)>) -where - R: Reader; - -impl<'iter, R: Reader> Iterator for RegisterRuleIter<'iter, R> { - type Item = &'iter (Register, RegisterRule<R>); - - fn next(&mut self) -> Option<Self::Item> { - self.0.next() - } -} - -/// A row in the virtual unwind table that describes how to find the values of -/// the registers in the *previous* frame for a range of PC addresses. -#[derive(PartialEq, Eq)] -pub struct UnwindTableRow<R: Reader, S: UnwindContextStorage<R> = StoreOnHeap> { - start_address: u64, - end_address: u64, - saved_args_size: u64, - cfa: CfaRule<R>, - registers: RegisterRuleMap<R, S>, -} - -impl<R: Reader, S: UnwindContextStorage<R>> Debug for UnwindTableRow<R, S> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("UnwindTableRow") - .field("start_address", &self.start_address) - .field("end_address", &self.end_address) - .field("saved_args_size", &self.saved_args_size) - .field("cfa", &self.cfa) - .field("registers", &self.registers) - .finish() - } -} - -impl<R: Reader, S: UnwindContextStorage<R>> Clone for UnwindTableRow<R, S> { - fn clone(&self) -> Self { - Self { - start_address: self.start_address, - end_address: self.end_address, - saved_args_size: self.saved_args_size, - cfa: self.cfa.clone(), - registers: self.registers.clone(), - } - } -} - -impl<R: Reader, S: UnwindContextStorage<R>> Default for UnwindTableRow<R, S> { - fn default() -> Self { - UnwindTableRow { - start_address: 0, - end_address: 0, - saved_args_size: 0, - cfa: Default::default(), - registers: Default::default(), - } - } -} - -impl<R: Reader, S: UnwindContextStorage<R>> UnwindTableRow<R, S> { - fn is_default(&self) -> bool { - self.start_address == 0 - && self.end_address == 0 - && self.cfa.is_default() - && self.registers.is_default() - } - - /// Get the starting PC address that this row applies to. - pub fn start_address(&self) -> u64 { - self.start_address - } - - /// Get the end PC address where this row's register rules become - /// unapplicable. - /// - /// In other words, this row describes how to recover the last frame's - /// registers for all PCs where `row.start_address() <= PC < - /// row.end_address()`. This row does NOT describe how to recover registers - /// when `PC == row.end_address()`. - pub fn end_address(&self) -> u64 { - self.end_address - } - - /// Return `true` if the given `address` is within this row's address range, - /// `false` otherwise. - pub fn contains(&self, address: u64) -> bool { - self.start_address <= address && address < self.end_address - } - - /// Returns the amount of args currently on the stack. - /// - /// When unwinding, if the personality function requested a change in IP, - /// the SP needs to be adjusted by saved_args_size. - pub fn saved_args_size(&self) -> u64 { - self.saved_args_size - } - - /// Get the canonical frame address (CFA) recovery rule for this row. - pub fn cfa(&self) -> &CfaRule<R> { - &self.cfa - } - - /// Get the register recovery rule for the given register number. - /// - /// The register number mapping is architecture dependent. For example, in - /// the x86-64 ABI the register number mapping is defined in Figure 3.36: - /// - /// > Figure 3.36: DWARF Register Number Mapping - /// > - /// > <table> - /// > <tr><th>Register Name</th> <th>Number</th> <th>Abbreviation</th></tr> - /// > <tr><td>General Purpose Register RAX</td> <td>0</td> <td>%rax</td></tr> - /// > <tr><td>General Purpose Register RDX</td> <td>1</td> <td>%rdx</td></tr> - /// > <tr><td>General Purpose Register RCX</td> <td>2</td> <td>%rcx</td></tr> - /// > <tr><td>General Purpose Register RBX</td> <td>3</td> <td>%rbx</td></tr> - /// > <tr><td>General Purpose Register RSI</td> <td>4</td> <td>%rsi</td></tr> - /// > <tr><td>General Purpose Register RDI</td> <td>5</td> <td>%rdi</td></tr> - /// > <tr><td>General Purpose Register RBP</td> <td>6</td> <td>%rbp</td></tr> - /// > <tr><td>Stack Pointer Register RSP</td> <td>7</td> <td>%rsp</td></tr> - /// > <tr><td>Extended Integer Registers 8-15</td> <td>8-15</td> <td>%r8-%r15</td></tr> - /// > <tr><td>Return Address RA</td> <td>16</td> <td></td></tr> - /// > <tr><td>Vector Registers 0–7</td> <td>17-24</td> <td>%xmm0–%xmm7</td></tr> - /// > <tr><td>Extended Vector Registers 8–15</td> <td>25-32</td> <td>%xmm8–%xmm15</td></tr> - /// > <tr><td>Floating Point Registers 0–7</td> <td>33-40</td> <td>%st0–%st7</td></tr> - /// > <tr><td>MMX Registers 0–7</td> <td>41-48</td> <td>%mm0–%mm7</td></tr> - /// > <tr><td>Flag Register</td> <td>49</td> <td>%rFLAGS</td></tr> - /// > <tr><td>Segment Register ES</td> <td>50</td> <td>%es</td></tr> - /// > <tr><td>Segment Register CS</td> <td>51</td> <td>%cs</td></tr> - /// > <tr><td>Segment Register SS</td> <td>52</td> <td>%ss</td></tr> - /// > <tr><td>Segment Register DS</td> <td>53</td> <td>%ds</td></tr> - /// > <tr><td>Segment Register FS</td> <td>54</td> <td>%fs</td></tr> - /// > <tr><td>Segment Register GS</td> <td>55</td> <td>%gs</td></tr> - /// > <tr><td>Reserved</td> <td>56-57</td> <td></td></tr> - /// > <tr><td>FS Base address</td> <td>58</td> <td>%fs.base</td></tr> - /// > <tr><td>GS Base address</td> <td>59</td> <td>%gs.base</td></tr> - /// > <tr><td>Reserved</td> <td>60-61</td> <td></td></tr> - /// > <tr><td>Task Register</td> <td>62</td> <td>%tr</td></tr> - /// > <tr><td>LDT Register</td> <td>63</td> <td>%ldtr</td></tr> - /// > <tr><td>128-bit Media Control and Status</td> <td>64</td> <td>%mxcsr</td></tr> - /// > <tr><td>x87 Control Word</td> <td>65</td> <td>%fcw</td></tr> - /// > <tr><td>x87 Status Word</td> <td>66</td> <td>%fsw</td></tr> - /// > <tr><td>Upper Vector Registers 16–31</td> <td>67-82</td> <td>%xmm16–%xmm31</td></tr> - /// > <tr><td>Reserved</td> <td>83-117</td> <td></td></tr> - /// > <tr><td>Vector Mask Registers 0–7</td> <td>118-125</td> <td>%k0–%k7</td></tr> - /// > <tr><td>Reserved</td> <td>126-129</td> <td></td></tr> - /// > </table> - pub fn register(&self, register: Register) -> RegisterRule<R> { - self.registers.get(register) - } - - /// Iterate over all defined register `(number, rule)` pairs. - /// - /// The rules are not iterated in any guaranteed order. Any register that - /// does not make an appearance in the iterator implicitly has the rule - /// `RegisterRule::Undefined`. - /// - /// ``` - /// # use gimli::{EndianSlice, LittleEndian, UnwindTableRow}; - /// # fn foo<'input>(unwind_table_row: UnwindTableRow<EndianSlice<'input, LittleEndian>>) { - /// for &(register, ref rule) in unwind_table_row.registers() { - /// // ... - /// # drop(register); drop(rule); - /// } - /// # } - /// ``` - pub fn registers(&self) -> RegisterRuleIter<R> { - self.registers.iter() - } -} - -/// The canonical frame address (CFA) recovery rules. -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum CfaRule<R: Reader> { - /// The CFA is given offset from the given register's value. - RegisterAndOffset { - /// The register containing the base value. - register: Register, - /// The offset from the register's base value. - offset: i64, - }, - /// The CFA is obtained by evaluating this `Reader` as a DWARF expression - /// program. - Expression(Expression<R>), -} - -impl<R: Reader> Default for CfaRule<R> { - fn default() -> Self { - CfaRule::RegisterAndOffset { - register: Register(0), - offset: 0, - } - } -} - -impl<R: Reader> CfaRule<R> { - fn is_default(&self) -> bool { - match *self { - CfaRule::RegisterAndOffset { register, offset } => { - register == Register(0) && offset == 0 - } - _ => false, - } - } -} - -/// An entry in the abstract CFI table that describes how to find the value of a -/// register. -/// -/// "The register columns contain rules that describe whether a given register -/// has been saved and the rule to find the value for the register in the -/// previous frame." -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum RegisterRule<R: Reader> { - /// > A register that has this rule has no recoverable value in the previous - /// > frame. (By convention, it is not preserved by a callee.) - Undefined, - - /// > This register has not been modified from the previous frame. (By - /// > convention, it is preserved by the callee, but the callee has not - /// > modified it.) - SameValue, - - /// "The previous value of this register is saved at the address CFA+N where - /// CFA is the current CFA value and N is a signed offset." - Offset(i64), - - /// "The previous value of this register is the value CFA+N where CFA is the - /// current CFA value and N is a signed offset." - ValOffset(i64), - - /// "The previous value of this register is stored in another register - /// numbered R." - Register(Register), - - /// "The previous value of this register is located at the address produced - /// by executing the DWARF expression." - Expression(Expression<R>), - - /// "The previous value of this register is the value produced by executing - /// the DWARF expression." - ValExpression(Expression<R>), - - /// "The rule is defined externally to this specification by the augmenter." - Architectural, - - /// This is a pseudo-register with a constant value. - Constant(u64), -} - -impl<R: Reader> RegisterRule<R> { - fn is_defined(&self) -> bool { - !matches!(*self, RegisterRule::Undefined) - } -} - -/// A parsed call frame instruction. -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum CallFrameInstruction<R: Reader> { - // 6.4.2.1 Row Creation Methods - /// > 1. DW_CFA_set_loc - /// > - /// > The DW_CFA_set_loc instruction takes a single operand that represents - /// > a target address. The required action is to create a new table row - /// > using the specified address as the location. All other values in the - /// > new row are initially identical to the current row. The new location - /// > value is always greater than the current one. If the segment_size - /// > field of this FDE's CIE is non- zero, the initial location is preceded - /// > by a segment selector of the given length. - SetLoc { - /// The target address. - address: u64, - }, - - /// The `AdvanceLoc` instruction is used for all of `DW_CFA_advance_loc` and - /// `DW_CFA_advance_loc{1,2,4}`. - /// - /// > 2. DW_CFA_advance_loc - /// > - /// > The DW_CFA_advance instruction takes a single operand (encoded with - /// > the opcode) that represents a constant delta. The required action is - /// > to create a new table row with a location value that is computed by - /// > taking the current entry’s location value and adding the value of - /// > delta * code_alignment_factor. All other values in the new row are - /// > initially identical to the current row. - AdvanceLoc { - /// The delta to be added to the current address. - delta: u32, - }, - - // 6.4.2.2 CFA Definition Methods - /// > 1. DW_CFA_def_cfa - /// > - /// > The DW_CFA_def_cfa instruction takes two unsigned LEB128 operands - /// > representing a register number and a (non-factored) offset. The - /// > required action is to define the current CFA rule to use the provided - /// > register and offset. - DefCfa { - /// The target register's number. - register: Register, - /// The non-factored offset. - offset: u64, - }, - - /// > 2. DW_CFA_def_cfa_sf - /// > - /// > The DW_CFA_def_cfa_sf instruction takes two operands: an unsigned - /// > LEB128 value representing a register number and a signed LEB128 - /// > factored offset. This instruction is identical to DW_CFA_def_cfa - /// > except that the second operand is signed and factored. The resulting - /// > offset is factored_offset * data_alignment_factor. - DefCfaSf { - /// The target register's number. - register: Register, - /// The factored offset. - factored_offset: i64, - }, - - /// > 3. DW_CFA_def_cfa_register - /// > - /// > The DW_CFA_def_cfa_register instruction takes a single unsigned LEB128 - /// > operand representing a register number. The required action is to - /// > define the current CFA rule to use the provided register (but to keep - /// > the old offset). This operation is valid only if the current CFA rule - /// > is defined to use a register and offset. - DefCfaRegister { - /// The target register's number. - register: Register, - }, - - /// > 4. DW_CFA_def_cfa_offset - /// > - /// > The DW_CFA_def_cfa_offset instruction takes a single unsigned LEB128 - /// > operand representing a (non-factored) offset. The required action is - /// > to define the current CFA rule to use the provided offset (but to keep - /// > the old register). This operation is valid only if the current CFA - /// > rule is defined to use a register and offset. - DefCfaOffset { - /// The non-factored offset. - offset: u64, - }, - - /// > 5. DW_CFA_def_cfa_offset_sf - /// > - /// > The DW_CFA_def_cfa_offset_sf instruction takes a signed LEB128 operand - /// > representing a factored offset. This instruction is identical to - /// > DW_CFA_def_cfa_offset except that the operand is signed and - /// > factored. The resulting offset is factored_offset * - /// > data_alignment_factor. This operation is valid only if the current CFA - /// > rule is defined to use a register and offset. - DefCfaOffsetSf { - /// The factored offset. - factored_offset: i64, - }, - - /// > 6. DW_CFA_def_cfa_expression - /// > - /// > The DW_CFA_def_cfa_expression instruction takes a single operand - /// > encoded as a DW_FORM_exprloc value representing a DWARF - /// > expression. The required action is to establish that expression as the - /// > means by which the current CFA is computed. - DefCfaExpression { - /// The DWARF expression. - expression: Expression<R>, - }, - - // 6.4.2.3 Register Rule Instructions - /// > 1. DW_CFA_undefined - /// > - /// > The DW_CFA_undefined instruction takes a single unsigned LEB128 - /// > operand that represents a register number. The required action is to - /// > set the rule for the specified register to “undefined.” - Undefined { - /// The target register's number. - register: Register, - }, - - /// > 2. DW_CFA_same_value - /// > - /// > The DW_CFA_same_value instruction takes a single unsigned LEB128 - /// > operand that represents a register number. The required action is to - /// > set the rule for the specified register to “same value.” - SameValue { - /// The target register's number. - register: Register, - }, - - /// The `Offset` instruction represents both `DW_CFA_offset` and - /// `DW_CFA_offset_extended`. - /// - /// > 3. DW_CFA_offset - /// > - /// > The DW_CFA_offset instruction takes two operands: a register number - /// > (encoded with the opcode) and an unsigned LEB128 constant representing - /// > a factored offset. The required action is to change the rule for the - /// > register indicated by the register number to be an offset(N) rule - /// > where the value of N is factored offset * data_alignment_factor. - Offset { - /// The target register's number. - register: Register, - /// The factored offset. - factored_offset: u64, - }, - - /// > 5. DW_CFA_offset_extended_sf - /// > - /// > The DW_CFA_offset_extended_sf instruction takes two operands: an - /// > unsigned LEB128 value representing a register number and a signed - /// > LEB128 factored offset. This instruction is identical to - /// > DW_CFA_offset_extended except that the second operand is signed and - /// > factored. The resulting offset is factored_offset * - /// > data_alignment_factor. - OffsetExtendedSf { - /// The target register's number. - register: Register, - /// The factored offset. - factored_offset: i64, - }, - - /// > 6. DW_CFA_val_offset - /// > - /// > The DW_CFA_val_offset instruction takes two unsigned LEB128 operands - /// > representing a register number and a factored offset. The required - /// > action is to change the rule for the register indicated by the - /// > register number to be a val_offset(N) rule where the value of N is - /// > factored_offset * data_alignment_factor. - ValOffset { - /// The target register's number. - register: Register, - /// The factored offset. - factored_offset: u64, - }, - - /// > 7. DW_CFA_val_offset_sf - /// > - /// > The DW_CFA_val_offset_sf instruction takes two operands: an unsigned - /// > LEB128 value representing a register number and a signed LEB128 - /// > factored offset. This instruction is identical to DW_CFA_val_offset - /// > except that the second operand is signed and factored. The resulting - /// > offset is factored_offset * data_alignment_factor. - ValOffsetSf { - /// The target register's number. - register: Register, - /// The factored offset. - factored_offset: i64, - }, - - /// > 8. DW_CFA_register - /// > - /// > The DW_CFA_register instruction takes two unsigned LEB128 operands - /// > representing register numbers. The required action is to set the rule - /// > for the first register to be register(R) where R is the second - /// > register. - Register { - /// The number of the register whose rule is being changed. - dest_register: Register, - /// The number of the register where the other register's value can be - /// found. - src_register: Register, - }, - - /// > 9. DW_CFA_expression - /// > - /// > The DW_CFA_expression instruction takes two operands: an unsigned - /// > LEB128 value representing a register number, and a DW_FORM_block value - /// > representing a DWARF expression. The required action is to change the - /// > rule for the register indicated by the register number to be an - /// > expression(E) rule where E is the DWARF expression. That is, the DWARF - /// > expression computes the address. The value of the CFA is pushed on the - /// > DWARF evaluation stack prior to execution of the DWARF expression. - Expression { - /// The target register's number. - register: Register, - /// The DWARF expression. - expression: Expression<R>, - }, - - /// > 10. DW_CFA_val_expression - /// > - /// > The DW_CFA_val_expression instruction takes two operands: an unsigned - /// > LEB128 value representing a register number, and a DW_FORM_block value - /// > representing a DWARF expression. The required action is to change the - /// > rule for the register indicated by the register number to be a - /// > val_expression(E) rule where E is the DWARF expression. That is, the - /// > DWARF expression computes the value of the given register. The value - /// > of the CFA is pushed on the DWARF evaluation stack prior to execution - /// > of the DWARF expression. - ValExpression { - /// The target register's number. - register: Register, - /// The DWARF expression. - expression: Expression<R>, - }, - - /// The `Restore` instruction represents both `DW_CFA_restore` and - /// `DW_CFA_restore_extended`. - /// - /// > 11. DW_CFA_restore - /// > - /// > The DW_CFA_restore instruction takes a single operand (encoded with - /// > the opcode) that represents a register number. The required action is - /// > to change the rule for the indicated register to the rule assigned it - /// > by the initial_instructions in the CIE. - Restore { - /// The register to be reset. - register: Register, - }, - - // 6.4.2.4 Row State Instructions - /// > 1. DW_CFA_remember_state - /// > - /// > The DW_CFA_remember_state instruction takes no operands. The required - /// > action is to push the set of rules for every register onto an implicit - /// > stack. - RememberState, - - /// > 2. DW_CFA_restore_state - /// > - /// > The DW_CFA_restore_state instruction takes no operands. The required - /// > action is to pop the set of rules off the implicit stack and place - /// > them in the current row. - RestoreState, - - /// > DW_CFA_GNU_args_size - /// > - /// > GNU Extension - /// > - /// > The DW_CFA_GNU_args_size instruction takes an unsigned LEB128 operand - /// > representing an argument size. This instruction specifies the total of - /// > the size of the arguments which have been pushed onto the stack. - ArgsSize { - /// The size of the arguments which have been pushed onto the stack - size: u64, - }, - - /// > DW_CFA_AARCH64_negate_ra_state - /// > - /// > AArch64 Extension - /// > - /// > The DW_CFA_AARCH64_negate_ra_state operation negates bit 0 of the - /// > RA_SIGN_STATE pseudo-register. It does not take any operands. The - /// > DW_CFA_AARCH64_negate_ra_state must not be mixed with other DWARF Register - /// > Rule Instructions on the RA_SIGN_STATE pseudo-register in one Common - /// > Information Entry (CIE) and Frame Descriptor Entry (FDE) program sequence. - NegateRaState, - - // 6.4.2.5 Padding Instruction - /// > 1. DW_CFA_nop - /// > - /// > The DW_CFA_nop instruction has no operands and no required actions. It - /// > is used as padding to make a CIE or FDE an appropriate size. - Nop, -} - -const CFI_INSTRUCTION_HIGH_BITS_MASK: u8 = 0b1100_0000; -const CFI_INSTRUCTION_LOW_BITS_MASK: u8 = !CFI_INSTRUCTION_HIGH_BITS_MASK; - -impl<R: Reader> CallFrameInstruction<R> { - fn parse( - input: &mut R, - address_encoding: Option<DwEhPe>, - parameters: &PointerEncodingParameters<R>, - vendor: Vendor, - ) -> Result<CallFrameInstruction<R>> { - let instruction = input.read_u8()?; - let high_bits = instruction & CFI_INSTRUCTION_HIGH_BITS_MASK; - - if high_bits == constants::DW_CFA_advance_loc.0 { - let delta = instruction & CFI_INSTRUCTION_LOW_BITS_MASK; - return Ok(CallFrameInstruction::AdvanceLoc { - delta: u32::from(delta), - }); - } - - if high_bits == constants::DW_CFA_offset.0 { - let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); - let offset = input.read_uleb128()?; - return Ok(CallFrameInstruction::Offset { - register, - factored_offset: offset, - }); - } - - if high_bits == constants::DW_CFA_restore.0 { - let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into()); - return Ok(CallFrameInstruction::Restore { register }); - } - - debug_assert_eq!(high_bits, 0); - let instruction = constants::DwCfa(instruction); - - match instruction { - constants::DW_CFA_nop => Ok(CallFrameInstruction::Nop), - - constants::DW_CFA_set_loc => { - let address = if let Some(encoding) = address_encoding { - parse_encoded_pointer(encoding, parameters, input)?.direct()? - } else { - input.read_address(parameters.address_size)? - }; - Ok(CallFrameInstruction::SetLoc { address }) - } - - constants::DW_CFA_advance_loc1 => { - let delta = input.read_u8()?; - Ok(CallFrameInstruction::AdvanceLoc { - delta: u32::from(delta), - }) - } - - constants::DW_CFA_advance_loc2 => { - let delta = input.read_u16()?; - Ok(CallFrameInstruction::AdvanceLoc { - delta: u32::from(delta), - }) - } - - constants::DW_CFA_advance_loc4 => { - let delta = input.read_u32()?; - Ok(CallFrameInstruction::AdvanceLoc { delta }) - } - - constants::DW_CFA_offset_extended => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let offset = input.read_uleb128()?; - Ok(CallFrameInstruction::Offset { - register, - factored_offset: offset, - }) - } - - constants::DW_CFA_restore_extended => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - Ok(CallFrameInstruction::Restore { register }) - } - - constants::DW_CFA_undefined => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - Ok(CallFrameInstruction::Undefined { register }) - } - - constants::DW_CFA_same_value => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - Ok(CallFrameInstruction::SameValue { register }) - } - - constants::DW_CFA_register => { - let dest = input.read_uleb128().and_then(Register::from_u64)?; - let src = input.read_uleb128().and_then(Register::from_u64)?; - Ok(CallFrameInstruction::Register { - dest_register: dest, - src_register: src, - }) - } - - constants::DW_CFA_remember_state => Ok(CallFrameInstruction::RememberState), - - constants::DW_CFA_restore_state => Ok(CallFrameInstruction::RestoreState), - - constants::DW_CFA_def_cfa => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let offset = input.read_uleb128()?; - Ok(CallFrameInstruction::DefCfa { register, offset }) - } - - constants::DW_CFA_def_cfa_register => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - Ok(CallFrameInstruction::DefCfaRegister { register }) - } - - constants::DW_CFA_def_cfa_offset => { - let offset = input.read_uleb128()?; - Ok(CallFrameInstruction::DefCfaOffset { offset }) - } - - constants::DW_CFA_def_cfa_expression => { - let len = input.read_uleb128().and_then(R::Offset::from_u64)?; - let expression = input.split(len)?; - Ok(CallFrameInstruction::DefCfaExpression { - expression: Expression(expression), - }) - } - - constants::DW_CFA_expression => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let len = input.read_uleb128().and_then(R::Offset::from_u64)?; - let expression = input.split(len)?; - Ok(CallFrameInstruction::Expression { - register, - expression: Expression(expression), - }) - } - - constants::DW_CFA_offset_extended_sf => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let offset = input.read_sleb128()?; - Ok(CallFrameInstruction::OffsetExtendedSf { - register, - factored_offset: offset, - }) - } - - constants::DW_CFA_def_cfa_sf => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let offset = input.read_sleb128()?; - Ok(CallFrameInstruction::DefCfaSf { - register, - factored_offset: offset, - }) - } - - constants::DW_CFA_def_cfa_offset_sf => { - let offset = input.read_sleb128()?; - Ok(CallFrameInstruction::DefCfaOffsetSf { - factored_offset: offset, - }) - } - - constants::DW_CFA_val_offset => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let offset = input.read_uleb128()?; - Ok(CallFrameInstruction::ValOffset { - register, - factored_offset: offset, - }) - } - - constants::DW_CFA_val_offset_sf => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let offset = input.read_sleb128()?; - Ok(CallFrameInstruction::ValOffsetSf { - register, - factored_offset: offset, - }) - } - - constants::DW_CFA_val_expression => { - let register = input.read_uleb128().and_then(Register::from_u64)?; - let len = input.read_uleb128().and_then(R::Offset::from_u64)?; - let expression = input.split(len)?; - Ok(CallFrameInstruction::ValExpression { - register, - expression: Expression(expression), - }) - } - - constants::DW_CFA_GNU_args_size => { - let size = input.read_uleb128()?; - Ok(CallFrameInstruction::ArgsSize { size }) - } - - constants::DW_CFA_AARCH64_negate_ra_state if vendor == Vendor::AArch64 => { - Ok(CallFrameInstruction::NegateRaState) - } - - otherwise => Err(Error::UnknownCallFrameInstruction(otherwise)), - } - } -} - -/// A lazy iterator parsing call frame instructions. -/// -/// Can be [used with -/// `FallibleIterator`](./index.html#using-with-fallibleiterator). -#[derive(Clone, Debug)] -pub struct CallFrameInstructionIter<'a, R: Reader> { - input: R, - address_encoding: Option<constants::DwEhPe>, - parameters: PointerEncodingParameters<'a, R>, - vendor: Vendor, -} - -impl<'a, R: Reader> CallFrameInstructionIter<'a, R> { - /// Parse the next call frame instruction. - pub fn next(&mut self) -> Result<Option<CallFrameInstruction<R>>> { - if self.input.is_empty() { - return Ok(None); - } - - match CallFrameInstruction::parse( - &mut self.input, - self.address_encoding, - &self.parameters, - self.vendor, - ) { - Ok(instruction) => Ok(Some(instruction)), - Err(e) => { - self.input.empty(); - Err(e) - } - } - } -} - -#[cfg(feature = "fallible-iterator")] -impl<'a, R: Reader> fallible_iterator::FallibleIterator for CallFrameInstructionIter<'a, R> { - type Item = CallFrameInstruction<R>; - type Error = Error; - - fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> { - CallFrameInstructionIter::next(self) - } -} - -/// Parse a `DW_EH_PE_*` pointer encoding. -#[doc(hidden)] -#[inline] -fn parse_pointer_encoding<R: Reader>(input: &mut R) -> Result<constants::DwEhPe> { - let eh_pe = input.read_u8()?; - let eh_pe = constants::DwEhPe(eh_pe); - - if eh_pe.is_valid_encoding() { - Ok(eh_pe) - } else { - Err(Error::UnknownPointerEncoding) - } -} - -/// A decoded pointer. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Pointer { - /// This value is the decoded pointer value. - Direct(u64), - - /// This value is *not* the pointer value, but points to the address of - /// where the real pointer value lives. In other words, deref this pointer - /// to get the real pointer value. - /// - /// Chase this pointer at your own risk: do you trust the DWARF data it came - /// from? - Indirect(u64), -} - -impl Default for Pointer { - #[inline] - fn default() -> Self { - Pointer::Direct(0) - } -} - -impl Pointer { - #[inline] - fn new(encoding: constants::DwEhPe, address: u64) -> Pointer { - if encoding.is_indirect() { - Pointer::Indirect(address) - } else { - Pointer::Direct(address) - } - } - - /// Return the direct pointer value. - #[inline] - pub fn direct(self) -> Result<u64> { - match self { - Pointer::Direct(p) => Ok(p), - Pointer::Indirect(_) => Err(Error::UnsupportedPointerEncoding), - } - } - - /// Return the pointer value, discarding indirectness information. - #[inline] - pub fn pointer(self) -> u64 { - match self { - Pointer::Direct(p) | Pointer::Indirect(p) => p, - } - } -} - -#[derive(Clone, Debug)] -struct PointerEncodingParameters<'a, R: Reader> { - bases: &'a SectionBaseAddresses, - func_base: Option<u64>, - address_size: u8, - section: &'a R, -} - -fn parse_encoded_pointer<R: Reader>( - encoding: constants::DwEhPe, - parameters: &PointerEncodingParameters<R>, - input: &mut R, -) -> Result<Pointer> { - // TODO: check this once only in parse_pointer_encoding - if !encoding.is_valid_encoding() { - return Err(Error::UnknownPointerEncoding); - } - - if encoding == constants::DW_EH_PE_omit { - return Err(Error::CannotParseOmitPointerEncoding); - } - - let base = match encoding.application() { - constants::DW_EH_PE_absptr => 0, - constants::DW_EH_PE_pcrel => { - if let Some(section_base) = parameters.bases.section { - let offset_from_section = input.offset_from(parameters.section); - section_base.wrapping_add(offset_from_section.into_u64()) - } else { - return Err(Error::PcRelativePointerButSectionBaseIsUndefined); - } - } - constants::DW_EH_PE_textrel => { - if let Some(text) = parameters.bases.text { - text - } else { - return Err(Error::TextRelativePointerButTextBaseIsUndefined); - } - } - constants::DW_EH_PE_datarel => { - if let Some(data) = parameters.bases.data { - data - } else { - return Err(Error::DataRelativePointerButDataBaseIsUndefined); - } - } - constants::DW_EH_PE_funcrel => { - if let Some(func) = parameters.func_base { - func - } else { - return Err(Error::FuncRelativePointerInBadContext); - } - } - constants::DW_EH_PE_aligned => return Err(Error::UnsupportedPointerEncoding), - _ => unreachable!(), - }; - - let offset = match encoding.format() { - // Unsigned variants. - constants::DW_EH_PE_absptr => input.read_address(parameters.address_size), - constants::DW_EH_PE_uleb128 => input.read_uleb128(), - constants::DW_EH_PE_udata2 => input.read_u16().map(u64::from), - constants::DW_EH_PE_udata4 => input.read_u32().map(u64::from), - constants::DW_EH_PE_udata8 => input.read_u64(), - - // Signed variants. Here we sign extend the values (happens by - // default when casting a signed integer to a larger range integer - // in Rust), return them as u64, and rely on wrapping addition to do - // the right thing when adding these offsets to their bases. - constants::DW_EH_PE_sleb128 => input.read_sleb128().map(|a| a as u64), - constants::DW_EH_PE_sdata2 => input.read_i16().map(|a| a as u64), - constants::DW_EH_PE_sdata4 => input.read_i32().map(|a| a as u64), - constants::DW_EH_PE_sdata8 => input.read_i64().map(|a| a as u64), - - // That was all of the valid encoding formats. - _ => unreachable!(), - }?; - - Ok(Pointer::new(encoding, base.wrapping_add(offset))) -} - -#[cfg(test)] -mod tests { - use super::*; - use super::{parse_cfi_entry, AugmentationData, RegisterRuleMap, UnwindContext}; - use crate::common::Format; - use crate::constants; - use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian}; - use crate::read::{ - EndianSlice, Error, Expression, Pointer, ReaderOffsetId, Result, Section as ReadSection, - }; - use crate::test_util::GimliSectionMethods; - use alloc::boxed::Box; - use alloc::vec::Vec; - use core::marker::PhantomData; - use core::mem; - use core::u64; - use test_assembler::{Endian, Label, LabelMaker, LabelOrNum, Section, ToLabelOrNum}; - - // Ensure each test tries to read the same section kind that it wrote. - #[derive(Clone, Copy)] - struct SectionKind<Section>(PhantomData<Section>); - - impl<T> SectionKind<T> { - fn endian<'input, E>(self) -> Endian - where - E: Endianity, - T: UnwindSection<EndianSlice<'input, E>>, - T::Offset: UnwindOffset<usize>, - { - if E::default().is_big_endian() { - Endian::Big - } else { - Endian::Little - } - } - - fn section<'input, E>(self, contents: &'input [u8]) -> T - where - E: Endianity, - T: UnwindSection<EndianSlice<'input, E>> + ReadSection<EndianSlice<'input, E>>, - T::Offset: UnwindOffset<usize>, - { - EndianSlice::new(contents, E::default()).into() - } - } - - fn debug_frame_le<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, LittleEndian>>> { - SectionKind(PhantomData) - } - - fn debug_frame_be<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, BigEndian>>> { - SectionKind(PhantomData) - } - - fn eh_frame_le<'a>() -> SectionKind<EhFrame<EndianSlice<'a, LittleEndian>>> { - SectionKind(PhantomData) - } - - fn parse_fde<Section, O, F, R>( - section: Section, - input: &mut R, - get_cie: F, - ) -> Result<FrameDescriptionEntry<R>> - where - R: Reader, - Section: UnwindSection<R, Offset = O>, - O: UnwindOffset<R::Offset>, - F: FnMut(&Section, &BaseAddresses, O) -> Result<CommonInformationEntry<R>>, - { - let bases = Default::default(); - match parse_cfi_entry(&bases, §ion, input) { - Ok(Some(CieOrFde::Fde(partial))) => partial.parse(get_cie), - Ok(_) => Err(Error::NoEntryAtGivenOffset), - Err(e) => Err(e), - } - } - - // Mixin methods for `Section` to help define binary test data. - - trait CfiSectionMethods: GimliSectionMethods { - fn cie<'aug, 'input, E, T>( - self, - _kind: SectionKind<T>, - augmentation: Option<&'aug str>, - cie: &mut CommonInformationEntry<EndianSlice<'input, E>>, - ) -> Self - where - E: Endianity, - T: UnwindSection<EndianSlice<'input, E>>, - T::Offset: UnwindOffset; - fn fde<'a, 'input, E, T, L>( - self, - _kind: SectionKind<T>, - cie_offset: L, - fde: &mut FrameDescriptionEntry<EndianSlice<'input, E>>, - ) -> Self - where - E: Endianity, - T: UnwindSection<EndianSlice<'input, E>>, - T::Offset: UnwindOffset, - L: ToLabelOrNum<'a, u64>; - } - - impl CfiSectionMethods for Section { - fn cie<'aug, 'input, E, T>( - self, - _kind: SectionKind<T>, - augmentation: Option<&'aug str>, - cie: &mut CommonInformationEntry<EndianSlice<'input, E>>, - ) -> Self - where - E: Endianity, - T: UnwindSection<EndianSlice<'input, E>>, - T::Offset: UnwindOffset, - { - cie.offset = self.size() as _; - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - let section = match cie.format { - Format::Dwarf32 => self.D32(&length).mark(&start).D32(0xffff_ffff), - Format::Dwarf64 => { - let section = self.D32(0xffff_ffff); - section.D64(&length).mark(&start).D64(0xffff_ffff_ffff_ffff) - } - }; - - let mut section = section.D8(cie.version); - - if let Some(augmentation) = augmentation { - section = section.append_bytes(augmentation.as_bytes()); - } - - // Null terminator for augmentation string. - let section = section.D8(0); - - let section = if T::has_address_and_segment_sizes(cie.version) { - section.D8(cie.address_size).D8(cie.segment_size) - } else { - section - }; - - let section = section - .uleb(cie.code_alignment_factor) - .sleb(cie.data_alignment_factor) - .uleb(cie.return_address_register.0.into()) - .append_bytes(cie.initial_instructions.slice()) - .mark(&end); - - cie.length = (&end - &start) as usize; - length.set_const(cie.length as u64); - - section - } - - fn fde<'a, 'input, E, T, L>( - self, - _kind: SectionKind<T>, - cie_offset: L, - fde: &mut FrameDescriptionEntry<EndianSlice<'input, E>>, - ) -> Self - where - E: Endianity, - T: UnwindSection<EndianSlice<'input, E>>, - T::Offset: UnwindOffset, - L: ToLabelOrNum<'a, u64>, - { - fde.offset = self.size() as _; - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - assert_eq!(fde.format, fde.cie.format); - - let section = match T::cie_offset_encoding(fde.format) { - CieOffsetEncoding::U32 => { - let section = self.D32(&length).mark(&start); - match cie_offset.to_labelornum() { - LabelOrNum::Label(ref l) => section.D32(l), - LabelOrNum::Num(o) => section.D32(o as u32), - } - } - CieOffsetEncoding::U64 => { - let section = self.D32(0xffff_ffff); - section.D64(&length).mark(&start).D64(cie_offset) - } - }; - - let section = match fde.cie.segment_size { - 0 => section, - 4 => section.D32(fde.initial_segment as u32), - 8 => section.D64(fde.initial_segment), - x => panic!("Unsupported test segment size: {}", x), - }; - - let section = match fde.cie.address_size { - 4 => section - .D32(fde.initial_address() as u32) - .D32(fde.len() as u32), - 8 => section.D64(fde.initial_address()).D64(fde.len()), - x => panic!("Unsupported address size: {}", x), - }; - - let section = if let Some(ref augmentation) = fde.augmentation { - let cie_aug = fde - .cie - .augmentation - .expect("FDE has augmentation, but CIE doesn't"); - - if let Some(lsda) = augmentation.lsda { - // We only support writing `DW_EH_PE_absptr` here. - assert_eq!( - cie_aug - .lsda - .expect("FDE has lsda, but CIE doesn't") - .format(), - constants::DW_EH_PE_absptr - ); - - // Augmentation data length - let section = section.uleb(u64::from(fde.cie.address_size)); - match fde.cie.address_size { - 4 => section.D32({ - let x: u64 = lsda.pointer(); - x as u32 - }), - 8 => section.D64({ - let x: u64 = lsda.pointer(); - x - }), - x => panic!("Unsupported address size: {}", x), - } - } else { - // Even if we don't have any augmentation data, if there is - // an augmentation defined, we need to put the length in. - section.uleb(0) - } - } else { - section - }; - - let section = section.append_bytes(fde.instructions.slice()).mark(&end); - - fde.length = (&end - &start) as usize; - length.set_const(fde.length as u64); - - section - } - } - - trait ResultExt { - fn map_eof(self, input: &[u8]) -> Self; - } - - impl<T> ResultExt for Result<T> { - fn map_eof(self, input: &[u8]) -> Self { - match self { - Err(Error::UnexpectedEof(id)) => { - let id = ReaderOffsetId(id.0 - input.as_ptr() as u64); - Err(Error::UnexpectedEof(id)) - } - r => r, - } - } - } - - fn assert_parse_cie<'input, E>( - kind: SectionKind<DebugFrame<EndianSlice<'input, E>>>, - section: Section, - address_size: u8, - expected: Result<( - EndianSlice<'input, E>, - CommonInformationEntry<EndianSlice<'input, E>>, - )>, - ) where - E: Endianity, - { - let section = section.get_contents().unwrap(); - let mut debug_frame = kind.section(§ion); - debug_frame.set_address_size(address_size); - let input = &mut EndianSlice::new(§ion, E::default()); - let bases = Default::default(); - let result = CommonInformationEntry::parse(&bases, &debug_frame, input); - let result = result.map(|cie| (*input, cie)).map_eof(§ion); - assert_eq!(result, expected); - } - - #[test] - fn test_parse_cie_incomplete_length_32() { - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()).L16(5); - assert_parse_cie( - kind, - section, - 8, - Err(Error::UnexpectedEof(ReaderOffsetId(0))), - ); - } - - #[test] - fn test_parse_cie_incomplete_length_64() { - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - .L32(0xffff_ffff) - .L32(12345); - assert_parse_cie( - kind, - section, - 8, - Err(Error::UnexpectedEof(ReaderOffsetId(4))), - ); - } - - #[test] - fn test_parse_cie_incomplete_id_32() { - let kind = debug_frame_be(); - let section = Section::with_endian(kind.endian()) - // The length is not large enough to contain the ID. - .B32(3) - .B32(0xffff_ffff); - assert_parse_cie( - kind, - section, - 8, - Err(Error::UnexpectedEof(ReaderOffsetId(4))), - ); - } - - #[test] - fn test_parse_cie_bad_id_32() { - let kind = debug_frame_be(); - let section = Section::with_endian(kind.endian()) - // Initial length - .B32(4) - // Not the CIE Id. - .B32(0xbad1_bad2); - assert_parse_cie(kind, section, 8, Err(Error::NotCieId)); - } - - #[test] - fn test_parse_cie_32_bad_version() { - let mut cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 99, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 2, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&[], LittleEndian), - }; - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); - assert_parse_cie(kind, section, 4, Err(Error::UnknownVersion(99))); - } - - #[test] - fn test_parse_cie_unknown_augmentation() { - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - let augmentation = Some("replicant"); - let expected_rest = [1, 2, 3]; - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - // Initial length - .L32(&length) - .mark(&start) - // CIE Id - .L32(0xffff_ffff) - // Version - .D8(4) - // Augmentation - .append_bytes(augmentation.unwrap().as_bytes()) - // Null terminator - .D8(0) - // Extra augmented data that we can't understand. - .L32(1) - .L32(2) - .L32(3) - .L32(4) - .L32(5) - .L32(6) - .mark(&end) - .append_bytes(&expected_rest); - - let expected_length = (&end - &start) as u64; - length.set_const(expected_length); - - assert_parse_cie(kind, section, 8, Err(Error::UnknownAugmentation)); - } - - fn test_parse_cie(format: Format, version: u8, address_size: u8) { - let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); - - let mut cie = CommonInformationEntry { - offset: 0, - length: 0, - format, - version, - augmentation: None, - address_size, - segment_size: 0, - code_alignment_factor: 16, - data_alignment_factor: 32, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), - }; - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - .cie(kind, None, &mut cie) - .append_bytes(&expected_rest); - - assert_parse_cie( - kind, - section, - address_size, - Ok((EndianSlice::new(&expected_rest, LittleEndian), cie)), - ); - } - - #[test] - fn test_parse_cie_32_ok() { - test_parse_cie(Format::Dwarf32, 1, 4); - test_parse_cie(Format::Dwarf32, 1, 8); - test_parse_cie(Format::Dwarf32, 4, 4); - test_parse_cie(Format::Dwarf32, 4, 8); - } - - #[test] - fn test_parse_cie_64_ok() { - test_parse_cie(Format::Dwarf64, 1, 4); - test_parse_cie(Format::Dwarf64, 1, 8); - test_parse_cie(Format::Dwarf64, 4, 4); - test_parse_cie(Format::Dwarf64, 4, 8); - } - - #[test] - fn test_parse_cie_length_too_big() { - let expected_instrs: Vec<_> = (0..13).map(|_| constants::DW_CFA_nop.0).collect(); - - let mut cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 0, - data_alignment_factor: 0, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian), - }; - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie); - - let mut contents = section.get_contents().unwrap(); - - // Overwrite the length to be too big. - contents[0] = 0; - contents[1] = 0; - contents[2] = 0; - contents[3] = 255; - - let debug_frame = DebugFrame::new(&contents, LittleEndian); - let bases = Default::default(); - assert_eq!( - CommonInformationEntry::parse( - &bases, - &debug_frame, - &mut EndianSlice::new(&contents, LittleEndian) - ) - .map_eof(&contents), - Err(Error::UnexpectedEof(ReaderOffsetId(4))) - ); - } - - #[test] - fn test_parse_fde_incomplete_length_32() { - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()).L16(5); - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, LittleEndian); - assert_eq!( - parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), - Err(Error::UnexpectedEof(ReaderOffsetId(0))) - ); - } - - #[test] - fn test_parse_fde_incomplete_length_64() { - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - .L32(0xffff_ffff) - .L32(12345); - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, LittleEndian); - assert_eq!( - parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), - Err(Error::UnexpectedEof(ReaderOffsetId(4))) - ); - } - - #[test] - fn test_parse_fde_incomplete_cie_pointer_32() { - let kind = debug_frame_be(); - let section = Section::with_endian(kind.endian()) - // The length is not large enough to contain the CIE pointer. - .B32(3) - .B32(1994); - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, BigEndian); - assert_eq!( - parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion), - Err(Error::UnexpectedEof(ReaderOffsetId(4))) - ); - } - - #[test] - fn test_parse_fde_32_ok() { - let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let cie_offset = 0xbad0_bad1; - let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); - - let cie = CommonInformationEntry { - offset: 0, - length: 100, - format: Format::Dwarf32, - version: 4, - augmentation: None, - // DWARF32 with a 64 bit address size! Holy moly! - address_size: 8, - segment_size: 0, - code_alignment_factor: 3, - data_alignment_factor: 2, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&[], LittleEndian), - }; - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 39, - augmentation: None, - instructions: EndianSlice::new(&expected_instrs, LittleEndian), - }; - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&expected_rest); - - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, LittleEndian); - - let get_cie = |_: &_, _: &_, offset| { - assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); - Ok(cie.clone()) - }; - - assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); - assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_fde_32_with_segment_ok() { - let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let cie_offset = 0xbad0_bad1; - let expected_instrs: Vec<_> = (0..92).map(|_| constants::DW_CFA_nop.0).collect(); - - let cie = CommonInformationEntry { - offset: 0, - length: 100, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 4, - code_alignment_factor: 3, - data_alignment_factor: 2, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&[], LittleEndian), - }; - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0xbadb_ad11, - initial_address: 0xfeed_beef, - address_range: 999, - augmentation: None, - instructions: EndianSlice::new(&expected_instrs, LittleEndian), - }; - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&expected_rest); - - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, LittleEndian); - - let get_cie = |_: &_, _: &_, offset| { - assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); - Ok(cie.clone()) - }; - - assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); - assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_fde_64_ok() { - let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let cie_offset = 0xbad0_bad1; - let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect(); - - let cie = CommonInformationEntry { - offset: 0, - length: 100, - format: Format::Dwarf64, - version: 4, - augmentation: None, - address_size: 8, - segment_size: 0, - code_alignment_factor: 3, - data_alignment_factor: 2, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&[], LittleEndian), - }; - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf64, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 999, - augmentation: None, - instructions: EndianSlice::new(&expected_instrs, LittleEndian), - }; - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&expected_rest); - - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, LittleEndian); - - let get_cie = |_: &_, _: &_, offset| { - assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); - Ok(cie.clone()) - }; - - assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde)); - assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_entry_on_cie_32_ok() { - let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); - - let mut cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 16, - data_alignment_factor: 32, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&expected_instrs, BigEndian), - }; - - let kind = debug_frame_be(); - let section = Section::with_endian(kind.endian()) - .cie(kind, None, &mut cie) - .append_bytes(&expected_rest); - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, BigEndian); - - let bases = Default::default(); - assert_eq!( - parse_cfi_entry(&bases, &debug_frame, rest), - Ok(Some(CieOrFde::Cie(cie))) - ); - assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); - } - - #[test] - fn test_parse_cfi_entry_on_fde_32_ok() { - let cie_offset = 0x1234_5678; - let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); - - let cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 16, - data_alignment_factor: 32, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&[], BigEndian), - }; - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 39, - augmentation: None, - instructions: EndianSlice::new(&expected_instrs, BigEndian), - }; - - let kind = debug_frame_be(); - let section = Section::with_endian(kind.endian()) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&expected_rest); - - let section = section.get_contents().unwrap(); - let debug_frame = kind.section(§ion); - let rest = &mut EndianSlice::new(§ion, BigEndian); - - let bases = Default::default(); - match parse_cfi_entry(&bases, &debug_frame, rest) { - Ok(Some(CieOrFde::Fde(partial))) => { - assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian)); - - assert_eq!(partial.length, fde.length); - assert_eq!(partial.format, fde.format); - assert_eq!(partial.cie_offset, DebugFrameOffset(cie_offset as usize)); - - let get_cie = |_: &_, _: &_, offset| { - assert_eq!(offset, DebugFrameOffset(cie_offset as usize)); - Ok(cie.clone()) - }; - - assert_eq!(partial.parse(get_cie), Ok(fde)); - } - otherwise => panic!("Unexpected result: {:#?}", otherwise), - } - } - - #[test] - fn test_cfi_entries_iter() { - let expected_instrs1: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect(); - - let expected_instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); - - let expected_instrs3: Vec<_> = (0..12).map(|_| constants::DW_CFA_nop.0).collect(); - - let expected_instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); - - let mut cie1 = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 2, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&expected_instrs1, BigEndian), - }; - - let mut cie2 = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 3, - data_alignment_factor: 2, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&expected_instrs2, BigEndian), - }; - - let cie1_location = Label::new(); - let cie2_location = Label::new(); - - // Write the CIEs first so that their length gets set before we clone - // them into the FDEs and our equality assertions down the line end up - // with all the CIEs always having he correct length. - let kind = debug_frame_be(); - let section = Section::with_endian(kind.endian()) - .mark(&cie1_location) - .cie(kind, None, &mut cie1) - .mark(&cie2_location) - .cie(kind, None, &mut cie2); - - let mut fde1 = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie1.clone(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 39, - augmentation: None, - instructions: EndianSlice::new(&expected_instrs3, BigEndian), - }; - - let mut fde2 = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie2.clone(), - initial_segment: 0, - initial_address: 0xfeed_face, - address_range: 9000, - augmentation: None, - instructions: EndianSlice::new(&expected_instrs4, BigEndian), - }; - - let section = - section - .fde(kind, &cie1_location, &mut fde1) - .fde(kind, &cie2_location, &mut fde2); - - section.start().set_const(0); - - let cie1_offset = cie1_location.value().unwrap() as usize; - let cie2_offset = cie2_location.value().unwrap() as usize; - - let contents = section.get_contents().unwrap(); - let debug_frame = kind.section(&contents); - - let bases = Default::default(); - let mut entries = debug_frame.entries(&bases); - - assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie1.clone())))); - assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie2.clone())))); - - match entries.next() { - Ok(Some(CieOrFde::Fde(partial))) => { - assert_eq!(partial.length, fde1.length); - assert_eq!(partial.format, fde1.format); - assert_eq!(partial.cie_offset, DebugFrameOffset(cie1_offset)); - - let get_cie = |_: &_, _: &_, offset| { - assert_eq!(offset, DebugFrameOffset(cie1_offset)); - Ok(cie1.clone()) - }; - assert_eq!(partial.parse(get_cie), Ok(fde1)); - } - otherwise => panic!("Unexpected result: {:#?}", otherwise), - } - - match entries.next() { - Ok(Some(CieOrFde::Fde(partial))) => { - assert_eq!(partial.length, fde2.length); - assert_eq!(partial.format, fde2.format); - assert_eq!(partial.cie_offset, DebugFrameOffset(cie2_offset)); - - let get_cie = |_: &_, _: &_, offset| { - assert_eq!(offset, DebugFrameOffset(cie2_offset)); - Ok(cie2.clone()) - }; - assert_eq!(partial.parse(get_cie), Ok(fde2)); - } - otherwise => panic!("Unexpected result: {:#?}", otherwise), - } - - assert_eq!(entries.next(), Ok(None)); - } - - #[test] - fn test_parse_cie_from_offset() { - let filler = [1, 2, 3, 4, 5, 6, 7, 8, 9]; - let instrs: Vec<_> = (0..5).map(|_| constants::DW_CFA_nop.0).collect(); - - let mut cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf64, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 4, - data_alignment_factor: 8, - return_address_register: Register(12), - initial_instructions: EndianSlice::new(&instrs, LittleEndian), - }; - - let cie_location = Label::new(); - - let kind = debug_frame_le(); - let section = Section::with_endian(kind.endian()) - .append_bytes(&filler) - .mark(&cie_location) - .cie(kind, None, &mut cie) - .append_bytes(&filler); - - section.start().set_const(0); - - let cie_offset = DebugFrameOffset(cie_location.value().unwrap() as usize); - - let contents = section.get_contents().unwrap(); - let debug_frame = kind.section(&contents); - let bases = Default::default(); - - assert_eq!(debug_frame.cie_from_offset(&bases, cie_offset), Ok(cie)); - } - - fn parse_cfi_instruction<R: Reader + Default>( - input: &mut R, - address_size: u8, - ) -> Result<CallFrameInstruction<R>> { - let parameters = &PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size, - section: &R::default(), - }; - CallFrameInstruction::parse(input, None, parameters, Vendor::Default) - } - - #[test] - fn test_parse_cfi_instruction_advance_loc() { - let expected_rest = [1, 2, 3, 4]; - let expected_delta = 42; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_advance_loc.0 | expected_delta) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::AdvanceLoc { - delta: u32::from(expected_delta), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_offset() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 3; - let expected_offset = 1997; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_offset.0 | expected_reg) - .uleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Offset { - register: Register(expected_reg.into()), - factored_offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_restore() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 3; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_restore.0 | expected_reg) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Restore { - register: Register(expected_reg.into()), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_nop() { - let expected_rest = [1, 2, 3, 4]; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_nop.0) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Nop) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_set_loc() { - let expected_rest = [1, 2, 3, 4]; - let expected_addr = 0xdead_beef; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_set_loc.0) - .L64(expected_addr) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::SetLoc { - address: expected_addr, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_set_loc_encoding() { - let text_base = 0xfeed_face; - let addr_offset = 0xbeef; - let expected_addr = text_base + addr_offset; - let expected_rest = [1, 2, 3, 4]; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_set_loc.0) - .L64(addr_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - let parameters = &PointerEncodingParameters { - bases: &BaseAddresses::default().set_text(text_base).eh_frame, - func_base: None, - address_size: 8, - section: &EndianSlice::new(&[], LittleEndian), - }; - assert_eq!( - CallFrameInstruction::parse( - input, - Some(constants::DW_EH_PE_textrel), - parameters, - Vendor::Default - ), - Ok(CallFrameInstruction::SetLoc { - address: expected_addr, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_advance_loc1() { - let expected_rest = [1, 2, 3, 4]; - let expected_delta = 8; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_advance_loc1.0) - .D8(expected_delta) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::AdvanceLoc { - delta: u32::from(expected_delta), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_advance_loc2() { - let expected_rest = [1, 2, 3, 4]; - let expected_delta = 500; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_advance_loc2.0) - .L16(expected_delta) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::AdvanceLoc { - delta: u32::from(expected_delta), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_advance_loc4() { - let expected_rest = [1, 2, 3, 4]; - let expected_delta = 1 << 20; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_advance_loc4.0) - .L32(expected_delta) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::AdvanceLoc { - delta: expected_delta, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_offset_extended() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 7; - let expected_offset = 33; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_offset_extended.0) - .uleb(expected_reg.into()) - .uleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Offset { - register: Register(expected_reg), - factored_offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_restore_extended() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 7; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_restore_extended.0) - .uleb(expected_reg.into()) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Restore { - register: Register(expected_reg), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_undefined() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 7; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_undefined.0) - .uleb(expected_reg.into()) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Undefined { - register: Register(expected_reg), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_same_value() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 7; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_same_value.0) - .uleb(expected_reg.into()) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::SameValue { - register: Register(expected_reg), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_register() { - let expected_rest = [1, 2, 3, 4]; - let expected_dest_reg = 7; - let expected_src_reg = 8; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_register.0) - .uleb(expected_dest_reg.into()) - .uleb(expected_src_reg.into()) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Register { - dest_register: Register(expected_dest_reg), - src_register: Register(expected_src_reg), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_remember_state() { - let expected_rest = [1, 2, 3, 4]; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_remember_state.0) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::RememberState) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_restore_state() { - let expected_rest = [1, 2, 3, 4]; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_restore_state.0) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::RestoreState) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_def_cfa() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 2; - let expected_offset = 0; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_def_cfa.0) - .uleb(expected_reg.into()) - .uleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::DefCfa { - register: Register(expected_reg), - offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_def_cfa_register() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 2; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_def_cfa_register.0) - .uleb(expected_reg.into()) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::DefCfaRegister { - register: Register(expected_reg), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_def_cfa_offset() { - let expected_rest = [1, 2, 3, 4]; - let expected_offset = 23; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_def_cfa_offset.0) - .uleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::DefCfaOffset { - offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_def_cfa_expression() { - let expected_rest = [1, 2, 3, 4]; - let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_def_cfa_expression.0) - .D8(&length) - .mark(&start) - .append_bytes(&expected_expr) - .mark(&end) - .append_bytes(&expected_rest); - - length.set_const((&end - &start) as u64); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::DefCfaExpression { - expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_expression() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 99; - let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_expression.0) - .uleb(expected_reg.into()) - .D8(&length) - .mark(&start) - .append_bytes(&expected_expr) - .mark(&end) - .append_bytes(&expected_rest); - - length.set_const((&end - &start) as u64); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::Expression { - register: Register(expected_reg), - expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_offset_extended_sf() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 7; - let expected_offset = -33; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_offset_extended_sf.0) - .uleb(expected_reg.into()) - .sleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::OffsetExtendedSf { - register: Register(expected_reg), - factored_offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_def_cfa_sf() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 2; - let expected_offset = -9999; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_def_cfa_sf.0) - .uleb(expected_reg.into()) - .sleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::DefCfaSf { - register: Register(expected_reg), - factored_offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_def_cfa_offset_sf() { - let expected_rest = [1, 2, 3, 4]; - let expected_offset = -123; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_def_cfa_offset_sf.0) - .sleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::DefCfaOffsetSf { - factored_offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_val_offset() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 50; - let expected_offset = 23; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_val_offset.0) - .uleb(expected_reg.into()) - .uleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::ValOffset { - register: Register(expected_reg), - factored_offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_val_offset_sf() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 50; - let expected_offset = -23; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_val_offset_sf.0) - .uleb(expected_reg.into()) - .sleb(expected_offset) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::ValOffsetSf { - register: Register(expected_reg), - factored_offset: expected_offset, - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_val_expression() { - let expected_rest = [1, 2, 3, 4]; - let expected_reg = 50; - let expected_expr = [2, 2, 1, 1, 5, 5]; - - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_val_expression.0) - .uleb(expected_reg.into()) - .D8(&length) - .mark(&start) - .append_bytes(&expected_expr) - .mark(&end) - .append_bytes(&expected_rest); - - length.set_const((&end - &start) as u64); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - - assert_eq!( - parse_cfi_instruction(input, 8), - Ok(CallFrameInstruction::ValExpression { - register: Register(expected_reg), - expression: Expression(EndianSlice::new(&expected_expr, LittleEndian)), - }) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_negate_ra_state() { - let expected_rest = [1, 2, 3, 4]; - let section = Section::with_endian(Endian::Little) - .D8(constants::DW_CFA_AARCH64_negate_ra_state.0) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - let parameters = &PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 8, - section: &EndianSlice::default(), - }; - assert_eq!( - CallFrameInstruction::parse(input, None, parameters, Vendor::AArch64), - Ok(CallFrameInstruction::NegateRaState) - ); - assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_cfi_instruction_unknown_instruction() { - let expected_rest = [1, 2, 3, 4]; - let unknown_instr = constants::DwCfa(0b0011_1111); - let section = Section::with_endian(Endian::Little) - .D8(unknown_instr.0) - .append_bytes(&expected_rest); - let contents = section.get_contents().unwrap(); - let input = &mut EndianSlice::new(&contents, LittleEndian); - assert_eq!( - parse_cfi_instruction(input, 8), - Err(Error::UnknownCallFrameInstruction(unknown_instr)) - ); - } - - #[test] - fn test_call_frame_instruction_iter_ok() { - let expected_reg = 50; - let expected_expr = [2, 2, 1, 1, 5, 5]; - let expected_delta = 230; - - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - let section = Section::with_endian(Endian::Big) - .D8(constants::DW_CFA_val_expression.0) - .uleb(expected_reg.into()) - .D8(&length) - .mark(&start) - .append_bytes(&expected_expr) - .mark(&end) - .D8(constants::DW_CFA_advance_loc1.0) - .D8(expected_delta); - - length.set_const((&end - &start) as u64); - let contents = section.get_contents().unwrap(); - let input = EndianSlice::new(&contents, BigEndian); - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 8, - section: &EndianSlice::default(), - }; - let mut iter = CallFrameInstructionIter { - input, - address_encoding: None, - parameters, - vendor: Vendor::Default, - }; - - assert_eq!( - iter.next(), - Ok(Some(CallFrameInstruction::ValExpression { - register: Register(expected_reg), - expression: Expression(EndianSlice::new(&expected_expr, BigEndian)), - })) - ); - - assert_eq!( - iter.next(), - Ok(Some(CallFrameInstruction::AdvanceLoc { - delta: u32::from(expected_delta), - })) - ); - - assert_eq!(iter.next(), Ok(None)); - } - - #[test] - fn test_call_frame_instruction_iter_err() { - // DW_CFA_advance_loc1 without an operand. - let section = Section::with_endian(Endian::Big).D8(constants::DW_CFA_advance_loc1.0); - - let contents = section.get_contents().unwrap(); - let input = EndianSlice::new(&contents, BigEndian); - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 8, - section: &EndianSlice::default(), - }; - let mut iter = CallFrameInstructionIter { - input, - address_encoding: None, - parameters, - vendor: Vendor::Default, - }; - - assert_eq!( - iter.next().map_eof(&contents), - Err(Error::UnexpectedEof(ReaderOffsetId(1))) - ); - assert_eq!(iter.next(), Ok(None)); - } - - fn assert_eval<'a, I>( - mut initial_ctx: UnwindContext<EndianSlice<'a, LittleEndian>>, - expected_ctx: UnwindContext<EndianSlice<'a, LittleEndian>>, - cie: CommonInformationEntry<EndianSlice<'a, LittleEndian>>, - fde: Option<FrameDescriptionEntry<EndianSlice<'a, LittleEndian>>>, - instructions: I, - ) where - I: AsRef< - [( - Result<bool>, - CallFrameInstruction<EndianSlice<'a, LittleEndian>>, - )], - >, - { - { - let section = &DebugFrame::from(EndianSlice::default()); - let bases = &BaseAddresses::default(); - let mut table = match fde { - Some(fde) => UnwindTable::new_for_fde(section, bases, &mut initial_ctx, &fde), - None => UnwindTable::new_for_cie(section, bases, &mut initial_ctx, &cie), - }; - for &(ref expected_result, ref instruction) in instructions.as_ref() { - assert_eq!(*expected_result, table.evaluate(instruction.clone())); - } - } - - assert_eq!(expected_ctx, initial_ctx); - } - - fn make_test_cie<'a>() -> CommonInformationEntry<EndianSlice<'a, LittleEndian>> { - CommonInformationEntry { - offset: 0, - format: Format::Dwarf64, - length: 0, - return_address_register: Register(0), - version: 4, - address_size: mem::size_of::<usize>() as u8, - initial_instructions: EndianSlice::new(&[], LittleEndian), - augmentation: None, - segment_size: 0, - data_alignment_factor: 2, - code_alignment_factor: 3, - } - } - - #[test] - fn test_eval_set_loc() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected.row_mut().end_address = 42; - let instructions = [(Ok(true), CallFrameInstruction::SetLoc { address: 42 })]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_set_loc_backwards() { - let cie = make_test_cie(); - let mut ctx = UnwindContext::new(); - ctx.row_mut().start_address = 999; - let expected = ctx.clone(); - let instructions = [( - Err(Error::InvalidAddressRange), - CallFrameInstruction::SetLoc { address: 42 }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_advance_loc() { - let cie = make_test_cie(); - let mut ctx = UnwindContext::new(); - ctx.row_mut().start_address = 3; - let mut expected = ctx.clone(); - expected.row_mut().end_address = 3 + 2 * cie.code_alignment_factor; - let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 2 })]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_advance_loc_overflow() { - let cie = make_test_cie(); - let mut ctx = UnwindContext::new(); - ctx.row_mut().start_address = u64::MAX; - let mut expected = ctx.clone(); - expected.row_mut().end_address = 42 * cie.code_alignment_factor - 1; - let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 42 })]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_def_cfa() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected.set_cfa(CfaRule::RegisterAndOffset { - register: Register(42), - offset: 36, - }); - let instructions = [( - Ok(false), - CallFrameInstruction::DefCfa { - register: Register(42), - offset: 36, - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_def_cfa_sf() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected.set_cfa(CfaRule::RegisterAndOffset { - register: Register(42), - offset: 36 * cie.data_alignment_factor as i64, - }); - let instructions = [( - Ok(false), - CallFrameInstruction::DefCfaSf { - register: Register(42), - factored_offset: 36, - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_def_cfa_register() { - let cie = make_test_cie(); - let mut ctx = UnwindContext::new(); - ctx.set_cfa(CfaRule::RegisterAndOffset { - register: Register(3), - offset: 8, - }); - let mut expected = ctx.clone(); - expected.set_cfa(CfaRule::RegisterAndOffset { - register: Register(42), - offset: 8, - }); - let instructions = [( - Ok(false), - CallFrameInstruction::DefCfaRegister { - register: Register(42), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_def_cfa_register_invalid_context() { - let cie = make_test_cie(); - let mut ctx = UnwindContext::new(); - ctx.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( - &[], - LittleEndian, - )))); - let expected = ctx.clone(); - let instructions = [( - Err(Error::CfiInstructionInInvalidContext), - CallFrameInstruction::DefCfaRegister { - register: Register(42), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_def_cfa_offset() { - let cie = make_test_cie(); - let mut ctx = UnwindContext::new(); - ctx.set_cfa(CfaRule::RegisterAndOffset { - register: Register(3), - offset: 8, - }); - let mut expected = ctx.clone(); - expected.set_cfa(CfaRule::RegisterAndOffset { - register: Register(3), - offset: 42, - }); - let instructions = [(Ok(false), CallFrameInstruction::DefCfaOffset { offset: 42 })]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_def_cfa_offset_invalid_context() { - let cie = make_test_cie(); - let mut ctx = UnwindContext::new(); - ctx.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( - &[], - LittleEndian, - )))); - let expected = ctx.clone(); - let instructions = [( - Err(Error::CfiInstructionInInvalidContext), - CallFrameInstruction::DefCfaOffset { offset: 1993 }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_def_cfa_expression() { - let expr = [1, 2, 3, 4]; - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected.set_cfa(CfaRule::Expression(Expression(EndianSlice::new( - &expr, - LittleEndian, - )))); - let instructions = [( - Ok(false), - CallFrameInstruction::DefCfaExpression { - expression: Expression(EndianSlice::new(&expr, LittleEndian)), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_undefined() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule(Register(5), RegisterRule::Undefined) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::Undefined { - register: Register(5), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_same_value() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule(Register(0), RegisterRule::SameValue) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::SameValue { - register: Register(0), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_offset() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule( - Register(2), - RegisterRule::Offset(3 * cie.data_alignment_factor), - ) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::Offset { - register: Register(2), - factored_offset: 3, - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_offset_extended_sf() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule( - Register(4), - RegisterRule::Offset(-3 * cie.data_alignment_factor), - ) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::OffsetExtendedSf { - register: Register(4), - factored_offset: -3, - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_val_offset() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule( - Register(5), - RegisterRule::ValOffset(7 * cie.data_alignment_factor), - ) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::ValOffset { - register: Register(5), - factored_offset: 7, - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_val_offset_sf() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule( - Register(5), - RegisterRule::ValOffset(-7 * cie.data_alignment_factor), - ) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::ValOffsetSf { - register: Register(5), - factored_offset: -7, - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_expression() { - let expr = [1, 2, 3, 4]; - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule( - Register(9), - RegisterRule::Expression(Expression(EndianSlice::new(&expr, LittleEndian))), - ) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::Expression { - register: Register(9), - expression: Expression(EndianSlice::new(&expr, LittleEndian)), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_val_expression() { - let expr = [1, 2, 3, 4]; - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule( - Register(9), - RegisterRule::ValExpression(Expression(EndianSlice::new(&expr, LittleEndian))), - ) - .unwrap(); - let instructions = [( - Ok(false), - CallFrameInstruction::ValExpression { - register: Register(9), - expression: Expression(EndianSlice::new(&expr, LittleEndian)), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_restore() { - let cie = make_test_cie(); - let fde = FrameDescriptionEntry { - offset: 0, - format: Format::Dwarf64, - length: 0, - address_range: 0, - augmentation: None, - initial_address: 0, - initial_segment: 0, - cie: cie.clone(), - instructions: EndianSlice::new(&[], LittleEndian), - }; - - let mut ctx = UnwindContext::new(); - ctx.set_register_rule(Register(0), RegisterRule::Offset(1)) - .unwrap(); - ctx.save_initial_rules().unwrap(); - let expected = ctx.clone(); - ctx.set_register_rule(Register(0), RegisterRule::Offset(2)) - .unwrap(); - - let instructions = [( - Ok(false), - CallFrameInstruction::Restore { - register: Register(0), - }, - )]; - assert_eval(ctx, expected, cie, Some(fde), instructions); - } - - #[test] - fn test_eval_restore_havent_saved_initial_context() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let expected = ctx.clone(); - let instructions = [( - Err(Error::CfiInstructionInInvalidContext), - CallFrameInstruction::Restore { - register: Register(0), - }, - )]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_remember_state() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected.push_row().unwrap(); - let instructions = [(Ok(false), CallFrameInstruction::RememberState)]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_restore_state() { - let cie = make_test_cie(); - - let mut ctx = UnwindContext::new(); - ctx.set_start_address(1); - ctx.set_register_rule(Register(0), RegisterRule::SameValue) - .unwrap(); - let mut expected = ctx.clone(); - ctx.push_row().unwrap(); - ctx.set_start_address(2); - ctx.set_register_rule(Register(0), RegisterRule::Offset(16)) - .unwrap(); - - // Restore state should preserve current location. - expected.set_start_address(2); - - let instructions = [ - // First one pops just fine. - (Ok(false), CallFrameInstruction::RestoreState), - // Second pop would try to pop out of bounds. - ( - Err(Error::PopWithEmptyStack), - CallFrameInstruction::RestoreState, - ), - ]; - - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_negate_ra_state() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(1)) - .unwrap(); - let instructions = [(Ok(false), CallFrameInstruction::NegateRaState)]; - assert_eval(ctx, expected, cie, None, instructions); - - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(0)) - .unwrap(); - let instructions = [ - (Ok(false), CallFrameInstruction::NegateRaState), - (Ok(false), CallFrameInstruction::NegateRaState), - ]; - assert_eval(ctx, expected, cie, None, instructions); - - // NegateRaState can't be used with other instructions. - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let mut expected = ctx.clone(); - expected - .set_register_rule( - crate::AArch64::RA_SIGN_STATE, - RegisterRule::Offset(cie.data_alignment_factor as i64), - ) - .unwrap(); - let instructions = [ - ( - Ok(false), - CallFrameInstruction::Offset { - register: crate::AArch64::RA_SIGN_STATE, - factored_offset: 1, - }, - ), - ( - Err(Error::CfiInstructionInInvalidContext), - CallFrameInstruction::NegateRaState, - ), - ]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_eval_nop() { - let cie = make_test_cie(); - let ctx = UnwindContext::new(); - let expected = ctx.clone(); - let instructions = [(Ok(false), CallFrameInstruction::Nop)]; - assert_eval(ctx, expected, cie, None, instructions); - } - - #[test] - fn test_unwind_table_cie_no_rule() { - let initial_instructions = Section::with_endian(Endian::Little) - // The CFA is -12 from register 4. - .D8(constants::DW_CFA_def_cfa_sf.0) - .uleb(4) - .sleb(-12) - .append_repeated(constants::DW_CFA_nop.0, 4); - let initial_instructions = initial_instructions.get_contents().unwrap(); - - let cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 8, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 1, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), - }; - - let instructions = Section::with_endian(Endian::Little) - // A bunch of nop padding. - .append_repeated(constants::DW_CFA_nop.0, 8); - let instructions = instructions.get_contents().unwrap(); - - let fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0, - address_range: 100, - augmentation: None, - instructions: EndianSlice::new(&instructions, LittleEndian), - }; - - let section = &DebugFrame::from(EndianSlice::default()); - let bases = &BaseAddresses::default(); - let mut ctx = Box::new(UnwindContext::new()); - - let mut table = fde - .rows(section, bases, &mut ctx) - .expect("Should run initial program OK"); - assert!(table.ctx.is_initialized); - let expected_initial_rule = (Register(0), RegisterRule::Undefined); - assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); - - { - let row = table.next_row().expect("Should evaluate first row OK"); - let expected = UnwindTableRow { - start_address: 0, - end_address: 100, - saved_args_size: 0, - cfa: CfaRule::RegisterAndOffset { - register: Register(4), - offset: -12, - }, - registers: [].iter().collect(), - }; - assert_eq!(Some(&expected), row); - } - - // All done! - assert_eq!(Ok(None), table.next_row()); - assert_eq!(Ok(None), table.next_row()); - } - - #[test] - fn test_unwind_table_cie_single_rule() { - let initial_instructions = Section::with_endian(Endian::Little) - // The CFA is -12 from register 4. - .D8(constants::DW_CFA_def_cfa_sf.0) - .uleb(4) - .sleb(-12) - // Register 3 is 4 from the CFA. - .D8(constants::DW_CFA_offset.0 | 3) - .uleb(4) - .append_repeated(constants::DW_CFA_nop.0, 4); - let initial_instructions = initial_instructions.get_contents().unwrap(); - - let cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 8, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 1, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), - }; - - let instructions = Section::with_endian(Endian::Little) - // A bunch of nop padding. - .append_repeated(constants::DW_CFA_nop.0, 8); - let instructions = instructions.get_contents().unwrap(); - - let fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0, - address_range: 100, - augmentation: None, - instructions: EndianSlice::new(&instructions, LittleEndian), - }; - - let section = &DebugFrame::from(EndianSlice::default()); - let bases = &BaseAddresses::default(); - let mut ctx = Box::new(UnwindContext::new()); - - let mut table = fde - .rows(section, bases, &mut ctx) - .expect("Should run initial program OK"); - assert!(table.ctx.is_initialized); - let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); - assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule)); - - { - let row = table.next_row().expect("Should evaluate first row OK"); - let expected = UnwindTableRow { - start_address: 0, - end_address: 100, - saved_args_size: 0, - cfa: CfaRule::RegisterAndOffset { - register: Register(4), - offset: -12, - }, - registers: [(Register(3), RegisterRule::Offset(4))].iter().collect(), - }; - assert_eq!(Some(&expected), row); - } - - // All done! - assert_eq!(Ok(None), table.next_row()); - assert_eq!(Ok(None), table.next_row()); - } - - #[test] - fn test_unwind_table_cie_invalid_rule() { - let initial_instructions1 = Section::with_endian(Endian::Little) - // Test that stack length is reset. - .D8(constants::DW_CFA_remember_state.0) - // Test that stack value is reset (different register from that used later). - .D8(constants::DW_CFA_offset.0 | 4) - .uleb(8) - // Invalid due to missing operands. - .D8(constants::DW_CFA_offset.0); - let initial_instructions1 = initial_instructions1.get_contents().unwrap(); - - let cie1 = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 8, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 1, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&initial_instructions1, LittleEndian), - }; - - let initial_instructions2 = Section::with_endian(Endian::Little) - // Register 3 is 4 from the CFA. - .D8(constants::DW_CFA_offset.0 | 3) - .uleb(4) - .append_repeated(constants::DW_CFA_nop.0, 4); - let initial_instructions2 = initial_instructions2.get_contents().unwrap(); - - let cie2 = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 8, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 1, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&initial_instructions2, LittleEndian), - }; - - let fde1 = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie1.clone(), - initial_segment: 0, - initial_address: 0, - address_range: 100, - augmentation: None, - instructions: EndianSlice::new(&[], LittleEndian), - }; - - let fde2 = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie2.clone(), - initial_segment: 0, - initial_address: 0, - address_range: 100, - augmentation: None, - instructions: EndianSlice::new(&[], LittleEndian), - }; - - let section = &DebugFrame::from(EndianSlice::default()); - let bases = &BaseAddresses::default(); - let mut ctx = Box::new(UnwindContext::new()); - - let table = fde1 - .rows(section, bases, &mut ctx) - .map_eof(&initial_instructions1); - assert_eq!(table.err(), Some(Error::UnexpectedEof(ReaderOffsetId(4)))); - assert!(!ctx.is_initialized); - assert_eq!(ctx.stack.len(), 2); - assert_eq!(ctx.initial_rule, None); - - let _table = fde2 - .rows(section, bases, &mut ctx) - .expect("Should run initial program OK"); - assert!(ctx.is_initialized); - assert_eq!(ctx.stack.len(), 1); - let expected_initial_rule = (Register(3), RegisterRule::Offset(4)); - assert_eq!(ctx.initial_rule, Some(expected_initial_rule)); - } - - #[test] - fn test_unwind_table_next_row() { - let initial_instructions = Section::with_endian(Endian::Little) - // The CFA is -12 from register 4. - .D8(constants::DW_CFA_def_cfa_sf.0) - .uleb(4) - .sleb(-12) - // Register 0 is 8 from the CFA. - .D8(constants::DW_CFA_offset.0 | 0) - .uleb(8) - // Register 3 is 4 from the CFA. - .D8(constants::DW_CFA_offset.0 | 3) - .uleb(4) - .append_repeated(constants::DW_CFA_nop.0, 4); - let initial_instructions = initial_instructions.get_contents().unwrap(); - - let cie = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 8, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 1, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian), - }; - - let instructions = Section::with_endian(Endian::Little) - // Initial instructions form a row, advance the address by 1. - .D8(constants::DW_CFA_advance_loc1.0) - .D8(1) - // Register 0 is -16 from the CFA. - .D8(constants::DW_CFA_offset_extended_sf.0) - .uleb(0) - .sleb(-16) - // Finish this row, advance the address by 32. - .D8(constants::DW_CFA_advance_loc1.0) - .D8(32) - // Register 3 is -4 from the CFA. - .D8(constants::DW_CFA_offset_extended_sf.0) - .uleb(3) - .sleb(-4) - // Finish this row, advance the address by 64. - .D8(constants::DW_CFA_advance_loc1.0) - .D8(64) - // Register 5 is 4 from the CFA. - .D8(constants::DW_CFA_offset.0 | 5) - .uleb(4) - // A bunch of nop padding. - .append_repeated(constants::DW_CFA_nop.0, 8); - let instructions = instructions.get_contents().unwrap(); - - let fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0, - address_range: 100, - augmentation: None, - instructions: EndianSlice::new(&instructions, LittleEndian), - }; - - let section = &DebugFrame::from(EndianSlice::default()); - let bases = &BaseAddresses::default(); - let mut ctx = Box::new(UnwindContext::new()); - - let mut table = fde - .rows(section, bases, &mut ctx) - .expect("Should run initial program OK"); - assert!(table.ctx.is_initialized); - assert!(table.ctx.initial_rule.is_none()); - let expected_initial_rules: RegisterRuleMap<_> = [ - (Register(0), RegisterRule::Offset(8)), - (Register(3), RegisterRule::Offset(4)), - ] - .iter() - .collect(); - assert_eq!(table.ctx.stack[0].registers, expected_initial_rules); - - { - let row = table.next_row().expect("Should evaluate first row OK"); - let expected = UnwindTableRow { - start_address: 0, - end_address: 1, - saved_args_size: 0, - cfa: CfaRule::RegisterAndOffset { - register: Register(4), - offset: -12, - }, - registers: [ - (Register(0), RegisterRule::Offset(8)), - (Register(3), RegisterRule::Offset(4)), - ] - .iter() - .collect(), - }; - assert_eq!(Some(&expected), row); - } - - { - let row = table.next_row().expect("Should evaluate second row OK"); - let expected = UnwindTableRow { - start_address: 1, - end_address: 33, - saved_args_size: 0, - cfa: CfaRule::RegisterAndOffset { - register: Register(4), - offset: -12, - }, - registers: [ - (Register(0), RegisterRule::Offset(-16)), - (Register(3), RegisterRule::Offset(4)), - ] - .iter() - .collect(), - }; - assert_eq!(Some(&expected), row); - } - - { - let row = table.next_row().expect("Should evaluate third row OK"); - let expected = UnwindTableRow { - start_address: 33, - end_address: 97, - saved_args_size: 0, - cfa: CfaRule::RegisterAndOffset { - register: Register(4), - offset: -12, - }, - registers: [ - (Register(0), RegisterRule::Offset(-16)), - (Register(3), RegisterRule::Offset(-4)), - ] - .iter() - .collect(), - }; - assert_eq!(Some(&expected), row); - } - - { - let row = table.next_row().expect("Should evaluate fourth row OK"); - let expected = UnwindTableRow { - start_address: 97, - end_address: 100, - saved_args_size: 0, - cfa: CfaRule::RegisterAndOffset { - register: Register(4), - offset: -12, - }, - registers: [ - (Register(0), RegisterRule::Offset(-16)), - (Register(3), RegisterRule::Offset(-4)), - (Register(5), RegisterRule::Offset(4)), - ] - .iter() - .collect(), - }; - assert_eq!(Some(&expected), row); - } - - // All done! - assert_eq!(Ok(None), table.next_row()); - assert_eq!(Ok(None), table.next_row()); - } - - #[test] - fn test_unwind_info_for_address_ok() { - let instrs1 = Section::with_endian(Endian::Big) - // The CFA is -12 from register 4. - .D8(constants::DW_CFA_def_cfa_sf.0) - .uleb(4) - .sleb(-12); - let instrs1 = instrs1.get_contents().unwrap(); - - let instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect(); - - let instrs3 = Section::with_endian(Endian::Big) - // Initial instructions form a row, advance the address by 100. - .D8(constants::DW_CFA_advance_loc1.0) - .D8(100) - // Register 0 is -16 from the CFA. - .D8(constants::DW_CFA_offset_extended_sf.0) - .uleb(0) - .sleb(-16); - let instrs3 = instrs3.get_contents().unwrap(); - - let instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect(); - - let mut cie1 = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 8, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 1, - return_address_register: Register(3), - initial_instructions: EndianSlice::new(&instrs1, BigEndian), - }; - - let mut cie2 = CommonInformationEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - version: 4, - augmentation: None, - address_size: 4, - segment_size: 0, - code_alignment_factor: 1, - data_alignment_factor: 1, - return_address_register: Register(1), - initial_instructions: EndianSlice::new(&instrs2, BigEndian), - }; - - let cie1_location = Label::new(); - let cie2_location = Label::new(); - - // Write the CIEs first so that their length gets set before we clone - // them into the FDEs and our equality assertions down the line end up - // with all the CIEs always having he correct length. - let kind = debug_frame_be(); - let section = Section::with_endian(kind.endian()) - .mark(&cie1_location) - .cie(kind, None, &mut cie1) - .mark(&cie2_location) - .cie(kind, None, &mut cie2); - - let mut fde1 = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie1.clone(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 200, - augmentation: None, - instructions: EndianSlice::new(&instrs3, BigEndian), - }; - - let mut fde2 = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie2.clone(), - initial_segment: 0, - initial_address: 0xfeed_face, - address_range: 9000, - augmentation: None, - instructions: EndianSlice::new(&instrs4, BigEndian), - }; - - let section = - section - .fde(kind, &cie1_location, &mut fde1) - .fde(kind, &cie2_location, &mut fde2); - section.start().set_const(0); - - let contents = section.get_contents().unwrap(); - let debug_frame = kind.section(&contents); - - // Get the second row of the unwind table in `instrs3`. - let bases = Default::default(); - let mut ctx = Box::new(UnwindContext::new()); - let result = debug_frame.unwind_info_for_address( - &bases, - &mut ctx, - 0xfeed_beef + 150, - DebugFrame::cie_from_offset, - ); - assert!(result.is_ok()); - let unwind_info = result.unwrap(); - - assert_eq!( - *unwind_info, - UnwindTableRow { - start_address: fde1.initial_address() + 100, - end_address: fde1.initial_address() + fde1.len(), - saved_args_size: 0, - cfa: CfaRule::RegisterAndOffset { - register: Register(4), - offset: -12, - }, - registers: [(Register(0), RegisterRule::Offset(-16))].iter().collect(), - } - ); - } - - #[test] - fn test_unwind_info_for_address_not_found() { - let debug_frame = DebugFrame::new(&[], NativeEndian); - let bases = Default::default(); - let mut ctx = Box::new(UnwindContext::new()); - let result = debug_frame.unwind_info_for_address( - &bases, - &mut ctx, - 0xbadb_ad99, - DebugFrame::cie_from_offset, - ); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress); - } - - #[test] - fn test_eh_frame_hdr_unknown_version() { - let bases = BaseAddresses::default(); - let buf = &[42]; - let result = EhFrameHdr::new(buf, NativeEndian).parse(&bases, 8); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), Error::UnknownVersion(42)); - } - - #[test] - fn test_eh_frame_hdr_omit_ehptr() { - let section = Section::with_endian(Endian::Little) - .L8(1) - .L8(0xff) - .L8(0x03) - .L8(0x0b) - .L32(2) - .L32(10) - .L32(1) - .L32(20) - .L32(2) - .L32(0); - let section = section.get_contents().unwrap(); - let bases = BaseAddresses::default(); - let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), Error::CannotParseOmitPointerEncoding); - } - - #[test] - fn test_eh_frame_hdr_omit_count() { - let section = Section::with_endian(Endian::Little) - .L8(1) - .L8(0x0b) - .L8(0xff) - .L8(0x0b) - .L32(0x12345); - let section = section.get_contents().unwrap(); - let bases = BaseAddresses::default(); - let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); - assert!(result.is_ok()); - let result = result.unwrap(); - assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); - assert!(result.table().is_none()); - } - - #[test] - fn test_eh_frame_hdr_omit_table() { - let section = Section::with_endian(Endian::Little) - .L8(1) - .L8(0x0b) - .L8(0x03) - .L8(0xff) - .L32(0x12345) - .L32(2); - let section = section.get_contents().unwrap(); - let bases = BaseAddresses::default(); - let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); - assert!(result.is_ok()); - let result = result.unwrap(); - assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); - assert!(result.table().is_none()); - } - - #[test] - fn test_eh_frame_hdr_varlen_table() { - let section = Section::with_endian(Endian::Little) - .L8(1) - .L8(0x0b) - .L8(0x03) - .L8(0x01) - .L32(0x12345) - .L32(2); - let section = section.get_contents().unwrap(); - let bases = BaseAddresses::default(); - let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); - assert!(result.is_ok()); - let result = result.unwrap(); - assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); - let table = result.table(); - assert!(table.is_some()); - let table = table.unwrap(); - assert_eq!( - table.lookup(0, &bases), - Err(Error::VariableLengthSearchTable) - ); - } - - #[test] - fn test_eh_frame_hdr_indirect_length() { - let section = Section::with_endian(Endian::Little) - .L8(1) - .L8(0x0b) - .L8(0x83) - .L8(0x0b) - .L32(0x12345) - .L32(2); - let section = section.get_contents().unwrap(); - let bases = BaseAddresses::default(); - let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); - assert!(result.is_err()); - assert_eq!(result.unwrap_err(), Error::UnsupportedPointerEncoding); - } - - #[test] - fn test_eh_frame_hdr_indirect_ptrs() { - let section = Section::with_endian(Endian::Little) - .L8(1) - .L8(0x8b) - .L8(0x03) - .L8(0x8b) - .L32(0x12345) - .L32(2) - .L32(10) - .L32(1) - .L32(20) - .L32(2); - let section = section.get_contents().unwrap(); - let bases = BaseAddresses::default(); - let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); - assert!(result.is_ok()); - let result = result.unwrap(); - assert_eq!(result.eh_frame_ptr(), Pointer::Indirect(0x12345)); - let table = result.table(); - assert!(table.is_some()); - let table = table.unwrap(); - assert_eq!( - table.lookup(0, &bases), - Err(Error::UnsupportedPointerEncoding) - ); - } - - #[test] - fn test_eh_frame_hdr_good() { - let section = Section::with_endian(Endian::Little) - .L8(1) - .L8(0x0b) - .L8(0x03) - .L8(0x0b) - .L32(0x12345) - .L32(2) - .L32(10) - .L32(1) - .L32(20) - .L32(2); - let section = section.get_contents().unwrap(); - let bases = BaseAddresses::default(); - let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8); - assert!(result.is_ok()); - let result = result.unwrap(); - assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345)); - let table = result.table(); - assert!(table.is_some()); - let table = table.unwrap(); - assert_eq!(table.lookup(0, &bases), Ok(Pointer::Direct(1))); - assert_eq!(table.lookup(9, &bases), Ok(Pointer::Direct(1))); - assert_eq!(table.lookup(10, &bases), Ok(Pointer::Direct(1))); - assert_eq!(table.lookup(11, &bases), Ok(Pointer::Direct(1))); - assert_eq!(table.lookup(19, &bases), Ok(Pointer::Direct(1))); - assert_eq!(table.lookup(20, &bases), Ok(Pointer::Direct(2))); - assert_eq!(table.lookup(21, &bases), Ok(Pointer::Direct(2))); - assert_eq!(table.lookup(100_000, &bases), Ok(Pointer::Direct(2))); - } - - #[test] - fn test_eh_frame_fde_for_address_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 kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .append_repeated(0, 16) - .mark(&start_of_cie) - .cie(kind, None, &mut cie) - .mark(&end_of_cie); - - let mut fde1 = FrameDescriptionEntry { - 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 = FrameDescriptionEntry { - 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(kind, (&start_of_fde1 - &start_of_cie + 4) as u64, &mut fde1) - .mark(&start_of_fde2) - .fde(kind, (&start_of_fde2 - &start_of_cie + 4) as u64, &mut fde2); - - section.start().set_const(0); - let section = section.get_contents().unwrap(); - let eh_frame = kind.section(§ion); - - // Setup eh_frame_hdr - let section = Section::with_endian(kind.endian()) - .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 mut iter = table.iter(&bases); - assert_eq!( - iter.next(), - Ok(Some(( - Pointer::Direct(10), - Pointer::Direct(0x12345 + start_of_fde1.value().unwrap() as u64) - ))) - ); - assert_eq!( - iter.next(), - Ok(Some(( - Pointer::Direct(20), - Pointer::Direct(0x12345 + start_of_fde2.value().unwrap() as u64) - ))) - ); - assert_eq!(iter.next(), Ok(None)); - - assert_eq!( - table.iter(&bases).nth(0), - Ok(Some(( - Pointer::Direct(10), - Pointer::Direct(0x12345 + start_of_fde1.value().unwrap() as u64) - ))) - ); - - assert_eq!( - table.iter(&bases).nth(1), - Ok(Some(( - Pointer::Direct(20), - Pointer::Direct(0x12345 + start_of_fde2.value().unwrap() as u64) - ))) - ); - assert_eq!(table.iter(&bases).nth(2), Ok(None)); - - let f = |_: &_, _: &_, o: EhFrameOffset| { - assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); - Ok(cie.clone()) - }; - assert_eq!( - table.fde_for_address(&eh_frame, &bases, 9, f), - Ok(fde1.clone()) - ); - assert_eq!( - table.fde_for_address(&eh_frame, &bases, 10, f), - Ok(fde1.clone()) - ); - assert_eq!(table.fde_for_address(&eh_frame, &bases, 11, f), Ok(fde1)); - assert_eq!( - table.fde_for_address(&eh_frame, &bases, 19, f), - Err(Error::NoUnwindInfoForAddress) - ); - assert_eq!( - table.fde_for_address(&eh_frame, &bases, 20, f), - Ok(fde2.clone()) - ); - assert_eq!(table.fde_for_address(&eh_frame, &bases, 21, f), Ok(fde2)); - assert_eq!( - table.fde_for_address(&eh_frame, &bases, 100_000, f), - Err(Error::NoUnwindInfoForAddress) - ); - } - - #[test] - fn test_eh_frame_stops_at_zero_length() { - let section = Section::with_endian(Endian::Little).L32(0); - let section = section.get_contents().unwrap(); - let rest = &mut EndianSlice::new(§ion, LittleEndian); - let bases = Default::default(); - - assert_eq!( - parse_cfi_entry(&bases, &EhFrame::new(&*section, LittleEndian), rest), - Ok(None) - ); - - assert_eq!( - EhFrame::new(§ion, LittleEndian).cie_from_offset(&bases, EhFrameOffset(0)), - Err(Error::NoEntryAtGivenOffset) - ); - } - - fn resolve_cie_offset(buf: &[u8], cie_offset: usize) -> Result<usize> { - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf64, - cie: make_test_cie(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 39, - augmentation: None, - instructions: EndianSlice::new(&[], LittleEndian), - }; - - let kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .append_bytes(&buf) - .fde(kind, cie_offset as u64, &mut fde) - .append_bytes(&buf); - - let section = section.get_contents().unwrap(); - let eh_frame = kind.section(§ion); - let input = &mut EndianSlice::new(§ion[buf.len()..], LittleEndian); - - let bases = Default::default(); - match parse_cfi_entry(&bases, &eh_frame, input) { - Ok(Some(CieOrFde::Fde(partial))) => Ok(partial.cie_offset.0), - Err(e) => Err(e), - otherwise => panic!("Unexpected result: {:#?}", otherwise), - } - } - - #[test] - fn test_eh_frame_resolve_cie_offset_ok() { - let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - let cie_offset = 2; - // + 4 for size of length field - assert_eq!( - resolve_cie_offset(&buf, buf.len() + 4 - cie_offset), - Ok(cie_offset) - ); - } - - #[test] - fn test_eh_frame_resolve_cie_offset_out_of_bounds() { - let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - assert_eq!( - resolve_cie_offset(&buf, buf.len() + 4 + 2), - Err(Error::OffsetOutOfBounds) - ); - } - - #[test] - fn test_eh_frame_resolve_cie_offset_underflow() { - let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; - assert_eq!( - resolve_cie_offset(&buf, ::core::usize::MAX), - Err(Error::OffsetOutOfBounds) - ); - } - - #[test] - fn test_eh_frame_fde_ok() { - 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(); - - // Write the CIE first so that its length gets set before we clone it - // into the FDE. - let kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .append_repeated(0, 16) - .mark(&start_of_cie) - .cie(kind, None, &mut cie) - .mark(&end_of_cie); - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 999, - augmentation: None, - instructions: EndianSlice::new(&[], LittleEndian), - }; - - let section = section - // +4 for the FDE length before the CIE offset. - .fde(kind, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde); - - section.start().set_const(0); - let section = section.get_contents().unwrap(); - let eh_frame = kind.section(§ion); - let section = EndianSlice::new(§ion, LittleEndian); - - let mut offset = None; - match parse_fde( - eh_frame, - &mut section.range_from(end_of_cie.value().unwrap() as usize..), - |_, _, o| { - offset = Some(o); - assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize)); - Ok(cie.clone()) - }, - ) { - Ok(actual) => assert_eq!(actual, fde), - otherwise => panic!("Unexpected result {:?}", otherwise), - } - assert!(offset.is_some()); - } - - #[test] - fn test_eh_frame_fde_out_of_bounds() { - let mut cie = make_test_cie(); - cie.version = 1; - - let end_of_cie = Label::new(); - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf64, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_beef, - address_range: 999, - augmentation: None, - instructions: EndianSlice::new(&[], LittleEndian), - }; - - let kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .cie(kind, None, &mut cie) - .mark(&end_of_cie) - .fde(kind, 99_999_999_999_999, &mut fde); - - section.start().set_const(0); - let section = section.get_contents().unwrap(); - let eh_frame = kind.section(§ion); - let section = EndianSlice::new(§ion, LittleEndian); - - let result = parse_fde( - eh_frame, - &mut section.range_from(end_of_cie.value().unwrap() as usize..), - UnwindSection::cie_from_offset, - ); - assert_eq!(result, Err(Error::OffsetOutOfBounds)); - } - - #[test] - fn test_augmentation_parse_not_z_augmentation() { - let augmentation = &mut EndianSlice::new(b"wtf", NativeEndian); - let bases = Default::default(); - let address_size = 8; - let section = EhFrame::new(&[], NativeEndian); - let input = &mut EndianSlice::new(&[], NativeEndian); - assert_eq!( - Augmentation::parse(augmentation, &bases, address_size, §ion, input), - Err(Error::UnknownAugmentation) - ); - } - - #[test] - fn test_augmentation_parse_just_signal_trampoline() { - let aug_str = &mut EndianSlice::new(b"S", LittleEndian); - let bases = Default::default(); - let address_size = 8; - let section = EhFrame::new(&[], LittleEndian); - let input = &mut EndianSlice::new(&[], LittleEndian); - - let mut augmentation = Augmentation::default(); - augmentation.is_signal_trampoline = true; - - assert_eq!( - Augmentation::parse(aug_str, &bases, address_size, §ion, input), - Ok(augmentation) - ); - } - - #[test] - fn test_augmentation_parse_unknown_part_of_z_augmentation() { - // The 'Z' character is not defined by the z-style augmentation. - let bases = Default::default(); - let address_size = 8; - let section = Section::with_endian(Endian::Little) - .uleb(4) - .append_repeated(4, 4) - .get_contents() - .unwrap(); - let section = EhFrame::new(§ion, LittleEndian); - let input = &mut section.section().clone(); - let augmentation = &mut EndianSlice::new(b"zZ", LittleEndian); - assert_eq!( - Augmentation::parse(augmentation, &bases, address_size, §ion, input), - Err(Error::UnknownAugmentation) - ); - } - - #[test] - #[allow(non_snake_case)] - fn test_augmentation_parse_L() { - let bases = Default::default(); - let address_size = 8; - let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let section = Section::with_endian(Endian::Little) - .uleb(1) - .D8(constants::DW_EH_PE_uleb128.0) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = EhFrame::new(§ion, LittleEndian); - let input = &mut section.section().clone(); - let aug_str = &mut EndianSlice::new(b"zL", LittleEndian); - - let mut augmentation = Augmentation::default(); - augmentation.lsda = Some(constants::DW_EH_PE_uleb128); - - assert_eq!( - Augmentation::parse(aug_str, &bases, address_size, §ion, input), - Ok(augmentation) - ); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - #[allow(non_snake_case)] - fn test_augmentation_parse_P() { - let bases = Default::default(); - let address_size = 8; - let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let section = Section::with_endian(Endian::Little) - .uleb(9) - .D8(constants::DW_EH_PE_udata8.0) - .L64(0xf00d_f00d) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = EhFrame::new(§ion, LittleEndian); - let input = &mut section.section().clone(); - let aug_str = &mut EndianSlice::new(b"zP", LittleEndian); - - let mut augmentation = Augmentation::default(); - augmentation.personality = Some((constants::DW_EH_PE_udata8, Pointer::Direct(0xf00d_f00d))); - - assert_eq!( - Augmentation::parse(aug_str, &bases, address_size, §ion, input), - Ok(augmentation) - ); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - #[allow(non_snake_case)] - fn test_augmentation_parse_R() { - let bases = Default::default(); - let address_size = 8; - let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let section = Section::with_endian(Endian::Little) - .uleb(1) - .D8(constants::DW_EH_PE_udata4.0) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = EhFrame::new(§ion, LittleEndian); - let input = &mut section.section().clone(); - let aug_str = &mut EndianSlice::new(b"zR", LittleEndian); - - let mut augmentation = Augmentation::default(); - augmentation.fde_address_encoding = Some(constants::DW_EH_PE_udata4); - - assert_eq!( - Augmentation::parse(aug_str, &bases, address_size, §ion, input), - Ok(augmentation) - ); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - #[allow(non_snake_case)] - fn test_augmentation_parse_S() { - let bases = Default::default(); - let address_size = 8; - let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let section = Section::with_endian(Endian::Little) - .uleb(0) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = EhFrame::new(§ion, LittleEndian); - let input = &mut section.section().clone(); - let aug_str = &mut EndianSlice::new(b"zS", LittleEndian); - - let mut augmentation = Augmentation::default(); - augmentation.is_signal_trampoline = true; - - assert_eq!( - Augmentation::parse(aug_str, &bases, address_size, §ion, input), - Ok(augmentation) - ); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - fn test_augmentation_parse_all() { - let bases = Default::default(); - let address_size = 8; - let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1]; - - let section = Section::with_endian(Endian::Little) - .uleb(1 + 9 + 1) - // L - .D8(constants::DW_EH_PE_uleb128.0) - // P - .D8(constants::DW_EH_PE_udata8.0) - .L64(0x1bad_f00d) - // R - .D8(constants::DW_EH_PE_uleb128.0) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = EhFrame::new(§ion, LittleEndian); - let input = &mut section.section().clone(); - let aug_str = &mut EndianSlice::new(b"zLPRS", LittleEndian); - - let augmentation = Augmentation { - lsda: Some(constants::DW_EH_PE_uleb128), - personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0x1bad_f00d))), - fde_address_encoding: Some(constants::DW_EH_PE_uleb128), - is_signal_trampoline: true, - }; - - assert_eq!( - Augmentation::parse(aug_str, &bases, address_size, §ion, input), - Ok(augmentation) - ); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - fn test_eh_frame_fde_no_augmentation() { - let instrs = [1, 2, 3, 4]; - let cie_offset = 1; - - let mut cie = make_test_cie(); - cie.format = Format::Dwarf32; - cie.version = 1; - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_face, - address_range: 9000, - augmentation: None, - instructions: EndianSlice::new(&instrs, LittleEndian), - }; - - let rest = [1, 2, 3, 4]; - - let kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = kind.section(§ion); - let input = &mut section.section().clone(); - - let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); - assert_eq!(result, Ok(fde)); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - fn test_eh_frame_fde_empty_augmentation() { - let instrs = [1, 2, 3, 4]; - let cie_offset = 1; - - let mut cie = make_test_cie(); - cie.format = Format::Dwarf32; - cie.version = 1; - cie.augmentation = Some(Augmentation::default()); - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_face, - address_range: 9000, - augmentation: Some(AugmentationData::default()), - instructions: EndianSlice::new(&instrs, LittleEndian), - }; - - let rest = [1, 2, 3, 4]; - - let kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = kind.section(§ion); - let input = &mut section.section().clone(); - - let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); - assert_eq!(result, Ok(fde)); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - fn test_eh_frame_fde_lsda_augmentation() { - let instrs = [1, 2, 3, 4]; - let cie_offset = 1; - - let mut cie = make_test_cie(); - cie.format = Format::Dwarf32; - cie.version = 1; - cie.augmentation = Some(Augmentation::default()); - cie.augmentation.as_mut().unwrap().lsda = Some(constants::DW_EH_PE_absptr); - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_face, - address_range: 9000, - augmentation: Some(AugmentationData { - lsda: Some(Pointer::Direct(0x1122_3344)), - }), - instructions: EndianSlice::new(&instrs, LittleEndian), - }; - - let rest = [1, 2, 3, 4]; - - let kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = kind.section(§ion); - let input = &mut section.section().clone(); - - let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); - assert_eq!(result, Ok(fde)); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - fn test_eh_frame_fde_lsda_function_relative() { - let instrs = [1, 2, 3, 4]; - let cie_offset = 1; - - let mut cie = make_test_cie(); - cie.format = Format::Dwarf32; - cie.version = 1; - cie.augmentation = Some(Augmentation::default()); - cie.augmentation.as_mut().unwrap().lsda = Some(constants::DwEhPe( - constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_absptr.0, - )); - - let mut fde = FrameDescriptionEntry { - offset: 0, - length: 0, - format: Format::Dwarf32, - cie: cie.clone(), - initial_segment: 0, - initial_address: 0xfeed_face, - address_range: 9000, - augmentation: Some(AugmentationData { - lsda: Some(Pointer::Direct(0xbeef)), - }), - instructions: EndianSlice::new(&instrs, LittleEndian), - }; - - let rest = [1, 2, 3, 4]; - - let kind = eh_frame_le(); - let section = Section::with_endian(kind.endian()) - .append_repeated(10, 10) - .fde(kind, cie_offset, &mut fde) - .append_bytes(&rest) - .get_contents() - .unwrap(); - let section = kind.section(§ion); - let input = &mut section.section().range_from(10..); - - // Adjust the FDE's augmentation to be relative to the function. - fde.augmentation.as_mut().unwrap().lsda = Some(Pointer::Direct(0xfeed_face + 0xbeef)); - - let result = parse_fde(section, input, |_, _, _| Ok(cie.clone())); - assert_eq!(result, Ok(fde)); - assert_eq!(*input, EndianSlice::new(&rest, LittleEndian)); - } - - #[test] - fn test_eh_frame_cie_personality_function_relative_bad_context() { - let instrs = [1, 2, 3, 4]; - - let length = Label::new(); - let start = Label::new(); - let end = Label::new(); - - let aug_len = Label::new(); - let aug_start = Label::new(); - let aug_end = Label::new(); - - let section = Section::with_endian(Endian::Little) - // Length - .L32(&length) - .mark(&start) - // CIE ID - .L32(0) - // Version - .D8(1) - // Augmentation - .append_bytes(b"zP\0") - // Code alignment factor - .uleb(1) - // Data alignment factor - .sleb(1) - // Return address register - .uleb(1) - // Augmentation data length. This is a uleb, be we rely on the value - // being less than 2^7 and therefore a valid uleb (can't use Label - // with uleb). - .D8(&aug_len) - .mark(&aug_start) - // Augmentation data. Personality encoding and then encoded pointer. - .D8(constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_uleb128.0) - .uleb(1) - .mark(&aug_end) - // Initial instructions - .append_bytes(&instrs) - .mark(&end); - - length.set_const((&end - &start) as u64); - aug_len.set_const((&aug_end - &aug_start) as u64); - - let section = section.get_contents().unwrap(); - let section = EhFrame::new(§ion, LittleEndian); - - let bases = BaseAddresses::default(); - let mut iter = section.entries(&bases); - assert_eq!(iter.next(), Err(Error::FuncRelativePointerInBadContext)); - } - - #[test] - fn register_rule_map_eq() { - // Different order, but still equal. - let map1: RegisterRuleMap<EndianSlice<LittleEndian>> = [ - (Register(0), RegisterRule::SameValue), - (Register(3), RegisterRule::Offset(1)), - ] - .iter() - .collect(); - let map2: RegisterRuleMap<EndianSlice<LittleEndian>> = [ - (Register(3), RegisterRule::Offset(1)), - (Register(0), RegisterRule::SameValue), - ] - .iter() - .collect(); - assert_eq!(map1, map2); - assert_eq!(map2, map1); - - // Not equal. - let map3: RegisterRuleMap<EndianSlice<LittleEndian>> = [ - (Register(0), RegisterRule::SameValue), - (Register(2), RegisterRule::Offset(1)), - ] - .iter() - .collect(); - let map4: RegisterRuleMap<EndianSlice<LittleEndian>> = [ - (Register(3), RegisterRule::Offset(1)), - (Register(0), RegisterRule::SameValue), - ] - .iter() - .collect(); - assert!(map3 != map4); - assert!(map4 != map3); - - // One has undefined explicitly set, other implicitly has undefined. - let mut map5 = RegisterRuleMap::<EndianSlice<LittleEndian>>::default(); - map5.set(Register(0), RegisterRule::SameValue).unwrap(); - map5.set(Register(0), RegisterRule::Undefined).unwrap(); - let map6 = RegisterRuleMap::<EndianSlice<LittleEndian>>::default(); - assert_eq!(map5, map6); - assert_eq!(map6, map5); - } - - #[test] - fn iter_register_rules() { - let mut row = UnwindTableRow::<EndianSlice<LittleEndian>>::default(); - row.registers = [ - (Register(0), RegisterRule::SameValue), - (Register(1), RegisterRule::Offset(1)), - (Register(2), RegisterRule::ValOffset(2)), - ] - .iter() - .collect(); - - let mut found0 = false; - let mut found1 = false; - let mut found2 = false; - - for &(register, ref rule) in row.registers() { - match register.0 { - 0 => { - assert_eq!(found0, false); - found0 = true; - assert_eq!(*rule, RegisterRule::SameValue); - } - 1 => { - assert_eq!(found1, false); - found1 = true; - assert_eq!(*rule, RegisterRule::Offset(1)); - } - 2 => { - assert_eq!(found2, false); - found2 = true; - assert_eq!(*rule, RegisterRule::ValOffset(2)); - } - x => panic!("Unexpected register rule: ({}, {:?})", x, rule), - } - } - - assert_eq!(found0, true); - assert_eq!(found1, true); - assert_eq!(found2, true); - } - - #[test] - #[cfg(target_pointer_width = "64")] - fn size_of_unwind_ctx() { - use core::mem; - let size = mem::size_of::<UnwindContext<EndianSlice<NativeEndian>>>(); - let max_size = 30968; - if size > max_size { - assert_eq!(size, max_size); - } - } - - #[test] - #[cfg(target_pointer_width = "64")] - fn size_of_register_rule_map() { - use core::mem; - let size = mem::size_of::<RegisterRuleMap<EndianSlice<NativeEndian>>>(); - let max_size = 6152; - if size > max_size { - assert_eq!(size, max_size); - } - } - - #[test] - fn test_parse_pointer_encoding_ok() { - use crate::endianity::NativeEndian; - let expected = - constants::DwEhPe(constants::DW_EH_PE_uleb128.0 | constants::DW_EH_PE_pcrel.0); - let input = [expected.0, 1, 2, 3, 4]; - let input = &mut EndianSlice::new(&input, NativeEndian); - assert_eq!(parse_pointer_encoding(input), Ok(expected)); - assert_eq!(*input, EndianSlice::new(&[1, 2, 3, 4], NativeEndian)); - } - - #[test] - fn test_parse_pointer_encoding_bad_encoding() { - use crate::endianity::NativeEndian; - let expected = - constants::DwEhPe((constants::DW_EH_PE_sdata8.0 + 1) | constants::DW_EH_PE_pcrel.0); - let input = [expected.0, 1, 2, 3, 4]; - let input = &mut EndianSlice::new(&input, NativeEndian); - assert_eq!( - Err(Error::UnknownPointerEncoding), - parse_pointer_encoding(input) - ); - } - - #[test] - fn test_parse_encoded_pointer_absptr() { - let encoding = constants::DW_EH_PE_absptr; - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .L32(0xf00d_f00d) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0xf00d_f00d)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_pcrel() { - let encoding = constants::DW_EH_PE_pcrel; - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .append_repeated(0, 0x10) - .L32(0x1) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input.range_from(0x10..); - - let parameters = PointerEncodingParameters { - bases: &BaseAddresses::default().set_eh_frame(0x100).eh_frame, - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x111)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_pcrel_undefined() { - let encoding = constants::DW_EH_PE_pcrel; - - let input = Section::with_endian(Endian::Little).L32(0x1); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Err(Error::PcRelativePointerButSectionBaseIsUndefined) - ); - } - - #[test] - fn test_parse_encoded_pointer_textrel() { - let encoding = constants::DW_EH_PE_textrel; - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .L32(0x1) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &BaseAddresses::default().set_text(0x10).eh_frame, - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x11)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_textrel_undefined() { - let encoding = constants::DW_EH_PE_textrel; - - let input = Section::with_endian(Endian::Little).L32(0x1); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Err(Error::TextRelativePointerButTextBaseIsUndefined) - ); - } - - #[test] - fn test_parse_encoded_pointer_datarel() { - let encoding = constants::DW_EH_PE_datarel; - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .L32(0x1) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &BaseAddresses::default().set_got(0x10).eh_frame, - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x11)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_datarel_undefined() { - let encoding = constants::DW_EH_PE_datarel; - - let input = Section::with_endian(Endian::Little).L32(0x1); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Err(Error::DataRelativePointerButDataBaseIsUndefined) - ); - } - - #[test] - fn test_parse_encoded_pointer_funcrel() { - let encoding = constants::DW_EH_PE_funcrel; - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .L32(0x1) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: Some(0x10), - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x11)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_funcrel_undefined() { - let encoding = constants::DW_EH_PE_funcrel; - - let input = Section::with_endian(Endian::Little).L32(0x1); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Err(Error::FuncRelativePointerInBadContext) - ); - } - - #[test] - fn test_parse_encoded_pointer_uleb128() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_uleb128.0); - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .uleb(0x12_3456) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x12_3456)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_udata2() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata2.0); - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .L16(0x1234) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x1234)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_udata4() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata4.0); - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .L32(0x1234_5678) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x1234_5678)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_udata8() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_udata8.0); - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .L64(0x1234_5678_1234_5678) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x1234_5678_1234_5678)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_sleb128() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_textrel.0 | constants::DW_EH_PE_sleb128.0); - let expected_rest = [1, 2, 3, 4]; - - let input = Section::with_endian(Endian::Little) - .sleb(-0x1111) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &BaseAddresses::default().set_text(0x1111_1111).eh_frame, - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(0x1111_0000)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_sdata2() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata2.0); - let expected_rest = [1, 2, 3, 4]; - let expected = 0x111 as i16; - - let input = Section::with_endian(Endian::Little) - .L16(expected as u16) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(expected as u64)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_sdata4() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata4.0); - let expected_rest = [1, 2, 3, 4]; - let expected = 0x111_1111 as i32; - - let input = Section::with_endian(Endian::Little) - .L32(expected as u32) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(expected as u64)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_sdata8() { - let encoding = - constants::DwEhPe(constants::DW_EH_PE_absptr.0 | constants::DW_EH_PE_sdata8.0); - let expected_rest = [1, 2, 3, 4]; - let expected = -0x11_1111_1222_2222 as i64; - - let input = Section::with_endian(Endian::Little) - .L64(expected as u64) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Direct(expected as u64)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } - - #[test] - fn test_parse_encoded_pointer_omit() { - let encoding = constants::DW_EH_PE_omit; - - let input = Section::with_endian(Endian::Little).L32(0x1); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Err(Error::CannotParseOmitPointerEncoding) - ); - assert_eq!(rest, input); - } - - #[test] - fn test_parse_encoded_pointer_bad_encoding() { - let encoding = constants::DwEhPe(constants::DW_EH_PE_sdata8.0 + 1); - - let input = Section::with_endian(Endian::Little).L32(0x1); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Err(Error::UnknownPointerEncoding) - ); - } - - #[test] - fn test_parse_encoded_pointer_aligned() { - // FIXME: support this encoding! - - let encoding = constants::DW_EH_PE_aligned; - - let input = Section::with_endian(Endian::Little).L32(0x1); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Err(Error::UnsupportedPointerEncoding) - ); - } - - #[test] - fn test_parse_encoded_pointer_indirect() { - let expected_rest = [1, 2, 3, 4]; - let encoding = constants::DW_EH_PE_indirect; - - let input = Section::with_endian(Endian::Little) - .L32(0x1234_5678) - .append_bytes(&expected_rest); - let input = input.get_contents().unwrap(); - let input = EndianSlice::new(&input, LittleEndian); - let mut rest = input; - - let parameters = PointerEncodingParameters { - bases: &SectionBaseAddresses::default(), - func_base: None, - address_size: 4, - section: &input, - }; - assert_eq!( - parse_encoded_pointer(encoding, ¶meters, &mut rest), - Ok(Pointer::Indirect(0x1234_5678)) - ); - assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian)); - } -} |