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}