1use std::borrow::Cow;
11use std::fmt;
12use std::result;
13
14use crate::common::*;
15use crate::msf::*;
16use crate::{FallibleIterator, SectionCharacteristics};
17
18#[derive(Debug)]
45pub struct DebugInformation<'s> {
46 stream: Stream<'s>,
47 header: DBIHeader,
48 header_len: usize,
49}
50
51impl<'s> DebugInformation<'s> {
52 pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> {
53 let mut buf = stream.parse_buffer();
54 let header = DBIHeader::parse_buf(&mut buf)?;
55 let header_len = buf.pos();
56
57 Ok(DebugInformation {
58 stream,
59 header,
60 header_len,
61 })
62 }
63
64 pub(crate) fn header(&self) -> DBIHeader {
65 self.header
66 }
67
68 pub fn machine_type(&self) -> Result<MachineType> {
70 Ok(self.header.machine_type.into())
71 }
72
73 #[must_use]
84 pub fn age(&self) -> Option<u32> {
85 match self.header.age {
86 0 => None,
87 age => Some(age),
88 }
89 }
90
91 #[must_use]
98 pub fn is_stripped(&self) -> bool {
99 (self.header.flags & 0x2) != 0
101 }
102
103 pub fn modules(&self) -> Result<ModuleIter<'_>> {
105 let mut buf = self.stream.parse_buffer();
106 buf.take(self.header_len)?;
108 let modules_buf = buf.take(self.header.module_list_size as usize)?;
109 Ok(ModuleIter {
110 buf: modules_buf.into(),
111 })
112 }
113
114 pub fn section_contributions(&self) -> Result<DBISectionContributionIter<'_>> {
116 let mut buf = self.stream.parse_buffer();
117 buf.take(self.header_len + self.header.module_list_size as usize)?;
119 let contributions_buf = buf.take(self.header.section_contribution_size as usize)?;
120 DBISectionContributionIter::parse(contributions_buf.into())
121 }
122
123 pub fn section_map(&self) -> Result<DBISectionMapIter<'_>> {
125 let mut buf = self.stream.parse_buffer();
126 let offset = self.header_len
128 + self.header.module_list_size as usize
129 + self.header.section_contribution_size as usize;
130
131 buf.take(offset)?;
132 let section_map_buf = buf.take(self.header.section_map_size as usize)?;
133 DBISectionMapIter::parse(section_map_buf.into())
134 }
135}
136
137#[non_exhaustive]
141#[derive(Debug, Copy, Clone)]
142#[allow(missing_docs)]
143pub enum HeaderVersion {
144 V41,
145 V50,
146 V60,
147 V70,
148 V110,
149 OtherValue(u32),
150}
151
152impl From<u32> for HeaderVersion {
153 #[allow(clippy::inconsistent_digit_grouping)]
154 fn from(v: u32) -> Self {
155 match v {
156 93_08_03 => Self::V41,
157 1996_03_07 => Self::V50,
158 1997_06_06 => Self::V60,
159 1999_09_03 => Self::V70,
160 2009_12_01 => Self::V110,
161 _ => Self::OtherValue(v),
162 }
163 }
164}
165
166#[derive(Debug, Copy, Clone)]
171#[allow(dead_code)] pub(crate) struct DBIHeader {
173 pub signature: u32,
174 pub version: HeaderVersion,
175 pub age: u32,
176 pub gs_symbols_stream: StreamIndex,
177
178 pub internal_version: u16,
195 pub ps_symbols_stream: StreamIndex,
196 pub pdb_dll_build_version: u16,
198
199 pub symbol_records_stream: StreamIndex,
200
201 pub pdb_dll_rbld_version: u16,
203 pub module_list_size: u32,
204 pub section_contribution_size: u32,
205 pub section_map_size: u32,
206 pub file_info_size: u32,
207
208 pub type_server_map_size: u32,
210
211 pub mfc_type_server_index: u32,
213
214 pub debug_header_size: u32,
216
217 pub ec_substream_size: u32,
219
220 pub flags: u16,
228
229 pub machine_type: u16,
230 pub reserved: u32,
231}
232
233impl DBIHeader {
234 pub fn parse(stream: Stream<'_>) -> Result<Self> {
235 Self::parse_buf(&mut stream.parse_buffer())
236 }
237
238 fn parse_buf(buf: &mut ParseBuffer<'_>) -> Result<Self> {
239 let header = Self {
240 signature: buf.parse_u32()?,
241 version: From::from(buf.parse_u32()?),
242 age: buf.parse_u32()?,
243 gs_symbols_stream: buf.parse()?,
244 internal_version: buf.parse_u16()?,
245 ps_symbols_stream: buf.parse()?,
246 pdb_dll_build_version: buf.parse_u16()?,
247 symbol_records_stream: buf.parse()?,
248 pdb_dll_rbld_version: buf.parse_u16()?,
249 module_list_size: buf.parse_u32()?,
250 section_contribution_size: buf.parse_u32()?,
251 section_map_size: buf.parse_u32()?,
252 file_info_size: buf.parse_u32()?,
253 type_server_map_size: buf.parse_u32()?,
254 mfc_type_server_index: buf.parse_u32()?,
255 debug_header_size: buf.parse_u32()?,
256 ec_substream_size: buf.parse_u32()?,
257 flags: buf.parse_u16()?,
258 machine_type: buf.parse_u16()?,
259 reserved: buf.parse_u32()?,
260 };
261
262 if header.signature != u32::MAX {
263 return Err(Error::UnimplementedFeature("ancient DBI header"));
269 }
270
271 Ok(header)
272 }
273}
274
275#[non_exhaustive]
278#[derive(Clone, Copy, Debug, Eq, PartialEq)]
279pub enum MachineType {
280 Unknown = 0x0,
282 Am33 = 0x13,
284 Amd64 = 0x8664,
286 Arm = 0x1C0,
288 Arm64 = 0xAA64,
290 ArmNT = 0x1C4,
292 Ebc = 0xEBC,
294 X86 = 0x14C,
296 Ia64 = 0x200,
298 M32R = 0x9041,
300 Mips16 = 0x266,
302 MipsFpu = 0x366,
304 MipsFpu16 = 0x466,
306 PowerPC = 0x1F0,
308 PowerPCFP = 0x1F1,
310 R4000 = 0x166,
312 RiscV32 = 0x5032,
314 RiscV64 = 0x5064,
316 RiscV128 = 0x5128,
318 SH3 = 0x1A2,
320 SH3DSP = 0x1A3,
322 SH4 = 0x1A6,
324 SH5 = 0x1A8,
326 Thumb = 0x1C2,
328 WceMipsV2 = 0x169,
330 Invalid = 0xffff,
332}
333
334impl fmt::Display for MachineType {
335 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
336 match self {
337 Self::Invalid => write!(f, "Invalid"),
338 Self::Unknown => write!(f, "Unknown"),
339 Self::Am33 => write!(f, "Am33"),
340 Self::Amd64 => write!(f, "Amd64"),
341 Self::Arm => write!(f, "Arm"),
342 Self::Arm64 => write!(f, "Arm64"),
343 Self::ArmNT => write!(f, "ArmNT"),
344 Self::Ebc => write!(f, "Ebc"),
345 Self::X86 => write!(f, "X86"),
346 Self::Ia64 => write!(f, "Ia64"),
347 Self::M32R => write!(f, "M32R"),
348 Self::Mips16 => write!(f, "Mips16"),
349 Self::MipsFpu => write!(f, "MipsFpu"),
350 Self::MipsFpu16 => write!(f, "MipsFpu16"),
351 Self::PowerPC => write!(f, "PowerPC"),
352 Self::PowerPCFP => write!(f, "PowerPCFP"),
353 Self::R4000 => write!(f, "R4000"),
354 Self::RiscV32 => write!(f, "RiscV32"),
355 Self::RiscV64 => write!(f, "RiscV64"),
356 Self::RiscV128 => write!(f, "RiscV128"),
357 Self::SH3 => write!(f, "SH3"),
358 Self::SH3DSP => write!(f, "SH3DSP"),
359 Self::SH4 => write!(f, "SH4"),
360 Self::SH5 => write!(f, "SH5"),
361 Self::Thumb => write!(f, "Thumb"),
362 Self::WceMipsV2 => write!(f, "WceMipsV2"),
363 }
364 }
365}
366
367impl From<u16> for MachineType {
368 fn from(value: u16) -> Self {
369 match value {
370 0xffff => Self::Invalid,
371 0x0 => Self::Unknown,
372 0x13 => Self::Am33,
373 0x8664 => Self::Amd64,
374 0x1C0 => Self::Arm,
375 0xAA64 => Self::Arm64,
376 0x1C4 => Self::ArmNT,
377 0xEBC => Self::Ebc,
378 0x14C => Self::X86,
379 0x200 => Self::Ia64,
380 0x9041 => Self::M32R,
381 0x266 => Self::Mips16,
382 0x366 => Self::MipsFpu,
383 0x466 => Self::MipsFpu16,
384 0x1F0 => Self::PowerPC,
385 0x1F1 => Self::PowerPCFP,
386 0x166 => Self::R4000,
387 0x5032 => Self::RiscV32,
388 0x5064 => Self::RiscV64,
389 0x5128 => Self::RiscV128,
390 0x1A2 => Self::SH3,
391 0x1A3 => Self::SH3DSP,
392 0x1A6 => Self::SH4,
393 0x1A8 => Self::SH5,
394 0x1C2 => Self::Thumb,
395 0x169 => Self::WceMipsV2,
396 _ => Self::Unknown,
397 }
398 }
399}
400
401#[derive(Debug, Copy, Clone)]
405pub struct DBISectionContribution {
406 pub offset: PdbInternalSectionOffset,
408 pub size: u32,
410 pub characteristics: SectionCharacteristics,
414 pub module: usize,
416 pub data_crc: u32,
418 pub reloc_crc: u32,
420}
421
422impl DBISectionContribution {
423 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
424 let section = buf.parse_u16()?;
425 let _padding = buf.parse_u16()?;
426 let offset = buf.parse_u32()?;
427 let size = buf.parse_u32()?;
428 let characteristics = buf.parse()?;
429 let module = buf.parse_u16()?.into();
430 let _padding = buf.parse_u16()?;
431
432 Ok(Self {
433 offset: PdbInternalSectionOffset { offset, section },
434 size,
435 characteristics,
436 module,
437 data_crc: buf.parse_u32()?,
438 reloc_crc: buf.parse_u32()?,
439 })
440 }
441}
442
443#[derive(Debug, Copy, Clone)]
448#[allow(dead_code)] pub(crate) struct DBIModuleInfo {
450 pub opened: u32,
452 pub section: DBISectionContribution,
454 pub flags: u16,
458 pub stream: StreamIndex,
460 pub symbols_size: u32,
462 pub lines_size: u32,
464 pub c13_lines_size: u32,
466 pub files: u16,
468 _padding: u16,
469 pub filename_offsets: u32,
471 pub source: u32,
473 pub compiler: u32,
475}
476
477impl DBIModuleInfo {
478 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
479 Ok(Self {
480 opened: buf.parse_u32()?,
481 section: DBISectionContribution::parse(buf)?,
482 flags: buf.parse_u16()?,
483 stream: buf.parse()?,
484 symbols_size: buf.parse_u32()?,
485 lines_size: buf.parse_u32()?,
486 c13_lines_size: buf.parse_u32()?,
487 files: buf.parse_u16()?,
488 _padding: buf.parse_u16()?,
489 filename_offsets: buf.parse_u32()?,
490 source: buf.parse_u32()?,
491 compiler: buf.parse_u32()?,
492 })
493 }
494}
495
496#[derive(Debug, Clone)]
504pub struct Module<'m> {
505 info: DBIModuleInfo,
506 module_name: RawString<'m>,
507 object_file_name: RawString<'m>,
508}
509
510impl<'m> Module<'m> {
511 pub(crate) fn info(&self) -> &DBIModuleInfo {
513 &self.info
514 }
515 #[must_use]
519 pub fn module_name(&self) -> Cow<'m, str> {
520 self.module_name.to_string()
521 }
522 #[must_use]
528 pub fn object_file_name(&self) -> Cow<'m, str> {
529 self.object_file_name.to_string()
530 }
531}
532
533#[derive(Debug)]
535pub struct ModuleIter<'m> {
536 buf: ParseBuffer<'m>,
537}
538
539impl<'m> FallibleIterator for ModuleIter<'m> {
540 type Item = Module<'m>;
541 type Error = Error;
542
543 fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
544 if self.buf.is_empty() {
546 return Ok(None);
547 }
548
549 let info = DBIModuleInfo::parse(&mut self.buf)?;
550 let module_name = self.buf.parse_cstring()?;
551 let object_file_name = self.buf.parse_cstring()?;
552 self.buf.align(4)?;
553 Ok(Some(Module {
554 info,
555 module_name,
556 object_file_name,
557 }))
558 }
559}
560
561#[derive(Debug, Copy, Clone, PartialEq)]
563#[allow(missing_docs)]
564enum DBISectionContributionStreamVersion {
565 V60,
566 V2,
567 OtherValue(u32),
568}
569
570impl From<u32> for DBISectionContributionStreamVersion {
571 fn from(v: u32) -> Self {
572 const V60: u32 = 0xeffe_0000 + 19_970_605;
573 const V2: u32 = 0xeffe_0000 + 20_140_516;
574 match v {
575 V60 => Self::V60,
576 V2 => Self::V2,
577 _ => Self::OtherValue(v),
578 }
579 }
580}
581
582#[derive(Debug)]
584pub struct DBISectionContributionIter<'c> {
585 buf: ParseBuffer<'c>,
586 version: DBISectionContributionStreamVersion,
587}
588
589impl<'c> DBISectionContributionIter<'c> {
590 fn parse(mut buf: ParseBuffer<'c>) -> Result<Self> {
591 let version = buf.parse_u32()?.into();
592 Ok(Self { buf, version })
593 }
594}
595
596impl FallibleIterator for DBISectionContributionIter<'_> {
597 type Item = DBISectionContribution;
598 type Error = Error;
599
600 fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
601 if self.buf.is_empty() {
603 return Ok(None);
604 }
605
606 let contribution = DBISectionContribution::parse(&mut self.buf)?;
607 if self.version == DBISectionContributionStreamVersion::V2 {
608 self.buf.parse_u32()?;
609 }
610 Ok(Some(contribution))
611 }
612}
613
614#[derive(Debug, Copy, Clone)]
619pub struct DBISectionMapItem {
620 pub flags: u8,
622 pub section_type: u8,
624 pub overlay: u16,
626 pub group: u16,
628 pub section_number: u16,
630 pub seg_name_index: u16,
632 pub class_name_index: u16,
634 pub rva_offset: u32,
636 pub section_length: u32,
638}
639
640#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
642#[repr(u8)]
643pub enum DBISectionMapItemFlag {
644 Read = 1 << 0,
646 Write = 1 << 1,
648 Execute = 1 << 2,
650 ThirtyTwoBit = 1 << 3,
652}
653
654#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
656#[repr(u8)]
657pub enum DBISectionMapItemSectionType {
658 Sel = 1 << 0,
660 Abs = 1 << 1,
662 Group = 1 << 4,
664}
665
666impl DBISectionMapItem {
667 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
668 Ok(Self {
669 flags: buf.parse_u8()?,
670 section_type: buf.parse_u8()?,
671 overlay: buf.parse_u16()?,
672 group: buf.parse_u16()?,
673 section_number: buf.parse_u16()?,
674 seg_name_index: buf.parse_u16()?,
675 class_name_index: buf.parse_u16()?,
676 rva_offset: buf.parse_u32()?,
677 section_length: buf.parse_u32()?,
678 })
679 }
680}
681
682#[derive(Debug)]
684pub struct DBISectionMapIter<'c> {
685 pub sec_count: u16,
687 pub sec_count_log: u16,
689 buf: ParseBuffer<'c>,
690}
691
692impl<'c> DBISectionMapIter<'c> {
693 fn parse(mut buf: ParseBuffer<'c>) -> Result<Self> {
694 let sec_count = buf.parse_u16()?;
695 let sec_count_log = buf.parse_u16()?;
696
697 Ok(Self {
698 buf,
699 sec_count,
700 sec_count_log,
701 })
702 }
703}
704
705impl FallibleIterator for DBISectionMapIter<'_> {
706 type Item = DBISectionMapItem;
707 type Error = Error;
708
709 fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> {
710 if self.buf.is_empty() {
712 return Ok(None);
713 }
714
715 let segmap = Self::Item::parse(&mut self.buf)?;
716 Ok(Some(segmap))
717 }
718}
719
720#[derive(Debug, Copy, Clone)]
722#[allow(dead_code)] pub(crate) struct DBIExtraStreams {
724 pub fpo: StreamIndex,
735 pub exception: StreamIndex,
736 pub fixup: StreamIndex,
737 pub omap_to_src: StreamIndex,
738 pub omap_from_src: StreamIndex,
739 pub section_headers: StreamIndex,
740 pub token_rid_map: StreamIndex,
741 pub xdata: StreamIndex,
742 pub pdata: StreamIndex,
743 pub framedata: StreamIndex,
744 pub original_section_headers: StreamIndex,
745}
746
747impl DBIExtraStreams {
748 pub(crate) fn new(debug_info: &DebugInformation<'_>) -> Result<Self> {
749 let header = debug_info.header;
751 let offset = debug_info.header_len
752 + (header.module_list_size
753 + header.section_contribution_size
754 + header.section_map_size
755 + header.file_info_size
756 + header.type_server_map_size
757 + header.ec_substream_size) as usize;
758
759 let mut buf = debug_info.stream.parse_buffer();
761 buf.take(offset)?;
762
763 let bytes = buf.take(header.debug_header_size as _)?;
765
766 let mut extra_streams_buf = ParseBuffer::from(bytes);
768 Self::parse(&mut extra_streams_buf)
769 }
770
771 pub(crate) fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
772 if buf.len() % 2 != 0 {
775 return Err(Error::InvalidStreamLength("DbgDataHdr"));
776 }
777
778 fn next_index(buf: &mut ParseBuffer<'_>) -> Result<StreamIndex> {
779 if buf.is_empty() {
780 Ok(StreamIndex::none())
781 } else {
782 buf.parse()
783 }
784 }
785
786 Ok(Self {
787 fpo: next_index(buf)?,
788 exception: next_index(buf)?,
789 fixup: next_index(buf)?,
790 omap_to_src: next_index(buf)?,
791 omap_from_src: next_index(buf)?,
792 section_headers: next_index(buf)?,
793 token_rid_map: next_index(buf)?,
794 xdata: next_index(buf)?,
795 pdata: next_index(buf)?,
796 framedata: next_index(buf)?,
797 original_section_headers: next_index(buf)?,
798 })
799 }
800}
801
802#[cfg(test)]
803mod tests {
804 use crate::dbi::*;
805
806 #[test]
807 fn test_dbi_extra_streams() {
808 let bytes = vec![0xff, 0xff, 0x01, 0x02, 0x03, 0x04, 0xff, 0xff, 0x05, 0x06];
809
810 let mut buf = ParseBuffer::from(bytes.as_slice());
811 let extra_streams = DBIExtraStreams::parse(&mut buf).expect("parse");
812
813 assert_eq!(extra_streams.fpo, StreamIndex::none());
815 assert_eq!(extra_streams.exception, StreamIndex(0x0201));
816 assert_eq!(extra_streams.fixup, StreamIndex(0x0403));
817 assert_eq!(extra_streams.omap_to_src, StreamIndex::none());
818 assert_eq!(extra_streams.omap_from_src, StreamIndex(0x0605));
819
820 assert_eq!(extra_streams.section_headers, StreamIndex::none());
822 assert_eq!(extra_streams.token_rid_map, StreamIndex::none());
823 assert_eq!(extra_streams.original_section_headers, StreamIndex::none());
824 }
825}