//! Functions for parsing DWARF `.debug_info` and `.debug_types` sections. use core::cell::Cell; use core::ops::{Range, RangeFrom, RangeTo}; use core::{u16, u8}; use crate::common::{ DebugAbbrevOffset, DebugAddrBase, DebugAddrIndex, DebugInfoOffset, DebugLineOffset, DebugLineStrOffset, DebugLocListsBase, DebugLocListsIndex, DebugMacinfoOffset, DebugMacroOffset, DebugRngListsBase, DebugRngListsIndex, DebugStrOffset, DebugStrOffsetsBase, DebugStrOffsetsIndex, DebugTypeSignature, DebugTypesOffset, DwoId, Encoding, Format, LocationListsOffset, RawRangeListsOffset, SectionId, UnitSectionOffset, }; use crate::constants; use crate::endianity::Endianity; use crate::read::abbrev::get_attribute_size; use crate::read::{ Abbreviation, Abbreviations, AttributeSpecification, DebugAbbrev, DebugStr, EndianSlice, Error, Expression, Reader, ReaderOffset, Result, Section, UnitOffset, }; impl DebugTypesOffset { /// Convert an offset to be relative to the start of the given unit, /// instead of relative to the start of the .debug_types section. /// Returns `None` if the offset is not within the unit entries. pub fn to_unit_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_types_offset()?; let offset = UnitOffset(self.0.checked_sub(unit_offset.0)?); if !unit.is_valid_offset(offset) { return None; } Some(offset) } } impl DebugInfoOffset { /// Convert an offset to be relative to the start of the given unit, /// instead of relative to the start of the .debug_info section. /// Returns `None` if the offset is not within this unit entries. pub fn to_unit_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_info_offset()?; let offset = UnitOffset(self.0.checked_sub(unit_offset.0)?); if !unit.is_valid_offset(offset) { return None; } Some(offset) } } impl UnitOffset { /// Convert an offset to be relative to the start of the .debug_info section, /// instead of relative to the start of the given unit. Returns None if the /// provided unit lives in the .debug_types section. pub fn to_debug_info_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_info_offset()?; Some(DebugInfoOffset(unit_offset.0 + self.0)) } /// Convert an offset to be relative to the start of the .debug_types section, /// instead of relative to the start of the given unit. Returns None if the /// provided unit lives in the .debug_info section. pub fn to_debug_types_offset(&self, unit: &UnitHeader) -> Option> where R: Reader, { let unit_offset = unit.offset().as_debug_types_offset()?; Some(DebugTypesOffset(unit_offset.0 + self.0)) } } /// The `DebugInfo` struct represents the DWARF debugging information found in /// the `.debug_info` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugInfo { debug_info_section: R, } impl<'input, Endian> DebugInfo> where Endian: Endianity, { /// Construct a new `DebugInfo` instance from the data in the `.debug_info` /// section. /// /// It is the caller's responsibility to read the `.debug_info` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugInfo, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_info_section_somehow = || &buf; /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); /// ``` pub fn new(debug_info_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_info_section, endian)) } } impl DebugInfo { /// Iterate the units in this `.debug_info` section. /// /// ``` /// use gimli::{DebugInfo, LittleEndian}; /// /// # let buf = []; /// # let read_debug_info_section_somehow = || &buf; /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); /// /// let mut iter = debug_info.units(); /// while let Some(unit) = iter.next().unwrap() { /// println!("unit's length is {}", unit.unit_length()); /// } /// ``` /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn units(&self) -> DebugInfoUnitHeadersIter { DebugInfoUnitHeadersIter { input: self.debug_info_section.clone(), offset: DebugInfoOffset(R::Offset::from_u8(0)), } } /// Get the UnitHeader located at offset from this .debug_info section. /// /// pub fn header_from_offset(&self, offset: DebugInfoOffset) -> Result> { let input = &mut self.debug_info_section.clone(); input.skip(offset.0)?; parse_unit_header(input, offset.into()) } } impl DebugInfo { /// Create a `DebugInfo` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// ## Example Usage /// /// ```rust,no_run /// # let load_section = || unimplemented!(); /// // Read the DWARF section into a `Vec` with whatever object loader you're using. /// let owned_section: gimli::DebugInfo> = load_section(); /// // Create a reference to the DWARF section. /// let section = owned_section.borrow(|section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// ``` pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugInfo where F: FnMut(&'a T) -> R, { borrow(&self.debug_info_section).into() } } impl Section for DebugInfo { fn id() -> SectionId { SectionId::DebugInfo } fn reader(&self) -> &R { &self.debug_info_section } } impl From for DebugInfo { fn from(debug_info_section: R) -> Self { DebugInfo { debug_info_section } } } /// An iterator over the units of a .debug_info section. /// /// See the [documentation on /// `DebugInfo::units`](./struct.DebugInfo.html#method.units) for more detail. #[derive(Clone, Debug)] pub struct DebugInfoUnitHeadersIter { input: R, offset: DebugInfoOffset, } impl DebugInfoUnitHeadersIter { /// Advance the iterator to the next unit header. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { Ok(None) } else { let len = self.input.len(); match parse_unit_header(&mut self.input, self.offset.into()) { Ok(header) => { self.offset.0 += len - self.input.len(); Ok(Some(header)) } Err(e) => { self.input.empty(); Err(e) } } } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for DebugInfoUnitHeadersIter { type Item = UnitHeader; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { DebugInfoUnitHeadersIter::next(self) } } /// Parse the unit type from the unit header. fn parse_unit_type(input: &mut R) -> Result { let val = input.read_u8()?; Ok(constants::DwUt(val)) } /// Parse the `debug_abbrev_offset` in the compilation unit header. fn parse_debug_abbrev_offset( input: &mut R, format: Format, ) -> Result> { input.read_offset(format).map(DebugAbbrevOffset) } /// Parse the `debug_info_offset` in the arange header. pub(crate) fn parse_debug_info_offset( input: &mut R, format: Format, ) -> Result> { input.read_offset(format).map(DebugInfoOffset) } /// This enum specifies the type of the unit and any type /// specific data carried in the header (e.g. the type /// signature/type offset of a type unit). #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UnitType where Offset: ReaderOffset, { /// In DWARF5, a unit with type `DW_UT_compile`. In previous DWARF versions, /// any unit appearing in the .debug_info section. Compilation, /// In DWARF5, a unit with type `DW_UT_type`. In DWARF4, any unit appearing /// in the .debug_types section. Type { /// The unique type signature for this type unit. type_signature: DebugTypeSignature, /// The offset within this type unit where the type is defined. type_offset: UnitOffset, }, /// A unit with type `DW_UT_partial`. The root DIE of this unit should be a /// `DW_TAG_partial_unit`. Partial, /// A unit with type `DW_UT_skeleton`. The enclosed dwo_id can be used to /// link this with the corresponding `SplitCompilation` unit in a dwo file. /// NB: The non-standard GNU split DWARF extension to DWARF 4 will instead /// be a `Compilation` unit with the dwo_id present as an attribute on the /// root DIE. Skeleton(DwoId), /// A unit with type `DW_UT_split_compile`. The enclosed dwo_id can be used to /// link this with the corresponding `Skeleton` unit in the original binary. /// NB: The non-standard GNU split DWARF extension to DWARF 4 will instead /// be a `Compilation` unit with the dwo_id present as an attribute on the /// root DIE. SplitCompilation(DwoId), /// A unit with type `DW_UT_split_type`. A split type unit is identical to a /// conventional type unit except for the section in which it appears. SplitType { /// The unique type signature for this type unit. type_signature: DebugTypeSignature, /// The offset within this type unit where the type is defined. type_offset: UnitOffset, }, } impl UnitType where Offset: ReaderOffset, { // TODO: This will be used by the DWARF writing code once it // supports unit types other than simple compilation units. #[allow(unused)] pub(crate) fn dw_ut(&self) -> constants::DwUt { match self { UnitType::Compilation => constants::DW_UT_compile, UnitType::Type { .. } => constants::DW_UT_type, UnitType::Partial => constants::DW_UT_partial, UnitType::Skeleton(_) => constants::DW_UT_skeleton, UnitType::SplitCompilation(_) => constants::DW_UT_split_compile, UnitType::SplitType { .. } => constants::DW_UT_split_type, } } } /// The common fields for the headers of compilation units and /// type units. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct UnitHeader::Offset> where R: Reader, Offset: ReaderOffset, { encoding: Encoding, unit_length: Offset, unit_type: UnitType, debug_abbrev_offset: DebugAbbrevOffset, unit_offset: UnitSectionOffset, entries_buf: R, } /// Static methods. impl UnitHeader where R: Reader, Offset: ReaderOffset, { /// Construct a new `UnitHeader`. pub fn new( encoding: Encoding, unit_length: Offset, unit_type: UnitType, debug_abbrev_offset: DebugAbbrevOffset, unit_offset: UnitSectionOffset, entries_buf: R, ) -> Self { UnitHeader { encoding, unit_length, unit_type, debug_abbrev_offset, unit_offset, entries_buf, } } } /// Instance methods. impl UnitHeader where R: Reader, Offset: ReaderOffset, { /// Get the offset of this unit within its section. pub fn offset(&self) -> UnitSectionOffset { self.unit_offset } /// Return the serialized size of the common unit header for the given /// DWARF format. pub fn size_of_header(&self) -> usize { let unit_length_size = self.encoding.format.initial_length_size() as usize; let version_size = 2; let debug_abbrev_offset_size = self.encoding.format.word_size() as usize; let address_size_size = 1; let unit_type_size = if self.encoding.version == 5 { 1 } else { 0 }; let type_specific_size = match self.unit_type { UnitType::Compilation | UnitType::Partial => 0, UnitType::Type { .. } | UnitType::SplitType { .. } => { let type_signature_size = 8; let type_offset_size = self.encoding.format.word_size() as usize; type_signature_size + type_offset_size } UnitType::Skeleton(_) | UnitType::SplitCompilation(_) => 8, }; unit_length_size + version_size + debug_abbrev_offset_size + address_size_size + unit_type_size + type_specific_size } /// Get the length of the debugging info for this compilation unit, not /// including the byte length of the encoded length itself. pub fn unit_length(&self) -> Offset { self.unit_length } /// Get the length of the debugging info for this compilation unit, /// including the byte length of the encoded length itself. pub fn length_including_self(&self) -> Offset { Offset::from_u8(self.format().initial_length_size()) + self.unit_length } /// Return the encoding parameters for this unit. pub fn encoding(&self) -> Encoding { self.encoding } /// Get the DWARF version of the debugging info for this compilation unit. pub fn version(&self) -> u16 { self.encoding.version } /// Get the UnitType of this unit. pub fn type_(&self) -> UnitType { self.unit_type } /// The offset into the `.debug_abbrev` section for this compilation unit's /// debugging information entries' abbreviations. pub fn debug_abbrev_offset(&self) -> DebugAbbrevOffset { self.debug_abbrev_offset } /// The size of addresses (in bytes) in this compilation unit. pub fn address_size(&self) -> u8 { self.encoding.address_size } /// Whether this compilation unit is encoded in 64- or 32-bit DWARF. pub fn format(&self) -> Format { self.encoding.format } /// The serialized size of the header for this compilation unit. pub fn header_size(&self) -> Offset { self.length_including_self() - self.entries_buf.len() } pub(crate) fn is_valid_offset(&self, offset: UnitOffset) -> bool { let size_of_header = self.header_size(); if offset.0 < size_of_header { return false; } let relative_to_entries_buf = offset.0 - size_of_header; relative_to_entries_buf < self.entries_buf.len() } /// Get the underlying bytes for the supplied range. pub fn range(&self, idx: Range>) -> Result { if !self.is_valid_offset(idx.start) { return Err(Error::OffsetOutOfBounds); } if !self.is_valid_offset(idx.end) { return Err(Error::OffsetOutOfBounds); } assert!(idx.start <= idx.end); let size_of_header = self.header_size(); let start = idx.start.0 - size_of_header; let end = idx.end.0 - size_of_header; let mut input = self.entries_buf.clone(); input.skip(start)?; input.truncate(end - start)?; Ok(input) } /// Get the underlying bytes for the supplied range. pub fn range_from(&self, idx: RangeFrom>) -> Result { if !self.is_valid_offset(idx.start) { return Err(Error::OffsetOutOfBounds); } let start = idx.start.0 - self.header_size(); let mut input = self.entries_buf.clone(); input.skip(start)?; Ok(input) } /// Get the underlying bytes for the supplied range. pub fn range_to(&self, idx: RangeTo>) -> Result { if !self.is_valid_offset(idx.end) { return Err(Error::OffsetOutOfBounds); } let end = idx.end.0 - self.header_size(); let mut input = self.entries_buf.clone(); input.truncate(end)?; Ok(input) } /// Read the `DebuggingInformationEntry` at the given offset. pub fn entry<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: UnitOffset, ) -> Result> { let mut input = self.range_from(offset..)?; let entry = DebuggingInformationEntry::parse(&mut input, self, abbreviations)?; entry.ok_or(Error::NoEntryAtGivenOffset) } /// Navigate this unit's `DebuggingInformationEntry`s. pub fn entries<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, ) -> EntriesCursor<'abbrev, 'me, R> { EntriesCursor { unit: self, input: self.entries_buf.clone(), abbreviations, cached_current: None, delta_depth: 0, } } /// Navigate this compilation unit's `DebuggingInformationEntry`s /// starting at the given offset. pub fn entries_at_offset<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: UnitOffset, ) -> Result> { let input = self.range_from(offset..)?; Ok(EntriesCursor { unit: self, input, abbreviations, cached_current: None, delta_depth: 0, }) } /// Navigate this unit's `DebuggingInformationEntry`s as a tree /// starting at the given offset. pub fn entries_tree<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: Option>, ) -> Result> { let input = match offset { Some(offset) => self.range_from(offset..)?, None => self.entries_buf.clone(), }; Ok(EntriesTree::new(input, self, abbreviations)) } /// Read the raw data that defines the Debugging Information Entries. pub fn entries_raw<'me, 'abbrev>( &'me self, abbreviations: &'abbrev Abbreviations, offset: Option>, ) -> Result> { let input = match offset { Some(offset) => self.range_from(offset..)?, None => self.entries_buf.clone(), }; Ok(EntriesRaw { input, unit: self, abbreviations, depth: 0, }) } /// Parse this unit's abbreviations. pub fn abbreviations(&self, debug_abbrev: &DebugAbbrev) -> Result { debug_abbrev.abbreviations(self.debug_abbrev_offset()) } } /// Parse a unit header. fn parse_unit_header( input: &mut R, unit_offset: UnitSectionOffset, ) -> Result> where R: Reader, Offset: ReaderOffset, { let (unit_length, format) = input.read_initial_length()?; let mut rest = input.split(unit_length)?; let version = rest.read_u16()?; let abbrev_offset; let address_size; let unit_type; // DWARF 1 was very different, and is obsolete, so isn't supported by this // reader. if 2 <= version && version <= 4 { abbrev_offset = parse_debug_abbrev_offset(&mut rest, format)?; address_size = rest.read_u8()?; // Before DWARF5, all units in the .debug_info section are compilation // units, and all units in the .debug_types section are type units. unit_type = match unit_offset { UnitSectionOffset::DebugInfoOffset(_) => constants::DW_UT_compile, UnitSectionOffset::DebugTypesOffset(_) => constants::DW_UT_type, }; } else if version == 5 { unit_type = parse_unit_type(&mut rest)?; address_size = rest.read_u8()?; abbrev_offset = parse_debug_abbrev_offset(&mut rest, format)?; } else { return Err(Error::UnknownVersion(u64::from(version))); } let encoding = Encoding { format, version, address_size, }; // Parse any data specific to this type of unit. let unit_type = match unit_type { constants::DW_UT_compile => UnitType::Compilation, constants::DW_UT_type => { let type_signature = parse_type_signature(&mut rest)?; let type_offset = parse_type_offset(&mut rest, format)?; UnitType::Type { type_signature, type_offset, } } constants::DW_UT_partial => UnitType::Partial, constants::DW_UT_skeleton => { let dwo_id = parse_dwo_id(&mut rest)?; UnitType::Skeleton(dwo_id) } constants::DW_UT_split_compile => { let dwo_id = parse_dwo_id(&mut rest)?; UnitType::SplitCompilation(dwo_id) } constants::DW_UT_split_type => { let type_signature = parse_type_signature(&mut rest)?; let type_offset = parse_type_offset(&mut rest, format)?; UnitType::SplitType { type_signature, type_offset, } } _ => return Err(Error::UnsupportedUnitType), }; Ok(UnitHeader::new( encoding, unit_length, unit_type, abbrev_offset, unit_offset, rest, )) } /// Parse a dwo_id from a header fn parse_dwo_id(input: &mut R) -> Result { Ok(DwoId(input.read_u64()?)) } /// A Debugging Information Entry (DIE). /// /// DIEs have a set of attributes and optionally have children DIEs as well. #[derive(Clone, Debug)] pub struct DebuggingInformationEntry<'abbrev, 'unit, R, Offset = ::Offset> where R: Reader, Offset: ReaderOffset, { offset: UnitOffset, attrs_slice: R, attrs_len: Cell>, abbrev: &'abbrev Abbreviation, unit: &'unit UnitHeader, } impl<'abbrev, 'unit, R, Offset> DebuggingInformationEntry<'abbrev, 'unit, R, Offset> where R: Reader, Offset: ReaderOffset, { /// Construct a new `DebuggingInformationEntry`. pub fn new( offset: UnitOffset, attrs_slice: R, abbrev: &'abbrev Abbreviation, unit: &'unit UnitHeader, ) -> Self { DebuggingInformationEntry { offset, attrs_slice, attrs_len: Cell::new(None), abbrev, unit, } } /// Get this entry's code. pub fn code(&self) -> u64 { self.abbrev.code() } /// Get this entry's offset. pub fn offset(&self) -> UnitOffset { self.offset } /// Get this entry's `DW_TAG_whatever` tag. /// /// ``` /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 12 /// # 0x0c, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # ]; /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_no /// # 0x00, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); /// # let unit = debug_info.units().next().unwrap().unwrap(); /// # let abbrevs = unit.abbreviations(&debug_abbrev).unwrap(); /// # let mut cursor = unit.entries(&abbrevs); /// # let (_, entry) = cursor.next_dfs().unwrap().unwrap(); /// # let mut get_some_entry = || entry; /// let entry = get_some_entry(); /// /// match entry.tag() { /// gimli::DW_TAG_subprogram => /// println!("this entry contains debug info about a function"), /// gimli::DW_TAG_inlined_subroutine => /// println!("this entry contains debug info about a particular instance of inlining"), /// gimli::DW_TAG_variable => /// println!("this entry contains debug info about a local variable"), /// gimli::DW_TAG_formal_parameter => /// println!("this entry contains debug info about a function parameter"), /// otherwise => /// println!("this entry is some other kind of data: {:?}", otherwise), /// }; /// ``` pub fn tag(&self) -> constants::DwTag { self.abbrev.tag() } /// Return true if this entry's type can have children, false otherwise. pub fn has_children(&self) -> bool { self.abbrev.has_children() } /// Iterate over this entry's set of attributes. /// /// ``` /// use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// /// // Read the `.debug_info` section. /// /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 12 /// # 0x0c, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # ]; /// # let read_debug_info_section_somehow = || &info_buf; /// let debug_info = DebugInfo::new(read_debug_info_section_somehow(), LittleEndian); /// /// // Get the data about the first compilation unit out of the `.debug_info`. /// /// let unit = debug_info.units().next() /// .expect("Should have at least one compilation unit") /// .expect("and it should parse ok"); /// /// // Read the `.debug_abbrev` section and parse the /// // abbreviations for our compilation unit. /// /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_no /// # 0x00, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let read_debug_abbrev_section_somehow = || &abbrev_buf; /// let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); /// let abbrevs = unit.abbreviations(&debug_abbrev).unwrap(); /// /// // Get the first entry from that compilation unit. /// /// let mut cursor = unit.entries(&abbrevs); /// let (_, entry) = cursor.next_dfs() /// .expect("Should parse next entry") /// .expect("Should have at least one entry"); /// /// // Finally, print the first entry's attributes. /// /// let mut attrs = entry.attrs(); /// while let Some(attr) = attrs.next().unwrap() { /// println!("Attribute name = {:?}", attr.name()); /// println!("Attribute value = {:?}", attr.value()); /// } /// ``` /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn attrs<'me>(&'me self) -> AttrsIter<'abbrev, 'me, 'unit, R> { AttrsIter { input: self.attrs_slice.clone(), attributes: self.abbrev.attributes(), entry: self, } } /// Find the first attribute in this entry which has the given name, /// and return it. Returns `Ok(None)` if no attribute is found. pub fn attr(&self, name: constants::DwAt) -> Result>> { let mut attrs = self.attrs(); while let Some(attr) = attrs.next()? { if attr.name() == name { return Ok(Some(attr)); } } Ok(None) } /// Find the first attribute in this entry which has the given name, /// and return its raw value. Returns `Ok(None)` if no attribute is found. pub fn attr_value_raw(&self, name: constants::DwAt) -> Result>> { self.attr(name) .map(|attr| attr.map(|attr| attr.raw_value())) } /// Find the first attribute in this entry which has the given name, /// and return its normalized value. Returns `Ok(None)` if no /// attribute is found. pub fn attr_value(&self, name: constants::DwAt) -> Result>> { self.attr(name).map(|attr| attr.map(|attr| attr.value())) } /// Return the input buffer after the last attribute. #[allow(clippy::inline_always)] #[inline(always)] fn after_attrs(&self) -> Result { if let Some(attrs_len) = self.attrs_len.get() { let mut input = self.attrs_slice.clone(); input.skip(attrs_len)?; Ok(input) } else { let mut attrs = self.attrs(); while let Some(_) = attrs.next()? {} Ok(attrs.input) } } /// Use the `DW_AT_sibling` attribute to find the input buffer for the /// next sibling. Returns `None` if the attribute is missing or invalid. fn sibling(&self) -> Option { let attr = self.attr_value(constants::DW_AT_sibling); if let Ok(Some(AttributeValue::UnitRef(offset))) = attr { if offset.0 > self.offset.0 { if let Ok(input) = self.unit.range_from(offset..) { return Some(input); } } } None } /// Parse an entry. Returns `Ok(None)` for null entries. #[allow(clippy::inline_always)] #[inline(always)] fn parse( input: &mut R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, ) -> Result> { let offset = unit.header_size() + input.offset_from(&unit.entries_buf); let code = input.read_uleb128()?; if code == 0 { return Ok(None); }; let abbrev = abbreviations.get(code).ok_or(Error::UnknownAbbreviation)?; Ok(Some(DebuggingInformationEntry { offset: UnitOffset(offset), attrs_slice: input.clone(), attrs_len: Cell::new(None), abbrev, unit, })) } } /// The value of an attribute in a `DebuggingInformationEntry`. // // Set the discriminant size so that all variants use the same alignment // for their data. This gives better code generation in `parse_attribute`. #[repr(u64)] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum AttributeValue::Offset> where R: Reader, Offset: ReaderOffset, { /// "Refers to some location in the address space of the described program." Addr(u64), /// A slice of an arbitrary number of bytes. Block(R), /// A one byte constant data value. How to interpret the byte depends on context. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data1(u8), /// A two byte constant data value. How to interpret the bytes depends on context. /// /// These bytes have been converted from `R::Endian`. This may need to be reversed /// if this was not required. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data2(u16), /// A four byte constant data value. How to interpret the bytes depends on context. /// /// These bytes have been converted from `R::Endian`. This may need to be reversed /// if this was not required. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data4(u32), /// An eight byte constant data value. How to interpret the bytes depends on context. /// /// These bytes have been converted from `R::Endian`. This may need to be reversed /// if this was not required. /// /// From section 7 of the standard: "Depending on context, it may be a /// signed integer, an unsigned integer, a floating-point constant, or /// anything else." Data8(u64), /// A signed integer constant. Sdata(i64), /// An unsigned integer constant. Udata(u64), /// "The information bytes contain a DWARF expression (see Section 2.5) or /// location description (see Section 2.6)." Exprloc(Expression), /// A boolean that indicates presence or absence of the attribute. Flag(bool), /// An offset into another section. Which section this is an offset into /// depends on context. SecOffset(Offset), /// An offset to a set of addresses in the `.debug_addr` section. DebugAddrBase(DebugAddrBase), /// An index into a set of addresses in the `.debug_addr` section. DebugAddrIndex(DebugAddrIndex), /// An offset into the current compilation unit. UnitRef(UnitOffset), /// An offset into the current `.debug_info` section, but possibly a /// different compilation unit from the current one. DebugInfoRef(DebugInfoOffset), /// An offset into the `.debug_info` section of the supplementary object file. DebugInfoRefSup(DebugInfoOffset), /// An offset into the `.debug_line` section. DebugLineRef(DebugLineOffset), /// An offset into either the `.debug_loc` section or the `.debug_loclists` section. LocationListsRef(LocationListsOffset), /// An offset to a set of offsets in the `.debug_loclists` section. DebugLocListsBase(DebugLocListsBase), /// An index into a set of offsets in the `.debug_loclists` section. DebugLocListsIndex(DebugLocListsIndex), /// An offset into the `.debug_macinfo` section. DebugMacinfoRef(DebugMacinfoOffset), /// An offset into the `.debug_macro` section. DebugMacroRef(DebugMacroOffset), /// An offset into the `.debug_ranges` section. RangeListsRef(RawRangeListsOffset), /// An offset to a set of offsets in the `.debug_rnglists` section. DebugRngListsBase(DebugRngListsBase), /// An index into a set of offsets in the `.debug_rnglists` section. DebugRngListsIndex(DebugRngListsIndex), /// A type signature. DebugTypesRef(DebugTypeSignature), /// An offset into the `.debug_str` section. DebugStrRef(DebugStrOffset), /// An offset into the `.debug_str` section of the supplementary object file. DebugStrRefSup(DebugStrOffset), /// An offset to a set of entries in the `.debug_str_offsets` section. DebugStrOffsetsBase(DebugStrOffsetsBase), /// An index into a set of entries in the `.debug_str_offsets` section. DebugStrOffsetsIndex(DebugStrOffsetsIndex), /// An offset into the `.debug_line_str` section. DebugLineStrRef(DebugLineStrOffset), /// A slice of bytes representing a string. Does not include a final null byte. /// Not guaranteed to be UTF-8 or anything like that. String(R), /// The value of a `DW_AT_encoding` attribute. Encoding(constants::DwAte), /// The value of a `DW_AT_decimal_sign` attribute. DecimalSign(constants::DwDs), /// The value of a `DW_AT_endianity` attribute. Endianity(constants::DwEnd), /// The value of a `DW_AT_accessibility` attribute. Accessibility(constants::DwAccess), /// The value of a `DW_AT_visibility` attribute. Visibility(constants::DwVis), /// The value of a `DW_AT_virtuality` attribute. Virtuality(constants::DwVirtuality), /// The value of a `DW_AT_language` attribute. Language(constants::DwLang), /// The value of a `DW_AT_address_class` attribute. AddressClass(constants::DwAddr), /// The value of a `DW_AT_identifier_case` attribute. IdentifierCase(constants::DwId), /// The value of a `DW_AT_calling_convention` attribute. CallingConvention(constants::DwCc), /// The value of a `DW_AT_inline` attribute. Inline(constants::DwInl), /// The value of a `DW_AT_ordering` attribute. Ordering(constants::DwOrd), /// An index into the filename entries from the line number information /// table for the compilation unit containing this value. FileIndex(u64), /// An implementation-defined identifier uniquely identifying a compilation /// unit. DwoId(DwoId), } /// An attribute in a `DebuggingInformationEntry`, consisting of a name and /// associated value. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Attribute { name: constants::DwAt, value: AttributeValue, } impl Attribute { /// Get this attribute's name. pub fn name(&self) -> constants::DwAt { self.name } /// Get this attribute's raw value. pub fn raw_value(&self) -> AttributeValue { self.value.clone() } /// Get this attribute's normalized value. /// /// Attribute values can potentially be encoded in multiple equivalent forms, /// and may have special meaning depending on the attribute name. This method /// converts the attribute value to a normalized form based on the attribute /// name. /// /// See "Table 7.5: Attribute encodings" and "Table 7.6: Attribute form encodings". #[allow(clippy::cyclomatic_complexity)] #[allow(clippy::match_same_arms)] pub fn value(&self) -> AttributeValue { // Table 7.5 shows the possible attribute classes for each name. // Table 7.6 shows the possible attribute classes for each form. // For each attribute name, we need to match on the form, and // convert it to one of the classes that is allowed for both // the name and the form. // // The individual class conversions rarely vary for each name, // so for each class conversion we define a macro that matches // on the allowed forms for that class. // // For some classes, we don't need to do any conversion, so their // macro is empty. In the future we may want to fill them in to // provide strict checking of the forms for each class. For now, // they simply provide a way to document the allowed classes for // each name. // DW_FORM_addr // DW_FORM_addrx // DW_FORM_addrx1 // DW_FORM_addrx2 // DW_FORM_addrx3 // DW_FORM_addrx4 macro_rules! address { () => {}; } // DW_FORM_sec_offset macro_rules! addrptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugAddrBase(DebugAddrBase(offset)); } }; } // DW_FORM_block // DW_FORM_block1 // DW_FORM_block2 // DW_FORM_block4 macro_rules! block { () => {}; } // DW_FORM_sdata // DW_FORM_udata // DW_FORM_data1 // DW_FORM_data2 // DW_FORM_data4 // DW_FORM_data8 // DW_FORM_data16 // DW_FORM_implicit_const macro_rules! constant { ($value:ident, $variant:ident) => { if let Some(value) = self.$value() { return AttributeValue::$variant(value); } }; ($value:ident, $variant:ident, $constant:ident) => { if let Some(value) = self.$value() { return AttributeValue::$variant(constants::$constant(value)); } }; } // DW_FORM_exprloc macro_rules! exprloc { () => { if let Some(value) = self.exprloc_value() { return AttributeValue::Exprloc(value); } }; } // DW_FORM_flag // DW_FORM_flag_present macro_rules! flag { () => {}; } // DW_FORM_sec_offset macro_rules! lineptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugLineRef(DebugLineOffset(offset)); } }; } // This also covers `loclist` in DWARF version 5. // DW_FORM_sec_offset // DW_FORM_loclistx macro_rules! loclistptr { () => { // DebugLocListsIndex is also an allowed form in DWARF version 5. if let Some(offset) = self.offset_value() { return AttributeValue::LocationListsRef(LocationListsOffset(offset)); } }; } // DW_FORM_sec_offset macro_rules! loclistsptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugLocListsBase(DebugLocListsBase(offset)); } }; } // DWARF version <= 4. // DW_FORM_sec_offset macro_rules! macinfoptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugMacinfoRef(DebugMacinfoOffset(offset)); } }; } // DWARF version >= 5. // DW_FORM_sec_offset macro_rules! macroptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugMacroRef(DebugMacroOffset(offset)); } }; } // DW_FORM_ref_addr // DW_FORM_ref1 // DW_FORM_ref2 // DW_FORM_ref4 // DW_FORM_ref8 // DW_FORM_ref_udata // DW_FORM_ref_sig8 // DW_FORM_ref_sup4 // DW_FORM_ref_sup8 macro_rules! reference { () => {}; } // This also covers `rnglist` in DWARF version 5. // DW_FORM_sec_offset // DW_FORM_rnglistx macro_rules! rangelistptr { () => { // DebugRngListsIndex is also an allowed form in DWARF version 5. if let Some(offset) = self.offset_value() { return AttributeValue::RangeListsRef(RawRangeListsOffset(offset)); } }; } // DW_FORM_sec_offset macro_rules! rnglistsptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugRngListsBase(DebugRngListsBase(offset)); } }; } // DW_FORM_string // DW_FORM_strp // DW_FORM_strx // DW_FORM_strx1 // DW_FORM_strx2 // DW_FORM_strx3 // DW_FORM_strx4 // DW_FORM_strp_sup // DW_FORM_line_strp macro_rules! string { () => {}; } // DW_FORM_sec_offset macro_rules! stroffsetsptr { () => { if let Some(offset) = self.offset_value() { return AttributeValue::DebugStrOffsetsBase(DebugStrOffsetsBase(offset)); } }; } // This isn't a separate form but it's useful to distinguish it from a generic udata. macro_rules! dwoid { () => { if let Some(value) = self.udata_value() { return AttributeValue::DwoId(DwoId(value)); } }; } // Perform the allowed class conversions for each attribute name. match self.name { constants::DW_AT_sibling => { reference!(); } constants::DW_AT_location => { exprloc!(); loclistptr!(); } constants::DW_AT_name => { string!(); } constants::DW_AT_ordering => { constant!(u8_value, Ordering, DwOrd); } constants::DW_AT_byte_size | constants::DW_AT_bit_offset | constants::DW_AT_bit_size => { constant!(udata_value, Udata); exprloc!(); reference!(); } constants::DW_AT_stmt_list => { lineptr!(); } constants::DW_AT_low_pc => { address!(); } constants::DW_AT_high_pc => { address!(); constant!(udata_value, Udata); } constants::DW_AT_language => { constant!(u16_value, Language, DwLang); } constants::DW_AT_discr => { reference!(); } constants::DW_AT_discr_value => { // constant: depends on type of DW_TAG_variant_part, // so caller must normalize. } constants::DW_AT_visibility => { constant!(u8_value, Visibility, DwVis); } constants::DW_AT_import => { reference!(); } constants::DW_AT_string_length => { exprloc!(); loclistptr!(); reference!(); } constants::DW_AT_common_reference => { reference!(); } constants::DW_AT_comp_dir => { string!(); } constants::DW_AT_const_value => { // TODO: constant: sign depends on DW_AT_type. block!(); string!(); } constants::DW_AT_containing_type => { reference!(); } constants::DW_AT_default_value => { // TODO: constant: sign depends on DW_AT_type. reference!(); flag!(); } constants::DW_AT_inline => { constant!(u8_value, Inline, DwInl); } constants::DW_AT_is_optional => { flag!(); } constants::DW_AT_lower_bound => { // TODO: constant: sign depends on DW_AT_type. exprloc!(); reference!(); } constants::DW_AT_producer => { string!(); } constants::DW_AT_prototyped => { flag!(); } constants::DW_AT_return_addr => { exprloc!(); loclistptr!(); } constants::DW_AT_start_scope => { // TODO: constant rangelistptr!(); } constants::DW_AT_bit_stride => { constant!(udata_value, Udata); exprloc!(); reference!(); } constants::DW_AT_upper_bound => { // TODO: constant: sign depends on DW_AT_type. exprloc!(); reference!(); } constants::DW_AT_abstract_origin => { reference!(); } constants::DW_AT_accessibility => { constant!(u8_value, Accessibility, DwAccess); } constants::DW_AT_address_class => { constant!(udata_value, AddressClass, DwAddr); } constants::DW_AT_artificial => { flag!(); } constants::DW_AT_base_types => { reference!(); } constants::DW_AT_calling_convention => { constant!(u8_value, CallingConvention, DwCc); } constants::DW_AT_count => { // TODO: constant exprloc!(); reference!(); } constants::DW_AT_data_member_location => { // Constants must be handled before loclistptr so that DW_FORM_data4/8 // are correctly interpreted for DWARF version 4+. constant!(udata_value, Udata); exprloc!(); loclistptr!(); } constants::DW_AT_decl_column => { constant!(udata_value, Udata); } constants::DW_AT_decl_file => { constant!(udata_value, FileIndex); } constants::DW_AT_decl_line => { constant!(udata_value, Udata); } constants::DW_AT_declaration => { flag!(); } constants::DW_AT_discr_list => { block!(); } constants::DW_AT_encoding => { constant!(u8_value, Encoding, DwAte); } constants::DW_AT_external => { flag!(); } constants::DW_AT_frame_base => { exprloc!(); loclistptr!(); } constants::DW_AT_friend => { reference!(); } constants::DW_AT_identifier_case => { constant!(u8_value, IdentifierCase, DwId); } constants::DW_AT_macro_info => { macinfoptr!(); } constants::DW_AT_namelist_item => { reference!(); } constants::DW_AT_priority => { reference!(); } constants::DW_AT_segment => { exprloc!(); loclistptr!(); } constants::DW_AT_specification => { reference!(); } constants::DW_AT_static_link => { exprloc!(); loclistptr!(); } constants::DW_AT_type => { reference!(); } constants::DW_AT_use_location => { exprloc!(); loclistptr!(); } constants::DW_AT_variable_parameter => { flag!(); } constants::DW_AT_virtuality => { constant!(u8_value, Virtuality, DwVirtuality); } constants::DW_AT_vtable_elem_location => { exprloc!(); loclistptr!(); } constants::DW_AT_allocated => { // TODO: constant exprloc!(); reference!(); } constants::DW_AT_associated => { // TODO: constant exprloc!(); reference!(); } constants::DW_AT_data_location => { exprloc!(); } constants::DW_AT_byte_stride => { constant!(udata_value, Udata); exprloc!(); reference!(); } constants::DW_AT_entry_pc => { // TODO: constant address!(); } constants::DW_AT_use_UTF8 => { flag!(); } constants::DW_AT_extension => { reference!(); } constants::DW_AT_ranges => { rangelistptr!(); } constants::DW_AT_trampoline => { address!(); flag!(); reference!(); string!(); } constants::DW_AT_call_column => { constant!(udata_value, Udata); } constants::DW_AT_call_file => { constant!(udata_value, FileIndex); } constants::DW_AT_call_line => { constant!(udata_value, Udata); } constants::DW_AT_description => { string!(); } constants::DW_AT_binary_scale => { // TODO: constant } constants::DW_AT_decimal_scale => { // TODO: constant } constants::DW_AT_small => { reference!(); } constants::DW_AT_decimal_sign => { constant!(u8_value, DecimalSign, DwDs); } constants::DW_AT_digit_count => { // TODO: constant } constants::DW_AT_picture_string => { string!(); } constants::DW_AT_mutable => { flag!(); } constants::DW_AT_threads_scaled => { flag!(); } constants::DW_AT_explicit => { flag!(); } constants::DW_AT_object_pointer => { reference!(); } constants::DW_AT_endianity => { constant!(u8_value, Endianity, DwEnd); } constants::DW_AT_elemental => { flag!(); } constants::DW_AT_pure => { flag!(); } constants::DW_AT_recursive => { flag!(); } constants::DW_AT_signature => { reference!(); } constants::DW_AT_main_subprogram => { flag!(); } constants::DW_AT_data_bit_offset => { // TODO: constant } constants::DW_AT_const_expr => { flag!(); } constants::DW_AT_enum_class => { flag!(); } constants::DW_AT_linkage_name => { string!(); } constants::DW_AT_string_length_bit_size => { // TODO: constant } constants::DW_AT_string_length_byte_size => { // TODO: constant } constants::DW_AT_rank => { // TODO: constant exprloc!(); } constants::DW_AT_str_offsets_base => { stroffsetsptr!(); } constants::DW_AT_addr_base | constants::DW_AT_GNU_addr_base => { addrptr!(); } constants::DW_AT_rnglists_base | constants::DW_AT_GNU_ranges_base => { rnglistsptr!(); } constants::DW_AT_dwo_name => { string!(); } constants::DW_AT_reference => { flag!(); } constants::DW_AT_rvalue_reference => { flag!(); } constants::DW_AT_macros => { macroptr!(); } constants::DW_AT_call_all_calls => { flag!(); } constants::DW_AT_call_all_source_calls => { flag!(); } constants::DW_AT_call_all_tail_calls => { flag!(); } constants::DW_AT_call_return_pc => { address!(); } constants::DW_AT_call_value => { exprloc!(); } constants::DW_AT_call_origin => { exprloc!(); } constants::DW_AT_call_parameter => { reference!(); } constants::DW_AT_call_pc => { address!(); } constants::DW_AT_call_tail_call => { flag!(); } constants::DW_AT_call_target => { exprloc!(); } constants::DW_AT_call_target_clobbered => { exprloc!(); } constants::DW_AT_call_data_location => { exprloc!(); } constants::DW_AT_call_data_value => { exprloc!(); } constants::DW_AT_noreturn => { flag!(); } constants::DW_AT_alignment => { // TODO: constant } constants::DW_AT_export_symbols => { flag!(); } constants::DW_AT_deleted => { flag!(); } constants::DW_AT_defaulted => { // TODO: constant } constants::DW_AT_loclists_base => { loclistsptr!(); } constants::DW_AT_GNU_dwo_id => { dwoid!(); } _ => {} } self.value.clone() } /// Try to convert this attribute's value to a u8. #[inline] pub fn u8_value(&self) -> Option { self.value.u8_value() } /// Try to convert this attribute's value to a u16. #[inline] pub fn u16_value(&self) -> Option { self.value.u16_value() } /// Try to convert this attribute's value to an unsigned integer. #[inline] pub fn udata_value(&self) -> Option { self.value.udata_value() } /// Try to convert this attribute's value to a signed integer. #[inline] pub fn sdata_value(&self) -> Option { self.value.sdata_value() } /// Try to convert this attribute's value to an offset. #[inline] pub fn offset_value(&self) -> Option { self.value.offset_value() } /// Try to convert this attribute's value to an expression or location buffer. /// /// Expressions and locations may be `DW_FORM_block*` or `DW_FORM_exprloc`. /// The standard doesn't mention `DW_FORM_block*` as a possible form, but /// it is encountered in practice. #[inline] pub fn exprloc_value(&self) -> Option> { self.value.exprloc_value() } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. #[inline] pub fn string_value(&self, debug_str: &DebugStr) -> Option { self.value.string_value(debug_str) } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, or a `DW_FORM_strp_sup` reference to an offset into a supplementary /// object file, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. #[inline] pub fn string_value_sup( &self, debug_str: &DebugStr, debug_str_sup: Option<&DebugStr>, ) -> Option { self.value.string_value_sup(debug_str, debug_str_sup) } } impl AttributeValue where R: Reader, Offset: ReaderOffset, { /// Try to convert this attribute's value to a u8. pub fn u8_value(&self) -> Option { if let Some(value) = self.udata_value() { if value <= u64::from(u8::MAX) { return Some(value as u8); } } None } /// Try to convert this attribute's value to a u16. pub fn u16_value(&self) -> Option { if let Some(value) = self.udata_value() { if value <= u64::from(u16::MAX) { return Some(value as u16); } } None } /// Try to convert this attribute's value to an unsigned integer. pub fn udata_value(&self) -> Option { Some(match *self { AttributeValue::Data1(data) => u64::from(data), AttributeValue::Data2(data) => u64::from(data), AttributeValue::Data4(data) => u64::from(data), AttributeValue::Data8(data) => data, AttributeValue::Udata(data) => data, AttributeValue::Sdata(data) => { if data < 0 { // Maybe we should emit a warning here return None; } data as u64 } _ => return None, }) } /// Try to convert this attribute's value to a signed integer. pub fn sdata_value(&self) -> Option { Some(match *self { AttributeValue::Data1(data) => i64::from(data as i8), AttributeValue::Data2(data) => i64::from(data as i16), AttributeValue::Data4(data) => i64::from(data as i32), AttributeValue::Data8(data) => data as i64, AttributeValue::Sdata(data) => data, AttributeValue::Udata(data) => { if data > i64::max_value() as u64 { // Maybe we should emit a warning here return None; } data as i64 } _ => return None, }) } /// Try to convert this attribute's value to an offset. pub fn offset_value(&self) -> Option { // While offsets will be DW_FORM_data4/8 in DWARF version 2/3, // these have already been converted to `SecOffset. if let AttributeValue::SecOffset(offset) = *self { Some(offset) } else { None } } /// Try to convert this attribute's value to an expression or location buffer. /// /// Expressions and locations may be `DW_FORM_block*` or `DW_FORM_exprloc`. /// The standard doesn't mention `DW_FORM_block*` as a possible form, but /// it is encountered in practice. pub fn exprloc_value(&self) -> Option> { Some(match *self { AttributeValue::Block(ref data) => Expression(data.clone()), AttributeValue::Exprloc(ref data) => data.clone(), _ => return None, }) } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. pub fn string_value(&self, debug_str: &DebugStr) -> Option { match *self { AttributeValue::String(ref string) => Some(string.clone()), AttributeValue::DebugStrRef(offset) => debug_str.get_str(offset).ok(), _ => None, } } /// Try to return this attribute's value as a string slice. /// /// If this attribute's value is either an inline `DW_FORM_string` string, /// or a `DW_FORM_strp` reference to an offset into the `.debug_str` /// section, or a `DW_FORM_strp_sup` reference to an offset into a supplementary /// object file, return the attribute's string value as `Some`. Other attribute /// value forms are returned as `None`. /// /// Warning: this function does not handle all possible string forms. /// Use `Dwarf::attr_string` instead. pub fn string_value_sup( &self, debug_str: &DebugStr, debug_str_sup: Option<&DebugStr>, ) -> Option { match *self { AttributeValue::String(ref string) => Some(string.clone()), AttributeValue::DebugStrRef(offset) => debug_str.get_str(offset).ok(), AttributeValue::DebugStrRefSup(offset) => { debug_str_sup.and_then(|s| s.get_str(offset).ok()) } _ => None, } } } fn length_u8_value(input: &mut R) -> Result { let len = input.read_u8().map(R::Offset::from_u8)?; input.split(len) } fn length_u16_value(input: &mut R) -> Result { let len = input.read_u16().map(R::Offset::from_u16)?; input.split(len) } fn length_u32_value(input: &mut R) -> Result { let len = input.read_u32().map(R::Offset::from_u32)?; input.split(len) } fn length_uleb128_value(input: &mut R) -> Result { let len = input.read_uleb128().and_then(R::Offset::from_u64)?; input.split(len) } // Return true if the given `name` can be a section offset in DWARF version 2/3. // This is required to correctly handle relocations. fn allow_section_offset(name: constants::DwAt, version: u16) -> bool { match name { constants::DW_AT_location | constants::DW_AT_stmt_list | constants::DW_AT_string_length | constants::DW_AT_return_addr | constants::DW_AT_start_scope | constants::DW_AT_frame_base | constants::DW_AT_macro_info | constants::DW_AT_macros | constants::DW_AT_segment | constants::DW_AT_static_link | constants::DW_AT_use_location | constants::DW_AT_vtable_elem_location | constants::DW_AT_ranges => true, constants::DW_AT_data_member_location => version == 2 || version == 3, _ => false, } } pub(crate) fn parse_attribute<'unit, R: Reader>( input: &mut R, encoding: Encoding, spec: AttributeSpecification, ) -> Result> { let mut form = spec.form(); loop { let value = match form { constants::DW_FORM_indirect => { let dynamic_form = input.read_uleb128_u16()?; form = constants::DwForm(dynamic_form); continue; } constants::DW_FORM_addr => { let addr = input.read_address(encoding.address_size)?; AttributeValue::Addr(addr) } constants::DW_FORM_block1 => { let block = length_u8_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_block2 => { let block = length_u16_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_block4 => { let block = length_u32_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_block => { let block = length_uleb128_value(input)?; AttributeValue::Block(block) } constants::DW_FORM_data1 => { let data = input.read_u8()?; AttributeValue::Data1(data) } constants::DW_FORM_data2 => { let data = input.read_u16()?; AttributeValue::Data2(data) } constants::DW_FORM_data4 => { // DWARF version 2/3 may use DW_FORM_data4/8 for section offsets. // Ensure we handle relocations here. if encoding.format == Format::Dwarf32 && allow_section_offset(spec.name(), encoding.version) { let offset = input.read_offset(Format::Dwarf32)?; AttributeValue::SecOffset(offset) } else { let data = input.read_u32()?; AttributeValue::Data4(data) } } constants::DW_FORM_data8 => { // DWARF version 2/3 may use DW_FORM_data4/8 for section offsets. // Ensure we handle relocations here. if encoding.format == Format::Dwarf64 && allow_section_offset(spec.name(), encoding.version) { let offset = input.read_offset(Format::Dwarf64)?; AttributeValue::SecOffset(offset) } else { let data = input.read_u64()?; AttributeValue::Data8(data) } } constants::DW_FORM_data16 => { let block = input.split(R::Offset::from_u8(16))?; AttributeValue::Block(block) } constants::DW_FORM_udata => { let data = input.read_uleb128()?; AttributeValue::Udata(data) } constants::DW_FORM_sdata => { let data = input.read_sleb128()?; AttributeValue::Sdata(data) } constants::DW_FORM_exprloc => { let block = length_uleb128_value(input)?; AttributeValue::Exprloc(Expression(block)) } constants::DW_FORM_flag => { let present = input.read_u8()?; AttributeValue::Flag(present != 0) } constants::DW_FORM_flag_present => { // FlagPresent is this weird compile time always true thing that // isn't actually present in the serialized DIEs, only in the abbreviation. AttributeValue::Flag(true) } constants::DW_FORM_sec_offset => { let offset = input.read_offset(encoding.format)?; AttributeValue::SecOffset(offset) } constants::DW_FORM_ref1 => { let reference = input.read_u8().map(R::Offset::from_u8)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref2 => { let reference = input.read_u16().map(R::Offset::from_u16)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref4 => { let reference = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref8 => { let reference = input.read_u64().and_then(R::Offset::from_u64)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref_udata => { let reference = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::UnitRef(UnitOffset(reference)) } constants::DW_FORM_ref_addr => { // This is an offset, but DWARF version 2 specifies that DW_FORM_ref_addr // has the same size as an address on the target system. This was changed // in DWARF version 3. let offset = if encoding.version == 2 { input.read_sized_offset(encoding.address_size)? } else { input.read_offset(encoding.format)? }; AttributeValue::DebugInfoRef(DebugInfoOffset(offset)) } constants::DW_FORM_ref_sig8 => { let signature = input.read_u64()?; AttributeValue::DebugTypesRef(DebugTypeSignature(signature)) } constants::DW_FORM_ref_sup4 => { let offset = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) } constants::DW_FORM_ref_sup8 => { let offset = input.read_u64().and_then(R::Offset::from_u64)?; AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) } constants::DW_FORM_GNU_ref_alt => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugInfoRefSup(DebugInfoOffset(offset)) } constants::DW_FORM_string => { let string = input.read_null_terminated_slice()?; AttributeValue::String(string) } constants::DW_FORM_strp => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugStrRef(DebugStrOffset(offset)) } constants::DW_FORM_strp_sup | constants::DW_FORM_GNU_strp_alt => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugStrRefSup(DebugStrOffset(offset)) } constants::DW_FORM_line_strp => { let offset = input.read_offset(encoding.format)?; AttributeValue::DebugLineStrRef(DebugLineStrOffset(offset)) } constants::DW_FORM_implicit_const => { let data = spec .implicit_const_value() .ok_or(Error::InvalidImplicitConst)?; AttributeValue::Sdata(data) } constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx1 => { let index = input.read_u8().map(R::Offset::from_u8)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx2 => { let index = input.read_u16().map(R::Offset::from_u16)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx3 => { let index = input.read_uint(3).and_then(R::Offset::from_u64)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_strx4 => { let index = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(index)) } constants::DW_FORM_addrx | constants::DW_FORM_GNU_addr_index => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx1 => { let index = input.read_u8().map(R::Offset::from_u8)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx2 => { let index = input.read_u16().map(R::Offset::from_u16)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx3 => { let index = input.read_uint(3).and_then(R::Offset::from_u64)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_addrx4 => { let index = input.read_u32().map(R::Offset::from_u32)?; AttributeValue::DebugAddrIndex(DebugAddrIndex(index)) } constants::DW_FORM_loclistx => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugLocListsIndex(DebugLocListsIndex(index)) } constants::DW_FORM_rnglistx => { let index = input.read_uleb128().and_then(R::Offset::from_u64)?; AttributeValue::DebugRngListsIndex(DebugRngListsIndex(index)) } _ => { return Err(Error::UnknownForm); } }; let attr = Attribute { name: spec.name(), value, }; return Ok(attr); } } pub(crate) fn skip_attributes<'unit, R: Reader>( input: &mut R, encoding: Encoding, specs: &[AttributeSpecification], ) -> Result<()> { let mut skip_bytes = R::Offset::from_u8(0); for spec in specs { let mut form = spec.form(); loop { if let Some(len) = get_attribute_size(form, encoding) { // We know the length of this attribute. Accumulate that length. skip_bytes += R::Offset::from_u8(len); break; } // We have encountered a variable-length attribute. if skip_bytes != R::Offset::from_u8(0) { // Skip the accumulated skip bytes and then read the attribute normally. input.skip(skip_bytes)?; skip_bytes = R::Offset::from_u8(0); } match form { constants::DW_FORM_indirect => { let dynamic_form = input.read_uleb128_u16()?; form = constants::DwForm(dynamic_form); continue; } constants::DW_FORM_block1 => { skip_bytes = input.read_u8().map(R::Offset::from_u8)?; } constants::DW_FORM_block2 => { skip_bytes = input.read_u16().map(R::Offset::from_u16)?; } constants::DW_FORM_block4 => { skip_bytes = input.read_u32().map(R::Offset::from_u32)?; } constants::DW_FORM_block | constants::DW_FORM_exprloc => { skip_bytes = input.read_uleb128().and_then(R::Offset::from_u64)?; } constants::DW_FORM_string => { let _ = input.read_null_terminated_slice()?; } constants::DW_FORM_udata | constants::DW_FORM_sdata | constants::DW_FORM_ref_udata | constants::DW_FORM_strx | constants::DW_FORM_GNU_str_index | constants::DW_FORM_addrx | constants::DW_FORM_GNU_addr_index | constants::DW_FORM_loclistx | constants::DW_FORM_rnglistx => { input.skip_leb128()?; } _ => { return Err(Error::UnknownForm); } }; break; } } if skip_bytes != R::Offset::from_u8(0) { // Skip the remaining accumulated skip bytes. input.skip(skip_bytes)?; } Ok(()) } /// An iterator over a particular entry's attributes. /// /// See [the documentation for /// `DebuggingInformationEntry::attrs()`](./struct.DebuggingInformationEntry.html#method.attrs) /// for details. /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). #[derive(Clone, Copy, Debug)] pub struct AttrsIter<'abbrev, 'entry, 'unit, R: Reader> { input: R, attributes: &'abbrev [AttributeSpecification], entry: &'entry DebuggingInformationEntry<'abbrev, 'unit, R>, } impl<'abbrev, 'entry, 'unit, R: Reader> AttrsIter<'abbrev, 'entry, 'unit, R> { /// Advance the iterator and return the next attribute. /// /// Returns `None` when iteration is finished. If an error /// occurs while parsing the next attribute, then this error /// is returned, and all subsequent calls return `None`. #[allow(clippy::inline_always)] #[inline(always)] pub fn next(&mut self) -> Result>> { if self.attributes.is_empty() { // Now that we have parsed all of the attributes, we know where // either (1) this entry's children start, if the abbreviation says // this entry has children; or (2) where this entry's siblings // begin. if let Some(end) = self.entry.attrs_len.get() { debug_assert_eq!(end, self.input.offset_from(&self.entry.attrs_slice)); } else { self.entry .attrs_len .set(Some(self.input.offset_from(&self.entry.attrs_slice))); } return Ok(None); } let spec = self.attributes[0]; let rest_spec = &self.attributes[1..]; match parse_attribute(&mut self.input, self.entry.unit.encoding(), spec) { Ok(attr) => { self.attributes = rest_spec; Ok(Some(attr)) } Err(e) => { self.input.empty(); Err(e) } } } } #[cfg(feature = "fallible-iterator")] impl<'abbrev, 'entry, 'unit, R: Reader> fallible_iterator::FallibleIterator for AttrsIter<'abbrev, 'entry, 'unit, R> { type Item = Attribute; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { AttrsIter::next(self) } } /// A raw reader of the data that defines the Debugging Information Entries. /// /// `EntriesRaw` provides primitives to read the components of Debugging Information /// Entries (DIEs). A DIE consists of an abbreviation code (read with `read_abbreviation`) /// followed by a number of attributes (read with `read_attribute`). /// The user must provide the control flow to read these correctly. /// In particular, all attributes must always be read before reading another /// abbreviation code. /// /// `EntriesRaw` lacks some features of `EntriesCursor`, such as the ability to skip /// to the next sibling DIE. However, this also allows it to optimize better, since it /// does not need to perform the extra bookkeeping required to support these features, /// and thus it is suitable for cases where performance is important. /// /// ## Example Usage /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let debug_info = gimli::DebugInfo::new(&[], gimli::LittleEndian); /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// let unit = get_some_unit(); /// # let debug_abbrev = gimli::DebugAbbrev::new(&[], gimli::LittleEndian); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut entries = unit.entries_raw(&abbrevs, None)?; /// while !entries.is_empty() { /// let abbrev = if let Some(abbrev) = entries.read_abbreviation()? { /// abbrev /// } else { /// // Null entry with no attributes. /// continue /// }; /// match abbrev.tag() { /// gimli::DW_TAG_subprogram => { /// // Loop over attributes for DIEs we care about. /// for spec in abbrev.attributes() { /// let attr = entries.read_attribute(*spec)?; /// match attr.name() { /// // Handle attributes. /// _ => {} /// } /// } /// } /// _ => { /// // Skip attributes for DIEs we don't care about. /// entries.skip_attributes(abbrev.attributes()); /// } /// } /// } /// # unreachable!() /// # } /// ``` #[derive(Clone, Debug)] pub struct EntriesRaw<'abbrev, 'unit, R> where R: Reader, { input: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, depth: isize, } impl<'abbrev, 'unit, R: Reader> EntriesRaw<'abbrev, 'unit, R> { /// Return true if there is no more input. #[inline] pub fn is_empty(&self) -> bool { self.input.is_empty() } /// Return the unit offset at which the reader will read next. /// /// If you want the offset of the next entry, then this must be called prior to reading /// the next entry. pub fn next_offset(&self) -> UnitOffset { UnitOffset(self.unit.header_size() + self.input.offset_from(&self.unit.entries_buf)) } /// Return the depth of the next entry. /// /// This depth is updated when `read_abbreviation` is called, and is updated /// based on null entries and the `has_children` field in the abbreviation. #[inline] pub fn next_depth(&self) -> isize { self.depth } /// Read an abbreviation code and lookup the corresponding `Abbreviation`. /// /// Returns `Ok(None)` for null entries. #[inline] pub fn read_abbreviation(&mut self) -> Result> { let code = self.input.read_uleb128()?; if code == 0 { self.depth -= 1; return Ok(None); }; let abbrev = self .abbreviations .get(code) .ok_or(Error::UnknownAbbreviation)?; if abbrev.has_children() { self.depth += 1; } Ok(Some(abbrev)) } /// Read an attribute. #[inline] pub fn read_attribute(&mut self, spec: AttributeSpecification) -> Result> { parse_attribute(&mut self.input, self.unit.encoding(), spec) } /// Skip all the attributes of an abbreviation. #[inline] pub fn skip_attributes(&mut self, specs: &[AttributeSpecification]) -> Result<()> { skip_attributes(&mut self.input, self.unit.encoding(), specs) } } /// A cursor into the Debugging Information Entries tree for a compilation unit. /// /// The `EntriesCursor` can traverse the DIE tree in DFS order using `next_dfs()`, /// or skip to the next sibling of the entry the cursor is currently pointing to /// using `next_sibling()`. /// /// It is also possible to traverse the DIE tree at a lower abstraction level /// using `next_entry()`. This method does not skip over null entries, or provide /// any indication of the current tree depth. In this case, you must use `current()` /// to obtain the current entry, and `current().has_children()` to determine if /// the entry following the current entry will be a sibling or child. `current()` /// will return `None` if the current entry is a null entry, which signifies the /// end of the current tree depth. #[derive(Clone, Debug)] pub struct EntriesCursor<'abbrev, 'unit, R> where R: Reader, { input: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, cached_current: Option>, delta_depth: isize, } impl<'abbrev, 'unit, R: Reader> EntriesCursor<'abbrev, 'unit, R> { /// Get a reference to the entry that the cursor is currently pointing to. /// /// If the cursor is not pointing at an entry, or if the current entry is a /// null entry, then `None` is returned. #[inline] pub fn current(&self) -> Option<&DebuggingInformationEntry<'abbrev, 'unit, R>> { self.cached_current.as_ref() } /// Move the cursor to the next DIE in the tree. /// /// Returns `Some` if there is a next entry, even if this entry is null. /// If there is no next entry, then `None` is returned. pub fn next_entry(&mut self) -> Result> { if let Some(ref current) = self.cached_current { self.input = current.after_attrs()?; } if self.input.is_empty() { self.cached_current = None; self.delta_depth = 0; return Ok(None); } match DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations) { Ok(Some(entry)) => { self.delta_depth = entry.has_children() as isize; self.cached_current = Some(entry); Ok(Some(())) } Ok(None) => { self.delta_depth = -1; self.cached_current = None; Ok(Some(())) } Err(e) => { self.input.empty(); self.delta_depth = 0; self.cached_current = None; Err(e) } } } /// Move the cursor to the next DIE in the tree in DFS order. /// /// Upon successful movement of the cursor, return the delta traversal /// depth and the entry: /// /// * If we moved down into the previous current entry's children, we get /// `Some((1, entry))`. /// /// * If we moved to the previous current entry's sibling, we get /// `Some((0, entry))`. /// /// * If the previous entry does not have any siblings and we move up to /// its parent's next sibling, then we get `Some((-1, entry))`. Note that /// if the parent doesn't have a next sibling, then it could go up to the /// parent's parent's next sibling and return `Some((-2, entry))`, etc. /// /// If there is no next entry, then `None` is returned. /// /// Here is an example that finds the first entry in a compilation unit that /// does not have any children. /// /// ``` /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 25 /// # 0x19, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # ]; /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); /// # /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_yes /// # 0x01, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); /// # /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// /// let unit = get_some_unit(); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut first_entry_with_no_children = None; /// let mut cursor = unit.entries(&abbrevs); /// /// // Move the cursor to the root. /// assert!(cursor.next_dfs().unwrap().is_some()); /// /// // Traverse the DIE tree in depth-first search order. /// let mut depth = 0; /// while let Some((delta_depth, current)) = cursor.next_dfs().expect("Should parse next dfs") { /// // Update depth value, and break out of the loop when we /// // return to the original starting position. /// depth += delta_depth; /// if depth <= 0 { /// break; /// } /// /// first_entry_with_no_children = Some(current.clone()); /// } /// /// println!("The first entry with no children is {:?}", /// first_entry_with_no_children.unwrap()); /// ``` #[allow(clippy::type_complexity)] pub fn next_dfs( &mut self, ) -> Result)>> { let mut delta_depth = self.delta_depth; loop { // The next entry should be the one we want. if self.next_entry()?.is_some() { if let Some(ref entry) = self.cached_current { return Ok(Some((delta_depth, entry))); } // next_entry() read a null entry. delta_depth += self.delta_depth; } else { return Ok(None); } } } /// Move the cursor to the next sibling DIE of the current one. /// /// Returns `Ok(Some(entry))` when the cursor has been moved to /// the next sibling, `Ok(None)` when there is no next sibling. /// /// The depth of the cursor is never changed if this method returns `Ok`. /// Once `Ok(None)` is returned, this method will continue to return /// `Ok(None)` until either `next_entry` or `next_dfs` is called. /// /// Here is an example that iterates over all of the direct children of the /// root entry: /// /// ``` /// # use gimli::{DebugAbbrev, DebugInfo, LittleEndian}; /// # let info_buf = [ /// # // Comilation unit header /// # /// # // 32-bit unit length = 25 /// # 0x19, 0x00, 0x00, 0x00, /// # // Version 4 /// # 0x04, 0x00, /// # // debug_abbrev_offset /// # 0x00, 0x00, 0x00, 0x00, /// # // Address size /// # 0x04, /// # /// # // DIEs /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // Abbreviation code /// # 0x01, /// # // Attribute of form DW_FORM_string = "foo\0" /// # 0x66, 0x6f, 0x6f, 0x00, /// # /// # // Children /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # /// # // End of children /// # 0x00, /// # ]; /// # let debug_info = DebugInfo::new(&info_buf, LittleEndian); /// # /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// /// # let abbrev_buf = [ /// # // Code /// # 0x01, /// # // DW_TAG_subprogram /// # 0x2e, /// # // DW_CHILDREN_yes /// # 0x01, /// # // Begin attributes /// # // Attribute name = DW_AT_name /// # 0x03, /// # // Attribute form = DW_FORM_string /// # 0x08, /// # // End attributes /// # 0x00, /// # 0x00, /// # // Null terminator /// # 0x00 /// # ]; /// # let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); /// # /// let unit = get_some_unit(); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut cursor = unit.entries(&abbrevs); /// /// // Move the cursor to the root. /// assert!(cursor.next_dfs().unwrap().is_some()); /// /// // Move the cursor to the root's first child. /// assert!(cursor.next_dfs().unwrap().is_some()); /// /// // Iterate the root's children. /// loop { /// { /// let current = cursor.current().expect("Should be at an entry"); /// println!("{:?} is a child of the root", current); /// } /// /// if cursor.next_sibling().expect("Should parse next sibling").is_none() { /// break; /// } /// } /// ``` pub fn next_sibling( &mut self, ) -> Result>> { if self.current().is_none() { // We're already at the null for the end of the sibling list. return Ok(None); } // Loop until we find an entry at the current level. let mut depth = 0; loop { // Use is_some() and unwrap() to keep borrow checker happy. if self.current().is_some() && self.current().unwrap().has_children() { if let Some(sibling_input) = self.current().unwrap().sibling() { // Fast path: this entry has a DW_AT_sibling // attribute pointing to its sibling, so jump // to it (which keeps us at the same depth). self.input = sibling_input; self.cached_current = None; } else { // This entry has children, so the next entry is // down one level. depth += 1; } } if self.next_entry()?.is_none() { // End of input. return Ok(None); } if depth == 0 { // Found an entry at the current level. return Ok(self.current()); } if self.current().is_none() { // A null entry means the end of a child list, so we're // back up a level. depth -= 1; } } } } /// The state information for a tree view of the Debugging Information Entries. /// /// The `EntriesTree` can be used to recursively iterate through the DIE /// tree, following the parent/child relationships. The `EntriesTree` contains /// shared state for all nodes in the tree, avoiding any duplicate parsing of /// entries during the traversal. /// /// ## Example Usage /// ```rust,no_run /// # fn example() -> Result<(), gimli::Error> { /// # let debug_info = gimli::DebugInfo::new(&[], gimli::LittleEndian); /// # let get_some_unit = || debug_info.units().next().unwrap().unwrap(); /// let unit = get_some_unit(); /// # let debug_abbrev = gimli::DebugAbbrev::new(&[], gimli::LittleEndian); /// # let get_abbrevs_for_unit = |_| unit.abbreviations(&debug_abbrev).unwrap(); /// let abbrevs = get_abbrevs_for_unit(&unit); /// /// let mut tree = unit.entries_tree(&abbrevs, None)?; /// let root = tree.root()?; /// process_tree(root)?; /// # unreachable!() /// # } /// /// fn process_tree(mut node: gimli::EntriesTreeNode) -> gimli::Result<()> /// where R: gimli::Reader /// { /// { /// // Examine the entry attributes. /// let mut attrs = node.entry().attrs(); /// while let Some(attr) = attrs.next()? { /// } /// } /// let mut children = node.children(); /// while let Some(child) = children.next()? { /// // Recursively process a child. /// process_tree(child); /// } /// Ok(()) /// } /// ``` #[derive(Clone, Debug)] pub struct EntriesTree<'abbrev, 'unit, R> where R: Reader, { root: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations, input: R, entry: Option>, depth: isize, } impl<'abbrev, 'unit, R: Reader> EntriesTree<'abbrev, 'unit, R> { fn new(root: R, unit: &'unit UnitHeader, abbreviations: &'abbrev Abbreviations) -> Self { let input = root.clone(); EntriesTree { root, unit, abbreviations, input, entry: None, depth: 0, } } /// Returns the root node of the tree. pub fn root<'me>(&'me mut self) -> Result> { self.input = self.root.clone(); self.entry = DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations)?; if self.entry.is_none() { return Err(Error::UnexpectedNull); } self.depth = 0; Ok(EntriesTreeNode::new(self, 1)) } /// Move the cursor to the next entry at the specified depth. /// /// Requires `depth <= self.depth + 1`. /// /// Returns `true` if successful. fn next(&mut self, depth: isize) -> Result { if self.depth < depth { debug_assert_eq!(self.depth + 1, depth); match self.entry { Some(ref entry) => { if !entry.has_children() { return Ok(false); } self.depth += 1; self.input = entry.after_attrs()?; } None => return Ok(false), } if self.input.is_empty() { self.entry = None; return Ok(false); } return match DebuggingInformationEntry::parse( &mut self.input, self.unit, self.abbreviations, ) { Ok(entry) => { self.entry = entry; Ok(self.entry.is_some()) } Err(e) => { self.input.empty(); self.entry = None; Err(e) } }; } loop { match self.entry { Some(ref entry) => { if entry.has_children() { if let Some(sibling_input) = entry.sibling() { // Fast path: this entry has a DW_AT_sibling // attribute pointing to its sibling, so jump // to it (which keeps us at the same depth). self.input = sibling_input; } else { // This entry has children, so the next entry is // down one level. self.depth += 1; self.input = entry.after_attrs()?; } } else { // This entry has no children, so next entry is at same depth. self.input = entry.after_attrs()?; } } None => { // This entry is a null, so next entry is up one level. self.depth -= 1; } } if self.input.is_empty() { self.entry = None; return Ok(false); } match DebuggingInformationEntry::parse(&mut self.input, self.unit, self.abbreviations) { Ok(entry) => { self.entry = entry; if self.depth == depth { return Ok(self.entry.is_some()); } } Err(e) => { self.input.empty(); self.entry = None; return Err(e); } } } } } /// A node in the Debugging Information Entry tree. /// /// The root node of a tree can be obtained /// via [`EntriesTree::root`](./struct.EntriesTree.html#method.root). #[derive(Debug)] pub struct EntriesTreeNode<'abbrev, 'unit, 'tree, R: Reader> { tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, } impl<'abbrev, 'unit, 'tree, R: Reader> EntriesTreeNode<'abbrev, 'unit, 'tree, R> { fn new( tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, ) -> EntriesTreeNode<'abbrev, 'unit, 'tree, R> { debug_assert!(tree.entry.is_some()); EntriesTreeNode { tree, depth } } /// Returns the current entry in the tree. pub fn entry(&self) -> &DebuggingInformationEntry<'abbrev, 'unit, R> { // We never create a node without an entry. self.tree.entry.as_ref().unwrap() } /// Create an iterator for the children of the current entry. /// /// The current entry can no longer be accessed after creating the /// iterator. pub fn children(self) -> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { EntriesTreeIter::new(self.tree, self.depth) } } /// An iterator that allows traversal of the children of an /// `EntriesTreeNode`. /// /// The items returned by this iterator are also `EntriesTreeNode`s, /// which allow recursive traversal of grandchildren, etc. #[derive(Debug)] pub struct EntriesTreeIter<'abbrev, 'unit, 'tree, R: Reader> { tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, empty: bool, } impl<'abbrev, 'unit, 'tree, R: Reader> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { fn new( tree: &'tree mut EntriesTree<'abbrev, 'unit, R>, depth: isize, ) -> EntriesTreeIter<'abbrev, 'unit, 'tree, R> { EntriesTreeIter { tree, depth, empty: false, } } /// Returns an `EntriesTreeNode` for the next child entry. /// /// Returns `None` if there are no more children. pub fn next<'me>(&'me mut self) -> Result>> { if self.empty { Ok(None) } else if self.tree.next(self.depth)? { Ok(Some(EntriesTreeNode::new(self.tree, self.depth + 1))) } else { self.empty = true; Ok(None) } } } /// Parse a type unit header's unique type signature. Callers should handle /// unique-ness checking. fn parse_type_signature(input: &mut R) -> Result { input.read_u64().map(DebugTypeSignature) } /// Parse a type unit header's type offset. fn parse_type_offset(input: &mut R, format: Format) -> Result> { input.read_offset(format).map(UnitOffset) } /// The `DebugTypes` struct represents the DWARF type information /// found in the `.debug_types` section. #[derive(Debug, Default, Clone, Copy)] pub struct DebugTypes { debug_types_section: R, } impl<'input, Endian> DebugTypes> where Endian: Endianity, { /// Construct a new `DebugTypes` instance from the data in the `.debug_types` /// section. /// /// It is the caller's responsibility to read the `.debug_types` section and /// present it as a `&[u8]` slice. That means using some ELF loader on /// Linux, a Mach-O loader on macOS, etc. /// /// ``` /// use gimli::{DebugTypes, LittleEndian}; /// /// # let buf = [0x00, 0x01, 0x02, 0x03]; /// # let read_debug_types_section_somehow = || &buf; /// let debug_types = DebugTypes::new(read_debug_types_section_somehow(), LittleEndian); /// ``` pub fn new(debug_types_section: &'input [u8], endian: Endian) -> Self { Self::from(EndianSlice::new(debug_types_section, endian)) } } impl DebugTypes { /// Create a `DebugTypes` section that references the data in `self`. /// /// This is useful when `R` implements `Reader` but `T` does not. /// /// ## Example Usage /// /// ```rust,no_run /// # let load_section = || unimplemented!(); /// // Read the DWARF section into a `Vec` with whatever object loader you're using. /// let owned_section: gimli::DebugTypes> = load_section(); /// // Create a reference to the DWARF section. /// let section = owned_section.borrow(|section| { /// gimli::EndianSlice::new(§ion, gimli::LittleEndian) /// }); /// ``` pub fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTypes where F: FnMut(&'a T) -> R, { borrow(&self.debug_types_section).into() } } impl Section for DebugTypes { fn id() -> SectionId { SectionId::DebugTypes } fn reader(&self) -> &R { &self.debug_types_section } } impl From for DebugTypes { fn from(debug_types_section: R) -> Self { DebugTypes { debug_types_section, } } } impl DebugTypes { /// Iterate the type-units in this `.debug_types` section. /// /// ``` /// use gimli::{DebugTypes, LittleEndian}; /// /// # let buf = []; /// # let read_debug_types_section_somehow = || &buf; /// let debug_types = DebugTypes::new(read_debug_types_section_somehow(), LittleEndian); /// /// let mut iter = debug_types.units(); /// while let Some(unit) = iter.next().unwrap() { /// println!("unit's length is {}", unit.unit_length()); /// } /// ``` /// /// Can be [used with /// `FallibleIterator`](./index.html#using-with-fallibleiterator). pub fn units(&self) -> DebugTypesUnitHeadersIter { DebugTypesUnitHeadersIter { input: self.debug_types_section.clone(), offset: DebugTypesOffset(R::Offset::from_u8(0)), } } } /// An iterator over the type-units of this `.debug_types` section. /// /// See the [documentation on /// `DebugTypes::units`](./struct.DebugTypes.html#method.units) for /// more detail. #[derive(Clone, Debug)] pub struct DebugTypesUnitHeadersIter { input: R, offset: DebugTypesOffset, } impl DebugTypesUnitHeadersIter { /// Advance the iterator to the next type unit header. pub fn next(&mut self) -> Result>> { if self.input.is_empty() { Ok(None) } else { let len = self.input.len(); match parse_unit_header(&mut self.input, self.offset.into()) { Ok(header) => { self.offset.0 += len - self.input.len(); Ok(Some(header)) } Err(e) => { self.input.empty(); Err(e) } } } } } #[cfg(feature = "fallible-iterator")] impl fallible_iterator::FallibleIterator for DebugTypesUnitHeadersIter { type Item = UnitHeader; type Error = Error; fn next(&mut self) -> ::core::result::Result, Self::Error> { DebugTypesUnitHeadersIter::next(self) } } #[cfg(test)] // Tests require leb128::write. #[cfg(feature = "write")] mod tests { use super::*; use crate::constants; use crate::constants::*; use crate::endianity::{Endianity, LittleEndian}; use crate::leb128; use crate::read::abbrev::tests::AbbrevSectionMethods; use crate::read::{ Abbreviation, AttributeSpecification, DebugAbbrev, EndianSlice, Error, Result, }; use crate::test_util::GimliSectionMethods; use alloc::vec::Vec; use core::cell::Cell; use test_assembler::{Endian, Label, LabelMaker, Section}; // Mixin methods for `Section` to help define binary test data. trait UnitSectionMethods { fn unit<'input, E>(self, unit: &mut UnitHeader>) -> Self where E: Endianity; fn die(self, code: u64, attr: F) -> Self where F: Fn(Section) -> Section; fn die_null(self) -> Self; fn attr_string(self, s: &str) -> Self; fn attr_ref1(self, o: u8) -> Self; fn offset(self, offset: usize, format: Format) -> Self; } impl UnitSectionMethods for Section { fn unit<'input, E>(self, unit: &mut UnitHeader>) -> Self where E: Endianity, { let size = self.size(); let length = Label::new(); let start = Label::new(); let end = Label::new(); let section = match unit.format() { Format::Dwarf32 => self.L32(&length), Format::Dwarf64 => self.L32(0xffff_ffff).L64(&length), }; let section = match unit.version() { 2 | 3 | 4 => section .mark(&start) .L16(unit.version()) .offset(unit.debug_abbrev_offset.0, unit.format()) .D8(unit.address_size()), 5 => section .mark(&start) .L16(unit.version()) .D8(unit.type_().dw_ut().0) .D8(unit.address_size()) .offset(unit.debug_abbrev_offset.0, unit.format()), _ => unreachable!(), }; let section = match unit.type_() { UnitType::Compilation | UnitType::Partial => { unit.unit_offset = DebugInfoOffset(size as usize).into(); section } UnitType::Type { type_signature, type_offset, } | UnitType::SplitType { type_signature, type_offset, } => { if unit.version() == 5 { unit.unit_offset = DebugInfoOffset(size as usize).into(); } else { unit.unit_offset = DebugTypesOffset(size as usize).into(); } section .L64(type_signature.0) .offset(type_offset.0, unit.format()) } UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => { unit.unit_offset = DebugInfoOffset(size as usize).into(); section.L64(dwo_id.0) } }; let section = section.append_bytes(unit.entries_buf.into()).mark(&end); unit.unit_length = (&end - &start) as usize; length.set_const(unit.unit_length as u64); section } fn die(self, code: u64, attr: F) -> Self where F: Fn(Section) -> Section, { let section = self.uleb(code); attr(section) } fn die_null(self) -> Self { self.D8(0) } fn attr_string(self, attr: &str) -> Self { self.append_bytes(attr.as_bytes()).D8(0) } fn attr_ref1(self, attr: u8) -> Self { self.D8(attr) } fn offset(self, offset: usize, format: Format) -> Self { match format { Format::Dwarf32 => self.L32(offset as u32), Format::Dwarf64 => self.L64(offset as u64), } } } /// Ensure that `UnitHeader` is covariant wrt R. #[test] fn test_unit_header_variance() { /// This only needs to compile. fn _f<'a: 'b, 'b, E: Endianity>( x: UnitHeader>, ) -> UnitHeader> { x } } #[test] fn test_parse_debug_abbrev_offset_32() { let section = Section::with_endian(Endian::Little).L32(0x0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf32) { Ok(val) => assert_eq!(val, DebugAbbrevOffset(0x0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_abbrev_offset_32_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf32) { Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_debug_abbrev_offset_64() { let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf64) { Ok(val) => assert_eq!(val, DebugAbbrevOffset(0x0807_0605_0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_abbrev_offset_64_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_abbrev_offset(buf, Format::Dwarf64) { Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_info_offset_32() { let section = Section::with_endian(Endian::Little).L32(0x0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf32) { Ok(val) => assert_eq!(val, DebugInfoOffset(0x0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_info_offset_32_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf32) { Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_debug_info_offset_64() { let section = Section::with_endian(Endian::Little).L64(0x0807_0605_0403_0201); let buf = section.get_contents().unwrap(); let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf64) { Ok(val) => assert_eq!(val, DebugInfoOffset(0x0807_0605_0403_0201)), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_debug_info_offset_64_incomplete() { let buf = [0x01, 0x02]; let buf = &mut EndianSlice::new(&buf, LittleEndian); match parse_debug_info_offset(buf, Format::Dwarf64) { Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] #[cfg(target_pointer_width = "64")] fn test_units() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let mut unit64 = UnitHeader { encoding: Encoding { format: Format::Dwarf64, version: 4, address_size: 8, }, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let mut unit32 = UnitHeader { encoding: Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut unit64) .unit(&mut unit32); let buf = section.get_contents().unwrap(); let debug_info = DebugInfo::new(&buf, LittleEndian); let mut units = debug_info.units(); assert_eq!(units.next(), Ok(Some(unit64))); assert_eq!(units.next(), Ok(Some(unit32))); assert_eq!(units.next(), Ok(None)); } #[test] fn test_unit_version_unknown_version() { let buf = [0x02, 0x00, 0x00, 0x00, 0xab, 0xcd]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_unit_header(rest, DebugInfoOffset(0).into()) { Err(Error::UnknownVersion(0xcdab)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; let buf = [0x02, 0x00, 0x00, 0x00, 0x1, 0x0]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_unit_header(rest, DebugInfoOffset(0).into()) { Err(Error::UnknownVersion(1)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_unit_version_incomplete() { let buf = [0x01, 0x00, 0x00, 0x00, 0x04]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_unit_header(rest, DebugInfoOffset(0).into()) { Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 4, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_partial_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Partial, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_partial_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Partial, debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_skeleton_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_skeleton_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Skeleton(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_split_compilation_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 4, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_split_compilation_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitCompilation(DwoId(0x0706_5040_0302_1000)), debug_abbrev_offset: DebugAbbrevOffset(0x0102_0304_0506_0708), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_type_offset_32_ok() { let buf = [0x12, 0x34, 0x56, 0x78, 0x00]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_type_offset(rest, Format::Dwarf32) { Ok(offset) => { assert_eq!(rest.len(), 1); assert_eq!(UnitOffset(0x7856_3412), offset); } otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_type_offset_64_ok() { let buf = [0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xff, 0x00]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_type_offset(rest, Format::Dwarf64) { Ok(offset) => { assert_eq!(rest.len(), 1); assert_eq!(UnitOffset(0xffde_bc9a_7856_3412), offset); } otherwise => panic!("Unexpected result: {:?}", otherwise), } } #[test] fn test_parse_type_offset_incomplete() { // Need at least 4 bytes. let buf = [0xff, 0xff, 0xff]; let rest = &mut EndianSlice::new(&buf, LittleEndian); match parse_type_offset(rest, Format::Dwarf32) { Err(Error::UnexpectedEof(_)) => assert!(true), otherwise => panic!("Unexpected result: {:?}", otherwise), }; } #[test] fn test_parse_type_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugTypesOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_type_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 4, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412_7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugTypesOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_type_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_type_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412_7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] fn test_parse_v5_split_type_unit_header_32_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf32, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitType { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_v5_split_type_unit_header_64_ok() { let expected_rest = &[1, 2, 3, 4, 5, 6, 7, 8, 9]; let encoding = Encoding { format: Format::Dwarf64, version: 5, address_size: 8, }; let mut expected_unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::SplitType { type_signature: DebugTypeSignature(0xdead_beef_dead_beef), type_offset: UnitOffset(0x7856_3412_7856_3412), }, debug_abbrev_offset: DebugAbbrevOffset(0x0807_0605), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(expected_rest, LittleEndian), }; let section = Section::with_endian(Endian::Little) .unit(&mut expected_unit) .append_bytes(expected_rest); let buf = section.get_contents().unwrap(); let rest = &mut EndianSlice::new(&buf, LittleEndian); assert_eq!( parse_unit_header(rest, DebugInfoOffset(0).into()), Ok(expected_unit) ); assert_eq!(*rest, EndianSlice::new(expected_rest, LittleEndian)); } fn section_contents(f: F) -> Vec where F: Fn(Section) -> Section, { f(Section::with_endian(Endian::Little)) .get_contents() .unwrap() } #[test] fn test_attribute_value() { let mut unit = test_parse_attribute_unit_default(); let endian = unit.entries_buf.endian(); let block_data = &[1, 2, 3, 4]; let buf = section_contents(|s| s.uleb(block_data.len() as u64).append_bytes(block_data)); let block = EndianSlice::new(&buf, endian); let buf = section_contents(|s| s.L32(0x0102_0304)); let data4 = EndianSlice::new(&buf, endian); let buf = section_contents(|s| s.L64(0x0102_0304_0506_0708)); let data8 = EndianSlice::new(&buf, endian); let tests = [ ( Format::Dwarf32, 2, constants::DW_AT_data_member_location, constants::DW_FORM_block, block, AttributeValue::Block(EndianSlice::new(block_data, endian)), AttributeValue::Exprloc(Expression(EndianSlice::new(block_data, endian))), ), ( Format::Dwarf32, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data4, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304)), ), ( Format::Dwarf64, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data4, data4, AttributeValue::Data4(0x0102_0304), AttributeValue::Udata(0x0102_0304), ), ( Format::Dwarf32, 4, constants::DW_AT_data_member_location, constants::DW_FORM_data4, data4, AttributeValue::Data4(0x0102_0304), AttributeValue::Udata(0x0102_0304), ), ( Format::Dwarf32, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data8, data8, AttributeValue::Data8(0x0102_0304_0506_0708), AttributeValue::Udata(0x0102_0304_0506_0708), ), #[cfg(target_pointer_width = "64")] ( Format::Dwarf64, 2, constants::DW_AT_data_member_location, constants::DW_FORM_data8, data8, AttributeValue::SecOffset(0x0102_0304_0506_0708), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304_0506_0708)), ), ( Format::Dwarf64, 4, constants::DW_AT_data_member_location, constants::DW_FORM_data8, data8, AttributeValue::Data8(0x0102_0304_0506_0708), AttributeValue::Udata(0x0102_0304_0506_0708), ), ( Format::Dwarf32, 4, constants::DW_AT_location, constants::DW_FORM_data4, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304)), ), #[cfg(target_pointer_width = "64")] ( Format::Dwarf64, 4, constants::DW_AT_location, constants::DW_FORM_data8, data8, AttributeValue::SecOffset(0x0102_0304_0506_0708), AttributeValue::LocationListsRef(LocationListsOffset(0x0102_0304_0506_0708)), ), ( Format::Dwarf32, 4, constants::DW_AT_str_offsets_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugStrOffsetsBase(DebugStrOffsetsBase(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_stmt_list, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugLineRef(DebugLineOffset(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_addr_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugAddrBase(DebugAddrBase(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_rnglists_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugRngListsBase(DebugRngListsBase(0x0102_0304)), ), ( Format::Dwarf32, 4, constants::DW_AT_loclists_base, constants::DW_FORM_sec_offset, data4, AttributeValue::SecOffset(0x0102_0304), AttributeValue::DebugLocListsBase(DebugLocListsBase(0x0102_0304)), ), ]; for test in tests.iter() { let (format, version, name, form, mut input, expect_raw, expect_value) = *test; unit.encoding.format = format; unit.encoding.version = version; let spec = AttributeSpecification::new(name, form, None); let attribute = parse_attribute(&mut input, unit.encoding(), spec).expect("Should parse attribute"); assert_eq!(attribute.raw_value(), expect_raw); assert_eq!(attribute.value(), expect_value); } } #[test] fn test_attribute_udata_sdata_value() { #[allow(clippy::type_complexity)] let tests: &[( AttributeValue>, Option, Option, )] = &[ (AttributeValue::Data1(1), Some(1), Some(1)), ( AttributeValue::Data1(core::u8::MAX), Some(u64::from(std::u8::MAX)), Some(-1), ), (AttributeValue::Data2(1), Some(1), Some(1)), ( AttributeValue::Data2(core::u16::MAX), Some(u64::from(std::u16::MAX)), Some(-1), ), (AttributeValue::Data4(1), Some(1), Some(1)), ( AttributeValue::Data4(core::u32::MAX), Some(u64::from(std::u32::MAX)), Some(-1), ), (AttributeValue::Data8(1), Some(1), Some(1)), ( AttributeValue::Data8(core::u64::MAX), Some(core::u64::MAX), Some(-1), ), (AttributeValue::Sdata(1), Some(1), Some(1)), (AttributeValue::Sdata(-1), None, Some(-1)), (AttributeValue::Udata(1), Some(1), Some(1)), (AttributeValue::Udata(1u64 << 63), Some(1u64 << 63), None), ]; for test in tests.iter() { let (value, expect_udata, expect_sdata) = *test; let attribute = Attribute { name: DW_AT_data_member_location, value, }; assert_eq!(attribute.udata_value(), expect_udata); assert_eq!(attribute.sdata_value(), expect_sdata); } } fn test_parse_attribute_unit( address_size: u8, format: Format, endian: Endian, ) -> UnitHeader> where Endian: Endianity, { let encoding = Encoding { format, version: 4, address_size, }; UnitHeader::new( encoding, 7, UnitType::Compilation, DebugAbbrevOffset(0x0807_0605), DebugInfoOffset(0).into(), EndianSlice::new(&[], endian), ) } fn test_parse_attribute_unit_default() -> UnitHeader> { test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian) } fn test_parse_attribute<'input, Endian>( buf: &'input [u8], len: usize, unit: &UnitHeader>, form: constants::DwForm, value: AttributeValue>, ) where Endian: Endianity, { let spec = AttributeSpecification::new(constants::DW_AT_low_pc, form, None); let expect = Attribute { name: constants::DW_AT_low_pc, value, }; let rest = &mut EndianSlice::new(buf, Endian::default()); match parse_attribute(rest, unit.encoding(), spec) { Ok(attr) => { assert_eq!(attr, expect); assert_eq!(*rest, EndianSlice::new(&buf[len..], Endian::default())); if let Some(size) = spec.size(unit) { assert_eq!(rest.len() + size, buf.len()); } } otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } }; } #[test] fn test_parse_attribute_addr() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_addr; let value = AttributeValue::Addr(0x0403_0201); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_addr8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]; let unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_addr; let value = AttributeValue::Addr(0x0807_0605_0403_0201); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_block1() { // Length of data (3), three bytes of data, two bytes of left over input. let buf = [0x03, 0x09, 0x09, 0x09, 0x00, 0x00]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block1; let value = AttributeValue::Block(EndianSlice::new(&buf[1..4], LittleEndian)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_block2() { // Two byte length of data (2), two bytes of data, two bytes of left over input. let buf = [0x02, 0x00, 0x09, 0x09, 0x00, 0x00]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block2; let value = AttributeValue::Block(EndianSlice::new(&buf[2..4], LittleEndian)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_block4() { // Four byte length of data (2), two bytes of data, no left over input. let buf = [0x02, 0x00, 0x00, 0x00, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block4; let value = AttributeValue::Block(EndianSlice::new(&buf[4..], LittleEndian)); test_parse_attribute(&buf, 6, &unit, form, value); } #[test] fn test_parse_attribute_block() { // LEB length of data (2, one byte), two bytes of data, no left over input. let buf = [0x02, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_block; let value = AttributeValue::Block(EndianSlice::new(&buf[1..], LittleEndian)); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_data1() { let buf = [0x03]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data1; let value = AttributeValue::Data1(0x03); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_data2() { let buf = [0x02, 0x01, 0x0]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data2; let value = AttributeValue::Data2(0x0102); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_data4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data4; let value = AttributeValue::Data4(0x0403_0201); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_data8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_data8; let value = AttributeValue::Data8(0x0807_0605_0403_0201); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_udata() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_udata; let value = AttributeValue::Udata(4097); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_sdata() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::signed(&mut writable, -4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_sdata; let value = AttributeValue::Sdata(-4097); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_exprloc() { // LEB length of data (2, one byte), two bytes of data, one byte left over input. let buf = [0x02, 0x99, 0x99, 0x11]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_exprloc; let value = AttributeValue::Exprloc(Expression(EndianSlice::new(&buf[1..3], LittleEndian))); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_flag_true() { let buf = [0x42]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_flag; let value = AttributeValue::Flag(true); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_flag_false() { let buf = [0x00]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_flag; let value = AttributeValue::Flag(false); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_flag_present() { let buf = [0x01, 0x02, 0x03, 0x04]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_flag_present; let value = AttributeValue::Flag(true); // DW_FORM_flag_present does not consume any bytes of the input stream. test_parse_attribute(&buf, 0, &unit, form, value); } #[test] fn test_parse_attribute_sec_offset_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_sec_offset; let value = AttributeValue::SecOffset(0x0403_0201); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_sec_offset_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_sec_offset; let value = AttributeValue::SecOffset(0x0807_0605_0403_0201); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_ref1() { let buf = [0x03]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref1; let value = AttributeValue::UnitRef(UnitOffset(3)); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_ref2() { let buf = [0x02, 0x01, 0x0]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref2; let value = AttributeValue::UnitRef(UnitOffset(258)); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_ref4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref4; let value = AttributeValue::UnitRef(UnitOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_ref8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref8; let value = AttributeValue::UnitRef(UnitOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_ref_sup4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_sup4; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_ref_sup8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_sup8; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_refudata() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_udata; let value = AttributeValue::UnitRef(UnitOffset(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_refaddr_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_refaddr_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_refaddr_version2() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let mut unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); unit.encoding.version = 2; let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_refaddr8_version2() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let mut unit = test_parse_attribute_unit(8, Format::Dwarf32, LittleEndian); unit.encoding.version = 2; let form = constants::DW_FORM_ref_addr; let value = AttributeValue::DebugInfoRef(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_gnu_ref_alt_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_GNU_ref_alt; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_gnu_ref_alt_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_GNU_ref_alt; let value = AttributeValue::DebugInfoRefSup(DebugInfoOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_refsig8() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_ref_sig8; let value = AttributeValue::DebugTypesRef(DebugTypeSignature(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_string() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x0, 0x99, 0x99]; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_string; let value = AttributeValue::String(EndianSlice::new(&buf[..5], LittleEndian)); test_parse_attribute(&buf, 6, &unit, form, value); } #[test] fn test_parse_attribute_strp_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_strp; let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_strp_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strp; let value = AttributeValue::DebugStrRef(DebugStrOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_strp_sup_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_strp_sup; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_strp_sup_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strp_sup; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_gnu_strp_alt_32() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf32, LittleEndian); let form = constants::DW_FORM_GNU_strp_alt; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] #[cfg(target_pointer_width = "64")] fn test_parse_attribute_gnu_strp_alt_64() { let buf = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_GNU_strp_alt; let value = AttributeValue::DebugStrRefSup(DebugStrOffset(0x0807_0605_0403_0201)); test_parse_attribute(&buf, 8, &unit, form, value); } #[test] fn test_parse_attribute_strx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_strx; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_strx1() { let buf = [0x01, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx1; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x01)); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_strx2() { let buf = [0x01, 0x02, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx2; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0201)); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_strx3() { let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx3; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x03_0201)); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_strx4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_strx4; let value = AttributeValue::DebugStrOffsetsIndex(DebugStrOffsetsIndex(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_addrx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_addrx; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_addrx1() { let buf = [0x01, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx1; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x01)); test_parse_attribute(&buf, 1, &unit, form, value); } #[test] fn test_parse_attribute_addrx2() { let buf = [0x01, 0x02, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx2; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0201)); test_parse_attribute(&buf, 2, &unit, form, value); } #[test] fn test_parse_attribute_addrx3() { let buf = [0x01, 0x02, 0x03, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx3; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x03_0201)); test_parse_attribute(&buf, 3, &unit, form, value); } #[test] fn test_parse_attribute_addrx4() { let buf = [0x01, 0x02, 0x03, 0x04, 0x99, 0x99]; let unit = test_parse_attribute_unit(4, Format::Dwarf64, LittleEndian); let form = constants::DW_FORM_addrx4; let value = AttributeValue::DebugAddrIndex(DebugAddrIndex(0x0403_0201)); test_parse_attribute(&buf, 4, &unit, form, value); } #[test] fn test_parse_attribute_loclistx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_loclistx; let value = AttributeValue::DebugLocListsIndex(DebugLocListsIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_rnglistx() { let mut buf = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, 4097).expect("should write ok") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_rnglistx; let value = AttributeValue::DebugRngListsIndex(DebugRngListsIndex(4097)); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_indirect() { let mut buf = [0; 100]; let bytes_written = { let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, constants::DW_FORM_udata.0.into()) .expect("should write udata") + leb128::write::unsigned(&mut writable, 9_999_999).expect("should write value") }; let unit = test_parse_attribute_unit_default(); let form = constants::DW_FORM_indirect; let value = AttributeValue::Udata(9_999_999); test_parse_attribute(&buf, bytes_written, &unit, form, value); } #[test] fn test_parse_attribute_indirect_implicit_const() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut buf = [0; 100]; let mut writable = &mut buf[..]; leb128::write::unsigned(&mut writable, constants::DW_FORM_implicit_const.0.into()) .expect("should write implicit_const"); let input = &mut EndianSlice::new(&buf, LittleEndian); let spec = AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_indirect, None); assert_eq!( parse_attribute(input, encoding, spec), Err(Error::InvalidImplicitConst) ); } #[test] fn test_attrs_iter() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let unit = UnitHeader::new( encoding, 7, UnitType::Compilation, DebugAbbrevOffset(0x0807_0605), DebugInfoOffset(0).into(), EndianSlice::new(&[], LittleEndian), ); let abbrev = Abbreviation::new( 42, constants::DW_TAG_subprogram, constants::DW_CHILDREN_yes, vec![ AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), AttributeSpecification::new( constants::DW_AT_high_pc, constants::DW_FORM_addr, None, ), ] .into(), ); // "foo", 42, 1337, 4 dangling bytes of 0xaa where children would be let buf = [ 0x66, 0x6f, 0x6f, 0x00, 0x2a, 0x00, 0x00, 0x00, 0x39, 0x05, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, ]; let entry = DebuggingInformationEntry { offset: UnitOffset(0), attrs_slice: EndianSlice::new(&buf, LittleEndian), attrs_len: Cell::new(None), abbrev: &abbrev, unit: &unit, }; let mut attrs = AttrsIter { input: EndianSlice::new(&buf, LittleEndian), attributes: abbrev.attributes(), entry: &entry, }; match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_name, value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), } ); } otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_low_pc, value: AttributeValue::Addr(0x2a), } ); } otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_high_pc, value: AttributeValue::Addr(0x539), } ); } otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); assert!(attrs.next().expect("should parse next").is_none()); assert!(entry.attrs_len.get().is_some()); assert_eq!( entry.attrs_len.get().expect("should have entry.attrs_len"), buf.len() - 4 ) } #[test] fn test_attrs_iter_incomplete() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let unit = UnitHeader::new( encoding, 7, UnitType::Compilation, DebugAbbrevOffset(0x0807_0605), DebugInfoOffset(0).into(), EndianSlice::new(&[], LittleEndian), ); let abbrev = Abbreviation::new( 42, constants::DW_TAG_subprogram, constants::DW_CHILDREN_yes, vec![ AttributeSpecification::new(constants::DW_AT_name, constants::DW_FORM_string, None), AttributeSpecification::new(constants::DW_AT_low_pc, constants::DW_FORM_addr, None), AttributeSpecification::new( constants::DW_AT_high_pc, constants::DW_FORM_addr, None, ), ] .into(), ); // "foo" let buf = [0x66, 0x6f, 0x6f, 0x00]; let entry = DebuggingInformationEntry { offset: UnitOffset(0), attrs_slice: EndianSlice::new(&buf, LittleEndian), attrs_len: Cell::new(None), abbrev: &abbrev, unit: &unit, }; let mut attrs = AttrsIter { input: EndianSlice::new(&buf, LittleEndian), attributes: abbrev.attributes(), entry: &entry, }; match attrs.next() { Ok(Some(attr)) => { assert_eq!( attr, Attribute { name: constants::DW_AT_name, value: AttributeValue::String(EndianSlice::new(b"foo", LittleEndian)), } ); } otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } } assert!(entry.attrs_len.get().is_none()); // Return error for incomplete attribute. assert!(attrs.next().is_err()); assert!(entry.attrs_len.get().is_none()); // Return error for all subsequent calls. assert!(attrs.next().is_err()); assert!(attrs.next().is_err()); assert!(attrs.next().is_err()); assert!(attrs.next().is_err()); assert!(entry.attrs_len.get().is_none()); } fn assert_entry_name(entry: &DebuggingInformationEntry>, name: &str) where Endian: Endianity, { let value = entry .attr_value(constants::DW_AT_name) .expect("Should have parsed the name attribute") .expect("Should have found the name attribute"); assert_eq!( value, AttributeValue::String(EndianSlice::new(name.as_bytes(), Endian::default())) ); } fn assert_current_name(cursor: &EntriesCursor>, name: &str) where Endian: Endianity, { let entry = cursor.current().expect("Should have an entry result"); assert_entry_name(entry, name); } fn assert_next_entry(cursor: &mut EntriesCursor>, name: &str) where Endian: Endianity, { cursor .next_entry() .expect("Should parse next entry") .expect("Should have an entry"); assert_current_name(cursor, name); } fn assert_next_entry_null(cursor: &mut EntriesCursor>) where Endian: Endianity, { cursor .next_entry() .expect("Should parse next entry") .expect("Should have an entry"); assert!(cursor.current().is_none()); } fn assert_next_dfs( cursor: &mut EntriesCursor>, name: &str, depth: isize, ) where Endian: Endianity, { { let (val, entry) = cursor .next_dfs() .expect("Should parse next dfs") .expect("Should not be done with traversal"); assert_eq!(val, depth); assert_entry_name(entry, name); } assert_current_name(cursor, name); } fn assert_next_sibling(cursor: &mut EntriesCursor>, name: &str) where Endian: Endianity, { { let entry = cursor .next_sibling() .expect("Should parse next sibling") .expect("Should not be done with traversal"); assert_entry_name(entry, name); } assert_current_name(cursor, name); } fn assert_valid_sibling_ptr(cursor: &EntriesCursor>) where Endian: Endianity, { let sibling_ptr = cursor .current() .expect("Should have current entry") .attr_value(constants::DW_AT_sibling); match sibling_ptr { Ok(Some(AttributeValue::UnitRef(offset))) => { cursor .unit .range_from(offset..) .expect("Sibling offset should be valid"); } _ => panic!("Invalid sibling pointer {:?}", sibling_ptr), } } fn entries_cursor_tests_abbrev_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null(); section.get_contents().unwrap() } fn entries_cursor_tests_debug_info_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .die(1, |s| s.attr_string("001")) .die(1, |s| s.attr_string("002")) .die(1, |s| s.attr_string("003")) .die_null() .die_null() .die(1, |s| s.attr_string("004")) .die(1, |s| s.attr_string("005")) .die_null() .die(1, |s| s.attr_string("006")) .die_null() .die_null() .die(1, |s| s.attr_string("007")) .die(1, |s| s.attr_string("008")) .die(1, |s| s.attr_string("009")) .die_null() .die_null() .die_null() .die(1, |s| s.attr_string("010")) .die_null() .die_null(); let entries_buf = section.get_contents().unwrap(); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&entries_buf, LittleEndian), }; let section = Section::with_endian(Endian::Little).unit(&mut unit); section.get_contents().unwrap() } #[test] fn test_cursor_next_entry_incomplete() { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .die(1, |s| s.attr_string("001")) .die(1, |s| s.attr_string("002")) .die(1, |s| s); let entries_buf = section.get_contents().unwrap(); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&entries_buf, LittleEndian), }; let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = §ion.get_contents().unwrap(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_entry(&mut cursor, "001"); assert_next_entry(&mut cursor, "002"); { // Entry code is present, but none of the attributes. cursor .next_entry() .expect("Should parse next entry") .expect("Should have an entry"); let entry = cursor.current().expect("Should have an entry result"); assert!(entry.attrs().next().is_err()); } assert!(cursor.next_entry().is_err()); assert!(cursor.next_entry().is_err()); } #[test] fn test_cursor_next_entry() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_entry(&mut cursor, "001"); assert_next_entry(&mut cursor, "002"); assert_next_entry(&mut cursor, "003"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "004"); assert_next_entry(&mut cursor, "005"); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "006"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "007"); assert_next_entry(&mut cursor, "008"); assert_next_entry(&mut cursor, "009"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert_next_entry(&mut cursor, "010"); assert_next_entry_null(&mut cursor); assert_next_entry_null(&mut cursor); assert!(cursor .next_entry() .expect("Should parse next entry") .is_none()); assert!(cursor.current().is_none()); } #[test] fn test_cursor_next_dfs() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_dfs(&mut cursor, "001", 0); assert_next_dfs(&mut cursor, "002", 1); assert_next_dfs(&mut cursor, "003", 1); assert_next_dfs(&mut cursor, "004", -1); assert_next_dfs(&mut cursor, "005", 1); assert_next_dfs(&mut cursor, "006", 0); assert_next_dfs(&mut cursor, "007", -1); assert_next_dfs(&mut cursor, "008", 1); assert_next_dfs(&mut cursor, "009", 1); assert_next_dfs(&mut cursor, "010", -2); assert!(cursor.next_dfs().expect("Should parse next dfs").is_none()); assert!(cursor.current().is_none()); } #[test] fn test_cursor_next_sibling_no_sibling_ptr() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_dfs(&mut cursor, "001", 0); // Down to the first child of the root entry. assert_next_dfs(&mut cursor, "002", 1); // Now iterate all children of the root via `next_sibling`. assert_next_sibling(&mut cursor, "004"); assert_next_sibling(&mut cursor, "007"); assert_next_sibling(&mut cursor, "010"); // There should be no more siblings. assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor.current().is_none()); } #[test] fn test_cursor_next_sibling_continuation() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); assert_next_dfs(&mut cursor, "001", 0); // Down to the first child of the root entry. assert_next_dfs(&mut cursor, "002", 1); // Get the next sibling, then iterate its children assert_next_sibling(&mut cursor, "004"); assert_next_dfs(&mut cursor, "005", 1); assert_next_sibling(&mut cursor, "006"); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); // And we should be able to continue with the children of the root entry. assert_next_dfs(&mut cursor, "007", -1); assert_next_sibling(&mut cursor, "010"); // There should be no more siblings. assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor.current().is_none()); } fn entries_cursor_sibling_abbrev_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr(DW_AT_sibling, DW_FORM_ref1) .abbrev_attr_null() .abbrev(2, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null(); section.get_contents().unwrap() } fn entries_cursor_sibling_entries_buf(header_size: usize) -> Vec { let start = Label::new(); let sibling004_ref = Label::new(); let sibling004 = Label::new(); let sibling009_ref = Label::new(); let sibling009 = Label::new(); #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .mark(&start) .die(2, |s| s.attr_string("001")) // Valid sibling attribute. .die(1, |s| s.attr_string("002").D8(&sibling004_ref)) // Invalid code to ensure the sibling attribute was used. .die(10, |s| s.attr_string("003")) .die_null() .die_null() .mark(&sibling004) // Invalid sibling attribute. .die(1, |s| s.attr_string("004").attr_ref1(255)) .die(2, |s| s.attr_string("005")) .die_null() .die_null() // Sibling attribute in child only. .die(2, |s| s.attr_string("006")) // Valid sibling attribute. .die(1, |s| s.attr_string("007").D8(&sibling009_ref)) // Invalid code to ensure the sibling attribute was used. .die(10, |s| s.attr_string("008")) .die_null() .die_null() .mark(&sibling009) .die(2, |s| s.attr_string("009")) .die_null() .die_null() // No sibling attribute. .die(2, |s| s.attr_string("010")) .die(2, |s| s.attr_string("011")) .die_null() .die_null() .die_null(); let offset = header_size as u64 + (&sibling004 - &start) as u64; sibling004_ref.set_const(offset); let offset = header_size as u64 + (&sibling009 - &start) as u64; sibling009_ref.set_const(offset); section.get_contents().unwrap() } fn test_cursor_next_sibling_with_ptr(cursor: &mut EntriesCursor>) { assert_next_dfs(cursor, "001", 0); // Down to the first child of the root. assert_next_dfs(cursor, "002", 1); // Now iterate all children of the root via `next_sibling`. assert_valid_sibling_ptr(&cursor); assert_next_sibling(cursor, "004"); assert_next_sibling(cursor, "006"); assert_next_sibling(cursor, "010"); // There should be no more siblings. assert!(cursor .next_sibling() .expect("Should parse next sibling") .is_none()); assert!(cursor.current().is_none()); } #[test] fn test_debug_info_next_sibling_with_ptr() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; let header_size = unit.size_of_header(); let entries_buf = entries_cursor_sibling_entries_buf(header_size); unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = section.get_contents().unwrap(); let debug_info = DebugInfo::new(&info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrev_buf = entries_cursor_sibling_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); test_cursor_next_sibling_with_ptr(&mut cursor); } #[test] fn test_debug_types_next_sibling_with_ptr() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0), type_offset: UnitOffset(0), }, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; let header_size = unit.size_of_header(); let entries_buf = entries_cursor_sibling_entries_buf(header_size); unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = section.get_contents().unwrap(); let debug_types = DebugTypes::new(&info_buf, LittleEndian); let unit = debug_types .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrev_buf = entries_cursor_sibling_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(&abbrev_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit.entries(&abbrevs); test_cursor_next_sibling_with_ptr(&mut cursor); } #[test] fn test_entries_at_offset() { let info_buf = &entries_cursor_tests_debug_info_buf(); let debug_info = DebugInfo::new(info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs_buf = &entries_cursor_tests_abbrev_buf(); let debug_abbrev = DebugAbbrev::new(abbrevs_buf, LittleEndian); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut cursor = unit .entries_at_offset(&abbrevs, UnitOffset(unit.header_size())) .unwrap(); assert_next_entry(&mut cursor, "001"); let cursor = unit.entries_at_offset(&abbrevs, UnitOffset(0)); match cursor { Err(Error::OffsetOutOfBounds) => {} otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } } } fn entries_tree_tests_debug_abbrevs_buf() -> Vec { #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev(2, DW_TAG_subprogram, DW_CHILDREN_no) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null() .get_contents() .unwrap(); section } fn entries_tree_tests_debug_info_buf(header_size: usize) -> (Vec, UnitOffset) { let start = Label::new(); let entry2 = Label::new(); #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .mark(&start) .die(1, |s| s.attr_string("root")) .die(1, |s| s.attr_string("1")) .die(1, |s| s.attr_string("1a")) .die_null() .die(2, |s| s.attr_string("1b")) .die_null() .mark(&entry2) .die(1, |s| s.attr_string("2")) .die(1, |s| s.attr_string("2a")) .die(1, |s| s.attr_string("2a1")) .die_null() .die_null() .die(1, |s| s.attr_string("2b")) .die(2, |s| s.attr_string("2b1")) .die_null() .die_null() .die(1, |s| s.attr_string("3")) .die(1, |s| s.attr_string("3a")) .die(2, |s| s.attr_string("3a1")) .die(2, |s| s.attr_string("3a2")) .die_null() .die(2, |s| s.attr_string("3b")) .die_null() .die(2, |s| s.attr_string("final")) .die_null() .get_contents() .unwrap(); let entry2 = UnitOffset(header_size + (&entry2 - &start) as usize); (section, entry2) } #[test] fn test_entries_tree() { fn assert_entry<'input, 'abbrev, 'unit, 'tree, Endian>( node: Result< Option>>, >, name: &str, ) -> EntriesTreeIter<'abbrev, 'unit, 'tree, EndianSlice<'input, Endian>> where Endian: Endianity, { let node = node .expect("Should parse entry") .expect("Should have entry"); assert_entry_name(node.entry(), name); node.children() } fn assert_null(node: Result>>>) { match node { Ok(None) => {} otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } } } let abbrevs_buf = entries_tree_tests_debug_abbrevs_buf(); let debug_abbrev = DebugAbbrev::new(&abbrevs_buf, LittleEndian); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; let header_size = unit.size_of_header(); let (entries_buf, entry2) = entries_tree_tests_debug_info_buf(header_size); unit.entries_buf = EndianSlice::new(&entries_buf, LittleEndian); let info_buf = Section::with_endian(Endian::Little) .unit(&mut unit) .get_contents() .unwrap(); let debug_info = DebugInfo::new(&info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("Should parse unit") .expect("and it should be some"); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut tree = unit .entries_tree(&abbrevs, None) .expect("Should have entries tree"); // Test we can restart iteration of the tree. { let mut iter = assert_entry(tree.root().map(Some), "root"); assert_entry(iter.next(), "1"); } { let mut iter = assert_entry(tree.root().map(Some), "root"); assert_entry(iter.next(), "1"); } let mut iter = assert_entry(tree.root().map(Some), "root"); { // Test iteration with children. let mut iter = assert_entry(iter.next(), "1"); { // Test iteration with children flag, but no children. let mut iter = assert_entry(iter.next(), "1a"); assert_null(iter.next()); assert_null(iter.next()); } { // Test iteration without children flag. let mut iter = assert_entry(iter.next(), "1b"); assert_null(iter.next()); assert_null(iter.next()); } assert_null(iter.next()); assert_null(iter.next()); } { // Test skipping over children. let mut iter = assert_entry(iter.next(), "2"); assert_entry(iter.next(), "2a"); assert_entry(iter.next(), "2b"); assert_null(iter.next()); } { // Test skipping after partial iteration. let mut iter = assert_entry(iter.next(), "3"); { let mut iter = assert_entry(iter.next(), "3a"); assert_entry(iter.next(), "3a1"); // Parent iter should be able to skip over "3a2". } assert_entry(iter.next(), "3b"); assert_null(iter.next()); } assert_entry(iter.next(), "final"); assert_null(iter.next()); // Test starting at an offset. let mut tree = unit .entries_tree(&abbrevs, Some(entry2)) .expect("Should have entries tree"); let mut iter = assert_entry(tree.root().map(Some), "2"); assert_entry(iter.next(), "2a"); assert_entry(iter.next(), "2b"); assert_null(iter.next()); } #[test] fn test_entries_raw() { fn assert_abbrev<'input, 'abbrev, 'unit, Endian>( entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>, tag: DwTag, ) -> &'abbrev Abbreviation where Endian: Endianity, { let abbrev = entries .read_abbreviation() .expect("Should parse abbrev") .expect("Should have abbrev"); assert_eq!(abbrev.tag(), tag); abbrev } fn assert_null<'input, 'abbrev, 'unit, Endian>( entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>, ) where Endian: Endianity, { match entries.read_abbreviation() { Ok(None) => {} otherwise => { assert!(false, "Unexpected parse result = {:#?}", otherwise); } } } fn assert_attr<'input, 'abbrev, 'unit, Endian>( entries: &mut EntriesRaw<'abbrev, 'unit, EndianSlice<'input, Endian>>, spec: Option, name: DwAt, value: &str, ) where Endian: Endianity, { let spec = spec.expect("Should have attribute specification"); let attr = entries .read_attribute(spec) .expect("Should parse attribute"); assert_eq!(attr.name(), name); assert_eq!( attr.value(), AttributeValue::String(EndianSlice::new(value.as_bytes(), Endian::default())) ); } #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .abbrev(1, DW_TAG_subprogram, DW_CHILDREN_yes) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr(DW_AT_linkage_name, DW_FORM_string) .abbrev_attr_null() .abbrev(2, DW_TAG_variable, DW_CHILDREN_no) .abbrev_attr(DW_AT_name, DW_FORM_string) .abbrev_attr_null() .abbrev_null(); let abbrevs_buf = section.get_contents().unwrap(); let debug_abbrev = DebugAbbrev::new(&abbrevs_buf, LittleEndian); #[rustfmt::skip] let section = Section::with_endian(Endian::Little) .die(1, |s| s.attr_string("f1").attr_string("l1")) .die(2, |s| s.attr_string("v1")) .die(2, |s| s.attr_string("v2")) .die(1, |s| s.attr_string("f2").attr_string("l2")) .die_null() .die_null(); let entries_buf = section.get_contents().unwrap(); let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&entries_buf, LittleEndian), }; let section = Section::with_endian(Endian::Little).unit(&mut unit); let info_buf = section.get_contents().unwrap(); let debug_info = DebugInfo::new(&info_buf, LittleEndian); let unit = debug_info .units() .next() .expect("should have a unit result") .expect("and it should be ok"); let abbrevs = unit .abbreviations(&debug_abbrev) .expect("Should parse abbreviations"); let mut entries = unit .entries_raw(&abbrevs, None) .expect("Should have entries"); assert_eq!(entries.next_depth(), 0); let abbrev = assert_abbrev(&mut entries, DW_TAG_subprogram); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "f1"); assert_attr(&mut entries, attrs.next(), DW_AT_linkage_name, "l1"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 1); let abbrev = assert_abbrev(&mut entries, DW_TAG_variable); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "v1"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 1); let abbrev = assert_abbrev(&mut entries, DW_TAG_variable); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "v2"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 1); let abbrev = assert_abbrev(&mut entries, DW_TAG_subprogram); let mut attrs = abbrev.attributes().iter().copied(); assert_attr(&mut entries, attrs.next(), DW_AT_name, "f2"); assert_attr(&mut entries, attrs.next(), DW_AT_linkage_name, "l2"); assert!(attrs.next().is_none()); assert_eq!(entries.next_depth(), 2); assert_null(&mut entries); assert_eq!(entries.next_depth(), 1); assert_null(&mut entries); assert_eq!(entries.next_depth(), 0); assert!(entries.is_empty()); } #[test] fn test_debug_info_offset() { let padding = &[0; 10]; let entries = &[0; 20]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(entries, LittleEndian), }; Section::with_endian(Endian::Little) .append_bytes(padding) .unit(&mut unit); let offset = padding.len(); let header_length = unit.size_of_header(); let length = unit.length_including_self(); assert_eq!(DebugInfoOffset(0).to_unit_offset(&unit), None); assert_eq!(DebugInfoOffset(offset - 1).to_unit_offset(&unit), None); assert_eq!(DebugInfoOffset(offset).to_unit_offset(&unit), None); assert_eq!( DebugInfoOffset(offset + header_length - 1).to_unit_offset(&unit), None ); assert_eq!( DebugInfoOffset(offset + header_length).to_unit_offset(&unit), Some(UnitOffset(header_length)) ); assert_eq!( DebugInfoOffset(offset + length - 1).to_unit_offset(&unit), Some(UnitOffset(length - 1)) ); assert_eq!(DebugInfoOffset(offset + length).to_unit_offset(&unit), None); assert_eq!( UnitOffset(header_length).to_debug_info_offset(&unit), Some(DebugInfoOffset(offset + header_length)) ); assert_eq!( UnitOffset(length - 1).to_debug_info_offset(&unit), Some(DebugInfoOffset(offset + length - 1)) ); } #[test] fn test_debug_types_offset() { let padding = &[0; 10]; let entries = &[0; 20]; let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Type { type_signature: DebugTypeSignature(0), type_offset: UnitOffset(0), }, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugTypesOffset(0).into(), entries_buf: EndianSlice::new(entries, LittleEndian), }; Section::with_endian(Endian::Little) .append_bytes(padding) .unit(&mut unit); let offset = padding.len(); let header_length = unit.size_of_header(); let length = unit.length_including_self(); assert_eq!(DebugTypesOffset(0).to_unit_offset(&unit), None); assert_eq!(DebugTypesOffset(offset - 1).to_unit_offset(&unit), None); assert_eq!(DebugTypesOffset(offset).to_unit_offset(&unit), None); assert_eq!( DebugTypesOffset(offset + header_length - 1).to_unit_offset(&unit), None ); assert_eq!( DebugTypesOffset(offset + header_length).to_unit_offset(&unit), Some(UnitOffset(header_length)) ); assert_eq!( DebugTypesOffset(offset + length - 1).to_unit_offset(&unit), Some(UnitOffset(length - 1)) ); assert_eq!( DebugTypesOffset(offset + length).to_unit_offset(&unit), None ); assert_eq!( UnitOffset(header_length).to_debug_types_offset(&unit), Some(DebugTypesOffset(offset + header_length)) ); assert_eq!( UnitOffset(length - 1).to_debug_types_offset(&unit), Some(DebugTypesOffset(offset + length - 1)) ); } #[test] fn test_length_including_self() { let encoding = Encoding { format: Format::Dwarf32, version: 4, address_size: 4, }; let mut unit = UnitHeader { encoding, unit_length: 0, unit_type: UnitType::Compilation, debug_abbrev_offset: DebugAbbrevOffset(0), unit_offset: DebugInfoOffset(0).into(), entries_buf: EndianSlice::new(&[], LittleEndian), }; unit.encoding.format = Format::Dwarf32; assert_eq!(unit.length_including_self(), 4); unit.encoding.format = Format::Dwarf64; assert_eq!(unit.length_including_self(), 12); unit.unit_length = 10; assert_eq!(unit.length_including_self(), 22); } #[test] fn test_parse_type_unit_abbrevs() { let types_buf = [ // Type unit header 0x25, 0x00, 0x00, 0x00, // 32-bit unit length = 37 0x04, 0x00, // Version 4 0x00, 0x00, 0x00, 0x00, // debug_abbrev_offset 0x04, // Address size 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // Type signature 0x01, 0x02, 0x03, 0x04, // Type offset // DIEs // Abbreviation code 0x01, // Attribute of form DW_FORM_string = "foo\0" 0x66, 0x6f, 0x6f, 0x00, // Children // Abbreviation code 0x01, // Attribute of form DW_FORM_string = "foo\0" 0x66, 0x6f, 0x6f, 0x00, // Children // Abbreviation code 0x01, // Attribute of form DW_FORM_string = "foo\0" 0x66, 0x6f, 0x6f, 0x00, // Children 0x00, // End of children 0x00, // End of children 0x00, // End of children ]; let debug_types = DebugTypes::new(&types_buf, LittleEndian); let abbrev_buf = [ // Code 0x01, // DW_TAG_subprogram 0x2e, // DW_CHILDREN_yes 0x01, // Begin attributes 0x03, // Attribute name = DW_AT_name 0x08, // Attribute form = DW_FORM_string 0x00, 0x00, // End attributes 0x00, // Null terminator ]; let get_some_type_unit = || debug_types.units().next().unwrap().unwrap(); let unit = get_some_type_unit(); let read_debug_abbrev_section_somehow = || &abbrev_buf; let debug_abbrev = DebugAbbrev::new(read_debug_abbrev_section_somehow(), LittleEndian); let _abbrevs_for_unit = unit.abbreviations(&debug_abbrev).unwrap(); } }