use arrayvec::ArrayVec;
use constants;
use endianity::{Endianity, EndianBuf};
use fallible_iterator::FallibleIterator;
use parser::{Error, Format, Pointer, Result, parse_address, parse_encoded_pointer,
parse_initial_length, parse_length_uleb_value, parse_null_terminated_string,
parse_pointer_encoding, parse_signed_lebe, parse_u8, parse_u8e, parse_u16, parse_u32,
parse_u32_as_u64, parse_u64, parse_unsigned_lebe, parse_unsigned_leb_as_u8e,
u64_to_offset};
use std::cell::RefCell;
use std::fmt::Debug;
use std::iter::FromIterator;
use std::marker::PhantomData;
use std::mem;
use std::str;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DebugFrameOffset(pub usize);
impl Into<usize> for DebugFrameOffset {
fn into(self) -> usize {
self.0
}
}
impl From<usize> for DebugFrameOffset {
fn from(o: usize) -> Self {
DebugFrameOffset(o)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct EhFrameOffset(pub usize);
impl Into<usize> for EhFrameOffset {
fn into(self) -> usize {
self.0
}
}
impl From<usize> for EhFrameOffset {
fn from(o: usize) -> Self {
EhFrameOffset(o)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DebugFrame<'input, Endian>(EndianBuf<'input, Endian>) where Endian: Endianity;
impl<'input, Endian> DebugFrame<'input, Endian>
where Endian: Endianity
{
pub fn new(section: &'input [u8]) -> DebugFrame<'input, Endian> {
DebugFrame(EndianBuf::new(section))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct EhFrame<'input, Endian>(EndianBuf<'input, Endian>) where Endian: Endianity;
impl<'input, Endian> EhFrame<'input, Endian>
where Endian: Endianity
{
pub fn new(section: &'input [u8]) -> EhFrame<'input, Endian> {
EhFrame(EndianBuf::new(section))
}
}
#[doc(hidden)]
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum CieOffsetEncoding {
U32,
U64,
}
#[doc(hidden)]
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ReturnAddressRegisterEncoding {
U8,
Uleb,
}
#[doc(hidden)]
pub trait _UnwindSectionPrivate<'input, Endian>
where Endian: Endianity
{
fn section(&self) -> EndianBuf<'input, Endian>;
fn length_value_is_end_of_entries(length: u64) -> bool;
fn is_cie(format: Format, id: u64) -> bool;
fn cie_offset_encoding(format: Format) -> CieOffsetEncoding;
fn resolve_cie_offset(&self,
input_before_offset: EndianBuf<'input, Endian>,
offset: usize)
-> Option<usize>;
fn compatible_version(version: u8) -> bool;
fn has_address_and_segment_sizes(version: u8) -> bool;
fn return_address_register_encoding(version: u8) -> ReturnAddressRegisterEncoding;
}
pub trait UnwindSection<'input, Endian>
: Copy + Debug + Eq + _UnwindSectionPrivate<'input, Endian>
where Endian: Endianity
{
type Offset: Copy + Debug + Eq + Into<usize> + From<usize>;
fn offset(offset: usize) -> Self::Offset {
offset.into()
}
fn entries<'bases>(&self,
bases: &'bases BaseAddresses)
-> CfiEntriesIter<'bases, 'input, Endian, Self> {
CfiEntriesIter {
section: *self,
bases: bases,
input: self.section(),
phantom: PhantomData,
}
}
fn cie_from_offset<'bases>(&self,
bases: &'bases BaseAddresses,
offset: Self::Offset)
-> Result<CommonInformationEntry<'input, Endian, Self>> {
let offset = offset.into();
if self.section().len() < offset {
return Err(Error::UnexpectedEof);
}
let input = self.section().range_from(offset..);
if let Some((_, entry)) = try!(CommonInformationEntry::parse(bases, *self, input)) {
Ok(entry)
} else {
Err(Error::NoEntryAtGivenOffset)
}
}
fn unwind_info_for_address<'bases>
(&self,
bases: &'bases BaseAddresses,
ctx: UninitializedUnwindContext<'input, Endian, Self>,
address: u64)
-> Result<(UnwindTableRow<'input, Endian>, UninitializedUnwindContext<'input, Endian, Self>)> {
let mut target_fde = None;
let mut entries = self.entries(bases);
while let Some(entry) = try!(entries.next()) {
match entry {
CieOrFde::Cie(_) => continue,
CieOrFde::Fde(partial) => {
let fde = try!(partial.parse(|offset| self.cie_from_offset(bases, offset)));
if fde.contains(address) {
target_fde = Some(fde);
break;
}
}
}
}
if let Some(fde) = target_fde {
let mut result_row = None;
let mut ctx = try!(ctx.initialize(fde.cie()));
{
let mut table = UnwindTable::new(&mut ctx, &fde);
while let Some(row) = try!(table.next_row()) {
if row.contains(address) {
result_row = Some(row.clone());
break;
}
}
}
if let Some(row) = result_row {
return Ok((row, ctx.reset()));
}
}
Err(Error::NoUnwindInfoForAddress)
}
}
impl<'input, Endian> _UnwindSectionPrivate<'input, Endian> for DebugFrame<'input, Endian>
where Endian: Endianity
{
fn section(&self) -> EndianBuf<'input, Endian> {
self.0
}
fn length_value_is_end_of_entries(_: u64) -> bool {
false
}
fn is_cie(format: Format, id: u64) -> bool {
match format {
Format::Dwarf32 => id == 0xffffffff,
Format::Dwarf64 => id == 0xffffffffffffffff,
}
}
fn cie_offset_encoding(format: Format) -> CieOffsetEncoding {
match format {
Format::Dwarf32 => CieOffsetEncoding::U32,
Format::Dwarf64 => CieOffsetEncoding::U64,
}
}
fn resolve_cie_offset(&self, _: EndianBuf<'input, Endian>, offset: usize) -> Option<usize> {
Some(offset)
}
fn compatible_version(version: u8) -> bool {
match version {
1 | 3 | 4 => true,
_ => false,
}
}
fn has_address_and_segment_sizes(version: u8) -> bool {
version == 4
}
fn return_address_register_encoding(version: u8) -> ReturnAddressRegisterEncoding {
if version == 1 {
ReturnAddressRegisterEncoding::U8
} else {
ReturnAddressRegisterEncoding::Uleb
}
}
}
impl<'input, Endian> UnwindSection<'input, Endian> for DebugFrame<'input, Endian>
where Endian: Endianity
{
type Offset = DebugFrameOffset;
}
impl<'input, Endian> _UnwindSectionPrivate<'input, Endian> for EhFrame<'input, Endian>
where Endian: Endianity
{
fn section(&self) -> EndianBuf<'input, Endian> {
self.0
}
fn length_value_is_end_of_entries(length: u64) -> bool {
length == 0
}
fn is_cie(_: Format, id: u64) -> bool {
id == 0
}
fn cie_offset_encoding(_format: Format) -> CieOffsetEncoding {
CieOffsetEncoding::U32
}
fn resolve_cie_offset(&self,
input_before_offset: EndianBuf<'input, Endian>,
input_relative_offset: usize)
-> Option<usize> {
let section = self.section();
debug_assert!(section.len() > 0);
debug_assert!(input_before_offset.len() > 0);
debug_assert!(section.as_ptr() as usize <= input_before_offset.as_ptr() as usize);
debug_assert!(input_before_offset.as_ptr() as usize + input_before_offset.len() <=
section.as_ptr() as usize + section.len());
let offset_ptr = (input_before_offset.as_ptr() as usize)
.saturating_sub(input_relative_offset);
if section.as_ptr() as usize <= offset_ptr &&
offset_ptr < section.as_ptr() as usize + section.len() {
let section_relative_offset = offset_ptr - section.as_ptr() as usize;
Some(section_relative_offset)
} else {
None
}
}
fn compatible_version(version: u8) -> bool {
version == 1
}
fn has_address_and_segment_sizes(_version: u8) -> bool {
false
}
fn return_address_register_encoding(_version: u8) -> ReturnAddressRegisterEncoding {
ReturnAddressRegisterEncoding::Uleb
}
}
impl<'input, Endian> UnwindSection<'input, Endian> for EhFrame<'input, Endian>
where Endian: Endianity
{
type Offset = EhFrameOffset;
}
#[derive(Clone, Default, Debug, PartialEq, Eq)]
pub struct BaseAddresses {
pub cfi: Option<u64>,
pub text: Option<u64>,
pub data: Option<u64>,
#[doc(hidden)]
#[allow(missing_docs)]
pub func: RefCell<Option<u64>>,
}
impl BaseAddresses {
pub fn set_cfi(mut self, addr: u64) -> Self {
self.cfi = Some(addr);
self
}
pub fn set_text(mut self, addr: u64) -> Self {
self.text = Some(addr);
self
}
pub fn set_data(mut self, addr: u64) -> Self {
self.data = Some(addr);
self
}
}
#[derive(Clone, Debug)]
pub struct CfiEntriesIter<'bases, 'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
section: Section,
bases: &'bases BaseAddresses,
input: EndianBuf<'input, Endian>,
phantom: PhantomData<Section>,
}
impl<'bases, 'input, Endian, Section> CfiEntriesIter<'bases, 'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
pub fn next(&mut self) -> Result<Option<CieOrFde<'bases, 'input, Endian, Section>>> {
if self.input.len() == 0 {
return Ok(None);
}
self.bases.func.borrow_mut().take();
match parse_cfi_entry(&self.bases, self.section, self.input) {
Err(e) => {
self.input = EndianBuf::new(&[]);
Err(e)
}
Ok(None) => {
self.input = EndianBuf::new(&[]);
Ok(None)
}
Ok(Some((rest, entry))) => {
self.input = rest;
Ok(Some(entry))
}
}
}
}
impl<'bases, 'input, Endian, Section> FallibleIterator
for CfiEntriesIter<'bases, 'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
type Item = CieOrFde<'bases, 'input, Endian, Section>;
type Error = Error;
fn next(&mut self) -> ::std::result::Result<Option<Self::Item>, Self::Error> {
CfiEntriesIter::next(self)
}
}
fn parse_cfi_entry_common<'input, Endian, Section>(input: EndianBuf<'input, Endian>)
-> Result<Option<(EndianBuf<'input, Endian>,
(u64,
Format,
EndianBuf<'input, Endian>,
u64,
EndianBuf<'input, Endian>))>>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
let (rest, (length, format)) = try!(parse_initial_length(input));
if Section::length_value_is_end_of_entries(length) {
return Ok(None);
}
if length as usize > rest.len() {
return Err(Error::BadLength);
}
let rest_rest = rest.range_from(length as usize..);
let cie_offset_input = rest.range_to(..length as usize);
let (rest, cie_id_or_offset) = match Section::cie_offset_encoding(format) {
CieOffsetEncoding::U32 => try!(parse_u32_as_u64(cie_offset_input)),
CieOffsetEncoding::U64 => try!(parse_u64(cie_offset_input)),
};
Ok(Some((rest_rest, (length, format, cie_offset_input, cie_id_or_offset, rest))))
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CieOrFde<'bases, 'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
Cie(CommonInformationEntry<'input, Endian, Section>),
Fde(PartialFrameDescriptionEntry<'bases, 'input, Endian, Section>),
}
fn parse_cfi_entry<'bases, 'input, Endian, Section>
(bases: &'bases BaseAddresses,
section: Section,
input: EndianBuf<'input, Endian>)
-> Result<Option<(EndianBuf<'input, Endian>, CieOrFde<'bases, 'input, Endian, Section>)>>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
let (rest_rest, (length, format, cie_offset_input, cie_id_or_offset, rest)) =
match try!(parse_cfi_entry_common::<Endian, Section>(input)) {
None => return Ok(None),
Some(common) => common,
};
if Section::is_cie(format, cie_id_or_offset) {
let cie = try!(CommonInformationEntry::parse_rest(length, format, bases, section, rest));
Ok(Some((rest_rest, CieOrFde::Cie(cie))))
} else {
let cie_offset = try!(u64_to_offset(cie_id_or_offset));
let cie_offset = match section.resolve_cie_offset(cie_offset_input, cie_offset) {
None => return Err(Error::OffsetOutOfBounds),
Some(offset) => offset,
};
let fde = PartialFrameDescriptionEntry {
length: length,
format: format,
cie_offset: Section::offset(cie_offset),
rest: rest,
section: section,
bases: bases,
};
Ok(Some((rest_rest, CieOrFde::Fde(fde))))
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Augmentation {
lsda: Option<constants::DwEhPe>,
personality: Option<Pointer>,
fde_address_encoding: Option<constants::DwEhPe>,
is_signal_trampoline: bool,
}
impl Default for Augmentation {
fn default() -> Augmentation {
Augmentation {
lsda: None,
personality: None,
fde_address_encoding: None,
is_signal_trampoline: false,
}
}
}
impl Augmentation {
fn parse<'aug, 'bases, 'input, Endian, Section>
(augmentation_str: &'aug str,
bases: &'bases BaseAddresses,
address_size: u8,
section: Section,
input: EndianBuf<'input, Endian>)
-> Result<(EndianBuf<'input, Endian>, Augmentation)>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
debug_assert!(augmentation_str.len() > 0,
"Augmentation::parse should only be called if we have an augmentation");
let mut chars = augmentation_str.chars();
let first = chars.next()
.expect("Is valid UTF-8 and length > 0, so must have at least one char.");
if first != 'z' {
return Err(Error::UnknownAugmentation);
}
let mut augmentation = Augmentation::default();
let (rest, augmentation_length) = try!(parse_unsigned_lebe(input));
let (mut rest, rest_rest) = try!(rest.try_split_at(augmentation_length as usize));
for ch in chars {
match ch {
'L' => {
let (rest_, encoding) = try!(parse_pointer_encoding(rest));
augmentation.lsda = Some(encoding);
rest = rest_;
}
'P' => {
let (rest_, encoding) = try!(parse_pointer_encoding(rest));
let (rest_, personality) = try!(parse_encoded_pointer(encoding,
bases,
address_size,
section.section(),
rest_));
augmentation.personality = Some(personality);
rest = rest_;
}
'R' => {
let (rest_, encoding) = try!(parse_pointer_encoding(rest));
augmentation.fde_address_encoding = Some(encoding);
rest = rest_;
}
'S' => augmentation.is_signal_trampoline = true,
_ => return Err(Error::UnknownAugmentation),
}
}
Ok((rest_rest, augmentation))
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct AugmentationData {
lsda: Option<Pointer>,
}
impl AugmentationData {
fn parse<'input, Endian, Section>(augmentation: &Augmentation,
bases: &BaseAddresses,
address_size: u8,
section: Section,
input: EndianBuf<'input, Endian>)
-> Result<(EndianBuf<'input, Endian>, AugmentationData)>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
let (rest, aug_data_len) = try!(parse_unsigned_lebe(input));
let (rest, rest_rest) = try!(rest.try_split_at(aug_data_len as usize));
let mut augmentation_data = AugmentationData::default();
if let Some(encoding) = augmentation.lsda {
let (_, lsda) =
try!(parse_encoded_pointer(encoding, bases, address_size, section.section(), rest));
augmentation_data.lsda = Some(lsda);
}
Ok((rest_rest, augmentation_data))
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct CommonInformationEntry<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
length: u64,
format: Format,
version: u8,
augmentation: Option<Augmentation>,
address_size: u8,
segment_size: u8,
code_alignment_factor: u64,
data_alignment_factor: i64,
return_address_register: u64,
initial_instructions: EndianBuf<'input, Endian>,
phantom: PhantomData<Section>,
}
impl<'input, Endian, Section> CommonInformationEntry<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
fn parse<'bases>
(bases: &'bases BaseAddresses,
section: Section,
input: EndianBuf<'input, Endian>)
-> Result<Option<(EndianBuf<'input, Endian>, CommonInformationEntry<'input, Endian, Section>)>> {
let (rest_rest, (length, format, _, cie_id, rest)) =
match try!(parse_cfi_entry_common::<Endian, Section>(input)) {
None => return Ok(None),
Some(common) => common,
};
if !Section::is_cie(format, cie_id) {
return Err(Error::NotCieId);
}
let entry = try!(Self::parse_rest(length, format, bases, section, rest));
Ok(Some((rest_rest, entry)))
}
fn parse_rest<'bases>(length: u64,
format: Format,
bases: &'bases BaseAddresses,
section: Section,
rest: EndianBuf<'input, Endian>)
-> Result<CommonInformationEntry<'input, Endian, Section>> {
let (rest, version) = try!(parse_u8(rest.into()));
if !Section::compatible_version(version) {
return Err(Error::UnknownVersion);
}
let (rest, augmentation_string) = try!(parse_null_terminated_string(rest));
let rest = EndianBuf::new(rest);
let aug_len = augmentation_string.to_bytes().len();
let (rest, address_size, segment_size) =
if Section::has_address_and_segment_sizes(version) {
let (rest, address_size) = try!(parse_u8e(rest));
let (rest, segment_size) = try!(parse_u8e(rest));
(rest, address_size, segment_size)
} else {
(rest, mem::size_of::<usize>() as u8, 0)
};
let (rest, code_alignment_factor) = try!(parse_unsigned_lebe(rest));
let (rest, data_alignment_factor) = try!(parse_signed_lebe(rest));
let (rest, return_address_register) =
match Section::return_address_register_encoding(version) {
ReturnAddressRegisterEncoding::U8 => {
let (rest, reg) = try!(parse_u8e(rest));
(rest, reg as u64)
}
ReturnAddressRegisterEncoding::Uleb => try!(parse_unsigned_lebe(rest)),
};
let (rest, augmentation) = if aug_len == 0 {
(rest, None)
} else {
let augmentation_string = try!(str::from_utf8(augmentation_string.to_bytes())
.map_err(|_| Error::BadUtf8));
let (rest, augmentation) =
try!(Augmentation::parse(augmentation_string, bases, address_size, section, rest));
(rest, Some(augmentation))
};
let entry = CommonInformationEntry {
length: length,
format: format,
version: version,
augmentation: augmentation,
address_size: address_size,
segment_size: segment_size,
code_alignment_factor: code_alignment_factor,
data_alignment_factor: data_alignment_factor,
return_address_register: return_address_register,
initial_instructions: rest,
phantom: PhantomData,
};
Ok(entry)
}
}
impl<'input, Endian, Section> CommonInformationEntry<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
pub fn instructions(&self) -> CallFrameInstructionIter<'input, Endian> {
CallFrameInstructionIter { input: self.initial_instructions }
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PartialFrameDescriptionEntry<'bases, 'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
length: u64,
format: Format,
cie_offset: Section::Offset,
rest: EndianBuf<'input, Endian>,
section: Section,
bases: &'bases BaseAddresses,
}
impl<'bases, 'input, Endian, Section> PartialFrameDescriptionEntry<'bases, 'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
pub fn parse<F>(&self, get_cie: F) -> Result<FrameDescriptionEntry<'input, Endian, Section>>
where F: FnMut(Section::Offset) -> Result<CommonInformationEntry<'input, Endian, Section>>
{
FrameDescriptionEntry::parse_rest(self.length,
self.format,
self.cie_offset,
self.rest,
self.section,
self.bases,
get_cie)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct FrameDescriptionEntry<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
length: u64,
format: Format,
cie: CommonInformationEntry<'input, Endian, Section>,
initial_segment: u64,
initial_address: u64,
address_range: u64,
augmentation: Option<AugmentationData>,
instructions: EndianBuf<'input, Endian>,
}
impl<'input, Endian, Section> FrameDescriptionEntry<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
fn parse_rest<F>(length: u64,
format: Format,
cie_pointer: Section::Offset,
rest: EndianBuf<'input, Endian>,
section: Section,
bases: &BaseAddresses,
mut get_cie: F)
-> Result<FrameDescriptionEntry<'input, Endian, Section>>
where F: FnMut(Section::Offset) -> Result<CommonInformationEntry<'input, Endian, Section>>
{
{
let mut func = bases.func.borrow_mut();
let offset = rest.as_ptr() as usize - section.section().as_ptr() as usize;
*func = Some(offset as u64);
}
let cie = try!(get_cie(cie_pointer));
let (rest, initial_segment) = if cie.segment_size > 0 {
try!(parse_address(rest, cie.segment_size))
} else {
(rest, 0)
};
let (rest, initial_address, address_range) =
try!(Self::parse_addresses(rest, &cie, bases, section));
let (rest, aug_data) = if let Some(ref augmentation) = cie.augmentation {
let (rest, aug_data) =
try!(AugmentationData::parse(augmentation, bases, cie.address_size, section, rest));
(rest, Some(aug_data))
} else {
(rest, None)
};
let entry = FrameDescriptionEntry {
length: length,
format: format,
cie: cie,
initial_segment: initial_segment,
initial_address: initial_address,
address_range: address_range,
augmentation: aug_data,
instructions: rest,
};
Ok(entry)
}
fn parse_addresses(input: EndianBuf<'input, Endian>,
cie: &CommonInformationEntry<'input, Endian, Section>,
bases: &BaseAddresses,
section: Section)
-> Result<(EndianBuf<'input, Endian>, u64, u64)> {
let encoding = cie.augmentation.as_ref().and_then(|a| a.fde_address_encoding);
if let Some(encoding) = encoding {
let (rest, initial_address) = try!(parse_encoded_pointer(encoding,
bases,
cie.address_size,
section.section(),
input));
let initial_address = initial_address.into();
let (rest, address_range) = try!(parse_encoded_pointer(encoding.format(),
bases,
cie.address_size,
section.section(),
rest));
Ok((rest, initial_address, address_range.into()))
} else {
let (rest, initial_address) = try!(parse_address(input, cie.address_size));
let (rest, address_range) = try!(parse_address(rest, cie.address_size));
Ok((rest, initial_address, address_range))
}
}
pub fn cie<'me>(&'me self) -> &'me CommonInformationEntry<'input, Endian, Section> {
&self.cie
}
pub fn instructions(&self) -> CallFrameInstructionIter<'input, Endian> {
CallFrameInstructionIter { input: self.instructions }
}
pub fn contains(&self, address: u64) -> bool {
let start = self.initial_address;
let end = start + self.address_range;
start <= address && address < end
}
pub fn lsda(&self) -> Option<Pointer> {
self.augmentation.as_ref().and_then(|a| a.lsda)
}
pub fn is_signal_trampoline(&self) -> bool {
self.cie().augmentation.map_or(false, |a| a.is_signal_trampoline)
}
pub fn personality(&self) -> Option<Pointer> {
self.cie().augmentation.as_ref().and_then(|a| a.personality)
}
}
#[derive(Clone, Debug)]
pub struct UninitializedUnwindContext<'input, Endian, Section>(Box<UnwindContext<'input,
Endian,
Section>>)
where Endian: Endianity,
Section: UnwindSection<'input, Endian>;
impl<'input, Endian, Section> UninitializedUnwindContext<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
pub fn new() -> UninitializedUnwindContext<'input, Endian, Section> {
UninitializedUnwindContext(Box::new(UnwindContext::new()))
}
}
impl<'input, Endian, Section> UninitializedUnwindContext<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
pub fn initialize(mut self,
cie: &CommonInformationEntry<'input, Endian, Section>)
-> Result<InitializedUnwindContext<'input, Endian, Section>> {
self.0.assert_fully_uninitialized();
{
let mut table = UnwindTable::new_internal(&mut self.0, cie, None);
while let Some(_) = try!(table.next_row()) {}
}
self.0.save_initial_rules();
Ok(InitializedUnwindContext(self.0))
}
}
#[derive(Clone, Debug)]
pub struct InitializedUnwindContext<'input, Endian, Section>(Box<UnwindContext<'input,
Endian,
Section>>)
where Endian: Endianity,
Section: UnwindSection<'input, Endian>;
impl<'input, Endian, Section> InitializedUnwindContext<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
pub fn reset(mut self) -> UninitializedUnwindContext<'input, Endian, Section> {
self.0.reset();
UninitializedUnwindContext(self.0)
}
}
const MAX_UNWIND_STACK_DEPTH: usize = 4;
type UnwindContextStack<'input, Endian> =
ArrayVec<[UnwindTableRow<'input, Endian>; MAX_UNWIND_STACK_DEPTH]>;
#[derive(Clone, Debug, PartialEq, Eq)]
struct UnwindContext<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
stack: UnwindContextStack<'input, Endian>,
initial_rules: RegisterRuleMap<'input, Endian>,
is_initialized: bool,
phantom: PhantomData<Section>,
}
impl<'input, Endian, Section> UnwindContext<'input, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
fn new() -> UnwindContext<'input, Endian, Section> {
let mut ctx = UnwindContext {
stack: Default::default(),
is_initialized: false,
initial_rules: Default::default(),
phantom: PhantomData,
};
ctx.reset();
ctx
}
fn reset(&mut self) {
self.stack.clear();
let res = self.stack.push(UnwindTableRow::default());
debug_assert!(res.is_none());
self.initial_rules.clear();
self.is_initialized = false;
self.assert_fully_uninitialized();
}
#[inline]
fn assert_fully_uninitialized(&self) {
assert_eq!(self.is_initialized, false);
assert_eq!(self.initial_rules.rules.len(), 0);
assert_eq!(self.stack.len(), 1);
assert_eq!(self.stack[0], UnwindTableRow::default());
}
fn row(&self) -> &UnwindTableRow<'input, Endian> {
self.stack.last().unwrap()
}
fn row_mut(&mut self) -> &mut UnwindTableRow<'input, Endian> {
self.stack.last_mut().unwrap()
}
fn save_initial_rules(&mut self) {
assert_eq!(self.is_initialized, false);
self.initial_rules.clone_from(&self.stack.last().unwrap().registers);
self.is_initialized = true;
}
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: u8,
rule: RegisterRule<'input, Endian>)
-> Result<()> {
let row = self.row_mut();
row.registers.set(register, rule)
}
fn get_initial_rule(&self, register: u8) -> Option<RegisterRule<'input, Endian>> {
if !self.is_initialized {
return None;
}
Some(self.initial_rules.get(register))
}
fn set_cfa(&mut self, cfa: CfaRule<'input, Endian>) {
self.row_mut().cfa = cfa;
}
fn cfa_mut(&mut self) -> &mut CfaRule<'input, Endian> {
&mut self.row_mut().cfa
}
fn push_row(&mut self) -> Result<()> {
let new_row = self.row().clone();
if let Some(_) = self.stack.push(new_row) {
Err(Error::CfiStackFull)
} else {
Ok(())
}
}
fn pop_row(&mut self) {
assert!(self.stack.len() > 1);
self.stack.pop();
}
}
#[derive(Debug)]
pub struct UnwindTable<'input, 'cie, 'fde, 'ctx, Endian, Section>
where Endian: 'cie + 'fde + 'ctx + Endianity,
Section: 'cie + 'fde + 'ctx + UnwindSection<'input, Endian>,
'input: 'cie + 'fde + 'ctx
{
cie: &'cie CommonInformationEntry<'input, Endian, Section>,
next_start_address: u64,
returned_last_row: bool,
instructions: CallFrameInstructionIter<'input, Endian>,
ctx: &'ctx mut UnwindContext<'input, Endian, Section>,
fde: Option<&'fde FrameDescriptionEntry<'input, Endian, Section>>,
}
impl<'input, 'fde, 'ctx, Endian, Section> UnwindTable<'input, 'fde, 'fde, 'ctx, Endian, Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
pub fn new(ctx: &'ctx mut InitializedUnwindContext<'input, Endian, Section>,
fde: &'fde FrameDescriptionEntry<'input, Endian, Section>)
-> UnwindTable<'input, 'fde, 'fde, 'ctx, Endian, Section> {
assert!(ctx.0.is_initialized);
Self::new_internal(&mut ctx.0, fde.cie(), Some(fde))
}
}
impl<'input, 'cie, 'fde, 'ctx, Endian, Section> UnwindTable<'input,
'cie,
'fde,
'ctx,
Endian,
Section>
where Endian: Endianity,
Section: UnwindSection<'input, Endian>
{
fn new_internal(ctx: &'ctx mut UnwindContext<'input, Endian, Section>,
cie: &'cie CommonInformationEntry<'input, Endian, Section>,
fde: Option<&'fde FrameDescriptionEntry<'input, Endian, Section>>)
-> UnwindTable<'input, 'cie, 'fde, 'ctx, Endian, Section> {
assert!(ctx.stack.len() >= 1);
let next_start_address = fde.map_or(0, |fde| fde.initial_address);
let instructions = fde.map_or_else(|| cie.instructions(), |fde| fde.instructions());
UnwindTable {
ctx: ctx,
cie: cie,
next_start_address: next_start_address,
returned_last_row: false,
instructions: instructions,
fde: fde,
}
}
pub fn next_row(&mut self) -> Result<Option<&UnwindTableRow<'input, Endian>>> {
assert!(self.ctx.stack.len() >= 1);
self.ctx.set_start_address(self.next_start_address);
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 = if let Some(fde) = self.fde {
fde.initial_address + fde.address_range
} else {
0
};
self.returned_last_row = true;
return Ok(Some(row));
}
Ok(Some(instruction)) => {
if try!(self.evaluate(instruction)) {
return Ok(Some(self.ctx.row()));
}
}
};
}
}
fn evaluate(&mut self, instruction: CallFrameInstruction<'input, Endian>) -> Result<bool> {
use 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 } => {
self.next_start_address = self.ctx.start_address() + delta as u64;
self.ctx.row_mut().end_address = self.next_start_address;
return Ok(true);
}
DefCfa { register, offset } => {
self.ctx.set_cfa(CfaRule::RegisterAndOffset {
register: register,
offset: offset as i64,
});
}
DefCfaSf { register, factored_offset } => {
let data_align = self.cie.data_alignment_factor as i64;
self.ctx.set_cfa(CfaRule::RegisterAndOffset {
register: register,
offset: factored_offset * data_align,
});
}
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.cie.data_alignment_factor as i64;
*off = factored_offset * data_align;
} else {
return Err(Error::CfiInstructionInInvalidContext);
}
}
DefCfaExpression { expression } => {
self.ctx.set_cfa(CfaRule::Expression(expression));
}
Undefined { register } => {
try!(self.ctx.set_register_rule(register, RegisterRule::Undefined));
}
SameValue { register } => {
try!(self.ctx.set_register_rule(register, RegisterRule::SameValue));
}
Offset { register, factored_offset } => {
let offset = factored_offset as i64 * self.cie.data_alignment_factor;
try!(self.ctx.set_register_rule(register, RegisterRule::Offset(offset)));
}
OffsetExtendedSf { register, factored_offset } => {
let offset = factored_offset * self.cie.data_alignment_factor;
try!(self.ctx.set_register_rule(register, RegisterRule::Offset(offset)));
}
ValOffset { register, factored_offset } => {
let offset = factored_offset as i64 * self.cie.data_alignment_factor;
try!(self.ctx.set_register_rule(register, RegisterRule::ValOffset(offset)));
}
ValOffsetSf { register, factored_offset } => {
let offset = factored_offset * self.cie.data_alignment_factor;
try!(self.ctx.set_register_rule(register, RegisterRule::ValOffset(offset)));
}
Register { dest_register, src_register } => {
try!(self.ctx
.set_register_rule(dest_register, RegisterRule::Register(src_register)));
}
Expression { register, expression } => {
let expression = RegisterRule::Expression(expression);
try!(self.ctx.set_register_rule(register, expression));
}
ValExpression { register, expression } => {
let expression = RegisterRule::ValExpression(expression);
try!(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);
};
try!(self.ctx.set_register_rule(register, initial_rule));
}
RememberState => {
try!(self.ctx.push_row());
}
RestoreState => {
assert!(self.ctx.stack.len() > 0);
if self.ctx.stack.len() == 1 {
return Err(Error::PopWithEmptyStack);
}
self.ctx.pop_row();
}
Nop => {}
};
Ok(false)
}
}
#[derive(Clone, Debug, Default)]
struct RegisterRuleMap<'input, Endian>
where Endian: Endianity
{
rules: ArrayVec<[(u8, RegisterRule<'input, Endian>); 32]>,
}
impl<'input, Endian> RegisterRuleMap<'input, Endian>
where Endian: Endianity
{
fn get(&self, register: u8) -> RegisterRule<'input, Endian> {
self.rules
.iter()
.find(|rule| rule.0 == register)
.map(|r| {
debug_assert!(r.1 != RegisterRule::Undefined);
r.1.clone()
})
.unwrap_or(RegisterRule::Undefined)
}
fn set(&mut self, register: u8, rule: RegisterRule<'input, Endian>) -> Result<()> {
if rule == RegisterRule::Undefined {
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 != RegisterRule::Undefined);
if reg == register {
mem::replace(old_rule, rule);
return Ok(());
}
}
if let Some(_) = self.rules.push((register, rule)) {
Err(Error::TooManyRegisterRules)
} else {
Ok(())
}
}
fn clear(&mut self) {
self.rules.clear();
}
fn iter<'me>(&'me self) -> RegisterRuleIter<'me, 'input, Endian> {
RegisterRuleIter(self.rules.iter())
}
}
impl<'a, 'input, Endian> FromIterator<&'a (u8, RegisterRule<'input, Endian>)> for RegisterRuleMap<'input, Endian>
where Endian: 'a + Endianity,
'input: 'a
{
fn from_iter<T>(iter: T) -> RegisterRuleMap<'input, Endian>
where T: IntoIterator<Item=&'a (u8, RegisterRule<'input, Endian>)>
{
let iter = iter.into_iter();
let mut rules = RegisterRuleMap::default();
for &(reg, ref rule) in iter.filter(|r| r.1 != RegisterRule::Undefined) {
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<'input, Endian> PartialEq for RegisterRuleMap<'input, Endian>
where Endian: Endianity
{
fn eq(&self, rhs: &Self) -> bool {
for &(reg, ref rule) in &self.rules {
debug_assert!(*rule != RegisterRule::Undefined);
if *rule != rhs.get(reg) {
return false;
}
}
for &(reg, ref rhs_rule) in &rhs.rules {
debug_assert!(*rhs_rule != RegisterRule::Undefined);
if *rhs_rule != self.get(reg) {
return false;
}
}
true
}
}
impl<'input, Endian> Eq for RegisterRuleMap<'input, Endian> where Endian: Endianity {}
#[derive(Debug, Clone)]
pub struct RegisterRuleIter<'iter, 'input, Endian>(::std::slice::Iter<'iter,
(u8,
RegisterRule<'input,
Endian>)>)
where Endian: 'iter + Endianity,
'input: 'iter;
impl<'iter, 'input, Endian> Iterator for RegisterRuleIter<'iter, 'input, Endian>
where Endian: Endianity
{
type Item = &'iter (u8, RegisterRule<'input, Endian>);
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct UnwindTableRow<'input, Endian>
where Endian: Endianity
{
start_address: u64,
end_address: u64,
cfa: CfaRule<'input, Endian>,
registers: RegisterRuleMap<'input, Endian>,
}
impl<'input, Endian> Default for UnwindTableRow<'input, Endian>
where Endian: Endianity
{
fn default() -> Self {
UnwindTableRow {
start_address: 0,
end_address: 0,
cfa: Default::default(),
registers: Default::default(),
}
}
}
impl<'input, Endian> UnwindTableRow<'input, Endian>
where Endian: Endianity
{
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 cfa(&self) -> &CfaRule<'input, Endian> {
&self.cfa
}
pub fn register(&self, register: u8) -> RegisterRule<'input, Endian> {
self.registers.get(register)
}
pub fn registers<'me>(&'me self) -> RegisterRuleIter<'me, 'input, Endian> {
self.registers.iter()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CfaRule<'input, Endian>
where Endian: Endianity
{
RegisterAndOffset {
register: u8,
offset: i64,
},
Expression(EndianBuf<'input, Endian>),
}
impl<'input, Endian> Default for CfaRule<'input, Endian>
where Endian: Endianity
{
fn default() -> Self {
CfaRule::RegisterAndOffset {
register: 0,
offset: 0,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RegisterRule<'input, Endian>
where Endian: Endianity
{
Undefined,
SameValue,
Offset(i64),
ValOffset(i64),
Register(u8),
Expression(EndianBuf<'input, Endian>),
ValExpression(EndianBuf<'input, Endian>),
Architectural,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum CallFrameInstruction<'input, Endian>
where Endian: Endianity
{
SetLoc {
address: u64,
},
AdvanceLoc {
delta: u32,
},
DefCfa {
register: u8,
offset: u64,
},
DefCfaSf {
register: u8,
factored_offset: i64,
},
DefCfaRegister {
register: u8,
},
DefCfaOffset {
offset: u64,
},
DefCfaOffsetSf {
factored_offset: i64,
},
DefCfaExpression {
expression: EndianBuf<'input, Endian>,
},
Undefined {
register: u8,
},
SameValue {
register: u8,
},
Offset {
register: u8,
factored_offset: u64,
},
OffsetExtendedSf {
register: u8,
factored_offset: i64,
},
ValOffset {
register: u8,
factored_offset: u64,
},
ValOffsetSf {
register: u8,
factored_offset: i64,
},
Register {
dest_register: u8,
src_register: u8,
},
Expression {
register: u8,
expression: EndianBuf<'input, Endian>,
},
ValExpression {
register: u8,
expression: EndianBuf<'input, Endian>,
},
Restore {
register: u8,
},
RememberState,
RestoreState,
Nop,
}
const CFI_INSTRUCTION_HIGH_BITS_MASK: u8 = 0b11000000;
const CFI_INSTRUCTION_LOW_BITS_MASK: u8 = !CFI_INSTRUCTION_HIGH_BITS_MASK;
impl<'input, Endian> CallFrameInstruction<'input, Endian>
where Endian: Endianity
{
fn parse(input: EndianBuf<'input, Endian>)
-> Result<(EndianBuf<'input, Endian>, CallFrameInstruction<'input, Endian>)> {
let (rest, instruction) = try!(parse_u8e(input));
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((rest, CallFrameInstruction::AdvanceLoc { delta: delta as u32 }));
}
if high_bits == constants::DW_CFA_offset.0 {
let register = instruction & CFI_INSTRUCTION_LOW_BITS_MASK;
let (rest, offset) = try!(parse_unsigned_lebe(rest));
return Ok((rest,
CallFrameInstruction::Offset {
register: register,
factored_offset: offset,
}));
}
if high_bits == constants::DW_CFA_restore.0 {
let register = instruction & CFI_INSTRUCTION_LOW_BITS_MASK;
return Ok((rest, CallFrameInstruction::Restore { register: register }));
}
debug_assert!(high_bits == 0);
let instruction = constants::DwCfa(instruction);
match instruction {
constants::DW_CFA_nop => Ok((rest, CallFrameInstruction::Nop)),
constants::DW_CFA_set_loc => {
let (rest, address) = try!(parse_unsigned_lebe(rest));
Ok((rest, CallFrameInstruction::SetLoc { address: address }))
}
constants::DW_CFA_advance_loc1 => {
let (rest, delta) = try!(parse_u8e(rest));
Ok((rest, CallFrameInstruction::AdvanceLoc { delta: delta as u32 }))
}
constants::DW_CFA_advance_loc2 => {
let (rest, delta) = try!(parse_u16(rest));
Ok((rest, CallFrameInstruction::AdvanceLoc { delta: delta as u32 }))
}
constants::DW_CFA_advance_loc4 => {
let (rest, delta) = try!(parse_u32(rest));
Ok((rest, CallFrameInstruction::AdvanceLoc { delta: delta }))
}
constants::DW_CFA_offset_extended => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, offset) = try!(parse_unsigned_lebe(rest));
Ok((rest,
CallFrameInstruction::Offset {
register: register,
factored_offset: offset,
}))
}
constants::DW_CFA_restore_extended => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
Ok((rest, CallFrameInstruction::Restore { register: register }))
}
constants::DW_CFA_undefined => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
Ok((rest, CallFrameInstruction::Undefined { register: register }))
}
constants::DW_CFA_same_value => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
Ok((rest, CallFrameInstruction::SameValue { register: register }))
}
constants::DW_CFA_register => {
let (rest, dest) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, src) = try!(parse_unsigned_leb_as_u8e(rest));
Ok((rest,
CallFrameInstruction::Register {
dest_register: dest,
src_register: src,
}))
}
constants::DW_CFA_remember_state => Ok((rest, CallFrameInstruction::RememberState)),
constants::DW_CFA_restore_state => Ok((rest, CallFrameInstruction::RestoreState)),
constants::DW_CFA_def_cfa => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, offset) = try!(parse_unsigned_lebe(rest));
Ok((rest,
CallFrameInstruction::DefCfa {
register: register,
offset: offset,
}))
}
constants::DW_CFA_def_cfa_register => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
Ok((rest, CallFrameInstruction::DefCfaRegister { register: register }))
}
constants::DW_CFA_def_cfa_offset => {
let (rest, offset) = try!(parse_unsigned_lebe(rest));
Ok((rest, CallFrameInstruction::DefCfaOffset { offset: offset }))
}
constants::DW_CFA_def_cfa_expression => {
let (rest, expression) = try!(parse_length_uleb_value(rest));
Ok((rest, CallFrameInstruction::DefCfaExpression { expression: expression }))
}
constants::DW_CFA_expression => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, expression) = try!(parse_length_uleb_value(rest));
Ok((rest,
CallFrameInstruction::Expression {
register: register,
expression: expression,
}))
}
constants::DW_CFA_offset_extended_sf => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, offset) = try!(parse_signed_lebe(rest));
Ok((rest,
CallFrameInstruction::OffsetExtendedSf {
register: register,
factored_offset: offset,
}))
}
constants::DW_CFA_def_cfa_sf => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, offset) = try!(parse_signed_lebe(rest));
Ok((rest,
CallFrameInstruction::DefCfaSf {
register: register,
factored_offset: offset,
}))
}
constants::DW_CFA_def_cfa_offset_sf => {
let (rest, offset) = try!(parse_signed_lebe(rest));
Ok((rest, CallFrameInstruction::DefCfaOffsetSf { factored_offset: offset }))
}
constants::DW_CFA_val_offset => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, offset) = try!(parse_unsigned_lebe(rest));
Ok((rest,
CallFrameInstruction::ValOffset {
register: register,
factored_offset: offset,
}))
}
constants::DW_CFA_val_offset_sf => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, offset) = try!(parse_signed_lebe(rest));
Ok((rest,
CallFrameInstruction::ValOffsetSf {
register: register,
factored_offset: offset,
}))
}
constants::DW_CFA_val_expression => {
let (rest, register) = try!(parse_unsigned_leb_as_u8e(rest));
let (rest, expression) = try!(parse_length_uleb_value(rest));
Ok((rest,
CallFrameInstruction::ValExpression {
register: register,
expression: expression,
}))
}
otherwise => Err(Error::UnknownCallFrameInstruction(otherwise)),
}
}
}
#[derive(Clone, Debug)]
pub struct CallFrameInstructionIter<'input, Endian>
where Endian: Endianity
{
input: EndianBuf<'input, Endian>,
}
impl<'input, Endian> CallFrameInstructionIter<'input, Endian>
where Endian: Endianity
{
pub fn next(&mut self) -> Result<Option<CallFrameInstruction<'input, Endian>>> {
if self.input.len() == 0 {
return Ok(None);
}
match CallFrameInstruction::parse(self.input) {
Ok((rest, instruction)) => {
self.input = rest;
Ok(Some(instruction))
}
Err(e) => {
self.input = EndianBuf::new(&[]);
Err(e)
}
}
}
}
impl<'input, Endian> FallibleIterator for CallFrameInstructionIter<'input, Endian>
where Endian: Endianity
{
type Item = CallFrameInstruction<'input, Endian>;
type Error = Error;
fn next(&mut self) -> ::std::result::Result<Option<Self::Item>, Self::Error> {
CallFrameInstructionIter::next(self)
}
}
#[cfg(test)]
mod tests {
extern crate test_assembler;
use super::*;
use super::{AugmentationData, parse_cfi_entry, RegisterRuleMap, UnwindContext};
use constants;
use endianity::{BigEndian, Endianity, EndianBuf, LittleEndian, NativeEndian};
use parser::{Error, Format, Pointer, Result};
use self::test_assembler::{Endian, Label, LabelMaker, LabelOrNum, Section, ToLabelOrNum};
use std::fmt::Debug;
use std::marker::PhantomData;
use std::mem;
use std::u64;
use test_util::GimliSectionMethods;
type DebugFrameCie<'input, Endian> = CommonInformationEntry<'input,
Endian,
DebugFrame<'input, Endian>>;
type EhFrameCie<'input, Endian> = CommonInformationEntry<'input,
Endian,
EhFrame<'input, Endian>>;
type DebugFrameFde<'input, Endian> = FrameDescriptionEntry<'input,
Endian,
DebugFrame<'input, Endian>>;
type EhFrameFde<'input, Endian> = FrameDescriptionEntry<'input,
Endian,
EhFrame<'input, Endian>>;
fn parse_fde<'input, Endian, Section, O, F>
(section: Section,
input: EndianBuf<'input, Endian>,
get_cie: F)
-> Result<(EndianBuf<'input, Endian>, FrameDescriptionEntry<'input, Endian, Section>)>
where Endian: Endianity,
Section: UnwindSection<'input, Endian, Offset = O>,
O: Copy + Debug + Eq + Into<usize> + From<usize>,
F: FnMut(O) -> Result<CommonInformationEntry<'input, Endian, Section>>
{
let bases = Default::default();
match parse_cfi_entry(&bases, section, input) {
Ok(Some((rest, CieOrFde::Fde(partial)))) => {
partial.parse(get_cie).map(|fde| (rest, fde))
}
Ok(_) => Err(Error::NoEntryAtGivenOffset),
Err(e) => Err(e),
}
}
trait CfiSectionMethods: GimliSectionMethods {
fn cie<'aug, 'input, E, T>(self,
endian: Endian,
augmentation: Option<&'aug str>,
cie: &mut CommonInformationEntry<'input, E, T>)
-> Self
where E: Endianity,
T: UnwindSection<'input, E>;
fn fde<'a, 'input, E, T, L>(self,
endian: Endian,
cie_offset: L,
fde: &mut FrameDescriptionEntry<'input, E, T>)
-> Self
where E: Endianity,
T: UnwindSection<'input, E>,
L: ToLabelOrNum<'a, u64>;
}
impl CfiSectionMethods for Section {
fn cie<'aug, 'input, E, T>(self,
endian: Endian,
augmentation: Option<&'aug str>,
cie: &mut CommonInformationEntry<'input, E, T>)
-> Self
where E: Endianity,
T: UnwindSection<'input, E>
{
let length = Label::new();
let start = Label::new();
let end = Label::new();
let section = match cie.format {
Format::Dwarf32 => {
self.e32(endian, &length)
.mark(&start)
.e32(endian, 0xffffffff)
}
Format::Dwarf64 => {
let section = self.e32(endian, 0xffffffff);
section.e64(endian, &length)
.mark(&start)
.e64(endian, 0xffffffffffffffff)
}
};
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(cie.segment_size)
} else {
section
};
let section = section.uleb(cie.code_alignment_factor)
.sleb(cie.data_alignment_factor)
.uleb(cie.return_address_register)
.append_bytes(cie.initial_instructions.into())
.mark(&end);
cie.length = (&end - &start) as u64;
length.set_const(cie.length);
section
}
fn fde<'a, 'input, E, T, L>(self,
endian: Endian,
cie_offset: L,
fde: &mut FrameDescriptionEntry<'input, E, T>)
-> Self
where E: Endianity,
T: UnwindSection<'input, E>,
L: ToLabelOrNum<'a, u64>
{
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.e32(endian, &length)
.mark(&start);
match cie_offset.to_labelornum() {
LabelOrNum::Label(ref l) => section.e32(endian, l),
LabelOrNum::Num(o) => section.e32(endian, o as u32),
}
}
CieOffsetEncoding::U64 => {
let section = self.e32(endian, 0xffffffff);
section.e64(endian, &length)
.mark(&start)
.e64(endian, cie_offset)
}
};
let section = match fde.cie.segment_size {
0 => section,
4 => section.e32(endian, fde.initial_segment as u32),
8 => section.e64(endian, fde.initial_segment),
x => panic!("Unsupported test segment size: {}", x),
};
let section = match fde.cie.address_size {
4 => {
section.e32(endian, fde.initial_address as u32)
.e32(endian, fde.address_range as u32)
}
8 => {
section.e64(endian, fde.initial_address)
.e64(endian, fde.address_range)
}
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(fde.cie.address_size as u64);
match fde.cie.address_size {
4 => {
section.e32(endian, {
let x: u64 = lsda.into();
x as u32
})
}
8 => {
section.e64(endian, {
let x: u64 = lsda.into();
x
})
}
x => panic!("Unsupported address size: {}", x),
}
} else {
section.uleb(0)
}
} else {
section
};
let section = section.append_bytes(fde.instructions.into()).mark(&end);
fde.length = (&end - &start) as u64;
length.set_const(fde.length);
section
}
}
fn assert_parse_cie<E>(section: Section,
expected: Result<Option<(EndianBuf<E>, DebugFrameCie<E>)>>)
where E: Endianity
{
let section = section.get_contents().unwrap();
let debug_frame = DebugFrame::<E>::new(§ion);
let input = EndianBuf::<E>::new(§ion);
let bases = Default::default();
assert_eq!(DebugFrameCie::parse(&bases, debug_frame, input), expected);
}
#[test]
fn test_parse_cie_incomplete_length_32() {
let section = Section::with_endian(Endian::Little).L16(5);
assert_parse_cie::<LittleEndian>(section, Err(Error::UnexpectedEof));
}
#[test]
fn test_parse_cie_incomplete_length_64() {
let section = Section::with_endian(Endian::Little)
.L32(0xffffffff)
.L32(12345);
assert_parse_cie::<LittleEndian>(section, Err(Error::UnexpectedEof));
}
#[test]
fn test_parse_cie_incomplete_id_32() {
let section = Section::with_endian(Endian::Big)
.B32(3)
.B32(0xffffffff);
assert_parse_cie::<BigEndian>(section, Err(Error::UnexpectedEof));
}
#[test]
fn test_parse_cie_bad_id_32() {
let section = Section::with_endian(Endian::Big)
.B32(4)
.B32(0xbad1bad2);
assert_parse_cie::<BigEndian>(section, Err(Error::NotCieId));
}
#[test]
fn test_parse_cie_32_bad_version() {
let mut cie = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 99,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 1,
data_alignment_factor: 2,
return_address_register: 3,
initial_instructions: EndianBuf::<LittleEndian>::new(&[]),
phantom: PhantomData,
};
let section = Section::with_endian(Endian::Little).cie(Endian::Little, None, &mut cie);
assert_parse_cie::<LittleEndian>(section, Err(Error::UnknownVersion));
}
#[test]
fn test_parse_cie_unknown_augmentation() {
let length = Label::new();
let start = Label::new();
let end = Label::new();
let augmentation = Some("replicant");
let expected_rest = [1, 2, 3];
let section = Section::with_endian(Endian::Little)
.L32(&length)
.mark(&start)
.L32(0xffffffff)
.D8(4)
.append_bytes(augmentation.unwrap().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::<LittleEndian>(section, Err(Error::UnknownAugmentation));
}
#[test]
fn test_parse_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: DebugFrameCie<LittleEndian> = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 16,
data_alignment_factor: 32,
return_address_register: 1,
initial_instructions: EndianBuf::new(&expected_instrs),
phantom: PhantomData,
};
let section = Section::with_endian(Endian::Little)
.cie(Endian::Little, None, &mut cie)
.append_bytes(&expected_rest);
assert_parse_cie::<LittleEndian>(section, Ok(Some((EndianBuf::new(&expected_rest), cie))));
}
#[test]
fn test_parse_cie_64_ok() {
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let expected_instrs: Vec<_> = (0..5).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie: DebugFrameCie<BigEndian> = DebugFrameCie {
length: 0,
format: Format::Dwarf64,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 16,
data_alignment_factor: 32,
return_address_register: 7,
initial_instructions: EndianBuf::new(&expected_instrs),
phantom: PhantomData,
};
let section = Section::with_endian(Endian::Big)
.cie(Endian::Big, None, &mut cie)
.append_bytes(&expected_rest);
assert_parse_cie(section, Ok(Some((EndianBuf::new(&expected_rest), cie))));
}
#[test]
fn test_parse_cie_length_too_big() {
let expected_instrs: Vec<_> = (0..13).map(|_| constants::DW_CFA_nop.0).collect();
let mut cie = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 0,
data_alignment_factor: 0,
return_address_register: 3,
initial_instructions: EndianBuf::<LittleEndian>::new(&expected_instrs),
phantom: PhantomData,
};
let section = Section::with_endian(Endian::Little).cie(Endian::Little, None, &mut cie);
let mut contents = section.get_contents().unwrap();
contents[0] = 0;
contents[1] = 0;
contents[2] = 0;
contents[3] = 255;
let bases = Default::default();
assert_eq!(DebugFrameCie::parse(&bases,
DebugFrame::new(&contents),
EndianBuf::<LittleEndian>::new(&contents)),
Err(Error::BadLength));
}
#[test]
fn test_parse_fde_incomplete_length_32() {
let section = Section::with_endian(Endian::Little).L16(5);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
assert_eq!(parse_fde(DebugFrame::new(section.into()), section, |_| unreachable!()),
Err(Error::UnexpectedEof));
}
#[test]
fn test_parse_fde_incomplete_length_64() {
let section = Section::with_endian(Endian::Little)
.L32(0xffffffff)
.L32(12345);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
assert_eq!(parse_fde(DebugFrame::new(section.into()), section, |_| unreachable!()),
Err(Error::UnexpectedEof));
}
#[test]
fn test_parse_fde_incomplete_cie_pointer_32() {
let section = Section::with_endian(Endian::Big)
.B32(3)
.B32(1994);
let section = section.get_contents().unwrap();
let section = EndianBuf::<BigEndian>::new(§ion);
assert_eq!(parse_fde(DebugFrame::new(section.into()), section, |_| unreachable!()),
Err(Error::UnexpectedEof));
}
#[test]
fn test_parse_fde_32_ok() {
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let cie_offset = 0xbad0bad1;
let expected_instrs: Vec<_> = (0..7).map(|_| constants::DW_CFA_nop.0).collect();
let cie = DebugFrameCie {
length: 100,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 8,
segment_size: 0,
code_alignment_factor: 3,
data_alignment_factor: 2,
return_address_register: 1,
initial_instructions: EndianBuf::new(&[]),
phantom: PhantomData,
};
let mut fde = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedbeef,
address_range: 39,
augmentation: None,
instructions: EndianBuf::<LittleEndian>::new(&expected_instrs),
};
let section = Section::with_endian(Endian::Little)
.fde(Endian::Little, cie_offset, &mut fde)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
let get_cie = |offset| {
assert_eq!(offset, DebugFrameOffset(cie_offset as usize));
Ok(cie.clone())
};
assert_eq!(parse_fde(DebugFrame::new(section.into()), section, get_cie),
Ok((EndianBuf::new(&expected_rest), fde)));
}
#[test]
fn test_parse_fde_32_with_segment_ok() {
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let cie_offset = 0xbad0bad1;
let expected_instrs: Vec<_> = (0..92)
.map(|_| constants::DW_CFA_nop.0)
.collect();
let cie = DebugFrameCie {
length: 100,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 4,
code_alignment_factor: 3,
data_alignment_factor: 2,
return_address_register: 1,
initial_instructions: EndianBuf::new(&[]),
phantom: PhantomData,
};
let mut fde = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0xbadbad11,
initial_address: 0xfeedbeef,
address_range: 999,
augmentation: None,
instructions: EndianBuf::new(&expected_instrs),
};
let section = Section::with_endian(Endian::Little)
.fde(Endian::Little, cie_offset, &mut fde)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
let get_cie = |offset| {
assert_eq!(offset, DebugFrameOffset(cie_offset as usize));
Ok(cie.clone())
};
assert_eq!(parse_fde(DebugFrame::new(section.into()), section, get_cie),
Ok((EndianBuf::new(&expected_rest), fde)));
}
#[test]
fn test_parse_fde_64_ok() {
let expected_rest = [1, 2, 3, 4, 5, 6, 7, 8, 9];
let cie_offset = 0xbad0bad1;
let expected_instrs: Vec<_> = (0..7)
.map(|_| constants::DW_CFA_nop.0)
.collect();
let cie = DebugFrameCie {
length: 100,
format: Format::Dwarf64,
version: 4,
augmentation: None,
address_size: 8,
segment_size: 0,
code_alignment_factor: 3,
data_alignment_factor: 2,
return_address_register: 1,
initial_instructions: EndianBuf::new(&[]),
phantom: PhantomData,
};
let mut fde = DebugFrameFde {
length: 0,
format: Format::Dwarf64,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedbeef,
address_range: 999,
augmentation: None,
instructions: EndianBuf::new(&expected_instrs),
};
let section = Section::with_endian(Endian::Little)
.fde(Endian::Little, cie_offset, &mut fde)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
let get_cie = |offset| {
assert_eq!(offset, DebugFrameOffset(cie_offset as usize));
Ok(cie.clone())
};
assert_eq!(parse_fde(DebugFrame::new(section.into()), section, get_cie),
Ok((EndianBuf::new(&expected_rest), fde)));
}
#[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 = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 16,
data_alignment_factor: 32,
return_address_register: 1,
initial_instructions: EndianBuf::new(&expected_instrs),
phantom: PhantomData,
};
let section = Section::with_endian(Endian::Big)
.cie(Endian::Big, None, &mut cie)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let section = EndianBuf::<BigEndian>::new(§ion);
let bases = Default::default();
assert_eq!(parse_cfi_entry(&bases, DebugFrame::new(section.into()), section),
Ok(Some((EndianBuf::new(&expected_rest), CieOrFde::Cie(cie)))));
}
#[test]
fn test_parse_cfi_entry_on_fde_32_ok() {
let cie_offset = 0x12345678;
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 = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 16,
data_alignment_factor: 32,
return_address_register: 1,
initial_instructions: EndianBuf::new(&[]),
phantom: PhantomData,
};
let mut fde = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedbeef,
address_range: 39,
augmentation: None,
instructions: EndianBuf::<BigEndian>::new(&expected_instrs),
};
let section = Section::with_endian(Endian::Big)
.fde(Endian::Big, cie_offset, &mut fde)
.append_bytes(&expected_rest);
let section = section.get_contents().unwrap();
let section = EndianBuf::<BigEndian>::new(§ion);
let bases = Default::default();
match parse_cfi_entry(&bases, DebugFrame::new(section.into()), section) {
Ok(Some((rest, CieOrFde::Fde(partial)))) => {
assert_eq!(rest, EndianBuf::new(&expected_rest));
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 = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 1,
data_alignment_factor: 2,
return_address_register: 3,
initial_instructions: EndianBuf::new(&expected_instrs1),
phantom: PhantomData,
};
let mut cie2 = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 3,
data_alignment_factor: 2,
return_address_register: 1,
initial_instructions: EndianBuf::new(&expected_instrs2),
phantom: PhantomData,
};
let cie1_location = Label::new();
let cie2_location = Label::new();
let section = Section::with_endian(Endian::Big)
.mark(&cie1_location)
.cie(Endian::Big, None, &mut cie1)
.mark(&cie2_location)
.cie(Endian::Big, None, &mut cie2);
let mut fde1 = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie1.clone(),
initial_segment: 0,
initial_address: 0xfeedbeef,
address_range: 39,
augmentation: None,
instructions: EndianBuf::<BigEndian>::new(&expected_instrs3),
};
let mut fde2 = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie2.clone(),
initial_segment: 0,
initial_address: 0xfeedface,
address_range: 9000,
augmentation: None,
instructions: EndianBuf::<BigEndian>::new(&expected_instrs4),
};
let section = section.fde(Endian::Big, &cie1_location, &mut fde1)
.fde(Endian::Big, &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 = DebugFrame::<BigEndian>::new(&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 = DebugFrameCie {
length: 0,
format: Format::Dwarf64,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 4,
data_alignment_factor: 8,
return_address_register: 12,
initial_instructions: EndianBuf::new(&instrs),
phantom: PhantomData,
};
let cie_location = Label::new();
let section = Section::with_endian(Endian::Little)
.append_bytes(&filler)
.mark(&cie_location)
.cie(Endian::Little, 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 = DebugFrame::<LittleEndian>::new(&contents);
let bases = Default::default();
assert_eq!(debug_frame.cie_from_offset(&bases, cie_offset), Ok(cie));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::AdvanceLoc { delta: expected_delta as u32 })));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::Offset {
register: expected_reg as u8,
factored_offset: expected_offset,
})));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::Restore { register: expected_reg as u8 })));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest), CallFrameInstruction::Nop)));
}
#[test]
fn test_parse_cfi_instruction_set_loc() {
let expected_rest = [1, 2, 3, 4];
let expected_addr = 0xdeadbeef;
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_set_loc.0)
.uleb(expected_addr)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::SetLoc { address: expected_addr })));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::AdvanceLoc { delta: expected_delta as u32 })));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::AdvanceLoc { delta: expected_delta as u32 })));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::AdvanceLoc { delta: expected_delta })));
}
#[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)
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::Offset {
register: expected_reg as u8,
factored_offset: expected_offset,
})));
}
#[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)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::Restore { register: expected_reg as u8 })));
}
#[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)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::Undefined { register: expected_reg as u8 })));
}
#[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)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::SameValue { register: expected_reg as u8 })));
}
#[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)
.uleb(expected_src_reg)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::Register {
dest_register: expected_dest_reg as u8,
src_register: expected_src_reg as u8,
})));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest), CallFrameInstruction::RememberState)));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest), CallFrameInstruction::RestoreState)));
}
#[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)
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::DefCfa {
register: expected_reg as u8,
offset: expected_offset,
})));
}
#[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)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::DefCfaRegister { register: expected_reg as u8 })));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::DefCfaOffset { offset: expected_offset })));
}
#[test]
fn test_parse_cfi_instruction_def_cfa_expression() {
let expected_rest = [1, 2, 3, 4];
let expected_expr = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1];
let length = Label::new();
let start = Label::new();
let end = Label::new();
let section = Section::with_endian(Endian::Little)
.D8(constants::DW_CFA_def_cfa_expression.0)
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.append_bytes(&expected_rest);
length.set_const((&end - &start) as u64);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::DefCfaExpression {
expression: EndianBuf::new(&expected_expr),
})));
}
#[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)
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.append_bytes(&expected_rest);
length.set_const((&end - &start) as u64);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::Expression {
register: expected_reg as u8,
expression: EndianBuf::new(&expected_expr),
})));
}
#[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)
.sleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::OffsetExtendedSf {
register: expected_reg as u8,
factored_offset: expected_offset,
})));
}
#[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)
.sleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::DefCfaSf {
register: expected_reg as u8,
factored_offset: expected_offset,
})));
}
#[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 = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::DefCfaOffsetSf { factored_offset: expected_offset })));
}
#[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)
.uleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::ValOffset {
register: expected_reg as u8,
factored_offset: expected_offset,
})));
}
#[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)
.sleb(expected_offset)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::ValOffsetSf {
register: expected_reg as u8,
factored_offset: expected_offset,
})));
}
#[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)
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.append_bytes(&expected_rest);
length.set_const((&end - &start) as u64);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
Ok((EndianBuf::new(&expected_rest),
CallFrameInstruction::ValExpression {
register: expected_reg as u8,
expression: EndianBuf::new(&expected_expr),
})));
}
#[test]
fn test_parse_cfi_instruction_unknown_instruction() {
let expected_rest = [1, 2, 3, 4];
let unknown_instr = constants::DwCfa(0b00111111);
let section = Section::with_endian(Endian::Little)
.D8(unknown_instr.0)
.append_bytes(&expected_rest);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<LittleEndian>::new(&contents);
assert_eq!(CallFrameInstruction::parse(input),
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)
.D8(&length)
.mark(&start)
.append_bytes(&expected_expr)
.mark(&end)
.D8(constants::DW_CFA_advance_loc1.0)
.D8(expected_delta);
length.set_const((&end - &start) as u64);
let contents = section.get_contents().unwrap();
let input = EndianBuf::<BigEndian>::new(&contents);
let mut iter = CallFrameInstructionIter { input: input };
assert_eq!(iter.next(),
Ok(Some(CallFrameInstruction::ValExpression {
register: expected_reg as u8,
expression: EndianBuf::new(&expected_expr),
})));
assert_eq!(iter.next(),
Ok(Some(CallFrameInstruction::AdvanceLoc { delta: expected_delta as u32 })));
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 = EndianBuf::<BigEndian>::new(&contents);
let mut iter = CallFrameInstructionIter { input: input };
assert_eq!(iter.next(), Err(Error::UnexpectedEof));
assert_eq!(iter.next(), Ok(None));
}
fn assert_eval<'a, I, T>(mut initial_ctx: UnwindContext<'a, LittleEndian, T>,
expected_ctx: UnwindContext<'a, LittleEndian, T>,
cie: CommonInformationEntry<'a, LittleEndian, T>,
fde: Option<FrameDescriptionEntry<'a, LittleEndian, T>>,
instructions: I)
where I: AsRef<[(Result<bool>, CallFrameInstruction<'a, LittleEndian>)]>,
T: UnwindSection<'a, LittleEndian>
{
{
let mut table = UnwindTable::new_internal(&mut initial_ctx, &cie, fde.as_ref());
for &(ref expected_result, ref instruction) in instructions.as_ref() {
assert_eq!(*expected_result, table.evaluate(instruction.clone()));
}
}
assert_eq!(expected_ctx, initial_ctx);
}
fn make_test_cie<'a, Section>() -> CommonInformationEntry<'a, LittleEndian, Section>
where Section: UnwindSection<'a, LittleEndian>
{
CommonInformationEntry {
format: Format::Dwarf64,
length: 0,
return_address_register: 0,
version: 4,
address_size: mem::size_of::<usize>() as u8,
initial_instructions: EndianBuf::new(&[]),
augmentation: None,
segment_size: 0,
data_alignment_factor: 2,
code_alignment_factor: 3,
phantom: PhantomData,
}
}
#[test]
fn test_eval_set_loc() {
let cie: DebugFrameCie<_> = 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: DebugFrameCie<_> = 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: DebugFrameCie<_> = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.row_mut().start_address = 3;
let mut expected = ctx.clone();
expected.row_mut().end_address = 4;
let instructions = [(Ok(true), CallFrameInstruction::AdvanceLoc { delta: 1 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
register: 42,
offset: 36,
});
let instructions = [(Ok(false),
CallFrameInstruction::DefCfa {
register: 42,
offset: 36,
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_sf() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
register: 42,
offset: 36 * cie.data_alignment_factor as i64,
});
let instructions = [(Ok(false),
CallFrameInstruction::DefCfaSf {
register: 42,
factored_offset: 36,
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_register() {
let cie: DebugFrameCie<_> = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::RegisterAndOffset {
register: 3,
offset: 8,
});
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
register: 42,
offset: 8,
});
let instructions = [(Ok(false), CallFrameInstruction::DefCfaRegister { register: 42 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_register_invalid_context() {
let cie: DebugFrameCie<_> = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::Expression(EndianBuf::new(&[])));
let expected = ctx.clone();
let instructions = [(Err(Error::CfiInstructionInInvalidContext),
CallFrameInstruction::DefCfaRegister { register: 42 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_offset() {
let cie: DebugFrameCie<_> = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::RegisterAndOffset {
register: 3,
offset: 8,
});
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::RegisterAndOffset {
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: DebugFrameCie<_> = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_cfa(CfaRule::Expression(EndianBuf::new(&[])));
let expected = ctx.clone();
let instructions = [(Err(Error::CfiInstructionInInvalidContext),
CallFrameInstruction::DefCfaOffset { offset: 1993 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_def_cfa_expression() {
let expr = [1, 2, 3, 4];
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_cfa(CfaRule::Expression(EndianBuf::new(&expr)));
let instructions =
[(Ok(false),
CallFrameInstruction::DefCfaExpression { expression: EndianBuf::new(&expr) })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_undefined() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(5, RegisterRule::Undefined).unwrap();
let instructions = [(Ok(false), CallFrameInstruction::Undefined { register: 5 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_same_value() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(0, RegisterRule::SameValue).unwrap();
let instructions = [(Ok(false), CallFrameInstruction::SameValue { register: 0 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_offset() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(2, RegisterRule::Offset(3 * cie.data_alignment_factor)).unwrap();
let instructions = [(Ok(false),
CallFrameInstruction::Offset {
register: 2,
factored_offset: 3,
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_offset_extended_sf() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(4, RegisterRule::Offset(-3 * cie.data_alignment_factor))
.unwrap();
let instructions = [(Ok(false),
CallFrameInstruction::OffsetExtendedSf {
register: 4,
factored_offset: -3,
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_val_offset() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(5, RegisterRule::ValOffset(7 * cie.data_alignment_factor))
.unwrap();
let instructions = [(Ok(false),
CallFrameInstruction::ValOffset {
register: 5,
factored_offset: 7,
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_val_offset_sf() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(5, RegisterRule::ValOffset(-7 * cie.data_alignment_factor))
.unwrap();
let instructions = [(Ok(false),
CallFrameInstruction::ValOffsetSf {
register: 5,
factored_offset: -7,
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_expression() {
let expr = [1, 2, 3, 4];
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(9, RegisterRule::Expression(EndianBuf::new(&expr))).unwrap();
let instructions = [(Ok(false),
CallFrameInstruction::Expression {
register: 9,
expression: EndianBuf::new(&expr),
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_val_expression() {
let expr = [1, 2, 3, 4];
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let mut expected = ctx.clone();
expected.set_register_rule(9, RegisterRule::ValExpression(EndianBuf::new(&expr))).unwrap();
let instructions = [(Ok(false),
CallFrameInstruction::ValExpression {
register: 9,
expression: EndianBuf::new(&expr),
})];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_restore() {
let cie: DebugFrameCie<_> = make_test_cie();
let fde = DebugFrameFde {
format: Format::Dwarf64,
length: 0,
address_range: 0,
augmentation: None,
initial_address: 0,
initial_segment: 0,
cie: cie.clone(),
instructions: EndianBuf::new(&[]),
};
let mut ctx = UnwindContext::new();
ctx.set_register_rule(0, RegisterRule::Offset(1)).unwrap();
ctx.save_initial_rules();
let expected = ctx.clone();
ctx.set_register_rule(0, RegisterRule::Offset(2)).unwrap();
let instructions = [(Ok(false), CallFrameInstruction::Restore { register: 0 })];
assert_eval(ctx, expected, cie, Some(fde), instructions);
}
#[test]
fn test_eval_restore_havent_saved_initial_context() {
let cie: DebugFrameCie<_> = make_test_cie();
let ctx = UnwindContext::new();
let expected = ctx.clone();
let instructions = [(Err(Error::CfiInstructionInInvalidContext),
CallFrameInstruction::Restore { register: 0 })];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_remember_state() {
let cie: DebugFrameCie<_> = 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: DebugFrameCie<_> = make_test_cie();
let mut ctx = UnwindContext::new();
ctx.set_register_rule(0, RegisterRule::SameValue).unwrap();
let expected = ctx.clone();
ctx.push_row().unwrap();
ctx.set_register_rule(0, RegisterRule::Offset(16)).unwrap();
let instructions = [ (Ok(false), CallFrameInstruction::RestoreState),
(Err(Error::PopWithEmptyStack), CallFrameInstruction::RestoreState)];
assert_eval(ctx, expected, cie, None, instructions);
}
#[test]
fn test_eval_nop() {
let cie: DebugFrameCie<_> = 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_next_row() {
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 = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: 3,
initial_instructions: EndianBuf::<LittleEndian>::new(&initial_instructions),
phantom: PhantomData,
};
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 = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0,
address_range: 100,
augmentation: None,
instructions: EndianBuf::<LittleEndian>::new(&instructions),
};
let ctx = UninitializedUnwindContext::new();
ctx.0.assert_fully_uninitialized();
let mut ctx = ctx.initialize(&cie).expect("Should run initial program OK");
assert!(ctx.0.is_initialized);
let expected_initial_rules: RegisterRuleMap<_> = [(0, RegisterRule::Offset(8)),
(3, RegisterRule::Offset(4))]
.into_iter()
.collect();
assert_eq!(ctx.0.initial_rules, expected_initial_rules);
let mut table = UnwindTable::new(&mut ctx, &fde);
{
let row = table.next_row().expect("Should evaluate first row OK");
let expected = UnwindTableRow {
start_address: 0,
end_address: 1,
cfa: CfaRule::RegisterAndOffset {
register: 4,
offset: -12,
},
registers: [(0, RegisterRule::Offset(8)), (3, RegisterRule::Offset(4))]
.into_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,
cfa: CfaRule::RegisterAndOffset {
register: 4,
offset: -12,
},
registers: [(0, RegisterRule::Offset(-16)), (3, RegisterRule::Offset(4))]
.into_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,
cfa: CfaRule::RegisterAndOffset {
register: 4,
offset: -12,
},
registers: [(0, RegisterRule::Offset(-16)), (3, RegisterRule::Offset(-4))]
.into_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,
cfa: CfaRule::RegisterAndOffset {
register: 4,
offset: -12,
},
registers: [(0, RegisterRule::Offset(-16)),
(3, RegisterRule::Offset(-4)),
(5, RegisterRule::Offset(4))]
.into_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 = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: 3,
initial_instructions: EndianBuf::new(&instrs1),
phantom: PhantomData,
};
let mut cie2 = DebugFrameCie {
length: 0,
format: Format::Dwarf32,
version: 4,
augmentation: None,
address_size: 4,
segment_size: 0,
code_alignment_factor: 1,
data_alignment_factor: 1,
return_address_register: 1,
initial_instructions: EndianBuf::new(&instrs2),
phantom: PhantomData,
};
let cie1_location = Label::new();
let cie2_location = Label::new();
let section = Section::with_endian(Endian::Big)
.mark(&cie1_location)
.cie(Endian::Big, None, &mut cie1)
.mark(&cie2_location)
.cie(Endian::Big, None, &mut cie2);
let mut fde1 = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie1.clone(),
initial_segment: 0,
initial_address: 0xfeedbeef,
address_range: 200,
augmentation: None,
instructions: EndianBuf::<BigEndian>::new(&instrs3),
};
let mut fde2 = DebugFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie2.clone(),
initial_segment: 0,
initial_address: 0xfeedface,
address_range: 9000,
augmentation: None,
instructions: EndianBuf::<BigEndian>::new(&instrs4),
};
let section = section.fde(Endian::Big, &cie1_location, &mut fde1)
.fde(Endian::Big, &cie2_location, &mut fde2);
section.start().set_const(0);
let contents = section.get_contents().unwrap();
let debug_frame = DebugFrame::<BigEndian>::new(&contents);
let bases = Default::default();
let ctx = UninitializedUnwindContext::new();
let result = debug_frame.unwind_info_for_address(&bases, ctx, 0xfeedbeef + 150);
assert!(result.is_ok());
let (unwind_info, _) = result.unwrap();
assert_eq!(unwind_info,
UnwindTableRow {
start_address: fde1.initial_address + 100,
end_address: fde1.initial_address + fde1.address_range,
cfa: CfaRule::RegisterAndOffset {
register: 4,
offset: -12,
},
registers: [(0, RegisterRule::Offset(-16))].into_iter().collect(),
});
}
#[test]
fn test_unwind_info_for_address_not_found() {
let debug_frame = DebugFrame::<NativeEndian>::new(&[]);
let bases = Default::default();
let ctx = UninitializedUnwindContext::new();
let result = debug_frame.unwind_info_for_address(&bases, ctx, 0xbadbad99);
assert!(result.is_err());
assert_eq!(result.unwrap_err(), Error::NoUnwindInfoForAddress);
}
#[test]
fn test_eh_frame_stops_at_zero_length() {
let section = Section::with_endian(Endian::Little).L32(0);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
let bases = Default::default();
assert_eq!(parse_cfi_entry(&bases, EhFrame::new(section.into()), section),
Ok(None));
assert_eq!(EhFrame::<LittleEndian>::new(§ion)
.cie_from_offset(&bases, EhFrameOffset(0)),
Err(Error::NoEntryAtGivenOffset));
}
#[test]
fn test_eh_frame_resolve_cie_offset_ok() {
let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let section = EhFrame::<BigEndian>::new(&buf);
let subslice = EndianBuf::new(&buf[6..8]);
assert_eq!(section.resolve_cie_offset(subslice, 4), Some(2));
}
#[test]
fn test_eh_frame_resolve_cie_offset_out_of_bounds() {
let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let section = EhFrame::<BigEndian>::new(&buf);
let subslice = EndianBuf::new(&buf[6..8]);
assert_eq!(section.resolve_cie_offset(subslice, 7), None);
}
#[test]
fn test_eh_frame_resolve_cie_offset_underflow() {
let buf = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
let section = EhFrame::<BigEndian>::new(&buf);
let subslice = EndianBuf::new(&buf[6..8]);
assert_eq!(section.resolve_cie_offset(subslice, ::std::usize::MAX),
None);
}
#[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 section = Section::with_endian(Endian::Little)
.append_repeated(0, 16)
.mark(&start_of_cie)
.cie(Endian::Little, None, &mut cie)
.mark(&end_of_cie);
let mut fde = EhFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedbeef,
address_range: 999,
augmentation: None,
instructions: EndianBuf::new(&[]),
};
let section = section
.fde(Endian::Little, (&end_of_cie - &start_of_cie + 4) as u64, &mut fde);
section.start().set_const(0);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
let mut offset = None;
match parse_fde(EhFrame::new(section.into()),
section.range_from(end_of_cie.value().unwrap() as usize..),
|o| {
offset = Some(o);
assert_eq!(o, EhFrameOffset(start_of_cie.value().unwrap() as usize));
Ok(cie.clone())
}) {
Ok((_, actual)) => assert_eq!(actual, fde),
otherwise => panic!("Unexpected result {:?}", otherwise),
}
assert!(offset.is_some());
}
#[test]
fn test_eh_frame_fde_out_of_bounds() {
let mut cie = make_test_cie();
cie.version = 1;
let end_of_cie = Label::new();
let mut fde = EhFrameFde {
length: 0,
format: Format::Dwarf64,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedbeef,
address_range: 999,
augmentation: None,
instructions: EndianBuf::new(&[]),
};
let section = Section::with_endian(Endian::Little)
.cie(Endian::Little, None, &mut cie)
.mark(&end_of_cie)
.fde(Endian::Little, 99999999999999, &mut fde);
section.start().set_const(0);
let section = section.get_contents().unwrap();
let section = EndianBuf::<LittleEndian>::new(§ion);
let result = parse_fde(EhFrame::new(section.into()),
section.range_from(end_of_cie.value().unwrap() as usize..),
|_| unreachable!());
assert_eq!(result, Err(Error::OffsetOutOfBounds));
}
#[test]
fn test_augmentation_parse_not_z_augmentation() {
let augmentation = "wtf";
let bases = Default::default();
let address_size = 8;
let section = EhFrame::<NativeEndian>::new(&[]);
let input = EndianBuf::new(&[]);
assert_eq!(Augmentation::parse(augmentation, &bases, address_size, section, input),
Err(Error::UnknownAugmentation));
}
#[test]
fn test_augmentation_parse_unknown_part_of_z_augmentation() {
let augmentation = "zZ";
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::<LittleEndian>::new(§ion);
let input = EndianBuf::new(section.section().into());
assert_eq!(Augmentation::parse(augmentation, &bases, address_size, section, input),
Err(Error::UnknownAugmentation));
}
#[test]
#[allow(non_snake_case)]
fn test_augmentation_parse_L() {
let aug_str = "zL";
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::<LittleEndian>::new(§ion);
let input = EndianBuf::new(section.section().into());
let mut augmentation = Augmentation::default();
augmentation.lsda = Some(constants::DW_EH_PE_uleb128);
assert_eq!(Augmentation::parse(aug_str, &bases, address_size, section, input),
Ok((EndianBuf::new(&rest), augmentation)));
}
#[test]
#[allow(non_snake_case)]
fn test_augmentation_parse_P() {
let aug_str = "zP";
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(0xf00df00d)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::<LittleEndian>::new(§ion);
let input = EndianBuf::new(section.section().into());
let mut augmentation = Augmentation::default();
augmentation.personality = Some(Pointer::Direct(0xf00df00d));
assert_eq!(Augmentation::parse(aug_str, &bases, address_size, section, input),
Ok((EndianBuf::new(&rest), augmentation)));
}
#[test]
#[allow(non_snake_case)]
fn test_augmentation_parse_R() {
let aug_str = "zR";
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::<LittleEndian>::new(§ion);
let input = EndianBuf::new(section.section().into());
let mut augmentation = Augmentation::default();
augmentation.fde_address_encoding = Some(constants::DW_EH_PE_udata4);
assert_eq!(Augmentation::parse(aug_str, &bases, address_size, section, input),
Ok((EndianBuf::new(&rest), augmentation)));
}
#[test]
#[allow(non_snake_case)]
fn test_augmentation_parse_S() {
let aug_str = "zS";
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::<LittleEndian>::new(§ion);
let input = EndianBuf::new(section.section().into());
let mut augmentation = Augmentation::default();
augmentation.is_signal_trampoline = true;
assert_eq!(Augmentation::parse(aug_str, &bases, address_size, section, input),
Ok((EndianBuf::new(&rest), augmentation)));
}
#[test]
fn test_augmentation_parse_all() {
let aug_str = "zLPRS";
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(0x1badf00d)
.D8(constants::DW_EH_PE_uleb128.0)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::<LittleEndian>::new(§ion);
let input = EndianBuf::new(section.section().into());
let augmentation = Augmentation {
lsda: Some(constants::DW_EH_PE_uleb128),
personality: Some(Pointer::Direct(0x1badf00d)),
fde_address_encoding: Some(constants::DW_EH_PE_uleb128),
is_signal_trampoline: true,
};
assert_eq!(Augmentation::parse(aug_str, &bases, address_size, section, input),
Ok((EndianBuf::new(&rest), augmentation)));
}
#[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 = EhFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedface,
address_range: 9000,
augmentation: None,
instructions: EndianBuf::<LittleEndian>::new(&instrs),
};
let rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.fde(Endian::Little, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::<LittleEndian>::new(§ion);
let result = parse_fde(section, section.section(), |_| Ok(cie.clone()));
assert_eq!(result, Ok((EndianBuf::new(&rest), fde)));
}
#[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 = EhFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedface,
address_range: 9000,
augmentation: Some(AugmentationData::default()),
instructions: EndianBuf::<LittleEndian>::new(&instrs),
};
let rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.fde(Endian::Little, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::<LittleEndian>::new(§ion);
let result = parse_fde(section, section.section(), |_| Ok(cie.clone()));
assert_eq!(result, Ok((EndianBuf::new(&rest), fde)));
}
#[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 = EhFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedface,
address_range: 9000,
augmentation: Some(AugmentationData { lsda: Some(Pointer::Direct(0x11223344)) }),
instructions: EndianBuf::<LittleEndian>::new(&instrs),
};
let rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.fde(Endian::Little, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::<LittleEndian>::new(§ion);
let result = parse_fde(section, section.section(), |_| Ok(cie.clone()));
assert_eq!(result, Ok((EndianBuf::new(&rest), fde)));
}
#[test]
fn test_eh_frame_fde_lsda_function_relative() {
let instrs = [1, 2, 3, 4];
let cie_offset = 1;
let mut cie = make_test_cie();
cie.format = Format::Dwarf32;
cie.version = 1;
cie.augmentation = Some(Augmentation::default());
cie.augmentation.as_mut().unwrap().lsda =
Some(constants::DwEhPe(constants::DW_EH_PE_funcrel.0 | constants::DW_EH_PE_absptr.0));
let mut fde = EhFrameFde {
length: 0,
format: Format::Dwarf32,
cie: cie.clone(),
initial_segment: 0,
initial_address: 0xfeedface,
address_range: 9000,
augmentation: Some(AugmentationData { lsda: Some(Pointer::Direct(1)) }),
instructions: EndianBuf::<LittleEndian>::new(&instrs),
};
let rest = [1, 2, 3, 4];
let section = Section::with_endian(Endian::Little)
.append_repeated(10, 10)
.fde(Endian::Little, cie_offset, &mut fde)
.append_bytes(&rest)
.get_contents()
.unwrap();
let section = EhFrame::<LittleEndian>::new(§ion);
let input = section.section().range_from(10..);
fde.augmentation.as_mut().unwrap().lsda = Some(Pointer::Direct(19));
let result = parse_fde(section, input, |_| Ok(cie.clone()));
assert_eq!(result, Ok((EndianBuf::new(&rest), fde)));
}
#[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::<LittleEndian>::new(§ion);
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<LittleEndian> = [(0, RegisterRule::SameValue),
(3, RegisterRule::Offset(1))]
.iter()
.collect();
let map2: RegisterRuleMap<LittleEndian> = [(3, RegisterRule::Offset(1)),
(0, RegisterRule::SameValue)]
.iter()
.collect();
assert_eq!(map1, map2);
assert_eq!(map2, map1);
let map3: RegisterRuleMap<LittleEndian> = [(0, RegisterRule::SameValue),
(2, RegisterRule::Offset(1))]
.iter()
.collect();
let map4: RegisterRuleMap<LittleEndian> = [(3, RegisterRule::Offset(1)),
(0, RegisterRule::SameValue)]
.iter()
.collect();
assert!(map3 != map4);
assert!(map4 != map3);
let mut map5 = RegisterRuleMap::<LittleEndian>::default();
map5.set(0, RegisterRule::SameValue).unwrap();
map5.set(0, RegisterRule::Undefined).unwrap();
let map6 = RegisterRuleMap::<LittleEndian>::default();
assert_eq!(map5, map6);
assert_eq!(map6, map5);
}
#[test]
fn iter_register_rules() {
let mut row = UnwindTableRow::<LittleEndian>::default();
row.registers = [(0, RegisterRule::SameValue),
(1, RegisterRule::Offset(1)),
(2, RegisterRule::ValOffset(2))]
.iter()
.collect();
let mut found0 = false;
let mut found1 = false;
let mut found2 = false;
for &(register, ref rule) in row.registers() {
match register {
0 => {
assert_eq!(found0, false);
found0 = true;
assert_eq!(*rule, RegisterRule::SameValue);
}
1 => {
assert_eq!(found1, false);
found1 = true;
assert_eq!(*rule, RegisterRule::Offset(1));
}
2 => {
assert_eq!(found2, false);
found2 = true;
assert_eq!(*rule, RegisterRule::ValOffset(2));
}
x => panic!("Unexpected register rule: ({}, {:?})", x, rule),
}
}
assert_eq!(found0, true);
assert_eq!(found1, true);
assert_eq!(found2, true);
}
#[test]
fn size_of_unwind_ctx() {
use std::mem;
assert_eq!(mem::size_of::<UnwindContext<NativeEndian, EhFrame<NativeEndian>>>(),
5384);
}
#[test]
fn size_of_register_rule_map() {
use std::mem;
assert_eq!(mem::size_of::<RegisterRuleMap<NativeEndian>>(), 1040);
}
}