#[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, ReaderAddress, ReaderOffset, Result, Section,
StoreOnHeap,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DebugFrame<R: Reader> {
section: R,
address_size: u8,
vendor: Vendor,
}
impl<R: Reader> DebugFrame<R> {
pub fn set_address_size(&mut self, address_size: u8) {
self.address_size = address_size
}
pub fn set_vendor(&mut self, vendor: Vendor) {
self.vendor = vendor;
}
}
impl<'input, Endian> DebugFrame<EndianSlice<'input, Endian>>
where
Endian: Endianity,
{
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 {
DebugFrame {
section,
address_size: mem::size_of::<usize>() as u8,
vendor: Vendor::Default,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EhFrameHdr<R: Reader>(R);
#[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,
{
pub fn new(section: &'input [u8], endian: Endian) -> Self {
Self::from(EndianSlice::new(section, endian))
}
}
impl<R: Reader> EhFrameHdr<R> {
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,
};
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 {
if fde_count_enc != fde_count_enc.format() {
return Err(Error::UnsupportedPointerEncoding);
}
fde_count = parse_encoded_value(fde_count_enc, ¶meters, &mut reader)?;
}
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> {
pub fn eh_frame_ptr(&self) -> Pointer {
self.eh_frame_ptr
}
pub fn table(&self) -> Option<EhHdrTable<'_, R>> {
if self.fde_count == 0 {
None
} else {
Some(EhHdrTable { hdr: self })
}
}
}
#[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> {
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)))
}
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)
}
}
#[derive(Debug, Clone)]
pub struct EhHdrTable<'a, R: Reader> {
hdr: &'a ParsedEhFrameHdr<R>,
}
impl<'a, R: Reader + 'a> EhHdrTable<'a, R> {
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(),
}
}
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)
}
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()?;
R::Offset::from_u64(ptr - eh_frame_ptr).map(EhFrameOffset)
}
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)
}
pub fn unwind_info_for_address<'ctx, F, S>(
&self,
frame: &EhFrame<R>,
bases: &BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, S>,
address: u64,
get_cie: F,
) -> Result<&'ctx UnwindTableRow<R::Offset, S>>
where
F: FnMut(
&EhFrame<R>,
&BaseAddresses,
EhFrameOffset<R::Offset>,
) -> Result<CommonInformationEntry<R>>,
S: UnwindContextStorage<R::Offset>,
{
let fde = self.fde_for_address(frame, bases, address, get_cie)?;
fde.unwind_info_for_address(frame, bases, ctx, address)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EhFrame<R: Reader> {
section: R,
address_size: u8,
vendor: Vendor,
}
impl<R: Reader> EhFrame<R> {
pub fn set_address_size(&mut self, address_size: u8) {
self.address_size = address_size
}
pub fn set_vendor(&mut self, vendor: Vendor) {
self.vendor = vendor;
}
}
impl<'input, Endian> EhFrame<EndianSlice<'input, Endian>>
where
Endian: Endianity,
{
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 {
EhFrame {
section,
address_size: mem::size_of::<usize>() as u8,
vendor: Vendor::Default,
}
}
}
#[doc(hidden)]
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CieOffsetEncoding {
U32,
U64,
}
pub trait UnwindOffset<T = usize>: Copy + Debug + Eq + From<T>
where
T: ReaderOffset,
{
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
}
}
#[doc(hidden)]
pub trait _UnwindSectionPrivate<R: Reader> {
fn section(&self) -> &R;
fn has_zero_terminator() -> bool;
fn is_cie(format: Format, id: u64) -> bool;
fn cie_offset_encoding(format: Format) -> CieOffsetEncoding;
fn resolve_cie_offset(&self, base: R::Offset, offset: R::Offset) -> Option<R::Offset>;
fn has_address_and_segment_sizes(version: u8) -> bool;
fn address_size(&self) -> u8;
fn vendor(&self) -> Vendor;
}
pub trait UnwindSection<R: Reader>: Clone + Debug + _UnwindSectionPrivate<R> {
type Offset: UnwindOffset<R::Offset>;
fn entries<'bases>(&self, bases: &'bases BaseAddresses) -> CfiEntriesIter<'bases, Self, R> {
CfiEntriesIter {
section: self.clone(),
bases,
input: self.section().clone(),
}
}
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)
}
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)
}
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)
}
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)
}
#[inline]
fn unwind_info_for_address<'ctx, F, S>(
&self,
bases: &BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, S>,
address: u64,
get_cie: F,
) -> Result<&'ctx UnwindTableRow<R::Offset, S>>
where
F: FnMut(&Self, &BaseAddresses, Self::Offset) -> Result<CommonInformationEntry<R>>,
S: UnwindContextStorage<R::Offset>,
{
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 has_zero_terminator() -> 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 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 has_zero_terminator() -> bool {
true
}
fn is_cie(_: Format, id: u64) -> bool {
id == 0
}
fn cie_offset_encoding(_format: Format) -> CieOffsetEncoding {
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 vendor(&self) -> Vendor {
self.vendor
}
}
impl<R: Reader> UnwindSection<R> for EhFrame<R> {
type Offset = EhFrameOffset<R::Offset>;
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct BaseAddresses {
pub eh_frame_hdr: SectionBaseAddresses,
pub eh_frame: SectionBaseAddresses,
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct SectionBaseAddresses {
pub section: Option<u64>,
pub text: Option<u64>,
pub data: Option<u64>,
}
impl BaseAddresses {
#[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
}
#[inline]
pub fn set_eh_frame(mut self, addr: u64) -> Self {
self.eh_frame.section = Some(addr);
self
}
#[inline]
pub fn set_text(mut self, addr: u64) -> Self {
self.eh_frame_hdr.text = Some(addr);
self.eh_frame.text = Some(addr);
self
}
#[inline]
pub fn set_got(mut self, addr: u64) -> Self {
self.eh_frame.data = Some(addr);
self
}
}
#[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>,
{
pub fn next(&mut self) -> Result<Option<CieOrFde<'bases, Section, R>>> {
loop {
if self.input.is_empty() {
return Ok(None);
}
match parse_cfi_entry(self.bases, &self.section, &mut self.input) {
Ok(Some(entry)) => return Ok(Some(entry)),
Err(e) => {
self.input.empty();
return Err(e);
}
Ok(None) => {
if Section::has_zero_terminator() {
self.input.empty();
return Ok(None);
}
continue;
}
}
}
}
}
#[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)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CieOrFde<'bases, Section, R>
where
R: Reader,
Section: UnwindSection<R>,
{
Cie(CommonInformationEntry<R>),
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 = input.offset_from(section.section());
let (length, format) = input.read_initial_length()?;
if length.into_u64() == 0 {
return Ok(None);
}
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)))
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub struct Augmentation {
lsda: Option<constants::DwEhPe>,
personality: Option<(constants::DwEhPe, Pointer)>,
fde_address_encoding: Option<constants::DwEhPe>,
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)
}
}
#[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> {
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)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommonInformationEntry<R, Offset = <R as Reader>::Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
offset: Offset,
length: Offset,
format: Format,
version: u8,
augmentation: Option<Augmentation>,
address_size: u8,
code_alignment_factor: u64,
data_alignment_factor: i64,
return_address_register: Register,
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()?;
match version {
1 | 3 | 4 => (),
_ => return Err(Error::UnknownVersion(u64::from(version))),
}
let mut augmentation_string = rest.read_null_terminated_slice()?;
let address_size = if Section::has_address_and_segment_sizes(version) {
let address_size = rest.read_address_size()?;
let segment_size = rest.read_u8()?;
if segment_size != 0 {
return Err(Error::UnsupportedSegmentSize);
}
address_size
} else {
section.address_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,
code_alignment_factor,
data_alignment_factor,
return_address_register,
initial_instructions: rest,
};
Ok(entry)
}
}
impl<R: Reader> CommonInformationEntry<R> {
pub fn offset(&self) -> R::Offset {
self.offset
}
pub fn encoding(&self) -> Encoding {
Encoding {
format: self.format,
version: u16::from(self.version),
address_size: self.address_size,
}
}
pub fn address_size(&self) -> u8 {
self.address_size
}
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(),
}
}
pub fn entry_len(&self) -> R::Offset {
self.length
}
pub fn version(&self) -> u8 {
self.version
}
pub fn augmentation(&self) -> Option<&Augmentation> {
self.augmentation.as_ref()
}
pub fn has_lsda(&self) -> bool {
self.augmentation.map_or(false, |a| a.lsda.is_some())
}
pub fn lsda_encoding(&self) -> Option<constants::DwEhPe> {
self.augmentation.and_then(|a| a.lsda)
}
pub fn personality_with_encoding(&self) -> Option<(constants::DwEhPe, Pointer)> {
self.augmentation.as_ref().and_then(|a| a.personality)
}
pub fn personality(&self) -> Option<Pointer> {
self.augmentation
.as_ref()
.and_then(|a| a.personality)
.map(|(_, p)| p)
}
pub fn fde_address_encoding(&self) -> Option<constants::DwEhPe> {
self.augmentation.and_then(|a| a.fde_address_encoding)
}
pub fn is_signal_trampoline(&self) -> bool {
self.augmentation.map_or(false, |a| a.is_signal_trampoline)
}
pub fn code_alignment_factor(&self) -> u64 {
self.code_alignment_factor
}
pub fn data_alignment_factor(&self) -> i64 {
self.data_alignment_factor
}
pub fn return_address_register(&self) -> Register {
self.return_address_register
}
}
#[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),
}
}
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,
)
}
pub fn offset(&self) -> R::Offset {
self.offset
}
pub fn cie_offset(&self) -> Section::Offset {
self.cie_offset
}
pub fn entry_len(&self) -> R::Offset {
self.length
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FrameDescriptionEntry<R, Offset = <R as Reader>::Offset>
where
R: Reader<Offset = Offset>,
Offset: ReaderOffset,
{
offset: Offset,
length: Offset,
format: Format,
cie: CommonInformationEntry<R, Offset>,
initial_address: u64,
address_range: u64,
augmentation: Option<AugmentationData>,
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 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_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)?.pointer();
let address_range = parse_encoded_value(encoding, parameters, input)?;
Ok((initial_address, address_range))
} else {
let initial_address = input.read_address(cie.address_size)?;
let address_range = input.read_address(cie.address_size)?;
Ok((initial_address, address_range))
}
}
#[inline]
pub fn rows<'a, 'ctx, Section, S>(
&self,
section: &'a Section,
bases: &'a BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, S>,
) -> Result<UnwindTable<'a, 'ctx, R, S>>
where
Section: UnwindSection<R>,
S: UnwindContextStorage<R::Offset>,
{
UnwindTable::new(section, bases, ctx, self)
}
pub fn unwind_info_for_address<'ctx, Section, S>(
&self,
section: &Section,
bases: &BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, S>,
address: u64,
) -> Result<&'ctx UnwindTableRow<R::Offset, S>>
where
Section: UnwindSection<R>,
S: UnwindContextStorage<R::Offset>,
{
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)
}
}
#[allow(clippy::len_without_is_empty)]
impl<R: Reader> FrameDescriptionEntry<R> {
pub fn offset(&self) -> R::Offset {
self.offset
}
pub fn cie(&self) -> &CommonInformationEntry<R> {
&self.cie
}
pub fn entry_len(&self) -> R::Offset {
self.length
}
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(),
}
}
pub fn initial_address(&self) -> u64 {
self.initial_address
}
pub fn end_address(&self) -> u64 {
self.initial_address
.wrapping_add_sized(self.address_range, self.cie.address_size)
}
pub fn len(&self) -> u64 {
self.address_range
}
pub fn contains(&self, address: u64) -> bool {
self.initial_address() <= address && address < self.end_address()
}
pub fn lsda(&self) -> Option<Pointer> {
self.augmentation.as_ref().and_then(|a| a.lsda)
}
#[inline]
pub fn is_signal_trampoline(&self) -> bool {
self.cie().is_signal_trampoline()
}
#[inline]
pub fn personality(&self) -> Option<Pointer> {
self.cie().personality()
}
}
#[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`].
"
)]
pub trait UnwindContextStorage<T: ReaderOffset>: Sized {
type Rules: ArrayLike<Item = (Register, RegisterRule<T>)>;
type Stack: ArrayLike<Item = UnwindTableRow<T, Self>>;
}
#[cfg(feature = "read")]
const MAX_RULES: usize = 192;
#[cfg(feature = "read")]
const MAX_UNWIND_STACK_DEPTH: usize = 4;
#[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]>;
}
#[derive(Clone, PartialEq, Eq)]
pub struct UnwindContext<T, S = StoreOnHeap>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
stack: ArrayVec<S::Stack>,
initial_rule: Option<(Register, RegisterRule<T>)>,
is_initialized: bool,
}
impl<T, S> Debug for UnwindContext<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UnwindContext")
.field("stack", &self.stack)
.field("initial_rule", &self.initial_rule)
.field("is_initialized", &self.is_initialized)
.finish()
}
}
impl<T, S> Default for UnwindContext<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn default() -> Self {
Self::new_in()
}
}
#[cfg(feature = "read")]
impl<T: ReaderOffset> UnwindContext<T> {
pub fn new() -> Self {
Self::new_in()
}
}
impl<T, S> UnwindContext<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
pub fn new_in() -> Self {
let mut ctx = UnwindContext {
stack: Default::default(),
initial_rule: None,
is_initialized: false,
};
ctx.reset();
ctx
}
fn initialize<Section, R>(
&mut self,
section: &Section,
bases: &BaseAddresses,
cie: &CommonInformationEntry<R>,
) -> Result<()>
where
R: Reader<Offset = T>,
Section: UnwindSection<R>,
{
self.reset();
let mut table = UnwindTable::new_for_cie(section, bases, self, cie);
while table.next_row()?.is_some() {}
self.save_initial_rules()?;
Ok(())
}
fn reset(&mut self) {
self.stack.clear();
self.stack.try_push(UnwindTableRow::default()).unwrap();
debug_assert!(self.stack[0].is_default());
self.initial_rule = None;
self.is_initialized = false;
}
fn row(&self) -> &UnwindTableRow<T, S> {
self.stack.last().unwrap()
}
fn row_mut(&mut self) -> &mut UnwindTableRow<T, S> {
self.stack.last_mut().unwrap()
}
fn save_initial_rules(&mut self) -> Result<()> {
debug_assert!(!self.is_initialized);
self.initial_rule = match *self.stack.last().unwrap().registers.rules {
[] => Some((Register(0), RegisterRule::Undefined)),
[ref rule] => Some(rule.clone()),
_ => {
let rules = self.stack.last().unwrap().clone();
self.stack
.try_insert(0, rules)
.map_err(|_| Error::StackFull)?;
None
}
};
self.is_initialized = true;
Ok(())
}
fn start_address(&self) -> u64 {
self.row().start_address
}
fn set_start_address(&mut self, start_address: u64) {
let row = self.row_mut();
row.start_address = start_address;
}
fn set_register_rule(&mut self, register: Register, rule: RegisterRule<T>) -> Result<()> {
let row = self.row_mut();
row.registers.set(register, rule)
}
fn get_initial_rule(&self, register: Register) -> Option<RegisterRule<T>> {
if !self.is_initialized {
return None;
}
Some(match self.initial_rule {
None => self.stack[0].registers.get(register),
Some((r, ref rule)) if r == register => rule.clone(),
_ => RegisterRule::Undefined,
})
}
fn set_cfa(&mut self, cfa: CfaRule<T>) {
self.row_mut().cfa = cfa;
}
fn cfa_mut(&mut self) -> &mut CfaRule<T> {
&mut self.row_mut().cfa
}
fn push_row(&mut self) -> Result<()> {
let new_row = self.row().clone();
self.stack.try_push(new_row).map_err(|_| Error::StackFull)
}
fn pop_row(&mut self) -> Result<()> {
let min_size = if self.is_initialized && self.initial_rule.is_none() {
2
} else {
1
};
if self.stack.len() <= min_size {
return Err(Error::PopWithEmptyStack);
}
self.stack.pop().unwrap();
Ok(())
}
}
#[derive(Debug)]
pub struct UnwindTable<'a, 'ctx, R, S = StoreOnHeap>
where
R: Reader,
S: UnwindContextStorage<R::Offset>,
{
code_alignment_factor: Wrapping<u64>,
data_alignment_factor: Wrapping<i64>,
address_size: u8,
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, S>,
}
impl<'a, 'ctx, R, S> UnwindTable<'a, 'ctx, R, S>
where
R: Reader,
S: UnwindContextStorage<R::Offset>,
{
pub fn new<Section: UnwindSection<R>>(
section: &'a Section,
bases: &'a BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, S>,
fde: &FrameDescriptionEntry<R>,
) -> Result<Self> {
ctx.initialize(section, bases, fde.cie())?;
Ok(Self::new_for_fde(section, bases, ctx, fde))
}
fn new_for_fde<Section: UnwindSection<R>>(
section: &'a Section,
bases: &'a BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, S>,
fde: &FrameDescriptionEntry<R>,
) -> Self {
assert!(ctx.stack.len() >= 1);
UnwindTable {
code_alignment_factor: Wrapping(fde.cie().code_alignment_factor()),
data_alignment_factor: Wrapping(fde.cie().data_alignment_factor()),
address_size: fde.cie().address_size,
next_start_address: fde.initial_address(),
last_end_address: fde.end_address(),
returned_last_row: false,
current_row_valid: false,
instructions: fde.instructions(section, bases),
ctx,
}
}
fn new_for_cie<Section: UnwindSection<R>>(
section: &'a Section,
bases: &'a BaseAddresses,
ctx: &'ctx mut UnwindContext<R::Offset, S>,
cie: &CommonInformationEntry<R>,
) -> Self {
assert!(ctx.stack.len() >= 1);
UnwindTable {
code_alignment_factor: Wrapping(cie.code_alignment_factor()),
data_alignment_factor: Wrapping(cie.data_alignment_factor()),
address_size: cie.address_size,
next_start_address: 0,
last_end_address: 0,
returned_last_row: false,
current_row_valid: false,
instructions: cie.instructions(section, bases),
ctx,
}
}
pub fn next_row(&mut self) -> Result<Option<&UnwindTableRow<R::Offset, S>>> {
assert!(self.ctx.stack.len() >= 1);
self.ctx.set_start_address(self.next_start_address);
self.current_row_valid = false;
loop {
match self.instructions.next() {
Err(e) => return Err(e),
Ok(None) => {
if self.returned_last_row {
return Ok(None);
}
let row = self.ctx.row_mut();
row.end_address = self.last_end_address;
self.returned_last_row = true;
self.current_row_valid = true;
return Ok(Some(row));
}
Ok(Some(instruction)) => {
if self.evaluate(instruction)? {
self.current_row_valid = true;
return Ok(Some(self.ctx.row()));
}
}
};
}
}
pub fn into_current_row(self) -> Option<&'ctx UnwindTableRow<R::Offset, S>> {
if self.current_row_valid {
Some(self.ctx.row())
} else {
None
}
}
fn evaluate(&mut self, instruction: CallFrameInstruction<R::Offset>) -> Result<bool> {
use crate::CallFrameInstruction::*;
match instruction {
SetLoc { address } => {
if address < self.ctx.start_address() {
return Err(Error::InvalidAddressRange);
}
self.next_start_address = address;
self.ctx.row_mut().end_address = self.next_start_address;
return Ok(true);
}
AdvanceLoc { delta } => {
let delta = Wrapping(u64::from(delta)) * self.code_alignment_factor;
self.next_start_address = self
.ctx
.start_address()
.add_sized(delta.0, self.address_size)?;
self.ctx.row_mut().end_address = self.next_start_address;
return Ok(true);
}
DefCfa { register, offset } => {
self.ctx.set_cfa(CfaRule::RegisterAndOffset {
register,
offset: offset as i64,
});
}
DefCfaSf {
register,
factored_offset,
} => {
let data_align = self.data_alignment_factor;
self.ctx.set_cfa(CfaRule::RegisterAndOffset {
register,
offset: (Wrapping(factored_offset) * data_align).0,
});
}
DefCfaRegister { register } => {
if let CfaRule::RegisterAndOffset {
register: ref mut reg,
..
} = *self.ctx.cfa_mut()
{
*reg = register;
} else {
return Err(Error::CfiInstructionInInvalidContext);
}
}
DefCfaOffset { offset } => {
if let CfaRule::RegisterAndOffset {
offset: ref mut off,
..
} = *self.ctx.cfa_mut()
{
*off = offset as i64;
} else {
return Err(Error::CfiInstructionInInvalidContext);
}
}
DefCfaOffsetSf { factored_offset } => {
if let CfaRule::RegisterAndOffset {
offset: ref mut off,
..
} = *self.ctx.cfa_mut()
{
let data_align = self.data_alignment_factor;
*off = (Wrapping(factored_offset) * data_align).0;
} else {
return Err(Error::CfiInstructionInInvalidContext);
}
}
DefCfaExpression { expression } => {
self.ctx.set_cfa(CfaRule::Expression(expression));
}
Undefined { register } => {
self.ctx
.set_register_rule(register, RegisterRule::Undefined)?;
}
SameValue { register } => {
self.ctx
.set_register_rule(register, RegisterRule::SameValue)?;
}
Offset {
register,
factored_offset,
} => {
let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor;
self.ctx
.set_register_rule(register, RegisterRule::Offset(offset.0))?;
}
OffsetExtendedSf {
register,
factored_offset,
} => {
let offset = Wrapping(factored_offset) * self.data_alignment_factor;
self.ctx
.set_register_rule(register, RegisterRule::Offset(offset.0))?;
}
ValOffset {
register,
factored_offset,
} => {
let offset = Wrapping(factored_offset as i64) * self.data_alignment_factor;
self.ctx
.set_register_rule(register, RegisterRule::ValOffset(offset.0))?;
}
ValOffsetSf {
register,
factored_offset,
} => {
let offset = Wrapping(factored_offset) * self.data_alignment_factor;
self.ctx
.set_register_rule(register, RegisterRule::ValOffset(offset.0))?;
}
Register {
dest_register,
src_register,
} => {
self.ctx
.set_register_rule(dest_register, RegisterRule::Register(src_register))?;
}
Expression {
register,
expression,
} => {
let expression = RegisterRule::Expression(expression);
self.ctx.set_register_rule(register, expression)?;
}
ValExpression {
register,
expression,
} => {
let expression = RegisterRule::ValExpression(expression);
self.ctx.set_register_rule(register, expression)?;
}
Restore { register } => {
let initial_rule = if let Some(rule) = self.ctx.get_initial_rule(register) {
rule
} else {
return Err(Error::CfiInstructionInInvalidContext);
};
self.ctx.set_register_rule(register, initial_rule)?;
}
RememberState => {
self.ctx.push_row()?;
}
RestoreState => {
let start_address = self.ctx.start_address();
self.ctx.pop_row()?;
self.ctx.set_start_address(start_address);
}
ArgsSize { size } => {
self.ctx.row_mut().saved_args_size = size;
}
NegateRaState => {
let register = crate::AArch64::RA_SIGN_STATE;
let value = match self.ctx.row().register(register) {
RegisterRule::Undefined => 0,
RegisterRule::Constant(value) => value,
_ => return Err(Error::CfiInstructionInInvalidContext),
};
self.ctx
.set_register_rule(register, RegisterRule::Constant(value ^ 1))?;
}
Nop => {}
};
Ok(false)
}
}
struct RegisterRuleMap<T, S = StoreOnHeap>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
rules: ArrayVec<S::Rules>,
}
impl<T, S> Debug for RegisterRuleMap<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("RegisterRuleMap")
.field("rules", &self.rules)
.finish()
}
}
impl<T, S> Clone for RegisterRuleMap<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn clone(&self) -> Self {
Self {
rules: self.rules.clone(),
}
}
}
impl<T, S> Default for RegisterRuleMap<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn default() -> Self {
RegisterRuleMap {
rules: Default::default(),
}
}
}
impl<T, S> RegisterRuleMap<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn is_default(&self) -> bool {
self.rules.is_empty()
}
fn get(&self, register: Register) -> RegisterRule<T> {
self.rules
.iter()
.find(|rule| rule.0 == register)
.map(|r| {
debug_assert!(r.1.is_defined());
r.1.clone()
})
.unwrap_or(RegisterRule::Undefined)
}
fn set(&mut self, register: Register, rule: RegisterRule<T>) -> Result<()> {
if !rule.is_defined() {
let idx = self
.rules
.iter()
.enumerate()
.find(|&(_, r)| r.0 == register)
.map(|(i, _)| i);
if let Some(idx) = idx {
self.rules.swap_remove(idx);
}
return Ok(());
}
for &mut (reg, ref mut old_rule) in &mut *self.rules {
debug_assert!(old_rule.is_defined());
if reg == register {
*old_rule = rule;
return Ok(());
}
}
self.rules
.try_push((register, rule))
.map_err(|_| Error::TooManyRegisterRules)
}
fn iter(&self) -> RegisterRuleIter<'_, T> {
RegisterRuleIter(self.rules.iter())
}
}
impl<'a, R, S> FromIterator<&'a (Register, RegisterRule<R>)> for RegisterRuleMap<R, S>
where
R: 'a + ReaderOffset,
S: UnwindContextStorage<R>,
{
fn from_iter<T>(iter: T) -> Self
where
T: IntoIterator<Item = &'a (Register, RegisterRule<R>)>,
{
let iter = iter.into_iter();
let mut rules = RegisterRuleMap::default();
for &(reg, ref rule) in iter.filter(|r| r.1.is_defined()) {
rules.set(reg, rule.clone()).expect(
"This is only used in tests, impl isn't exposed publicly.
If you trip this, fix your test",
);
}
rules
}
}
impl<T, S> PartialEq for RegisterRuleMap<T, S>
where
T: ReaderOffset + PartialEq,
S: UnwindContextStorage<T>,
{
fn eq(&self, rhs: &Self) -> bool {
for &(reg, ref rule) in &*self.rules {
debug_assert!(rule.is_defined());
if *rule != rhs.get(reg) {
return false;
}
}
for &(reg, ref rhs_rule) in &*rhs.rules {
debug_assert!(rhs_rule.is_defined());
if *rhs_rule != self.get(reg) {
return false;
}
}
true
}
}
impl<T, S> Eq for RegisterRuleMap<T, S>
where
T: ReaderOffset + Eq,
S: UnwindContextStorage<T>,
{
}
#[derive(Debug, Clone)]
pub struct 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>);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[derive(PartialEq, Eq)]
pub struct UnwindTableRow<T, S = StoreOnHeap>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
start_address: u64,
end_address: u64,
saved_args_size: u64,
cfa: CfaRule<T>,
registers: RegisterRuleMap<T, S>,
}
impl<T, S> Debug for UnwindTableRow<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UnwindTableRow")
.field("start_address", &self.start_address)
.field("end_address", &self.end_address)
.field("saved_args_size", &self.saved_args_size)
.field("cfa", &self.cfa)
.field("registers", &self.registers)
.finish()
}
}
impl<T, S> Clone for UnwindTableRow<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn clone(&self) -> Self {
Self {
start_address: self.start_address,
end_address: self.end_address,
saved_args_size: self.saved_args_size,
cfa: self.cfa.clone(),
registers: self.registers.clone(),
}
}
}
impl<T, S> Default for UnwindTableRow<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn default() -> Self {
UnwindTableRow {
start_address: 0,
end_address: 0,
saved_args_size: 0,
cfa: Default::default(),
registers: Default::default(),
}
}
}
impl<T, S> UnwindTableRow<T, S>
where
T: ReaderOffset,
S: UnwindContextStorage<T>,
{
fn is_default(&self) -> bool {
self.start_address == 0
&& self.end_address == 0
&& self.cfa.is_default()
&& self.registers.is_default()
}
pub fn start_address(&self) -> u64 {
self.start_address
}
pub fn end_address(&self) -> u64 {
self.end_address
}
pub fn contains(&self, address: u64) -> bool {
self.start_address <= address && address < self.end_address
}
pub fn saved_args_size(&self) -> u64 {
self.saved_args_size
}
pub fn cfa(&self) -> &CfaRule<T> {
&self.cfa
}
pub fn register(&self, register: Register) -> RegisterRule<T> {
self.registers.get(register)
}
pub fn registers(&self) -> RegisterRuleIter<'_, T> {
self.registers.iter()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CfaRule<T: ReaderOffset> {
RegisterAndOffset {
register: Register,
offset: i64,
},
Expression(UnwindExpression<T>),
}
impl<T: ReaderOffset> Default for CfaRule<T> {
fn default() -> Self {
CfaRule::RegisterAndOffset {
register: Register(0),
offset: 0,
}
}
}
impl<T: ReaderOffset> CfaRule<T> {
fn is_default(&self) -> bool {
match *self {
CfaRule::RegisterAndOffset { register, offset } => {
register == Register(0) && offset == 0
}
_ => false,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum RegisterRule<T: ReaderOffset> {
Undefined,
SameValue,
Offset(i64),
ValOffset(i64),
Register(Register),
Expression(UnwindExpression<T>),
ValExpression(UnwindExpression<T>),
Architectural,
Constant(u64),
}
impl<T: ReaderOffset> RegisterRule<T> {
fn is_defined(&self) -> bool {
!matches!(*self, RegisterRule::Undefined)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum CallFrameInstruction<T: ReaderOffset> {
SetLoc {
address: u64,
},
AdvanceLoc {
delta: u32,
},
DefCfa {
register: Register,
offset: u64,
},
DefCfaSf {
register: Register,
factored_offset: i64,
},
DefCfaRegister {
register: Register,
},
DefCfaOffset {
offset: u64,
},
DefCfaOffsetSf {
factored_offset: i64,
},
DefCfaExpression {
expression: UnwindExpression<T>,
},
Undefined {
register: Register,
},
SameValue {
register: Register,
},
Offset {
register: Register,
factored_offset: u64,
},
OffsetExtendedSf {
register: Register,
factored_offset: i64,
},
ValOffset {
register: Register,
factored_offset: u64,
},
ValOffsetSf {
register: Register,
factored_offset: i64,
},
Register {
dest_register: Register,
src_register: Register,
},
Expression {
register: Register,
expression: UnwindExpression<T>,
},
ValExpression {
register: Register,
expression: UnwindExpression<T>,
},
Restore {
register: Register,
},
RememberState,
RestoreState,
ArgsSize {
size: u64,
},
NegateRaState,
Nop,
}
const CFI_INSTRUCTION_HIGH_BITS_MASK: u8 = 0b1100_0000;
const CFI_INSTRUCTION_LOW_BITS_MASK: u8 = !CFI_INSTRUCTION_HIGH_BITS_MASK;
impl<T: ReaderOffset> CallFrameInstruction<T> {
fn parse<R: Reader<Offset = T>>(
input: &mut R,
address_encoding: Option<DwEhPe>,
parameters: &PointerEncodingParameters<'_, R>,
vendor: Vendor,
) -> Result<CallFrameInstruction<T>> {
let instruction = input.read_u8()?;
let high_bits = instruction & CFI_INSTRUCTION_HIGH_BITS_MASK;
if high_bits == constants::DW_CFA_advance_loc.0 {
let delta = instruction & CFI_INSTRUCTION_LOW_BITS_MASK;
return Ok(CallFrameInstruction::AdvanceLoc {
delta: u32::from(delta),
});
}
if high_bits == constants::DW_CFA_offset.0 {
let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into());
let offset = input.read_uleb128()?;
return Ok(CallFrameInstruction::Offset {
register,
factored_offset: offset,
});
}
if high_bits == constants::DW_CFA_restore.0 {
let register = Register((instruction & CFI_INSTRUCTION_LOW_BITS_MASK).into());
return Ok(CallFrameInstruction::Restore { register });
}
debug_assert_eq!(high_bits, 0);
let instruction = constants::DwCfa(instruction);
match instruction {
constants::DW_CFA_nop => Ok(CallFrameInstruction::Nop),
constants::DW_CFA_set_loc => {
let address = if let Some(encoding) = address_encoding {
parse_encoded_pointer(encoding, parameters, input)?.direct()?
} else {
input.read_address(parameters.address_size)?
};
Ok(CallFrameInstruction::SetLoc { address })
}
constants::DW_CFA_advance_loc1 => {
let delta = input.read_u8()?;
Ok(CallFrameInstruction::AdvanceLoc {
delta: u32::from(delta),
})
}
constants::DW_CFA_advance_loc2 => {
let delta = input.read_u16()?;
Ok(CallFrameInstruction::AdvanceLoc {
delta: u32::from(delta),
})
}
constants::DW_CFA_advance_loc4 => {
let delta = input.read_u32()?;
Ok(CallFrameInstruction::AdvanceLoc { delta })
}
constants::DW_CFA_offset_extended => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let offset = input.read_uleb128()?;
Ok(CallFrameInstruction::Offset {
register,
factored_offset: offset,
})
}
constants::DW_CFA_restore_extended => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
Ok(CallFrameInstruction::Restore { register })
}
constants::DW_CFA_undefined => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
Ok(CallFrameInstruction::Undefined { register })
}
constants::DW_CFA_same_value => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
Ok(CallFrameInstruction::SameValue { register })
}
constants::DW_CFA_register => {
let dest = input.read_uleb128().and_then(Register::from_u64)?;
let src = input.read_uleb128().and_then(Register::from_u64)?;
Ok(CallFrameInstruction::Register {
dest_register: dest,
src_register: src,
})
}
constants::DW_CFA_remember_state => Ok(CallFrameInstruction::RememberState),
constants::DW_CFA_restore_state => Ok(CallFrameInstruction::RestoreState),
constants::DW_CFA_def_cfa => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let offset = input.read_uleb128()?;
Ok(CallFrameInstruction::DefCfa { register, offset })
}
constants::DW_CFA_def_cfa_register => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
Ok(CallFrameInstruction::DefCfaRegister { register })
}
constants::DW_CFA_def_cfa_offset => {
let offset = input.read_uleb128()?;
Ok(CallFrameInstruction::DefCfaOffset { offset })
}
constants::DW_CFA_def_cfa_expression => {
let length = input.read_uleb128().and_then(R::Offset::from_u64)?;
let offset = input.offset_from(parameters.section);
input.skip(length)?;
Ok(CallFrameInstruction::DefCfaExpression {
expression: UnwindExpression { offset, length },
})
}
constants::DW_CFA_expression => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let length = input.read_uleb128().and_then(R::Offset::from_u64)?;
let offset = input.offset_from(parameters.section);
input.skip(length)?;
Ok(CallFrameInstruction::Expression {
register,
expression: UnwindExpression { offset, length },
})
}
constants::DW_CFA_offset_extended_sf => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let offset = input.read_sleb128()?;
Ok(CallFrameInstruction::OffsetExtendedSf {
register,
factored_offset: offset,
})
}
constants::DW_CFA_def_cfa_sf => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let offset = input.read_sleb128()?;
Ok(CallFrameInstruction::DefCfaSf {
register,
factored_offset: offset,
})
}
constants::DW_CFA_def_cfa_offset_sf => {
let offset = input.read_sleb128()?;
Ok(CallFrameInstruction::DefCfaOffsetSf {
factored_offset: offset,
})
}
constants::DW_CFA_val_offset => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let offset = input.read_uleb128()?;
Ok(CallFrameInstruction::ValOffset {
register,
factored_offset: offset,
})
}
constants::DW_CFA_val_offset_sf => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let offset = input.read_sleb128()?;
Ok(CallFrameInstruction::ValOffsetSf {
register,
factored_offset: offset,
})
}
constants::DW_CFA_val_expression => {
let register = input.read_uleb128().and_then(Register::from_u64)?;
let length = input.read_uleb128().and_then(R::Offset::from_u64)?;
let offset = input.offset_from(parameters.section);
input.skip(length)?;
Ok(CallFrameInstruction::ValExpression {
register,
expression: UnwindExpression { offset, length },
})
}
constants::DW_CFA_GNU_args_size => {
let size = input.read_uleb128()?;
Ok(CallFrameInstruction::ArgsSize { size })
}
constants::DW_CFA_AARCH64_negate_ra_state if vendor == Vendor::AArch64 => {
Ok(CallFrameInstruction::NegateRaState)
}
otherwise => Err(Error::UnknownCallFrameInstruction(otherwise)),
}
}
}
#[derive(Clone, Debug)]
pub struct CallFrameInstructionIter<'a, R: Reader> {
input: R,
address_encoding: Option<constants::DwEhPe>,
parameters: PointerEncodingParameters<'a, R>,
vendor: Vendor,
}
impl<'a, R: Reader> CallFrameInstructionIter<'a, R> {
pub fn next(&mut self) -> Result<Option<CallFrameInstruction<R::Offset>>> {
if self.input.is_empty() {
return Ok(None);
}
match CallFrameInstruction::parse(
&mut self.input,
self.address_encoding,
&self.parameters,
self.vendor,
) {
Ok(instruction) => Ok(Some(instruction)),
Err(e) => {
self.input.empty();
Err(e)
}
}
}
}
#[cfg(feature = "fallible-iterator")]
impl<'a, R: Reader> fallible_iterator::FallibleIterator for CallFrameInstructionIter<'a, R> {
type Item = CallFrameInstruction<R::Offset>;
type Error = Error;
fn next(&mut self) -> ::core::result::Result<Option<Self::Item>, Self::Error> {
CallFrameInstructionIter::next(self)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UnwindExpression<T: ReaderOffset> {
pub offset: T,
pub length: T,
}
impl<T: ReaderOffset> UnwindExpression<T> {
pub fn get<R, S>(&self, section: &S) -> Result<Expression<R>>
where
R: Reader<Offset = T>,
S: UnwindSection<R>,
{
let input = &mut section.section().clone();
input.skip(self.offset)?;
let data = input.split(self.length)?;
Ok(Expression(data))
}
}
#[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))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Pointer {
Direct(u64),
Indirect(u64),
}
impl Default for Pointer {
#[inline]
fn default() -> Self {
Pointer::Direct(0)
}
}
impl Pointer {
#[inline]
fn new(encoding: constants::DwEhPe, address: u64) -> Pointer {
if encoding.is_indirect() {
Pointer::Indirect(address)
} else {
Pointer::Direct(address)
}
}
#[inline]
pub fn direct(self) -> Result<u64> {
match self {
Pointer::Direct(p) => Ok(p),
Pointer::Indirect(_) => Err(Error::UnsupportedPointerEncoding),
}
}
#[inline]
pub fn pointer(self) -> u64 {
match self {
Pointer::Direct(p) | Pointer::Indirect(p) => p,
}
}
}
#[derive(Clone, Debug)]
struct PointerEncodingParameters<'a, R: Reader> {
bases: &'a SectionBaseAddresses,
func_base: Option<u64>,
address_size: u8,
section: &'a R,
}
fn parse_encoded_pointer<R: Reader>(
encoding: constants::DwEhPe,
parameters: &PointerEncodingParameters<'_, R>,
input: &mut R,
) -> Result<Pointer> {
if !encoding.is_valid_encoding() {
return Err(Error::UnknownPointerEncoding(encoding));
}
if encoding == constants::DW_EH_PE_omit {
return Err(Error::CannotParseOmitPointerEncoding);
}
let base = match encoding.application() {
constants::DW_EH_PE_absptr => 0,
constants::DW_EH_PE_pcrel => {
if let Some(section_base) = parameters.bases.section {
let offset_from_section = input.offset_from(parameters.section);
section_base
.wrapping_add_sized(offset_from_section.into_u64(), parameters.address_size)
} else {
return Err(Error::PcRelativePointerButSectionBaseIsUndefined);
}
}
constants::DW_EH_PE_textrel => {
if let Some(text) = parameters.bases.text {
text
} else {
return Err(Error::TextRelativePointerButTextBaseIsUndefined);
}
}
constants::DW_EH_PE_datarel => {
if let Some(data) = parameters.bases.data {
data
} else {
return Err(Error::DataRelativePointerButDataBaseIsUndefined);
}
}
constants::DW_EH_PE_funcrel => {
if let Some(func) = parameters.func_base {
func
} else {
return Err(Error::FuncRelativePointerInBadContext);
}
}
constants::DW_EH_PE_aligned => return Err(Error::UnsupportedPointerEncoding),
_ => unreachable!(),
};
let offset = parse_encoded_value(encoding, parameters, input)?;
Ok(Pointer::new(
encoding,
base.wrapping_add_sized(offset, parameters.address_size),
))
}
fn parse_encoded_value<R: Reader>(
encoding: constants::DwEhPe,
parameters: &PointerEncodingParameters<'_, R>,
input: &mut R,
) -> Result<u64> {
match encoding.format() {
constants::DW_EH_PE_absptr => input.read_address(parameters.address_size),
constants::DW_EH_PE_uleb128 => input.read_uleb128(),
constants::DW_EH_PE_udata2 => input.read_u16().map(u64::from),
constants::DW_EH_PE_udata4 => input.read_u32().map(u64::from),
constants::DW_EH_PE_udata8 => input.read_u64(),
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),
_ => unreachable!(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::{parse_cfi_entry, AugmentationData, RegisterRuleMap, UnwindContext};
use crate::common::Format;
use crate::constants;
use crate::endianity::{BigEndian, Endianity, LittleEndian, NativeEndian};
use crate::read::{
EndianSlice, Error, Pointer, ReaderOffsetId, Result, Section as ReadSection,
};
use crate::test_util::GimliSectionMethods;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::marker::PhantomData;
use core::mem;
use test_assembler::{Endian, Label, LabelMaker, LabelOrNum, Section, ToLabelOrNum};
#[derive(Clone, Copy)]
struct SectionKind<Section>(PhantomData<Section>);
impl<T> SectionKind<T> {
fn endian<'input, E>(self) -> Endian
where
E: Endianity,
T: UnwindSection<EndianSlice<'input, E>>,
T::Offset: UnwindOffset<usize>,
{
if E::default().is_big_endian() {
Endian::Big
} else {
Endian::Little
}
}
fn section<'input, E>(self, contents: &'input [u8]) -> T
where
E: Endianity,
T: UnwindSection<EndianSlice<'input, E>> + ReadSection<EndianSlice<'input, E>>,
T::Offset: UnwindOffset<usize>,
{
EndianSlice::new(contents, E::default()).into()
}
}
fn debug_frame_le<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, LittleEndian>>> {
SectionKind(PhantomData)
}
fn debug_frame_be<'a>() -> SectionKind<DebugFrame<EndianSlice<'a, BigEndian>>> {
SectionKind(PhantomData)
}
fn eh_frame_le<'a>() -> SectionKind<EhFrame<EndianSlice<'a, LittleEndian>>> {
SectionKind(PhantomData)
}
fn parse_fde<Section, O, F, R>(
section: Section,
input: &mut R,
get_cie: F,
) -> Result<FrameDescriptionEntry<R>>
where
R: Reader,
Section: UnwindSection<R, Offset = O>,
O: UnwindOffset<R::Offset>,
F: FnMut(&Section, &BaseAddresses, O) -> Result<CommonInformationEntry<R>>,
{
let bases = Default::default();
match parse_cfi_entry(&bases, §ion, input) {
Ok(Some(CieOrFde::Fde(partial))) => partial.parse(get_cie),
Ok(_) => Err(Error::NoEntryAtGivenOffset),
Err(e) => Err(e),
}
}
trait CfiSectionMethods: GimliSectionMethods {
fn cie<'aug, 'input, E, T>(
self,
_kind: SectionKind<T>,
augmentation: Option<&'aug str>,
cie: &mut CommonInformationEntry<EndianSlice<'input, E>>,
) -> Self
where
E: Endianity,
T: UnwindSection<EndianSlice<'input, E>>,
T::Offset: UnwindOffset;
fn fde<'a, 'input, E, T, L>(
self,
_kind: SectionKind<T>,
cie_offset: L,
fde: &mut FrameDescriptionEntry<EndianSlice<'input, E>>,
) -> Self
where
E: Endianity,
T: UnwindSection<EndianSlice<'input, E>>,
T::Offset: UnwindOffset,
L: ToLabelOrNum<'a, u64>;
}
impl CfiSectionMethods for Section {
fn cie<'aug, 'input, E, T>(
self,
_kind: SectionKind<T>,
augmentation: Option<&'aug str>,
cie: &mut CommonInformationEntry<EndianSlice<'input, E>>,
) -> Self
where
E: Endianity,
T: UnwindSection<EndianSlice<'input, E>>,
T::Offset: UnwindOffset,
{
cie.offset = self.size() as _;
let length = Label::new();
let start = Label::new();
let end = Label::new();
let section = match cie.format {
Format::Dwarf32 => self.D32(&length).mark(&start).D32(0xffff_ffff),
Format::Dwarf64 => {
let section = self.D32(0xffff_ffff);
section.D64(&length).mark(&start).D64(0xffff_ffff_ffff_ffff)
}
};
let mut section = section.D8(cie.version);
if let Some(augmentation) = augmentation {
section = section.append_bytes(augmentation.as_bytes());
}
let section = section.D8(0);
let section = if T::has_address_and_segment_sizes(cie.version) {
section.D8(cie.address_size).D8(0)
} 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.address_size {
4 => section
.D32(fde.initial_address() as u32)
.D32(fde.len() as u32),
8 => section.D64(fde.initial_address()).D64(fde.len()),
x => panic!("Unsupported address size: {}", x),
};
let section = if let Some(ref augmentation) = fde.augmentation {
let cie_aug = fde
.cie
.augmentation
.expect("FDE has augmentation, but CIE doesn't");
if let Some(lsda) = augmentation.lsda {
assert_eq!(
cie_aug
.lsda
.expect("FDE has lsda, but CIE doesn't")
.format(),
constants::DW_EH_PE_absptr
);
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 {
section.uleb(0)
}
} else {
section
};
let section = section.append_bytes(fde.instructions.slice()).mark(&end);
fde.length = (&end - &start) as usize;
length.set_const(fde.length as u64);
section
}
}
trait ResultExt {
fn map_eof(self, input: &[u8]) -> Self;
}
impl<T> ResultExt for Result<T> {
fn map_eof(self, input: &[u8]) -> Self {
match self {
Err(Error::UnexpectedEof(id)) => {
let id = ReaderOffsetId(id.0 - input.as_ptr() as u64);
Err(Error::UnexpectedEof(id))
}
r => r,
}
}
}
fn assert_parse_cie<'input, E>(
kind: SectionKind<DebugFrame<EndianSlice<'input, E>>>,
section: Section,
address_size: u8,
expected: Result<(
EndianSlice<'input, E>,
CommonInformationEntry<EndianSlice<'input, E>>,
)>,
) where
E: Endianity,
{
let section = section.get_contents().unwrap();
let mut debug_frame = kind.section(§ion);
debug_frame.set_address_size(address_size);
let input = &mut EndianSlice::new(§ion, E::default());
let bases = Default::default();
let result = CommonInformationEntry::parse(&bases, &debug_frame, input);
let result = result.map(|cie| (*input, cie)).map_eof(§ion);
assert_eq!(result, expected);
}
#[test]
fn test_parse_cie_incomplete_length_32() {
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian()).L16(5);
assert_parse_cie(
kind,
section,
8,
Err(Error::UnexpectedEof(ReaderOffsetId(0))),
);
}
#[test]
fn test_parse_cie_incomplete_length_64() {
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian())
.L32(0xffff_ffff)
.L32(12345);
assert_parse_cie(
kind,
section,
8,
Err(Error::UnexpectedEof(ReaderOffsetId(4))),
);
}
#[test]
fn test_parse_cie_incomplete_id_32() {
let kind = debug_frame_be();
let section = Section::with_endian(kind.endian())
.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())
.B32(4)
.B32(0xbad1_bad2);
assert_parse_cie(kind, section, 8, Err(Error::NotCieId));
}
#[test]
fn test_parse_cie_32_bad_version() {
let mut cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 99,
augmentation: None,
address_size: 4,
code_alignment_factor: 1,
data_alignment_factor: 2,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&[], LittleEndian),
};
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie);
assert_parse_cie(kind, section, 4, Err(Error::UnknownVersion(99)));
}
#[test]
fn test_parse_cie_unknown_augmentation() {
let length = Label::new();
let start = Label::new();
let end = Label::new();
let augmentation = "replicant";
let expected_rest = [1, 2, 3];
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian())
.L32(&length)
.mark(&start)
.L32(0xffff_ffff)
.D8(4)
.append_bytes(augmentation.as_bytes())
.D8(0)
.L32(1)
.L32(2)
.L32(3)
.L32(4)
.L32(5)
.L32(6)
.mark(&end)
.append_bytes(&expected_rest);
let expected_length = (&end - &start) as u64;
length.set_const(expected_length);
assert_parse_cie(kind, section, 8, Err(Error::UnknownAugmentation));
}
fn test_parse_cie(format: Format, version: u8, address_size: u8) {
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie = CommonInformationEntry {
offset: 0,
length: 0,
format,
version,
augmentation: None,
address_size,
code_alignment_factor: 16,
data_alignment_factor: 32,
return_address_register: Register(1),
initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian),
};
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian())
.cie(kind, None, &mut cie)
.append_bytes(&expected_rest);
assert_parse_cie(
kind,
section,
address_size,
Ok((EndianSlice::new(&expected_rest, LittleEndian), cie)),
);
}
#[test]
fn test_parse_cie_32_ok() {
test_parse_cie(Format::Dwarf32, 1, 4);
test_parse_cie(Format::Dwarf32, 1, 8);
test_parse_cie(Format::Dwarf32, 4, 4);
test_parse_cie(Format::Dwarf32, 4, 8);
}
#[test]
fn test_parse_cie_64_ok() {
test_parse_cie(Format::Dwarf64, 1, 4);
test_parse_cie(Format::Dwarf64, 1, 8);
test_parse_cie(Format::Dwarf64, 4, 4);
test_parse_cie(Format::Dwarf64, 4, 8);
}
#[test]
fn test_parse_cie_length_too_big() {
let expected_instrs: Vec<_> = (0..13).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
code_alignment_factor: 0,
data_alignment_factor: 0,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&expected_instrs, LittleEndian),
};
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian()).cie(kind, None, &mut cie);
let mut contents = section.get_contents().unwrap();
contents[0] = 0;
contents[1] = 0;
contents[2] = 0;
contents[3] = 255;
let debug_frame = DebugFrame::new(&contents, LittleEndian);
let bases = Default::default();
assert_eq!(
CommonInformationEntry::parse(
&bases,
&debug_frame,
&mut EndianSlice::new(&contents, LittleEndian)
)
.map_eof(&contents),
Err(Error::UnexpectedEof(ReaderOffsetId(4)))
);
}
#[test]
fn test_parse_fde_incomplete_length_32() {
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian()).L16(5);
let section = section.get_contents().unwrap();
let debug_frame = kind.section(§ion);
let rest = &mut EndianSlice::new(§ion, LittleEndian);
assert_eq!(
parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion),
Err(Error::UnexpectedEof(ReaderOffsetId(0)))
);
}
#[test]
fn test_parse_fde_incomplete_length_64() {
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian())
.L32(0xffff_ffff)
.L32(12345);
let section = section.get_contents().unwrap();
let debug_frame = kind.section(§ion);
let rest = &mut EndianSlice::new(§ion, LittleEndian);
assert_eq!(
parse_fde(debug_frame, rest, UnwindSection::cie_from_offset).map_eof(§ion),
Err(Error::UnexpectedEof(ReaderOffsetId(4)))
);
}
#[test]
fn test_parse_fde_incomplete_cie_pointer_32() {
let kind = debug_frame_be();
let section = Section::with_endian(kind.endian())
.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,
address_size: 8,
code_alignment_factor: 3,
data_alignment_factor: 2,
return_address_register: Register(1),
initial_instructions: EndianSlice::new(&[], LittleEndian),
};
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0xfeed_beef,
address_range: 39,
augmentation: None,
instructions: EndianSlice::new(&expected_instrs, LittleEndian),
};
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian())
.fde(kind, cie_offset, &mut fde)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let debug_frame = kind.section(§ion);
let rest = &mut EndianSlice::new(§ion, LittleEndian);
let get_cie = |_: &_, _: &_, offset| {
assert_eq!(offset, DebugFrameOffset(cie_offset as usize));
Ok(cie.clone())
};
assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde));
assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_fde_64_ok() {
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let cie_offset = 0xbad0_bad1;
let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect();
let cie = CommonInformationEntry {
offset: 0,
length: 100,
format: Format::Dwarf64,
version: 4,
augmentation: None,
address_size: 8,
code_alignment_factor: 3,
data_alignment_factor: 2,
return_address_register: Register(1),
initial_instructions: EndianSlice::new(&[], LittleEndian),
};
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf64,
cie: cie.clone(),
initial_address: 0xfeed_beef,
address_range: 999,
augmentation: None,
instructions: EndianSlice::new(&expected_instrs, LittleEndian),
};
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian())
.fde(kind, cie_offset, &mut fde)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let debug_frame = kind.section(§ion);
let rest = &mut EndianSlice::new(§ion, LittleEndian);
let get_cie = |_: &_, _: &_, offset| {
assert_eq!(offset, DebugFrameOffset(cie_offset as usize));
Ok(cie.clone())
};
assert_eq!(parse_fde(debug_frame, rest, get_cie), Ok(fde));
assert_eq!(*rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_entry_on_cie_32_ok() {
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
code_alignment_factor: 16,
data_alignment_factor: 32,
return_address_register: Register(1),
initial_instructions: EndianSlice::new(&expected_instrs, BigEndian),
};
let kind = debug_frame_be();
let section = Section::with_endian(kind.endian())
.cie(kind, None, &mut cie)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let debug_frame = kind.section(§ion);
let rest = &mut EndianSlice::new(§ion, BigEndian);
let bases = Default::default();
assert_eq!(
parse_cfi_entry(&bases, &debug_frame, rest),
Ok(Some(CieOrFde::Cie(cie)))
);
assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian));
}
#[test]
fn test_parse_cfi_entry_on_fde_32_ok() {
let cie_offset = 0x1234_5678;
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let expected_instrs: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect();
let cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
code_alignment_factor: 16,
data_alignment_factor: 32,
return_address_register: Register(1),
initial_instructions: EndianSlice::new(&[], BigEndian),
};
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0xfeed_beef,
address_range: 39,
augmentation: None,
instructions: EndianSlice::new(&expected_instrs, BigEndian),
};
let kind = debug_frame_be();
let section = Section::with_endian(kind.endian())
.fde(kind, cie_offset, &mut fde)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let debug_frame = kind.section(§ion);
let rest = &mut EndianSlice::new(§ion, BigEndian);
let bases = Default::default();
match parse_cfi_entry(&bases, &debug_frame, rest) {
Ok(Some(CieOrFde::Fde(partial))) => {
assert_eq!(*rest, EndianSlice::new(&expected_rest, BigEndian));
assert_eq!(partial.length, fde.length);
assert_eq!(partial.format, fde.format);
assert_eq!(partial.cie_offset, DebugFrameOffset(cie_offset as usize));
let get_cie = |_: &_, _: &_, offset| {
assert_eq!(offset, DebugFrameOffset(cie_offset as usize));
Ok(cie.clone())
};
assert_eq!(partial.parse(get_cie), Ok(fde));
}
otherwise => panic!("Unexpected result: {:#?}", otherwise),
}
}
#[test]
fn test_cfi_entries_iter() {
let expected_instrs1: Vec<_> = (0..4).map(|_| constants::DW_CFA_nop.0).collect();
let expected_instrs2: Vec<_> = (0..8).map(|_| constants::DW_CFA_nop.0).collect();
let expected_instrs3: Vec<_> = (0..12).map(|_| constants::DW_CFA_nop.0).collect();
let expected_instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie1 = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
code_alignment_factor: 1,
data_alignment_factor: 2,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&expected_instrs1, BigEndian),
};
let mut cie2 = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
code_alignment_factor: 3,
data_alignment_factor: 2,
return_address_register: Register(1),
initial_instructions: EndianSlice::new(&expected_instrs2, BigEndian),
};
let cie1_location = Label::new();
let cie2_location = Label::new();
let kind = debug_frame_be();
let section = Section::with_endian(kind.endian())
.mark(&cie1_location)
.cie(kind, None, &mut cie1)
.mark(&cie2_location)
.cie(kind, None, &mut cie2);
let mut fde1 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie1.clone(),
initial_address: 0xfeed_beef,
address_range: 39,
augmentation: None,
instructions: EndianSlice::new(&expected_instrs3, BigEndian),
};
let mut fde2 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie2.clone(),
initial_address: 0xfeed_face,
address_range: 9000,
augmentation: None,
instructions: EndianSlice::new(&expected_instrs4, BigEndian),
};
let section =
section
.fde(kind, &cie1_location, &mut fde1)
.fde(kind, &cie2_location, &mut fde2);
section.start().set_const(0);
let cie1_offset = cie1_location.value().unwrap() as usize;
let cie2_offset = cie2_location.value().unwrap() as usize;
let contents = section.get_contents().unwrap();
let debug_frame = kind.section(&contents);
let bases = Default::default();
let mut entries = debug_frame.entries(&bases);
assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie1.clone()))));
assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie2.clone()))));
match entries.next() {
Ok(Some(CieOrFde::Fde(partial))) => {
assert_eq!(partial.length, fde1.length);
assert_eq!(partial.format, fde1.format);
assert_eq!(partial.cie_offset, DebugFrameOffset(cie1_offset));
let get_cie = |_: &_, _: &_, offset| {
assert_eq!(offset, DebugFrameOffset(cie1_offset));
Ok(cie1.clone())
};
assert_eq!(partial.parse(get_cie), Ok(fde1));
}
otherwise => panic!("Unexpected result: {:#?}", otherwise),
}
match entries.next() {
Ok(Some(CieOrFde::Fde(partial))) => {
assert_eq!(partial.length, fde2.length);
assert_eq!(partial.format, fde2.format);
assert_eq!(partial.cie_offset, DebugFrameOffset(cie2_offset));
let get_cie = |_: &_, _: &_, offset| {
assert_eq!(offset, DebugFrameOffset(cie2_offset));
Ok(cie2.clone())
};
assert_eq!(partial.parse(get_cie), Ok(fde2));
}
otherwise => panic!("Unexpected result: {:#?}", otherwise),
}
assert_eq!(entries.next(), Ok(None));
}
#[test]
fn test_parse_cie_from_offset() {
let filler = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let instrs: Vec<_> = (0..5).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf64,
version: 4,
augmentation: None,
address_size: 4,
code_alignment_factor: 4,
data_alignment_factor: 8,
return_address_register: Register(12),
initial_instructions: EndianSlice::new(&instrs, LittleEndian),
};
let cie_location = Label::new();
let kind = debug_frame_le();
let section = Section::with_endian(kind.endian())
.append_bytes(&filler)
.mark(&cie_location)
.cie(kind, None, &mut cie)
.append_bytes(&filler);
section.start().set_const(0);
let cie_offset = DebugFrameOffset(cie_location.value().unwrap() as usize);
let contents = section.get_contents().unwrap();
let debug_frame = kind.section(&contents);
let bases = Default::default();
assert_eq!(debug_frame.cie_from_offset(&bases, cie_offset), Ok(cie));
}
fn parse_cfi_instruction<R: Reader + Default>(
input: &mut R,
address_size: u8,
) -> Result<CallFrameInstruction<R::Offset>> {
let section = input.clone();
let parameters = &PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size,
section: §ion,
};
CallFrameInstruction::parse(input, None, parameters, Vendor::Default)
}
#[test]
fn test_parse_cfi_instruction_advance_loc() {
let expected_rest = [1, 2, 3, 4];
let expected_delta = 42;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_advance_loc.0 | expected_delta)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::AdvanceLoc {
delta: u32::from(expected_delta),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_offset() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 3;
let expected_offset = 1997;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_offset.0 | expected_reg)
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Offset {
register: Register(expected_reg.into()),
factored_offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_restore() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 3;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_restore.0 | expected_reg)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Restore {
register: Register(expected_reg.into()),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_nop() {
let expected_rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_nop.0)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Nop)
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_set_loc() {
let expected_rest = [1, 2, 3, 4];
let expected_addr = 0xdead_beef;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_set_loc.0)
.L64(expected_addr)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::SetLoc {
address: expected_addr,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_set_loc_encoding() {
let text_base = 0xfeed_face;
let addr_offset = 0xbeef;
let expected_addr = text_base + addr_offset;
let expected_rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_set_loc.0)
.L64(addr_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
let parameters = &PointerEncodingParameters {
bases: &BaseAddresses::default().set_text(text_base).eh_frame,
func_base: None,
address_size: 8,
section: &EndianSlice::new(&[], LittleEndian),
};
assert_eq!(
CallFrameInstruction::parse(
input,
Some(constants::DW_EH_PE_textrel),
parameters,
Vendor::Default
),
Ok(CallFrameInstruction::SetLoc {
address: expected_addr,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_advance_loc1() {
let expected_rest = [1, 2, 3, 4];
let expected_delta = 8;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_advance_loc1.0)
.D8(expected_delta)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::AdvanceLoc {
delta: u32::from(expected_delta),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_advance_loc2() {
let expected_rest = [1, 2, 3, 4];
let expected_delta = 500;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_advance_loc2.0)
.L16(expected_delta)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::AdvanceLoc {
delta: u32::from(expected_delta),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_advance_loc4() {
let expected_rest = [1, 2, 3, 4];
let expected_delta = 1 << 20;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_advance_loc4.0)
.L32(expected_delta)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::AdvanceLoc {
delta: expected_delta,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_offset_extended() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 7;
let expected_offset = 33;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_offset_extended.0)
.uleb(expected_reg.into())
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Offset {
register: Register(expected_reg),
factored_offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_restore_extended() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 7;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_restore_extended.0)
.uleb(expected_reg.into())
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Restore {
register: Register(expected_reg),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_undefined() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 7;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_undefined.0)
.uleb(expected_reg.into())
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Undefined {
register: Register(expected_reg),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_same_value() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 7;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_same_value.0)
.uleb(expected_reg.into())
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::SameValue {
register: Register(expected_reg),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_register() {
let expected_rest = [1, 2, 3, 4];
let expected_dest_reg = 7;
let expected_src_reg = 8;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_register.0)
.uleb(expected_dest_reg.into())
.uleb(expected_src_reg.into())
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Register {
dest_register: Register(expected_dest_reg),
src_register: Register(expected_src_reg),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_remember_state() {
let expected_rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_remember_state.0)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::RememberState)
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_restore_state() {
let expected_rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_restore_state.0)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::RestoreState)
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_def_cfa() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 2;
let expected_offset = 0;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa.0)
.uleb(expected_reg.into())
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::DefCfa {
register: Register(expected_reg),
offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_def_cfa_register() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 2;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_register.0)
.uleb(expected_reg.into())
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::DefCfaRegister {
register: Register(expected_reg),
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_def_cfa_offset() {
let expected_rest = [1, 2, 3, 4];
let expected_offset = 23;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_offset.0)
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::DefCfaOffset {
offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_def_cfa_expression() {
let expected_rest = [1, 2, 3, 4];
let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
let length = Label::new();
let start = Label::new();
let end = Label::new();
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_expression.0)
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.append_bytes(&expected_rest);
length.set_const((&end - &start) as u64);
let expected_expression = UnwindExpression {
offset: (&start - §ion.start()) as usize,
length: (&end - &start) as usize,
};
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::DefCfaExpression {
expression: expected_expression,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_expression() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 99;
let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
let length = Label::new();
let start = Label::new();
let end = Label::new();
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_expression.0)
.uleb(expected_reg.into())
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.append_bytes(&expected_rest);
length.set_const((&end - &start) as u64);
let expected_expression = UnwindExpression {
offset: (&start - §ion.start()) as usize,
length: (&end - &start) as usize,
};
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::Expression {
register: Register(expected_reg),
expression: expected_expression,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_offset_extended_sf() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 7;
let expected_offset = -33;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_offset_extended_sf.0)
.uleb(expected_reg.into())
.sleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::OffsetExtendedSf {
register: Register(expected_reg),
factored_offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_def_cfa_sf() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 2;
let expected_offset = -9999;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_sf.0)
.uleb(expected_reg.into())
.sleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::DefCfaSf {
register: Register(expected_reg),
factored_offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_def_cfa_offset_sf() {
let expected_rest = [1, 2, 3, 4];
let expected_offset = -123;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_offset_sf.0)
.sleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::DefCfaOffsetSf {
factored_offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_val_offset() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 50;
let expected_offset = 23;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_val_offset.0)
.uleb(expected_reg.into())
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::ValOffset {
register: Register(expected_reg),
factored_offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_val_offset_sf() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 50;
let expected_offset = -23;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_val_offset_sf.0)
.uleb(expected_reg.into())
.sleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::ValOffsetSf {
register: Register(expected_reg),
factored_offset: expected_offset,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_val_expression() {
let expected_rest = [1, 2, 3, 4];
let expected_reg = 50;
let expected_expr = [2, 2, 1, 1, 5, 5];
let length = Label::new();
let start = Label::new();
let end = Label::new();
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_val_expression.0)
.uleb(expected_reg.into())
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.append_bytes(&expected_rest);
length.set_const((&end - &start) as u64);
let expected_expression = UnwindExpression {
offset: (&start - §ion.start()) as usize,
length: (&end - &start) as usize,
};
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Ok(CallFrameInstruction::ValExpression {
register: Register(expected_reg),
expression: expected_expression,
})
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_negate_ra_state() {
let expected_rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_AARCH64_negate_ra_state.0)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
let parameters = &PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 8,
section: &EndianSlice::default(),
};
assert_eq!(
CallFrameInstruction::parse(input, None, parameters, Vendor::AArch64),
Ok(CallFrameInstruction::NegateRaState)
);
assert_eq!(*input, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_cfi_instruction_unknown_instruction() {
let expected_rest = [1, 2, 3, 4];
let unknown_instr = constants::DwCfa(0b0011_1111);
let section = Section::with_endian(Endian::Little)
.D8(unknown_instr.0)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&contents, LittleEndian);
assert_eq!(
parse_cfi_instruction(input, 8),
Err(Error::UnknownCallFrameInstruction(unknown_instr))
);
}
#[test]
fn test_call_frame_instruction_iter_ok() {
let expected_reg = 50;
let expected_expr = [2, 2, 1, 1, 5, 5];
let expected_delta = 230;
let length = Label::new();
let start = Label::new();
let end = Label::new();
let section = Section::with_endian(Endian::Big)
.D8(constants::DW_CFA_val_expression.0)
.uleb(expected_reg.into())
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.D8(constants::DW_CFA_advance_loc1.0)
.D8(expected_delta);
length.set_const((&end - &start) as u64);
let expected_expression = UnwindExpression {
offset: (&start - §ion.start()) as usize,
length: (&end - &start) as usize,
};
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: &input,
};
let mut iter = CallFrameInstructionIter {
input,
address_encoding: None,
parameters,
vendor: Vendor::Default,
};
assert_eq!(
iter.next(),
Ok(Some(CallFrameInstruction::ValExpression {
register: Register(expected_reg),
expression: expected_expression,
}))
);
assert_eq!(
iter.next(),
Ok(Some(CallFrameInstruction::AdvanceLoc {
delta: u32::from(expected_delta),
}))
);
assert_eq!(iter.next(), Ok(None));
}
#[test]
fn test_call_frame_instruction_iter_err() {
let section = Section::with_endian(Endian::Big).D8(constants::DW_CFA_advance_loc1.0);
let contents = section.get_contents().unwrap();
let input = EndianSlice::new(&contents, BigEndian);
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 8,
section: &EndianSlice::default(),
};
let mut iter = CallFrameInstructionIter {
input,
address_encoding: None,
parameters,
vendor: Vendor::Default,
};
assert_eq!(
iter.next().map_eof(&contents),
Err(Error::UnexpectedEof(ReaderOffsetId(1)))
);
assert_eq!(iter.next(), Ok(None));
}
fn assert_eval<'a, I>(
mut initial_ctx: UnwindContext<usize>,
expected_ctx: UnwindContext<usize>,
cie: CommonInformationEntry<EndianSlice<'a, LittleEndian>>,
fde: Option<FrameDescriptionEntry<EndianSlice<'a, LittleEndian>>>,
instructions: I,
) where
I: AsRef<[(Result<bool>, CallFrameInstruction<usize>)]>,
{
{
let section = &DebugFrame::from(EndianSlice::default());
let bases = &BaseAddresses::default();
let mut table = match fde {
Some(fde) => UnwindTable::new_for_fde(section, bases, &mut initial_ctx, &fde),
None => UnwindTable::new_for_cie(section, bases, &mut initial_ctx, &cie),
};
for (expected_result, instruction) in instructions.as_ref() {
assert_eq!(*expected_result, table.evaluate(instruction.clone()));
}
}
assert_eq!(expected_ctx, initial_ctx);
}
fn make_test_cie<'a>() -> CommonInformationEntry<EndianSlice<'a, LittleEndian>> {
CommonInformationEntry {
offset: 0,
format: Format::Dwarf64,
length: 0,
return_address_register: Register(0),
version: 4,
address_size: mem::size_of::<usize>() as u8,
initial_instructions: EndianSlice::new(&[], LittleEndian),
augmentation: None,
data_alignment_factor: 2,
code_alignment_factor: 3,
}
}
#[test]
fn test_eval_set_loc() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.row_mut().end_address = 42;
let instructions = [(Ok(true), CallFrameInstruction::SetLoc { address: 42 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_set_loc_backwards() {
let cie = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.row_mut().start_address = 999;
let expected = ctx.clone();
let instructions = [(
Err(Error::InvalidAddressRange),
CallFrameInstruction::SetLoc { address: 42 },
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_advance_loc() {
let cie = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.row_mut().start_address = 3;
let mut expected = ctx.clone();
expected.row_mut().end_address = 3 + 2 * cie.code_alignment_factor;
let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 2 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_advance_loc_overflow_32() {
let mut cie = make_test_cie();
cie.address_size = 4;
let mut ctx = UnwindContext::new();
ctx.row_mut().start_address = u32::MAX.into();
let expected = ctx.clone();
let instructions = [(
Err(Error::AddressOverflow),
CallFrameInstruction::AdvanceLoc { delta: 42 },
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_advance_loc_overflow_64() {
let mut cie = make_test_cie();
cie.address_size = 8;
let mut ctx = UnwindContext::new();
ctx.row_mut().start_address = u64::MAX;
let expected = ctx.clone();
let instructions = [(
Err(Error::AddressOverflow),
CallFrameInstruction::AdvanceLoc { delta: 42 },
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
register: Register(42),
offset: 36,
});
let instructions = [(
Ok(false),
CallFrameInstruction::DefCfa {
register: Register(42),
offset: 36,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_sf() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
register: Register(42),
offset: 36 * cie.data_alignment_factor as i64,
});
let instructions = [(
Ok(false),
CallFrameInstruction::DefCfaSf {
register: Register(42),
factored_offset: 36,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_register() {
let cie = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::RegisterAndOffset {
register: Register(3),
offset: 8,
});
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
register: Register(42),
offset: 8,
});
let instructions = [(
Ok(false),
CallFrameInstruction::DefCfaRegister {
register: Register(42),
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_register_invalid_context() {
let cie = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::Expression(UnwindExpression {
offset: 0,
length: 0,
}));
let expected = ctx.clone();
let instructions = [(
Err(Error::CfiInstructionInInvalidContext),
CallFrameInstruction::DefCfaRegister {
register: Register(42),
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_offset() {
let cie = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::RegisterAndOffset {
register: Register(3),
offset: 8,
});
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
register: Register(3),
offset: 42,
});
let instructions = [(Ok(false), CallFrameInstruction::DefCfaOffset { offset: 42 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_offset_invalid_context() {
let cie = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::Expression(UnwindExpression {
offset: 10,
length: 11,
}));
let expected = ctx.clone();
let instructions = [(
Err(Error::CfiInstructionInInvalidContext),
CallFrameInstruction::DefCfaOffset { offset: 1993 },
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_expression() {
let expr = UnwindExpression {
offset: 10,
length: 11,
};
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::Expression(expr));
let instructions = [(
Ok(false),
CallFrameInstruction::DefCfaExpression { expression: expr },
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_undefined() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(Register(5), RegisterRule::Undefined)
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::Undefined {
register: Register(5),
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_same_value() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(Register(0), RegisterRule::SameValue)
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::SameValue {
register: Register(0),
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_offset() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(
Register(2),
RegisterRule::Offset(3 * cie.data_alignment_factor),
)
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::Offset {
register: Register(2),
factored_offset: 3,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_offset_extended_sf() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(
Register(4),
RegisterRule::Offset(-3 * cie.data_alignment_factor),
)
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::OffsetExtendedSf {
register: Register(4),
factored_offset: -3,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_val_offset() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(
Register(5),
RegisterRule::ValOffset(7 * cie.data_alignment_factor),
)
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::ValOffset {
register: Register(5),
factored_offset: 7,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_val_offset_sf() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(
Register(5),
RegisterRule::ValOffset(-7 * cie.data_alignment_factor),
)
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::ValOffsetSf {
register: Register(5),
factored_offset: -7,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_expression() {
let expr = UnwindExpression {
offset: 10,
length: 11,
};
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(Register(9), RegisterRule::Expression(expr))
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::Expression {
register: Register(9),
expression: expr,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_val_expression() {
let expr = UnwindExpression {
offset: 10,
length: 11,
};
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(Register(9), RegisterRule::ValExpression(expr))
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::ValExpression {
register: Register(9),
expression: expr,
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_restore() {
let cie = make_test_cie();
let fde = FrameDescriptionEntry {
offset: 0,
format: Format::Dwarf64,
length: 0,
address_range: 0,
augmentation: None,
initial_address: 0,
cie: cie.clone(),
instructions: EndianSlice::new(&[], LittleEndian),
};
let mut ctx = UnwindContext::new();
ctx.set_register_rule(Register(0), RegisterRule::Offset(1))
.unwrap();
ctx.save_initial_rules().unwrap();
let expected = ctx.clone();
ctx.set_register_rule(Register(0), RegisterRule::Offset(2))
.unwrap();
let instructions = [(
Ok(false),
CallFrameInstruction::Restore {
register: Register(0),
},
)];
assert_eval(ctx, expected, cie, Some(fde), instructions);
}
#[test]
fn test_eval_restore_havent_saved_initial_context() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let expected = ctx.clone();
let instructions = [(
Err(Error::CfiInstructionInInvalidContext),
CallFrameInstruction::Restore {
register: Register(0),
},
)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_remember_state() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.push_row().unwrap();
let instructions = [(Ok(false), CallFrameInstruction::RememberState)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_restore_state() {
let cie = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_start_address(1);
ctx.set_register_rule(Register(0), RegisterRule::SameValue)
.unwrap();
let mut expected = ctx.clone();
ctx.push_row().unwrap();
ctx.set_start_address(2);
ctx.set_register_rule(Register(0), RegisterRule::Offset(16))
.unwrap();
expected.set_start_address(2);
let instructions = [
(Ok(false), CallFrameInstruction::RestoreState),
(
Err(Error::PopWithEmptyStack),
CallFrameInstruction::RestoreState,
),
];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_negate_ra_state() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(1))
.unwrap();
let instructions = [(Ok(false), CallFrameInstruction::NegateRaState)];
assert_eval(ctx, expected, cie, None, instructions);
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(crate::AArch64::RA_SIGN_STATE, RegisterRule::Constant(0))
.unwrap();
let instructions = [
(Ok(false), CallFrameInstruction::NegateRaState),
(Ok(false), CallFrameInstruction::NegateRaState),
];
assert_eval(ctx, expected, cie, None, instructions);
let cie = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected
.set_register_rule(
crate::AArch64::RA_SIGN_STATE,
RegisterRule::Offset(cie.data_alignment_factor as i64),
)
.unwrap();
let instructions = [
(
Ok(false),
CallFrameInstruction::Offset {
register: crate::AArch64::RA_SIGN_STATE,
factored_offset: 1,
},
),
(
Err(Error::CfiInstructionInInvalidContext),
CallFrameInstruction::NegateRaState,
),
];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_nop() {
let cie = make_test_cie();
let ctx = UnwindContext::new();
let expected = ctx.clone();
let instructions = [(Ok(false), CallFrameInstruction::Nop)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_unwind_table_cie_no_rule() {
let initial_instructions = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_sf.0)
.uleb(4)
.sleb(-12)
.append_repeated(constants::DW_CFA_nop.0, 4);
let initial_instructions = initial_instructions.get_contents().unwrap();
let cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 8,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian),
};
let instructions = Section::with_endian(Endian::Little)
.append_repeated(constants::DW_CFA_nop.0, 8);
let instructions = instructions.get_contents().unwrap();
let fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0,
address_range: 100,
augmentation: None,
instructions: EndianSlice::new(&instructions, LittleEndian),
};
let section = &DebugFrame::from(EndianSlice::default());
let bases = &BaseAddresses::default();
let mut ctx = Box::new(UnwindContext::new());
let mut table = fde
.rows(section, bases, &mut ctx)
.expect("Should run initial program OK");
assert!(table.ctx.is_initialized);
let expected_initial_rule = (Register(0), RegisterRule::Undefined);
assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule));
{
let row = table.next_row().expect("Should evaluate first row OK");
let expected = UnwindTableRow {
start_address: 0,
end_address: 100,
saved_args_size: 0,
cfa: CfaRule::RegisterAndOffset {
register: Register(4),
offset: -12,
},
registers: [].iter().collect(),
};
assert_eq!(Some(&expected), row);
}
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)
.D8(constants::DW_CFA_def_cfa_sf.0)
.uleb(4)
.sleb(-12)
.D8(constants::DW_CFA_offset.0 | 3)
.uleb(4)
.append_repeated(constants::DW_CFA_nop.0, 4);
let initial_instructions = initial_instructions.get_contents().unwrap();
let cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 8,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian),
};
let instructions = Section::with_endian(Endian::Little)
.append_repeated(constants::DW_CFA_nop.0, 8);
let instructions = instructions.get_contents().unwrap();
let fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0,
address_range: 100,
augmentation: None,
instructions: EndianSlice::new(&instructions, LittleEndian),
};
let section = &DebugFrame::from(EndianSlice::default());
let bases = &BaseAddresses::default();
let mut ctx = Box::new(UnwindContext::new());
let mut table = fde
.rows(section, bases, &mut ctx)
.expect("Should run initial program OK");
assert!(table.ctx.is_initialized);
let expected_initial_rule = (Register(3), RegisterRule::Offset(4));
assert_eq!(table.ctx.initial_rule, Some(expected_initial_rule));
{
let row = table.next_row().expect("Should evaluate first row OK");
let expected = UnwindTableRow {
start_address: 0,
end_address: 100,
saved_args_size: 0,
cfa: CfaRule::RegisterAndOffset {
register: Register(4),
offset: -12,
},
registers: [(Register(3), RegisterRule::Offset(4))].iter().collect(),
};
assert_eq!(Some(&expected), row);
}
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)
.D8(constants::DW_CFA_remember_state.0)
.D8(constants::DW_CFA_offset.0 | 4)
.uleb(8)
.D8(constants::DW_CFA_offset.0);
let initial_instructions1 = initial_instructions1.get_contents().unwrap();
let cie1 = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 8,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&initial_instructions1, LittleEndian),
};
let initial_instructions2 = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_offset.0 | 3)
.uleb(4)
.append_repeated(constants::DW_CFA_nop.0, 4);
let initial_instructions2 = initial_instructions2.get_contents().unwrap();
let cie2 = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 8,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&initial_instructions2, LittleEndian),
};
let fde1 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie1.clone(),
initial_address: 0,
address_range: 100,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let fde2 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie2.clone(),
initial_address: 0,
address_range: 100,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let section = &DebugFrame::from(EndianSlice::default());
let bases = &BaseAddresses::default();
let mut ctx = Box::new(UnwindContext::new());
let table = fde1
.rows(section, bases, &mut ctx)
.map_eof(&initial_instructions1);
assert_eq!(table.err(), Some(Error::UnexpectedEof(ReaderOffsetId(4))));
assert!(!ctx.is_initialized);
assert_eq!(ctx.stack.len(), 2);
assert_eq!(ctx.initial_rule, None);
let _table = fde2
.rows(section, bases, &mut ctx)
.expect("Should run initial program OK");
assert!(ctx.is_initialized);
assert_eq!(ctx.stack.len(), 1);
let expected_initial_rule = (Register(3), RegisterRule::Offset(4));
assert_eq!(ctx.initial_rule, Some(expected_initial_rule));
}
#[test]
fn test_unwind_table_next_row() {
#[allow(clippy::identity_op)]
let initial_instructions = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_sf.0)
.uleb(4)
.sleb(-12)
.D8(constants::DW_CFA_offset.0 | 0)
.uleb(8)
.D8(constants::DW_CFA_offset.0 | 3)
.uleb(4)
.append_repeated(constants::DW_CFA_nop.0, 4);
let initial_instructions = initial_instructions.get_contents().unwrap();
let cie = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 8,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&initial_instructions, LittleEndian),
};
let instructions = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_advance_loc1.0)
.D8(1)
.D8(constants::DW_CFA_offset_extended_sf.0)
.uleb(0)
.sleb(-16)
.D8(constants::DW_CFA_advance_loc1.0)
.D8(32)
.D8(constants::DW_CFA_offset_extended_sf.0)
.uleb(3)
.sleb(-4)
.D8(constants::DW_CFA_advance_loc1.0)
.D8(64)
.D8(constants::DW_CFA_offset.0 | 5)
.uleb(4)
.append_repeated(constants::DW_CFA_nop.0, 8);
let instructions = instructions.get_contents().unwrap();
let fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0,
address_range: 100,
augmentation: None,
instructions: EndianSlice::new(&instructions, LittleEndian),
};
let section = &DebugFrame::from(EndianSlice::default());
let bases = &BaseAddresses::default();
let mut ctx = Box::new(UnwindContext::new());
let mut table = fde
.rows(section, bases, &mut ctx)
.expect("Should run initial program OK");
assert!(table.ctx.is_initialized);
assert!(table.ctx.initial_rule.is_none());
let expected_initial_rules: RegisterRuleMap<_> = [
(Register(0), RegisterRule::Offset(8)),
(Register(3), RegisterRule::Offset(4)),
]
.iter()
.collect();
assert_eq!(table.ctx.stack[0].registers, expected_initial_rules);
{
let row = table.next_row().expect("Should evaluate first row OK");
let expected = UnwindTableRow {
start_address: 0,
end_address: 1,
saved_args_size: 0,
cfa: CfaRule::RegisterAndOffset {
register: Register(4),
offset: -12,
},
registers: [
(Register(0), RegisterRule::Offset(8)),
(Register(3), RegisterRule::Offset(4)),
]
.iter()
.collect(),
};
assert_eq!(Some(&expected), row);
}
{
let row = table.next_row().expect("Should evaluate second row OK");
let expected = UnwindTableRow {
start_address: 1,
end_address: 33,
saved_args_size: 0,
cfa: CfaRule::RegisterAndOffset {
register: Register(4),
offset: -12,
},
registers: [
(Register(0), RegisterRule::Offset(-16)),
(Register(3), RegisterRule::Offset(4)),
]
.iter()
.collect(),
};
assert_eq!(Some(&expected), row);
}
{
let row = table.next_row().expect("Should evaluate third row OK");
let expected = UnwindTableRow {
start_address: 33,
end_address: 97,
saved_args_size: 0,
cfa: CfaRule::RegisterAndOffset {
register: Register(4),
offset: -12,
},
registers: [
(Register(0), RegisterRule::Offset(-16)),
(Register(3), RegisterRule::Offset(-4)),
]
.iter()
.collect(),
};
assert_eq!(Some(&expected), row);
}
{
let row = table.next_row().expect("Should evaluate fourth row OK");
let expected = UnwindTableRow {
start_address: 97,
end_address: 100,
saved_args_size: 0,
cfa: CfaRule::RegisterAndOffset {
register: Register(4),
offset: -12,
},
registers: [
(Register(0), RegisterRule::Offset(-16)),
(Register(3), RegisterRule::Offset(-4)),
(Register(5), RegisterRule::Offset(4)),
]
.iter()
.collect(),
};
assert_eq!(Some(&expected), row);
}
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)
.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)
.D8(constants::DW_CFA_advance_loc1.0)
.D8(100)
.D8(constants::DW_CFA_offset_extended_sf.0)
.uleb(0)
.sleb(-16);
let instrs3 = instrs3.get_contents().unwrap();
let instrs4: Vec<_> = (0..16).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie1 = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 8,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: Register(3),
initial_instructions: EndianSlice::new(&instrs1, BigEndian),
};
let mut cie2 = CommonInformationEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: Register(1),
initial_instructions: EndianSlice::new(&instrs2, BigEndian),
};
let cie1_location = Label::new();
let cie2_location = Label::new();
let kind = debug_frame_be();
let section = Section::with_endian(kind.endian())
.mark(&cie1_location)
.cie(kind, None, &mut cie1)
.mark(&cie2_location)
.cie(kind, None, &mut cie2);
let mut fde1 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie1.clone(),
initial_address: 0xfeed_beef,
address_range: 200,
augmentation: None,
instructions: EndianSlice::new(&instrs3, BigEndian),
};
let mut fde2 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie2.clone(),
initial_address: 0xfeed_face,
address_range: 9000,
augmentation: None,
instructions: EndianSlice::new(&instrs4, BigEndian),
};
let section =
section
.fde(kind, &cie1_location, &mut fde1)
.fde(kind, &cie2_location, &mut fde2);
section.start().set_const(0);
let contents = section.get_contents().unwrap();
let debug_frame = kind.section(&contents);
let bases = Default::default();
let mut ctx = Box::new(UnwindContext::new());
let result = debug_frame.unwind_info_for_address(
&bases,
&mut ctx,
0xfeed_beef + 150,
DebugFrame::cie_from_offset,
);
assert!(result.is_ok());
let unwind_info = result.unwrap();
assert_eq!(
*unwind_info,
UnwindTableRow {
start_address: fde1.initial_address() + 100,
end_address: fde1.end_address(),
saved_args_size: 0,
cfa: CfaRule::RegisterAndOffset {
register: Register(4),
offset: -12,
},
registers: [(Register(0), RegisterRule::Offset(-16))].iter().collect(),
}
);
}
#[test]
fn test_unwind_info_for_address_not_found() {
let debug_frame = DebugFrame::new(&[], NativeEndian);
let bases = Default::default();
let mut ctx = Box::new(UnwindContext::new());
let result = debug_frame.unwind_info_for_address(
&bases,
&mut ctx,
0xbadb_ad99,
DebugFrame::cie_from_offset,
);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress);
}
#[test]
fn test_eh_frame_hdr_unknown_version() {
let bases = BaseAddresses::default();
let buf = &[42];
let result = EhFrameHdr::new(buf, NativeEndian).parse(&bases, 8);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::UnknownVersion(42));
}
#[test]
fn test_eh_frame_hdr_omit_ehptr() {
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0xff)
.L8(0x03)
.L8(0x0b)
.L32(2)
.L32(10)
.L32(1)
.L32(20)
.L32(2)
.L32(0);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::CannotParseOmitPointerEncoding);
}
#[test]
fn test_eh_frame_hdr_omit_count() {
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0x0b)
.L8(0xff)
.L8(0x0b)
.L32(0x12345);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345));
assert!(result.table().is_none());
}
#[test]
fn test_eh_frame_hdr_omit_table() {
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0x0b)
.L8(0x03)
.L8(0xff)
.L32(0x12345)
.L32(2);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345));
assert!(result.table().is_none());
}
#[test]
fn test_eh_frame_hdr_varlen_table() {
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0x0b)
.L8(0x03)
.L8(0x01)
.L32(0x12345)
.L32(2);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345));
let table = result.table();
assert!(table.is_some());
let table = table.unwrap();
assert_eq!(
table.lookup(0, &bases),
Err(Error::VariableLengthSearchTable)
);
}
#[test]
fn test_eh_frame_hdr_indirect_length() {
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0x0b)
.L8(0x83)
.L8(0x0b)
.L32(0x12345)
.L32(2);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::UnsupportedPointerEncoding);
}
#[test]
fn test_eh_frame_hdr_indirect_ptrs() {
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0x8b)
.L8(0x03)
.L8(0x8b)
.L32(0x12345)
.L32(2)
.L32(10)
.L32(1)
.L32(20)
.L32(2);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.eh_frame_ptr(), Pointer::Indirect(0x12345));
let table = result.table();
assert!(table.is_some());
let table = table.unwrap();
assert_eq!(
table.lookup(0, &bases),
Err(Error::UnsupportedPointerEncoding)
);
}
#[test]
fn test_eh_frame_hdr_good() {
let section = Section::with_endian(Endian::Little)
.L8(1)
.L8(0x0b)
.L8(0x03)
.L8(0x0b)
.L32(0x12345)
.L32(2)
.L32(10)
.L32(1)
.L32(20)
.L32(2);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let result = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(result.is_ok());
let result = result.unwrap();
assert_eq!(result.eh_frame_ptr(), Pointer::Direct(0x12345));
let table = result.table();
assert!(table.is_some());
let table = table.unwrap();
assert_eq!(table.lookup(0, &bases), Ok(Pointer::Direct(1)));
assert_eq!(table.lookup(9, &bases), Ok(Pointer::Direct(1)));
assert_eq!(table.lookup(10, &bases), Ok(Pointer::Direct(1)));
assert_eq!(table.lookup(11, &bases), Ok(Pointer::Direct(1)));
assert_eq!(table.lookup(19, &bases), Ok(Pointer::Direct(1)));
assert_eq!(table.lookup(20, &bases), Ok(Pointer::Direct(2)));
assert_eq!(table.lookup(21, &bases), Ok(Pointer::Direct(2)));
assert_eq!(table.lookup(100_000, &bases), Ok(Pointer::Direct(2)));
}
#[test]
fn test_eh_frame_fde_for_address_good() {
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;
let start_of_cie = Label::new();
let end_of_cie = Label::new();
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.append_repeated(0, 16)
.mark(&start_of_cie)
.cie(kind, None, &mut cie)
.mark(&end_of_cie);
let mut fde1 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 9,
address_range: 4,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let mut fde2 = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 20,
address_range: 8,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let start_of_fde1 = Label::new();
let start_of_fde2 = Label::new();
let section = section
.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);
let section = Section::with_endian(kind.endian())
.L8(1)
.L8(0x0b)
.L8(0x03)
.L8(0x0b)
.L32(0x12345)
.L32(2)
.L32(10)
.L32(0x12345 + start_of_fde1.value().unwrap() as u32)
.L32(20)
.L32(0x12345 + start_of_fde2.value().unwrap() as u32);
let section = section.get_contents().unwrap();
let bases = BaseAddresses::default();
let eh_frame_hdr = EhFrameHdr::new(§ion, LittleEndian).parse(&bases, 8);
assert!(eh_frame_hdr.is_ok());
let eh_frame_hdr = eh_frame_hdr.unwrap();
let table = eh_frame_hdr.table();
assert!(table.is_some());
let table = table.unwrap();
let bases = Default::default();
let mut iter = table.iter(&bases);
assert_eq!(
iter.next(),
Ok(Some((
Pointer::Direct(10),
Pointer::Direct(0x12345 + start_of_fde1.value().unwrap())
)))
);
assert_eq!(
iter.next(),
Ok(Some((
Pointer::Direct(20),
Pointer::Direct(0x12345 + start_of_fde2.value().unwrap())
)))
);
assert_eq!(iter.next(), Ok(None));
assert_eq!(
table.iter(&bases).nth(0),
Ok(Some((
Pointer::Direct(10),
Pointer::Direct(0x12345 + start_of_fde1.value().unwrap())
)))
);
assert_eq!(
table.iter(&bases).nth(1),
Ok(Some((
Pointer::Direct(20),
Pointer::Direct(0x12345 + start_of_fde2.value().unwrap())
)))
);
assert_eq!(table.iter(&bases).nth(2), Ok(None));
let f = |_: &_, _: &_, o: EhFrameOffset| {
assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize));
Ok(cie.clone())
};
assert_eq!(
table.fde_for_address(&eh_frame, &bases, 9, f),
Ok(fde1.clone())
);
assert_eq!(
table.fde_for_address(&eh_frame, &bases, 10, f),
Ok(fde1.clone())
);
assert_eq!(table.fde_for_address(&eh_frame, &bases, 11, f), Ok(fde1));
assert_eq!(
table.fde_for_address(&eh_frame, &bases, 19, f),
Err(Error::NoUnwindInfoForAddress)
);
assert_eq!(
table.fde_for_address(&eh_frame, &bases, 20, f),
Ok(fde2.clone())
);
assert_eq!(table.fde_for_address(&eh_frame, &bases, 21, f), Ok(fde2));
assert_eq!(
table.fde_for_address(&eh_frame, &bases, 100_000, f),
Err(Error::NoUnwindInfoForAddress)
);
}
#[test]
fn test_eh_frame_stops_at_zero_length() {
let mut cie = make_test_cie();
let kind = eh_frame_le();
let section = Section::with_endian(Endian::Little)
.L32(0)
.cie(kind, None, &mut cie)
.L32(0);
let contents = section.get_contents().unwrap();
let eh_frame = kind.section(&contents);
let bases = Default::default();
let mut entries = eh_frame.entries(&bases);
assert_eq!(entries.next(), Ok(None));
assert_eq!(
eh_frame.cie_from_offset(&bases, EhFrameOffset(0)),
Err(Error::NoEntryAtGivenOffset)
);
}
#[test]
fn test_debug_frame_skips_zero_length() {
let mut cie = make_test_cie();
let kind = debug_frame_le();
let section = Section::with_endian(Endian::Little)
.L32(0)
.cie(kind, None, &mut cie)
.L32(0);
let contents = section.get_contents().unwrap();
let debug_frame = kind.section(&contents);
let bases = Default::default();
let mut entries = debug_frame.entries(&bases);
assert_eq!(entries.next(), Ok(Some(CieOrFde::Cie(cie))));
assert_eq!(entries.next(), Ok(None));
assert_eq!(
debug_frame.cie_from_offset(&bases, DebugFrameOffset(0)),
Err(Error::NoEntryAtGivenOffset)
);
}
fn resolve_cie_offset(buf: &[u8], cie_offset: usize) -> Result<usize> {
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf64,
cie: make_test_cie(),
initial_address: 0xfeed_beef,
address_range: 39,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.append_bytes(buf)
.fde(kind, cie_offset as u64, &mut fde)
.append_bytes(buf);
let section = section.get_contents().unwrap();
let eh_frame = kind.section(§ion);
let input = &mut EndianSlice::new(§ion[buf.len()..], LittleEndian);
let bases = Default::default();
match parse_cfi_entry(&bases, &eh_frame, input) {
Ok(Some(CieOrFde::Fde(partial))) => Ok(partial.cie_offset.0),
Err(e) => Err(e),
otherwise => panic!("Unexpected result: {:#?}", otherwise),
}
}
#[test]
fn test_eh_frame_resolve_cie_offset_ok() {
let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let cie_offset = 2;
assert_eq!(
resolve_cie_offset(&buf, buf.len() + 4 - cie_offset),
Ok(cie_offset)
);
}
#[test]
fn test_eh_frame_resolve_cie_offset_out_of_bounds() {
let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(
resolve_cie_offset(&buf, buf.len() + 4 + 2),
Err(Error::OffsetOutOfBounds)
);
}
#[test]
fn test_eh_frame_resolve_cie_offset_underflow() {
let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
assert_eq!(
resolve_cie_offset(&buf, usize::MAX),
Err(Error::OffsetOutOfBounds)
);
}
#[test]
fn test_eh_frame_fde_ok() {
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;
let start_of_cie = Label::new();
let end_of_cie = Label::new();
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.append_repeated(0, 16)
.mark(&start_of_cie)
.cie(kind, None, &mut cie)
.mark(&end_of_cie);
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0xfeed_beef,
address_range: 999,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let section = section
.fde(kind, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde);
section.start().set_const(0);
let section = section.get_contents().unwrap();
let eh_frame = kind.section(§ion);
let section = EndianSlice::new(§ion, LittleEndian);
let mut offset = None;
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());
}
#[test]
fn test_eh_frame_fde_out_of_bounds() {
let mut cie = make_test_cie();
cie.version = 1;
let end_of_cie = Label::new();
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf64,
cie: cie.clone(),
initial_address: 0xfeed_beef,
address_range: 999,
augmentation: None,
instructions: EndianSlice::new(&[], LittleEndian),
};
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.cie(kind, None, &mut cie)
.mark(&end_of_cie)
.fde(kind, 99_999_999_999_999, &mut fde);
section.start().set_const(0);
let section = section.get_contents().unwrap();
let eh_frame = kind.section(§ion);
let section = EndianSlice::new(§ion, LittleEndian);
let result = parse_fde(
eh_frame,
&mut section.range_from(end_of_cie.value().unwrap() as usize..),
UnwindSection::cie_from_offset,
);
assert_eq!(result, Err(Error::OffsetOutOfBounds));
}
#[test]
fn test_augmentation_parse_not_z_augmentation() {
let augmentation = &mut EndianSlice::new(b"wtf", NativeEndian);
let bases = Default::default();
let address_size = 8;
let section = EhFrame::new(&[], NativeEndian);
let input = &mut EndianSlice::new(&[], NativeEndian);
assert_eq!(
Augmentation::parse(augmentation, &bases, address_size, §ion, input),
Err(Error::UnknownAugmentation)
);
}
#[test]
fn test_augmentation_parse_just_signal_trampoline() {
let aug_str = &mut EndianSlice::new(b"S", LittleEndian);
let bases = Default::default();
let address_size = 8;
let section = EhFrame::new(&[], LittleEndian);
let input = &mut EndianSlice::new(&[], LittleEndian);
let augmentation = Augmentation {
is_signal_trampoline: true,
..Default::default()
};
assert_eq!(
Augmentation::parse(aug_str, &bases, address_size, §ion, input),
Ok(augmentation)
);
}
#[test]
fn test_augmentation_parse_unknown_part_of_z_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()
};
assert_eq!(
Augmentation::parse(aug_str, &bases, address_size, §ion, input),
Ok(augmentation)
);
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
#[allow(non_snake_case)]
fn test_augmentation_parse_P() {
let bases = Default::default();
let address_size = 8;
let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1];
let section = Section::with_endian(Endian::Little)
.uleb(9)
.D8(constants::DW_EH_PE_udata8.0)
.L64(0xf00d_f00d)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::new(§ion, LittleEndian);
let input = &mut section.section().clone();
let aug_str = &mut EndianSlice::new(b"zP", LittleEndian);
let augmentation = Augmentation {
personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0xf00d_f00d))),
..Default::default()
};
assert_eq!(
Augmentation::parse(aug_str, &bases, address_size, §ion, input),
Ok(augmentation)
);
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
#[allow(non_snake_case)]
fn test_augmentation_parse_R() {
let bases = Default::default();
let address_size = 8;
let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1];
let section = Section::with_endian(Endian::Little)
.uleb(1)
.D8(constants::DW_EH_PE_udata4.0)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::new(§ion, LittleEndian);
let input = &mut section.section().clone();
let aug_str = &mut EndianSlice::new(b"zR", LittleEndian);
let augmentation = Augmentation {
fde_address_encoding: Some(constants::DW_EH_PE_udata4),
..Default::default()
};
assert_eq!(
Augmentation::parse(aug_str, &bases, address_size, §ion, input),
Ok(augmentation)
);
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
#[allow(non_snake_case)]
fn test_augmentation_parse_S() {
let bases = Default::default();
let address_size = 8;
let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1];
let section = Section::with_endian(Endian::Little)
.uleb(0)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::new(§ion, LittleEndian);
let input = &mut section.section().clone();
let aug_str = &mut EndianSlice::new(b"zS", LittleEndian);
let augmentation = Augmentation {
is_signal_trampoline: true,
..Default::default()
};
assert_eq!(
Augmentation::parse(aug_str, &bases, address_size, §ion, input),
Ok(augmentation)
);
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
fn test_augmentation_parse_all() {
let bases = Default::default();
let address_size = 8;
let rest = [9, 8, 7, 6, 5, 4, 3, 2, 1];
let section = Section::with_endian(Endian::Little)
.uleb(1 + 9 + 1)
.D8(constants::DW_EH_PE_uleb128.0)
.D8(constants::DW_EH_PE_udata8.0)
.L64(0x1bad_f00d)
.D8(constants::DW_EH_PE_uleb128.0)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::new(§ion, LittleEndian);
let input = &mut section.section().clone();
let aug_str = &mut EndianSlice::new(b"zLPRS", LittleEndian);
let augmentation = Augmentation {
lsda: Some(constants::DW_EH_PE_uleb128),
personality: Some((constants::DW_EH_PE_udata8, Pointer::Direct(0x1bad_f00d))),
fde_address_encoding: Some(constants::DW_EH_PE_uleb128),
is_signal_trampoline: true,
};
assert_eq!(
Augmentation::parse(aug_str, &bases, address_size, §ion, input),
Ok(augmentation)
);
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
fn test_eh_frame_fde_no_augmentation() {
let instrs = [1, 2, 3, 4];
let cie_offset = 1;
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0xfeed_face,
address_range: 9000,
augmentation: None,
instructions: EndianSlice::new(&instrs, LittleEndian),
};
let rest = [1, 2, 3, 4];
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.fde(kind, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = kind.section(§ion);
let input = &mut section.section().clone();
let result = parse_fde(section, input, |_, _, _| Ok(cie.clone()));
assert_eq!(result, Ok(fde));
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
fn test_eh_frame_fde_empty_augmentation() {
let instrs = [1, 2, 3, 4];
let cie_offset = 1;
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;
cie.augmentation = Some(Augmentation::default());
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0xfeed_face,
address_range: 9000,
augmentation: Some(AugmentationData::default()),
instructions: EndianSlice::new(&instrs, LittleEndian),
};
let rest = [1, 2, 3, 4];
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.fde(kind, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = kind.section(§ion);
let input = &mut section.section().clone();
let result = parse_fde(section, input, |_, _, _| Ok(cie.clone()));
assert_eq!(result, Ok(fde));
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
fn test_eh_frame_fde_lsda_augmentation() {
let instrs = [1, 2, 3, 4];
let cie_offset = 1;
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;
cie.augmentation = Some(Augmentation::default());
cie.augmentation.as_mut().unwrap().lsda = Some(constants::DW_EH_PE_absptr);
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0xfeed_face,
address_range: 9000,
augmentation: Some(AugmentationData {
lsda: Some(Pointer::Direct(0x1122_3344)),
}),
instructions: EndianSlice::new(&instrs, LittleEndian),
};
let rest = [1, 2, 3, 4];
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.fde(kind, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = kind.section(§ion);
let input = &mut section.section().clone();
let result = parse_fde(section, input, |_, _, _| Ok(cie.clone()));
assert_eq!(result, Ok(fde));
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
fn test_eh_frame_fde_lsda_function_relative() {
let instrs = [1, 2, 3, 4];
let cie_offset = 1;
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;
cie.augmentation = Some(Augmentation::default());
cie.augmentation.as_mut().unwrap().lsda =
Some(constants::DW_EH_PE_funcrel | constants::DW_EH_PE_absptr);
let mut fde = FrameDescriptionEntry {
offset: 0,
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_address: 0xfeed_face,
address_range: 9000,
augmentation: Some(AugmentationData {
lsda: Some(Pointer::Direct(0xbeef)),
}),
instructions: EndianSlice::new(&instrs, LittleEndian),
};
let rest = [1, 2, 3, 4];
let kind = eh_frame_le();
let section = Section::with_endian(kind.endian())
.append_repeated(10, 10)
.fde(kind, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = kind.section(§ion);
let input = &mut section.section().range_from(10..);
fde.augmentation.as_mut().unwrap().lsda = Some(Pointer::Direct(0xfeed_face + 0xbeef));
let result = parse_fde(section, input, |_, _, _| Ok(cie.clone()));
assert_eq!(result, Ok(fde));
assert_eq!(*input, EndianSlice::new(&rest, LittleEndian));
}
#[test]
fn test_eh_frame_cie_personality_function_relative_bad_context() {
let instrs = [1, 2, 3, 4];
let length = Label::new();
let start = Label::new();
let end = Label::new();
let aug_len = Label::new();
let aug_start = Label::new();
let aug_end = Label::new();
let section = Section::with_endian(Endian::Little)
.L32(&length)
.mark(&start)
.L32(0)
.D8(1)
.append_bytes(b"zP\0")
.uleb(1)
.sleb(1)
.uleb(1)
.D8(&aug_len)
.mark(&aug_start)
.D8(constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_uleb128.0)
.uleb(1)
.mark(&aug_end)
.append_bytes(&instrs)
.mark(&end);
length.set_const((&end - &start) as u64);
aug_len.set_const((&aug_end - &aug_start) as u64);
let section = section.get_contents().unwrap();
let section = EhFrame::new(§ion, LittleEndian);
let bases = BaseAddresses::default();
let mut iter = section.entries(&bases);
assert_eq!(iter.next(), Err(Error::FuncRelativePointerInBadContext));
}
#[test]
fn register_rule_map_eq() {
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);
let map3: RegisterRuleMap<usize> = [
(Register(0), RegisterRule::SameValue),
(Register(2), RegisterRule::Offset(1)),
]
.iter()
.collect();
let map4: RegisterRuleMap<usize> = [
(Register(3), RegisterRule::Offset(1)),
(Register(0), RegisterRule::SameValue),
]
.iter()
.collect();
assert!(map3 != map4);
assert!(map4 != map3);
let mut map5 = RegisterRuleMap::<usize>::default();
map5.set(Register(0), RegisterRule::SameValue).unwrap();
map5.set(Register(0), RegisterRule::Undefined).unwrap();
let map6 = RegisterRuleMap::<usize>::default();
assert_eq!(map5, map6);
assert_eq!(map6, map5);
}
#[test]
fn iter_register_rules() {
let row = UnwindTableRow::<usize> {
registers: [
(Register(0), RegisterRule::SameValue),
(Register(1), RegisterRule::Offset(1)),
(Register(2), RegisterRule::ValOffset(2)),
]
.iter()
.collect(),
..Default::default()
};
let mut found0 = false;
let mut found1 = false;
let mut found2 = false;
for &(register, ref rule) in row.registers() {
match register.0 {
0 => {
assert!(!found0);
found0 = true;
assert_eq!(*rule, RegisterRule::SameValue);
}
1 => {
assert!(!found1);
found1 = true;
assert_eq!(*rule, RegisterRule::Offset(1));
}
2 => {
assert!(!found2);
found2 = true;
assert_eq!(*rule, RegisterRule::ValOffset(2));
}
x => panic!("Unexpected register rule: ({}, {:?})", x, rule),
}
}
assert!(found0);
assert!(found1);
assert!(found2);
}
#[test]
#[cfg(target_pointer_width = "64")]
fn size_of_unwind_ctx() {
use core::mem;
let size = mem::size_of::<UnwindContext<usize>>();
let max_size = 30968;
if size > max_size {
assert_eq!(size, max_size);
}
}
#[test]
#[cfg(target_pointer_width = "64")]
fn size_of_register_rule_map() {
use core::mem;
let size = mem::size_of::<RegisterRuleMap<usize>>();
let max_size = 6152;
if size > max_size {
assert_eq!(size, max_size);
}
}
#[test]
fn test_parse_pointer_encoding_ok() {
use crate::endianity::NativeEndian;
let expected = constants::DW_EH_PE_uleb128 | constants::DW_EH_PE_pcrel;
let input = [expected.0, 1, 2, 3, 4];
let input = &mut EndianSlice::new(&input, NativeEndian);
assert_eq!(parse_pointer_encoding(input), Ok(expected));
assert_eq!(*input, EndianSlice::new(&[1, 2, 3, 4], NativeEndian));
}
#[test]
fn test_parse_pointer_encoding_bad_encoding() {
use crate::endianity::NativeEndian;
let expected =
constants::DwEhPe((constants::DW_EH_PE_sdata8.0 + 1) | constants::DW_EH_PE_pcrel.0);
let input = [expected.0, 1, 2, 3, 4];
let input = &mut EndianSlice::new(&input, NativeEndian);
assert_eq!(
Err(Error::UnknownPointerEncoding(expected)),
parse_pointer_encoding(input)
);
}
#[test]
fn test_parse_encoded_pointer_absptr() {
let encoding = constants::DW_EH_PE_absptr;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.L32(0xf00d_f00d)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0xf00d_f00d))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_pcrel() {
let encoding = constants::DW_EH_PE_pcrel;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.append_repeated(0, 0x10)
.L32(0x1)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input.range_from(0x10..);
let parameters = PointerEncodingParameters {
bases: &BaseAddresses::default().set_eh_frame(0x100).eh_frame,
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x111))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_pcrel_undefined() {
let encoding = constants::DW_EH_PE_pcrel;
let input = Section::with_endian(Endian::Little).L32(0x1);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Err(Error::PcRelativePointerButSectionBaseIsUndefined)
);
}
#[test]
fn test_parse_encoded_pointer_textrel() {
let encoding = constants::DW_EH_PE_textrel;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.L32(0x1)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &BaseAddresses::default().set_text(0x10).eh_frame,
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x11))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_textrel_undefined() {
let encoding = constants::DW_EH_PE_textrel;
let input = Section::with_endian(Endian::Little).L32(0x1);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Err(Error::TextRelativePointerButTextBaseIsUndefined)
);
}
#[test]
fn test_parse_encoded_pointer_datarel() {
let encoding = constants::DW_EH_PE_datarel;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.L32(0x1)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &BaseAddresses::default().set_got(0x10).eh_frame,
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x11))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_datarel_undefined() {
let encoding = constants::DW_EH_PE_datarel;
let input = Section::with_endian(Endian::Little).L32(0x1);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Err(Error::DataRelativePointerButDataBaseIsUndefined)
);
}
#[test]
fn test_parse_encoded_pointer_funcrel() {
let encoding = constants::DW_EH_PE_funcrel;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.L32(0x1)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: Some(0x10),
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x11))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_funcrel_undefined() {
let encoding = constants::DW_EH_PE_funcrel;
let input = Section::with_endian(Endian::Little).L32(0x1);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Err(Error::FuncRelativePointerInBadContext)
);
}
#[test]
fn test_parse_encoded_pointer_uleb128() {
let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_uleb128;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.uleb(0x12_3456)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x12_3456))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_udata2() {
let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata2;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.L16(0x1234)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x1234))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_udata4() {
let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata4;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.L32(0x1234_5678)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x1234_5678))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_udata8() {
let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_udata8;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.L64(0x1234_5678_1234_5678)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 8,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x1234_5678_1234_5678))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_sleb128() {
let encoding = constants::DW_EH_PE_textrel | constants::DW_EH_PE_sleb128;
let expected_rest = [1, 2, 3, 4];
let input = Section::with_endian(Endian::Little)
.sleb(-0x1111)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &BaseAddresses::default().set_text(0x1111_1111).eh_frame,
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(0x1111_0000))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_sdata2() {
let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata2;
let expected_rest = [1, 2, 3, 4];
let expected = 0x111_i16;
let input = Section::with_endian(Endian::Little)
.L16(expected as u16)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(expected as u64))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_sdata4() {
let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata4;
let expected_rest = [1, 2, 3, 4];
let expected = 0x111_1111_i32;
let input = Section::with_endian(Endian::Little)
.L32(expected as u32)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(expected as u64))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_sdata8() {
let encoding = constants::DW_EH_PE_absptr | constants::DW_EH_PE_sdata8;
let expected_rest = [1, 2, 3, 4];
let expected = -0x11_1111_1222_2222_i64;
let input = Section::with_endian(Endian::Little)
.L64(expected as u64)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 8,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Direct(expected as u64))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[test]
fn test_parse_encoded_pointer_omit() {
let encoding = constants::DW_EH_PE_omit;
let input = Section::with_endian(Endian::Little).L32(0x1);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Err(Error::CannotParseOmitPointerEncoding)
);
assert_eq!(rest, input);
}
#[test]
fn test_parse_encoded_pointer_bad_encoding() {
let encoding = constants::DwEhPe(constants::DW_EH_PE_sdata8.0 + 1);
let input = Section::with_endian(Endian::Little).L32(0x1);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Err(Error::UnknownPointerEncoding(encoding))
);
}
#[test]
fn test_parse_encoded_pointer_aligned() {
let encoding = constants::DW_EH_PE_aligned;
let input = Section::with_endian(Endian::Little).L32(0x1);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Err(Error::UnsupportedPointerEncoding)
);
}
#[test]
fn test_parse_encoded_pointer_indirect() {
let expected_rest = [1, 2, 3, 4];
let encoding = constants::DW_EH_PE_indirect;
let input = Section::with_endian(Endian::Little)
.L32(0x1234_5678)
.append_bytes(&expected_rest);
let input = input.get_contents().unwrap();
let input = EndianSlice::new(&input, LittleEndian);
let mut rest = input;
let parameters = PointerEncodingParameters {
bases: &SectionBaseAddresses::default(),
func_base: None,
address_size: 4,
section: &input,
};
assert_eq!(
parse_encoded_pointer(encoding, ¶meters, &mut rest),
Ok(Pointer::Indirect(0x1234_5678))
);
assert_eq!(rest, EndianSlice::new(&expected_rest, LittleEndian));
}
#[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);
}
let mut ctx: UnwindContext<usize> = UnwindContext::new();
{
let data1 = vec![];
unwind_one(&mut ctx, &data1);
}
{
let data2 = vec![];
unwind_one(&mut ctx, &data2);
}
}
}