pdb2/
pdb.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
8use fallible_iterator::FallibleIterator;
9
10use crate::dbi::{
11    DBIExtraStreams, DBIHeader, DBISectionMapItemFlag, DBISectionMapItemSectionType,
12    DebugInformation, Module,
13};
14use crate::framedata::FrameTable;
15use crate::modi::ModuleInfo;
16use crate::msf::{self, Msf, Stream};
17use crate::omap::{AddressMap, OMAPTable};
18use crate::pdbi::PDBInformation;
19use crate::pe::{self, ImageSectionHeader};
20use crate::source::Source;
21use crate::strings::StringTable;
22use crate::symbol::SymbolTable;
23use crate::tpi::{IdInformation, TypeInformation};
24use crate::{common::*, SectionCharacteristics};
25
26// Some streams have a fixed stream index.
27// http://llvm.org/docs/PDB/index.html
28
29const PDB_STREAM: u32 = 1;
30const TPI_STREAM: u32 = 2;
31const DBI_STREAM: u32 = 3;
32const IPI_STREAM: u32 = 4;
33
34/// `PDB` provides access to the data within a PDB file.
35///
36/// A PDB file is internally a Multi-Stream File (MSF), composed of multiple independent
37/// (and usually discontiguous) data streams on-disk. `PDB` provides lazy access to these data
38/// structures, which means the `PDB` accessor methods usually cause disk accesses.
39#[derive(Debug)]
40pub struct PDB<'s, S> {
41    /// `msf` provides access to the underlying data streams
42    msf: Box<dyn Msf<'s, S> + Send + 's>,
43
44    /// Memoize the `dbi::Header`, since it contains stream numbers we sometimes need
45    dbi_header: Option<DBIHeader>,
46
47    /// Memoize the `dbi::DBIExtraStreams`, since it too contains stream numbers we sometimes need
48    dbi_extra_streams: Option<DBIExtraStreams>,
49}
50
51// Assert that the PDB type is Send.
52const _: fn() = || {
53    fn assert<T: ?Sized + Send>() {}
54    // Use a dummy *const () for `S` as that will be !Send and !Sync.
55    // In practice (e.g. to make a PDB) `S` will need to be Send, but it doesn't matter here.
56    assert::<PDB<*const ()>>();
57};
58
59impl<'s, S: Source<'s> + 's> PDB<'s, S> {
60    /// Create a new `PDB` for a `Source`.
61    ///
62    /// `open()` accesses enough of the source file to find the MSF stream table. This usually
63    /// involves reading the header, a block near the end of the file, and finally the stream table
64    /// itself. It does not access or validate any of the contents of the rest of the PDB.
65    ///
66    /// # Errors
67    ///
68    /// * `Error::UnimplementedFeature` if the PDB file predates ~2002
69    /// * `Error::UnrecognizedFileFormat` if the `Source` does not appear to be a PDB file
70    /// * `Error::IoError` if returned by the `Source`
71    /// * `Error::PageReferenceOutOfRange`, `Error::InvalidPageSize` if the PDB file seems corrupt
72    pub fn open(source: S) -> Result<PDB<'s, S>>
73    where
74        S: Send,
75    {
76        Ok(PDB {
77            msf: msf::open_msf(source)?,
78            dbi_header: None,
79            dbi_extra_streams: None,
80        })
81    }
82
83    /// Retrieve the `PDBInformation` for this PDB.
84    ///
85    /// The `PDBInformation` object contains the GUID and age fields that can be used to verify
86    /// that a PDB file matches a binary, as well as the stream indicies of named PDB streams.
87    ///
88    /// # Errors
89    ///
90    /// * `Error::StreamNotFound` if the PDB somehow does not contain the PDB information stream
91    /// * `Error::IoError` if returned by the `Source`
92    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
93    pub fn pdb_information(&mut self) -> Result<PDBInformation<'s>> {
94        let stream = self.msf.get(PDB_STREAM, None)?;
95        PDBInformation::parse(stream)
96    }
97
98    /// Retrieve the `TypeInformation` for this PDB.
99    ///
100    /// The `TypeInformation` object owns a `SourceView` for the type information ("TPI") stream.
101    /// This is usually the single largest stream of the PDB file.
102    ///
103    /// # Errors
104    ///
105    /// * `Error::StreamNotFound` if the PDB does not contain the type information stream
106    /// * `Error::IoError` if returned by the `Source`
107    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
108    /// * `Error::InvalidTypeInformationHeader` if the type information stream header was not
109    ///   understood
110    pub fn type_information(&mut self) -> Result<TypeInformation<'s>> {
111        let stream = self.msf.get(TPI_STREAM, None)?;
112        TypeInformation::parse(stream)
113    }
114
115    /// Retrieve the `IdInformation` for this PDB.
116    ///
117    /// The `IdInformation` object owns a `SourceView` for the type information ("IPI") stream.
118    ///
119    /// # Errors
120    ///
121    /// * `Error::StreamNotFound` if the PDB does not contain the id information stream
122    /// * `Error::IoError` if returned by the `Source`
123    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
124    /// * `Error::InvalidTypeInformationHeader` if the id information stream header was not
125    ///   understood
126    pub fn id_information(&mut self) -> Result<IdInformation<'s>> {
127        let stream = self.msf.get(IPI_STREAM, None)?;
128        IdInformation::parse(stream)
129    }
130
131    /// Retrieve the `DebugInformation` for this PDB.
132    ///
133    /// The `DebugInformation` object owns a `SourceView` for the debug information ("DBI") stream.
134    ///
135    /// # Errors
136    ///
137    /// * `Error::StreamNotFound` if the PDB somehow does not contain a symbol records stream
138    /// * `Error::IoError` if returned by the `Source`
139    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
140    /// * `Error::UnimplementedFeature` if the debug information header predates ~1995
141    pub fn debug_information(&mut self) -> Result<DebugInformation<'s>> {
142        let stream = self.msf.get(DBI_STREAM, None)?;
143        let debug_info = DebugInformation::parse(stream)?;
144
145        // Grab its header, since we need that for unrelated operations
146        self.dbi_header = Some(debug_info.header());
147        Ok(debug_info)
148    }
149
150    fn dbi_header(&mut self) -> Result<DBIHeader> {
151        // see if we've already got a header
152        if let Some(ref h) = self.dbi_header {
153            return Ok(*h);
154        }
155
156        // get just the first little bit of the DBI stream
157        let stream = self.msf.get(DBI_STREAM, Some(1024))?;
158        let header = DBIHeader::parse(stream)?;
159
160        self.dbi_header = Some(header);
161        Ok(header)
162    }
163
164    /// Retrieve the global symbol table for this PDB.
165    ///
166    /// The `SymbolTable` object owns a `SourceView` for the symbol records stream. This is usually
167    /// the second-largest stream of the PDB file.
168    ///
169    /// The debug information stream indicates which stream is the symbol records stream, so
170    /// `global_symbols()` accesses the debug information stream to read the header unless
171    /// `debug_information()` was called first.
172    ///
173    /// # Errors
174    ///
175    /// * `Error::StreamNotFound` if the PDB somehow does not contain a symbol records stream
176    /// * `Error::IoError` if returned by the `Source`
177    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
178    ///
179    /// If `debug_information()` was not already called, `global_symbols()` will additionally read
180    /// the debug information header, in which case it can also return:
181    ///
182    /// * `Error::StreamNotFound` if the PDB somehow does not contain a debug information stream
183    /// * `Error::UnimplementedFeature` if the debug information header predates ~1995
184    pub fn global_symbols(&mut self) -> Result<SymbolTable<'s>> {
185        // the global symbol table is stored in a stream number described by the DBI header
186        // so, start by getting the DBI header
187        let dbi_header = self.dbi_header()?;
188
189        // open the appropriate stream, assuming that it is always present.
190        let stream = self
191            .raw_stream(dbi_header.symbol_records_stream)?
192            .ok_or(Error::GlobalSymbolsNotFound)?;
193
194        Ok(SymbolTable::new(stream))
195    }
196
197    /// Retrieve the module info stream for a specific `Module`.
198    ///
199    /// Some information for each module is stored in a separate stream per-module. `Module`s can be
200    /// retrieved from the `PDB` by first calling [`debug_information`](Self::debug_information) to
201    /// get the debug information stream, and then calling [`modules`](DebugInformation::modules) on
202    /// that.
203    ///
204    /// # Errors
205    ///
206    /// * `Error::StreamNotFound` if the PDB does not contain this module info stream
207    /// * `Error::IoError` if returned by the `Source`
208    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
209    /// * `Error::UnimplementedFeature` if the module information stream is an unsupported version
210    ///
211    /// # Example
212    ///
213    /// ```
214    /// # use pdb2::FallibleIterator;
215    /// #
216    /// # fn test() -> pdb2::Result<()> {
217    /// let file = std::fs::File::open("fixtures/self/foo.pdb")?;
218    /// let mut pdb = pdb2::PDB::open(file)?;
219    /// let dbi = pdb.debug_information()?;
220    /// let mut modules = dbi.modules()?;
221    /// if let Some(module) = modules.next()? {
222    ///     println!("module name: {}, object file name: {}",
223    ///              module.module_name(), module.object_file_name());
224    ///     match pdb.module_info(&module)? {
225    ///         Some(info) => println!("contains {} symbols", info.symbols()?.count()?),
226    ///         None => println!("module information not available"),
227    ///     }
228    /// }
229    ///
230    /// # Ok(())
231    /// # }
232    /// ```
233    pub fn module_info<'m>(&mut self, module: &Module<'m>) -> Result<Option<ModuleInfo<'s>>> {
234        Ok(self
235            .raw_stream(module.info().stream)?
236            .map(|stream| ModuleInfo::parse(stream, module)))
237    }
238
239    /// Retrieve the executable's section headers, as stored inside this PDB.
240    ///
241    /// The debug information stream indicates which stream contains the section headers, so
242    /// `sections()` accesses the debug information stream to read the header unless
243    /// `debug_information()` was called first.
244    ///
245    /// # Errors
246    ///
247    /// * `Error::StreamNotFound` if the PDB somehow does not contain section headers
248    /// * `Error::IoError` if returned by the `Source`
249    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
250    /// * `Error::UnexpectedEof` if the section headers are truncated mid-record
251    ///
252    /// If `debug_information()` was not already called, `sections()` will additionally read
253    /// the debug information header, in which case it can also return:
254    ///
255    /// * `Error::StreamNotFound` if the PDB somehow does not contain a debug information stream
256    /// * `Error::UnimplementedFeature` if the debug information header predates ~1995
257    pub fn sections(&mut self) -> Result<Option<Vec<ImageSectionHeader>>> {
258        let index = self.extra_streams()?.section_headers;
259        let stream = match self.raw_stream(index)? {
260            Some(stream) => stream,
261            None => return self.maybe_synthesize_section(),
262        };
263
264        let mut buf = stream.parse_buffer();
265        let mut headers = Vec::with_capacity(buf.len() / 40);
266        while !buf.is_empty() {
267            headers.push(ImageSectionHeader::parse(&mut buf)?);
268        }
269
270        Ok(Some(headers))
271    }
272
273    // If there are no section_headers in the file, attempt to synthesize sections
274    // based on the section map. This seems to be necessary to handle NGEN-generated PDB
275    // files (.ni.pdb from Crossgen2).
276    fn maybe_synthesize_section(&mut self) -> Result<Option<Vec<ImageSectionHeader>>> {
277        // If we have OMAP From data, I don't believe we can do this, because the RVAs
278        // won't map. But I'm not 100% sure of that, be conservative.
279        if self.omap_from_src()?.is_some() {
280            return Ok(None);
281        }
282
283        let debug_info = self.debug_information()?;
284        let sec_map = debug_info.section_map()?;
285        if sec_map.sec_count != sec_map.sec_count_log {
286            return Ok(None);
287        }
288        let sec_map = sec_map.collect::<Vec<_>>()?;
289
290        let mut rva = 0x1000u32; // in the absence of explicit section data, this starts at 0x1000
291        let sections = sec_map.into_iter()
292        .filter(|sm| {
293            // the section with a bogus section length also doesn't have any rwx flags,
294            // and has section_type == 2
295            sm.section_type == DBISectionMapItemSectionType::Sel as u8 &&
296            sm.section_length != u32::MAX // shouldn't happen, but just in case
297        })
298        .map(|sm| {
299            let mut characteristics = 0u32;
300            if sm.flags & DBISectionMapItemFlag::Read as u8 != 0 {
301                characteristics |= pe::IMAGE_SCN_MEM_READ;
302            }
303            if sm.flags & DBISectionMapItemFlag::Write as u8 != 0 {
304                characteristics |= pe::IMAGE_SCN_MEM_WRITE;
305            }
306            if sm.flags & DBISectionMapItemFlag::Execute as u8 != 0 {
307                characteristics |= pe::IMAGE_SCN_MEM_EXECUTE;
308                characteristics |= pe::IMAGE_SCN_CNT_CODE;
309            }
310
311            if sm.rva_offset != 0 {
312                eprintln!("pdb: synthesizing section with rva_offset != 0, might not be correct! {:?}", sm);
313            }
314
315            let this_rva = rva + sm.rva_offset;
316            rva = this_rva + sm.section_length;
317            ImageSectionHeader {
318                name: [0; 8],
319                virtual_size: sm.section_length,
320                virtual_address: this_rva,
321                size_of_raw_data: sm.section_length,
322                pointer_to_raw_data: 0,
323                pointer_to_relocations: 0,
324                pointer_to_line_numbers: 0,
325                number_of_relocations: 0,
326                number_of_line_numbers: 0,
327                characteristics: SectionCharacteristics(characteristics),
328            }
329        }).collect::<Vec<_>>();
330
331        Ok(Some(sections))
332    }
333
334    /// Retrieve the global frame data table.
335    ///
336    /// This table describes the stack frame layout for functions from all modules in the PDB. Not
337    /// every function in the image file must have FPO information defined for it. Those functions
338    /// that do not have FPO information are assumed to have normal stack frames.
339    ///
340    /// If this PDB does not contain frame data, the returned table is empty.
341    ///
342    /// # Errors
343    ///
344    /// * `Error::StreamNotFound` if the PDB does not contain the referenced streams
345    /// * `Error::IoError` if returned by the `Source`
346    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
347    ///
348    /// # Example
349    ///
350    /// ```rust
351    /// # use pdb2::{PDB, Rva, FallibleIterator};
352    /// #
353    /// # fn test() -> pdb2::Result<()> {
354    /// # let source = std::fs::File::open("fixtures/self/foo.pdb")?;
355    /// let mut pdb = PDB::open(source)?;
356    ///
357    /// // Read the tables once and reuse them
358    /// let address_map = pdb.address_map()?;
359    /// let frame_table = pdb.frame_table()?;
360    /// let mut frames = frame_table.iter();
361    ///
362    /// // Iterate frame data in internal RVA order
363    /// while let Some(frame) = frames.next()? {
364    ///     println!("{:#?}", frame);
365    /// }
366    /// # Ok(())
367    /// # }
368    /// # test().unwrap()
369    /// ```
370    pub fn frame_table(&mut self) -> Result<FrameTable<'s>> {
371        let extra = self.extra_streams()?;
372        let old_stream = self.raw_stream(extra.fpo)?;
373        let new_stream = self.raw_stream(extra.framedata)?;
374        FrameTable::parse(old_stream, new_stream)
375    }
376
377    pub(crate) fn original_sections(&mut self) -> Result<Option<Vec<ImageSectionHeader>>> {
378        let index = self.extra_streams()?.original_section_headers;
379        let stream = match self.raw_stream(index)? {
380            Some(stream) => stream,
381            None => return Ok(None),
382        };
383
384        let mut buf = stream.parse_buffer();
385        let mut headers = Vec::with_capacity(buf.len() / 40);
386        while !buf.is_empty() {
387            headers.push(ImageSectionHeader::parse(&mut buf)?);
388        }
389
390        Ok(Some(headers))
391    }
392
393    pub(crate) fn omap_from_src(&mut self) -> Result<Option<OMAPTable<'s>>> {
394        let index = self.extra_streams()?.omap_from_src;
395        match self.raw_stream(index)? {
396            Some(stream) => OMAPTable::parse(stream).map(Some),
397            None => Ok(None),
398        }
399    }
400
401    pub(crate) fn omap_to_src(&mut self) -> Result<Option<OMAPTable<'s>>> {
402        let index = self.extra_streams()?.omap_to_src;
403        match self.raw_stream(index)? {
404            Some(stream) => OMAPTable::parse(stream).map(Some),
405            None => Ok(None),
406        }
407    }
408
409    /// Build a map translating between different kinds of offsets and virtual addresses.
410    ///
411    /// For more information on address translation, see [`AddressMap`].
412    ///
413    /// This reads `omap_from_src` and either `original_sections` or `sections` from this PDB and
414    /// chooses internally which strategy to use for resolving RVAs. Consider to reuse this instance
415    /// for multiple translations.
416    ///
417    /// # Errors
418    ///
419    /// * `Error::OmapNotFound` if an OMAP is required for translation but missing
420    /// * `Error::StreamNotFound` if the PDB somehow does not contain section headers
421    /// * `Error::IoError` if returned by the `Source`
422    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
423    /// * `Error::UnexpectedEof` if the section headers are truncated mid-record
424    ///
425    /// If `debug_information()` was not already called, `omap_table()` will additionally read the
426    /// debug information header, in which case it can also return:
427    ///
428    /// * `Error::StreamNotFound` if the PDB somehow does not contain a debug information stream
429    /// * `Error::UnimplementedFeature` if the debug information header predates ~1995
430    ///
431    /// # Example
432    ///
433    /// ```rust
434    /// # use pdb2::{Rva, FallibleIterator};
435    /// #
436    /// # fn test() -> pdb2::Result<()> {
437    /// # let source = std::fs::File::open("fixtures/self/foo.pdb")?;
438    /// let mut pdb = pdb2::PDB::open(source)?;
439    ///
440    /// // Compute the address map once and reuse it
441    /// let address_map = pdb.address_map()?;
442    ///
443    /// # let symbol_table = pdb.global_symbols()?;
444    /// # let symbol = symbol_table.iter().next()?.unwrap();
445    /// # match symbol.parse() { Ok(pdb2::SymbolData::Public(pubsym)) => {
446    /// // Obtain some section offset, eg from a symbol, and convert it
447    /// match pubsym.offset.to_rva(&address_map) {
448    ///     Some(rva) => {
449    ///         println!("symbol is at {}", rva);
450    /// #       assert_eq!(rva, Rva(26048));
451    ///     }
452    ///     None => {
453    ///         println!("symbol refers to eliminated code");
454    /// #       panic!("symbol should exist");
455    ///     }
456    /// }
457    /// # } _ => unreachable!() }
458    /// # Ok(())
459    /// # }
460    /// # test().unwrap()
461    /// ```
462    pub fn address_map(&mut self) -> Result<AddressMap<'s>> {
463        let sections = self.sections()?.unwrap_or_default();
464        Ok(match self.original_sections()? {
465            Some(original_sections) => {
466                let omap_from_src = self.omap_from_src()?.ok_or(Error::AddressMapNotFound)?;
467                let omap_to_src = self.omap_to_src()?.ok_or(Error::AddressMapNotFound)?;
468
469                AddressMap {
470                    original_sections,
471                    transformed_sections: Some(sections),
472                    original_to_transformed: Some(omap_from_src),
473                    transformed_to_original: Some(omap_to_src),
474                }
475            }
476            None => AddressMap {
477                original_sections: sections,
478                transformed_sections: None,
479                original_to_transformed: None,
480                transformed_to_original: None,
481            },
482        })
483    }
484
485    /// Retrieve the global string table of this PDB.
486    ///
487    /// Long strings, such as file names, are stored in a global deduplicated string table. They are
488    /// referred to by the [`StringRef`] type, which contains an offset into that table. Strings in
489    /// the table are stored as null-terminated C strings. Modern PDBs only store valid UTF-8 data
490    /// in the string table, but for older types a decoding might be necessary.
491    ///
492    /// The string table offers cheap zero-copy access to the underlying string data. It is
493    /// therefore cheap to build.
494    ///
495    /// # Example
496    ///
497    /// ```
498    /// # use pdb2::{FallibleIterator, StringRef, PDB};
499    /// #
500    /// # fn test() -> pdb2::Result<()> {
501    /// # let file = std::fs::File::open("fixtures/self/foo.pdb")?;
502    /// let mut pdb = PDB::open(file)?;
503    /// let strings = pdb.string_table()?;
504    ///
505    /// // obtain a string ref somehow
506    /// # let string_ref = StringRef(0);
507    /// let raw_string = strings.get(string_ref)?;
508    /// println!("{}", raw_string.to_string());
509    ///
510    /// // alternatively, use convenience methods
511    /// println!("{}", string_ref.to_string_lossy(&strings)?);
512    ///
513    /// # Ok(())
514    /// # }
515    /// ```
516    ///
517    /// # Errors
518    ///
519    /// * `Error::StreamNotFound` if the PDB somehow does not contain section headers
520    /// * `Error::IoError` if returned by the `Source`
521    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
522    /// * `Error::UnexpectedEof` if the string table ends prematurely
523    pub fn string_table(&mut self) -> Result<StringTable<'s>> {
524        let stream = self.named_stream(b"/names")?;
525        StringTable::parse(stream)
526    }
527
528    /// Retrieve a stream by its index to read its contents as bytes.
529    ///
530    /// # Errors
531    ///
532    /// * `Error::StreamNotFound` if the PDB does not contain this stream
533    /// * `Error::IoError` if returned by the `Source`
534    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
535    ///
536    /// # Example
537    ///
538    /// ```
539    /// # fn test() -> pdb2::Result<()> {
540    /// let file = std::fs::File::open("fixtures/self/foo.pdb")?;
541    /// let mut pdb = pdb2::PDB::open(file)?;
542    /// // This is the index of the "mystream" stream that was added using pdbstr.exe.
543    /// let s = pdb.raw_stream(pdb2::StreamIndex(208))?.expect("stream exists");
544    /// assert_eq!(s.as_slice(), b"hello world\n");
545    /// # Ok(())
546    /// # }
547    /// ```
548    pub fn raw_stream(&mut self, index: StreamIndex) -> Result<Option<Stream<'s>>> {
549        match index.msf_number() {
550            Some(number) => self.msf.get(number, None).map(Some),
551            None => Ok(None),
552        }
553    }
554
555    /// Retrieve a stream by its name, as declared in the PDB info stream.
556    ///
557    /// # Errors
558    ///
559    /// * `Error::StreamNameNotFound` if the PDB does not specify a stream with that name
560    /// * `Error::StreamNotFound` if the PDB does not contain the stream referred to
561    /// * `Error::IoError` if returned by the `Source`
562    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
563    pub fn named_stream(&mut self, name: &[u8]) -> Result<Stream<'s>> {
564        let info = self.pdb_information()?;
565        let names = info.stream_names()?;
566        for named_stream in &names {
567            if named_stream.name.as_bytes() == name {
568                return self
569                    .raw_stream(named_stream.stream_id)?
570                    .ok_or(Error::StreamNameNotFound);
571            }
572        }
573        Err(Error::StreamNameNotFound)
574    }
575
576    /// Loads the Optional Debug Header Stream, which contains offsets into extra streams.
577    ///
578    /// this stream is always returned, but its members are all optional depending on the data
579    /// present in the PDB.
580    ///
581    /// The optional header begins at offset 0 immediately after the EC Substream ends.
582    fn extra_streams(&mut self) -> Result<DBIExtraStreams> {
583        if let Some(extra) = self.dbi_extra_streams {
584            return Ok(extra);
585        }
586
587        // Parse and grab information on extra streams, since we might also need that
588        let debug_info = self.debug_information()?;
589        let extra = DBIExtraStreams::new(&debug_info)?;
590        self.dbi_extra_streams = Some(extra);
591
592        Ok(extra)
593    }
594}
595
596impl StreamIndex {
597    /// Load the raw data of this stream from the PDB.
598    ///
599    /// Returns `None` if this index is none. Otherwise, this will try to read the stream from the
600    /// PDB, which might fail if the stream is missing.
601    ///
602    /// # Errors
603    ///
604    /// * `Error::StreamNotFound` if the PDB does not contain this stream
605    /// * `Error::IoError` if returned by the `Source`
606    /// * `Error::PageReferenceOutOfRange` if the PDB file seems corrupt
607    pub fn get<'s, S>(self, pdb: &mut PDB<'s, S>) -> Result<Option<Stream<'s>>>
608    where
609        S: Source<'s> + 's,
610    {
611        pdb.raw_stream(self)
612    }
613}