Skip to main content

gimli/read/
index.rs

1use core::slice;
2
3use crate::common::SectionId;
4use crate::constants;
5use crate::endianity::Endianity;
6use crate::read::{EndianSlice, Error, Reader, ReaderOffset, Result, Section};
7
8/// The data in the `.debug_cu_index` section of a `.dwp` file.
9///
10/// This section contains the compilation unit index.
11#[derive(Debug, Default, Clone, Copy)]
12pub struct DebugCuIndex<R> {
13    section: R,
14}
15
16impl<'input, Endian> DebugCuIndex<EndianSlice<'input, Endian>>
17where
18    Endian: Endianity,
19{
20    /// Construct a new `DebugCuIndex` instance from the data in the `.debug_cu_index`
21    /// section.
22    pub fn new(section: &'input [u8], endian: Endian) -> Self {
23        Self::from(EndianSlice::new(section, endian))
24    }
25}
26
27impl<T> DebugCuIndex<T> {
28    /// Create a `DebugCuIndex` section that references the data in `self`.
29    ///
30    /// This is useful when `R` implements `Reader` but `T` does not.
31    ///
32    /// Used by `DwarfPackageSections::borrow`.
33    pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugCuIndex<R>
34    where
35        F: FnMut(&'a T) -> R,
36    {
37        borrow(&self.section).into()
38    }
39}
40
41impl<R> Section<R> for DebugCuIndex<R> {
42    fn id() -> SectionId {
43        SectionId::DebugCuIndex
44    }
45
46    fn reader(&self) -> &R {
47        &self.section
48    }
49}
50
51impl<R> From<R> for DebugCuIndex<R> {
52    fn from(section: R) -> Self {
53        DebugCuIndex { section }
54    }
55}
56
57impl<R: Reader> DebugCuIndex<R> {
58    /// Parse the index header.
59    pub fn index(self) -> Result<UnitIndex<R>> {
60        UnitIndex::parse(self.section)
61    }
62}
63
64/// The data in the `.debug_tu_index` section of a `.dwp` file.
65///
66/// This section contains the type unit index.
67#[derive(Debug, Default, Clone, Copy)]
68pub struct DebugTuIndex<R> {
69    section: R,
70}
71
72impl<'input, Endian> DebugTuIndex<EndianSlice<'input, Endian>>
73where
74    Endian: Endianity,
75{
76    /// Construct a new `DebugTuIndex` instance from the data in the `.debug_tu_index`
77    /// section.
78    pub fn new(section: &'input [u8], endian: Endian) -> Self {
79        Self::from(EndianSlice::new(section, endian))
80    }
81}
82
83impl<T> DebugTuIndex<T> {
84    /// Create a `DebugTuIndex` section that references the data in `self`.
85    ///
86    /// This is useful when `R` implements `Reader` but `T` does not.
87    ///
88    /// Used by `DwarfPackageSections::borrow`.
89    pub(crate) fn borrow<'a, F, R>(&'a self, mut borrow: F) -> DebugTuIndex<R>
90    where
91        F: FnMut(&'a T) -> R,
92    {
93        borrow(&self.section).into()
94    }
95}
96
97impl<R> Section<R> for DebugTuIndex<R> {
98    fn id() -> SectionId {
99        SectionId::DebugTuIndex
100    }
101
102    fn reader(&self) -> &R {
103        &self.section
104    }
105}
106
107impl<R> From<R> for DebugTuIndex<R> {
108    fn from(section: R) -> Self {
109        DebugTuIndex { section }
110    }
111}
112
113impl<R: Reader> DebugTuIndex<R> {
114    /// Parse the index header.
115    pub fn index(self) -> Result<UnitIndex<R>> {
116        UnitIndex::parse(self.section)
117    }
118}
119
120const SECTION_COUNT_MAX: u8 = 8;
121
122/// The partially parsed index from a `DebugCuIndex` or `DebugTuIndex`.
123#[derive(Debug, Clone)]
124pub struct UnitIndex<R: Reader> {
125    version: u16,
126    section_count: u32,
127    unit_count: u32,
128    slot_count: u32,
129    hash_ids: R,
130    hash_rows: R,
131    // Only `section_count` values are valid.
132    sections: [IndexSectionId; SECTION_COUNT_MAX as usize],
133    offsets: R,
134    sizes: R,
135}
136
137impl<R: Reader> UnitIndex<R> {
138    fn parse(mut input: R) -> Result<UnitIndex<R>> {
139        if input.is_empty() {
140            return Ok(UnitIndex {
141                version: 0,
142                section_count: 0,
143                unit_count: 0,
144                slot_count: 0,
145                hash_ids: input.clone(),
146                hash_rows: input.clone(),
147                sections: [IndexSectionId::DebugAbbrev; SECTION_COUNT_MAX as usize],
148                offsets: input.clone(),
149                sizes: input.clone(),
150            });
151        }
152
153        // GNU split-dwarf extension to DWARF 4 uses a 32-bit version,
154        // but DWARF 5 uses a 16-bit version followed by 16-bit padding.
155        let mut original_input = input.clone();
156        let version;
157        if input.read_u32()? == 2 {
158            version = 2
159        } else {
160            version = original_input.read_u16()?;
161            if version != 5 {
162                return Err(Error::UnknownVersion(version.into()));
163            }
164        }
165
166        let section_count = input.read_u32()?;
167        let unit_count = input.read_u32()?;
168        let slot_count = input.read_u32()?;
169        if slot_count != 0 && (slot_count & (slot_count - 1) != 0 || slot_count <= unit_count) {
170            return Err(Error::InvalidIndexSlotCount(slot_count));
171        }
172
173        let hash_ids = input.split(R::Offset::from_u64(u64::from(slot_count) * 8)?)?;
174        let hash_rows = input.split(R::Offset::from_u64(u64::from(slot_count) * 4)?)?;
175
176        let mut sections = [IndexSectionId::DebugAbbrev; SECTION_COUNT_MAX as usize];
177        if section_count > SECTION_COUNT_MAX.into() {
178            return Err(Error::UnsupportedIndexSectionCount(section_count));
179        }
180        for i in 0..section_count {
181            let section = input.read_u32()?;
182            sections[i as usize] = if version == 2 {
183                match constants::DwSectV2(section) {
184                    constants::DW_SECT_V2_INFO => IndexSectionId::DebugInfo,
185                    constants::DW_SECT_V2_TYPES => IndexSectionId::DebugTypes,
186                    constants::DW_SECT_V2_ABBREV => IndexSectionId::DebugAbbrev,
187                    constants::DW_SECT_V2_LINE => IndexSectionId::DebugLine,
188                    constants::DW_SECT_V2_LOC => IndexSectionId::DebugLoc,
189                    constants::DW_SECT_V2_STR_OFFSETS => IndexSectionId::DebugStrOffsets,
190                    constants::DW_SECT_V2_MACINFO => IndexSectionId::DebugMacinfo,
191                    constants::DW_SECT_V2_MACRO => IndexSectionId::DebugMacro,
192                    section => return Err(Error::UnknownIndexSectionV2(section)),
193                }
194            } else {
195                match constants::DwSect(section) {
196                    constants::DW_SECT_INFO => IndexSectionId::DebugInfo,
197                    constants::DW_SECT_ABBREV => IndexSectionId::DebugAbbrev,
198                    constants::DW_SECT_LINE => IndexSectionId::DebugLine,
199                    constants::DW_SECT_LOCLISTS => IndexSectionId::DebugLocLists,
200                    constants::DW_SECT_STR_OFFSETS => IndexSectionId::DebugStrOffsets,
201                    constants::DW_SECT_MACRO => IndexSectionId::DebugMacro,
202                    constants::DW_SECT_RNGLISTS => IndexSectionId::DebugRngLists,
203                    section => return Err(Error::UnknownIndexSection(section)),
204                }
205            };
206        }
207
208        let offsets = input.split(R::Offset::from_u64(
209            u64::from(unit_count) * u64::from(section_count) * 4,
210        )?)?;
211        let sizes = input.split(R::Offset::from_u64(
212            u64::from(unit_count) * u64::from(section_count) * 4,
213        )?)?;
214
215        Ok(UnitIndex {
216            version,
217            section_count,
218            unit_count,
219            slot_count,
220            hash_ids,
221            hash_rows,
222            sections,
223            offsets,
224            sizes,
225        })
226    }
227
228    /// Find `id` in the index hash table, and return the row index.
229    ///
230    /// `id` may be a compilation unit ID if this index is from `.debug_cu_index`,
231    /// or a type signature if this index is from `.debug_tu_index`.
232    pub fn find(&self, id: u64) -> Option<u32> {
233        if self.slot_count == 0 {
234            return None;
235        }
236        let mask = u64::from(self.slot_count - 1);
237        let mut hash1 = id & mask;
238        let hash2 = ((id >> 32) & mask) | 1;
239        for _ in 0..self.slot_count {
240            // The length of these arrays was validated in `UnitIndex::parse`.
241            let mut hash_ids = self.hash_ids.clone();
242            hash_ids.skip(R::Offset::from_u64(hash1 * 8).ok()?).ok()?;
243            let hash_id = hash_ids.read_u64().ok()?;
244            if hash_id == id {
245                let mut hash_rows = self.hash_rows.clone();
246                hash_rows.skip(R::Offset::from_u64(hash1 * 4).ok()?).ok()?;
247                let hash_row = hash_rows.read_u32().ok()?;
248                return Some(hash_row);
249            }
250            if hash_id == 0 {
251                return None;
252            }
253            hash1 = (hash1 + hash2) & mask;
254        }
255        None
256    }
257
258    /// Return the section offsets and sizes for the given row index.
259    pub fn sections(&self, row: u32) -> Result<UnitIndexSectionIterator<'_, R>> {
260        if row == 0 || row > self.unit_count {
261            return Err(Error::InvalidIndexRow(row));
262        }
263        let row_offset =
264            R::Offset::from_u64(u64::from(row - 1) * u64::from(self.section_count) * 4)?;
265        let mut offsets = self.offsets.clone();
266        offsets.skip(row_offset)?;
267        let mut sizes = self.sizes.clone();
268        sizes.skip(row_offset)?;
269        Ok(UnitIndexSectionIterator {
270            sections: self.sections[..self.section_count as usize].iter(),
271            offsets,
272            sizes,
273        })
274    }
275
276    /// Return the version.
277    ///
278    /// Defaults to 0 for empty sections.
279    pub fn version(&self) -> u16 {
280        self.version
281    }
282
283    /// Return the number of sections.
284    pub fn section_count(&self) -> u32 {
285        self.section_count
286    }
287
288    /// Return the number of units.
289    pub fn unit_count(&self) -> u32 {
290        self.unit_count
291    }
292
293    /// Return the number of slots.
294    pub fn slot_count(&self) -> u32 {
295        self.slot_count
296    }
297}
298
299/// An iterator over the section offsets and sizes for a row in a `UnitIndex`.
300#[derive(Debug, Clone)]
301pub struct UnitIndexSectionIterator<'index, R: Reader> {
302    sections: slice::Iter<'index, IndexSectionId>,
303    offsets: R,
304    sizes: R,
305}
306
307impl<'index, R: Reader> Iterator for UnitIndexSectionIterator<'index, R> {
308    type Item = UnitIndexSection;
309
310    fn next(&mut self) -> Option<UnitIndexSection> {
311        let section = *self.sections.next()?;
312        // The length of these arrays was validated in `UnitIndex::parse`.
313        let offset = self.offsets.read_u32().ok()?;
314        let size = self.sizes.read_u32().ok()?;
315        Some(UnitIndexSection {
316            section,
317            offset,
318            size,
319        })
320    }
321}
322
323/// Information about a unit's contribution to a section in a `.dwp` file.
324#[derive(Debug, Clone, Copy, PartialEq, Eq)]
325pub struct UnitIndexSection {
326    /// The section kind.
327    pub section: IndexSectionId,
328    /// The base offset of the unit's contribution to the section.
329    pub offset: u32,
330    /// The size of the unit's contribution to the section.
331    pub size: u32,
332}
333
334/// Section kinds which are permitted in a `.dwp` index.
335#[derive(Debug, Clone, Copy, PartialEq, Eq)]
336pub enum IndexSectionId {
337    /// The `.debug_abbrev.dwo` section.
338    DebugAbbrev,
339    /// The `.debug_info.dwo` section.
340    DebugInfo,
341    /// The `.debug_line.dwo` section.
342    DebugLine,
343    /// The `.debug_loc.dwo` section.
344    DebugLoc,
345    /// The `.debug_loclists.dwo` section.
346    DebugLocLists,
347    /// The `.debug_macinfo.dwo` section.
348    DebugMacinfo,
349    /// The `.debug_macro.dwo` section.
350    DebugMacro,
351    /// The `.debug_rnglists.dwo` section.
352    DebugRngLists,
353    /// The `.debug_str_offsets.dwo` section.
354    DebugStrOffsets,
355    /// The `.debug_types.dwo` section.
356    DebugTypes,
357}
358
359impl IndexSectionId {
360    /// Returns the corresponding `SectionId`.
361    pub fn section_id(self) -> SectionId {
362        match self {
363            IndexSectionId::DebugAbbrev => SectionId::DebugAbbrev,
364            IndexSectionId::DebugInfo => SectionId::DebugInfo,
365            IndexSectionId::DebugLine => SectionId::DebugLine,
366            IndexSectionId::DebugLoc => SectionId::DebugLoc,
367            IndexSectionId::DebugLocLists => SectionId::DebugLocLists,
368            IndexSectionId::DebugMacro => SectionId::DebugMacro,
369            IndexSectionId::DebugMacinfo => SectionId::DebugMacinfo,
370            IndexSectionId::DebugRngLists => SectionId::DebugRngLists,
371            IndexSectionId::DebugStrOffsets => SectionId::DebugStrOffsets,
372            IndexSectionId::DebugTypes => SectionId::DebugTypes,
373        }
374    }
375
376    /// Returns the ELF section name for this kind, when found in a .dwo or .dwp file.
377    pub fn dwo_name(self) -> &'static str {
378        self.section_id().dwo_name().unwrap()
379    }
380}
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385    use crate::endianity::BigEndian;
386    use test_assembler::{Endian, Section};
387
388    #[test]
389    fn test_empty() {
390        let buf = EndianSlice::new(&[], BigEndian);
391        let index = UnitIndex::parse(buf).unwrap();
392        assert_eq!(index.version(), 0);
393        assert_eq!(index.unit_count(), 0);
394        assert_eq!(index.slot_count(), 0);
395        assert!(index.find(0).is_none());
396    }
397
398    #[test]
399    fn test_zero_slots() {
400        #[rustfmt::skip]
401        let section = Section::with_endian(Endian::Big)
402            // Header.
403            .D32(2).D32(0).D32(0).D32(0);
404        let buf = section.get_contents().unwrap();
405        let buf = EndianSlice::new(&buf, BigEndian);
406        let index = UnitIndex::parse(buf).unwrap();
407        assert_eq!(index.version(), 2);
408        assert_eq!(index.unit_count(), 0);
409        assert_eq!(index.slot_count(), 0);
410        assert!(index.find(0).is_none());
411    }
412
413    #[test]
414    fn test_version_2() {
415        #[rustfmt::skip]
416        let section = Section::with_endian(Endian::Big)
417            // Header.
418            .D32(2).D32(0).D32(0).D32(1)
419            // Slots.
420            .D64(0).D32(0);
421        let buf = section.get_contents().unwrap();
422        let buf = EndianSlice::new(&buf, BigEndian);
423        let index = UnitIndex::parse(buf).unwrap();
424        assert_eq!(index.version, 2);
425    }
426
427    #[test]
428    fn test_version_5() {
429        #[rustfmt::skip]
430        let section = Section::with_endian(Endian::Big)
431            // Header.
432            .D16(5).D16(0).D32(0).D32(0).D32(1)
433            // Slots.
434            .D64(0).D32(0);
435        let buf = section.get_contents().unwrap();
436        let buf = EndianSlice::new(&buf, BigEndian);
437        let index = UnitIndex::parse(buf).unwrap();
438        assert_eq!(index.version, 5);
439    }
440
441    #[test]
442    fn test_version_5_invalid() {
443        #[rustfmt::skip]
444        let section = Section::with_endian(Endian::Big)
445            // Header.
446            .D32(5).D32(0).D32(0).D32(1)
447            // Slots.
448            .D64(0).D32(0);
449        let buf = section.get_contents().unwrap();
450        let buf = EndianSlice::new(&buf, BigEndian);
451        assert!(UnitIndex::parse(buf).is_err());
452    }
453
454    #[test]
455    fn test_version_2_sections() {
456        #[rustfmt::skip]
457        let section = Section::with_endian(Endian::Big)
458            // Header.
459            .D32(2).D32(8).D32(1).D32(2)
460            // Slots.
461            .D64(0).D64(0).D32(0).D32(0)
462            // Sections.
463            .D32(constants::DW_SECT_V2_INFO.0)
464            .D32(constants::DW_SECT_V2_TYPES.0)
465            .D32(constants::DW_SECT_V2_ABBREV.0)
466            .D32(constants::DW_SECT_V2_LINE.0)
467            .D32(constants::DW_SECT_V2_LOC.0)
468            .D32(constants::DW_SECT_V2_STR_OFFSETS.0)
469            .D32(constants::DW_SECT_V2_MACINFO.0)
470            .D32(constants::DW_SECT_V2_MACRO.0)
471            // Offsets.
472            .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17).D32(18)
473            // Sizes.
474            .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27).D32(28);
475        let buf = section.get_contents().unwrap();
476        let buf = EndianSlice::new(&buf, BigEndian);
477        let index = UnitIndex::parse(buf).unwrap();
478        assert_eq!(index.section_count, 8);
479        assert_eq!(
480            index.sections,
481            [
482                IndexSectionId::DebugInfo,
483                IndexSectionId::DebugTypes,
484                IndexSectionId::DebugAbbrev,
485                IndexSectionId::DebugLine,
486                IndexSectionId::DebugLoc,
487                IndexSectionId::DebugStrOffsets,
488                IndexSectionId::DebugMacinfo,
489                IndexSectionId::DebugMacro,
490            ]
491        );
492        #[rustfmt::skip]
493        let expect = [
494            UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 },
495            UnitIndexSection { section: IndexSectionId::DebugTypes, offset: 12, size: 22 },
496            UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 13, size: 23 },
497            UnitIndexSection { section: IndexSectionId::DebugLine, offset: 14, size: 24 },
498            UnitIndexSection { section: IndexSectionId::DebugLoc, offset: 15, size: 25 },
499            UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 16, size: 26 },
500            UnitIndexSection { section: IndexSectionId::DebugMacinfo, offset: 17, size: 27 },
501            UnitIndexSection { section: IndexSectionId::DebugMacro, offset: 18, size: 28 },
502        ];
503        let mut sections = index.sections(1).unwrap();
504        for section in &expect {
505            assert_eq!(*section, sections.next().unwrap());
506        }
507        assert!(sections.next().is_none());
508    }
509
510    #[test]
511    fn test_version_5_sections() {
512        #[rustfmt::skip]
513        let section = Section::with_endian(Endian::Big)
514            // Header.
515            .D16(5).D16(0).D32(7).D32(1).D32(2)
516            // Slots.
517            .D64(0).D64(0).D32(0).D32(0)
518            // Sections.
519            .D32(constants::DW_SECT_INFO.0)
520            .D32(constants::DW_SECT_ABBREV.0)
521            .D32(constants::DW_SECT_LINE.0)
522            .D32(constants::DW_SECT_LOCLISTS.0)
523            .D32(constants::DW_SECT_STR_OFFSETS.0)
524            .D32(constants::DW_SECT_MACRO.0)
525            .D32(constants::DW_SECT_RNGLISTS.0)
526            // Offsets.
527            .D32(11).D32(12).D32(13).D32(14).D32(15).D32(16).D32(17)
528            // Sizes.
529            .D32(21).D32(22).D32(23).D32(24).D32(25).D32(26).D32(27);
530        let buf = section.get_contents().unwrap();
531        let buf = EndianSlice::new(&buf, BigEndian);
532        let index = UnitIndex::parse(buf).unwrap();
533        assert_eq!(index.section_count, 7);
534        assert_eq!(
535            index.sections[..7],
536            [
537                IndexSectionId::DebugInfo,
538                IndexSectionId::DebugAbbrev,
539                IndexSectionId::DebugLine,
540                IndexSectionId::DebugLocLists,
541                IndexSectionId::DebugStrOffsets,
542                IndexSectionId::DebugMacro,
543                IndexSectionId::DebugRngLists,
544            ]
545        );
546        #[rustfmt::skip]
547        let expect = [
548            UnitIndexSection { section: IndexSectionId::DebugInfo, offset: 11, size: 21 },
549            UnitIndexSection { section: IndexSectionId::DebugAbbrev, offset: 12, size: 22 },
550            UnitIndexSection { section: IndexSectionId::DebugLine, offset: 13, size: 23 },
551            UnitIndexSection { section: IndexSectionId::DebugLocLists, offset: 14, size: 24 },
552            UnitIndexSection { section: IndexSectionId::DebugStrOffsets, offset: 15, size: 25 },
553            UnitIndexSection { section: IndexSectionId::DebugMacro, offset: 16, size: 26 },
554            UnitIndexSection { section: IndexSectionId::DebugRngLists, offset: 17, size: 27 },
555        ];
556        let mut sections = index.sections(1).unwrap();
557        for section in &expect {
558            assert_eq!(*section, sections.next().unwrap());
559        }
560        assert!(sections.next().is_none());
561
562        assert!(index.sections(0).is_err());
563        assert!(index.sections(2).is_err());
564    }
565
566    #[test]
567    fn test_hash() {
568        #[rustfmt::skip]
569        let section = Section::with_endian(Endian::Big)
570            // Header.
571            .D16(5).D16(0).D32(2).D32(3).D32(4)
572            // Slots.
573            .D64(0xffff_fff2_ffff_fff1)
574            .D64(0xffff_fff0_ffff_fff1)
575            .D64(0xffff_fff1_ffff_fff1)
576            .D64(0)
577            .D32(3).D32(1).D32(2).D32(0)
578            // Sections.
579            .D32(constants::DW_SECT_INFO.0)
580            .D32(constants::DW_SECT_ABBREV.0)
581            // Offsets.
582            .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0)
583            // Sizes.
584            .D32(0).D32(0).D32(0).D32(0).D32(0).D32(0);
585        let buf = section.get_contents().unwrap();
586        let buf = EndianSlice::new(&buf, BigEndian);
587        let index = UnitIndex::parse(buf).unwrap();
588        assert_eq!(index.version(), 5);
589        assert_eq!(index.slot_count(), 4);
590        assert_eq!(index.unit_count(), 3);
591        assert_eq!(index.section_count(), 2);
592        assert_eq!(index.find(0xffff_fff0_ffff_fff1), Some(1));
593        assert_eq!(index.find(0xffff_fff1_ffff_fff1), Some(2));
594        assert_eq!(index.find(0xffff_fff2_ffff_fff1), Some(3));
595        assert_eq!(index.find(0xffff_fff3_ffff_fff1), None);
596    }
597
598    #[test]
599    fn test_cu_index() {
600        #[rustfmt::skip]
601        let section = Section::with_endian(Endian::Big)
602            // Header.
603            .D16(5).D16(0).D32(0).D32(0).D32(1)
604            // Slots.
605            .D64(0).D32(0);
606        let buf = section.get_contents().unwrap();
607        let cu_index = DebugCuIndex::new(&buf, BigEndian);
608        let index = cu_index.index().unwrap();
609        assert_eq!(index.version, 5);
610    }
611
612    #[test]
613    fn test_tu_index() {
614        #[rustfmt::skip]
615        let section = Section::with_endian(Endian::Big)
616            // Header.
617            .D16(5).D16(0).D32(0).D32(0).D32(1)
618            // Slots.
619            .D64(0).D32(0);
620        let buf = section.get_contents().unwrap();
621        let tu_index = DebugTuIndex::new(&buf, BigEndian);
622        let index = tu_index.index().unwrap();
623        assert_eq!(index.version, 5);
624    }
625}