pdb/
dbi.rs

1// Copyright 2017 pdb Developers
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8// DBI = "Debug Information"
9
10use std::borrow::Cow;
11use std::fmt;
12use std::result;
13
14use crate::common::*;
15use crate::msf::*;
16use crate::{FallibleIterator, SectionCharacteristics};
17
18/// Provides access to the "DBI" stream inside the PDB.
19///
20/// This is only minimally implemented; it's really just so `PDB` can find the global symbol table.
21///
22/// # Example
23///
24/// ```
25/// # use pdb::FallibleIterator;
26/// #
27/// # fn test() -> pdb::Result<usize> {
28/// let file = std::fs::File::open("fixtures/self/foo.pdb")?;
29/// let mut pdb = pdb::PDB::open(file)?;
30///
31/// let dbi = pdb.debug_information()?;
32
33///
34/// # let mut count: usize = 0;
35/// let mut modules = dbi.modules()?;
36/// while let Some(module) = modules.next()? {
37///     println!("module name: {}, object file name: {}",
38///              module.module_name(), module.object_file_name());
39/// #   count += 1;
40/// }
41///
42/// # Ok(count)
43/// # }
44/// # assert!(test().expect("test") == 194);
45#[derive(Debug)]
46pub struct DebugInformation<'s> {
47    stream: Stream<'s>,
48    header: DBIHeader,
49    header_len: usize,
50}
51
52impl<'s> DebugInformation<'s> {
53    pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> {
54        let mut buf = stream.parse_buffer();
55        let header = DBIHeader::parse_buf(&mut buf)?;
56        let header_len = buf.pos();
57
58        Ok(DebugInformation {
59            stream,
60            header,
61            header_len,
62        })
63    }
64
65    pub(crate) fn header(&self) -> DBIHeader {
66        self.header
67    }
68
69    /// Returns the target's machine type (architecture).
70    pub fn machine_type(&self) -> Result<MachineType> {
71        Ok(self.header.machine_type.into())
72    }
73
74    /// Returns this PDB's original `age`.
75    ///
76    /// This number is written by the linker and should be equal to the image's `age` value. In
77    /// contrast, [`PDBInformation::age`] may be bumped by other tools and should be greater or
78    /// equal to the image's `age` value.
79    ///
80    /// Old PDB files may not specify an age, in which case only [`PDBInformation::age`] should be
81    /// checked for matching the image.
82    ///
83    /// [`PDBInformation::age`]: crate::PDBInformation::age
84    pub fn age(&self) -> Option<u32> {
85        match self.header.age {
86            0 => None,
87            age => Some(age),
88        }
89    }
90
91    /// Returns an iterator that can traverse the modules list in sequential order.
92    pub fn modules(&self) -> Result<ModuleIter<'_>> {
93        let mut buf = self.stream.parse_buffer();
94        // drop the header
95        buf.take(self.header_len)?;
96        let modules_buf = buf.take(self.header.module_list_size as usize)?;
97        Ok(ModuleIter {
98            buf: modules_buf.into(),
99        })
100    }
101
102    /// Returns an iterator that can traverse the section contributions list in sequential order.
103    pub fn section_contributions(&self) -> Result<DBISectionContributionIter<'_>> {
104        let mut buf = self.stream.parse_buffer();
105        // drop the header and modules list
106        buf.take(self.header_len + self.header.module_list_size as usize)?;
107        let contributions_buf = buf.take(self.header.section_contribution_size as usize)?;
108        DBISectionContributionIter::parse(contributions_buf.into())
109    }
110}
111
112/// The version of the PDB format.
113///
114/// This version type is used in multiple locations: the DBI header, and the PDBI header.
115#[non_exhaustive]
116#[derive(Debug, Copy, Clone)]
117#[allow(missing_docs)]
118pub enum HeaderVersion {
119    V41,
120    V50,
121    V60,
122    V70,
123    V110,
124    OtherValue(u32),
125}
126
127impl From<u32> for HeaderVersion {
128    #[allow(clippy::inconsistent_digit_grouping)]
129    fn from(v: u32) -> Self {
130        match v {
131            93_08_03 => Self::V41,
132            1996_03_07 => Self::V50,
133            1997_06_06 => Self::V60,
134            1999_09_03 => Self::V70,
135            2009_12_01 => Self::V110,
136            _ => Self::OtherValue(v),
137        }
138    }
139}
140
141/// A DBI header -- `NewDBIHdr`, really -- parsed from a stream.
142///
143/// Reference:
144/// <https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L124>
145#[derive(Debug, Copy, Clone)]
146#[allow(dead_code)] // reason = "unused fields added for completeness"
147pub(crate) struct DBIHeader {
148    pub signature: u32,
149    pub version: HeaderVersion,
150    pub age: u32,
151    pub gs_symbols_stream: StreamIndex,
152
153    /*
154    https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L143-L155:
155        union {
156        struct {
157            USHORT      usVerPdbDllMin : 8; // minor version and
158            USHORT      usVerPdbDllMaj : 7; // major version and
159            USHORT      fNewVerFmt     : 1; // flag telling us we have rbld stored elsewhere (high bit of original major version)
160        } vernew;                           // that built this pdb last.
161        struct {
162            USHORT      usVerPdbDllRbld: 4;
163            USHORT      usVerPdbDllMin : 7;
164            USHORT      usVerPdbDllMaj : 5;
165        } verold;
166        USHORT          usVerAll;
167    };
168    */
169    pub internal_version: u16,
170    pub ps_symbols_stream: StreamIndex,
171    // "build version of the pdb dll that built this pdb last."
172    pub pdb_dll_build_version: u16,
173
174    pub symbol_records_stream: StreamIndex,
175
176    // "rbld version of the pdb dll that built this pdb last."
177    pub pdb_dll_rbld_version: u16,
178    pub module_list_size: u32,
179    pub section_contribution_size: u32,
180    pub section_map_size: u32,
181    pub file_info_size: u32,
182
183    // "size of the Type Server Map substream"
184    pub type_server_map_size: u32,
185
186    // "index of MFC type server"
187    pub mfc_type_server_index: u32,
188
189    // "size of optional DbgHdr info appended to the end of the stream"
190    pub debug_header_size: u32,
191
192    // "number of bytes in EC substream, or 0 if EC no EC enabled Mods"
193    pub ec_substream_size: u32,
194
195    /*
196    https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L187-L192:
197        USHORT  fIncLink:1;     // true if linked incrmentally (really just if ilink thunks are present)
198        USHORT  fStripped:1;    // true if PDB::CopyTo stripped the private data out
199        USHORT  fCTypes:1;      // true if this PDB is using CTypes.
200        USHORT  unused:13;      // reserved, must be 0.
201    */
202    pub flags: u16,
203
204    pub machine_type: u16,
205    pub reserved: u32,
206}
207
208impl DBIHeader {
209    pub fn parse(stream: Stream<'_>) -> Result<Self> {
210        Self::parse_buf(&mut stream.parse_buffer())
211    }
212
213    fn parse_buf(buf: &mut ParseBuffer<'_>) -> Result<Self> {
214        let header = Self {
215            signature: buf.parse_u32()?,
216            version: From::from(buf.parse_u32()?),
217            age: buf.parse_u32()?,
218            gs_symbols_stream: buf.parse()?,
219            internal_version: buf.parse_u16()?,
220            ps_symbols_stream: buf.parse()?,
221            pdb_dll_build_version: buf.parse_u16()?,
222            symbol_records_stream: buf.parse()?,
223            pdb_dll_rbld_version: buf.parse_u16()?,
224            module_list_size: buf.parse_u32()?,
225            section_contribution_size: buf.parse_u32()?,
226            section_map_size: buf.parse_u32()?,
227            file_info_size: buf.parse_u32()?,
228            type_server_map_size: buf.parse_u32()?,
229            mfc_type_server_index: buf.parse_u32()?,
230            debug_header_size: buf.parse_u32()?,
231            ec_substream_size: buf.parse_u32()?,
232            flags: buf.parse_u16()?,
233            machine_type: buf.parse_u16()?,
234            reserved: buf.parse_u32()?,
235        };
236
237        if header.signature != u32::max_value() {
238            // this is likely a DBIHdr, not a NewDBIHdr
239            // it could be promoted:
240            //   https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.cpp#L291-L313
241            //   https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/langapi/include/pdb.h#L1180-L1184
242            // but that seems like a lot of work
243            return Err(Error::UnimplementedFeature("ancient DBI header"));
244        }
245
246        Ok(header)
247    }
248}
249
250/// The target machine's architecture.
251/// Reference: <https://docs.microsoft.com/en-us/windows/desktop/debug/pe-format#machine-types>
252#[non_exhaustive]
253#[derive(Clone, Copy, Debug, Eq, PartialEq)]
254pub enum MachineType {
255    /// The contents of this field are assumed to be applicable to any machine type.
256    Unknown = 0x0,
257    /// Matsushita AM33
258    Am33 = 0x13,
259    /// x64
260    Amd64 = 0x8664,
261    /// ARM little endian
262    Arm = 0x1C0,
263    /// ARM64 little endian
264    Arm64 = 0xAA64,
265    /// ARM Thumb-2 little endian
266    ArmNT = 0x1C4,
267    /// EFI byte code
268    Ebc = 0xEBC,
269    /// Intel 386 or later processors and compatible processors
270    X86 = 0x14C,
271    /// Intel Itanium processor family
272    Ia64 = 0x200,
273    /// Mitsubishi M32R little endian
274    M32R = 0x9041,
275    /// MIPS16
276    Mips16 = 0x266,
277    /// MIPS with FPU
278    MipsFpu = 0x366,
279    /// MIPS16 with FPU
280    MipsFpu16 = 0x466,
281    /// Power PC little endian
282    PowerPC = 0x1F0,
283    /// Power PC with floating point support
284    PowerPCFP = 0x1F1,
285    /// MIPS little endian
286    R4000 = 0x166,
287    /// RISC-V 32-bit address space
288    RiscV32 = 0x5032,
289    /// RISC-V 64-bit address space
290    RiscV64 = 0x5064,
291    /// RISC-V 128-bit address space
292    RiscV128 = 0x5128,
293    /// Hitachi SH3
294    SH3 = 0x1A2,
295    /// Hitachi SH3 DSP
296    SH3DSP = 0x1A3,
297    /// Hitachi SH4
298    SH4 = 0x1A6,
299    /// Hitachi SH5
300    SH5 = 0x1A8,
301    /// Thumb
302    Thumb = 0x1C2,
303    /// MIPS little-endian WCE v2
304    WceMipsV2 = 0x169,
305    /// Invalid value
306    Invalid = 0xffff,
307}
308
309impl fmt::Display for MachineType {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        match self {
312            Self::Invalid => write!(f, "Invalid"),
313            Self::Unknown => write!(f, "Unknown"),
314            Self::Am33 => write!(f, "Am33"),
315            Self::Amd64 => write!(f, "Amd64"),
316            Self::Arm => write!(f, "Arm"),
317            Self::Arm64 => write!(f, "Arm64"),
318            Self::ArmNT => write!(f, "ArmNT"),
319            Self::Ebc => write!(f, "Ebc"),
320            Self::X86 => write!(f, "X86"),
321            Self::Ia64 => write!(f, "Ia64"),
322            Self::M32R => write!(f, "M32R"),
323            Self::Mips16 => write!(f, "Mips16"),
324            Self::MipsFpu => write!(f, "MipsFpu"),
325            Self::MipsFpu16 => write!(f, "MipsFpu16"),
326            Self::PowerPC => write!(f, "PowerPC"),
327            Self::PowerPCFP => write!(f, "PowerPCFP"),
328            Self::R4000 => write!(f, "R4000"),
329            Self::RiscV32 => write!(f, "RiscV32"),
330            Self::RiscV64 => write!(f, "RiscV64"),
331            Self::RiscV128 => write!(f, "RiscV128"),
332            Self::SH3 => write!(f, "SH3"),
333            Self::SH3DSP => write!(f, "SH3DSP"),
334            Self::SH4 => write!(f, "SH4"),
335            Self::SH5 => write!(f, "SH5"),
336            Self::Thumb => write!(f, "Thumb"),
337            Self::WceMipsV2 => write!(f, "WceMipsV2"),
338        }
339    }
340}
341
342impl From<u16> for MachineType {
343    fn from(value: u16) -> Self {
344        match value {
345            0xffff => Self::Invalid,
346            0x0 => Self::Unknown,
347            0x13 => Self::Am33,
348            0x8664 => Self::Amd64,
349            0x1C0 => Self::Arm,
350            0xAA64 => Self::Arm64,
351            0x1C4 => Self::ArmNT,
352            0xEBC => Self::Ebc,
353            0x14C => Self::X86,
354            0x200 => Self::Ia64,
355            0x9041 => Self::M32R,
356            0x266 => Self::Mips16,
357            0x366 => Self::MipsFpu,
358            0x466 => Self::MipsFpu16,
359            0x1F0 => Self::PowerPC,
360            0x1F1 => Self::PowerPCFP,
361            0x166 => Self::R4000,
362            0x5032 => Self::RiscV32,
363            0x5064 => Self::RiscV64,
364            0x5128 => Self::RiscV128,
365            0x1A2 => Self::SH3,
366            0x1A3 => Self::SH3DSP,
367            0x1A6 => Self::SH4,
368            0x1A8 => Self::SH5,
369            0x1C2 => Self::Thumb,
370            0x169 => Self::WceMipsV2,
371            _ => Self::Unknown,
372        }
373    }
374}
375
376/// Information about a module's contribution to a section.
377/// `struct SC` in Microsoft's code:
378/// <https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/include/dbicommon.h#L42>
379#[derive(Debug, Copy, Clone)]
380pub struct DBISectionContribution {
381    /// Start offset of the section.
382    pub offset: PdbInternalSectionOffset,
383    /// The size of the contribution, in bytes.
384    pub size: u32,
385    /// The characteristics, which map to [`ImageSectionHeader::characteristics`] in binaries.
386    ///
387    /// [`ImageSectionHeader::characteristics`]: crate::ImageSectionHeader::characteristics
388    pub characteristics: SectionCharacteristics,
389    /// Index of the module in [`DebugInformation::modules`] containing the actual symbol.
390    pub module: usize,
391    /// CRC of the contribution(?)
392    pub data_crc: u32,
393    /// CRC of relocations(?)
394    pub reloc_crc: u32,
395}
396
397impl DBISectionContribution {
398    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
399        let section = buf.parse_u16()?;
400        let _padding = buf.parse_u16()?;
401        let offset = buf.parse_u32()?;
402        let size = buf.parse_u32()?;
403        let characteristics = buf.parse()?;
404        let module = buf.parse_u16()?.into();
405        let _padding = buf.parse_u16()?;
406
407        Ok(Self {
408            offset: PdbInternalSectionOffset { offset, section },
409            size,
410            characteristics,
411            module,
412            data_crc: buf.parse_u32()?,
413            reloc_crc: buf.parse_u32()?,
414        })
415    }
416}
417
418/// Information about a module parsed from the DBI stream.
419///
420/// Named `MODI` in the Microsoft PDB source:
421/// <https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L1197>
422#[derive(Debug, Copy, Clone)]
423#[allow(dead_code)] // reason = "unused fields added for completeness"
424pub(crate) struct DBIModuleInfo {
425    /// Currently open module.
426    pub opened: u32,
427    /// This module's first section contribution.
428    pub section: DBISectionContribution,
429    /// Flags, expressed as bitfields in the C struct:
430    /// written, EC enabled, unused, tsm
431    /// <https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L1201-L1204>
432    pub flags: u16,
433    /// Stream number of module debug info (syms, lines, fpo).
434    pub stream: StreamIndex,
435    /// Size of local symbols debug info in `stream`.
436    pub symbols_size: u32,
437    /// Size of line number debug info in `stream`.
438    pub lines_size: u32,
439    /// Size of C13 style line number info in `stream`.
440    pub c13_lines_size: u32,
441    /// Number of files contributing to this module.
442    pub files: u16,
443    _padding: u16,
444    /// Used as a pointer into an array of filename indicies in the Microsoft code.
445    pub filename_offsets: u32,
446    /// Source file name index.
447    pub source: u32,
448    /// Path to compiler PDB name index.
449    pub compiler: u32,
450}
451
452impl DBIModuleInfo {
453    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
454        Ok(Self {
455            opened: buf.parse_u32()?,
456            section: DBISectionContribution::parse(buf)?,
457            flags: buf.parse_u16()?,
458            stream: buf.parse()?,
459            symbols_size: buf.parse_u32()?,
460            lines_size: buf.parse_u32()?,
461            c13_lines_size: buf.parse_u32()?,
462            files: buf.parse_u16()?,
463            _padding: buf.parse_u16()?,
464            filename_offsets: buf.parse_u32()?,
465            source: buf.parse_u32()?,
466            compiler: buf.parse_u32()?,
467        })
468    }
469}
470
471/// Represents a module from the DBI stream.
472///
473/// A `Module` is a single item that contributes to the binary, such as an object file or import
474/// library.
475///
476/// Much of the useful information for a `Module` is stored in a separate stream in the PDB. It can
477/// be retrieved by calling [`PDB::module_info`](crate::PDB::module_info) with a specific module.
478#[derive(Debug, Clone)]
479pub struct Module<'m> {
480    info: DBIModuleInfo,
481    module_name: RawString<'m>,
482    object_file_name: RawString<'m>,
483}
484
485impl<'m> Module<'m> {
486    /// The `DBIModuleInfo` from the module info substream in the DBI stream.
487    pub(crate) fn info(&self) -> &DBIModuleInfo {
488        &self.info
489    }
490    /// The module name.
491    ///
492    /// Usually either a full path to an object file or a string of the form `Import:<dll name>`.
493    pub fn module_name(&self) -> Cow<'m, str> {
494        self.module_name.to_string()
495    }
496    /// The object file name.
497    ///
498    /// May be the same as `module_name` for object files passed directly
499    /// to the linker. For modules from static libraries, this is usually
500    /// the full path to the archive.
501    pub fn object_file_name(&self) -> Cow<'m, str> {
502        self.object_file_name.to_string()
503    }
504}
505
506/// A `ModuleIter` iterates over the modules in the DBI section, producing `Module`s.
507#[derive(Debug)]
508pub struct ModuleIter<'m> {
509    buf: ParseBuffer<'m>,
510}
511
512impl<'m> FallibleIterator for ModuleIter<'m> {
513    type Item = Module<'m>;
514    type Error = Error;
515
516    fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
517        // see if we're at EOF
518        if self.buf.is_empty() {
519            return Ok(None);
520        }
521
522        let info = DBIModuleInfo::parse(&mut self.buf)?;
523        let module_name = self.buf.parse_cstring()?;
524        let object_file_name = self.buf.parse_cstring()?;
525        self.buf.align(4)?;
526        Ok(Some(Module {
527            info,
528            module_name,
529            object_file_name,
530        }))
531    }
532}
533
534/// The version of the section contribution stream.
535#[derive(Debug, Copy, Clone, PartialEq)]
536#[allow(missing_docs)]
537enum DBISectionContributionStreamVersion {
538    V60,
539    V2,
540    OtherValue(u32),
541}
542
543impl From<u32> for DBISectionContributionStreamVersion {
544    fn from(v: u32) -> Self {
545        const V60: u32 = 0xeffe_0000 + 19_970_605;
546        const V2: u32 = 0xeffe_0000 + 20_140_516;
547        match v {
548            V60 => Self::V60,
549            V2 => Self::V2,
550            _ => Self::OtherValue(v),
551        }
552    }
553}
554
555/// A `DBISectionContributionIter` iterates over the section contributions in the DBI section, producing `DBISectionContribution`s.
556#[derive(Debug)]
557pub struct DBISectionContributionIter<'c> {
558    buf: ParseBuffer<'c>,
559    version: DBISectionContributionStreamVersion,
560}
561
562impl<'c> DBISectionContributionIter<'c> {
563    fn parse(mut buf: ParseBuffer<'c>) -> Result<Self> {
564        let version = buf.parse_u32()?.into();
565        Ok(Self { buf, version })
566    }
567}
568
569impl<'c> FallibleIterator for DBISectionContributionIter<'c> {
570    type Item = DBISectionContribution;
571    type Error = Error;
572
573    fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
574        // see if we're at EOF
575        if self.buf.is_empty() {
576            return Ok(None);
577        }
578
579        let contribution = DBISectionContribution::parse(&mut self.buf)?;
580        if self.version == DBISectionContributionStreamVersion::V2 {
581            self.buf.parse_u32()?;
582        }
583        Ok(Some(contribution))
584    }
585}
586
587/// A `DbgDataHdr`, which contains a series of (optional) MSF stream numbers.
588#[derive(Debug, Copy, Clone)]
589#[allow(dead_code)] // reason = "unused fields added for completeness"
590pub(crate) struct DBIExtraStreams {
591    // The struct itself is defined at:
592    //    https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/PDB/dbi/dbi.h#L250-L274
593    // It's just an array of stream numbers; `u16`s where 0xffff means "no stream".
594    //
595    // The array indices are:
596    //    https://github.com/Microsoft/microsoft-pdb/blob/082c5290e5aff028ae84e43affa8be717aa7af73/langapi/include/pdb.h#L439-L449
597    // We'll map those to fields.
598    //
599    // The struct itself can be truncated. This is an internal struct; we'll treat missing fields as
600    // StreamIndex::none() even if it's a short read, so long as the short read stops on a u16 boundary.
601    pub fpo: StreamIndex,
602    pub exception: StreamIndex,
603    pub fixup: StreamIndex,
604    pub omap_to_src: StreamIndex,
605    pub omap_from_src: StreamIndex,
606    pub section_headers: StreamIndex,
607    pub token_rid_map: StreamIndex,
608    pub xdata: StreamIndex,
609    pub pdata: StreamIndex,
610    pub framedata: StreamIndex,
611    pub original_section_headers: StreamIndex,
612}
613
614impl DBIExtraStreams {
615    pub(crate) fn new(debug_info: &DebugInformation<'_>) -> Result<Self> {
616        // calculate the location of the extra stream information
617        let header = debug_info.header;
618        let offset = debug_info.header_len
619            + (header.module_list_size
620                + header.section_contribution_size
621                + header.section_map_size
622                + header.file_info_size
623                + header.type_server_map_size
624                + header.ec_substream_size) as usize;
625
626        // seek
627        let mut buf = debug_info.stream.parse_buffer();
628        buf.take(offset)?;
629
630        // grab that section as bytes
631        let bytes = buf.take(header.debug_header_size as _)?;
632
633        // parse those bytes
634        let mut extra_streams_buf = ParseBuffer::from(bytes);
635        Self::parse(&mut extra_streams_buf)
636    }
637
638    pub(crate) fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
639        // short reads are okay, as are long reads -- this struct is actually an array
640        // what's _not_ okay are
641        if buf.len() % 2 != 0 {
642            return Err(Error::InvalidStreamLength("DbgDataHdr"));
643        }
644
645        fn next_index(buf: &mut ParseBuffer<'_>) -> Result<StreamIndex> {
646            if buf.is_empty() {
647                Ok(StreamIndex::none())
648            } else {
649                buf.parse()
650            }
651        }
652
653        Ok(Self {
654            fpo: next_index(buf)?,
655            exception: next_index(buf)?,
656            fixup: next_index(buf)?,
657            omap_to_src: next_index(buf)?,
658            omap_from_src: next_index(buf)?,
659            section_headers: next_index(buf)?,
660            token_rid_map: next_index(buf)?,
661            xdata: next_index(buf)?,
662            pdata: next_index(buf)?,
663            framedata: next_index(buf)?,
664            original_section_headers: next_index(buf)?,
665        })
666    }
667}
668
669#[cfg(test)]
670mod tests {
671    use crate::dbi::*;
672
673    #[test]
674    fn test_dbi_extra_streams() {
675        let bytes = vec![0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0xff, 0xff, 0x05, 0x06];
676
677        let mut buf = ParseBuffer::from(bytes.as_slice());
678        let extra_streams = DBIExtraStreams::parse(&mut buf).expect("parse");
679
680        // check readback
681        assert_eq!(extra_streams.fpo, StreamIndex::none());
682        assert_eq!(extra_streams.exception, StreamIndex(0x0201));
683        assert_eq!(extra_streams.fixup, StreamIndex(0x0403));
684        assert_eq!(extra_streams.omap_to_src, StreamIndex::none());
685        assert_eq!(extra_streams.omap_from_src, StreamIndex(0x0605));
686
687        // check that short reads => StreamIndex::none()
688        assert_eq!(extra_streams.section_headers, StreamIndex::none());
689        assert_eq!(extra_streams.token_rid_map, StreamIndex::none());
690        assert_eq!(extra_streams.original_section_headers, StreamIndex::none());
691    }
692}