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