1use core::iter::FusedIterator;
2
3use crate::error;
4use log::debug;
5use scroll::{Pread, Pwrite, SizeWith};
6
7use crate::pe::data_directories;
8use crate::pe::options;
9use crate::pe::section_table;
10use crate::pe::utils;
11
12pub const IMAGE_DEBUG_DIRECTORY_SIZE: usize = 0x1C;
14
15#[derive(Debug, Copy, Clone)]
17pub struct ImageDebugDirectoryIterator<'a> {
18 data: &'a [u8],
20 rva_offset: u32,
25}
26
27impl Iterator for ImageDebugDirectoryIterator<'_> {
28 type Item = error::Result<ImageDebugDirectory>;
29
30 fn next(&mut self) -> Option<Self::Item> {
31 if self.data.is_empty() {
32 return None;
33 }
34
35 Some(
36 match self.data.pread_with::<ImageDebugDirectory>(0, scroll::LE) {
37 Ok(func) => {
38 self.data = &self.data[IMAGE_DEBUG_DIRECTORY_SIZE..];
39
40 let idd = ImageDebugDirectory {
42 address_of_raw_data: func.address_of_raw_data.wrapping_sub(self.rva_offset),
43 pointer_to_raw_data: func.pointer_to_raw_data.wrapping_sub(self.rva_offset),
44 ..func
45 };
46
47 debug!(
48 "ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}",
49 idd.address_of_raw_data.wrapping_add(self.rva_offset),
50 idd.address_of_raw_data,
51 );
52
53 debug!(
54 "ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
55 idd.pointer_to_raw_data.wrapping_add(self.rva_offset),
56 idd.pointer_to_raw_data,
57 );
58
59 Ok(idd)
60 }
61 Err(error) => {
62 self.data = &[];
63 Err(error.into())
64 }
65 },
66 )
67 }
68
69 fn size_hint(&self) -> (usize, Option<usize>) {
70 let len = self.data.len() / IMAGE_DEBUG_DIRECTORY_SIZE;
71 (len, Some(len))
72 }
73}
74
75impl FusedIterator for ImageDebugDirectoryIterator<'_> {}
76impl ExactSizeIterator for ImageDebugDirectoryIterator<'_> {}
77
78impl<'a> ImageDebugDirectoryIterator<'a> {
79 pub fn find_type(&self, data_type: u32) -> Option<ImageDebugDirectory> {
81 self.filter_map(Result::ok)
82 .find(|idd| idd.data_type == data_type)
83 }
84}
85
86#[derive(Debug, PartialEq, Clone, Default)]
88pub struct DebugData<'a> {
89 data: &'a [u8],
91 rva_offset: u32,
96 pub codeview_pdb70_debug_info: Option<CodeviewPDB70DebugInfo<'a>>,
103 pub codeview_pdb20_debug_info: Option<CodeviewPDB20DebugInfo<'a>>,
110 pub vcfeature_info: Option<VCFeatureInfo>,
117 pub ex_dll_characteristics_info: Option<ExDllCharacteristicsInfo>,
124 pub repro_info: Option<ReproInfo<'a>>,
132 pub pogo_info: Option<POGOInfo<'a>>,
141}
142
143impl<'a> DebugData<'a> {
144 pub fn parse(
145 bytes: &'a [u8],
146 dd: data_directories::DataDirectory,
147 sections: &[section_table::SectionTable],
148 file_alignment: u32,
149 ) -> error::Result<Self> {
150 Self::parse_with_opts(
151 bytes,
152 dd,
153 sections,
154 file_alignment,
155 &options::ParseOptions::default(),
156 )
157 }
158
159 pub fn parse_with_opts(
160 bytes: &'a [u8],
161 dd: data_directories::DataDirectory,
162 sections: &[section_table::SectionTable],
163 file_alignment: u32,
164 opts: &options::ParseOptions,
165 ) -> error::Result<Self> {
166 Self::parse_with_opts_and_fixup(bytes, dd, sections, file_alignment, opts, 0)
167 }
168
169 pub fn parse_with_opts_and_fixup(
170 bytes: &'a [u8],
171 dd: data_directories::DataDirectory,
172 sections: &[section_table::SectionTable],
173 file_alignment: u32,
174 opts: &options::ParseOptions,
175 rva_offset: u32,
176 ) -> error::Result<Self> {
177 let offset =
178 utils::find_offset(dd.virtual_address as usize, sections, file_alignment, opts)
179 .ok_or_else(|| {
180 error::Error::Malformed(format!(
181 "Cannot map ImageDebugDirectory rva {:#x} into offset",
182 dd.virtual_address
183 ))
184 })?;
185
186 if offset + dd.size as usize > bytes.len() {
188 return Err(error::Error::Malformed(format!(
189 "ImageDebugDirectory offset {:#x} and size {:#x} exceeds the bounds of the bytes size {:#x}",
190 offset, dd.size, bytes.len()
191 )));
192 }
193 let data = &bytes[offset..offset + dd.size as usize];
194 let it = ImageDebugDirectoryIterator { data, rva_offset };
195
196 let mut codeview_pdb70_debug_info = None;
197 let mut codeview_pdb20_debug_info = None;
198 let mut vcfeature_info = None;
199 let mut ex_dll_characteristics_info = None;
200 let mut repro_info = None;
201 let mut pogo_info = None;
202
203 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_CODEVIEW) {
204 codeview_pdb70_debug_info = CodeviewPDB70DebugInfo::parse_with_opts(bytes, idd, opts)?;
205 codeview_pdb20_debug_info = CodeviewPDB20DebugInfo::parse_with_opts(bytes, idd, opts)?;
206 }
207 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_VC_FEATURE) {
208 vcfeature_info = Some(VCFeatureInfo::parse_with_opts(bytes, idd, opts)?);
209 }
210 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) {
211 ex_dll_characteristics_info =
212 Some(ExDllCharacteristicsInfo::parse_with_opts(bytes, idd, opts)?);
213 }
214 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_REPRO) {
215 repro_info = Some(ReproInfo::parse_with_opts(bytes, idd, opts)?);
216 }
217 if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_POGO) {
218 pogo_info = POGOInfo::parse_with_opts(bytes, idd, opts)?;
219 }
220
221 Ok(DebugData {
222 data,
223 rva_offset,
224 codeview_pdb70_debug_info,
225 codeview_pdb20_debug_info,
226 vcfeature_info,
227 ex_dll_characteristics_info,
228 repro_info,
229 pogo_info,
230 })
231 }
232
233 pub fn guid(&self) -> Option<[u8; 16]> {
235 self.codeview_pdb70_debug_info.map(|pdb70| pdb70.signature)
236 }
237
238 pub fn find_type(&self, data_type: u32) -> Option<ImageDebugDirectory> {
240 self.entries().find_type(data_type)
241 }
242
243 pub fn entries(&self) -> ImageDebugDirectoryIterator<'a> {
245 ImageDebugDirectoryIterator {
246 data: &self.data,
247 rva_offset: self.rva_offset,
248 }
249 }
250}
251
252#[repr(C)]
262#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
263pub struct ImageDebugDirectory {
264 pub characteristics: u32,
266 pub time_date_stamp: u32,
268 pub major_version: u16,
270 pub minor_version: u16,
272 pub data_type: u32,
274 pub size_of_data: u32,
276 pub address_of_raw_data: u32,
278 pub pointer_to_raw_data: u32,
280}
281
282pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0;
284pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1;
286pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2;
288pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3;
290pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4;
292pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5;
294pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
296pub const IMAGE_DEBUG_TYPE_OMAP_TO_SRC: u32 = 7;
298pub const IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: u32 = 8;
300pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;
302pub const IMAGE_DEBUG_TYPE_RESERVED10: u32 = 10;
304pub const IMAGE_DEBUG_TYPE_BBT: u32 = IMAGE_DEBUG_TYPE_RESERVED10;
306pub const IMAGE_DEBUG_TYPE_CLSID: u32 = 11;
308pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12;
310pub const IMAGE_DEBUG_TYPE_POGO: u32 = 13;
312pub const IMAGE_DEBUG_TYPE_ILTCG: u32 = 14;
314pub const IMAGE_DEBUG_TYPE_MPX: u32 = 15;
316pub const IMAGE_DEBUG_TYPE_REPRO: u32 = 16;
318pub const IMAGE_DEBUG_TYPE_EMBEDDEDPORTABLEPDB: u32 = 17;
320pub const IMAGE_DEBUG_TYPE_SPGO: u32 = 18;
322pub const IMAGE_DEBUG_TYPE_PDBCHECKSUM: u32 = 19;
324pub const IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS: u32 = 20;
326pub const IMAGE_DEBUG_TYPE_PERFMAP: u32 = 21;
328
329pub const CODEVIEW_PDB70_MAGIC: u32 = 0x5344_5352;
331pub const CODEVIEW_PDB20_MAGIC: u32 = 0x3031_424e;
333pub const CODEVIEW_CV50_MAGIC: u32 = 0x3131_424e;
335pub const CODEVIEW_CV41_MAGIC: u32 = 0x3930_424e;
337
338#[repr(C)]
340#[derive(Debug, PartialEq, Copy, Clone, Default)]
341pub struct CodeviewPDB70DebugInfo<'a> {
342 pub codeview_signature: u32,
343 pub signature: [u8; 16],
344 pub age: u32,
345 pub filename: &'a [u8],
346}
347
348impl<'a> CodeviewPDB70DebugInfo<'a> {
349 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
350 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
351 }
352
353 pub fn parse_with_opts(
354 bytes: &'a [u8],
355 idd: &ImageDebugDirectory,
356 opts: &options::ParseOptions,
357 ) -> error::Result<Option<Self>> {
358 let mut offset: usize = match opts.resolve_rva {
360 true => idd.pointer_to_raw_data as usize,
361 false => idd.address_of_raw_data as usize,
362 };
363
364 let filename_length = idd.size_of_data as isize - 24;
366 if filename_length < 0 {
367 return Err(error::Error::Malformed(format!(
369 "ImageDebugDirectory size of data seems wrong: {:?}",
370 idd.size_of_data
371 )));
372 }
373 let filename_length = filename_length as usize;
374
375 let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
377 if codeview_signature != CODEVIEW_PDB70_MAGIC {
378 return Ok(None);
379 }
380
381 let mut signature: [u8; 16] = [0; 16];
383 signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?);
384 let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
385 if let Some(filename) = bytes.get(offset..offset + filename_length) {
386 Ok(Some(CodeviewPDB70DebugInfo {
387 codeview_signature,
388 signature,
389 age,
390 filename,
391 }))
392 } else {
393 Err(error::Error::Malformed(format!(
394 "ImageDebugDirectory seems corrupted: {:?}",
395 idd
396 )))
397 }
398 }
399}
400
401#[repr(C)]
403#[derive(Debug, PartialEq, Copy, Clone, Default)]
404pub struct VCFeatureInfo {
405 pub pre_vc_plusplus_count: u32,
407 pub c_and_cplusplus_count: u32,
409 pub guard_stack_count: u32,
411 pub sdl_count: u32,
413 pub guard_count: u32,
415}
416
417impl<'a> VCFeatureInfo {
418 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
419 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
420 }
421
422 pub fn parse_with_opts(
423 bytes: &'a [u8],
424 idd: &ImageDebugDirectory,
425 opts: &options::ParseOptions,
426 ) -> error::Result<Self> {
427 let mut offset: usize = match opts.resolve_rva {
428 true => idd.pointer_to_raw_data as usize,
429 false => idd.address_of_raw_data as usize,
430 };
431
432 let pre_vc_plusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
433 let c_and_cplusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
434 let guard_stack_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
435 let sdl_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
436 let guard_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
437
438 Ok(VCFeatureInfo {
439 pre_vc_plusplus_count,
440 c_and_cplusplus_count,
441 guard_stack_count,
442 sdl_count,
443 guard_count,
444 })
445 }
446}
447
448#[repr(C)]
450#[derive(Debug, PartialEq, Copy, Clone, Default)]
451pub struct CodeviewPDB20DebugInfo<'a> {
452 pub codeview_signature: u32,
453 pub codeview_offset: u32,
454 pub signature: u32,
455 pub age: u32,
456 pub filename: &'a [u8],
457}
458
459impl<'a> CodeviewPDB20DebugInfo<'a> {
460 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
461 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
462 }
463
464 pub fn parse_with_opts(
465 bytes: &'a [u8],
466 idd: &ImageDebugDirectory,
467 opts: &options::ParseOptions,
468 ) -> error::Result<Option<Self>> {
469 let mut offset: usize = match opts.resolve_rva {
471 true => idd.pointer_to_raw_data as usize,
472 false => idd.address_of_raw_data as usize,
473 };
474
475 let filename_length = idd.size_of_data as isize - 16;
477 if filename_length < 0 {
478 return Err(error::Error::Malformed(format!(
480 "ImageDebugDirectory size of data seems wrong: {:?}",
481 idd.size_of_data
482 )));
483 }
484 let filename_length = filename_length as usize;
485
486 let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
488 if codeview_signature != CODEVIEW_PDB20_MAGIC {
489 return Ok(None);
490 }
491 let codeview_offset: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
492
493 let signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
495 let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
496 if let Some(filename) = bytes.get(offset..offset + filename_length) {
497 Ok(Some(CodeviewPDB20DebugInfo {
498 codeview_signature,
499 codeview_offset,
500 signature,
501 age,
502 filename,
503 }))
504 } else {
505 Err(error::Error::Malformed(format!(
506 "ImageDebugDirectory seems corrupted: {:?}",
507 idd
508 )))
509 }
510 }
511}
512
513#[derive(Debug, PartialEq, Copy, Clone)]
520pub enum ReproInfo<'a> {
521 TimeDateStamp(u32),
526 Buffer {
531 length: u32,
533 buffer: &'a [u8],
535 },
536}
537
538impl<'a> ReproInfo<'a> {
539 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
540 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
541 }
542
543 pub fn parse_with_opts(
544 bytes: &'a [u8],
545 idd: &ImageDebugDirectory,
546 opts: &options::ParseOptions,
547 ) -> error::Result<Self> {
548 let mut offset: usize = match opts.resolve_rva {
549 true => idd.pointer_to_raw_data as usize,
550 false => idd.address_of_raw_data as usize,
551 };
552
553 if idd.size_of_data > 0 {
556 let length: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
557 if let Some(buffer) = bytes.get(offset..offset + length as usize) {
558 Ok(Self::Buffer { length, buffer })
559 } else {
560 Err(error::Error::Malformed(format!(
561 "ImageDebugDirectory seems corrupted: {:?}",
562 idd
563 )))
564 }
565 } else {
566 Ok(Self::TimeDateStamp(idd.time_date_stamp))
567 }
568 }
569}
570
571#[repr(C)]
579#[derive(Debug, PartialEq, Copy, Clone, Default)]
580pub struct ExDllCharacteristicsInfo {
581 pub characteristics_ex: u32,
597}
598
599pub const IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT: u32 = 0x1;
602pub const IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE: u32 = 0x2;
605pub const IMAGE_DLLCHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE: u32 = 0x4;
608pub const IMAGE_DLLCHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY: u32 = 0x8;
611pub const IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_1: u32 = 0x10;
613pub const IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_2: u32 = 0x20;
615pub const IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT: u32 = 0x40;
621pub const IMAGE_DLLCHARACTERISTICS_EX_HOTPATCH_COMPATIBLE: u32 = 0x80;
628
629impl<'a> ExDllCharacteristicsInfo {
630 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
631 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
632 }
633
634 pub fn parse_with_opts(
635 bytes: &'a [u8],
636 idd: &ImageDebugDirectory,
637 opts: &options::ParseOptions,
638 ) -> error::Result<Self> {
639 let mut offset: usize = match opts.resolve_rva {
641 true => idd.pointer_to_raw_data as usize,
642 false => idd.address_of_raw_data as usize,
643 };
644
645 let characteristics_ex: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
646
647 Ok(ExDllCharacteristicsInfo { characteristics_ex })
648 }
649}
650
651#[repr(C)]
661#[derive(Debug, PartialEq, Copy, Clone, Default)]
662pub struct POGOInfo<'a> {
663 pub signature: u32,
670 pub data: &'a [u8],
672}
673
674#[derive(Debug, PartialEq, Copy, Clone, Default)]
676pub struct POGOInfoEntry<'a> {
677 pub rva: u32,
679 pub size: u32,
681 pub name: &'a [u8],
686}
687
688#[derive(Debug, Copy, Clone)]
690pub struct POGOEntryIterator<'a> {
691 data: &'a [u8],
693}
694
695pub const IMAGE_DEBUG_POGO_SIGNATURE_LTCG: u32 = 0x4C544347;
700pub const IMAGE_DEBUG_POGO_SIGNATURE_PGU: u32 = 0x50475500;
705pub const POGO_SIGNATURE_SIZE: usize = core::mem::size_of::<u32>();
707
708impl<'a> POGOInfo<'a> {
709 pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
710 Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
711 }
712
713 pub fn parse_with_opts(
714 bytes: &'a [u8],
715 idd: &ImageDebugDirectory,
716 opts: &options::ParseOptions,
717 ) -> error::Result<Option<Self>> {
718 let mut offset: usize = match opts.resolve_rva {
720 true => idd.pointer_to_raw_data as usize,
721 false => idd.address_of_raw_data as usize,
722 };
723
724 let signature = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
725 if signature != IMAGE_DEBUG_POGO_SIGNATURE_LTCG
726 && signature != IMAGE_DEBUG_POGO_SIGNATURE_PGU
727 {
728 return Ok(None);
730 }
731
732 if offset + idd.size_of_data as usize - POGO_SIGNATURE_SIZE > bytes.len() {
733 return Err(error::Error::Malformed(format!(
734 "ImageDebugDirectory offset {:#x} and size {:#x} exceeds the bounds of the bytes size {:#x}",
735 offset, idd.size_of_data, bytes.len()
736 )));
737 }
738 let data = &bytes[offset..offset + idd.size_of_data as usize - POGO_SIGNATURE_SIZE];
739 Ok(Some(POGOInfo { signature, data }))
740 }
741
742 pub fn entries(&self) -> POGOEntryIterator<'a> {
744 POGOEntryIterator { data: &self.data }
745 }
746}
747
748impl<'a> Iterator for POGOEntryIterator<'a> {
749 type Item = error::Result<POGOInfoEntry<'a>>;
750
751 fn next(&mut self) -> Option<Self::Item> {
752 if self.data.is_empty() {
753 return None;
754 }
755
756 let mut offset = 0;
757 let rva = match self.data.gread_with::<u32>(&mut offset, scroll::LE) {
758 Ok(rva) => rva,
759 Err(error) => return Some(Err(error.into())),
760 };
761 let size = match self.data.gread_with::<u32>(&mut offset, scroll::LE) {
762 Ok(size) => size,
763 Err(error) => return Some(Err(error.into())),
764 };
765
766 if offset >= self.data.len() {
767 return Some(Err(error::Error::Malformed(format!(
768 "Offset {:#x} is too big for containing name field of POGO entry (rva {:#x} and size {:#X})",
769 offset,rva, size
770 ))));
771 }
772 let name = match self.data[offset..].iter().position(|&b| b == 0) {
773 Some(pos) => {
774 if offset + pos as usize >= self.data.len() {
775 return Some(Err(error::Error::Malformed(format!(
776 "Null-terminator for POGO entry (rva {:#x} and size {:#X}) found but exceeds iterator buffer",
777 rva, size
778 ))));
779 }
780 let name = &self.data[offset..offset + pos + 1];
781 offset = offset + pos + 1;
782 offset = (offset + 3) & !3; name
785 }
786 None => {
787 return Some(Err(error::Error::Malformed(format!(
788 "Cannot find null-terimnator for POGO entry (rva {:#x} and size {:#X})",
789 rva, size
790 ))
791 .into()));
792 }
793 };
794
795 self.data = &self.data[offset..];
796 Some(Ok(POGOInfoEntry { rva, size, name }))
797 }
798}
799
800impl FusedIterator for POGOEntryIterator<'_> {}
801
802#[cfg(test)]
803mod tests {
804 use super::{
805 ExDllCharacteristicsInfo, ImageDebugDirectory, POGOInfoEntry, ReproInfo, VCFeatureInfo,
806 CODEVIEW_PDB70_MAGIC, IMAGE_DEBUG_POGO_SIGNATURE_LTCG, IMAGE_DEBUG_TYPE_CODEVIEW,
807 IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, IMAGE_DEBUG_TYPE_ILTCG, IMAGE_DEBUG_TYPE_POGO,
808 IMAGE_DEBUG_TYPE_REPRO, IMAGE_DEBUG_TYPE_VC_FEATURE,
809 IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT, IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE,
810 POGO_SIGNATURE_SIZE,
811 };
812
813 const NO_DEBUG_DIRECTORIES_BIN: &[u8] =
814 include_bytes!("../../tests/bins/pe/no_debug_directories.exe.bin");
815 const DEBUG_DIRECTORIES_TEST_MSVC_BIN: &[u8] =
816 include_bytes!("../../tests/bins/pe/debug_directories-msvc.exe.bin");
817 const DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN: &[u8] =
818 include_bytes!("../../tests/bins/pe/debug_directories-clang_lld.exe.bin");
819
820 fn ffi_to_string(bytes: &[u8]) -> String {
821 unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
822 .to_string_lossy()
823 .to_string()
824 }
825
826 #[test]
827 fn parse_no_debug_directories() {
828 let binary =
829 crate::pe::PE::parse(NO_DEBUG_DIRECTORIES_BIN).expect("Unable to parse binary");
830 assert_eq!(binary.debug_data.is_none(), true);
831 }
832
833 #[test]
834 fn parse_debug_entries_iterator() {
835 let binary =
836 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
837 assert_eq!(binary.debug_data.is_some(), true);
838 let debug_data = binary.debug_data.unwrap();
839 let entries = debug_data.entries().collect::<Result<Vec<_>, _>>();
840 assert_eq!(entries.is_ok(), true);
841 let entries = entries.unwrap();
842 let entries_expect = vec![
843 ImageDebugDirectory {
844 characteristics: 0x0,
845 time_date_stamp: 0x80AC7661,
846 major_version: 0x0,
847 minor_version: 0x0,
848 data_type: IMAGE_DEBUG_TYPE_CODEVIEW,
849 size_of_data: 0x38,
850 address_of_raw_data: 0x20c0,
851 pointer_to_raw_data: 0x4c0,
852 },
853 ImageDebugDirectory {
854 characteristics: 0x0,
855 time_date_stamp: 0x80AC7661,
856 major_version: 0x0,
857 minor_version: 0x0,
858 data_type: IMAGE_DEBUG_TYPE_VC_FEATURE,
859 size_of_data: 0x14,
860 address_of_raw_data: 0x20f8,
861 pointer_to_raw_data: 0x4f8,
862 },
863 ImageDebugDirectory {
864 characteristics: 0x0,
865 time_date_stamp: 0x80AC7661,
866 major_version: 0x0,
867 minor_version: 0x0,
868 data_type: IMAGE_DEBUG_TYPE_POGO,
869 size_of_data: 0x58,
870 address_of_raw_data: 0x210c,
871 pointer_to_raw_data: 0x50c,
872 },
873 ImageDebugDirectory {
874 characteristics: 0x0,
875 time_date_stamp: 0x80AC7661,
876 major_version: 0x0,
877 minor_version: 0x0,
878 data_type: IMAGE_DEBUG_TYPE_ILTCG,
879 size_of_data: 0x0,
880 address_of_raw_data: 0x0,
881 pointer_to_raw_data: 0x0,
882 },
883 ImageDebugDirectory {
884 characteristics: 0x0,
885 time_date_stamp: 0x80AC7661,
886 major_version: 0x0,
887 minor_version: 0x0,
888 data_type: IMAGE_DEBUG_TYPE_REPRO,
889 size_of_data: 0x24,
890 address_of_raw_data: 0x2164,
891 pointer_to_raw_data: 0x564,
892 },
893 ImageDebugDirectory {
894 characteristics: 0x0,
895 time_date_stamp: 0x80AC7661,
896 major_version: 0x0,
897 minor_version: 0x0,
898 data_type: IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,
899 size_of_data: 0x4,
900 address_of_raw_data: 0x2188,
901 pointer_to_raw_data: 0x588,
902 },
903 ];
904 assert_eq!(entries, entries_expect);
905 }
906
907 #[test]
908 fn parse_debug_codeview_pdb70_msvc() {
909 let binary =
910 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
911 assert_eq!(binary.debug_data.is_some(), true);
912 let debug_data = binary.debug_data.unwrap();
913 assert_eq!(debug_data.codeview_pdb70_debug_info.is_some(), true);
914 let codeview_pdb70_debug_info = debug_data.codeview_pdb70_debug_info.unwrap();
915 let filename = ffi_to_string(codeview_pdb70_debug_info.filename);
916 assert_eq!(filename, String::from("THIS-IS-BINARY-FOR-GOBLIN-TESTS"));
917 assert_eq!(codeview_pdb70_debug_info.age, 3);
918 assert_eq!(
919 codeview_pdb70_debug_info.codeview_signature,
920 CODEVIEW_PDB70_MAGIC
921 );
922 assert_eq!(
923 codeview_pdb70_debug_info.signature,
924 [
925 0x1F, 0x4F, 0x58, 0x9C, 0x3C, 0xEA, 0x00, 0x83, 0x3F, 0x57, 0x00, 0xCC, 0x36, 0xA7,
926 0x84, 0xDF,
927 ]
928 );
929 }
930
931 #[test]
932 fn parse_debug_codeview_pdb70_clang() {
933 let binary = crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN)
934 .expect("Unable to parse binary");
935 assert_eq!(binary.debug_data.is_some(), true);
936 let debug_data = binary.debug_data.unwrap();
937 assert_eq!(debug_data.codeview_pdb70_debug_info.is_some(), true);
938 let codeview_pdb70_debug_info = debug_data.codeview_pdb70_debug_info.unwrap();
939 let filename = ffi_to_string(codeview_pdb70_debug_info.filename);
940 assert_eq!(filename, String::from("THIS-IS-BINARY-FOR-GOBLIN-TESTS"));
941 assert_eq!(codeview_pdb70_debug_info.age, 1);
942 assert_eq!(
943 codeview_pdb70_debug_info.codeview_signature,
944 CODEVIEW_PDB70_MAGIC
945 );
946 assert_eq!(
947 codeview_pdb70_debug_info.signature,
948 [
949 0xC8, 0xBA, 0xF6, 0xAB, 0xB2, 0x98, 0xD1, 0x9E, 0x4C, 0x4C, 0x44, 0x20, 0x50, 0x44,
950 0x42, 0x2E,
951 ]
952 );
953 }
954
955 #[test]
956 fn parse_debug_vcfeature() {
957 let binary =
958 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
959 assert_eq!(binary.debug_data.is_some(), true);
960 let debug_data = binary.debug_data.unwrap();
961 assert_eq!(debug_data.vcfeature_info.is_some(), true);
962 let vcfeature_info = debug_data.vcfeature_info.unwrap();
963 let vcfeature_info_expect = VCFeatureInfo {
964 pre_vc_plusplus_count: 0,
965 c_and_cplusplus_count: 1,
966 guard_stack_count: 0,
967 sdl_count: 0,
968 guard_count: 0,
969 };
970 assert_eq!(vcfeature_info, vcfeature_info_expect);
971 }
972
973 #[test]
974 fn parse_debug_repro_msvc() {
975 let binary =
976 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
977 assert_eq!(binary.debug_data.is_some(), true);
978 let debug_data = binary.debug_data.unwrap();
979 assert_eq!(debug_data.repro_info.is_some(), true);
980 let repro_info = debug_data.repro_info.unwrap();
981 let repro_info_expect = ReproInfo::Buffer {
982 length: 32,
983 buffer: &[
984 0x1F, 0x4F, 0x58, 0x9C, 0x3C, 0xEA, 0x00, 0x83, 0x3F, 0x57, 0x00, 0xCC, 0x36, 0xA7,
985 0x84, 0xDF, 0xF7, 0x7C, 0x70, 0xE0, 0xEF, 0x7A, 0xBA, 0x08, 0xD0, 0xA6, 0x8B, 0x7F,
986 0x61, 0x76, 0xAC, 0x80,
987 ],
988 };
989 assert_eq!(repro_info, repro_info_expect);
990 }
991
992 #[test]
993 fn parse_debug_repro_clang_lld() {
994 let binary = crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN)
995 .expect("Unable to parse binary");
996 assert_eq!(binary.debug_data.is_some(), true);
997 let debug_data = binary.debug_data.unwrap();
998 assert_eq!(debug_data.repro_info.is_some(), true);
999 let repro_info = debug_data.repro_info.unwrap();
1000 let repro_info_expect = ReproInfo::TimeDateStamp(0xDB2F3908);
1001 assert_eq!(repro_info, repro_info_expect);
1002 }
1003
1004 #[test]
1005 fn parse_debug_exdllcharacteristics() {
1006 let binary =
1007 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1008 assert_eq!(binary.debug_data.is_some(), true);
1009 let debug_data = binary.debug_data.unwrap();
1010 assert_eq!(debug_data.ex_dll_characteristics_info.is_some(), true);
1011 let ex_dll_characteristics_info = debug_data.ex_dll_characteristics_info.unwrap();
1012 let ex_dll_characteristics_info_expect = ExDllCharacteristicsInfo {
1013 characteristics_ex: IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT
1014 | IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE,
1015 };
1016 assert_eq!(
1017 ex_dll_characteristics_info,
1018 ex_dll_characteristics_info_expect
1019 );
1020 }
1021
1022 #[test]
1023 fn parse_debug_pogo() {
1024 let binary =
1025 crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1026 assert_eq!(binary.debug_data.is_some(), true);
1027 let debug_data = binary.debug_data.unwrap();
1028 assert_eq!(debug_data.pogo_info.is_some(), true);
1029 let pogo_info = debug_data.pogo_info.unwrap();
1030 assert_eq!(pogo_info.signature, IMAGE_DEBUG_POGO_SIGNATURE_LTCG);
1031 assert_eq!(pogo_info.data.len(), 88 - POGO_SIGNATURE_SIZE);
1032 let entries = pogo_info.entries().collect::<Result<Vec<_>, _>>().unwrap();
1033 let entries_expect = vec![
1034 POGOInfoEntry {
1035 rva: 0x1000,
1036 size: 0x3,
1037 name: b".text$mn\0",
1038 },
1039 POGOInfoEntry {
1040 rva: 0x2000,
1041 size: 0xA8,
1042 name: b".rdata\0",
1043 },
1044 POGOInfoEntry {
1045 rva: 0x20A8,
1046 size: 0x18,
1047 name: b".rdata$voltmd\0",
1048 },
1049 POGOInfoEntry {
1050 rva: 0x20C0,
1051 size: 0xCC,
1052 name: b".rdata$zzzdbg\0",
1053 },
1054 ];
1055 assert_eq!(entries, entries_expect);
1056 }
1057}