use core::fmt::{self, Debug};
use core::result;
#[cfg(feature = "std")]
use std::{error, io};
use crate::common::{Register, SectionId};
use crate::constants;
mod addr;
pub use self::addr::*;
mod cfi;
pub use self::cfi::*;
mod dwarf;
pub use self::dwarf::*;
mod endian_slice;
pub use self::endian_slice::*;
mod endian_reader;
pub use self::endian_reader::*;
mod reader;
pub use self::reader::*;
mod abbrev;
pub use self::abbrev::*;
mod aranges;
pub use self::aranges::*;
mod line;
pub use self::line::*;
mod loclists;
pub use self::loclists::*;
mod lookup;
mod op;
pub use self::op::*;
mod pubnames;
pub use self::pubnames::*;
mod pubtypes;
pub use self::pubtypes::*;
mod rnglists;
pub use self::rnglists::*;
mod str;
pub use self::str::*;
mod unit;
pub use self::unit::*;
mod value;
pub use self::value::*;
#[deprecated(note = "EndianBuf has been renamed to EndianSlice, use that instead.")]
pub type EndianBuf<'input, Endian> = EndianSlice<'input, Endian>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
Io,
PcRelativePointerButSectionBaseIsUndefined,
TextRelativePointerButTextBaseIsUndefined,
DataRelativePointerButDataBaseIsUndefined,
FuncRelativePointerInBadContext,
CannotParseOmitPointerEncoding,
BadUnsignedLeb128,
BadSignedLeb128,
AbbreviationTagZero,
AttributeFormZero,
BadHasChildren,
BadLength,
UnknownForm,
ExpectedZero,
DuplicateAbbreviationCode,
DuplicateArange,
UnknownReservedLength,
UnknownVersion(u64),
UnknownAbbreviation,
UnexpectedEof(ReaderOffsetId),
UnexpectedNull,
UnknownStandardOpcode(constants::DwLns),
UnknownExtendedOpcode(constants::DwLne),
UnsupportedAddressSize(u8),
UnsupportedOffsetSize(u8),
UnsupportedFieldSize(u8),
MinimumInstructionLengthZero,
MaximumOperationsPerInstructionZero,
LineRangeZero,
OpcodeBaseZero,
BadUtf8,
NotCieId,
NotCiePointer,
NotFdePointer,
BadBranchTarget(u64),
InvalidPushObjectAddress,
NotEnoughStackItems,
TooManyIterations,
InvalidExpression(constants::DwOp),
InvalidPiece,
InvalidExpressionTerminator(u64),
DivisionByZero,
TypeMismatch,
IntegralTypeRequired,
UnsupportedTypeOperation,
InvalidShiftExpression,
UnknownCallFrameInstruction(constants::DwCfa),
InvalidAddressRange,
InvalidLocationAddressRange,
CfiInstructionInInvalidContext,
PopWithEmptyStack,
NoUnwindInfoForAddress,
UnsupportedOffset,
UnknownPointerEncoding,
NoEntryAtGivenOffset,
OffsetOutOfBounds,
UnknownAugmentation,
UnsupportedPointerEncoding,
UnsupportedRegister(u64),
TooManyRegisterRules,
CfiStackFull,
VariableLengthSearchTable,
UnsupportedUnitType,
UnsupportedAddressIndex,
UnsupportedSegmentSize,
MissingUnitDie,
UnsupportedAttributeForm,
MissingFileEntryFormatPath,
ExpectedStringAttributeValue,
}
impl fmt::Display for Error {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> ::core::result::Result<(), fmt::Error> {
write!(f, "{}", self.description())
}
}
impl Error {
pub fn description(&self) -> &str {
match *self {
Error::Io => "An I/O error occurred while reading.",
Error::PcRelativePointerButSectionBaseIsUndefined => {
"Found a PC relative pointer, but the section base is undefined."
}
Error::TextRelativePointerButTextBaseIsUndefined => {
"Found a `.text` relative pointer, but the `.text` base is undefined."
}
Error::DataRelativePointerButDataBaseIsUndefined => {
"Found a data relative pointer, but the data base is undefined."
}
Error::FuncRelativePointerInBadContext => {
"Found a function relative pointer in a context that does not have a function base."
}
Error::CannotParseOmitPointerEncoding => {
"Cannot parse a pointer with a `DW_EH_PE_omit` encoding."
}
Error::BadUnsignedLeb128 => "An error parsing an unsigned LEB128 value",
Error::BadSignedLeb128 => "An error parsing a signed LEB128 value",
Error::AbbreviationTagZero => {
"An abbreviation declared that its tag is zero,
but zero is reserved for null records"
}
Error::AttributeFormZero => {
"An attribute specification declared that its form is zero,
but zero is reserved for null records"
}
Error::BadHasChildren => {
"The abbreviation's has-children byte was not one of
`DW_CHILDREN_{yes,no}`"
}
Error::BadLength => "The specified length is impossible",
Error::UnknownForm => "Found an unknown `DW_FORM_*` type",
Error::ExpectedZero => "Expected a zero, found something else",
Error::DuplicateAbbreviationCode => {
"Found an abbreviation code that has already been used"
}
Error::DuplicateArange => "Found a duplicate arange",
Error::UnknownReservedLength => "Found an unknown reserved length value",
Error::UnknownVersion(_) => "Found an unknown DWARF version",
Error::UnknownAbbreviation => "Found a record with an unknown abbreviation code",
Error::UnexpectedEof(_) => "Hit the end of input before it was expected",
Error::UnexpectedNull => "Read a null entry before it was expected.",
Error::UnknownStandardOpcode(_) => "Found an unknown standard opcode",
Error::UnknownExtendedOpcode(_) => "Found an unknown extended opcode",
Error::UnsupportedAddressSize(_) => "The specified address size is not supported",
Error::UnsupportedOffsetSize(_) => "The specified offset size is not supported",
Error::UnsupportedFieldSize(_) => "The specified field size is not supported",
Error::MinimumInstructionLengthZero => {
"The minimum instruction length must not be zero."
}
Error::MaximumOperationsPerInstructionZero => {
"The maximum operations per instruction must not be zero."
}
Error::LineRangeZero => "The line range must not be zero.",
Error::OpcodeBaseZero => "The opcode base must not be zero.",
Error::BadUtf8 => "Found an invalid UTF-8 string.",
Error::NotCieId => "Expected to find the CIE ID, but found something else.",
Error::NotCiePointer => "Expected to find a CIE pointer, but found the CIE ID instead.",
Error::NotFdePointer => {
"Expected to find an FDE pointer, but found a CIE pointer instead."
}
Error::BadBranchTarget(_) => "Invalid branch target in DWARF expression",
Error::InvalidPushObjectAddress => {
"DW_OP_push_object_address used but no object address given"
}
Error::NotEnoughStackItems => "Not enough items on stack when evaluating expression",
Error::TooManyIterations => "Too many iterations to evaluate DWARF expression",
Error::InvalidExpression(_) => "Invalid opcode in DWARF expression",
Error::InvalidPiece => {
"DWARF expression has piece followed by non-piece expression at end"
}
Error::InvalidExpressionTerminator(_) => "Expected DW_OP_piece or DW_OP_bit_piece",
Error::DivisionByZero => "Division or modulus by zero when evaluating expression",
Error::TypeMismatch => "Type mismatch when evaluating expression",
Error::IntegralTypeRequired => "Integral type expected when evaluating expression",
Error::UnsupportedTypeOperation => {
"An expression operation used types that are not supported"
}
Error::InvalidShiftExpression => {
"The shift value in an expression must be a non-negative integer."
}
Error::UnknownCallFrameInstruction(_) => "An unknown DW_CFA_* instructiion",
Error::InvalidAddressRange => {
"The end of an address range must not be before the beginning."
}
Error::InvalidLocationAddressRange => {
"The end offset of a location list entry must not be before the beginning."
}
Error::CfiInstructionInInvalidContext => {
"Encountered a call frame instruction in a context in which it is not valid."
}
Error::PopWithEmptyStack => {
"When evaluating call frame instructions, found a `DW_CFA_restore_state` stack pop \
instruction, but the stack was empty, and had nothing to pop."
}
Error::NoUnwindInfoForAddress => "Do not have unwind info for the given address.",
Error::UnsupportedOffset => {
"An offset value was larger than the maximum supported value."
}
Error::UnknownPointerEncoding => {
"The given pointer encoding is either unknown or invalid."
}
Error::NoEntryAtGivenOffset => "Did not find an entry at the given offset.",
Error::OffsetOutOfBounds => "The given offset is out of bounds.",
Error::UnknownAugmentation => "Found an unknown CFI augmentation.",
Error::UnsupportedPointerEncoding => {
"We do not support the given pointer encoding yet."
}
Error::UnsupportedRegister(_) => "Registers larger than `u16` are not supported.",
Error::TooManyRegisterRules => {
"The CFI program defined more register rules than we have storage for."
}
Error::CfiStackFull => {
"Attempted to push onto the CFI stack, but it was already at full capacity."
}
Error::VariableLengthSearchTable => {
"The `.eh_frame_hdr` binary search table claims to be variable-length encoded, \
which makes binary search impossible."
}
Error::UnsupportedUnitType => "The `DW_UT_*` value for this unit is not supported yet",
Error::UnsupportedAddressIndex => "Ranges involving AddressIndex are not supported yet",
Error::UnsupportedSegmentSize => "Nonzero segment size not supported yet",
Error::MissingUnitDie => {
"A compilation unit or type unit is missing its top level DIE."
}
Error::UnsupportedAttributeForm => "A DIE attribute used an unsupported form.",
Error::MissingFileEntryFormatPath => "Missing DW_LNCT_path in file entry format.",
Error::ExpectedStringAttributeValue => {
"Expected an attribute value to be a string form."
}
}
}
}
#[cfg(feature = "std")]
impl error::Error for Error {}
#[cfg(feature = "std")]
impl From<io::Error> for Error {
fn from(_: io::Error) -> Self {
Error::Io
}
}
pub type Result<T> = result::Result<T, Error>;
pub trait Section<R>: From<R> {
fn id() -> SectionId;
fn section_name() -> &'static str {
Self::id().name()
}
fn load<F, E>(f: F) -> core::result::Result<Self, E>
where
F: FnOnce(SectionId) -> core::result::Result<R, E>,
{
f(Self::id()).map(From::from)
}
fn reader(&self) -> &R
where
R: Reader;
fn lookup_offset_id(&self, id: ReaderOffsetId) -> Option<(SectionId, R::Offset)>
where
R: Reader,
{
self.reader()
.lookup_offset_id(id)
.map(|offset| (Self::id(), offset))
}
}
impl Register {
pub(crate) fn from_u64(x: u64) -> Result<Register> {
let y = x as u16;
if u64::from(y) == x {
Ok(Register(y))
} else {
Err(Error::UnsupportedRegister(x))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::Format;
use crate::endianity::LittleEndian;
use test_assembler::{Endian, Section};
#[test]
fn test_parse_initial_length_32_ok() {
let section = Section::with_endian(Endian::Little).L32(0x7856_3412);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_initial_length() {
Ok((length, format)) => {
assert_eq!(input.len(), 0);
assert_eq!(format, Format::Dwarf32);
assert_eq!(0x7856_3412, length);
}
otherwise => panic!("Unexpected result: {:?}", otherwise),
}
}
#[test]
fn test_parse_initial_length_64_ok() {
let section = Section::with_endian(Endian::Little)
.L32(0xffff_ffff)
.L64(0xffde_bc9a_7856_3412);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
#[cfg(target_pointer_width = "64")]
match input.read_initial_length() {
Ok((length, format)) => {
assert_eq!(input.len(), 0);
assert_eq!(format, Format::Dwarf64);
assert_eq!(0xffde_bc9a_7856_3412, length);
}
otherwise => panic!("Unexpected result: {:?}", otherwise),
}
#[cfg(target_pointer_width = "32")]
match input.read_initial_length() {
Err(Error::UnsupportedOffset) => {}
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
#[test]
fn test_parse_initial_length_unknown_reserved_value() {
let section = Section::with_endian(Endian::Little).L32(0xffff_fffe);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_initial_length() {
Err(Error::UnknownReservedLength) => assert!(true),
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
#[test]
fn test_parse_initial_length_incomplete() {
let buf = [0xff, 0xff, 0xff];
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_initial_length() {
Err(Error::UnexpectedEof(_)) => assert!(true),
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
#[test]
fn test_parse_initial_length_64_incomplete() {
let section = Section::with_endian(Endian::Little)
.L32(0xffff_ffff)
.L32(0x7856_3412);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_initial_length() {
Err(Error::UnexpectedEof(_)) => assert!(true),
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
#[test]
fn test_parse_offset_32() {
let section = Section::with_endian(Endian::Little).L32(0x0123_4567);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_offset(Format::Dwarf32) {
Ok(val) => {
assert_eq!(input.len(), 0);
assert_eq!(val, 0x0123_4567);
}
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
#[test]
fn test_parse_offset_64_small() {
let section = Section::with_endian(Endian::Little).L64(0x0123_4567);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_offset(Format::Dwarf64) {
Ok(val) => {
assert_eq!(input.len(), 0);
assert_eq!(val, 0x0123_4567);
}
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
#[test]
#[cfg(target_pointer_width = "64")]
fn test_parse_offset_64_large() {
let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_offset(Format::Dwarf64) {
Ok(val) => {
assert_eq!(input.len(), 0);
assert_eq!(val, 0x0123_4567_89ab_cdef);
}
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
#[test]
#[cfg(target_pointer_width = "32")]
fn test_parse_offset_64_large() {
let section = Section::with_endian(Endian::Little).L64(0x0123_4567_89ab_cdef);
let buf = section.get_contents().unwrap();
let input = &mut EndianSlice::new(&buf, LittleEndian);
match input.read_offset(Format::Dwarf64) {
Err(Error::UnsupportedOffset) => assert!(true),
otherwise => panic!("Unexpected result: {:?}", otherwise),
};
}
}