Coverage Report

Created: 2026-11-11 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/goblin-0.10.7/src/pe/debug.rs
Line
Count
Source
1
use core::iter::FusedIterator;
2
3
use crate::error;
4
use crate::options::Permissive;
5
use log::debug;
6
use scroll::{Pread, Pwrite, SizeWith};
7
8
use crate::pe::data_directories;
9
use crate::pe::options;
10
use crate::pe::section_table;
11
use crate::pe::utils;
12
13
/// Size of [`ImageDebugDirectory`]
14
pub const IMAGE_DEBUG_DIRECTORY_SIZE: usize = 0x1C;
15
16
/// Iterator over debug directory entries in [`DebugData`].
17
#[derive(Debug, Copy, Clone)]
18
pub struct ImageDebugDirectoryIterator<'a> {
19
    /// Raw data reference that scoped to the next element if appropriate
20
    data: &'a [u8],
21
    /// Fixup RVA offset used for TE fixups
22
    ///
23
    /// - **When zero**: no fixup is performed
24
    /// - **When non-zero**: fixup is performed
25
    rva_offset: u32,
26
}
27
28
impl Iterator for ImageDebugDirectoryIterator<'_> {
29
    type Item = error::Result<ImageDebugDirectory>;
30
31
1.67M
    fn next(&mut self) -> Option<Self::Item> {
32
1.67M
        if self.data.is_empty() {
33
3.34k
            return None;
34
1.66M
        }
35
36
        Some(
37
1.66M
            match self.data.pread_with::<ImageDebugDirectory>(0, scroll::LE) {
38
1.66M
                Ok(func) => {
39
1.66M
                    self.data = &self.data[IMAGE_DEBUG_DIRECTORY_SIZE..];
40
41
                    // Adjust all addresses in the TE binary debug data if fixup is specified
42
1.66M
                    let idd = ImageDebugDirectory {
43
1.66M
                        address_of_raw_data: func.address_of_raw_data.wrapping_sub(self.rva_offset),
44
1.66M
                        pointer_to_raw_data: func.pointer_to_raw_data.wrapping_sub(self.rva_offset),
45
1.66M
                        ..func
46
1.66M
                    };
47
48
1.66M
                    debug!(
49
0
                        "ImageDebugDirectory address of raw data fixed up from: 0x{:X} to 0x{:X}",
50
0
                        idd.address_of_raw_data.wrapping_add(self.rva_offset),
51
                        idd.address_of_raw_data,
52
                    );
53
54
1.66M
                    debug!(
55
0
                        "ImageDebugDirectory pointer to raw data fixed up from: 0x{:X} to 0x{:X}",
56
0
                        idd.pointer_to_raw_data.wrapping_add(self.rva_offset),
57
                        idd.pointer_to_raw_data,
58
                    );
59
60
1.66M
                    Ok(idd)
61
                }
62
1.63k
                Err(error) => {
63
1.63k
                    self.data = &[];
64
1.63k
                    Err(error.into())
65
                }
66
            },
67
        )
68
1.67M
    }
69
70
0
    fn size_hint(&self) -> (usize, Option<usize>) {
71
0
        let len = self.data.len() / IMAGE_DEBUG_DIRECTORY_SIZE;
72
0
        (len, Some(len))
73
0
    }
74
}
75
76
impl FusedIterator for ImageDebugDirectoryIterator<'_> {}
77
impl ExactSizeIterator for ImageDebugDirectoryIterator<'_> {}
78
79
impl<'a> ImageDebugDirectoryIterator<'a> {
80
    /// Find a specific debug type in the debug data.
81
3.99k
    pub fn find_type(&self, data_type: u32) -> Option<ImageDebugDirectory> {
82
3.99k
        self.filter_map(Result::ok)
83
1.66M
            .find(|idd| idd.data_type == data_type)
84
3.99k
    }
85
}
86
87
/// Represents debug data extracted from a PE (Portable Executable) or TE (Terse Executable) file.
88
#[derive(Debug, PartialEq, Clone, Default)]
89
pub struct DebugData<'a> {
90
    /// Raw data covering bytes of entire [`ImageDebugDirectory`]
91
    data: &'a [u8],
92
    /// Fixup RVA offset used for TE fixups
93
    ///
94
    /// - **When zero**: no fixup is performed
95
    /// - **When non-zero**: fixup is performed
96
    rva_offset: u32,
97
    /// Parsed CodeView PDB 7.0 (RSDS) debug information, if available.
98
    ///
99
    /// CodeView PDB 7.0 contains a GUID, an age value, and the path to the PDB file.
100
    /// This is commonly used in modern PDB files.
101
    ///
102
    /// [`IMAGE_DEBUG_TYPE_CODEVIEW`]
103
    pub codeview_pdb70_debug_info: Option<CodeviewPDB70DebugInfo<'a>>,
104
    /// Parsed CodeView PDB 2.0 (NB10) debug information, if available.
105
    ///
106
    /// CodeView PDB 2.0 includes a signature, an age value, and the path to the PDB file.
107
    /// It is used in older PDB formats.
108
    ///
109
    /// [`IMAGE_DEBUG_TYPE_CODEVIEW`]
110
    pub codeview_pdb20_debug_info: Option<CodeviewPDB20DebugInfo<'a>>,
111
    /// Visual C++ feature data, if available.
112
    ///
113
    /// This includes information about specific features or optimizations enabled
114
    /// in Visual C++ builds.
115
    ///
116
    /// [`IMAGE_DEBUG_TYPE_VC_FEATURE`]
117
    pub vcfeature_info: Option<VCFeatureInfo>,
118
    /// Extended DLL characteristics information, if available.
119
    ///
120
    /// This data includes extended properties of the DLL that may affect
121
    /// how the operating system handles the DLL, such as security features.
122
    ///
123
    /// [`IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS`]
124
    pub ex_dll_characteristics_info: Option<ExDllCharacteristicsInfo>,
125
    /// Reproducible build (Repro) information, if available.
126
    ///
127
    /// - **MSVC builds**: Contains a 32-byte hash stored directly in the raw data.
128
    /// - **Clang builds**: Uses the [`ImageDebugDirectory::time_date_stamp`] as a hash,
129
    ///   with no dedicated raw data.
130
    ///
131
    /// [`IMAGE_DEBUG_TYPE_REPRO`]
132
    pub repro_info: Option<ReproInfo<'a>>,
133
    /// Profile-guided optimization (POGO aka PGO) data, if available.
134
    ///
135
    /// This data provides information relevant to Profile-Guided Optimization
136
    /// (POGO) processes, including function and data block optimizations.
137
    ///
138
    /// [`IMAGE_DEBUG_TYPE_POGO`]
139
    ///
140
    /// Reference: <https://devblogs.microsoft.com/cppblog/pogo>
141
    pub pogo_info: Option<POGOInfo<'a>>,
