| use core::slice; |
| |
| use crate::common::SectionId; |
| use crate::constants; |
| use crate::endianity::Endianity; |
| use crate::read::{EndianSlice, Error, Reader, ReaderOffset, Result, Section}; |
| |
| /// The data in the `.debug_cu_index` section of a `.dwp` file. |
| /// |
| /// This section contains the compilation unit index. |
| #[derive(Debug, Default, Clone, Copy)] |
| pub struct DebugCuIndex<R> { |
| section: R, |
| } |
| |
| impl<'input, Endian> DebugCuIndex<EndianSlice<'input, Endian>> |
| where |
| Endian: Endianity, |
| { |
| /// Construct a new `DebugCuIndex` instance from the data in the `.debug_cu_index` |
| /// section. |
| pub fn new(section: &'input [u8], endian: Endian) -> Self { |
| Self::from(EndianSlice::new(section, endian)) |
| } |
| } |
| |
| impl<R> Section<R> for DebugCuIndex<R> { |
| fn id() -> SectionId { |
| SectionId::DebugCuIndex |
| } |
| |
| fn reader(&self) -> &R { |
| &self.section |
| } |
| } |
| |
| impl<R> From<R> for DebugCuIndex<R> { |
| fn from(section: R) -> Self { |
| DebugCuIndex { section } |
| } |
| } |
| |
| impl<R: Reader> DebugCuIndex<R> { |
| /// Parse the index header. |
| pub fn index(self) -> Result<UnitIndex<R>> { |
| UnitIndex::parse(self.section) |
| } |
| } |
| |
| /// The data in the `.debug_tu_index` section of a `.dwp` file. |
| /// |
| /// This section contains the type unit index. |
| #[derive(Debug, Default, Clone, Copy)] |
| pub struct DebugTuIndex<R> { |
| section: R, |
| } |
| |
| impl<'input, Endian> DebugTuIndex<EndianSlice<'input, Endian>> |
| where |
| Endian: Endianity, |
| { |
| /// Construct a new `DebugTuIndex` instance from the data in the `.debug_tu_index` |
| /// section. |
| pub fn new(section: &'input [u8], endian: Endian) -> Self { |
| Self::from(EndianSlice::new(section, endian)) |
| } |
| } |
| |
| impl<R> Section<R> for DebugTuIndex<R> { |
| fn id() -> SectionId { |
| SectionId::DebugTuIndex |
| } |
| |
| fn reader(&self) -> &R { |
| &self.section |
| } |
| } |
| |
| impl<R> From<R> for DebugTuIndex<R> { |
| fn from(section: R) -> Self { |
| DebugTuIndex { section } |
| } |
| } |
| |
| impl<R: Reader> DebugTuIndex<R> { |
| /// Parse the index header. |
| pub fn index(self) -> Result<UnitIndex<R>> { |
| UnitIndex::parse(self.section) |
| } |
| } |
| |
| const SECTION_COUNT_MAX: u8 = 8; |
| |
| /// The partially parsed index from a `DebugCuIndex` or `DebugTuIndex`. |
| #[derive(Debug, Clone)] |
| pub struct UnitIndex<R: Reader> { |
| version: u16, |
| section_count: u32, |
| unit_count: u32, |
| slot_count: u32, |
| hash_ids: R, |
| hash_rows: R, |
| // Only `section_count` values are valid. |
| sections: [SectionId; SECTION_COUNT_MAX as usize], |
| offsets: R, |
| sizes: R, |
| } |
| |
| impl<R: Reader> UnitIndex<R> { |
| fn parse(mut input: R) -> Result<UnitIndex<R>> { |
| if input.is_empty() { |
| return Ok(UnitIndex { |
| version: 5, |
| section_count: 0, |
| unit_count: 0, |
| slot_count: 0, |
| hash_ids: input.clone(), |
| hash_rows: input.clone(), |
| sections: [SectionId::DebugAbbrev; SECTION_COUNT_MAX as usize], |
| offsets: input.clone(), |
| sizes: input.clone(), |
| }); |
| } |
| |
| // GNU split-dwarf extension to DWARF 4 uses a 32-bit version, |
| // but DWARF 5 uses a 16-bit version followed by 16-bit padding. |
| let mut original_input = input.clone(); |
| let version; |
| if input.read_u32()? == 2 { |
| version = 2 |
| } else { |
| version = original_input.read_u16()?; |
| if version != 5 { |
| return Err(Error::UnknownVersion(version.into())); |
| } |
| } |
| |
| let section_count = input.read_u32()?; |
| let unit_count = input.read_u32()?; |
| let slot_count = input.read_u32()?; |
| if slot_count == 0 || slot_count & (slot_count - 1) != 0 || slot_count <= unit_count { |
| return Err(Error::InvalidIndexSlotCount); |
| } |
| |
| let hash_ids = input.split(R::Offset::from_u64(u64::from(slot_count) * 8)?)?; |
| let hash_rows = input.split(R::Offset::from_u64(u64::from(slot_count) * 4)?)?; |
| |
| let mut sections = [SectionId::DebugAbbrev; SECTION_COUNT_MAX as usize]; |
| if section_count > SECTION_COUNT_MAX.into() { |
| return Err(Error::InvalidIndexSectionCount); |
| } |
| for i in 0..section_count { |
| let section = input.read_u32()?; |
| sections[i as usize] = if version == 2 { |
| match constants::DwSectV2(section) { |
| constants::DW_SECT_V2_INFO => SectionId::DebugInfo, |
| constants::DW_SECT_V2_TYPES => SectionId::DebugTypes, |
| constants::DW_SECT_V2_ABBREV => SectionId::DebugAbbrev, |
| constants::DW_SECT_V2_LINE => SectionId::DebugLine, |
| constants::DW_SECT_V2_LOC => SectionId::DebugLoc, |
| constants::DW_SECT_V2_STR_OFFSETS => SectionId::DebugStrOffsets, |
| constants::DW_SECT_V2_MACINFO => SectionId::DebugMacinfo, |
| constants::DW_SECT_V2_MACRO => SectionId::DebugMacro, |
| _ => return Err(Error::UnknownIndexSection), |
| } |
| } else { |
| match constants::DwSect(section) { |
| constants::DW_SECT_INFO => SectionId::DebugInfo, |
| constants::DW_SECT_ABBREV => SectionId::DebugAbbrev, |
| constants::DW_SECT_LINE => SectionId::DebugLine, |
| constants::DW_SECT_LOCLISTS => SectionId::DebugLocLists, |
| constants::DW_SECT_STR_OFFSETS => SectionId::DebugStrOffsets, |
| constants::DW_SECT_MACRO => SectionId::DebugMacro, |
| constants::DW_SECT_RNGLISTS => SectionId::DebugRngLists, |
| _ => return Err(Error::UnknownIndexSection), |
| } |
| }; |
| } |
| |
| let offsets = input.split(R::Offset::from_u64( |
| u64::from(unit_count) * u64::from(section_count) * 4, |
| )?)?; |
| let sizes = input.split(R::Offset::from_u64( |
| u64::from(unit_count) * u64::from(section_count) * 4, |
| )?)?; |
| |
| Ok(UnitIndex { |
| version, |
| section_count, |
| unit_count, |
| slot_count, |
| hash_ids, |
| hash_rows, |
| sections, |
| offsets, |
| sizes, |
| }) |
| } |
| |
| /// Find `id` in the index hash table, and return the row index. |
| /// |
| /// `id` may be a compilation unit ID if this index is from `.debug_cu_index`, |
| /// or a type signature if this index is from `.debug_tu_index`. |
| pub fn find(&self, id: u64) -> Option<u32> { |
| if self.slot_count == 0 { |
| return None; |
| } |
| let mask = u64::from(self.slot_count - 1); |
| let mut hash1 = id & mask; |
| let hash2 = ((id >> 32) & mask) | 1; |
| for _ in 0..self.slot_count { |
| // The length of these arrays was validated in `UnitIndex::parse`. |
| let mut hash_ids = self.hash_ids.clone(); |
| hash_ids.skip(R::Offset::from_u64(hash1 * 8).ok()?).ok()?; |
| let hash_id = hash_ids.read_u64().ok()?; |
| if hash_id == id { |
| let mut hash_rows = self.hash_rows.clone(); |
| hash_rows.skip(R::Offset::from_u64(hash1 * 4).ok()?).ok()?; |
| let hash_row = hash_rows.read_u32().ok()?; |
| return Some(hash_row); |
| } |
| if hash_id == 0 { |
| return None; |
| } |
| hash1 = (hash1 + hash2) & mask; |
| } |
| None |
| } |
| |
| /// Return the section offsets and sizes for the given row index. |
| pub fn sections(&self, mut row: u32) -> Result<UnitIndexSectionIterator<R>> { |
| if row == 0 { |
| return Err(Error::InvalidIndexRow); |
| } |
| row -= 1; |
| if row >= self.unit_count { |
| return Err(Error::InvalidIndexRow); |
| } |
| let mut offsets = self.offsets.clone(); |
| offsets.skip(R::Offset::from_u64( |
| u64::from(row) * u64::from(self.section_count) * 4, |
| )?)?; |
| let mut sizes = self.sizes.clone(); |
| sizes.skip(R::Offset::from_u64( |
| u64::from(row) * u64::from(self.section_count) * 4, |
| )?)?; |
| Ok(UnitIndexSectionIterator { |
| sections: self.sections[..self.section_count as usize].iter(), |
| offsets, |
| sizes, |
| }) |
| } |
| |
| /// Return the version. |
| pub fn version(&self) -> u16 { |
| self.version |
| } |
| |
| /// Return the number of sections. |
| pub fn section_count(&self) -> u32 { |
| self.section_count |
| } |
| |
| /// Return the number of units. |
| pub fn unit_count(&self) -> u32 { |
| self.unit_count |
| } |
| |
| /// Return the number of slots. |
| pub fn slot_count(&self) -> u32 { |
| self.slot_count |
| } |
| } |
| |
| /// An iterator over the section offsets and sizes for a row in a `UnitIndex`. |
| #[derive(Debug, Clone)] |
| pub struct UnitIndexSectionIterator<'index, R: Reader> { |
| sections: slice::Iter<'index, SectionId>, |
| offsets: R, |
| sizes: R, |
| } |
| |
| impl<'index, R: Reader> Iterator for UnitIndexSectionIterator<'index, R> { |
| type Item = UnitIndexSection; |
| |
| fn next(&mut self) -> Option<UnitIndexSection> { |
| let section = *self.sections.next()?; |
| // The length of these arrays was validated in `UnitIndex::parse`. |
| let offset = self.offsets.read_u32().ok()?; |
| let size = self.sizes.read_u32().ok()?; |
| Some(UnitIndexSection { |
| section, |
| offset, |
| size, |
| }) |
| } |
| } |
| |
| /// Information about a unit's contribution to a section in a `.dwp` file. |
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| pub struct UnitIndexSection { |
| /// The section kind. |
| pub section: SectionId, |
| /// The base offset of the unit's contribution to the section. |
| pub offset: u32, |
| /// The size of the unit's contribution to the section. |
| pub size: u32, |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use crate::endianity::BigEndian; |
| use test_assembler::{Endian, Section}; |
| |
| #[test] |
| fn test_empty() { |
| let buf = EndianSlice::new(&[], BigEndian); |
| let index = UnitIndex::parse(buf).unwrap(); |
| assert!(index.find(0).is_none()); |
| } |
| |
| #[test] |
| fn test_version_2() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D32(2).D32(0).D32(0).D32(1) |
| // Slots. |
| .D64(0).D32(0); |
| let buf = section.get_contents().unwrap(); |
| let buf = EndianSlice::new(&buf, BigEndian); |
| let index = UnitIndex::parse(buf).unwrap(); |
| assert_eq!(index.version, 2); |
| } |
| |
| #[test] |
| fn test_version_5() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D16(5).D16(0).D32(0).D32(0).D32(1) |
| // Slots. |
| .D64(0).D32(0); |
| let buf = section.get_contents().unwrap(); |
| let buf = EndianSlice::new(&buf, BigEndian); |
| let index = UnitIndex::parse(buf).unwrap(); |
| assert_eq!(index.version, 5); |
| } |
| |
| #[test] |
| fn test_version_5_invalid() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D32(5).D32(0).D32(0).D32(1) |
| // Slots. |
| .D64(0).D32(0); |
| let buf = section.get_contents().unwrap(); |
| let buf = EndianSlice::new(&buf, BigEndian); |
| assert!(UnitIndex::parse(buf).is_err()); |
| } |
| |
| #[test] |
| fn test_version_2_sections() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D32(2).D32(8).D32(1).D32(2) |
| // Slots. |
| .D64(0).D64(0).D32(0).D32(0) |
| // Sections. |
| .D32(constants::DW_SECT_V2_INFO.0) |
| .D32(constants::DW_SECT_V2_TYPES.0) |
| .D32(constants::DW_SECT_V2_ABBREV.0) |
| .D32(constants::DW_SECT_V2_LINE.0) |
| .D32(constants::DW_SECT_V2_LOC.0) |
| .D32(constants::DW_SECT_V2_STR_OFFSETS.0) |
| .D32(constants::DW_SECT_V2_MACINFO.0) |
| .D32(constants::DW_SECT_V2_MACRO.0) |
| // Offsets. |
| .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17).D32(18) |
| // Sizes. |
| .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27).D32(28); |
| let buf = section.get_contents().unwrap(); |
| let buf = EndianSlice::new(&buf, BigEndian); |
| let index = UnitIndex::parse(buf).unwrap(); |
| assert_eq!(index.section_count, 8); |
| assert_eq!( |
| index.sections, |
| [ |
| SectionId::DebugInfo, |
| SectionId::DebugTypes, |
| SectionId::DebugAbbrev, |
| SectionId::DebugLine, |
| SectionId::DebugLoc, |
| SectionId::DebugStrOffsets, |
| SectionId::DebugMacinfo, |
| SectionId::DebugMacro, |
| ] |
| ); |
| #[rustfmt::skip] |
| let expect = [ |
| UnitIndexSection { section: SectionId::DebugInfo, offset: 11, size: 21 }, |
| UnitIndexSection { section: SectionId::DebugTypes, offset: 12, size: 22 }, |
| UnitIndexSection { section: SectionId::DebugAbbrev, offset: 13, size: 23 }, |
| UnitIndexSection { section: SectionId::DebugLine, offset: 14, size: 24 }, |
| UnitIndexSection { section: SectionId::DebugLoc, offset: 15, size: 25 }, |
| UnitIndexSection { section: SectionId::DebugStrOffsets, offset: 16, size: 26 }, |
| UnitIndexSection { section: SectionId::DebugMacinfo, offset: 17, size: 27 }, |
| UnitIndexSection { section: SectionId::DebugMacro, offset: 18, size: 28 }, |
| ]; |
| let mut sections = index.sections(1).unwrap(); |
| for section in &expect { |
| assert_eq!(*section, sections.next().unwrap()); |
| } |
| assert!(sections.next().is_none()); |
| } |
| |
| #[test] |
| fn test_version_5_sections() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D16(5).D16(0).D32(7).D32(1).D32(2) |
| // Slots. |
| .D64(0).D64(0).D32(0).D32(0) |
| // Sections. |
| .D32(constants::DW_SECT_INFO.0) |
| .D32(constants::DW_SECT_ABBREV.0) |
| .D32(constants::DW_SECT_LINE.0) |
| .D32(constants::DW_SECT_LOCLISTS.0) |
| .D32(constants::DW_SECT_STR_OFFSETS.0) |
| .D32(constants::DW_SECT_MACRO.0) |
| .D32(constants::DW_SECT_RNGLISTS.0) |
| // Offsets. |
| .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17) |
| // Sizes. |
| .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27); |
| let buf = section.get_contents().unwrap(); |
| let buf = EndianSlice::new(&buf, BigEndian); |
| let index = UnitIndex::parse(buf).unwrap(); |
| assert_eq!(index.section_count, 7); |
| assert_eq!( |
| index.sections[..7], |
| [ |
| SectionId::DebugInfo, |
| SectionId::DebugAbbrev, |
| SectionId::DebugLine, |
| SectionId::DebugLocLists, |
| SectionId::DebugStrOffsets, |
| SectionId::DebugMacro, |
| SectionId::DebugRngLists, |
| ] |
| ); |
| #[rustfmt::skip] |
| let expect = [ |
| UnitIndexSection { section: SectionId::DebugInfo, offset: 11, size: 21 }, |
| UnitIndexSection { section: SectionId::DebugAbbrev, offset: 12, size: 22 }, |
| UnitIndexSection { section: SectionId::DebugLine, offset: 13, size: 23 }, |
| UnitIndexSection { section: SectionId::DebugLocLists, offset: 14, size: 24 }, |
| UnitIndexSection { section: SectionId::DebugStrOffsets, offset: 15, size: 25 }, |
| UnitIndexSection { section: SectionId::DebugMacro, offset: 16, size: 26 }, |
| UnitIndexSection { section: SectionId::DebugRngLists, offset: 17, size: 27 }, |
| ]; |
| let mut sections = index.sections(1).unwrap(); |
| for section in &expect { |
| assert_eq!(*section, sections.next().unwrap()); |
| } |
| assert!(sections.next().is_none()); |
| |
| assert!(index.sections(0).is_err()); |
| assert!(index.sections(2).is_err()); |
| } |
| |
| #[test] |
| fn test_hash() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D16(5).D16(0).D32(2).D32(3).D32(4) |
| // Slots. |
| .D64(0xffff_fff2_ffff_fff1) |
| .D64(0xffff_fff0_ffff_fff1) |
| .D64(0xffff_fff1_ffff_fff1) |
| .D64(0) |
| .D32(3).D32(1).D32(2).D32(0) |
| // Sections. |
| .D32(constants::DW_SECT_INFO.0) |
| .D32(constants::DW_SECT_ABBREV.0) |
| // Offsets. |
| .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0) |
| // Sizes. |
| .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0); |
| let buf = section.get_contents().unwrap(); |
| let buf = EndianSlice::new(&buf, BigEndian); |
| let index = UnitIndex::parse(buf).unwrap(); |
| assert_eq!(index.version(), 5); |
| assert_eq!(index.slot_count(), 4); |
| assert_eq!(index.unit_count(), 3); |
| assert_eq!(index.section_count(), 2); |
| assert_eq!(index.find(0xffff_fff0_ffff_fff1), Some(1)); |
| assert_eq!(index.find(0xffff_fff1_ffff_fff1), Some(2)); |
| assert_eq!(index.find(0xffff_fff2_ffff_fff1), Some(3)); |
| assert_eq!(index.find(0xffff_fff3_ffff_fff1), None); |
| } |
| |
| #[test] |
| fn test_cu_index() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D16(5).D16(0).D32(0).D32(0).D32(1) |
| // Slots. |
| .D64(0).D32(0); |
| let buf = section.get_contents().unwrap(); |
| let cu_index = DebugCuIndex::new(&buf, BigEndian); |
| let index = cu_index.index().unwrap(); |
| assert_eq!(index.version, 5); |
| } |
| |
| #[test] |
| fn test_tu_index() { |
| #[rustfmt::skip] |
| let section = Section::with_endian(Endian::Big) |
| // Header. |
| .D16(5).D16(0).D32(0).D32(0).D32(1) |
| // Slots. |
| .D64(0).D32(0); |
| let buf = section.get_contents().unwrap(); |
| let tu_index = DebugTuIndex::new(&buf, BigEndian); |
| let index = tu_index.index().unwrap(); |
| assert_eq!(index.version, 5); |
| } |
| } |