Skip to main content

ms_pdb/
dbi.rs

1//! Provides access to the DBI Stream (Debug Information).
2//!
3//! The DBI Stream is a central data structure of the PDB. It contains many vital fields, and
4//! points to other streams that contain other important information. The DBI is stream 3.
5//!
6//! Briefly, the DBI contains these substreams:
7//!
8//! * Modules: This lists the modules (compilands / translation units) that compose an executable.
9//!   Each Module Info structure contains many important fields, including the stream number for
10//!   a Module Stream.
11//!
12//! * Section Contributions Substream
13//!
14//! * Section Map Substream
15//!
16//! * Sources Substream: This lists the source files that were inputs to all of the translation units.
17//!
18//! * Type Server Map Substream
19//!
20//! * Optional Debug Header Substream
21//!
22//! * Edit-and-Continue Substream
23//!
24//! The `Dbi` stream holds section contributions and the list of modules (compilands).
25//!
26//! * <https://llvm.org/docs/PDB/DbiStream.html>
27//! * <https://github.com/microsoft/microsoft-pdb/blob/805655a28bd8198004be2ac27e6e0290121a5e89/langapi/include/pdb.h#L860>
28
29use crate::dbi::optional_dbg::{OptionalDebugHeaders, OptionalDebugStream};
30use crate::{Container, NIL_STREAM_INDEX};
31use crate::{Stream, get_or_init_err};
32use crate::{StreamIndexIsNilError, StreamIndexU16};
33use anyhow::{Result, bail};
34use ms_codeview::parser::{Parser, ParserError, ParserMut};
35use ms_coff::IMAGE_SECTION_HEADER;
36use std::mem::size_of;
37use std::ops::Range;
38use sync_file::ReadAt;
39use tracing::{error, warn};
40use zerocopy::{
41    FromBytes, FromZeros, I32, Immutable, IntoBytes, KnownLayout, LE, U16, U32, Unaligned,
42};
43
44#[cfg(doc)]
45use crate::Pdb;
46
47pub mod fixups;
48pub mod modules;
49pub mod optional_dbg;
50pub mod section_contrib;
51pub mod section_map;
52pub mod sources;
53
54pub use modules::*;
55#[doc(inline)]
56pub use section_contrib::*;
57#[doc(inline)]
58pub use sources::*;
59
60/// The header of the DBI (Debug Information) stream.
61#[repr(C)]
62#[derive(IntoBytes, FromBytes, KnownLayout, Immutable, Unaligned, Debug, Clone)]
63#[allow(missing_docs)]
64pub struct DbiStreamHeader {
65    /// Always -1
66    pub signature: I32<LE>,
67
68    /// One of the `DBI_STREAM_VERSION_*` values; typically, `DBI_STREAM_VERSION_V110`.
69    pub version: U32<LE>,
70
71    /// The number of times this PDB has been modified. The value is set to 1 when a PDB is
72    /// first created. This value must match the same field within the PE header.
73    pub age: U32<LE>,
74
75    /// The index of the Global Symbol Index, which contains a name-to-symbol lookup table for
76    /// global symbols. The symbol records are not stored in this stream; they are stored in the
77    /// Global Symbol Stream.
78    pub global_symbol_index_stream: StreamIndexU16,
79
80    pub build_number: U16<LE>,
81
82    /// The index of the stream that contains the Public Symbol Index (GSI). This contains a
83    /// name-to-symbol map and an address-to-symbol map. See [`crate::globals::gsi`].
84    pub public_symbol_index_stream: StreamIndexU16,
85
86    /// The version of the MSPDB DLL which produced this DBI stream.
87    pub pdb_dll_version: U16<LE>,
88
89    /// The stream that contains the Global Symbol Stream. This contains symbol records, which can
90    /// be decoded using [`crate::syms::SymIter`].
91    pub global_symbol_stream: StreamIndexU16,
92
93    pub pdb_dll_rbld: U16<LE>,
94
95    // Substreams
96    pub mod_info_size: I32<LE>,
97    pub section_contribution_size: I32<LE>,
98    pub section_map_size: I32<LE>,
99    pub source_info_size: I32<LE>,
100    pub type_server_map_size: I32<LE>,
101    /// This field is _not_ a substream size. Not sure what it is.
102    pub mfc_type_server_index: U32<LE>,
103    pub optional_dbg_header_size: I32<LE>,
104    pub edit_and_continue_size: I32<LE>,
105
106    pub flags: U16<LE>,
107    pub machine: U16<LE>,
108    pub padding: U32<LE>,
109}
110
111/// Data for an empty DBI stream
112pub static EMPTY_DBI_STREAM_HEADER: [u8; DBI_STREAM_HEADER_LEN] = [
113    0xFF, 0xFF, 0xFF, 0xFF, // signature
114    0x77, 0x09, 0x31, 0x01, // version
115    0x01, 0x00, 0x00, 0x00, // age
116    0xFF, 0xFF, // global_stream_index
117    0x00, 0x00, // build_number
118    0xFF, 0xFF, // public_stream_index
119    0x00, 0x00, // pdb_dll_version
120    0xFF, 0xFF, // sym_record_stream
121    0x00, 0x00, // pdb_dll_rbld
122    0x00, 0x00, 0x00, 0x00, // mod_info_size
123    0x00, 0x00, 0x00, 0x00, // section_contribution_size
124    0x00, 0x00, 0x00, 0x00, // section_map_size
125    0x00, 0x00, 0x00, 0x00, // source_info_size
126    0x00, 0x00, 0x00, 0x00, // type_server_map_size
127    0x00, 0x00, 0x00, 0x00, // mfc_type_server_index
128    0x00, 0x00, 0x00, 0x00, // optional_dbg_header_size
129    0x00, 0x00, 0x00, 0x00, // edit_and_continue_size
130    0x00, 0x00, // flags
131    0x00, 0x00, // machine
132    0x00, 0x00, 0x00, 0x00, // padding
133];
134
135#[test]
136fn test_parse_empty_dbi_stream_header() {
137    let h = DbiStreamHeader::read_from_bytes(EMPTY_DBI_STREAM_HEADER.as_slice()).unwrap();
138    assert!(h.global_symbol_index_stream.get().is_none());
139}
140
141impl DbiStreamHeader {
142    /// Gets the stream index for the Global Symbol Stream.
143    pub fn sym_record_stream(&self) -> Result<u32, StreamIndexIsNilError> {
144        self.global_symbol_stream.get_err()
145    }
146
147    /// Gets the stream index for the Public Symbol Index.
148    pub fn public_stream_index(&self) -> Result<u32, StreamIndexIsNilError> {
149        self.public_symbol_index_stream.get_err()
150    }
151
152    /// Gets the stream index for the Global Symbol Index.
153    pub fn global_stream_index(&self) -> Result<u32, StreamIndexIsNilError> {
154        self.global_symbol_index_stream.get_err()
155    }
156
157    /// Byte range of the Modules substream.
158    pub fn modules_range(&self) -> anyhow::Result<Range<usize>> {
159        let start = DBI_STREAM_HEADER_LEN;
160        let size = self.mod_info_size.get() as usize;
161        Ok(start..start + size)
162    }
163
164    /// Byte range of the Modules substream.
165    pub fn sources_range(&self) -> anyhow::Result<Range<usize>> {
166        let start = DBI_STREAM_HEADER_LEN
167            + self.mod_info_size.get() as usize
168            + self.section_contribution_size.get() as usize
169            + self.section_map_size.get() as usize;
170        let size = self.source_info_size.get() as usize;
171        Ok(start..start + size)
172    }
173
174    /// The total length of all substreams, or None if this value cannot be computed.
175    ///
176    /// In a well-formed DBI stream, this value can be computed and the value is less than
177    /// the size of the data that follows the DBI Stream Header.
178    pub fn total_substreams_len(&self) -> Option<u32> {
179        // Read the fields and (where necessary) convert them from i32 to u32.
180        // If a value is negative, then we return None.
181        u32::try_from(self.mod_info_size.get())
182            .ok()?
183            .checked_add(u32::try_from(self.section_contribution_size.get()).ok()?)?
184            .checked_add(u32::try_from(self.section_map_size.get()).ok()?)?
185            .checked_add(u32::try_from(self.source_info_size.get()).ok()?)?
186            .checked_add(u32::try_from(self.type_server_map_size.get()).ok()?)?
187            .checked_add(u32::try_from(self.optional_dbg_header_size.get()).ok()?)?
188            .checked_add(u32::try_from(self.edit_and_continue_size.get()).ok()?)
189    }
190}
191
192static_assertions::const_assert_eq!(size_of::<DbiStreamHeader>(), DBI_STREAM_HEADER_LEN);
193/// The size of the DBI stream header.
194pub const DBI_STREAM_HEADER_LEN: usize = 64;
195
196/// MSVC version 4.1
197pub const DBI_STREAM_VERSION_VC41: u32 = 930803;
198/// MSVC version 5.0
199pub const DBI_STREAM_VERSION_V50: u32 = 19960307;
200/// MSVC version 6.0
201pub const DBI_STREAM_VERSION_V60: u32 = 19970606;
202/// MSVC version 7.0
203pub const DBI_STREAM_VERSION_V70: u32 = 19990903;
204/// MSVC version 11.0
205pub const DBI_STREAM_VERSION_V110: u32 = 20091201;
206
207/// Holds or refers to the DBI stream.
208///
209/// The `StreamData` type parameter can be any type that can contain `[u8]`.
210///
211/// This type contains (or refers to) the _entire_ DBI stream, not just the header.
212#[derive(Clone)]
213pub struct DbiStream<StreamData = Vec<u8>>
214where
215    StreamData: AsRef<[u8]>,
216{
217    /// The contents of the stream.
218    pub stream_data: StreamData,
219
220    /// The byte ranges of the substreams.
221    pub substreams: DbiSubstreamRanges,
222}
223
224// The DBI stream contains a fixed number of "substreams". The DBI header specifies the
225// length of each substream.  The position of each substream is found by computing the
226// sum of all previous substreams (and the header).
227macro_rules! dbi_substreams {
228    (
229        $(
230            $name:ident,
231            $mut_name:ident,
232            $size_field:ident ;
233        )*
234    ) => {
235        /// Contains the byte ranges of the substreams within the DBI stream.
236        #[derive(Clone, Debug, Default)]
237        pub struct DbiSubstreamRanges {
238            $(
239                #[doc = concat!("The range of the ", stringify!($name), " substream.")]
240                pub $name: Range<usize>,
241            )*
242        }
243
244        impl<StreamData: AsRef<[u8]>> DbiStream<StreamData> {
245            $(
246                #[doc = concat!("The unparsed contents of the ", stringify!($name), " substream.")]
247                pub fn $name(&self) -> &[u8] {
248                    self.substream_data(self.substreams.$name.clone())
249                }
250
251                #[doc = concat!("The unparsed contents of the ", stringify!($name), " substream.")]
252                pub fn $mut_name(&mut self) -> &mut [u8]
253                where
254                    StreamData: AsMut<[u8]>,
255                {
256                    self.substream_data_mut(self.substreams.$name.clone())
257                }
258
259            )*
260        }
261
262        impl DbiSubstreamRanges {
263            pub(crate) fn from_sizes(sizes: &DbiStreamHeader, stream_len: usize) -> anyhow::Result<Self> {
264                let mut pos: usize = DBI_STREAM_HEADER_LEN;
265                if pos > stream_len {
266                    bail!("DBI stream is too short; pos = {}, stream_len = {}", pos, stream_len);
267                }
268
269                $(
270                    assert!(pos <= stream_len);
271                    let size: i32 = sizes.$size_field.get();
272                    if size < 0 {
273                        bail!("Substream {} length in DBI header is invalid (is negative)", stringify!($size_field));
274                    }
275
276                    let len = size as usize;
277                    let available = stream_len - pos;
278                    if len > available {
279                        bail!("Substream {} length in DBI header is invalid. It extends beyond the end of the stream.", stringify!($size_field));
280                    }
281                    let start = pos;
282                    pos += len;
283
284                    let $name = start..pos;
285                )*
286
287                if pos < stream_len {
288                    warn!(pos, stream_len, "Something is wrong with the code that finds the ranges of substreams. Expected pos to be equal to stream_len.");
289                } else if pos > stream_len {
290                    error!(pos, stream_len, "Something is very wrong with the DBI header. The sum of the subtream lengths (pos) exceeds the stream len.");
291                } else {
292                    // Substream sizes look good.
293                }
294
295                Ok(Self {
296                    $( $name, )*
297                })
298            }
299        }
300    }
301}
302
303dbi_substreams! {
304    // The order of these determines the order of the substream data in the stream.
305    modules_bytes, modules_bytes_mut, mod_info_size;
306    section_contributions_bytes, section_contributions_bytes_mut, section_contribution_size;
307    section_map_bytes, section_map_bytes_mut, section_map_size;
308    source_info, source_info_mut, source_info_size;
309    type_server_map, type_server_map_mut, type_server_map_size;
310    edit_and_continue, edit_and_continue_mut, edit_and_continue_size;
311    optional_debug_header_bytes, optional_debug_header_bytes_mut, optional_dbg_header_size;
312}
313
314impl<StreamData: AsRef<[u8]>> DbiStream<StreamData> {
315    /// Returns the DBI stream header.
316    pub fn header(&self) -> Result<&DbiStreamHeader> {
317        if let Ok((header, _)) = DbiStreamHeader::ref_from_prefix(self.stream_data.as_ref()) {
318            Ok(header)
319        } else {
320            bail!("The DBI stream is too small to contain a valid header.")
321        }
322    }
323
324    /// Provides mutable access to the DBI stream header.
325    pub fn header_mut(&mut self) -> Result<&mut DbiStreamHeader>
326    where
327        StreamData: AsMut<[u8]>,
328    {
329        if let Ok((header, _)) = DbiStreamHeader::mut_from_prefix(self.stream_data.as_mut()) {
330            Ok(header)
331        } else {
332            bail!("The DBI stream is too small to contain a valid header.")
333        }
334    }
335
336    fn substream_data(&self, range: Range<usize>) -> &[u8] {
337        &self.stream_data.as_ref()[range]
338    }
339
340    fn substream_data_mut(&mut self, range: Range<usize>) -> &mut [u8]
341    where
342        StreamData: AsMut<[u8]>,
343    {
344        &mut self.stream_data.as_mut()[range]
345    }
346
347    /// Reads the Module Information substream.
348    pub fn modules(&self) -> ModInfoSubstream<&[u8]> {
349        ModInfoSubstream {
350            substream_data: self.modules_bytes(),
351        }
352    }
353
354    /// Iterates the Module records in the Module Information Substream.
355    pub fn iter_modules(&self) -> IterModuleInfo<'_> {
356        IterModuleInfo::new(self.modules_bytes())
357    }
358
359    /// Iterates the Module records in the Module Information Substream, with mutable access.
360    pub fn iter_modules_mut(&mut self) -> IterModuleInfoMut<'_>
361    where
362        StreamData: AsMut<[u8]>,
363    {
364        IterModuleInfoMut::new(self.modules_bytes_mut())
365    }
366
367    /// Return a DbiStream over just a a reference
368    pub fn as_slice(&self) -> DbiStream<&[u8]> {
369        DbiStream {
370            stream_data: self.stream_data.as_ref(),
371            substreams: self.substreams.clone(),
372        }
373    }
374
375    /// Read the DBI Stream header and validate it.
376    pub fn parse(stream_data: StreamData) -> anyhow::Result<Self> {
377        let stream_bytes: &[u8] = stream_data.as_ref();
378
379        if stream_bytes.is_empty() {
380            return Ok(Self {
381                substreams: Default::default(),
382                stream_data,
383            });
384        }
385
386        let mut p = Parser::new(stream_bytes);
387        let dbi_header: &DbiStreamHeader = p.get()?;
388
389        let substreams = DbiSubstreamRanges::from_sizes(dbi_header, stream_bytes.len())?;
390
391        // We just computed the ranges for each of the substreams, and we verified that the end of
392        // the substreams is equal to the size of the entire stream. That implicitly validates all
393        // of the range checks for the substreams, so we don't need explicit / verbose checks.
394        // We can simply use normal range indexing.
395
396        Ok(Self {
397            stream_data,
398            substreams,
399        })
400    }
401
402    /// Parses the DBI Sources Substream section.
403    pub fn sources(&self) -> anyhow::Result<sources::DbiSourcesSubstream<'_>> {
404        DbiSourcesSubstream::parse(self.source_info())
405    }
406
407    /// Parses the header of the Section Contributions Substream and returns an object which can
408    /// query it.
409    pub fn section_contributions(
410        &self,
411    ) -> anyhow::Result<section_contrib::SectionContributionsSubstream<'_>> {
412        let substream_bytes = self.section_contributions_bytes();
413        section_contrib::SectionContributionsSubstream::parse(substream_bytes)
414    }
415
416    /// Parses the header of the Section Map Substream and returns an object which can query it.
417    pub fn section_map(&self) -> anyhow::Result<section_map::SectionMap<'_>> {
418        let section_map_bytes = self.section_map_bytes();
419        section_map::SectionMap::parse(section_map_bytes)
420    }
421
422    /// Parses the Optional Debug Header Substream and returns an object which can query it.
423    pub fn optional_debug_header(&self) -> anyhow::Result<optional_dbg::OptionalDebugHeader<'_>> {
424        optional_dbg::OptionalDebugHeader::parse(self.optional_debug_header_bytes())
425    }
426
427    /// Gets a mutable reference to the Optional Debug Header substream.
428    pub fn optional_debug_header_mut(&mut self) -> anyhow::Result<&mut [U16<LE>]>
429    where
430        StreamData: AsMut<[u8]>,
431    {
432        if self.substreams.optional_debug_header_bytes.is_empty() {
433            Ok(&mut [])
434        } else {
435            let substream_bytes =
436                &mut self.stream_data.as_mut()[self.substreams.optional_debug_header_bytes.clone()];
437
438            if let Ok(slice) = <[U16<LE>]>::mut_from_bytes(substream_bytes) {
439                Ok(slice)
440            } else {
441                bail!(
442                    "The Optional Debug Header substream within the DBI stream is malformed (length is not valid)."
443                );
444            }
445        }
446    }
447}
448
449/// Reads the header of the DBI stream. This does **not** validate the header.
450///
451/// This is a free function because we need to use it before constructing an instance of [`Pdb`].
452pub fn read_dbi_stream_header<F: ReadAt>(msf: &Container<F>) -> anyhow::Result<DbiStreamHeader> {
453    let stream_reader = msf.get_stream_reader(Stream::DBI.into())?;
454    if !stream_reader.is_empty() {
455        let mut dbi_header = DbiStreamHeader::new_zeroed();
456        stream_reader.read_exact_at(dbi_header.as_mut_bytes(), 0)?;
457        Ok(dbi_header)
458    } else {
459        Ok(DbiStreamHeader::read_from_bytes(EMPTY_DBI_STREAM_HEADER.as_slice()).unwrap())
460    }
461}
462
463/// Reads the entire DBI Stream, validates the header, and then returns an object that
464/// can be used for further queries of the DBI Stream.
465///
466/// This is a free function because we need to use it before constructing an instance of [`Pdb`].
467pub fn read_dbi_stream<F: ReadAt>(
468    container: &Container<F>,
469) -> Result<DbiStream<Vec<u8>>, anyhow::Error> {
470    let mut dbi_stream_data = container.read_stream_to_vec(Stream::DBI.into())?;
471    if dbi_stream_data.is_empty() {
472        dbi_stream_data = EMPTY_DBI_STREAM_HEADER.to_vec();
473    }
474
475    DbiStream::parse(dbi_stream_data)
476}
477
478impl<F: ReadAt> crate::Pdb<F> {
479    /// Reads the header of the DBI stream. This does **not** validate the header.
480    pub fn read_dbi_stream_header(&self) -> anyhow::Result<DbiStreamHeader> {
481        read_dbi_stream_header(&self.container)
482    }
483
484    /// Reads the entire DBI Stream, validates the header, and then returns an object that
485    /// can be used for further queries of the DBI Stream.
486    pub fn read_dbi_stream(&self) -> Result<DbiStream<Vec<u8>>, anyhow::Error> {
487        read_dbi_stream(&self.container)
488    }
489
490    fn read_dbi_substream(&self, range: Range<usize>) -> anyhow::Result<Vec<u8>> {
491        let len = range.len();
492        let mut substream_data = vec![0; len];
493        let reader = self.container.get_stream_reader(Stream::DBI.into())?;
494        reader.read_exact_at(&mut substream_data, range.start as u64)?;
495        Ok(substream_data)
496    }
497
498    fn read_dbi_substream_u32(&self, range: Range<usize>) -> anyhow::Result<Vec<u32>> {
499        let len_bytes = range.len();
500        let len_u32 = len_bytes / 4;
501        let mut substream_data = vec![0u32; len_u32];
502        let reader = self.container.get_stream_reader(Stream::DBI.into())?;
503        reader.read_exact_at(substream_data.as_mut_bytes(), range.start as u64)?;
504        Ok(substream_data)
505    }
506
507    /// Reads the module substream data from the DBI stream.
508    ///
509    /// This function always reads the data from the file. It does not cache the data.
510    pub fn read_modules(&self) -> anyhow::Result<ModInfoSubstream<Vec<u8>>> {
511        let substream_data = self.read_dbi_substream(self.dbi_substreams.modules_bytes.clone())?;
512        Ok(ModInfoSubstream { substream_data })
513    }
514
515    /// Gets access to the DBI Modules Substream. This will read the DBI Modules Substream
516    /// on-demand, and will cache it.
517    pub fn modules(&self) -> anyhow::Result<&ModInfoSubstream<Vec<u8>>> {
518        get_or_init_err(&self.cached.dbi_modules_cell, || self.read_modules())
519    }
520
521    /// Reads the DBI Sources Substream. This always reads the data, and does not cache it.
522    pub fn read_sources_data(&self) -> Result<Vec<u8>> {
523        self.read_dbi_substream(self.dbi_substreams.source_info.clone())
524    }
525
526    /// Gets access to the DBI Sources Substream data.
527    pub fn sources_data(&self) -> Result<&[u8]> {
528        let sources_data =
529            get_or_init_err(&self.cached.dbi_sources_cell, || self.read_sources_data())?;
530        Ok(sources_data)
531    }
532
533    /// Gets access to the DBI Sources Substream and parses the header.
534    pub fn sources(&self) -> Result<sources::DbiSourcesSubstream<'_>> {
535        let sources_data = self.sources_data()?;
536        sources::DbiSourcesSubstream::parse(sources_data)
537    }
538
539    /// Drops the cached DBI Sources Substream data, if any.
540    pub fn drop_sources(&mut self) {
541        self.cached.dbi_sources_cell = Default::default();
542    }
543
544    /// Reads the contents of the DBI Section Contributions Substream. This function never caches
545    /// the data; it is always read unconditionally.
546    ///
547    /// The returned buffer is `Vec<u32>` instead of `Vec<u8>` so that natural alignment is
548    /// guaranteed.
549    pub fn read_section_contributions(&self) -> Result<Vec<u32>> {
550        self.read_dbi_substream_u32(self.dbi_substreams.section_contributions_bytes.clone())
551    }
552
553    /// Reads (uncached) the DBI Optional Debug Streams Substream.
554    pub fn read_optional_debug_streams(&self) -> anyhow::Result<OptionalDebugHeaders> {
555        let num_opt_streams = self.dbi_substreams.optional_debug_header_bytes.len() / 2;
556        if num_opt_streams == 0 {
557            return Ok(OptionalDebugHeaders {
558                streams: Vec::new(),
559            });
560        }
561
562        let mut streams: Vec<u16> = vec![0; num_opt_streams];
563        let sr = self.get_stream_reader(Stream::DBI.value() as u32)?;
564        sr.read_exact_at(
565            streams.as_mut_bytes(),
566            self.dbi_substreams.optional_debug_header_bytes.start as u64,
567        )?;
568
569        Ok(OptionalDebugHeaders { streams })
570    }
571
572    /// Gets the stream index of the `FIXUP_DATA` optional debug stream.
573    pub fn fixup_stream(&self) -> anyhow::Result<Option<u32>> {
574        self.optional_debug_stream(OptionalDebugStream::FIXUP_DATA)
575    }
576
577    /// Gets the DBI Optional Debug Streams Substream.
578    pub fn optional_debug_streams(&self) -> anyhow::Result<&OptionalDebugHeaders> {
579        get_or_init_err(&self.cached.optional_dbg_streams, || {
580            self.read_optional_debug_streams()
581        })
582    }
583
584    /// Gets the stream index of a specific Optional Debug Stream.
585    pub fn optional_debug_stream(&self, i: OptionalDebugStream) -> anyhow::Result<Option<u32>> {
586        let streams = self.optional_debug_streams()?;
587        if let Some(&s) = streams.streams.get(i.0 as usize) {
588            if s != NIL_STREAM_INDEX {
589                Ok(Some(s as u32))
590            } else {
591                Ok(None)
592            }
593        } else {
594            Ok(None)
595        }
596    }
597
598    /// Reads (uncached) the contents of the "Section Headers" Optional Debug Stream.
599    pub fn read_section_headers(&self) -> anyhow::Result<Box<[IMAGE_SECTION_HEADER]>> {
600        let Some(stream) = self.optional_debug_stream(OptionalDebugStream::SECTION_HEADER_DATA)?
601        else {
602            // This information is not available.
603            return Ok(Box::from([]));
604        };
605
606        let sr = self.get_stream_reader(stream)?;
607        let stream_size = sr.stream_size();
608        let num_sections = stream_size as usize / core::mem::size_of::<IMAGE_SECTION_HEADER>();
609        let mut section_headers: Box<[IMAGE_SECTION_HEADER]> =
610            <[IMAGE_SECTION_HEADER]>::new_box_zeroed_with_elems(num_sections).unwrap();
611        sr.read_exact_at(section_headers.as_mut_bytes(), 0)?;
612        Ok(section_headers)
613    }
614
615    /// Gets the contents of the "Section Headers" Optional Debug Stream.
616    pub fn section_headers(&self) -> anyhow::Result<&[IMAGE_SECTION_HEADER]> {
617        get_or_init_err(&self.cached.section_headers, || self.read_section_headers()).map(|v| &**v)
618    }
619}
620
621/// Reads fields of the DBI Stream and validates them for consistency with the specification.
622pub fn validate_dbi_stream(stream_data: &[u8]) -> anyhow::Result<()> {
623    let dbi_stream = DbiStream::parse(stream_data)?;
624
625    // For now, the only validation that we do in this function is decoding the ModuleInfo records.
626    let num_modules: usize = dbi_stream.modules().iter().count();
627
628    let sources = DbiSourcesSubstream::parse(dbi_stream.source_info())?;
629    if sources.num_modules() != num_modules {
630        bail!(
631            "Number of modules found in Sources substream ({}) does not match number of Module Info structs found in Modules substream ({}).",
632            sources.num_modules(),
633            num_modules
634        );
635    }
636
637    Ok(())
638}