142
}
143
144
impl<'a> DebugData<'a> {
145
0
    pub fn parse(
146
0
        bytes: &'a [u8],
147
0
        dd: data_directories::DataDirectory,
148
0
        sections: &[section_table::SectionTable],
149
0
        file_alignment: u32,
150
0
    ) -> error::Result<Self> {
151
0
        Self::parse_with_opts(
152
0
            bytes,
153
0
            dd,
154
0
            sections,
155
0
            file_alignment,
156
0
            &options::ParseOptions::default(),
157
        )
158
0
    }
159
160
1.06k
    pub fn parse_with_opts(
161
1.06k
        bytes: &'a [u8],
162
1.06k
        dd: data_directories::DataDirectory,
163
1.06k
        sections: &[section_table::SectionTable],
164
1.06k
        file_alignment: u32,
165
1.06k
        opts: &options::ParseOptions,
166
1.06k
    ) -> error::Result<Self> {
167
1.06k
        Self::parse_with_opts_and_fixup(bytes, dd, sections, file_alignment, opts, 0)
168
1.06k
    }
169
170
1.06k
    pub fn parse_with_opts_and_fixup(
171
1.06k
        bytes: &'a [u8],
172
1.06k
        dd: data_directories::DataDirectory,
173
1.06k
        sections: &[section_table::SectionTable],
174
1.06k
        file_alignment: u32,
175
1.06k
        opts: &options::ParseOptions,
176
1.06k
        rva_offset: u32,
177
1.06k
    ) -> error::Result<Self> {
178
871
        let offset =
179
1.06k
            match utils::find_offset(dd.virtual_address as usize, sections, file_alignment, opts) {
180
871
                Some(offset) => offset,
181
                None => {
182
194
                    return Err(error::Error::Malformed(format!(
183
194
                        "Cannot map ImageDebugDirectory rva {:#x} into offset",
184
194
                        dd.virtual_address
185
194
                    )))?;
186
                }
187
            };
188
189
        // Ensure that the offset and size do not exceed the length of the bytes slice
190
871
        let available_size = if offset + dd.size as usize > bytes.len() {
191
664
            let remaining_bytes = bytes.len().saturating_sub(offset);
192
664
            Err(error::Error::Malformed(format!(
193
664
                "ImageDebugDirectory offset {:#x} and size {:#x} exceeds the bounds of the bytes size {:#x}. \
194
664
                This may indicate a packed binary.",
195
664
                offset,
196
664
                dd.size,
197
664
                bytes.len()
198
664
            )))
199
664
            .or_permissive_and_value(
200
664
                opts.parse_mode.is_permissive(),
201
664
                "ImageDebugDirectory exceeds bounds; truncating",
202
664
                remaining_bytes,
203
0
            )?
204
        } else {
205
207
            dd.size as usize
206
        };
207
208
871
        let data = if available_size > 0 {
209
629
            bytes.pread_with::<&[u8]>(offset, available_size)?
210
        } else {
211
242
            &[]
212
        };
213
871
        let it = ImageDebugDirectoryIterator { data, rva_offset };
214
215
871
        let mut codeview_pdb70_debug_info = None;
216
871
        let mut codeview_pdb20_debug_info = None;
217
871
        let mut vcfeature_info = None;
218
871
        let mut ex_dll_characteristics_info = None;
219
871
        let mut repro_info = None;
220
871
        let mut pogo_info = None;
221
222
871
        if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_CODEVIEW) {
223
224
            codeview_pdb70_debug_info = CodeviewPDB70DebugInfo::parse_with_opts(bytes, idd, opts)?;
224
180
            codeview_pdb20_debug_info = CodeviewPDB20DebugInfo::parse_with_opts(bytes, idd, opts)?;
225
647
        }
226
825
        if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_VC_FEATURE) {
227
100
            vcfeature_info = Some(VCFeatureInfo::parse_with_opts(bytes, idd, opts)?);
228
725
        }
229
789
        if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS) {
230
            ex_dll_characteristics_info =
231
38
                Some(ExDllCharacteristicsInfo::parse_with_opts(bytes, idd, opts)?);
232
751
        }
233
777
        if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_REPRO) {
234
196
            repro_info = Some(ReproInfo::parse_with_opts(bytes, idd, opts)?);
235
581
        }
236
697
        if let Some(idd) = &it.find_type(IMAGE_DEBUG_TYPE_POGO) {
237
55
            pogo_info = POGOInfo::parse_with_opts(bytes, idd, opts)?;
238
642
        }
239
240
693
        Ok(DebugData {
241
693
            data,
242
693
            rva_offset,
243
693
            codeview_pdb70_debug_info,
244
693
            codeview_pdb20_debug_info,
245
693
            vcfeature_info,
246
693
            ex_dll_characteristics_info,
247
693
            repro_info,
248
693
            pogo_info,
249
693
        })
250
1.06k
    }
251
252
    /// Return this executable's debugging GUID, suitable for matching against a PDB file.
253
0
    pub fn guid(&self) -> Option<[u8; 16]> {
254
0
        self.codeview_pdb70_debug_info.map(|pdb70| pdb70.signature)
255
0
    }
256
257
    /// Find a specific debug type in the debug data.
258
32
    pub fn find_type(&self, data_type: u32) -> Option<ImageDebugDirectory> {
259
32
        self.entries().find_type(data_type)
260
32
    }
261
262
    /// Returns iterator for [`ImageDebugDirectory`]
263
32
    pub fn entries(&self) -> ImageDebugDirectoryIterator<'a> {
264
32
        ImageDebugDirectoryIterator {
265
32
            data: &self.data,
266
32
            rva_offset: self.rva_offset,
267
32
        }
268
32
    }
