Impressum cfi.rs
Sprache: unbekannt
|
|
Spracherkennung für: .rs vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]
#[cfg(feature = "read")]
use alloc::boxed::Box;
use core::cmp::Ordering;
use core::fmt::{self, Debug};
use core::iter::FromIterator;
use core::mem;
use core::num::Wrapping;
use super::util::{ArrayLike, ArrayVec};
use crate::common::{
DebugFrameOffset, EhFrameOffset, Encoding, Format, Register, SectionId, Vendor,
};
use crate::constants::{self, DwEhPe};
use crate::endianity::Endianity;
use crate::read::{
EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section, StoreOnHeap,
};
/// `DebugFrame` contains the `.debug_frame` section's frame unwinding
/// information required to unwind to and recover registers from older frames on
/// the stack. For example, this is useful for a debugger that wants to print
/// locals in a backtrace.
///
/// Most interesting methods are defined in the
/// [`UnwindSection`](trait.UnwindSection.html) trait.
///
/// ### Differences between `.debug_frame` and `.eh_frame`
///
/// While the `.debug_frame` section's information has a lot of overlap with the
/// `.eh_frame` section's information, the `.eh_frame` information tends to only
/// encode the subset of information needed for exception handling. Often, only
/// one of `.eh_frame` or `.debug_frame` will be present in an object file.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DebugFrame<R: Reader> {
section: R,
address_size: u8,
segment_size: u8,
vendor: Vendor,
}
impl<R: Reader> DebugFrame<R> {
/// Set the size of a target address in bytes.
///
/// This defaults to the native word size.
/// This is only used if the CIE version is less than 4.
pub fn set_address_size(&mut self, address_size: u8) {
self.address_size = address_size
}
/// Set the size of a segment selector in bytes.
///
/// This defaults to 0.
/// This is only used if the CIE version is less than 4.
pub fn set_segment_size(&mut self, segment_size: u8) {
self.segment_size = segment_size
}
/// Set the vendor extensions to use.
///
/// This defaults to `Vendor::Default`.
pub fn set_vendor(&mut self, vendor: Vendor) {
self.vendor = vendor;
}
}
impl<'input, Endian> DebugFrame<EndianSlice<'input, Endian>>
where
Endian: Endianity,
{
/// Construct a new `DebugFrame` instance from the data in the
/// `.debug_frame` section.
///
/// It is the caller's responsibility to read the section and present it as
/// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O
/// loader on macOS, etc.
///
/// ```
/// use gimli::{DebugFrame, NativeEndian};
///
/// // Use with `.debug_frame`
/// # let buf = [0x00, 0x01, 0x02, 0x03];
/// # let read_debug_frame_section_somehow = || &buf;
/// let debug_frame = DebugFrame::new(read_debug_frame_section_somehow(), NativeEndia n);
/// ```
pub fn new(section: &'input [u8], endian: Endian) -> Self {
Self::from(EndianSlice::new(section, endian))
}
}
impl<R: Reader> Section<R> for DebugFrame<R> {
fn id() -> SectionId {
SectionId::DebugFrame
}
fn reader(&self) -> &R {
&self.section
}
}
impl<R: Reader> From<R> for DebugFrame<R> {
fn from(section: R) -> Self {
// Default to no segments and native word size.
DebugFrame {
section,
address_size: mem::size_of::<usize>() as u8,
segment_size: 0,
vendor: Vendor::Default,
}
}
}
/// `EhFrameHdr` contains the information about the `.eh_frame_hdr` section.
///
/// A pointer to the start of the `.eh_frame` data, and optionally, a binary
/// search table of pointers to the `.eh_frame` records that are found in this section.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EhFrameHdr<R: Reader>(R);
/// `ParsedEhFrameHdr` contains the parsed information from the `.eh_frame_hdr` section.
#[derive(Clone, Debug)]
pub struct ParsedEhFrameHdr<R: Reader> {
address_size: u8,
section: R,
eh_frame_ptr: Pointer,
fde_count: u64,
table_enc: DwEhPe,
table: R,
}
impl<'input, Endian> EhFrameHdr<EndianSlice<'input, Endian>>
where
Endian: Endianity,
{
/// Constructs a new `EhFrameHdr` instance from the data in the `.eh_frame_hdr` section.
pub fn new(section: &'input [u8], endian: Endian) -> Self {
Self::from(EndianSlice::new(section, endian))
}
}
impl<R: Reader> EhFrameHdr<R> {
/// Parses this `EhFrameHdr` to a `ParsedEhFrameHdr`.
pub fn parse(&self, bases: &BaseAddresses, address_size: u8) -> Result<ParsedEhFrameHdr<R>> {
let mut reader = self.0.clone();
let version = reader.read_u8()?;
if version != 1 {
return Err(Error::UnknownVersion(u64::from(version)));
}
let eh_frame_ptr_enc = parse_pointer_encoding(&mut reader)?;
let fde_count_enc = parse_pointer_encoding(&mut reader)?;
let table_enc = parse_pointer_encoding(&mut reader)?;
let parameters = PointerEncodingParameters {
bases: &bases.eh_frame_hdr,
func_base: None,
address_size,
section: &self.0,
};
// Omitting this pointer is not valid (defeats the purpose of .eh_frame_hdr entirely)
if eh_frame_ptr_enc == constants::DW_EH_PE_omit {
return Err(Error::CannotParseOmitPointerEncoding);
}
let eh_frame_ptr = parse_encoded_pointer(eh_frame_ptr_enc, ¶meters, &mut reader)?;
let fde_count;
if fde_count_enc == constants::DW_EH_PE_omit || table_enc == constants::DW_EH_PE_omit {
fde_count = 0
} else {
fde_count = parse_encoded_pointer(fde_count_enc, ¶meters, &mut reader)?.direct()?;
}
Ok(ParsedEhFrameHdr {
address_size,
section: self.0.clone(),
eh_frame_ptr,
fde_count,
table_enc,
table: reader,
})
}
}
impl<R: Reader> Section<R> for EhFrameHdr<R> {
fn id() -> SectionId {
SectionId::EhFrameHdr
}
fn reader(&self) -> &R {
&self.0
}
}
impl<R: Reader> From<R> for EhFrameHdr<R> {
fn from(section: R) -> Self {
EhFrameHdr(section)
}
}
impl<R: Reader> ParsedEhFrameHdr<R> {
/// Returns the address of the binary's `.eh_frame` section.
pub fn eh_frame_ptr(&self) -> Pointer {
self.eh_frame_ptr
}
/// Retrieves the CFI binary search table, if there is one.
pub fn table(&self) -> Option<EhHdrTable<'_, R>> {
// There are two big edge cases here:
// * You search the table for an invalid address. As this is just a binary
// search table, we always have to return a valid result for that (unless
// you specify an address that is lower than the first address in the
// table). Since this means that you have to recheck that the FDE contains
// your address anyways, we just return the first FDE even when the address
// is too low. After all, we're just doing a normal binary search.
// * This falls apart when the table is empty - there is no entry we could
// return. We conclude that an empty table is not really a table at all.
if self.fde_count == 0 {
None
} else {
Some(EhHdrTable { hdr: self })
}
}
}
/// An iterator for `.eh_frame_hdr` section's binary search table.
///
/// Each table entry consists of a tuple containing an `initial_location` and `address`.
/// The `initial location` represents the first address that the targeted FDE
/// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section.
/// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE.
#[derive(Debug)]
pub struct EhHdrTableIter<'a, 'bases, R: Reader> {
hdr: &'a ParsedEhFrameHdr<R>,
table: R,
bases: &'bases BaseAddresses,
remain: u64,
}
impl<'a, 'bases, R: Reader> EhHdrTableIter<'a, 'bases, R> {
/// Yield the next entry in the `EhHdrTableIter`.
pub fn next(&mut self) -> Result<Option<(Pointer, Pointer)>> {
if self.remain == 0 {
return Ok(None);
}
let parameters = PointerEncodingParameters {
bases: &self.bases.eh_frame_hdr,
func_base: None,
address_size: self.hdr.address_size,
section: &self.hdr.section,
};
self.remain -= 1;
let from = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?;
let to = parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut self.table)?;
Ok(Some((from, to)))
}
/// Yield the nth entry in the `EhHdrTableIter`
pub fn nth(&mut self, n: usize) -> Result<Option<(Pointer, Pointer)>> {
use core::convert::TryFrom;
let size = match self.hdr.table_enc.format() {
constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => {
return Err(Error::VariableLengthSearchTable);
}
constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2,
constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4,
constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8,
_ => return Err(Error::UnknownPointerEncoding(self.hdr.table_enc)),
};
let row_size = size * 2;
let n = u64::try_from(n).map_err(|_| Error::UnsupportedOffset)?;
self.remain = self.remain.saturating_sub(n);
self.table.skip(R::Offset::from_u64(n * row_size)?)?;
self.next()
}
}
#[cfg(feature = "fallible-iterator")]
impl<'a, 'bases, R: Reader> fallible_iterator::FallibleIterator for EhHdrTableIter<'a, 'bases, R> {
type Item = (Pointer, Pointer);
type Error = Error;
fn next(&mut self) -> Result<Option<Self::Item>> {
EhHdrTableIter::next(self)
}
fn size_hint(&self) -> (usize, Option<usize>) {
use core::convert::TryInto;
(
self.remain.try_into().unwrap_or(0),
self.remain.try_into().ok(),
)
}
fn nth(&mut self, n: usize) -> Result<Option<Self::Item>> {
EhHdrTableIter::nth(self, n)
}
}
/// The CFI binary search table that is an optional part of the `.eh_frame_hdr` section.
#[derive(Debug, Clone)]
pub struct EhHdrTable<'a, R: Reader> {
hdr: &'a ParsedEhFrameHdr<R>,
}
impl<'a, R: Reader + 'a> EhHdrTable<'a, R> {
/// Return an iterator that can walk the `.eh_frame_hdr` table.
///
/// Each table entry consists of a tuple containing an `initial_location` and `address`.
/// The `initial location` represents the first address that the targeted FDE
/// is able to decode. The `address` is the address of the FDE in the `.eh_frame` section.
/// The `address` can be converted with `EhHdrTable::pointer_to_offset` and `EhFrame::fde_from_offset` to an FDE.
pub fn iter<'bases>(&self, bases: &'bases BaseAddresses) -> EhHdrTableIter<'_, 'bases, R> {
EhHdrTableIter {
hdr: self.hdr,
bases,
remain: self.hdr.fde_count,
table: self.hdr.table.clone(),
}
}
/// *Probably* returns a pointer to the FDE for the given address.
///
/// This performs a binary search, so if there is no FDE for the given address,
/// this function **will** return a pointer to any other FDE that's close by.
///
/// To be sure, you **must** call `contains` on the FDE.
pub fn lookup(&self, address: u64, bases: &BaseAddresses) -> Result<Pointer> {
let size = match self.hdr.table_enc.format() {
constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_sleb128 => {
return Err(Error::VariableLengthSearchTable);
}
constants::DW_EH_PE_sdata2 | constants::DW_EH_PE_udata2 => 2,
constants::DW_EH_PE_sdata4 | constants::DW_EH_PE_udata4 => 4,
constants::DW_EH_PE_sdata8 | constants::DW_EH_PE_udata8 => 8,
_ => return Err(Error::UnknownPointerEncoding(self.hdr.table_enc)),
};
let row_size = size * 2;
let mut len = self.hdr.fde_count;
let mut reader = self.hdr.table.clone();
let parameters = PointerEncodingParameters {
bases: &bases.eh_frame_hdr,
func_base: None,
address_size: self.hdr.address_size,
section: &self.hdr.section,
};
while len > 1 {
let head = reader.split(R::Offset::from_u64((len / 2) * row_size)?)?;
let tail = reader.clone();
let pivot =
parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader)?.direct()?;
match pivot.cmp(&address) {
Ordering::Equal => {
reader = tail;
break;
}
Ordering::Less => {
reader = tail;
len = len - (len / 2);
}
Ordering::Greater => {
reader = head;
len /= 2;
}
}
}
reader.skip(R::Offset::from_u64(size)?)?;
parse_encoded_pointer(self.hdr.table_enc, ¶meters, &mut reader)
}
/// Convert a `Pointer` to a section offset.
///
/// This does not support indirect pointers.
pub fn pointer_to_offset(&self, ptr: Pointer) -> Result<EhFrameOffset<R::Offset>> {
let ptr = ptr.direct()?;
let eh_frame_ptr = self.hdr.eh_frame_ptr().direct()?;
// Calculate the offset in the EhFrame section
R::Offset::from_u64(ptr - eh_frame_ptr).map(EhFrameOffset)
}
/// Returns a parsed FDE for the given address, or `NoUnwindInfoForAddress`
/// if there are none.
///
/// You must provide a function to get its associated CIE. See
/// `PartialFrameDescriptionEntry::parse` for more information.
///
/// # Example
///
/// ```
/// # use gimli::{BaseAddresses, EhFrame, ParsedEhFrameHdr, EndianSlice, NativeEndian, Error, UnwindSection};
/// # fn foo() -> Result<(), Error> {
/// # let eh_frame: EhFrame<EndianSlice<NativeEndian>> = unreachable!();
/// # let eh_frame_hdr: ParsedEhFrameHdr<EndianSlice<NativeEndian>> = unimplemented!();
/// # let addr = 0;
/// # let bases = unimplemented!();
/// let table = eh_frame_hdr.table().unwrap();
/// let fde = table.fde_for_address(&eh_frame, &bases, addr, EhFrame::cie_from_offset)?;
/// # Ok(())
/// # }
/// ```
pub fn fde_for_address<F>(
&self,
frame: &EhFrame<R>,
bases: &BaseAddresses,
address: u64,
get_cie: F,
) -> Result<FrameDescriptionEntry<R>>
where
F: FnMut(
&EhFrame<R>,
&BaseAddresses,
EhFrameOffset<R::Offset>,
) -> Result<CommonInformationEntry<R>>,
{
let fdeptr = self.lookup(address, bases)?;
let offset = self.pointer_to_offset(fdeptr)?;
let entry = frame.fde_from_offset(bases, offset, get_cie)?;
if entry.contains(address) {
Ok(entry)
} else {
Err(Error::NoUnwindInfoForAddress)
}
}
#[inline]
#[doc(hidden)]
#[deprecated(note = "Method renamed to fde_for_address; use that instead.")]
pub fn lookup_and_parse<F>(
&self,
address: u64,
bases: &BaseAddresses,
frame: EhFrame<R>,
get_cie: F,
) -> Result<FrameDescriptionEntry<R>>
where
F: FnMut(
&EhFrame<R>,
&BaseAddresses,
EhFrameOffset<R::Offset>,
) -> Result<CommonInformationEntry<R>>,
{
self.fde_for_address(&frame, bases, address, get_cie)
}
/// Returns the frame unwind information for the given address,
/// or `NoUnwindInfoForAddress` if there are none.
///
/// You must provide a function to get the associated CIE. See
/// `PartialFrameDescriptionEntry::parse` for more information.
pub fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage<R::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)]
pub struct EhFrame<R: Reader> {
section: R,
address_size: u8,
vendor: Vendor,
}
impl<R: Reader> EhFrame<R> {
/// Set the size of a target address in bytes.
///
/// This defaults to the native word size.
pub fn set_address_size(&mut self, address_size: u8) {
self.address_size = address_size
}
/// Set the vendor extensions to use.
///
/// This defaults to `Vendor::Default`.
pub fn set_vendor(&mut self, vendor: Vendor) {
self.vendor = vendor;
}
}
impl<'input, Endian> EhFrame<EndianSlice<'input, Endian>>
where
Endian: Endianity,
{
/// Construct a new `EhFrame` instance from the data in the
/// `.eh_frame` section.
///
/// It is the caller's responsibility to read the section and present it as
/// a `&[u8]` slice. That means using some ELF loader on Linux, a Mach-O
/// loader on macOS, etc.
///
/// ```
/// use gimli::{EhFrame, EndianSlice, NativeEndian};
///
/// // Use with `.eh_frame`
/// # let buf = [0x00, 0x01, 0x02, 0x03];
/// # let read_eh_frame_section_somehow = || &buf;
/// let eh_frame = EhFrame::new(read_eh_frame_section_somehow(), NativeEndian);
/// ```
pub fn new(section: &'input [u8], endian: Endian) -> Self {
Self::from(EndianSlice::new(section, endian))
}
}
impl<R: Reader> Section<R> for EhFrame<R> {
fn id() -> SectionId {
SectionId::EhFrame
}
fn reader(&self) -> &R {
&self.section
}
}
impl<R: Reader> From<R> for EhFrame<R> {
fn from(section: R) -> Self {
// Default to native word size.
EhFrame {
section,
address_size: mem::size_of::<usize>() as u8,
vendor: Vendor::Default,
}
}
}
// This has to be `pub` to silence a warning (that is deny(..)'d by default) in
// rustc. Eventually, not having this `pub` will become a hard error.
#[doc(hidden)]
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CieOffsetEncoding {
U32,
U64,
}
/// An offset into an `UnwindSection`.
//
// Needed to avoid conflicting implementations of `Into<T>`.
pub trait UnwindOffset<T = usize>: Copy + Debug + Eq + From<T>
where
T: ReaderOffset,
{
/// Convert an `UnwindOffset<T>` into a `T`.
fn into(self) -> T;
}
impl<T> UnwindOffset<T> for DebugFrameOffset<T>
where
T: ReaderOffset,
{
#[inline]
fn into(self) -> T {
self.0
}
}
impl<T> UnwindOffset<T> for EhFrameOffset<T>
where
T: ReaderOffset,
{
#[inline]
fn into(self) -> T {
self.0
}
}
/// This trait completely encapsulates everything that is different between
/// `.eh_frame` and `.debug_frame`, as well as all the bits that can change
/// between DWARF versions.
#[doc(hidden)]
pub trait _UnwindSectionPrivate<R: Reader> {
/// Get the underlying section data.
fn section(&self) -> &R;
/// Returns true if the given length value should be considered an
/// end-of-entries sentinel.
fn length_value_is_end_of_entries(length: R::Offset) -> bool;
/// Return true if the given offset if the CIE sentinel, false otherwise.
fn is_cie(format: Format, id: u64) -> bool;
/// Return the CIE offset/ID encoding used by this unwind section with the
/// given DWARF format.
fn cie_offset_encoding(format: Format) -> CieOffsetEncoding;
/// For `.eh_frame`, CIE offsets are relative to the current position. For
/// `.debug_frame`, they are relative to the start of the section. We always
/// internally store them relative to the section, so we handle translating
/// `.eh_frame`'s relative offsets in this method. If the offset calculation
/// underflows, return `None`.
fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<R::Offset>;
/// Does this version of this unwind section encode address and segment
/// sizes in its CIEs?
fn has_address_and_segment_sizes(version: u8) -> bool;
/// The address size to use if `has_address_and_segment_sizes` returns false.
fn address_size(&self) -> u8;
/// The segment size to use if `has_address_and_segment_sizes` returns false.
fn segment_size(&self) -> u8;
/// The vendor extensions to use.
fn vendor(&self) -> Vendor;
}
/// A section holding unwind information: either `.debug_frame` or
/// `.eh_frame`. See [`DebugFrame`](./struct.DebugFrame.html) and
/// [`EhFrame`](./struct.EhFrame.html) respectively.
pub trait UnwindSection<R: Reader>: Clone + Debug + _UnwindSectionPrivate<R> {
/// The offset type associated with this CFI section. Either
/// `DebugFrameOffset` or `EhFrameOffset`.
type Offset: UnwindOffset<R::Offset>;
/// Iterate over the `CommonInformationEntry`s and `FrameDescriptionEntry`s
/// in this `.debug_frame` section.
///
/// Can be [used with
/// `FallibleIterator`](./index.html#using-with-fallibleiterator).
fn entries<'bases>(&self, bases: &'bases BaseAddresses) -> CfiEntriesIter<'bases, Self, R> {
CfiEntriesIter {
section: self.clone(),
bases,
input: self.section().clone(),
}
}
/// Parse the `CommonInformationEntry` at the given offset.
fn cie_from_offset(
&self,
bases: &BaseAddresses,
offset: Self::Offset,
) -> Result<CommonInformationEntry<R>> {
let offset = UnwindOffset::into(offset);
let input = &mut self.section().clone();
input.skip(offset)?;
CommonInformationEntry::parse(bases, self, input)
}
/// Parse the `PartialFrameDescriptionEntry` at the given offset.
fn partial_fde_from_offset<'bases>(
&self,
bases: &'bases BaseAddresses,
offset: Self::Offset,
) -> Result<PartialFrameDescriptionEntry<'bases, Self, R>> {
let offset = UnwindOffset::into(offset);
let input = &mut self.section().clone();
input.skip(offset)?;
PartialFrameDescriptionEntry::parse_partial(self, bases, input)
}
/// Parse the `FrameDescriptionEntry` at the given offset.
fn fde_from_offset<F>(
&self,
bases: &BaseAddresses,
offset: Self::Offset,
get_cie: F,
) -> Result<FrameDescriptionEntry<R>>
where
F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>,
{
let partial = self.partial_fde_from_offset(bases, offset)?;
partial.parse(get_cie)
}
/// Find the `FrameDescriptionEntry` for the given address.
///
/// If found, the FDE is returned. If not found,
/// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned.
/// If parsing fails, the error is returned.
///
/// You must provide a function to get its associated CIE. See
/// `PartialFrameDescriptionEntry::parse` for more information.
///
/// Note: this iterates over all FDEs. If available, it is possible
/// to do a binary search with `EhFrameHdr::fde_for_address` instead.
fn fde_for_address<F>(
&self,
bases: &BaseAddresses,
address: u64,
mut get_cie: F,
) -> Result<FrameDescriptionEntry<R>>
where
F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>,
{
let mut entries = self.entries(bases);
while let Some(entry) = entries.next()? {
match entry {
CieOrFde::Cie(_) => {}
CieOrFde::Fde(partial) => {
let fde = partial.parse(&mut get_cie)?;
if fde.contains(address) {
return Ok(fde);
}
}
}
}
Err(Error::NoUnwindInfoForAddress)
}
/// Find the frame unwind information for the given address.
///
/// If found, the unwind information is returned. If not found,
/// `Err(gimli::Error::NoUnwindInfoForAddress)` is returned. If parsing or
/// CFI evaluation fails, the error is returned.
///
/// ```
/// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindContext,
/// UnwindSection};
///
/// # fn foo() -> gimli::Result<()> {
/// # let read_eh_frame_section = || unimplemented!();
/// // Get the `.eh_frame` section from the object file. Alternatively,
/// // use `EhFrame` with the `.eh_frame` section of the object file.
/// let eh_frame = EhFrame::new(read_eh_frame_section(), NativeEndian);
///
/// # let get_frame_pc = || unimplemented!();
/// // Get the address of the PC for a frame you'd like to unwind.
/// let address = get_frame_pc();
///
/// // This context is reusable, which cuts down on heap allocations.
/// let ctx = UnwindContext::new();
///
/// // Optionally provide base addresses for any relative pointers. If a
/// // base address isn't provided and a pointer is found that is relative to
/// // it, we will return an `Err`.
/// # let address_of_text_section_in_memory = unimplemented!();
/// # let address_of_got_section_in_memory = unimplemented!();
/// let bases = BaseAddresses::default()
/// .set_text(address_of_text_section_in_memory)
/// .set_got(address_of_got_section_in_memory);
///
/// let unwind_info = eh_frame.unwind_info_for_address(
/// &bases,
/// &mut ctx,
/// address,
/// EhFrame::cie_from_offset,
/// )?;
///
/// # let do_stuff_with = |_| unimplemented!();
/// do_stuff_with(unwind_info);
/// # let _ = ctx;
/// # unreachable!()
/// # }
/// ```
#[inline]
fn unwind_info_for_address<'ctx, F, A: UnwindContextStorage<R::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> _UnwindSectionPrivate<R> for DebugFrame<R> {
fn section(&self) -> &R {
&self.section
}
fn length_value_is_end_of_entries(_: R::Offset) -> bool {
false
}
fn is_cie(format: Format, id: u64) -> bool {
match format {
Format::Dwarf32 => id == 0xffff_ffff,
Format::Dwarf64 => id == 0xffff_ffff_ffff_ffff,
}
}
fn cie_offset_encoding(format: Format) -> CieOffsetEncoding {
match format {
Format::Dwarf32 => CieOffsetEncoding::U32,
Format::Dwarf64 => CieOffsetEncoding::U64,
}
}
fn resolve_cie_offset(&self, _: R::Offset, offset: R::Offset) -> Option<R::Offset> {
Some(offset)
}
fn has_address_and_segment_sizes(version: u8) -> bool {
version == 4
}
fn address_size(&self) -> u8 {
self.address_size
}
fn segment_size(&self) -> u8 {
self.segment_size
}
fn vendor(&self) -> Vendor {
self.vendor
}
}
impl<R: Reader> UnwindSection<R> for DebugFrame<R> {
type Offset = DebugFrameOffset<R::Offset>;
}
impl<R: Reader> _UnwindSectionPrivate<R> for EhFrame<R> {
fn section(&self) -> &R {
&self.section
}
fn length_value_is_end_of_entries(length: R::Offset) -> bool {
length.into_u64() == 0
}
fn is_cie(_: Format, id: u64) -> bool {
id == 0
}
fn cie_offset_encoding(_format: Format) -> CieOffsetEncoding {
// `.eh_frame` offsets are always 4 bytes, regardless of the DWARF
// format.
CieOffsetEncoding::U32
}
fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<R::Offset> {
base.checked_sub(offset)
}
fn has_address_and_segment_sizes(_version: u8) -> bool {
false
}
fn address_size(&self) -> u8 {
self.address_size
}
fn segment_size(&self) -> u8 {
0
}
fn vendor(&self) -> Vendor {
self.vendor
}
}
impl<R: Reader> UnwindSection<R> for EhFrame<R> {
type Offset = EhFrameOffset<R::Offset>;
}
/// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers.
///
/// During CIE/FDE parsing, if a relative pointer is encountered for a base
/// address that is unknown, an Err will be returned.
///
/// ```
/// use gimli::BaseAddresses;
///
/// # fn foo() {
/// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!();
/// # let address_of_eh_frame_section_in_memory = unimplemented!();
/// # let address_of_text_section_in_memory = unimplemented!();
/// # let address_of_got_section_in_memory = unimplemented!();
/// # let address_of_the_start_of_current_func = unimplemented!();
/// let bases = BaseAddresses::default()
/// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory)
/// .set_eh_frame(address_of_eh_frame_section_in_memory)
/// .set_text(address_of_text_section_in_memory)
/// .set_got(address_of_got_section_in_memory);
/// # let _ = bases;
/// # }
/// ```
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct BaseAddresses {
/// The base addresses to use for pointers in the `.eh_frame_hdr` section.
pub eh_frame_hdr: SectionBaseAddresses,
/// The base addresses to use for pointers in the `.eh_frame` section.
pub eh_frame: SectionBaseAddresses,
}
/// Optional base addresses for the relative `DW_EH_PE_*` encoded pointers
/// in a particular section.
///
/// See `BaseAddresses` for methods that are helpful in setting these addresses.
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct SectionBaseAddresses {
/// The address of the section containing the pointer.
pub section: Option<u64>,
/// The base address for text relative pointers.
/// This is generally the address of the `.text` section.
pub text: Option<u64>,
/// The base address for data relative pointers.
///
/// For pointers in the `.eh_frame_hdr` section, this is the address
/// of the `.eh_frame_hdr` section
///
/// For pointers in the `.eh_frame` section, this is generally the
/// global pointer, such as the address of the `.got` section.
pub data: Option<u64>,
}
impl BaseAddresses {
/// Set the `.eh_frame_hdr` section base address.
#[inline]
pub fn set_eh_frame_hdr(mut self, addr: u64) -> Self {
self.eh_frame_hdr.section = Some(addr);
self.eh_frame_hdr.data = Some(addr);
self
}
/// Set the `.eh_frame` section base address.
#[inline]
pub fn set_eh_frame(mut self, addr: u64) -> Self {
self.eh_frame.section = Some(addr);
self
}
/// Set the `.text` section base address.
#[inline]
pub fn set_text(mut self, addr: u64) -> Self {
self.eh_frame_hdr.text = Some(addr);
self.eh_frame.text = Some(addr);
self
}
/// Set the `.got` section base address.
#[inline]
pub fn set_got(mut self, addr: u64) -> Self {
self.eh_frame.data = Some(addr);
self
}
}
/// An iterator over CIE and FDE entries in a `.debug_frame` or `.eh_frame`
/// section.
///
/// Some pointers may be encoded relative to various base addresses. Use the
/// [`BaseAddresses`](./struct.BaseAddresses.html) parameter to provide them. By
/// default, none are provided. If a relative pointer is encountered for a base
/// address that is unknown, an `Err` will be returned and iteration will abort.
///
/// Can be [used with
/// `FallibleIterator`](./index.html#using-with-fallibleiterator).
///
/// ```
/// use gimli::{BaseAddresses, EhFrame, EndianSlice, NativeEndian, UnwindSection};
///
/// # fn foo() -> gimli::Result<()> {
/// # let read_eh_frame_somehow = || unimplemented!();
/// let eh_frame = EhFrame::new(read_eh_frame_somehow(), NativeEndian);
///
/// # let address_of_eh_frame_hdr_section_in_memory = unimplemented!();
/// # let address_of_eh_frame_section_in_memory = unimplemented!();
/// # let address_of_text_section_in_memory = unimplemented!();
/// # let address_of_got_section_in_memory = unimplemented!();
/// # let address_of_the_start_of_current_func = unimplemented!();
/// // Provide base addresses for relative pointers.
/// let bases = BaseAddresses::default()
/// .set_eh_frame_hdr(address_of_eh_frame_hdr_section_in_memory)
/// .set_eh_frame(address_of_eh_frame_section_in_memory)
/// .set_text(address_of_text_section_in_memory)
/// .set_got(address_of_got_section_in_memory);
///
/// let mut entries = eh_frame.entries(&bases);
///
/// # let do_stuff_with = |_| unimplemented!();
/// while let Some(entry) = entries.next()? {
/// do_stuff_with(entry)
/// }
/// # unreachable!()
/// # }
/// ```
#[derive(Clone, Debug)]
pub struct CfiEntriesIter<'bases, Section, R>
where
R: Reader,
Section: UnwindSection<R>,
{
section: Section,
bases: &'bases BaseAddresses,
input: R,
}
impl<'bases, Section, R> CfiEntriesIter<'bases, Section, R>
where
R: Reader,
Section: UnwindSection<R>,
{
/// Advance the iterator to the next entry.
pub fn next(&mut self) -> Result<Option<CieOrFde<'bases, Section, R>>> {
if self.input.is_empty() {
return Ok(None);
}
match parse_cfi_entry(self.bases, &self.section, &mut self.input) {
Err(e) => {
self.input.empty();
Err(e)
}
Ok(None) => {
self.input.empty();
Ok(None)
}
Ok(Some(entry)) => Ok(Some(entry)),
}
}
}
#[cfg(feature = "fallible-iterator")]
impl<'bases, Section, R> fallible_iterator::FallibleIterator for CfiEntriesIter<'bases, Section, R>
where
R: Reader,
Section: UnwindSection<R>,
{
type Item = CieOrFde<'bases, Section, R>;
type Error = Error;
fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
CfiEntriesIter::next(self)
}
}
/// Either a `CommonInformationEntry` (CIE) or a `FrameDescriptionEntry` (FDE).
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CieOrFde<'bases, Section, R>
where
R: Reader,
Section: UnwindSection<R>,
{
/// This CFI entry is a `CommonInformationEntry`.
Cie(CommonInformationEntry<R>),
/// This CFI entry is a `FrameDescriptionEntry`, however fully parsing it
/// requires parsing its CIE first, so it is left in a partially parsed
/// state.
Fde(PartialFrameDescriptionEntry<'bases, Section, R>),
}
fn parse_cfi_entry<'bases, Section, R>(
bases: &'bases BaseAddresses,
section: &Section,
input: &mut R,
) -> Result<Option<CieOrFde<'bases, Section, R>>>
where
R: Reader,
Section: UnwindSection<R>,
{
let (offset, length, format) = loop {
let offset = input.offset_from(section.section());
let (length, format) = input.read_initial_length()?;
if Section::length_value_is_end_of_entries(length) {
return Ok(None);
}
// Hack: skip zero padding inserted by buggy compilers/linkers.
// We require that the padding is a multiple of 32-bits, otherwise
// there is no reliable way to determine when the padding ends. This
// should be okay since CFI entries must be aligned to the address size.
if length.into_u64() != 0 || format != Format::Dwarf32 {
break (offset, length, format);
}
};
let mut rest = input.split(length)?;
let cie_offset_base = rest.offset_from(section.section());
let cie_id_or_offset = match Section::cie_offset_encoding(format) {
CieOffsetEncoding::U32 => rest.read_u32().map(u64::from)?,
CieOffsetEncoding::U64 => rest.read_u64()?,
};
if Section::is_cie(format, cie_id_or_offset) {
let cie = CommonInformationEntry::parse_rest(offset, length, format, bases, section, rest)?;
Ok(Some(CieOrFde::Cie(cie)))
} else {
let cie_offset = R::Offset::from_u64(cie_id_or_offset)?;
let cie_offset = match section.resolve_cie_offset(cie_offset_base, cie_offset) {
None => return Err(Error::OffsetOutOfBounds),
Some(cie_offset) => cie_offset,
};
let fde = PartialFrameDescriptionEntry {
offset,
length,
format,
cie_offset: cie_offset.into(),
rest,
section: section.clone(),
bases,
};
Ok(Some(CieOrFde::Fde(fde)))
}
}
/// We support the z-style augmentation [defined by `.eh_frame`][ehframe].
///
/// [ehframe]: https://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Augmentation {
/// > A 'L' may be present at any position after the first character of the
/// > string. This character may only be present if 'z' is the first character
/// > of the string. If present, it indicates the presence of one argument in
/// > the Augmentation Data of the CIE, and a corresponding argument in the
/// > Augmentation Data of the FDE. The argument in the Augmentation Data of
/// > the CIE is 1-byte and represents the pointer encoding used for the
/// > argument in the Augmentation Data of the FDE, which is the address of a
/// > language-specific data area (LSDA). The size of the LSDA pointer is
/// > specified by the pointer encoding used.
lsda: Option<constants::DwEhPe>,
/// > A 'P' may be present at any position after the first character of the
/// > string. This character may only be present if 'z' is the first character
/// > of the string. If present, it indicates the presence of two arguments in
/// > the Augmentation Data of the CIE. The first argument is 1-byte and
/// > represents the pointer encoding used for the second argument, which is
/// > the address of a personality routine handler. The size of the
/// > personality routine pointer is specified by the pointer encoding used.
personality: Option<(constants::DwEhPe, Pointer)>,
/// > A 'R' may be present at any position after the first character of the
/// > string. This character may only be present if 'z' is the first character
/// > of the string. If present, The Augmentation Data shall include a 1 byte
/// > argument that represents the pointer encoding for the address pointers
/// > used in the FDE.
fde_address_encoding: Option<constants::DwEhPe>,
/// True if this CIE's FDEs are trampolines for signal handlers.
is_signal_trampoline: bool,
}
impl Augmentation {
fn parse<Section, R>(
augmentation_str: &mut R,
bases: &BaseAddresses,
address_size: u8,
section: &Section,
input: &mut R,
) -> Result<Augmentation>
where
R: Reader,
Section: UnwindSection<R>,
{
debug_assert!(
!augmentation_str.is_empty(),
"Augmentation::parse should only be called if we have an augmentation"
);
let mut augmentation = Augmentation::default();
let mut parsed_first = false;
let mut data = None;
while !augmentation_str.is_empty() {
let ch = augmentation_str.read_u8()?;
match ch {
b'z' => {
if parsed_first {
return Err(Error::UnknownAugmentation);
}
let augmentation_length = input.read_uleb128().and_then(R::Offset::from_u64)?;
data = Some(input.split(augmentation_length)?);
}
b'L' => {
let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?;
let encoding = parse_pointer_encoding(rest)?;
augmentation.lsda = Some(encoding);
}
b'P' => {
let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?;
let encoding = parse_pointer_encoding(rest)?;
let parameters = PointerEncodingParameters {
bases: &bases.eh_frame,
func_base: None,
address_size,
section: section.section(),
};
let personality = parse_encoded_pointer(encoding, ¶meters, rest)?;
augmentation.personality = Some((encoding, personality));
}
b'R' => {
let rest = data.as_mut().ok_or(Error::UnknownAugmentation)?;
let encoding = parse_pointer_encoding(rest)?;
augmentation.fde_address_encoding = Some(encoding);
}
b'S' => augmentation.is_signal_trampoline = true,
_ => return Err(Error::UnknownAugmentation),
}
parsed_first = true;
}
Ok(augmentation)
}
}
/// Parsed augmentation data for a `FrameDescriptEntry`.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct AugmentationData {
lsda: Option<Pointer>,
}
impl AugmentationData {
fn parse<R: Reader>(
augmentation: &Augmentation,
encoding_parameters: &PointerEncodingParameters<'_, R>,
input: &mut R,
) -> Result<AugmentationData> {
// In theory, we should be iterating over the original augmentation
// string, interpreting each character, and reading the appropriate bits
// out of the augmentation data as we go. However, the only character
// that defines augmentation data in the FDE is the 'L' character, so we
// can just check for its presence directly.
let aug_data_len = input.read_uleb128().and_then(R::Offset::from_u64)?;
let rest = &mut input.split(aug_data_len)?;
let mut augmentation_data = AugmentationData::default();
if let Some(encoding) = augmentation.lsda {
let lsda = parse_encoded_pointer(encoding, encoding_parameters, rest)?;
augmentation_data.lsda = Some(lsda);
}
Ok(augmentation_data)
}
}
/// > A Common Information Entry holds information that is shared among many
/// > Frame Description Entries. There is at least one CIE in every non-empty
/// > `.debug_frame` section.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommonInformationEntry<R, Offset = <R as Reader>::Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
/// The offset of this entry from the start of its containing section.
offset: Offset,
/// > A constant that gives the number of bytes of the CIE structure, not
/// > including the length field itself (see Section 7.2.2). The size of the
/// > length field plus the value of length must be an integral multiple of
/// > the address size.
length: Offset,
format: Format,
/// > A version number (see Section 7.23). This number is specific to the
/// > call frame information and is independent of the DWARF version number.
version: u8,
/// The parsed augmentation, if any.
augmentation: Option<Augmentation>,
/// > The size of a target address in this CIE and any FDEs that use it, in
/// > bytes. If a compilation unit exists for this frame, its address size
/// > must match the address size here.
address_size: u8,
/// "The size of a segment selector in this CIE and any FDEs that use it, in
/// bytes."
segment_size: u8,
/// "A constant that is factored out of all advance location instructions
/// (see Section 6.4.2.1)."
code_alignment_factor: u64,
/// > A constant that is factored out of certain offset instructions (see
/// > below). The resulting value is (operand * data_alignment_factor).
data_alignment_factor: i64,
/// > An unsigned LEB128 constant that indicates which column in the rule
/// > table represents the return address of the function. Note that this
/// > column might not correspond to an actual machine register.
return_address_register: Register,
/// > A sequence of rules that are interpreted to create the initial setting
/// > of each column in the table.
///
/// > The default rule for all columns before interpretation of the initial
/// > instructions is the undefined rule. However, an ABI authoring body or a
/// > compilation system authoring body may specify an alternate default
/// > value for any or all columns.
///
/// This is followed by `DW_CFA_nop` padding until the end of `length` bytes
/// in the input.
initial_instructions: R,
}
impl<R: Reader> CommonInformationEntry<R> {
fn parse<Section: UnwindSection<R>>(
bases: &BaseAddresses,
section: &Section,
input: &mut R,
) -> Result<CommonInformationEntry<R>> {
match parse_cfi_entry(bases, section, input)? {
Some(CieOrFde::Cie(cie)) => Ok(cie),
Some(CieOrFde::Fde(_)) => Err(Error::NotCieId),
None => Err(Error::NoEntryAtGivenOffset),
}
}
fn parse_rest<Section: UnwindSection<R>>(
offset: R::Offset,
length: R::Offset,
format: Format,
bases: &BaseAddresses,
section: &Section,
mut rest: R,
) -> Result<CommonInformationEntry<R>> {
let version = rest.read_u8()?;
// Version 1 of `.debug_frame` corresponds to DWARF 2, and then for
// DWARF 3 and 4, I think they decided to just match the standard's
// version.
match version {
1 | 3 | 4 => (),
_ => return Err(Error::UnknownVersion(u64::from(version))),
}
let mut augmentation_string = rest.read_null_terminated_slice()?;
let (address_size, segment_size) = if Section::has_address_and_segment_sizes(version) {
let address_size = rest.read_u8()?;
let segment_size = rest.read_u8()?;
(address_size, segment_size)
} else {
(section.address_size(), section.segment_size())
};
let code_alignment_factor = rest.read_uleb128()?;
let data_alignment_factor = rest.read_sleb128()?;
let return_address_register = if version == 1 {
Register(rest.read_u8()?.into())
} else {
rest.read_uleb128().and_then(Register::from_u64)?
};
let augmentation = if augmentation_string.is_empty() {
None
} else {
Some(Augmentation::parse(
&mut augmentation_string,
bases,
address_size,
section,
&mut rest,
)?)
};
let entry = CommonInformationEntry {
offset,
length,
format,
version,
augmentation,
address_size,
segment_size,
code_alignment_factor,
data_alignment_factor,
return_address_register,
initial_instructions: rest,
};
Ok(entry)
}
}
/// # Signal Safe Methods
///
/// These methods are guaranteed not to allocate, acquire locks, or perform any
/// other signal-unsafe operations.
impl<R: Reader> CommonInformationEntry<R> {
/// Get the offset of this entry from the start of its containing section.
pub fn offset(&self) -> R::Offset {
self.offset
}
/// Return the encoding parameters for this CIE.
pub fn encoding(&self) -> Encoding {
Encoding {
format: self.format,
version: u16::from(self.version),
address_size: self.address_size,
}
}
/// The size of addresses (in bytes) in this CIE.
pub fn address_size(&self) -> u8 {
self.address_size
}
/// Iterate over this CIE's initial instructions.
///
/// Can be [used with
/// `FallibleIterator`](./index.html#using-with-fallibleiterator).
pub fn instructions<'a, Section>(
&self,
section: &'a Section,
bases: &'a BaseAddresses,
) -> CallFrameInstructionIter<'a, R>
where
Section: UnwindSection<R>,
{
CallFrameInstructionIter {
input: self.initial_instructions.clone(),
address_encoding: None,
parameters: PointerEncodingParameters {
bases: &bases.eh_frame,
func_base: None,
address_size: self.address_size,
section: section.section(),
},
vendor: section.vendor(),
}
}
/// > A constant that gives the number of bytes of the CIE structure, not
/// > including the length field itself (see Section 7.2.2). The size of the
/// > length field plus the value of length must be an integral multiple of
/// > the address size.
pub fn entry_len(&self) -> R::Offset {
self.length
}
/// > A version number (see Section 7.23). This number is specific to the
/// > call frame information and is independent of the DWARF version number.
pub fn version(&self) -> u8 {
self.version
}
/// Get the augmentation data, if any exists.
///
/// The only augmentation understood by `gimli` is that which is defined by
/// `.eh_frame`.
pub fn augmentation(&self) -> Option<&Augmentation> {
self.augmentation.as_ref()
}
/// True if this CIE's FDEs have a LSDA.
pub fn has_lsda(&self) -> bool {
self.augmentation.map_or(false, |a| a.lsda.is_some())
}
/// Return the encoding of the LSDA address for this CIE's FDEs.
pub fn lsda_encoding(&self) -> Option<constants::DwEhPe> {
self.augmentation.and_then(|a| a.lsda)
}
/// Return the encoding and address of the personality routine handler
/// for this CIE's FDEs.
pub fn personality_with_encoding(&self) -> Option<(constants::DwEhPe, Pointer)> {
self.augmentation.as_ref().and_then(|a| a.personality)
}
/// Return the address of the personality routine handler
/// for this CIE's FDEs.
pub fn personality(&self) -> Option<Pointer> {
self.augmentation
.as_ref()
.and_then(|a| a.personality)
.map(|(_, p)| p)
}
/// Return the encoding of the addresses for this CIE's FDEs.
pub fn fde_address_encoding(&self) -> Option<constants::DwEhPe> {
self.augmentation.and_then(|a| a.fde_address_encoding)
}
/// True if this CIE's FDEs are trampolines for signal handlers.
pub fn is_signal_trampoline(&self) -> bool {
self.augmentation.map_or(false, |a| a.is_signal_trampoline)
}
/// > A constant that is factored out of all advance location instructions
/// > (see Section 6.4.2.1).
pub fn code_alignment_factor(&self) -> u64 {
self.code_alignment_factor
}
/// > A constant that is factored out of certain offset instructions (see
/// > below). The resulting value is (operand * data_alignment_factor).
pub fn data_alignment_factor(&self) -> i64 {
self.data_alignment_factor
}
/// > An unsigned ... constant that indicates which column in the rule
/// > table represents the return address of the function. Note that this
/// > column might not correspond to an actual machine register.
pub fn return_address_register(&self) -> Register {
self.return_address_register
}
}
/// A partially parsed `FrameDescriptionEntry`.
///
/// Fully parsing this FDE requires first parsing its CIE.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PartialFrameDescriptionEntry<'bases, Section, R>
where
R: Reader,
Section: UnwindSection<R>,
{
offset: R::Offset,
length: R::Offset,
format: Format,
cie_offset: Section::Offset,
rest: R,
section: Section,
bases: &'bases BaseAddresses,
}
impl<'bases, Section, R> PartialFrameDescriptionEntry<'bases, Section, R>
where
R: Reader,
Section: UnwindSection<R>,
{
fn parse_partial(
section: &Section,
bases: &'bases BaseAddresses,
input: &mut R,
) -> Result<PartialFrameDescriptionEntry<'bases, Section, R>> {
match parse_cfi_entry(bases, section, input)? {
Some(CieOrFde::Cie(_)) => Err(Error::NotFdePointer),
Some(CieOrFde::Fde(partial)) => Ok(partial),
None => Err(Error::NoEntryAtGivenOffset),
}
}
/// Fully parse this FDE.
///
/// You must provide a function get its associated CIE (either by parsing it
/// on demand, or looking it up in some table mapping offsets to CIEs that
/// you've already parsed, etc.)
pub fn parse<F>(&self, get_cie: F) -> Result<FrameDescriptionEntry<R>>
where
F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>,
{
FrameDescriptionEntry::parse_rest(
self.offset,
self.length,
self.format,
self.cie_offset,
self.rest.clone(),
&self.section,
self.bases,
get_cie,
)
}
/// Get the offset of this entry from the start of its containing section.
pub fn offset(&self) -> R::Offset {
self.offset
}
/// Get the offset of this FDE's CIE.
pub fn cie_offset(&self) -> Section::Offset {
self.cie_offset
}
/// > A constant that gives the number of bytes of the header and
/// > instruction stream for this function, not including the length field
/// > itself (see Section 7.2.2). The size of the length field plus the value
/// > of length must be an integral multiple of the address size.
pub fn entry_len(&self) -> R::Offset {
self.length
}
}
/// A `FrameDescriptionEntry` is a set of CFA instructions for an address range.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FrameDescriptionEntry<R, Offset = <R as Reader>::Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
/// The start of this entry within its containing section.
offset: Offset,
/// > A constant that gives the number of bytes of the header and
/// > instruction stream for this function, not including the length field
/// > itself (see Section 7.2.2). The size of the length field plus the value
/// > of length must be an integral multiple of the address size.
length: Offset,
format: Format,
/// "A constant offset into the .debug_frame section that denotes the CIE
/// that is associated with this FDE."
///
/// This is the CIE at that offset.
cie: CommonInformationEntry<R, Offset>,
/// > The address of the first location associated with this table entry. If
/// > the segment_size field of this FDE's CIE is non-zero, the initial
/// > location is preceded by a segment selector of the given length.
initial_segment: u64,
initial_address: u64,
/// "The number of bytes of program instructions described by this entry."
address_range: u64,
/// The parsed augmentation data, if we have any.
augmentation: Option<AugmentationData>,
/// "A sequence of table defining instructions that are described below."
///
/// This is followed by `DW_CFA_nop` padding until `length` bytes of the
/// input are consumed.
instructions: R,
}
impl<R: Reader> FrameDescriptionEntry<R> {
fn parse_rest<Section, F>(
offset: R::Offset,
length: R::Offset,
format: Format,
cie_pointer: Section::Offset,
mut rest: R,
section: &Section,
bases: &BaseAddresses,
mut get_cie: F,
) -> Result<FrameDescriptionEntry<R>>
where
Section: UnwindSection<R>,
F: FnMut(&Section, &BaseAddresses, Section::Offset) -> Result<CommonInformationEntry<R>>,
{
let cie = get_cie(section, bases, cie_pointer)?;
let initial_segment = if cie.segment_size > 0 {
rest.read_address(cie.segment_size)?
} else {
0
};
let mut parameters = PointerEncodingParameters {
bases: &bases.eh_frame,
func_base: None,
address_size: cie.address_size,
section: section.section(),
};
let (initial_address, address_range) = Self::parse_addresses(&mut rest, &cie, ¶meters)?;
parameters.func_base = Some(initial_address);
let aug_data = if let Some(ref augmentation) = cie.augmentation {
Some(AugmentationData::parse(
augmentation,
¶meters,
&mut rest,
)?)
} else {
None
};
let entry = FrameDescriptionEntry {
offset,
length,
format,
cie,
initial_segment,
initial_address,
address_range,
augmentation: aug_data,
instructions: rest,
};
Ok(entry)
}
fn parse_addresses(
input: &mut R,
cie: &CommonInformationEntry<R>,
parameters: &PointerEncodingParameters<'_, R>,
) -> Result<(u64, u64)> {
let encoding = cie.augmentation().and_then(|a| a.fde_address_encoding);
if let Some(encoding) = encoding {
let initial_address = parse_encoded_pointer(encoding, parameters, input)?;
// Ignore indirection.
let initial_address = initial_address.pointer();
// Address ranges cannot be relative to anything, so just grab the
// data format bits from the encoding.
let address_range = parse_encoded_pointer(encoding.format(), parameters, input)?;
Ok((initial_address, address_range.pointer()))
} else {
let initial_address = input.read_address(cie.address_size)?;
let address_range = input.read_address(cie.address_size)?;
Ok((initial_address, address_range))
}
}
/// Return the table of unwind information for this FDE.
#[inline]
pub fn rows<'a, 'ctx, Section: UnwindSection<R>, A: UnwindContextStorage<R::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.
pub fn 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>> {
let mut table = self.rows(section, bases, ctx)?;
while let Some(row) = table.next_row()? {
if row.contains(address) {
return Ok(table.ctx.row());
}
}
Err(Error::NoUnwindInfoForAddress)
}
}
/// # Signal Safe Methods
///
/// These methods are guaranteed not to allocate, acquire locks, or perform any
/// other signal-unsafe operations.
#[allow(clippy::len_without_is_empty)]
impl<R: Reader> FrameDescriptionEntry<R> {
/// Get the offset of this entry from the start of its containing section.
pub fn offset(&self) -> R::Offset {
self.offset
}
/// Get a reference to this FDE's CIE.
pub fn cie(&self) -> &CommonInformationEntry<R> {
&self.cie
}
/// > A constant that gives the number of bytes of the header and
/// > instruction stream for this function, not including the length field
/// > itself (see Section 7.2.2). The size of the length field plus the value
/// > of length must be an integral multiple of the address size.
pub fn entry_len(&self) -> R::Offset {
self.length
}
/// Iterate over this FDE's instructions.
///
/// Will not include the CIE's initial instructions, if you want those do
/// `fde.cie().instructions()` first.
///
/// Can be [used with
/// `FallibleIterator`](./index.html#using-with-fallibleiterator).
pub fn instructions<'a, Section>(
&self,
section: &'a Section,
bases: &'a BaseAddresses,
) -> CallFrameInstructionIter<'a, R>
where
Section: UnwindSection<R>,
{
CallFrameInstructionIter {
input: self.instructions.clone(),
address_encoding: self.cie.augmentation().and_then(|a| a.fde_address_encoding),
parameters: PointerEncodingParameters {
bases: &bases.eh_frame,
func_base: None,
address_size: self.cie.address_size,
section: section.section(),
},
vendor: section.vendor(),
}
}
/// The first address for which this entry has unwind information for.
pub fn initial_address(&self) -> u64 {
self.initial_address
}
/// The number of bytes of instructions that this entry has unwind
/// information for.
pub fn len(&self) -> u64 {
self.address_range
}
/// Return `true` if the given address is within this FDE, `false`
/// otherwise.
///
/// This is equivalent to `entry.initial_address() <= address <
/// entry.initial_address() + entry.len()`.
pub fn contains(&self, address: u64) -> bool {
let start = self.initial_address();
let end = start + self.len();
start <= address && address < end
}
/// The address of this FDE's language-specific data area (LSDA), if it has
/// any.
pub fn lsda(&self) -> Option<Pointer> {
self.augmentation.as_ref().and_then(|a| a.lsda)
}
/// Return true if this FDE's function is a trampoline for a signal handler.
#[inline]
pub fn is_signal_trampoline(&self) -> bool {
self.cie().is_signal_trampoline()
}
/// Return the address of the FDE's function's personality routine
/// handler. The personality routine does language-specific clean up when
/// unwinding the stack frames with the intent to not run them again.
#[inline]
pub fn personality(&self) -> Option<Pointer> {
self.cie().personality()
}
}
/// Specification of what storage should be used for [`UnwindContext`].
///
#[cfg_attr(
feature = "read",
doc = "
Normally you would only need to use [`StoreOnHeap`], which places the stack
on the heap using [`Box`]. This is the default storage type parameter for [`UnwindContext`].
You may want to supply your own storage type for 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();
///
--> --------------------
--> maximum size reached
--> --------------------
[ Seitenstruktur0.88Drucken
]
|
2026-04-04
|