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