269
}
270
271
/// Represents the IMAGE_DEBUG_DIRECTORY structure in a Portable Executable (PE) file.
272
///
273
/// This structure holds information about the debug data in a PE file. It is used
274
/// to locate debug information such as PDB files or other types of debugging data.
275
/// The fields correspond to the Windows-specific IMAGE_DEBUG_DIRECTORY structure.
276
///
277
/// For more details, see the [Microsoft documentation](https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#image-debug_directory).
278
///
279
/// <https://msdn.microsoft.com/en-us/library/windows/desktop/ms680307(v=vs.85).aspx>
280
#[repr(C)]
281
#[derive(Debug, PartialEq, Copy, Clone, Default, Pread, Pwrite, SizeWith)]
282
pub struct ImageDebugDirectory {
283
    /// The characteristics of the debug data, reserved for future use.
284
    pub characteristics: u32,
285
    /// The time and date when the debug data was created, represented as a Unix timestamp.
286
    pub time_date_stamp: u32,
287
    /// The major version number of the debug data format.
288
    pub major_version: u16,
289
    /// The minor version number of the debug data format.
290
    pub minor_version: u16,
291
    /// The type of debug data, such as codeview or coff.
292
    pub data_type: u32,
293
    /// The size of the debug data in bytes.
294
    pub size_of_data: u32,
295
    /// The address of the debug data when loaded into memory.
296
    pub address_of_raw_data: u32,
297
    /// The file pointer to the debug data within the PE file.
298
    pub pointer_to_raw_data: u32,
299
}
300
301
/// Represents an unknown debug data type.
302
pub const IMAGE_DEBUG_TYPE_UNKNOWN: u32 = 0;
303
/// Represents COFF (Common Object File Format) debug information.
304
pub const IMAGE_DEBUG_TYPE_COFF: u32 = 1;
305
/// Represents CodeView debug information, often used for PDB (Program Database) files.
306
pub const IMAGE_DEBUG_TYPE_CODEVIEW: u32 = 2;
307
/// Represents FPO (Frame Pointer Omission) information.
308
pub const IMAGE_DEBUG_TYPE_FPO: u32 = 3;
309
/// Represents miscellaneous debug information.
310
pub const IMAGE_DEBUG_TYPE_MISC: u32 = 4;
311
/// Represents exception handling information.
312
pub const IMAGE_DEBUG_TYPE_EXCEPTION: u32 = 5;
313
/// Represents fixup information, used for relocation.
314
pub const IMAGE_DEBUG_TYPE_FIXUP: u32 = 6;
315
/// Represents OMAP (Optimized Map) information from source to compiled addresses.
316
pub const IMAGE_DEBUG_TYPE_OMAP_TO_SRC: u32 = 7;
317
/// Represents OMAP information from compiled addresses to source.
318
pub const IMAGE_DEBUG_TYPE_OMAP_FROM_SRC: u32 = 8;
319
/// Represents Borland-specific debug information.
320
pub const IMAGE_DEBUG_TYPE_BORLAND: u32 = 9;
321
/// Reserved debug data type (value 10).
322
pub const IMAGE_DEBUG_TYPE_RESERVED10: u32 = 10;
323
/// Represents BBT (Basic Block Transfer) information, an alias for reserved type 10.
324
pub const IMAGE_DEBUG_TYPE_BBT: u32 = IMAGE_DEBUG_TYPE_RESERVED10;
325
/// Represents a CLSID (Class ID) associated with the debug data.
326
pub const IMAGE_DEBUG_TYPE_CLSID: u32 = 11;
327
/// Represents Visual C++ feature data.
328
pub const IMAGE_DEBUG_TYPE_VC_FEATURE: u32 = 12;
329
/// Represents POGO (Profile Guided Optimization) information.
330
pub const IMAGE_DEBUG_TYPE_POGO: u32 = 13;
331
/// Represents ILTCG (Incremental Link Time Code Generation) optimization data.
332
pub const IMAGE_DEBUG_TYPE_ILTCG: u32 = 14;
333
/// Represents MPX (Memory Protection Extensions) related debug information.
334
pub const IMAGE_DEBUG_TYPE_MPX: u32 = 15;
335
/// Represents repro information, typically used for reproducible builds.
336
pub const IMAGE_DEBUG_TYPE_REPRO: u32 = 16;
337
/// Represents an embedded Portable PDB, a .NET-specific debug information format.
338
pub const IMAGE_DEBUG_TYPE_EMBEDDEDPORTABLEPDB: u32 = 17;
339
/// Represents SPGO (Static Profile Guided Optimization) information.
340
pub const IMAGE_DEBUG_TYPE_SPGO: u32 = 18;
341
/// Represents a checksum for the PDB file.
342
pub const IMAGE_DEBUG_TYPE_PDBCHECKSUM: u32 = 19;
343
/// Represents extended DLL characteristics for debugging.
344
pub const IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS: u32 = 20;
345
/// Represents a performance map for profiling.
346
pub const IMAGE_DEBUG_TYPE_PERFMAP: u32 = 21;
347
348
/// Magic number for CodeView PDB 7.0 signature (`'SDSR'`).
349
pub const CODEVIEW_PDB70_MAGIC: u32 = 0x5344_5352;
350
/// Magic number for CodeView PDB 2.0 signature (`'01BN'`).
351
pub const CODEVIEW_PDB20_MAGIC: u32 = 0x3031_424e;
352
/// Magic number for CodeView CV 5.0 signature (`'11BN'`).
353
pub const CODEVIEW_CV50_MAGIC: u32 = 0x3131_424e;
354
/// Magic number for CodeView CV 4.1 signature (`'90BN'`).
355
pub const CODEVIEW_CV41_MAGIC: u32 = 0x3930_424e;
356
357
// http://llvm.org/doxygen/CVDebugRecord_8h_source.html
358
#[repr(C)]
359
#[derive(Debug, PartialEq, Copy, Clone, Default)]
360
pub struct CodeviewPDB70DebugInfo<'a> {
361
    pub codeview_signature: u32,
362
    pub signature: [u8; 16],
363
    pub age: u32,
364
    pub filename: &'a [u8],
365
}
366
367
impl<'a> CodeviewPDB70DebugInfo<'a> {
368
0
    pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
369
0
        Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
370
0
    }
371
372
224
    pub fn parse_with_opts(
373
224
        bytes: &'a [u8],
374
224
        idd: &ImageDebugDirectory,
375
224
        opts: &options::ParseOptions,
376
224
    ) -> error::Result<Option<Self>> {
377
        // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
378
224
        let mut offset: usize = match opts.resolve_rva {
379
224
            true => idd.pointer_to_raw_data as usize,
380
0
            false => idd.address_of_raw_data as usize,
381
        };
382
383
        // calculate how long the eventual filename will be, which doubles as a check of the record size
384
224
        let filename_length = idd.size_of_data as isize - 24;
385
224
        if filename_length < 0 {
386
            // the record is too short to be plausible
387
62
            return Err(error::Error::Malformed(format!(
388
62
                "ImageDebugDirectory size of data seems wrong: {:?}",
389
62
                idd.size_of_data
390
62
            )))
391
62
            .or_permissive_and_default(
392
62
                opts.parse_mode.is_permissive(),
393
62
                "ImageDebugDirectory size of data seems wrong",
394
            );
395
162
        }
396
162
        let filename_length = filename_length as usize;
397
398
        // check the codeview signature
399
162
        let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
400
118
        if codeview_signature != CODEVIEW_PDB70_MAGIC {
401
86
            return Ok(None);
402
32
        }
403
404
        // read the rest
405
32
        let mut signature: [u8; 16] = [0; 16];
406
32
        signature.copy_from_slice(bytes.gread_with(&mut offset, 16)?);
407
32
        let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
408
32
        if let Some(filename) = bytes.get(offset..offset + filename_length) {
409
32
            Ok(Some(CodeviewPDB70DebugInfo {
410
32
                codeview_signature,
411
32
                signature,
412
32
                age,
413
32
                filename,
414
32
            }))
415
        } else {
416
0
            Err(error::Error::Malformed(format!(
417
0
                "ImageDebugDirectory seems corrupted: {:?}",
418
0
                idd
419
0
            )))
420
        }
421
224
    }
