use core::slice;
use crate::common::SectionId;
use crate::constants;
use crate::endianity::Endianity;
use crate::read::{EndianSlice, Error, Reader, ReaderOffset, Result, Section};
#[derive(Debug, Default, Clone, Copy)]
pub struct DebugCuIndex<R> {
section: R,
}
impl<'input, Endian> DebugCuIndex<EndianSlice<'input, Endian>>
where
Endian: Endianity,
{
pub fn new(section: &'input [u8], endian: Endian) -> Self {
Self::from(EndianSlice::new(section, endian))
}
}
impl<T> DebugCuIndex<T> {
pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugCuIndex<R>
where
F: FnMut(&'a T) -> R,
{
borrow(&self.section).into()
}
}
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> {
pub fn index(self) -> Result<UnitIndex<R>> {
UnitIndex::parse(self.section)
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct DebugTuIndex<R> {
section: R,
}
impl<'input, Endian> DebugTuIndex<EndianSlice<'input, Endian>>
where
Endian: Endianity,
{
pub fn new(section: &'input [u8], endian: Endian) -> Self {
Self::from(EndianSlice::new(section, endian))
}
}
impl<T> DebugTuIndex<T> {
pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTuIndex<R>
where
F: FnMut(&'a T) -> R,
{
borrow(&self.section).into()
}
}
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> {
pub fn index(self) -> Result<UnitIndex<R>> {
UnitIndex::parse(self.section)
}
}
const SECTION_COUNT_MAX: u8 = 8;
#[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,
sections: [IndexSectionId; 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: 0,
section_count: 0,
unit_count: 0,
slot_count: 0,
hash_ids: input.clone(),
hash_rows: input.clone(),
sections: [IndexSectionId::DebugAbbrev; SECTION_COUNT_MAX as usize],
offsets: input.clone(),
sizes: input.clone(),
});
}
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 = [IndexSectionId::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 => IndexSectionId::DebugInfo,
constants::DW_SECT_V2_TYPES => IndexSectionId::DebugTypes,
constants::DW_SECT_V2_ABBREV => IndexSectionId::DebugAbbrev,
constants::DW_SECT_V2_LINE => IndexSectionId::DebugLine,
constants::DW_SECT_V2_LOC => IndexSectionId::DebugLoc,
constants::DW_SECT_V2_STR_OFFSETS => IndexSectionId::DebugStrOffsets,
constants::DW_SECT_V2_MACINFO => IndexSectionId::DebugMacinfo,
constants::DW_SECT_V2_MACRO => IndexSectionId::DebugMacro,
section => return Err(Error::UnknownIndexSectionV2(section)),
}
} else {
match constants::DwSect(section) {
constants::DW_SECT_INFO => IndexSectionId::DebugInfo,
constants::DW_SECT_ABBREV => IndexSectionId::DebugAbbrev,
constants::DW_SECT_LINE => IndexSectionId::DebugLine,
constants::DW_SECT_LOCLISTS => IndexSectionId::DebugLocLists,
constants::DW_SECT_STR_OFFSETS => IndexSectionId::DebugStrOffsets,
constants::DW_SECT_MACRO => IndexSectionId::DebugMacro,
constants::DW_SECT_RNGLISTS => IndexSectionId::DebugRngLists,
section => return Err(Error::UnknownIndexSection(section)),
}
};
}
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,
})
}
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 {
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
}
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,
})
}
pub fn version(&self) -> u16 {
self.version
}
pub fn section_count(&self) -> u32 {
self.section_count
}
pub fn unit_count(&self) -> u32 {
self.unit_count
}
pub fn slot_count(&self) -> u32 {
self.slot_count
}
}
#[derive(Debug, Clone)]
pub struct UnitIndexSectionIterator<'index, R: Reader> {
sections: slice::Iter<'index, IndexSectionId>,
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()?;
let offset = self.offsets.read_u32().ok()?;
let size = self.sizes.read_u32().ok()?;
Some(UnitIndexSection {
section,
offset,
size,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UnitIndexSection {
pub section: IndexSectionId,
pub offset: u32,
pub size: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IndexSectionId {
DebugAbbrev,
DebugInfo,
DebugLine,
DebugLoc,
DebugLocLists,
DebugMacinfo,
DebugMacro,
DebugRngLists,
DebugStrOffsets,
DebugTypes,
}
impl IndexSectionId {
pub fn section_id(self) -> SectionId {
match self {
IndexSectionId::DebugAbbrev => SectionId::DebugAbbrev,
IndexSectionId::DebugInfo => SectionId::DebugInfo,
IndexSectionId::DebugLine => SectionId::DebugLine,
IndexSectionId::DebugLoc => SectionId::DebugLoc,
IndexSectionId::DebugLocLists => SectionId::DebugLocLists,
IndexSectionId::DebugMacro => SectionId::DebugMacro,
IndexSectionId::DebugMacinfo => SectionId::DebugMacinfo,
IndexSectionId::DebugRngLists => SectionId::DebugRngLists,
IndexSectionId::DebugStrOffsets => SectionId::DebugStrOffsets,
IndexSectionId::DebugTypes => SectionId::DebugTypes,
}
}
pub fn dwo_name(self) -> &'static str {
self.section_id().dwo_name().unwrap()
}
}
#[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_eq!(index.version(), 0);
assert_eq!(index.unit_count(), 0);
assert_eq!(index.slot_count(), 0);
assert!(index.find(0).is_none());
}
#[test]
fn test_zero_slots() {
#[rustfmt::skip]
let section = Section::with_endian(Endian::Big)
.D32(2).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(), 2);
assert_eq!(index.unit_count(), 0);
assert_eq!(index.slot_count(), 0);
assert!(index.find(0).is_none());
}
#[test]
fn test_version_2() {
#[rustfmt::skip]
let section = Section::with_endian(Endian::Big)
.D32(2).D32(0).D32(0).D32(1)
.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)
.D16(5).D16(0).D32(0).D32(0).D32(1)
.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)
.D32(5).D32(0).D32(0).D32(1)
.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)
.D32(2).D32(8).D32(1).D32(2)
.D64(0).D64(0).D32(0).D32(0)
.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)
.D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17).D32(18)
.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,
[
IndexSectionId::DebugInfo,
IndexSectionId::DebugTypes,
IndexSectionId::DebugAbbrev,
IndexSectionId::DebugLine,
IndexSectionId::DebugLoc,
IndexSectionId::DebugStrOffsets,
IndexSectionId::DebugMacinfo,
IndexSectionId::DebugMacro,
]
);
#[rustfmt::skip]
let expect = [
UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 },
UnitIndexSection { section: IndexSectionId::DebugTypes, offset: 12, size: 22 },
UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 13, size: 23 },
UnitIndexSection { section: IndexSectionId::DebugLine, offset: 14, size: 24 },
UnitIndexSection { section: IndexSectionId::DebugLoc, offset: 15, size: 25 },
UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 16, size: 26 },
UnitIndexSection { section: IndexSectionId::DebugMacinfo, offset: 17, size: 27 },
UnitIndexSection { section: IndexSectionId::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)
.D16(5).D16(0).D32(7).D32(1).D32(2)
.D64(0).D64(0).D32(0).D32(0)
.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)
.D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17)
.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],
[
IndexSectionId::DebugInfo,
IndexSectionId::DebugAbbrev,
IndexSectionId::DebugLine,
IndexSectionId::DebugLocLists,
IndexSectionId::DebugStrOffsets,
IndexSectionId::DebugMacro,
IndexSectionId::DebugRngLists,
]
);
#[rustfmt::skip]
let expect = [
UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 },
UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 12, size: 22 },
UnitIndexSection { section: IndexSectionId::DebugLine, offset: 13, size: 23 },
UnitIndexSection { section: IndexSectionId::DebugLocLists, offset: 14, size: 24 },
UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 15, size: 25 },
UnitIndexSection { section: IndexSectionId::DebugMacro, offset: 16, size: 26 },
UnitIndexSection { section: IndexSectionId::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)
.D16(5).D16(0).D32(2).D32(3).D32(4)
.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)
.D32(constants::DW_SECT_INFO.0)
.D32(constants::DW_SECT_ABBREV.0)
.D32(0).D32(0).D32(0).D32(0).D32(0).D32(0)
.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)
.D16(5).D16(0).D32(0).D32(0).D32(1)
.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)
.D16(5).D16(0).D32(0).D32(0).D32(1)
.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);
}
}