/// `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)] pubstruct 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. pubfn set_address_size(&mutself, 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. pubfn set_segment_size(&mutself, segment_size: u8) { self.segment_size = segment_size
}
/// Set the vendor extensions to use. /// /// This defaults to `Vendor::Default`. pubfn set_vendor(&mutself, 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); /// ``` pubfn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian))
}
}
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)] pubstruct EhFrameHdr<R: Reader>(R);
/// `ParsedEhFrameHdr` contains the parsed information from the `.eh_frame_hdr` section. #[derive(Clone, Debug)] pubstruct ParsedEhFrameHdr<R: Reader> {
address_size: u8,
section: 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. pubfn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian))
}
}
impl<R: Reader> EhFrameHdr<R> { /// Parses this `EhFrameHdr` to a `ParsedEhFrameHdr`. pubfn parse(&self, bases: &BaseAddresses, address_size: u8) -> Result<ParsedEhFrameHdr<R>> { letmut 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)?;
// 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)?;
impl<R: Reader> ParsedEhFrameHdr<R> { /// Returns the address of the binary's `.eh_frame` section. pubfn eh_frame_ptr(&self) -> Pointer { self.eh_frame_ptr
}
/// Retrieves the CFI binary search table, if there is one. pubfn 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. ifself.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)] pubstruct 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`. pubfn next(&mutself) -> Result<Option<(Pointer, Pointer)>> { ifself.remain == 0 { return Ok(None);
}
/// The CFI binary search table that is an optional part of the `.eh_frame_hdr` section. #[derive(Debug, Clone)] pubstruct 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. pubfn 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. pubfn lookup(&self, address: u64, bases: &BaseAddresses) -> Result<Pointer> { let size = matchself.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(self.hdr.table_enc)),
};
/// Convert a `Pointer` to a section offset. /// /// This does not support indirect pointers. pubfn 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(()) /// # } /// ``` pubfn 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.")] pubfn 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. pubfn unwind_info_for_address<'ctx, F, A: UnwindContextStorage<R::Offset>>(
&self,
frame: &EhFrame<R>,
bases: &BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, A>,
address: u64,
get_cie: F,
) -> Result<&'ctx UnwindTableRow<R::Offset, 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)] pubstruct 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. pubfn set_address_size(&mutself, address_size: u8) { self.address_size = address_size
}
/// Set the vendor extensions to use. /// /// This defaults to `Vendor::Default`. pubfn set_vendor(&mutself, 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); /// ``` pubfn new(section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(section, endian))
}
}
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)] pubenum CieOffsetEncoding {
U32,
U64,
}
/// An offset into an `UnwindSection`. // // Needed to avoid conflicting implementations of `Into<T>`. pubtrait 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)] pubtrait _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. pubtrait 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 = &mutself.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 = &mutself.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>>,
{ letmut entries = self.entries(bases); whilelet 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::Offset>>(
&self,
bases: &BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, A>,
address: u64,
get_cie: F,
) -> Result<&'ctx UnwindTableRow<R::Offset, 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> 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)] pubstruct 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)] pubstruct 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] pubfn set_eh_frame_hdr(mutself, 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] pubfn set_eh_frame(mutself, addr: u64) -> Self { self.eh_frame.section = Some(addr); self
}
/// Set the `.text` section base address. #[inline] pubfn set_text(mutself, addr: u64) -> Self { self.eh_frame_hdr.text = Some(addr); self.eh_frame.text = Some(addr); self
}
/// Set the `.got` section base address. #[inline] pubfn set_got(mutself, 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)] pubstruct 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. pubfn next(&mutself) -> Result<Option<CieOrFde<'bases, Section, R>>> { ifself.input.is_empty() { return Ok(None);
}
/// Either a `CommonInformationEntry` (CIE) or a `FrameDescriptionEntry` (FDE). #[derive(Clone, Debug, PartialEq, Eq)] pubenum 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);
}
};
letmut 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)] pubstruct 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"
);
letmut augmentation = Augmentation::default();
letmut parsed_first = false; letmut 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(),
};
/// 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.
/// > 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)] pubstruct 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,
}
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 (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. pubfn offset(&self) -> R::Offset { self.offset
}
/// Return the encoding parameters for this CIE. pubfn 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. pubfn address_size(&self) -> u8 { self.address_size
}
/// Iterate over this CIE's initial instructions. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pubfn 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. pubfn 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. pubfn 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`. pubfn augmentation(&self) -> Option<&Augmentation> { self.augmentation.as_ref()
}
/// True if this CIE's FDEs have a LSDA. pubfn 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. pubfn 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. pubfn 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. pubfn 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. pubfn 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. pubfn 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). pubfn 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). pubfn 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. pubfn 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)] pubstruct 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,
}
/// 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.) pubfn 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. pubfn offset(&self) -> R::Offset { self.offset
}
/// Get the offset of this FDE's CIE. pubfn 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. pubfn entry_len(&self) -> R::Offset { self.length
}
}
/// A `FrameDescriptionEntry` is a set of CFA instructions for an address range. #[derive(Clone, Debug, PartialEq, Eq)] pubstruct 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,
}
// 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] pubfn rows<'a, 'ctx, Section: UnwindSection<R>, A: UnwindContextStorage<R::Offset>>(
&self,
section: &'a Section,
bases: &'a BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, 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. pubfn unwind_info_for_address< 'ctx,
Section: UnwindSection<R>,
A: UnwindContextStorage<R::Offset>,
>(
&self,
section: &Section,
bases: &BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, A>,
address: u64,
) -> Result<&'ctx UnwindTableRow<R::Offset, A>> { letmut table = self.rows(section, bases, ctx)?; whilelet 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. pubfn offset(&self) -> R::Offset { self.offset
}
/// Get a reference to this FDE's CIE. pubfn 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. pubfn 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). pubfn 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. pubfn initial_address(&self) -> u64 { self.initial_address
}
/// The number of bytes of instructions that this entry has unwind /// information for. pubfn 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()`. pubfn 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. pubfn 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] pubfn 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] pubfn 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 [`Box`]. This is the default storage type parameter for [`UnwindContext`].
You may want to supply your own storage typefor one of the following reasons:
1. In rare cases you may run into failed unwinds due to the fixed stack size
used by [`StoreOnHeap`], so you may want to try a larger `Box`. If denial
of service is not a concern, then you could also try a `Vec`-based stack which
can grow as needed. 2. You may want to avoid heap allocations entirely. You can use a fixed-size
stack with in-line arrays, which will place the entire storage in-line into
[`UnwindContext`]. "
)] /// /// Here's an implementation which uses a fixed-size stack and allocates everything in-line, /// which will cause `UnwindContext` to be large: /// /// ```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<T: ReaderOffset> UnwindContextStorage<T> for StoreOnStack { /// type Rules = [(Register, RegisterRule<T>); 192]; /// type Stack = [UnwindTableRow<T, 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!() /// # } /// ``` pubtrait UnwindContextStorage<T: ReaderOffset>: 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<T>)>;
/// The storage used for unwind table row stack. type Stack: ArrayLike<Item = UnwindTableRow<T, Self>>;
}
#[cfg(feature = "read")] impl<T: ReaderOffset> UnwindContextStorage<T> for StoreOnHeap { type Rules = [(Register, RegisterRule<T>); MAX_RULES]; type Stack = Box<[UnwindTableRow<T, Self>; MAX_UNWIND_STACK_DEPTH]>;
}
/// Common context needed when evaluating the call frame unwinding information. /// /// By default, this structure is small and allocates its internal storage /// on the heap using [`Box`] during [`UnwindContext::new`]. /// /// This can be overridden by providing a custom [`UnwindContextStorage`] type parameter. /// When using a custom storage with in-line arrays, the [`UnwindContext`] type itself /// will be big, so in that case it's recommended to place [`UnwindContext`] on the /// heap, e.g. using `Box::new(UnwindContext::<R, MyCustomStorage>::new_in())`. /// /// To avoid re-allocating the context multiple times when evaluating multiple /// CFI programs, the same [`UnwindContext`] can be reused for multiple unwinds. /// /// ``` /// 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 = 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)] pubstruct UnwindContext<T: ReaderOffset, A: UnwindContextStorage<T> = 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<T>)>,
/// # 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<T: ReaderOffset, A: UnwindContextStorage<T>> UnwindContext<T, A> { /// Construct a new call frame unwinding context. pubfn new_in() -> Self { letmut 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, R>(
&mutself,
section: &Section,
bases: &BaseAddresses,
cie: &CommonInformationEntry<R>,
) -> Result<()> where
R: Reader<Offset = T>,
Section: UnwindSection<R>,
{ // Always reset because previous initialization failure may leave dirty state. self.reset();
letmut table = UnwindTable::new_for_cie(section, bases, self, cie); while table.next_row()?.is_some() {}
/// 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)] pubstruct UnwindTable<'a, 'ctx, R: Reader, A: UnwindContextStorage<R::Offset> = 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::Offset, 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::Offset>> UnwindTable<'a, 'ctx, R, A> { /// Construct a new `UnwindTable` for the given /// `FrameDescriptionEntry`'s CFI unwinding program. pubfn new<Section: UnwindSection<R>>(
section: &'a Section,
bases: &'a BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, A>,
fde: &FrameDescriptionEntry<R>,
) -> Result<Self> {
ctx.initialize(section, bases, fde.cie())?;
Ok(Self::new_for_fde(section, bases, ctx, fde))
}
/// 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. pubfn next_row(&mutself) -> Result<Option<&UnwindTableRow<R::Offset, A>>> {
assert!(self.ctx.stack.len() >= 1); self.ctx.set_start_address(self.next_start_address); self.current_row_valid = false;
/// Returns the current row with the lifetime of the context. pubfn into_current_row(self) -> Option<&'ctx UnwindTableRow<R::Offset, A>> { ifself.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(&mutself, instruction: CallFrameInstruction<R::Offset>) -> Result<bool> { usecrate::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);
}
// 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;
}
/// # Signal Safe Methods /// /// These methods are guaranteed not to allocate, acquire locks, or perform any /// other signal-unsafe operations. impl<T: ReaderOffset, S: UnwindContextStorage<T>> RegisterRuleMap<T, S> { fn is_default(&self) -> bool { self.rules.is_empty()
}
impl<'a, R, S: UnwindContextStorage<R>> FromIterator<&'a (Register, RegisterRule<R>)> for RegisterRuleMap<R, S> where
R: 'a + ReaderOffset,
{ fn from_iter<T>(iter: T) -> Self where
T: IntoIterator<Item = &'a (Register, RegisterRule<R>)>,
{ let iter = iter.into_iter(); letmut 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<T, S: UnwindContextStorage<T>> PartialEq for RegisterRuleMap<T, S> where
T: ReaderOffset + PartialEq,
{ fn eq(&self, rhs: &Self) -> bool { for &(reg, ref rule) in &*self.rules {
debug_assert!(rule.is_defined()); if *rule != rhs.get(reg) { returnfalse;
}
}
for &(reg, ref rhs_rule) in &*rhs.rules {
debug_assert!(rhs_rule.is_defined()); if *rhs_rule != self.get(reg) { returnfalse;
}
}
true
}
}
impl<T, S: UnwindContextStorage<T>> Eq for RegisterRuleMap<T, S> where T: ReaderOffset + Eq {}
/// An unordered iterator for register rules. #[derive(Debug, Clone)] pubstruct RegisterRuleIter<'iter, T>(::core::slice::Iter<'iter, (Register, RegisterRule<T>)>) where
T: ReaderOffset;
impl<'iter, T: ReaderOffset> Iterator for RegisterRuleIter<'iter, T> { type Item = &'iter (Register, RegisterRule<T>);
/// 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)] pubstruct UnwindTableRow<T: ReaderOffset, S: UnwindContextStorage<T> = StoreOnHeap> {
start_address: u64,
end_address: u64,
saved_args_size: u64,
cfa: CfaRule<T>,
registers: RegisterRuleMap<T, S>,
}
/// Get the starting PC address that this row applies to. pubfn 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()`. pubfn end_address(&self) -> u64 { self.end_address
}
/// Return `true` if the given `address` is within this row's address range, /// `false` otherwise. pubfn 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. pubfn saved_args_size(&self) -> u64 { self.saved_args_size
}
/// Get the canonical frame address (CFA) recovery rule for this row. pubfn cfa(&self) -> &CfaRule<T> {
&self.cfa
}
/// 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<usize>) { /// for &(register, ref rule) in unwind_table_row.registers() { /// // ... /// # drop(register); drop(rule); /// } /// # } /// ``` pubfn registers(&self) -> RegisterRuleIter<'_, T> { self.registers.iter()
}
}
/// The canonical frame address (CFA) recovery rules. #[derive(Clone, Debug, PartialEq, Eq)] pubenum CfaRule<T: ReaderOffset> { /// 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(UnwindExpression<T>),
}
/// 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] pubenum RegisterRule<T: ReaderOffset> { /// > 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(UnwindExpression<T>),
/// "The previous value of this register is the value produced by executing /// the DWARF expression."
ValExpression(UnwindExpression<T>),
/// "The rule is defined externally to this specification by the augmenter."
Architectural,
/// This is a pseudo-register with a constant value.
Constant(u64),
}
/// A parsed call frame instruction. #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pubenum CallFrameInstruction<T: ReaderOffset> { // 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 location of the DWARF expression.
expression: UnwindExpression<T>,
},
// 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 location of the DWARF expression.
expression: UnwindExpression<T>,
},
/// > 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 location of the DWARF expression.
expression: UnwindExpression<T>,
},
/// 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,
}
/// The location of a DWARF expression within an unwind section. /// /// This is stored as an offset and length within the section instead of as a /// `Reader` to avoid lifetime issues when reusing [`UnwindContext`]. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pubstruct UnwindExpression<T: ReaderOffset> { /// The offset of the expression within the section. pub offset: T, /// The length of the expression. pub length: T,
}
impl<T: ReaderOffset> UnwindExpression<T> { /// Get the expression from the section. /// /// The offset and length were previously validated when the /// `UnwindExpression` was created, so this should not fail. pubfn get<R: Reader<Offset = T>, S: UnwindSection<R>>(
&self,
section: &S,
) -> Result<Expression<R>> { let input = &mut section.section().clone();
input.skip(self.offset)?; let data = input.split(self.length)?;
Ok(Expression(data))
}
}
/// 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(eh_pe))
}
}
/// A decoded pointer. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pubenum 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),
}
// 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!(),
}?;
// 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 = iflet Some(ref augmentation) = fde.augmentation { let cie_aug = fde
.cie
.augmentation
.expect("FDE has augmentation, but CIE doesn't");
iflet 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);
#[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_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 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);
#[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(),
}; letmut iter = CallFrameInstructionIter {
input,
address_encoding: None,
parameters,
vendor: Vendor::Default,
};
// 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,
),
];
#[test] fn test_eval_negate_ra_state() { let cie = make_test_cie(); let ctx = UnwindContext::new(); letmut 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(); letmut 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(); letmut 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 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();
// 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 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();
// 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 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 _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() { #[allow(clippy::identity_op)] 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 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();
// 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 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 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(); letmut 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();
#[test] fn test_unwind_info_for_address_not_found() { let debug_frame = DebugFrame::new(&[], NativeEndian); let bases = Default::default(); letmut 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. letmut 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 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 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 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 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);
letmut offset = None; let result = 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())
},
); match result {
Ok(actual) => assert_eq!(actual, fde),
otherwise => panic!("Unexpected result {:?}", otherwise),
}
assert!(offset.is_some());
}
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 augmentation = Augmentation {
is_signal_trampoline: true,
..Default::default()
};
#[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 augmentation = Augmentation {
lsda: Some(constants::DW_EH_PE_uleb128),
..Default::default()
};
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));
}
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(); letmut 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<usize> = [
(Register(0), RegisterRule::SameValue),
(Register(3), RegisterRule::Offset(1)),
]
.iter()
.collect(); let map2: RegisterRuleMap<usize> = [
(Register(3), RegisterRule::Offset(1)),
(Register(0), RegisterRule::SameValue),
]
.iter()
.collect();
assert_eq!(map1, map2);
assert_eq!(map2, map1);
#[test] fn test_unwind_context_reuse() { fn unwind_one(ctx: &mut UnwindContext<usize>, data: &[u8]) { let debug_frame = DebugFrame::new(data, NativeEndian); let bases = Default::default(); let result = debug_frame.unwind_info_for_address(
&bases,
ctx, 0xbadb_ad99,
DebugFrame::cie_from_offset,
);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress);
}
// Use the same context for two different data lifetimes. letmut ctx: UnwindContext<usize> = UnwindContext::new();
{ let data1 = vec![];
unwind_one(&mut ctx, &data1);
}
{ let data2 = vec![];
unwind_one(&mut ctx, &data2);
}
}
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.258Bemerkung:
(vorverarbeitet am 2026-06-19)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.