422
}
423
424
/// Represents the `IMAGE_DEBUG_VC_FEATURE_ENTRY` structure
425
#[repr(C)]
426
#[derive(Debug, PartialEq, Copy, Clone, Default)]
427
pub struct VCFeatureInfo {
428
    /// The count of pre-VC++
429
    pub pre_vc_plusplus_count: u32,
430
    /// The count of C and C++
431
    pub c_and_cplusplus_count: u32,
432
    /// The count of guard stack
433
    pub guard_stack_count: u32,
434
    /// The count of SDL
435
    pub sdl_count: u32,
436
    /// The count of guard
437
    pub guard_count: u32,
438
}
439
440
impl<'a> VCFeatureInfo {
441
0
    pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
442
0
        Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
443
0
    }
444
445
100
    pub fn parse_with_opts(
446
100
        bytes: &'a [u8],
447
100
        idd: &ImageDebugDirectory,
448
100
        opts: &options::ParseOptions,
449
100
    ) -> error::Result<Self> {
450
100
        let mut offset: usize = match opts.resolve_rva {
451
100
            true => idd.pointer_to_raw_data as usize,
452
0
            false => idd.address_of_raw_data as usize,
453
        };
454
455
100
        let pre_vc_plusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
456
66
        let c_and_cplusplus_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
457
64
        let guard_stack_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
458
64
        let sdl_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
459
64
        let guard_count: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
460
461
64
        Ok(VCFeatureInfo {
462
64
            pre_vc_plusplus_count,
463
64
            c_and_cplusplus_count,
464
64
            guard_stack_count,
465
64
            sdl_count,
466
64
            guard_count,
467
64
        })
468
100
    }
469
}
470
471
// http://llvm.org/doxygen/CVDebugRecord_8h_source.html
472
#[repr(C)]
473
#[derive(Debug, PartialEq, Copy, Clone, Default)]
474
pub struct CodeviewPDB20DebugInfo<'a> {
475
    pub codeview_signature: u32,
476
    pub codeview_offset: u32,
477
    pub signature: u32,
478
    pub age: u32,
479
    pub filename: &'a [u8],
480
}
481
482
impl<'a> CodeviewPDB20DebugInfo<'a> {
483
0
    pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
484
0
        Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
485
0
    }
486
487
180
    pub fn parse_with_opts(
488
180
        bytes: &'a [u8],
489
180
        idd: &ImageDebugDirectory,
490
180
        opts: &options::ParseOptions,
491
180
    ) -> error::Result<Option<Self>> {
492
        // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
493
180
        let mut offset: usize = match opts.resolve_rva {
494
180
            true => idd.pointer_to_raw_data as usize,
495
0
            false => idd.address_of_raw_data as usize,
496
        };
497
498
        // calculate how long the eventual filename will be, which doubles as a check of the record size
499
180
        let filename_length = idd.size_of_data as isize - 16;
500
180
        if filename_length < 0 {
501
            // the record is too short to be plausible
502
58
            return Err(error::Error::Malformed(format!(
503
58
                "ImageDebugDirectory size of data seems wrong: {:?}",
504
58
                idd.size_of_data
505
58
            )))
506
58
            .or_permissive_and_default(
507
58
                opts.parse_mode.is_permissive(),
508
58
                "ImageDebugDirectory size of data seems wrong",
509
            );
510
122
        }
511
122
        let filename_length = filename_length as usize;
512
513
        // check the codeview signature
514
122
        let codeview_signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
515
120
        if codeview_signature != CODEVIEW_PDB20_MAGIC {
516
120
            return Ok(None);
517
0
        }
518
0
        let codeview_offset: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
519
520
        // read the rest
521
0
        let signature: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
522
0
        let age: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
523
0
        if let Some(filename) = bytes.get(offset..offset + filename_length) {
524
0
            Ok(Some(CodeviewPDB20DebugInfo {
525
0
                codeview_signature,
526
0
                codeview_offset,
527
0
                signature,
528
0
                age,
529
0
                filename,
530
0
            }))
531
        } else {
532
0
            Err(error::Error::Malformed(format!(
533
0
                "ImageDebugDirectory seems corrupted: {:?}",
534
0
                idd
535
0
            )))
536
        }
537
180
    }
538
}
539
540
/// Represents the reproducible build (Repro) information extracted from a PE (Portable Executable) file.
541
///
542
/// The Repro information differs based on the compiler used to build the executable:
543
/// - For MSVC (Microsoft Visual C++), the Repro information is written directly into the raw data as a 32-byte hash.
544
/// - For Clang/(correctly, LLD linker), there is no dedicated raw data for the Repro information. Instead, the [`ImageDebugDirectory::time_date_stamp`]
545
///   field functions as a hash, providing a unique identifier for the reproducible build.
546
#[derive(Debug, PartialEq, Copy, Clone)]
547
pub enum ReproInfo<'a> {
548
    /// Represents a hash stored in the [`ImageDebugDirectory::time_date_stamp`] field.
549
    ///
550
    /// This variant is used primarily for executables built with Clang/LLD, where the
551
    /// [`ImageDebugDirectory::time_date_stamp`] acts as the Repro hash.
552
    TimeDateStamp(u32),
553
    /// Represents a buffer containing the 32-byte Repro hash.
554
    ///
555
    /// This variant is used for MSVC-built executables, where the Repro hash is directly
556
    /// stored as raw data in the debug directory.
557
    Buffer {
558
        /// The length of the buffer containing the Repro data. For MSVC, this is typically 32 bytes long.
559
        length: u32,
560
        /// A reference to the buffer containing the Repro hash data.
561
        buffer: &'a [u8],
562
    },
563
}
564
565
impl<'a> ReproInfo<'a> {
566
0
    pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
567
0
        Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
568
0
    }
569
570
196
    pub fn parse_with_opts(
571
196
        bytes: &'a [u8],
572
196
        idd: &ImageDebugDirectory,
573
196
        opts: &options::ParseOptions,
574
196
    ) -> error::Result<Self> {
575
196
        let mut offset: usize = match opts.resolve_rva {
576
196
            true => idd.pointer_to_raw_data as usize,
577
0
            false => idd.address_of_raw_data as usize,
578
        };
579
580
        // Clang(LLD) produces no data, uses timestamp field instead
581
        // MSVC(link.exe) produces 32-byte data
582
196
        if idd.size_of_data > 0 {
583
86
            let length: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
584
46
            if let Some(buffer) = bytes.get(offset..offset + length as usize) {
585
6
                Ok(Self::Buffer { length, buffer })
586
            } else {
587
40
                Err(error::Error::Malformed(format!(
588
40
                    "ImageDebugDirectory seems corrupted: {:?}",
589
40
                    idd
590
40
                )))
591
            }
592
        } else {
593
110
            Ok(Self::TimeDateStamp(idd.time_date_stamp))
594
        }
595
196
    }
596
}
597
598
/// Represents extended DLL characteristics information.
599
///
600
/// This structure holds additional characteristics of a DLL that may influence
601
/// how the operating system loads or manages the DLL, especially in terms of
602
/// security features and optimizations. These characteristics can include
603
/// settings related to Intel CET (Control-flow Enforcement Technology) and other
604
/// security-relevant attributes.
605
#[repr(C)]
606
#[derive(Debug, PartialEq, Copy, Clone, Default)]
607
pub struct ExDllCharacteristicsInfo {
608
    /// The extended characteristics of the DLL.
609
    ///
610
    /// This field is a bitmask of flags that define various security and performance
611
    /// properties of the DLL. The specific flags are defined by the PE format specification.
612
    ///
613
    /// This field contains one or more bitflags of:
614
    ///
615
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT`]
616
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE`]
617
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE`]
618
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY`]
619
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_1`]
620
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_2`]
621
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT`]
622
    /// - [`IMAGE_DLLCHARACTERISTICS_EX_HOTPATCH_COMPATIBLE`]
623
    pub characteristics_ex: u32,
624
}
625
626
/// Indicates that Control Flow Enforcement Technology (CET) is enabled for the DLL,
627
/// enhancing security via control-flow integrity.
628
pub const IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT: u32 = 0x1;
629
/// Indicates that CET is enforced in strict mode, increasing security measures against
630
/// control-flow attacks but may impact compatibility.
631
pub const IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE: u32 = 0x2;
632
/// Indicates that relaxed mode for Context IP Validation under CET is allowed,
633
/// providing a balance between security and performance.
634
pub const IMAGE_DLLCHARACTERISTICS_EX_CET_SET_CONTEXT_IP_VALIDATION_RELAXED_MODE: u32 = 0x4;
635
/// Indicates that the use of dynamic APIs is restricted to processes only,
636
/// enhancing security by limiting external API calls under CET.
637
pub const IMAGE_DLLCHARACTERISTICS_EX_CET_DYNAMIC_APIS_ALLOW_IN_PROC_ONLY: u32 = 0x8;
638
/// Reserved for future.
639
pub const IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_1: u32 = 0x10;
640
/// Reserved for future.
641
pub const IMAGE_DLLCHARACTERISTICS_EX_CET_RESERVED_2: u32 = 0x20;
642
/// Indicates that the DLL is compatible with Forward Control Flow Integrity (CFI).
643
///
644
/// This flag signifies that the DLL is designed to support forward CFI, a security
645
/// feature that helps prevent certain types of control flow attacks by ensuring
646
/// that control flow transfers occur only to valid targets.
647
pub const IMAGE_DLLCHARACTERISTICS_EX_FORWARD_CFI_COMPAT: u32 = 0x40;
648
/// Indicates that the DLL is hotpatch-compatible.
649
///
650
/// This flag indicates that the DLL can be modified while in use (hotpatching),
651
/// allowing updates or fixes to be applied without needing to restart the application
652
/// or service that is using the DLL. This can be useful for maintaining uptime and
653
/// applying critical patches in a live environment.
654
pub const IMAGE_DLLCHARACTERISTICS_EX_HOTPATCH_COMPATIBLE: u32 = 0x80;
655
656
impl<'a> ExDllCharacteristicsInfo {
657
0
    pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Self> {
658
0
        Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
659
0
    }
660
661
38
    pub fn parse_with_opts(
662
38
        bytes: &'a [u8],
663
38
        idd: &ImageDebugDirectory,
664
38
        opts: &options::ParseOptions,
665
38
    ) -> error::Result<Self> {
666
        // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
667
38
        let mut offset: usize = match opts.resolve_rva {
668
38
            true => idd.pointer_to_raw_data as usize,
669
0
            false => idd.address_of_raw_data as usize,
670
        };
671
672
38
        let characteristics_ex: u32 = bytes.gread_with(&mut offset, scroll::LE)?;
673
674
26
        Ok(ExDllCharacteristicsInfo { characteristics_ex })
675
38
    }
676
}
677
678
/// Represents the POGO info structure, which provides information
679
/// about Profile-Guided Optimization (POGO aka PGO) data within a PE file.
680
///
681
/// PGO is a compiler optimization technique that uses data collected from program
682
/// execution to optimize code layout and improve runtime performance. This structure
683
/// contains details such as the relative virtual address (RVA), size, and the associated
684
/// name of the function or data block for which PGO data is provided.
685
///
686
/// <https://devblogs.microsoft.com/cppblog/pogo>
687
#[repr(C)]
688
#[derive(Debug, PartialEq, Copy, Clone, Default)]
689
pub struct POGOInfo<'a> {
690
    /// The signature of POGO debug directory entry is always first 4-bytes
691
    ///
692
    /// Either one of:
693
    ///
694
    /// - [`IMAGE_DEBUG_POGO_SIGNATURE_LTCG`]
695
    /// - [`IMAGE_DEBUG_POGO_SIGNATURE_PGU`]
696
    pub signature: u32,
697
    /// Raw bytes of POGO debug entry, without first 4-bytes signatures field
698
    pub data: &'a [u8],
699
}
700
701
/// Represents the `IMAGE_DEBUG_POGO_ENTRY` structure
702
#[derive(Debug, PartialEq, Copy, Clone, Default)]
703
pub struct POGOInfoEntry<'a> {
704
    /// The relative virtual address (RVA) of the PGO data.
705
    pub rva: u32,
706
    /// The size of the PGO data block.
707
    pub size: u32,
708
    /// The name of the function or data block associated with the PGO data as a byte slice.
709
    ///
710
    /// This may contain a null-terminated string that represents the function or block
711
    /// name for which PGO optimization data is provided.
712
    pub name: &'a [u8],
713
}
714
715
/// Iterator over POGO entries in [`POGOInfo`].
716
#[derive(Debug, Copy, Clone)]
717
pub struct POGOEntryIterator<'a> {
718
    /// The raw data of [`POGOInfo::data`] without the signature field
719
    data: &'a [u8],
720
}
721
722
/// Indicates the PGO signature for Link-Time Code Generation (LTCG) (`u32` hex representation of `'LTCG'`).
723
///
724
/// This constant is used in the `IMAGE_DEBUG_DIRECTORY` to identify sections of
725
/// PGO data generated specifically for LTCG optimizations.
726
pub const IMAGE_DEBUG_POGO_SIGNATURE_LTCG: u32 = 0x4C544347;
727
/// Indicates the PGO signature for Profile-Guided Optimization (PGO) updates (PGU) (`u32` hex representation of `'PGU\0'`).
728
///
729
/// This constant is used to signify sections of PGO data associated with PGO updates,
730
/// which are incremental optimizations based on profiling data collected over time.
731
pub const IMAGE_DEBUG_POGO_SIGNATURE_PGU: u32 = 0x50475500;
732
/// Size of [`IMAGE_DEBUG_POGO_SIGNATURE_LTCG`] or [`IMAGE_DEBUG_POGO_SIGNATURE_PGU`]
733
pub const POGO_SIGNATURE_SIZE: usize = core::mem::size_of::<u32>();
734
735
impl<'a> POGOInfo<'a> {
736
0
    pub fn parse(bytes: &'a [u8], idd: &ImageDebugDirectory) -> error::Result<Option<Self>> {
737
0
        Self::parse_with_opts(bytes, idd, &options::ParseOptions::default())
738
0
    }
739
740
55
    pub fn parse_with_opts(
741
55
        bytes: &'a [u8],
742
55
        idd: &ImageDebugDirectory,
743
55
        opts: &options::ParseOptions,
744
55
    ) -> error::Result<Option<Self>> {
745
        // ImageDebugDirectory.pointer_to_raw_data stores a raw offset -- not a virtual offset -- which we can use directly
746
55
        let mut offset: usize = match opts.resolve_rva {
747
55
            true => idd.pointer_to_raw_data as usize,
748
0
            false => idd.address_of_raw_data as usize,
749
        };
750
751
55
        let signature = bytes.gread_with::<u32>(&mut offset, scroll::LE)?;
752
51
        if signature != IMAGE_DEBUG_POGO_SIGNATURE_LTCG
753
39
            && signature != IMAGE_DEBUG_POGO_SIGNATURE_PGU
754
        {
755
            // This is not something we support
756
39
            return Ok(None);
757
12
        }
758
759
12
        if idd.size_of_data as usize <= POGO_SIGNATURE_SIZE {
760
0
            return Err(error::Error::Malformed(format!(
761
0
                "ImageDebugDirectory size_of_data {:#x} is smaller or equal to POGO_SIGNATURE_SIZE {:#x}",
762
0
                idd.size_of_data, POGO_SIGNATURE_SIZE
763
0
            )));
764
12
        }
765
766
12
        let offset_end = offset
767
12
            .checked_add(idd.size_of_data as usize - POGO_SIGNATURE_SIZE)
768
12
            .ok_or_else(|| {
769
0
                error::Error::Malformed(format!(
770
0
                    "ImageDebugDirectory offset ({:#x}) and size ({:#x}) cause an integer overflow",
771
0
                    offset,
772
0
                    idd.size_of_data as usize - POGO_SIGNATURE_SIZE
773
0
                ))
774
0
            })?;
775
776
12
        if offset > bytes.len() || offset_end > bytes.len() {
777
0
            return Err(error::Error::Malformed(format!(
778
0
                "ImageDebugDirectory offset_start {:#x} or offset_end {:#x} exceed the bounds of the bytes size {:#x}",
779
0
                offset,
780
0
                offset_end,
781
0
                bytes.len()
782
0
            )));
783
12
        }
784
785
12
        let data = &bytes[offset..offset_end];
786
12
        Ok(Some(POGOInfo { signature, data }))
787
55
    }
788
789
    /// Returns iterator for [`POGOInfoEntry`]
790
0
    pub fn entries(&self) -> POGOEntryIterator<'a> {
791
0
        POGOEntryIterator { data: &self.data }
792
0
    }
793
}
794
795
impl<'a> Iterator for POGOEntryIterator<'a> {
796
    type Item = error::Result<POGOInfoEntry<'a>>;
797
798
0
    fn next(&mut self) -> Option<Self::Item> {
799
0
        if self.data.is_empty() {
800
0
            return None;
801
0
        }
802
803
0
        let mut offset = 0;
804
0
        let rva = match self.data.gread_with::<u32>(&mut offset, scroll::LE) {
805
0
            Ok(rva) => rva,
806
0
            Err(error) => return Some(Err(error.into())),
807
        };
808
0
        let size = match self.data.gread_with::<u32>(&mut offset, scroll::LE) {
809
0
            Ok(size) => size,
810
0
            Err(error) => return Some(Err(error.into())),
811
        };
812
813
        // Use >= to avoid empty slice, that we want to emit an error early here for
814
        // malformed name in a POGO entry.
815
0
        if offset >= self.data.len() {
816
0
            return Some(Err(error::Error::Malformed(format!(
817
0
                "Offset {offset:#x} is too big for containing name field of POGO entry (rva {rva:#x} and size {size:#X})",
818
0
            ))));
819
0
        }
820
0
        let name = match self.data[offset..].iter().position(|&b| b == 0) {
821
0
            Some(pos) => {
822
                // + 1 nul
823
0
                let Some(name) = self.data.gread_with::<&[u8]>(&mut offset, pos + 1).ok() else {
824
0
                    return Some(Err(error::Error::Malformed(format!(
825
0
                        "Null-terminator for POGO entry (rva {rva:#x} and size {size:#X}) found but exceeds iterator buffer",
826
0
                    ))));
827
                };
828
                // Round up to the nearest multiple of 4
829
0
                offset = (offset + 3) & !3;
830
0
                name
831
            }
832
            None => {
833
0
                return Some(Err(error::Error::Malformed(format!(
834
0
                    "Cannot find null-terminator for POGO entry (rva {rva:#x} and size {size:#X})",
835
0
                ))
836
0
                .into()));
837
            }
838
        };
839
840
0
        if offset > self.data.len() {
841
0
            return Some(Err(error::Error::Malformed(format!(
842
0
                "Offset {offset:#x} exceeds buffer length {:#x}",
843
0
                self.data.len()
844
0
            ))));
845
0
        }
846
0
        self.data = &self.data[offset..];
847
0
        Some(Ok(POGOInfoEntry { rva, size, name }))
848
0
    }
849
}
850
851
impl FusedIterator for POGOEntryIterator<'_> {}
852
853
#[cfg(test)]
854
mod tests {
855
    use super::{
856
        CODEVIEW_PDB70_MAGIC, ExDllCharacteristicsInfo, IMAGE_DEBUG_POGO_SIGNATURE_LTCG,
857
        IMAGE_DEBUG_TYPE_CODEVIEW, IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS, IMAGE_DEBUG_TYPE_ILTCG,
858
        IMAGE_DEBUG_TYPE_POGO, IMAGE_DEBUG_TYPE_REPRO, IMAGE_DEBUG_TYPE_VC_FEATURE,
859
        IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT, IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE,
860
        ImageDebugDirectory, POGO_SIGNATURE_SIZE, POGOEntryIterator, POGOInfoEntry, ReproInfo,
861
        VCFeatureInfo,
862
    };
863
864
    const NO_DEBUG_DIRECTORIES_BIN: &[u8] =
865
        include_bytes!("../../tests/bins/pe/no_debug_directories.exe.bin");
866
    const DEBUG_DIRECTORIES_TEST_MSVC_BIN: &[u8] =
867
        include_bytes!("../../tests/bins/pe/debug_directories-msvc.exe.bin");
868
    const DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN: &[u8] =
869
        include_bytes!("../../tests/bins/pe/debug_directories-clang_lld.exe.bin");
870
871
    fn ffi_to_string(bytes: &[u8]) -> String {
872
        unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(bytes) }
873
            .to_string_lossy()
874
            .to_string()
875
    }
876
877
    #[test]
878
    fn parse_no_debug_directories() {
879
        let binary =
880
            crate::pe::PE::parse(NO_DEBUG_DIRECTORIES_BIN).expect("Unable to parse binary");
881
        assert_eq!(binary.debug_data.is_none(), true);
882
    }
883
884
    #[test]
885
    fn parse_debug_entries_iterator() {
886
        let binary =
887
            crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
888
        assert_eq!(binary.debug_data.is_some(), true);
889
        let debug_data = binary.debug_data.unwrap();
890
        let entries = debug_data.entries().collect::<Result<Vec<_>, _>>();
891
        assert_eq!(entries.is_ok(), true);
892
        let entries = entries.unwrap();
893
        let entries_expect = vec![
894
            ImageDebugDirectory {
895
                characteristics: 0x0,
896
                time_date_stamp: 0x80AC7661,
897
                major_version: 0x0,
898
                minor_version: 0x0,
899
                data_type: IMAGE_DEBUG_TYPE_CODEVIEW,
900
                size_of_data: 0x38,
901
                address_of_raw_data: 0x20c0,
902
                pointer_to_raw_data: 0x4c0,
903
            },
904
            ImageDebugDirectory {
905
                characteristics: 0x0,
906
                time_date_stamp: 0x80AC7661,
907
                major_version: 0x0,
908
                minor_version: 0x0,
909
                data_type: IMAGE_DEBUG_TYPE_VC_FEATURE,
910
                size_of_data: 0x14,
911
                address_of_raw_data: 0x20f8,
912
                pointer_to_raw_data: 0x4f8,
913
            },
914
            ImageDebugDirectory {
915
                characteristics: 0x0,
916
                time_date_stamp: 0x80AC7661,
917
                major_version: 0x0,
918
                minor_version: 0x0,
919
                data_type: IMAGE_DEBUG_TYPE_POGO,
920
                size_of_data: 0x58,
921
                address_of_raw_data: 0x210c,
922
                pointer_to_raw_data: 0x50c,
923
            },
924
            ImageDebugDirectory {
925
                characteristics: 0x0,
926
                time_date_stamp: 0x80AC7661,
927
                major_version: 0x0,
928
                minor_version: 0x0,
929
                data_type: IMAGE_DEBUG_TYPE_ILTCG,
930
                size_of_data: 0x0,
931
                address_of_raw_data: 0x0,
932
                pointer_to_raw_data: 0x0,
933
            },
934
            ImageDebugDirectory {
935
                characteristics: 0x0,
936
                time_date_stamp: 0x80AC7661,
937
                major_version: 0x0,
938
                minor_version: 0x0,
939
                data_type: IMAGE_DEBUG_TYPE_REPRO,
940
                size_of_data: 0x24,
941
                address_of_raw_data: 0x2164,
942
                pointer_to_raw_data: 0x564,
943
            },
944
            ImageDebugDirectory {
945
                characteristics: 0x0,
946
                time_date_stamp: 0x80AC7661,
947
                major_version: 0x0,
948
                minor_version: 0x0,
949
                data_type: IMAGE_DEBUG_TYPE_EX_DLLCHARACTERISTICS,
950
                size_of_data: 0x4,
951
                address_of_raw_data: 0x2188,
952
                pointer_to_raw_data: 0x588,
953
            },
954
        ];
955
        assert_eq!(entries, entries_expect);
956
    }
957
958
    #[test]
959
    fn parse_debug_codeview_pdb70_msvc() {
960
        let binary =
961
            crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
962
        assert_eq!(binary.debug_data.is_some(), true);
963
        let debug_data = binary.debug_data.unwrap();
964
        assert_eq!(debug_data.codeview_pdb70_debug_info.is_some(), true);
965
        let codeview_pdb70_debug_info = debug_data.codeview_pdb70_debug_info.unwrap();
966
        let filename = ffi_to_string(codeview_pdb70_debug_info.filename);
967
        assert_eq!(filename, String::from("THIS-IS-BINARY-FOR-GOBLIN-TESTS"));
968
        assert_eq!(codeview_pdb70_debug_info.age, 3);
969
        assert_eq!(
970
            codeview_pdb70_debug_info.codeview_signature,
971
            CODEVIEW_PDB70_MAGIC
972
        );
973
        assert_eq!(
974
            codeview_pdb70_debug_info.signature,
975
            [
976
                0x1F, 0x4F, 0x58, 0x9C, 0x3C, 0xEA, 0x00, 0x83, 0x3F, 0x57, 0x00, 0xCC, 0x36, 0xA7,
977
                0x84, 0xDF,
978
            ]
979
        );
980
    }
981
982
    #[test]
983
    fn parse_debug_codeview_pdb70_clang() {
984
        let binary = crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN)
985
            .expect("Unable to parse binary");
986
        assert_eq!(binary.debug_data.is_some(), true);
987
        let debug_data = binary.debug_data.unwrap();
988
        assert_eq!(debug_data.codeview_pdb70_debug_info.is_some(), true);
989
        let codeview_pdb70_debug_info = debug_data.codeview_pdb70_debug_info.unwrap();
990
        let filename = ffi_to_string(codeview_pdb70_debug_info.filename);
991
        assert_eq!(filename, String::from("THIS-IS-BINARY-FOR-GOBLIN-TESTS"));
992
        assert_eq!(codeview_pdb70_debug_info.age, 1);
993
        assert_eq!(
994
            codeview_pdb70_debug_info.codeview_signature,
995
            CODEVIEW_PDB70_MAGIC
996
        );
997
        assert_eq!(
998
            codeview_pdb70_debug_info.signature,
999
            [
1000
                0xC8, 0xBA, 0xF6, 0xAB, 0xB2, 0x98, 0xD1, 0x9E, 0x4C, 0x4C, 0x44, 0x20, 0x50, 0x44,
1001
                0x42, 0x2E,
1002
            ]
1003
        );
1004
    }
1005
1006
    #[test]
1007
    fn parse_debug_vcfeature() {
1008
        let binary =
1009
            crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1010
        assert_eq!(binary.debug_data.is_some(), true);
1011
        let debug_data = binary.debug_data.unwrap();
1012
        assert_eq!(debug_data.vcfeature_info.is_some(), true);
1013
        let vcfeature_info = debug_data.vcfeature_info.unwrap();
1014
        let vcfeature_info_expect = VCFeatureInfo {
1015
            pre_vc_plusplus_count: 0,
1016
            c_and_cplusplus_count: 1,
1017
            guard_stack_count: 0,
1018
            sdl_count: 0,
1019
            guard_count: 0,
1020
        };
1021
        assert_eq!(vcfeature_info, vcfeature_info_expect);
1022
    }
1023
1024
    #[test]
1025
    fn parse_debug_repro_msvc() {
1026
        let binary =
1027
            crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1028
        assert_eq!(binary.debug_data.is_some(), true);
1029
        let debug_data = binary.debug_data.unwrap();
1030
        assert_eq!(debug_data.repro_info.is_some(), true);
1031
        let repro_info = debug_data.repro_info.unwrap();
1032
        let repro_info_expect = ReproInfo::Buffer {
1033
            length: 32,
1034
            buffer: &[
1035
                0x1F, 0x4F, 0x58, 0x9C, 0x3C, 0xEA, 0x00, 0x83, 0x3F, 0x57, 0x00, 0xCC, 0x36, 0xA7,
1036
                0x84, 0xDF, 0xF7, 0x7C, 0x70, 0xE0, 0xEF, 0x7A, 0xBA, 0x08, 0xD0, 0xA6, 0x8B, 0x7F,
1037
                0x61, 0x76, 0xAC, 0x80,
1038
            ],
1039
        };
1040
        assert_eq!(repro_info, repro_info_expect);
1041
    }
1042
1043
    #[test]
1044
    fn parse_debug_repro_clang_lld() {
1045
        let binary = crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_CLANG_LLD_BIN)
1046
            .expect("Unable to parse binary");
1047
        assert_eq!(binary.debug_data.is_some(), true);
1048
        let debug_data = binary.debug_data.unwrap();
1049
        assert_eq!(debug_data.repro_info.is_some(), true);
1050
        let repro_info = debug_data.repro_info.unwrap();
1051
        let repro_info_expect = ReproInfo::TimeDateStamp(0xDB2F3908);
1052
        assert_eq!(repro_info, repro_info_expect);
1053
    }
1054
1055
    #[test]
1056
    fn parse_debug_exdllcharacteristics() {
1057
        let binary =
1058
            crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1059
        assert_eq!(binary.debug_data.is_some(), true);
1060
        let debug_data = binary.debug_data.unwrap();
1061
        assert_eq!(debug_data.ex_dll_characteristics_info.is_some(), true);
1062
        let ex_dll_characteristics_info = debug_data.ex_dll_characteristics_info.unwrap();
1063
        let ex_dll_characteristics_info_expect = ExDllCharacteristicsInfo {
1064
            characteristics_ex: IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT
1065
                | IMAGE_DLLCHARACTERISTICS_EX_CET_COMPAT_STRICT_MODE,
1066
        };
1067
        assert_eq!(
1068
            ex_dll_characteristics_info,
1069
            ex_dll_characteristics_info_expect
1070
        );
1071
    }
1072
1073
    #[test]
1074
    fn parse_debug_pogo() {
1075
        let binary =
1076
            crate::pe::PE::parse(DEBUG_DIRECTORIES_TEST_MSVC_BIN).expect("Unable to parse binary");
1077
        assert_eq!(binary.debug_data.is_some(), true);
1078
        let debug_data = binary.debug_data.unwrap();
1079
        assert_eq!(debug_data.pogo_info.is_some(), true);
1080
        let pogo_info = debug_data.pogo_info.unwrap();
1081
        assert_eq!(pogo_info.signature, IMAGE_DEBUG_POGO_SIGNATURE_LTCG);
1082
        assert_eq!(pogo_info.data.len(), 88 - POGO_SIGNATURE_SIZE);
1083
        let entries = pogo_info.entries().collect::<Result<Vec<_>, _>>().unwrap();
1084
        let entries_expect = vec![
1085
            POGOInfoEntry {
1086
                rva: 0x1000,
1087
                size: 0x3,
1088
                name: b".text$mn\0",
1089
            },
1090
            POGOInfoEntry {
1091
                rva: 0x2000,
1092
                size: 0xA8,
1093
                name: b".rdata\0",
1094
            },
1095
            POGOInfoEntry {
1096
                rva: 0x20A8,
1097
                size: 0x18,
1098
                name: b".rdata$voltmd\0",
1099
            },
1100
            POGOInfoEntry {
1101
                rva: 0x20C0,
1102
                size: 0xCC,
1103
                name: b".rdata$zzzdbg\0",
1104
            },
1105
        ];
1106
        assert_eq!(entries, entries_expect);
1107
    }
1108
1109
    #[rustfmt::skip]
1110
    const MALFORMED_POGO_NAME: &[u8; 83] = &[
1111
        // Entry 1: .text$mn
1112
        0x00, 0x10, 0x00, 0x00, // RVA: 0x00001000
1113
        0x03, 0x00, 0x00, 0x00, // Size: 0x00000003
1114
        0x2e, 0x74, 0x65, 0x78, // Name: ".text$mn\0"
1115
        0x74, 0x24, 0x6d, 0x6e,
1116
        0x00, 0x00, 0x00, 0x00,
1117
        // Entry 2: .rdata
1118
        0x00, 0x20, 0x00, 0x00, // RVA: 0x00002000
1119
        0xa8, 0x00, 0x00, 0x00, // Size: 0x000000A8
1120
        0x2e, 0x72, 0x64, 0x61, // Name: ".rdata\0"
1121
        0x74, 0x61, 0x00, 0x00,
1122
        // Entry 3: .rdata$voltmd
1123
        0xa8, 0x20, 0x00, 0x00, // RVA: 0x000020A8
1124
        0x18, 0x00, 0x00, 0x00, // Size: 0x00000018
1125
        0x2e, 0x72, 0x64, 0x61, // Name: ".rdata$voltmd\0"
1126
        0x74, 0x61, 0x24, 0x76,
1127
        0x6f, 0x6c, 0x74, 0x6d,
1128
        0x64, 0x00, 0x00, 0x00,
1129
        // Entry 4: .rdata$zzzdbg
1130
        0xc0, 0x20, 0x00, 0x00, // RVA: 0x000020C0
1131
        0xcc, 0x00, 0x00, 0x00, // Size: 0x000000CC
1132
        0x2e, 0x72, 0x64, 0x61, // Name: ".rdata$zzzdbg\0"
1133
        0x74, 0x61, 0x24, 0x7a,
1134
        0x7a, 0x7a, 0x64, 0x62,
1135
        0x67, 0x00, 0x00, /* truncated 0x00 */
1136
    ];
1137
1138
    #[test]
1139
    #[should_panic = "Malformed(\"Offset 0x18 exceeds buffer length 0x17\")"]
1140
    fn parse_debug_pogo_malformed_name() {
1141
        let entries = POGOEntryIterator {
1142
            data: MALFORMED_POGO_NAME,
1143
        };
1144
        let _ = entries.collect::<Result<Vec<_>, _>>().unwrap();
1145
    }
1146
}