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/pdb2-0.10.2/src/modi/c13.rs
Line
Count
Source
1
use std::fmt;
2
use std::mem;
3
use std::slice;
4
5
use scroll::{ctx::TryFromCtx, Endian, Pread};
6
7
use crate::common::*;
8
use crate::modi::{
9
    constants, CrossModuleExport, CrossModuleRef, FileChecksum, FileInfo, LineInfo, LineInfoKind,
10
    ModuleRef,
11
};
12
use crate::symbol::{BinaryAnnotation, BinaryAnnotationsIter, InlineSiteSymbol};
13
use crate::FallibleIterator;
14
15
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16
#[repr(u32)]
17
#[allow(unused)]
18
enum DebugSubsectionKind {
19
    // Native
20
    Symbols = 0xf1,
21
    Lines = 0xf2,
22
    StringTable = 0xf3,
23
    FileChecksums = 0xf4,
24
    FrameData = 0xf5,
25
    InlineeLines = 0xf6,
26
    CrossScopeImports = 0xf7,
27
    CrossScopeExports = 0xf8,
28
29
    // .NET
30
    ILLines = 0xf9,
31
    FuncMDTokenMap = 0xfa,
32
    TypeMDTokenMap = 0xfb,
33
    MergedAssemblyInput = 0xfc,
34
35
    CoffSymbolRva = 0xfd,
36
}
37
38
impl DebugSubsectionKind {
39
102k
    fn parse(value: u32) -> Result<Option<Self>> {
40
102k
        if (0xf1..=0xfd).contains(&value) {
41
100k
            Ok(Some(unsafe {
42
100k
                std::mem::transmute::<u32, DebugSubsectionKind>(value)
43
100k
            }))
44
1.82k
        } else if value == constants::DEBUG_S_IGNORE {
45
0
            Ok(None)
46
        } else {
47
1.82k
            Err(Error::UnimplementedDebugSubsection(value))
48
        }
49
102k
    }
50
}
51
52
#[derive(Clone, Copy, Debug)]
53
struct DebugSubsectionHeader {
54
    /// The kind of this subsection.
55
    kind: u32,
56
    /// The length of this subsection in bytes, following the header.
57
    len: u32,
58
}
59
60
impl<'t> TryFromCtx<'t, Endian> for DebugSubsectionHeader {
61
    type Error = scroll::Error;
62
63
106k
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
64
106k
        let mut offset = 0;
65
106k
        let data = Self {
66
106k
            kind: this.gread_with(&mut offset, le)?,
67
106k
            len: this.gread_with(&mut offset, le)?,
68
        };
69
106k
        Ok((data, offset))
70
106k
    }
71
}
72
73
impl DebugSubsectionHeader {
74
102k
    fn kind(self) -> Result<Option<DebugSubsectionKind>> {
75
102k
        DebugSubsectionKind::parse(self.kind)
76
102k
    }
77
78
106k
    fn len(self) -> usize {
79
106k
        self.len as usize
80
106k
    }
81
}
82
83
#[derive(Clone, Copy, Debug)]
84
struct DebugSubsection<'a> {
85
    pub kind: DebugSubsectionKind,
86
    pub data: &'a [u8],
87
}
88
89
#[derive(Clone, Debug, Default)]
90
struct DebugSubsectionIterator<'a> {
91
    buf: ParseBuffer<'a>,
92
}
93
94
impl<'a> DebugSubsectionIterator<'a> {
95
62.3k
    fn new(data: &'a [u8]) -> Self {
96
62.3k
        Self {
97
62.3k
            buf: ParseBuffer::from(data),
98
62.3k
        }
99
62.3k
    }
100
}
101
102
impl<'a> FallibleIterator for DebugSubsectionIterator<'a> {
103
    type Item = DebugSubsection<'a>;
104
    type Error = Error;
105
106
159k
    fn next(&mut self) -> Result<Option<Self::Item>> {
107
159k
        while !self.buf.is_empty() {
108
106k
            let header = self.buf.parse::<DebugSubsectionHeader>()?;
109
106k
            let data = self.buf.take(header.len())?;
110
102k
            let kind = match header.kind()? {
111
100k
                Some(kind) => kind,
112
0
                None => continue,
113
            };
114
115
100k
            return Ok(Some(DebugSubsection { kind, data }));
116
        }
117
118
52.5k
        Ok(None)
119
159k
    }
120
}
121
122
#[derive(Clone, Copy, Debug, Default)]
123
struct DebugInlineeLinesHeader {
124
    /// The signature of the inlinees
125
    signature: u32,
126
}
127
128
impl<'t> TryFromCtx<'t, Endian> for DebugInlineeLinesHeader {
129
    type Error = scroll::Error;
130
131
2.53k
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
132
2.53k
        let mut offset = 0;
133
2.53k
        let data = Self {
134
2.53k
            signature: this.gread_with(&mut offset, le)?,
135
        };
136
2.53k
        Ok((data, offset))
137
2.53k
    }
138
}
139
140
impl DebugInlineeLinesHeader {
141
55.5k
    pub fn has_extra_files(self) -> bool {
142
55.5k
        self.signature == constants::CV_INLINEE_SOURCE_LINE_SIGNATURE_EX
143
55.5k
    }
144
}
145
146
#[derive(Clone, Copy, Debug, Default, PartialEq)]
147
pub struct InlineeSourceLine<'a> {
148
    pub inlinee: IdIndex,
149
    pub file_id: FileIndex,
150
    pub line: u32,
151
    extra_files: &'a [u8],
152
}
153
154
impl InlineeSourceLine<'_> {
155
    // TODO: Implement extra files iterator when needed.
156
}
157
158
impl<'a> TryFromCtx<'a, DebugInlineeLinesHeader> for InlineeSourceLine<'a> {
159
    type Error = Error;
160
161
55.5k
    fn try_from_ctx(this: &'a [u8], header: DebugInlineeLinesHeader) -> Result<(Self, usize)> {
162
55.5k
        let mut buf = ParseBuffer::from(this);
163
55.5k
        let inlinee = buf.parse()?;
164
55.5k
        let file_id = buf.parse()?;
165
55.5k
        let line = buf.parse()?;
166
167
55.5k
        let extra_files = if header.has_extra_files() {
168
0
            let file_count = buf.parse::<u32>()? as usize;
169
0
            buf.take(file_count * std::mem::size_of::<u32>())?
170
        } else {
171
55.5k
            &[]
172
        };
173
174
55.5k
        let source_line = Self {
175
55.5k
            inlinee,
176
55.5k
            file_id,
177
55.5k
            line,
178
55.5k
            extra_files,
179
55.5k
        };
180
181
55.5k
        Ok((source_line, buf.pos()))
182
55.5k
    }
183
}
184
185
#[derive(Debug, Clone, Default)]
186
struct DebugInlineeLinesIterator<'a> {
187
    header: DebugInlineeLinesHeader,
188
    buf: ParseBuffer<'a>,
189
}
190
191
impl<'a> FallibleIterator for DebugInlineeLinesIterator<'a> {
192
    type Item = InlineeSourceLine<'a>;
193
    type Error = Error;
194
195
73.5k
    fn next(&mut self) -> Result<Option<Self::Item>> {
196
73.5k
        if self.buf.is_empty() {
197
17.9k
            Ok(None)
198
        } else {
199
55.5k
            Ok(Some(self.buf.parse_with(self.header)?))
200
        }
201
73.5k
    }
202
}
203
204
#[derive(Clone, Debug, Default)]
205
struct DebugInlineeLinesSubsection<'a> {
206
    header: DebugInlineeLinesHeader,
207
    data: &'a [u8],
208
}
209
210
impl<'a> DebugInlineeLinesSubsection<'a> {
211
2.53k
    fn parse(data: &'a [u8]) -> Result<Self> {
212
2.53k
        let mut buf = ParseBuffer::from(data);
213
2.53k
        let header = buf.parse::<DebugInlineeLinesHeader>()?;
214
215
2.53k
        Ok(Self {
216
2.53k
            header,
217
2.53k
            data: &data[buf.pos()..],
218
2.53k
        })
219
2.53k
    }
220
221
    /// Iterate through all inlinees.
222
17.9k
    fn lines(&self) -> DebugInlineeLinesIterator<'a> {
223
17.9k
        DebugInlineeLinesIterator {
224
17.9k
            header: self.header,
225
17.9k
            buf: ParseBuffer::from(self.data),
226
17.9k
        }
227
17.9k
    }
228
}
229
230
#[derive(Clone, Copy, Debug, Default)]
231
struct DebugLinesHeader {
232
    /// Section offset of this line contribution.
233
    offset: PdbInternalSectionOffset,
234
    /// See `LineFlags` enumeration.
235
    flags: u16,
236
    /// Code size of this line contribution.
237
    code_size: u32,
238
}
239
240
impl<'t> TryFromCtx<'t, Endian> for DebugLinesHeader {
241
    type Error = scroll::Error;
242
243
42.7k
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
244
42.7k
        let mut offset = 0;
245
42.7k
        let data = Self {
246
42.7k
            offset: this.gread_with(&mut offset, le)?,
247
42.7k
            flags: this.gread_with(&mut offset, le)?,
248
42.7k
            code_size: this.gread_with(&mut offset, le)?,
249
        };
250
42.7k
        Ok((data, offset))
251
42.7k
    }
252
}
253
254
impl DebugLinesHeader {
255
22.4k
    fn has_columns(self) -> bool {
256
22.4k
        self.flags & constants::CV_LINES_HAVE_COLUMNS != 0
257
22.4k
    }
258
}
259
260
#[derive(Clone, Copy, Debug)]
261
struct DebugLinesSubsection<'a> {
262
    header: DebugLinesHeader,
263
    data: &'a [u8],
264
}
265
266
impl<'a> DebugLinesSubsection<'a> {
267
42.7k
    fn parse(data: &'a [u8]) -> Result<Self> {
268
42.7k
        let mut buf = ParseBuffer::from(data);
269
42.7k
        let header = buf.parse()?;
270
42.7k
        let data = &data[buf.pos()..];
271
42.7k
        Ok(Self { header, data })
272
42.7k
    }
273
274
22.4k
    fn blocks(&self) -> DebugLinesBlockIterator<'a> {
275
22.4k
        DebugLinesBlockIterator {
276
22.4k
            header: self.header,
277
22.4k
            buf: ParseBuffer::from(self.data),
278
22.4k
        }
279
22.4k
    }
280
}
281
282
/// Marker instructions for a line offset.
283
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
284
enum LineMarkerKind {
285
    /// A debugger should skip this address.
286
    DoNotStepOnto,
287
    /// A debugger should not step into this address.
288
    DoNotStepInto,
289
}
290
291
/// The raw line number entry in a PDB.
292
#[repr(C)]
293
#[derive(Clone, Copy, Debug)]
294
struct LineNumberHeader {
295
    /// Offset to start of code bytes for line number.
296
    offset: u32,
297
    /// Combined information on the start line, end line and entry type:
298
    ///
299
    /// ```ignore
300
    /// unsigned long   linenumStart:24;  // line where statement/expression starts
301
    /// unsigned long   deltaLineEnd:7;   // delta to line where statement ends (optional)
302
    /// unsigned long   fStatement  :1;   // true if a statement line number, else an expression
303
    /// ```
304
    flags: u32,
305
}
306
307
impl<'t> TryFromCtx<'t, Endian> for LineNumberHeader {
308
    type Error = scroll::Error;
309
310
194k
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
311
194k
        let mut offset = 0;
312
194k
        let data = Self {
313
194k
            offset: this.gread_with(&mut offset, le)?,
314
194k
            flags: this.gread_with(&mut offset, le)?,
315
        };
316
194k
        Ok((data, offset))
317
194k
    }
318
}
319
320
/// A mapping of code section offsets to source line numbers.
321
#[derive(Clone, Debug)]
322
struct LineNumberEntry {
323
    /// Delta offset to the start of this line contribution (debug lines subsection).
324
    pub offset: u32,
325
    /// Start line number of the statement or expression.
326
    pub start_line: u32,
327
    /// End line number of the statement or expression.
328
    pub end_line: u32,
329
    /// The type of code construct this line entry refers to.
330
    pub kind: LineInfoKind,
331
}
332
333
/// Marker for debugging purposes.
334
#[derive(Clone, Debug)]
335
struct LineMarkerEntry {
336
    /// Delta offset to the start of this line contribution (debug lines subsection).
337
    #[allow(dead_code)] // reason = "unused until TODO in LineIterator is resolved"
338
    pub offset: u32,
339
    /// The marker kind, hinting a debugger how to deal with code at this offset.
340
    #[allow(dead_code)] // reason = "debugger instructions are not exposed"
341
    pub kind: LineMarkerKind,
342
}
343
344
/// A parsed line entry.
345
#[derive(Clone, Debug)]
346
enum LineEntry {
347
    /// Declares a source line number.
348
    Number(LineNumberEntry),
349
    /// Declares a debugging marker.
350
    #[allow(dead_code)] // reason = "the inner `LineMarkerEntry` is not (yet) accessed"
351
    Marker(LineMarkerEntry),
352
}
353
354
impl LineNumberHeader {
355
    /// Parse this line number header into a line entry.
356
194k
    pub fn parse(self) -> LineEntry {
357
        // The compiler generates special line numbers to hint the debugger. Separate these out so
358
        // that they are not confused with actual line number entries.
359
194k
        let start_line = self.flags & 0x00ff_ffff;
360
194k
        let marker = match start_line {
361
0
            0xfeefee => Some(LineMarkerKind::DoNotStepOnto),
362
224
            0xf00f00 => Some(LineMarkerKind::DoNotStepInto),
363
194k
            _ => None,
364
        };
365
366
194k
        if let Some(kind) = marker {
367
224
            return LineEntry::Marker(LineMarkerEntry {
368
224
                offset: self.offset,
369
224
                kind,
370
224
            });
371
194k
        }
372
373
        // It has been observed in some PDBs that this does not store a delta to start_line but
374
        // actually just the truncated value of `end_line`. Therefore, prefer to use `end_line` and
375
        // compute the deta from `end_line` and `start_line`, if needed.
376
194k
        let line_delta = self.flags & 0x7f00_0000 >> 24;
377
378
        // The line_delta contains the lower 7 bits of the end line number. We take all higher bits
379
        // from the start line and OR them with the lower delta bits. This combines to the full
380
        // original end line number.
381
194k
        let high_start = start_line & !0x7f;
382
194k
        let mut end_line = high_start | line_delta;
383
384
        // If the end line number is smaller than the start line, we have to assume an overflow.
385
        // The end line will most likely be within 128 lines from the start line. Thus, we account
386
        // for the overflow by adding 1 to the 8th bit.
387
194k
        if end_line < start_line {
388
0
            end_line += 1 << 7;
389
194k
        }
390
391
194k
        let kind = if self.flags & 0x8000_0000 != 0 {
392
194k
            LineInfoKind::Statement
393
        } else {
394
0
            LineInfoKind::Expression
395
        };
396
397
194k
        LineEntry::Number(LineNumberEntry {
398
194k
            offset: self.offset,
399
194k
            start_line,
400
194k
            end_line,
401
194k
            kind,
402
194k
        })
403
194k
    }
404
}
405
406
#[derive(Clone, Debug, Default)]
407
struct DebugLinesIterator<'a> {
408
    block: DebugLinesBlockHeader,
409
    buf: ParseBuffer<'a>,
410
}
411
412
impl FallibleIterator for DebugLinesIterator<'_> {
413
    type Item = LineEntry;
414
    type Error = Error;
415
416
263k
    fn next(&mut self) -> Result<Option<Self::Item>> {
417
263k
        if self.buf.is_empty() {
418
69.5k
            return Ok(None);
419
194k
        }
420
421
194k
        self.buf.parse().map(LineNumberHeader::parse).map(Some)
422
263k
    }
423
}
424
425
#[repr(C)]
426
#[derive(Clone, Copy, Debug, Default)]
427
struct ColumnNumberEntry {
428
    start_column: u16,
429
    end_column: u16,
430
}
431
432
impl<'t> TryFromCtx<'t, Endian> for ColumnNumberEntry {
433
    type Error = scroll::Error;
434
435
0
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
436
0
        let mut offset = 0;
437
0
        let data = Self {
438
0
            start_column: this.gread_with(&mut offset, le)?,
439
0
            end_column: this.gread_with(&mut offset, le)?,
440
        };
441
0
        Ok((data, offset))
442
0
    }
443
}
444
445
#[derive(Clone, Debug, Default)]
446
struct DebugColumnsIterator<'a> {
447
    buf: ParseBuffer<'a>,
448
}
449
450
impl FallibleIterator for DebugColumnsIterator<'_> {
451
    type Item = ColumnNumberEntry;
452
    type Error = Error;
453
454
194k
    fn next(&mut self) -> Result<Option<Self::Item>> {
455
194k
        if self.buf.is_empty() {
456
194k
            return Ok(None);
457
0
        }
458
459
0
        self.buf.parse().map(Some)
460
194k
    }
461
}
462
463
#[repr(C)]
464
#[derive(Clone, Copy, Debug, Default)]
465
struct DebugLinesBlockHeader {
466
    /// Offset of the file checksum in the file checksums debug subsection.
467
    file_index: u32,
468
469
    /// Number of line entries in this block.
470
    ///
471
    /// If the debug lines subsection also contains column information (see `has_columns`), then the
472
    /// same number of column entries will be present after the line entries.
473
    num_lines: u32,
474
475
    /// Total byte size of this block, including following line and column entries.
476
    block_size: u32,
477
}
478
479
impl<'t> TryFromCtx<'t, Endian> for DebugLinesBlockHeader {
480
    type Error = scroll::Error;
481
482
22.4k
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
483
22.4k
        let mut offset = 0;
484
22.4k
        let data = Self {
485
22.4k
            file_index: this.gread_with(&mut offset, le)?,
486
22.4k
            num_lines: this.gread_with(&mut offset, le)?,
487
22.4k
            block_size: this.gread_with(&mut offset, le)?,
488
        };
489
22.4k
        Ok((data, offset))
490
22.4k
    }
491
}
492
493
impl DebugLinesBlockHeader {
494
    /// The byte size of all line and column records combined.
495
22.4k
    fn data_size(&self) -> usize {
496
22.4k
        self.block_size as usize - std::mem::size_of::<Self>()
497
22.4k
    }
498
499
    /// The byte size of all line number entries combined.
500
22.4k
    fn line_size(&self) -> usize {
501
22.4k
        self.num_lines as usize * std::mem::size_of::<LineNumberHeader>()
502
22.4k
    }
503
504
    /// The byte size of all column number entries combined.
505
22.4k
    fn column_size(&self, subsection: DebugLinesHeader) -> usize {
506
22.4k
        if subsection.has_columns() {
507
0
            self.num_lines as usize * std::mem::size_of::<ColumnNumberEntry>()
508
        } else {
509
22.4k
            0
510
        }
511
22.4k
    }
512
}
513
514
#[derive(Clone, Debug)]
515
struct DebugLinesBlock<'a> {
516
    header: DebugLinesBlockHeader,
517
    line_data: &'a [u8],
518
    column_data: &'a [u8],
519
}
520
521
impl<'a> DebugLinesBlock<'a> {
522
    #[allow(unused)]
523
0
    fn file_index(&self) -> FileIndex {
524
0
        FileIndex(self.header.file_index)
525
0
    }
526
527
22.4k
    fn lines(&self) -> DebugLinesIterator<'a> {
528
22.4k
        DebugLinesIterator {
529
22.4k
            block: self.header,
530
22.4k
            buf: ParseBuffer::from(self.line_data),
531
22.4k
        }
532
22.4k
    }
533
534
22.4k
    fn columns(&self) -> DebugColumnsIterator<'a> {
535
22.4k
        DebugColumnsIterator {
536
22.4k
            buf: ParseBuffer::from(self.column_data),
537
22.4k
        }
538
22.4k
    }
539
}
540
541
#[derive(Clone, Debug, Default)]
542
struct DebugLinesBlockIterator<'a> {
543
    header: DebugLinesHeader,
544
    buf: ParseBuffer<'a>,
545
}
546
547
impl<'a> FallibleIterator for DebugLinesBlockIterator<'a> {
548
    type Item = DebugLinesBlock<'a>;
549
    type Error = Error;
550
551
69.5k
    fn next(&mut self) -> Result<Option<Self::Item>> {
552
69.5k
        if self.buf.is_empty() {
553
47.1k
            return Ok(None);
554
22.4k
        }
555
556
        // The header is followed by a variable-size chunk of data, specified by `data_size`. Load
557
        // all of it at once to ensure we're not reading garbage in case there is more information
558
        // we do not yet understand.
559
22.4k
        let header = self.buf.parse::<DebugLinesBlockHeader>()?;
560
22.4k
        let data = self.buf.take(header.data_size())?;
561
562
        // The first data is a set of line entries, optionally followed by column entries. Load both
563
        // and discard eventual data that follows
564
22.4k
        let (line_data, data) = data.split_at(header.line_size());
565
22.4k
        let (column_data, remainder) = data.split_at(header.column_size(self.header));
566
567
        // In case the PDB format is extended with more information, we'd like to know here.
568
22.4k
        debug_assert!(remainder.is_empty());
569
570
22.4k
        Ok(Some(DebugLinesBlock {
571
22.4k
            header,
572
22.4k
            line_data,
573
22.4k
            column_data,
574
22.4k
        }))
575
69.5k
    }
576
}
577
578
/// Possible representations of file checksums in the file checksums subsection.
579
#[repr(u8)]
580
#[allow(unused)]
581
#[derive(Clone, Copy, Debug, Eq, Ord, Hash, PartialEq, PartialOrd)]
582
enum FileChecksumKind {
583
    None = 0,
584
    Md5 = 1,
585
    Sha1 = 2,
586
    Sha256 = 3,
587
}
588
589
impl FileChecksumKind {
590
    /// Parses the checksum kind from its raw value.
591
520k
    fn parse(value: u8) -> Result<Self> {
592
520k
        if value <= 3 {
593
520k
            Ok(unsafe { std::mem::transmute::<u8, FileChecksumKind>(value) })
594
        } else {
595
108
            Err(Error::UnimplementedFileChecksumKind(value))
596
        }
597
520k
    }
598
}
599
600
/// Raw header of a single file checksum entry.
601
#[derive(Clone, Copy, Debug)]
602
struct FileChecksumHeader {
603
    name_offset: u32,
604
    checksum_size: u8,
605
    checksum_kind: u8,
606
}
607
608
impl<'t> TryFromCtx<'t, Endian> for FileChecksumHeader {
609
    type Error = scroll::Error;
610
611
520k
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
612
520k
        let mut offset = 0;
613
520k
        let data = Self {
614
520k
            name_offset: this.gread_with(&mut offset, le)?,
615
520k
            checksum_size: this.gread_with(&mut offset, le)?,
616
520k
            checksum_kind: this.gread_with(&mut offset, le)?,
617
        };
618
520k
        Ok((data, offset))
619
520k
    }
620
}
621
622
/// A file checksum entry.
623
#[derive(Clone, Debug)]
624
struct FileChecksumEntry<'a> {
625
    /// Reference to the file name in the string table.
626
    name: StringRef,
627
    /// File checksum value.
628
    checksum: FileChecksum<'a>,
629
}
630
631
#[derive(Clone, Debug, Default)]
632
struct DebugFileChecksumsIterator<'a> {
633
    buf: ParseBuffer<'a>,
634
}
635
636
impl<'a> FallibleIterator for DebugFileChecksumsIterator<'a> {
637
    type Item = FileChecksumEntry<'a>;
638
    type Error = Error;
639
640
542k
    fn next(&mut self) -> Result<Option<Self::Item>> {
641
542k
        if self.buf.is_empty() {
642
21.8k
            return Ok(None);
643
520k
        }
644
645
520k
        let header = self.buf.parse::<FileChecksumHeader>()?;
646
520k
        let checksum_data = self.buf.take(header.checksum_size as usize)?;
647
648
520k
        let checksum = match FileChecksumKind::parse(header.checksum_kind)? {
649
36
            FileChecksumKind::None => FileChecksum::None,
650
520k
            FileChecksumKind::Md5 => FileChecksum::Md5(checksum_data),
651
0
            FileChecksumKind::Sha1 => FileChecksum::Sha1(checksum_data),
652
0
            FileChecksumKind::Sha256 => FileChecksum::Sha256(checksum_data),
653
        };
654
655
520k
        self.buf.align(4)?;
656
657
520k
        Ok(Some(FileChecksumEntry {
658
520k
            name: StringRef(header.name_offset),
659
520k
            checksum,
660
520k
        }))
661
542k
    }
662
}
663
664
#[derive(Clone, Debug, Default)]
665
struct DebugFileChecksumsSubsection<'a> {
666
    data: &'a [u8],
667
}
668
669
impl<'a> DebugFileChecksumsSubsection<'a> {
670
    /// Creates a new file checksums subsection.
671
23.8k
    fn new(data: &'a [u8]) -> Self {
672
23.8k
        Self { data }
673
23.8k
    }
674
675
    /// Returns an iterator over all file checksum entries.
676
    #[allow(unused)]
677
18.4k
    fn entries(&self) -> Result<DebugFileChecksumsIterator<'a>> {
678
18.4k
        self.entries_at_offset(FileIndex(0))
679
18.4k
    }
680
681
    /// Returns an iterator over file checksum entries starting at the given offset.
682
293k
    fn entries_at_offset(&self, offset: FileIndex) -> Result<DebugFileChecksumsIterator<'a>> {
683
293k
        let mut buf = ParseBuffer::from(self.data);
684
293k
        buf.take(offset.0 as usize)?;
685
293k
        Ok(DebugFileChecksumsIterator { buf })
686
293k
    }
687
}
688
689
#[derive(Clone, Copy, Debug)]
690
struct CrossScopeImportModule<'a> {
691
    name: ModuleRef,
692
    /// unparsed in LE byteorder
693
    imports: &'a [u32],
694
}
695
696
impl CrossScopeImportModule<'_> {
697
    /// Returns the local reference at the given offset.
698
    ///
699
    /// This function performs an "unsafe" conversion of the raw value into `Local<I>`. It is
700
    /// assumed that this function is only called from contexts where `I` can be statically
701
    /// inferred.
702
12.1k
    fn get<I>(self, import: usize) -> Option<Local<I>>
703
12.1k
    where
704
12.1k
        I: ItemIndex,
705
    {
706
12.1k
        let value = self.imports.get(import)?;
707
12.1k
        let index = u32::from_le(*value).into();
708
12.1k
        Some(Local(index))
709
12.1k
    }
<pdb2::modi::c13::CrossScopeImportModule>::get::<pdb2::common::IdIndex>
Line
Count
Source
702
12.1k
    fn get<I>(self, import: usize) -> Option<Local<I>>
703
12.1k
    where
704
12.1k
        I: ItemIndex,
705
    {
706
12.1k
        let value = self.imports.get(import)?;
707
12.1k
        let index = u32::from_le(*value).into();
708
12.1k
        Some(Local(index))
709
12.1k
    }
Unexecuted instantiation: <pdb2::modi::c13::CrossScopeImportModule>::get::<pdb2::common::TypeIndex>
Unexecuted instantiation: <pdb2::modi::c13::CrossScopeImportModule>::get::<_>
710
}
711
712
#[derive(Clone, Debug, Default)]
713
struct CrossScopeImportModuleIter<'a> {
714
    buf: ParseBuffer<'a>,
715
}
716
717
impl<'a> FallibleIterator for CrossScopeImportModuleIter<'a> {
718
    type Item = CrossScopeImportModule<'a>;
719
    type Error = Error;
720
721
2.10k
    fn next(&mut self) -> Result<Option<Self::Item>> {
722
2.10k
        if self.buf.is_empty() {
723
1.05k
            return Ok(None);
724
1.05k
        }
725
726
1.05k
        let name = ModuleRef(self.buf.parse()?);
727
1.05k
        let count = self.buf.parse::<u32>()? as usize;
728
729
1.05k
        let data = self.buf.take(count * mem::size_of::<u32>())?;
730
1.05k
        let imports = cast_aligned(data).ok_or(Error::InvalidStreamLength("CrossScopeImports"))?;
731
732
1.05k
        Ok(Some(CrossScopeImportModule { name, imports }))
733
2.10k
    }
734
}
735
736
#[derive(Clone, Copy, Debug, Default)]
737
struct DebugCrossScopeImportsSubsection<'a> {
738
    data: &'a [u8],
739
}
740
741
impl<'a> DebugCrossScopeImportsSubsection<'a> {
742
1.05k
    fn new(data: &'a [u8]) -> Self {
743
1.05k
        Self { data }
744
1.05k
    }
745
746
1.05k
    fn modules(self) -> CrossScopeImportModuleIter<'a> {
747
1.05k
        let buf = ParseBuffer::from(self.data);
748
1.05k
        CrossScopeImportModuleIter { buf }
749
1.05k
    }
750
}
751
752
/// Provides efficient access to imported types and IDs from other modules.
753
///
754
/// This can be used to resolve cross module references. See [`ItemIndex::is_cross_module`] for more
755
/// information.
756
#[derive(Clone, Debug, Default)]
757
pub struct CrossModuleImports<'a> {
758
    modules: Vec<CrossScopeImportModule<'a>>,
759
}
760
761
impl<'a> CrossModuleImports<'a> {
762
    /// Creates `CrossModuleImports` from the imports debug subsection.
763
1.05k
    fn from_section(section: DebugCrossScopeImportsSubsection<'a>) -> Result<Self> {
764
1.05k
        let modules = section.modules().collect()?;
765
1.05k
        Ok(Self { modules })
766
1.05k
    }
767
768
    /// Loads `CrossModuleImports` from the debug subsections data.
769
1.05k
    pub(crate) fn parse(data: &'a [u8]) -> Result<Self> {
770
1.05k
        let import_data = DebugSubsectionIterator::new(data)
771
3.15k
            .find(|sec| Ok(sec.kind == DebugSubsectionKind::CrossScopeImports))?
772
1.05k
            .map(|sec| sec.data);
773
774
1.05k
        match import_data {
775
1.05k
            Some(d) => Self::from_section(DebugCrossScopeImportsSubsection::new(d)),
776
0
            None => Ok(Self::default()),
777
        }
778
1.05k
    }
779
780
    /// Resolves the referenced module and local index for the index.
781
    ///
782
    /// The given index **must** be a cross module reference. Use `ItemIndex::is_cross_module` to
783
    /// check this before invoking this function. If successful, this function returns a reference
784
    /// to the module that declares the type, as well as the local index of the type in that module.
785
    ///
786
    /// # Errors
787
    ///
788
    /// * `Error::NotACrossModuleRef` if the given index is already a global index and not a cross
789
    ///   module reference.
790
    /// * `Error::CrossModuleRefNotFound` if the cross module reference points to a module or local
791
    ///   index that is not indexed by this import table.
792
12.1k
    pub fn resolve_import<I>(&self, index: I) -> Result<CrossModuleRef<I>>
793
12.1k
    where
794
12.1k
        I: ItemIndex,
795
    {
796
12.1k
        let raw_index = index.into();
797
12.1k
        if !index.is_cross_module() {
798
0
            return Err(Error::NotACrossModuleRef(raw_index));
799
12.1k
        }
800
801
12.1k
        let module_index = ((raw_index >> 20) & 0x7ff) as usize;
802
12.1k
        let import_index = (raw_index & 0x000f_ffff) as usize;
803
804
12.1k
        let module = self
805
12.1k
            .modules
806
12.1k
            .get(module_index)
807
12.1k
            .ok_or(Error::CrossModuleRefNotFound(raw_index))?;
808
809
12.1k
        let local_index = module
810
12.1k
            .get(import_index)
811
12.1k
            .ok_or(Error::CrossModuleRefNotFound(raw_index))?;
812
813
12.1k
        Ok(CrossModuleRef(module.name, local_index))
814
12.1k
    }
<pdb2::modi::c13::CrossModuleImports>::resolve_import::<pdb2::common::IdIndex>
Line
Count
Source
792
12.1k
    pub fn resolve_import<I>(&self, index: I) -> Result<CrossModuleRef<I>>
793
12.1k
    where
794
12.1k
        I: ItemIndex,
795
    {
796
12.1k
        let raw_index = index.into();
797
12.1k
        if !index.is_cross_module() {
798
0
            return Err(Error::NotACrossModuleRef(raw_index));
799
12.1k
        }
800
801
12.1k
        let module_index = ((raw_index >> 20) & 0x7ff) as usize;
802
12.1k
        let import_index = (raw_index & 0x000f_ffff) as usize;
803
804
12.1k
        let module = self
805
12.1k
            .modules
806
12.1k
            .get(module_index)
807
12.1k
            .ok_or(Error::CrossModuleRefNotFound(raw_index))?;
808
809
12.1k
        let local_index = module
810
12.1k
            .get(import_index)
811
12.1k
            .ok_or(Error::CrossModuleRefNotFound(raw_index))?;
812
813
12.1k
        Ok(CrossModuleRef(module.name, local_index))
814
12.1k
    }
Unexecuted instantiation: <pdb2::modi::c13::CrossModuleImports>::resolve_import::<pdb2::common::TypeIndex>
Unexecuted instantiation: <pdb2::modi::c13::CrossModuleImports>::resolve_import::<_>
815
}
816
817
/// Raw representation of `CrossModuleExport`.
818
///
819
/// This type can directly be mapped onto a slice of binary data and exposes the underlying `local`
820
/// and `global` fields with correct endianness via getter methods. There are two ways to use this:
821
///
822
///  1. Binary search over a slice of exports to find the one matching a given local index
823
///  2. Enumerate all for debugging purposes
824
#[repr(C)]
825
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
826
struct RawCrossScopeExport {
827
    /// The local index within the module.
828
    ///
829
    /// This maps to `Local<I: ItemIndex>` in the public type signature.
830
    local: u32,
831
832
    /// The index in the global type or id stream.
833
    ///
834
    /// This maps to `I: ItemIndex` in the public type signature.
835
    global: u32,
836
}
837
838
impl<'t> TryFromCtx<'t, Endian> for RawCrossScopeExport {
839
    type Error = scroll::Error;
840
841
22.6k
    fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
842
22.6k
        let mut offset = 0;
843
22.6k
        let data = Self {
844
22.6k
            local: this.gread_with(&mut offset, le)?,
845
22.6k
            global: this.gread_with(&mut offset, le)?,
846
        };
847
22.6k
        Ok((data, offset))
848
22.6k
    }
849
}
850
851
impl From<RawCrossScopeExport> for CrossModuleExport {
852
0
    fn from(raw: RawCrossScopeExport) -> Self {
853
0
        if (raw.local & 0x8000_0000) != 0 {
854
0
            Self::Id(Local(IdIndex(raw.local)), IdIndex(raw.global))
855
        } else {
856
0
            Self::Type(Local(TypeIndex(raw.local)), TypeIndex(raw.global))
857
        }
858
0
    }
859
}
860
861
struct RawCrossScopeExportsIter<'a> {
862
    buf: ParseBuffer<'a>,
863
}
864
865
impl FallibleIterator for RawCrossScopeExportsIter<'_> {
866
    type Item = RawCrossScopeExport;
867
    type Error = Error;
868
869
22.8k
    fn next(&mut self) -> Result<Option<Self::Item>> {
870
22.8k
        if self.buf.is_empty() {
871
238
            return Ok(None);
872
22.6k
        }
873
874
22.6k
        self.buf.parse().map(Some)
875
22.8k
    }
876
}
877
878
#[derive(Clone, Copy, Debug, Default)]
879
struct DebugCrossScopeExportsSubsection<'a> {
880
    data: &'a [u8],
881
}
882
883
impl<'a> DebugCrossScopeExportsSubsection<'a> {
884
    /// Creates a new cross scope exports subsection.
885
238
    fn parse(data: &'a [u8]) -> Result<Self> {
886
238
        if cast_aligned::<RawCrossScopeExport>(data).is_none() {
887
0
            return Err(Error::InvalidStreamLength(
888
0
                "DebugCrossScopeExportsSubsection",
889
0
            ));
890
238
        }
891
892
238
        Ok(Self { data })
893
238
    }
894
895
238
    fn exports(self) -> RawCrossScopeExportsIter<'a> {
896
238
        let buf = ParseBuffer::from(self.data);
897
238
        RawCrossScopeExportsIter { buf }
898
238
    }
899
}
900
901
/// Iterator returned by [`CrossModuleExports::exports`].
902
#[derive(Clone, Debug)]
903
pub struct CrossModuleExportIter<'a> {
904
    exports: slice::Iter<'a, RawCrossScopeExport>,
905
}
906
907
impl Default for CrossModuleExportIter<'_> {
908
0
    fn default() -> Self {
909
0
        Self { exports: [].iter() }
910
0
    }
911
}
912
913
impl FallibleIterator for CrossModuleExportIter<'_> {
914
    type Item = CrossModuleExport;
915
    type Error = Error;
916
917
0
    fn next(&mut self) -> Result<Option<Self::Item>> {
918
0
        Ok(self.exports.next().map(|r| (*r).into()))
919
0
    }
920
}
921
922
/// A table of exports declared by this module.
923
///
924
/// Other modules can import types and ids from this module by using [cross module
925
/// references](ItemIndex::is_cross_module).
926
#[derive(Clone, Debug, Default)]
927
pub struct CrossModuleExports {
928
    raw_exports: Vec<RawCrossScopeExport>,
929
}
930
931
impl CrossModuleExports {
932
238
    fn from_section(section: DebugCrossScopeExportsSubsection<'_>) -> Result<Self> {
933
238
        let raw_exports = section.exports().collect()?;
934
238
        Ok(Self { raw_exports })
935
238
    }
936
937
244
    pub(crate) fn parse(data: &[u8]) -> Result<Self> {
938
244
        let export_data = DebugSubsectionIterator::new(data)
939
476
            .find(|sec| Ok(sec.kind == DebugSubsectionKind::CrossScopeExports))?
940
238
            .map(|sec| sec.data);
941
942
238
        match export_data {
943
238
            Some(d) => Self::from_section(DebugCrossScopeExportsSubsection::parse(d)?),
944
0
            None => Ok(Self::default()),
945
        }
946
244
    }
947
948
    /// Returns the number of exported types or ids from this module.
949
    #[inline]
950
    #[must_use]
951
0
    pub fn len(&self) -> usize {
952
0
        self.raw_exports.len()
953
0
    }
954
955
    /// Returns `true` if this module does not export types or ids.
956
    #[inline]
957
    #[must_use]
958
0
    pub fn is_empty(&self) -> bool {
959
0
        self.raw_exports.is_empty()
960
0
    }
961
962
    /// Returns an iterator over all cross scope exports.
963
    #[must_use]
964
0
    pub fn exports(&self) -> CrossModuleExportIter<'_> {
965
0
        CrossModuleExportIter {
966
0
            exports: self.raw_exports.iter(),
967
0
        }
968
0
    }
969
970
    /// Resolves the global index of the given cross module import's local index.
971
    ///
972
    /// The global index can be used to retrieve items from the
973
    /// [`TypeInformation`](crate::TypeInformation) or [`IdInformation`](crate::IdInformation)
974
    /// streams. If the given local index is not listed in the export list, this function returns
975
    /// `Ok(None)`.
976
11.5k
    pub fn resolve_import<I>(&self, local_index: Local<I>) -> Result<Option<I>>
977
11.5k
    where
978
11.5k
        I: ItemIndex,
979
    {
980
11.5k
        let local = local_index.0.into();
981
11.5k
        let exports = &self.raw_exports;
982
983
11.5k
        Ok(match exports.binary_search_by_key(&local, |r| r.local) {
984
11.5k
            Ok(i) => Some(I::from(exports[i].global)),
985
0
            Err(_) => None,
986
        })
987
11.5k
    }
<pdb2::modi::c13::CrossModuleExports>::resolve_import::<pdb2::common::IdIndex>
Line
Count
Source
976
11.5k
    pub fn resolve_import<I>(&self, local_index: Local<I>) -> Result<Option<I>>
977
11.5k
    where
978
11.5k
        I: ItemIndex,
979
    {
980
11.5k
        let local = local_index.0.into();
981
11.5k
        let exports = &self.raw_exports;
982
983
11.5k
        Ok(match exports.binary_search_by_key(&local, |r| r.local) {
984
11.5k
            Ok(i) => Some(I::from(exports[i].global)),
985
0
            Err(_) => None,
986
        })
987
11.5k
    }
Unexecuted instantiation: <pdb2::modi::c13::CrossModuleExports>::resolve_import::<pdb2::common::TypeIndex>
Unexecuted instantiation: <pdb2::modi::c13::CrossModuleExports>::resolve_import::<_>
988
}
989
990
#[derive(Clone)]
991
pub struct LineIterator<'a> {
992
    /// Iterator over all subsections in the current module.
993
    sections: std::slice::Iter<'a, DebugLinesSubsection<'a>>,
994
    /// Iterator over all blocks in the current lines subsection.
995
    blocks: DebugLinesBlockIterator<'a>,
996
    /// Iterator over lines in the current block.
997
    lines: DebugLinesIterator<'a>,
998
    /// Iterator over optional columns in the current block.
999
    columns: DebugColumnsIterator<'a>,
1000
    /// Previous line info before length can be inferred.
1001
    last_info: Option<LineInfo>,
1002
}
1003
1004
impl FallibleIterator for LineIterator<'_> {
1005
    type Item = LineInfo;
1006
    type Error = Error;
1007
1008
218k
    fn next(&mut self) -> Result<Option<Self::Item>> {
1009
        loop {
1010
263k
            if let Some(entry) = self.lines.next()? {
1011
                // A column entry is only returned if the debug lines subsection contains column
1012
                // information. Otherwise, the columns iterator is empty. We can safely assume that
1013
                // the number of line entries and column entries returned from the two iterators is
1014
                // equivalent. If it were not, the creation of the block would already have failed.
1015
194k
                let column_entry = self.columns.next()?;
1016
1017
                // The high-level line iterator is only interested in actual line entries. It might
1018
                // make sense to eventually fold markers at the same offset into the `LineInfo`
1019
                // record.
1020
194k
                let line_entry = match entry {
1021
194k
                    LineEntry::Number(line_entry) => line_entry,
1022
224
                    LineEntry::Marker(_) => continue,
1023
                };
1024
1025
194k
                let section_header = self.blocks.header;
1026
194k
                let block_header = self.lines.block;
1027
1028
194k
                let offset = section_header.offset + line_entry.offset;
1029
1030
194k
                let line_info = LineInfo {
1031
194k
                    offset,
1032
194k
                    length: None, // Length is inferred in the next iteration.
1033
194k
                    file_index: FileIndex(block_header.file_index),
1034
194k
                    line_start: line_entry.start_line,
1035
194k
                    line_end: line_entry.end_line,
1036
194k
                    column_start: column_entry.map(|e| e.start_column.into()),
1037
194k
                    column_end: column_entry.map(|e| e.end_column.into()),
1038
194k
                    kind: line_entry.kind,
1039
                };
1040
1041
194k
                let mut last_info = match std::mem::replace(&mut self.last_info, Some(line_info)) {
1042
171k
                    Some(last_info) => last_info,
1043
22.4k
                    None => continue,
1044
                };
1045
1046
171k
                last_info.set_end(offset);
1047
171k
                return Ok(Some(last_info));
1048
69.5k
            }
1049
1050
69.5k
            if let Some(block) = self.blocks.next()? {
1051
22.4k
                self.lines = block.lines();
1052
22.4k
                self.columns = block.columns();
1053
22.4k
                continue;
1054
47.1k
            }
1055
1056
            // The current debug lines subsection ends. Fix up the length of the last line record
1057
            // using the code size of the lines section, before continuing iteration. This ensures
1058
            // the most accurate length of the line record, even if there are gaps between sections.
1059
47.1k
            if let Some(ref mut last_line) = self.last_info {
1060
22.4k
                let section_header = self.blocks.header;
1061
22.4k
                last_line.set_end(section_header.offset + section_header.code_size);
1062
24.6k
            }
1063
1064
47.1k
            if let Some(lines_section) = self.sections.next() {
1065
0
                self.blocks = lines_section.blocks();
1066
0
                continue;
1067
47.1k
            }
1068
1069
47.1k
            return Ok(self.last_info.take());
1070
        }
1071
218k
    }
1072
}
1073
1074
impl Default for LineIterator<'_> {
1075
2.20k
    fn default() -> Self {
1076
2.20k
        Self {
1077
2.20k
            sections: [].iter(),
1078
2.20k
            blocks: DebugLinesBlockIterator::default(),
1079
2.20k
            lines: DebugLinesIterator::default(),
1080
2.20k
            columns: DebugColumnsIterator::default(),
1081
2.20k
            last_info: None,
1082
2.20k
        }
1083
2.20k
    }
1084
}
1085
1086
impl fmt::Debug for LineIterator<'_> {
1087
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1088
0
        f.debug_struct("LineIterator")
1089
0
            .field("sections", &self.sections.as_slice())
1090
0
            .field("blocks", &self.blocks)
1091
0
            .field("lines", &self.lines)
1092
0
            .field("columns", &self.columns)
1093
0
            .field("last_info", &self.last_info)
1094
0
            .finish()
1095
0
    }
1096
}
1097
1098
/// An iterator over line information records in a module.
1099
#[derive(Clone, Debug, Default)]
1100
pub struct InlineeLineIterator<'a> {
1101
    annotations: BinaryAnnotationsIter<'a>,
1102
    file_index: FileIndex,
1103
    code_offset_base: u32,
1104
    code_offset: PdbInternalSectionOffset,
1105
    code_length: Option<u32>,
1106
    line: u32,
1107
    line_length: u32,
1108
    col_start: Option<u32>,
1109
    col_end: Option<u32>,
1110
    line_kind: LineInfoKind,
1111
    last_info: Option<LineInfo>,
1112
}
1113
1114
impl<'a> InlineeLineIterator<'a> {
1115
39.2k
    fn new(
1116
39.2k
        parent_offset: PdbInternalSectionOffset,
1117
39.2k
        inline_site: &InlineSiteSymbol<'a>,
1118
39.2k
        inlinee_line: InlineeSourceLine<'a>,
1119
39.2k
    ) -> Self {
1120
39.2k
        Self {
1121
39.2k
            annotations: inline_site.annotations.iter(),
1122
39.2k
            file_index: inlinee_line.file_id,
1123
39.2k
            code_offset_base: 0,
1124
39.2k
            code_offset: parent_offset,
1125
39.2k
            code_length: None,
1126
39.2k
            line: inlinee_line.line,
1127
39.2k
            line_length: 1,
1128
39.2k
            col_start: None,
1129
39.2k
            col_end: None,
1130
39.2k
            line_kind: LineInfoKind::Statement,
1131
39.2k
            last_info: None,
1132
39.2k
        }
1133
39.2k
    }
1134
}
1135
1136
impl FallibleIterator for InlineeLineIterator<'_> {
1137
    type Item = LineInfo;
1138
    type Error = Error;
1139
1140
131k
    fn next(&mut self) -> Result<Option<Self::Item>> {
1141
195k
        while let Some(op) = self.annotations.next()? {
1142
116k
            match op {
1143
0
                BinaryAnnotation::CodeOffset(code_offset) => {
1144
0
                    self.code_offset.offset = code_offset;
1145
0
                }
1146
0
                BinaryAnnotation::ChangeCodeOffsetBase(code_offset_base) => {
1147
0
                    self.code_offset_base = code_offset_base;
1148
0
                }
1149
16.3k
                BinaryAnnotation::ChangeCodeOffset(delta) => {
1150
16.3k
                    self.code_offset = self.code_offset.wrapping_add(delta);
1151
16.3k
                }
1152
0
                BinaryAnnotation::ChangeCodeLength(code_length) => {
1153
0
                    if let Some(ref mut last_info) = self.last_info {
1154
0
                        if last_info.length.is_none() && last_info.kind == self.line_kind {
1155
0
                            last_info.length = Some(code_length);
1156
0
                        }
1157
0
                    }
1158
1159
0
                    self.code_offset = self.code_offset.wrapping_add(code_length);
1160
                }
1161
0
                BinaryAnnotation::ChangeFile(file_index) => {
1162
0
                    // NOTE: There seems to be a bug in VS2015-VS2019 compilers that generates
1163
0
                    // invalid binary annotations when file changes are involved. This can be
1164
0
                    // triggered by #including files directly into inline functions. The
1165
0
                    // `ChangeFile` annotations are generated in the wrong spot or missing
1166
0
                    // completely. This renders information on the file effectively useless in a lot
1167
0
                    // of cases.
1168
0
                    self.file_index = file_index;
1169
0
                }
1170
24.8k
                BinaryAnnotation::ChangeLineOffset(delta) => {
1171
24.8k
                    self.line = (i64::from(self.line) + i64::from(delta)) as u32;
1172
24.8k
                }
1173
0
                BinaryAnnotation::ChangeLineEndDelta(line_length) => {
1174
0
                    self.line_length = line_length;
1175
0
                }
1176
0
                BinaryAnnotation::ChangeRangeKind(kind) => {
1177
0
                    self.line_kind = match kind {
1178
0
                        0 => LineInfoKind::Expression,
1179
0
                        1 => LineInfoKind::Statement,
1180
0
                        _ => self.line_kind,
1181
                    };
1182
                }
1183
0
                BinaryAnnotation::ChangeColumnStart(col_start) => {
1184
0
                    self.col_start = Some(col_start);
1185
0
                }
1186
0
                BinaryAnnotation::ChangeColumnEndDelta(delta) => {
1187
0
                    self.col_end = self
1188
0
                        .col_end
1189
0
                        .map(|col_end| (i64::from(col_end) + i64::from(delta)) as u32)
1190
                }
1191
15.2k
                BinaryAnnotation::ChangeCodeOffsetAndLineOffset(code_delta, line_delta) => {
1192
15.2k
                    self.code_offset += code_delta;
1193
15.2k
                    self.line = (i64::from(self.line) + i64::from(line_delta)) as u32;
1194
15.2k
                }
1195
60.2k
                BinaryAnnotation::ChangeCodeLengthAndCodeOffset(code_length, code_delta) => {
1196
60.2k
                    self.code_length = Some(code_length);
1197
60.2k
                    self.code_offset += code_delta;
1198
60.2k
                }
1199
0
                BinaryAnnotation::ChangeColumnEnd(col_end) => {
1200
0
                    self.col_end = Some(col_end);
1201
0
                }
1202
            }
1203
1204
116k
            if !op.emits_line_info() {
1205
24.8k
                continue;
1206
91.7k
            }
1207
1208
91.7k
            let line_offset = self.code_offset + self.code_offset_base;
1209
91.7k
            if let Some(ref mut last_info) = self.last_info {
1210
52.4k
                if last_info.length.is_none() && last_info.kind == self.line_kind {
1211
31.5k
                    last_info.length = Some(line_offset.offset - last_info.offset.offset);
1212
31.5k
                }
1213
39.2k
            }
1214
1215
91.7k
            let line_info = LineInfo {
1216
91.7k
                kind: self.line_kind,
1217
91.7k
                file_index: self.file_index,
1218
91.7k
                offset: line_offset,
1219
91.7k
                length: self.code_length,
1220
91.7k
                line_start: self.line,
1221
91.7k
                line_end: self.line + self.line_length,
1222
91.7k
                column_start: self.col_start,
1223
91.7k
                column_end: self.col_end,
1224
91.7k
            };
1225
1226
            // Code length resets with every line record.
1227
91.7k
            self.code_length = None;
1228
1229
            // Finish the previous record and emit it. The current record is stored so that the
1230
            // length can be inferred from subsequent operators or the next line info.
1231
91.7k
            if let Some(last_info) = std::mem::replace(&mut self.last_info, Some(line_info)) {
1232
52.4k
                return Ok(Some(last_info));
1233
39.2k
            }
1234
        }
1235
1236
78.5k
        Ok(self.last_info.take())
1237
131k
    }
1238
}
1239
1240
/// An inlined function that can evaluate to line information.
1241
#[derive(Clone, Debug, Default)]
1242
pub struct Inlinee<'a>(InlineeSourceLine<'a>);
1243
1244
impl<'a> Inlinee<'a> {
1245
    /// The index of this inlinee in the `IdInformation` stream (IPI).
1246
    #[must_use]
1247
55.5k
    pub fn index(&self) -> IdIndex {
1248
55.5k
        self.0.inlinee
1249
55.5k
    }
1250
1251
    /// Returns an iterator over line records for an inline site.
1252
    ///
1253
    /// Note that line records are not guaranteed to be ordered by source code offset. If a
1254
    /// monotonic order by `PdbInternalSectionOffset` or `Rva` is required, the lines have to be
1255
    /// sorted manually.
1256
    #[must_use]
1257
39.2k
    pub fn lines(
1258
39.2k
        &self,
1259
39.2k
        parent_offset: PdbInternalSectionOffset,
1260
39.2k
        inline_site: &InlineSiteSymbol<'a>,
1261
39.2k
    ) -> InlineeLineIterator<'a> {
1262
39.2k
        InlineeLineIterator::new(parent_offset, inline_site, self.0)
1263
39.2k
    }
1264
}
1265
1266
/// An iterator over line information records in a module.
1267
#[derive(Clone, Debug, Default)]
1268
pub struct InlineeIterator<'a> {
1269
    inlinee_lines: DebugInlineeLinesIterator<'a>,
1270
}
1271
1272
impl<'a> InlineeIterator<'a> {
1273
17.9k
    pub(crate) fn parse(data: &'a [u8]) -> Result<Self> {
1274
17.9k
        let inlinee_data = DebugSubsectionIterator::new(data)
1275
22.5k
            .find(|sec| Ok(sec.kind == DebugSubsectionKind::InlineeLines))?
1276
17.9k
            .map(|sec| sec.data);
1277
1278
17.9k
        let inlinee_lines = match inlinee_data {
1279
2.53k
            Some(d) => DebugInlineeLinesSubsection::parse(d)?,
1280
15.4k
            None => DebugInlineeLinesSubsection::default(),
1281
        };
1282
1283
17.9k
        Ok(Self {
1284
17.9k
            inlinee_lines: inlinee_lines.lines(),
1285
17.9k
        })
1286
17.9k
    }
1287
}
1288
1289
impl<'a> FallibleIterator for InlineeIterator<'a> {
1290
    type Item = Inlinee<'a>;
1291
    type Error = Error;
1292
1293
73.5k
    fn next(&mut self) -> Result<Option<Self::Item>> {
1294
73.5k
        match self.inlinee_lines.next() {
1295
55.5k
            Ok(Some(inlinee_line)) => Ok(Some(Inlinee(inlinee_line))),
1296
17.9k
            Ok(None) => Ok(None),
1297
0
            Err(error) => Err(error),
1298
        }
1299
73.5k
    }
1300
}
1301
1302
#[derive(Clone, Debug, Default)]
1303
pub struct FileIterator<'a> {
1304
    checksums: DebugFileChecksumsIterator<'a>,
1305
}
1306
1307
impl<'a> FallibleIterator for FileIterator<'a> {
1308
    type Item = FileInfo<'a>;
1309
    type Error = Error;
1310
1311
266k
    fn next(&mut self) -> Result<Option<Self::Item>> {
1312
266k
        match self.checksums.next() {
1313
244k
            Ok(Some(entry)) => Ok(Some(FileInfo {
1314
244k
                name: entry.name,
1315
244k
                checksum: entry.checksum,
1316
244k
            })),
1317
21.8k
            Ok(None) => Ok(None),
1318
118
            Err(error) => Err(error),
1319
        }
1320
266k
    }
1321
}
1322
1323
pub struct LineProgram<'a> {
1324
    file_checksums: DebugFileChecksumsSubsection<'a>,
1325
    line_sections: Vec<DebugLinesSubsection<'a>>,
1326
}
1327
1328
impl<'a> LineProgram<'a> {
1329
43.1k
    pub(crate) fn parse(data: &'a [u8]) -> Result<Self> {
1330
43.1k
        let mut file_checksums = DebugFileChecksumsSubsection::default();
1331
43.1k
        let mut line_sections = Vec::new();
1332
1333
43.1k
        let mut section_iter = DebugSubsectionIterator::new(data);
1334
117k
        while let Some(sec) = section_iter.next()? {
1335
74.3k
            match sec.kind {
1336
23.8k
                DebugSubsectionKind::FileChecksums => {
1337
23.8k
                    file_checksums = DebugFileChecksumsSubsection::new(sec.data);
1338
23.8k
                }
1339
                DebugSubsectionKind::Lines => {
1340
42.7k
                    line_sections.push(DebugLinesSubsection::parse(sec.data)?);
1341
                }
1342
7.76k
                _ => {}
1343
            }
1344
        }
1345
1346
37.1k
        line_sections.sort_unstable_by_key(Self::lines_key);
1347
1348
37.1k
        Ok(Self {
1349
37.1k
            file_checksums,
1350
37.1k
            line_sections,
1351
37.1k
        })
1352
43.1k
    }
1353
1354
0
    pub(crate) fn lines(&self) -> LineIterator<'_> {
1355
0
        LineIterator {
1356
0
            sections: self.line_sections.iter(),
1357
0
            blocks: DebugLinesBlockIterator::default(),
1358
0
            lines: DebugLinesIterator::default(),
1359
0
            columns: DebugColumnsIterator::default(),
1360
0
            last_info: None,
1361
0
        }
1362
0
    }
1363
1364
24.6k
    pub(crate) fn lines_for_symbol(&self, offset: PdbInternalSectionOffset) -> LineIterator<'_> {
1365
        // Search for the lines subsection that covers the given offset. They are non-overlapping
1366
        // and not empty, so there will be at most one match. In most cases, there will be an exact
1367
        // match for each symbol. However, ASM sometimes yields line records outside of the stated
1368
        // symbol range `[offset, offset+len)`. In this case, search for the section covering the
1369
        // offset.
1370
24.6k
        let key = Self::lines_offset_key(offset);
1371
24.6k
        let index_result = self
1372
24.6k
            .line_sections
1373
24.6k
            .binary_search_by_key(&key, Self::lines_key);
1374
1375
24.6k
        let section = match index_result {
1376
2.20k
            Err(0) => return LineIterator::default(),
1377
1.57k
            Err(i) => self.line_sections[i - 1],
1378
20.8k
            Ok(i) => self.line_sections[i],
1379
        };
1380
1381
        // In the `Err(i)` case, we might have chosen a lines subsection pointing into a different
1382
        // section. In this case, bail out.
1383
22.4k
        if section.header.offset.section != offset.section {
1384
0
            return LineIterator::default();
1385
22.4k
        }
1386
1387
22.4k
        LineIterator {
1388
22.4k
            sections: [].iter(),
1389
22.4k
            blocks: section.blocks(),
1390
22.4k
            lines: DebugLinesIterator::default(),
1391
22.4k
            columns: DebugColumnsIterator::default(),
1392
22.4k
            last_info: None,
1393
22.4k
        }
1394
24.6k
    }
1395
1396
18.4k
    pub(crate) fn files(&self) -> FileIterator<'a> {
1397
18.4k
        FileIterator {
1398
18.4k
            checksums: self.file_checksums.entries().unwrap_or_default(),
1399
18.4k
        }
1400
18.4k
    }
1401
1402
275k
    pub(crate) fn get_file_info(&self, index: FileIndex) -> Result<FileInfo<'a>> {
1403
        // The file index actually contains the byte offset value into the file_checksums
1404
        // subsection. Therefore, treat it as the offset.
1405
275k
        let mut entries = self.file_checksums.entries_at_offset(index)?;
1406
275k
        let entry = entries
1407
275k
            .next()?
1408
275k
            .ok_or(Error::InvalidFileChecksumOffset(index.0))?;
1409
1410
275k
        Ok(FileInfo {
1411
275k
            name: entry.name,
1412
275k
            checksum: entry.checksum,
1413
275k
        })
1414
275k
    }
1415
1416
280k
    fn lines_offset_key(offset: PdbInternalSectionOffset) -> (u16, u32) {
1417
280k
        (offset.section, offset.offset)
1418
280k
    }
1419
1420
255k
    fn lines_key(lines: &DebugLinesSubsection<'_>) -> (u16, u32) {
1421
255k
        Self::lines_offset_key(lines.header.offset)
1422
255k
    }
1423
}
1424
1425
#[cfg(test)]
1426
mod tests {
1427
    use super::*;
1428
1429
    use crate::symbol::BinaryAnnotations;
1430
1431
    #[test]
1432
    fn test_line_number_header() {
1433
        assert_eq!(mem::size_of::<LineNumberHeader>(), 8);
1434
        assert_eq!(mem::align_of::<LineNumberHeader>(), 4);
1435
    }
1436
1437
    #[test]
1438
    fn test_column_number_header() {
1439
        assert_eq!(mem::size_of::<ColumnNumberEntry>(), 4);
1440
        assert_eq!(mem::align_of::<ColumnNumberEntry>(), 2);
1441
    }
1442
1443
    #[test]
1444
    fn test_debug_lines_block_header() {
1445
        assert_eq!(mem::size_of::<DebugLinesBlockHeader>(), 12);
1446
        assert_eq!(mem::align_of::<DebugLinesBlockHeader>(), 4);
1447
    }
1448
1449
    #[test]
1450
    fn test_raw_cross_scope_export() {
1451
        assert_eq!(mem::size_of::<RawCrossScopeExport>(), 8);
1452
        assert_eq!(mem::align_of::<RawCrossScopeExport>(), 4);
1453
    }
1454
1455
    #[test]
1456
    fn test_iter_lines() {
1457
        let data = &[
1458
            244, 0, 0, 0, 24, 0, 0, 0, 169, 49, 0, 0, 16, 1, 115, 121, 2, 198, 45, 116, 88, 98,
1459
            157, 13, 221, 82, 225, 34, 192, 51, 0, 0, 242, 0, 0, 0, 48, 0, 0, 0, 132, 160, 0, 0, 1,
1460
            0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 128,
1461
            0, 0, 0, 0, 23, 0, 0, 128, 11, 0, 0, 0, 24, 0, 0, 128,
1462
        ];
1463
1464
        let line_program = LineProgram::parse(data).expect("parse line program");
1465
        let lines: Vec<_> = line_program.lines().collect().expect("collect lines");
1466
1467
        let expected = [
1468
            LineInfo {
1469
                offset: PdbInternalSectionOffset {
1470
                    section: 0x1,
1471
                    offset: 0xa084,
1472
                },
1473
                length: Some(0),
1474
                file_index: FileIndex(0x0),
1475
                line_start: 22,
1476
                line_end: 22,
1477
                column_start: None,
1478
                column_end: None,
1479
                kind: LineInfoKind::Statement,
1480
            },
1481
            LineInfo {
1482
                offset: PdbInternalSectionOffset {
1483
                    section: 0x1,
1484
                    offset: 0xa084,
1485
                },
1486
                length: Some(11),
1487
                file_index: FileIndex(0x0),
1488
                line_start: 23,
1489
                line_end: 23,
1490
                column_start: None,
1491
                column_end: None,
1492
                kind: LineInfoKind::Statement,
1493
            },
1494
            LineInfo {
1495
                offset: PdbInternalSectionOffset {
1496
                    section: 0x1,
1497
                    offset: 0xa08f,
1498
                },
1499
                length: Some(1),
1500
                file_index: FileIndex(0x0),
1501
                line_start: 24,
1502
                line_end: 24,
1503
                column_start: None,
1504
                column_end: None,
1505
                kind: LineInfoKind::Statement,
1506
            },
1507
        ];
1508
1509
        assert_eq!(lines, expected);
1510
    }
1511
1512
    #[test]
1513
    fn test_lines_for_symbol() {
1514
        let data = &[
1515
            244, 0, 0, 0, 24, 0, 0, 0, 169, 49, 0, 0, 16, 1, 115, 121, 2, 198, 45, 116, 88, 98,
1516
            157, 13, 221, 82, 225, 34, 192, 51, 0, 0, 242, 0, 0, 0, 48, 0, 0, 0, 132, 160, 0, 0, 1,
1517
            0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 36, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 128,
1518
            0, 0, 0, 0, 23, 0, 0, 128, 11, 0, 0, 0, 24, 0, 0, 128,
1519
        ];
1520
1521
        let offset = PdbInternalSectionOffset {
1522
            section: 0x0001,
1523
            offset: 0xa084,
1524
        };
1525
1526
        let line_program = LineProgram::parse(data).expect("parse line program");
1527
        let line = line_program
1528
            .lines_for_symbol(offset)
1529
            .next()
1530
            .expect("get line");
1531
1532
        let expected = Some(LineInfo {
1533
            offset: PdbInternalSectionOffset {
1534
                section: 0x1,
1535
                offset: 0xa084,
1536
            },
1537
            length: Some(0),
1538
            file_index: FileIndex(0x0),
1539
            line_start: 22,
1540
            line_end: 22,
1541
            column_start: None,
1542
            column_end: None,
1543
            kind: LineInfoKind::Statement,
1544
        });
1545
1546
        assert_eq!(expected, line);
1547
    }
1548
1549
    #[test]
1550
    fn test_lines_for_symbol_asm() {
1551
        // This test is similar to lines_for_symbol, but it tests with an offset that points beyond
1552
        // the beginning of a lines subsection. This happens when dealing with MASM.
1553
1554
        let data = &[
1555
            244, 0, 0, 0, 96, 0, 0, 0, 177, 44, 0, 0, 16, 1, 148, 43, 19, 100, 121, 95, 165, 113,
1556
            45, 169, 112, 53, 233, 149, 174, 133, 0, 0, 248, 44, 0, 0, 16, 1, 54, 176, 28, 14, 163,
1557
            149, 3, 189, 0, 215, 91, 24, 204, 45, 117, 241, 0, 0, 59, 45, 0, 0, 16, 1, 191, 40,
1558
            129, 240, 15, 71, 114, 239, 184, 146, 206, 88, 119, 218, 136, 139, 0, 0, 126, 45, 0, 0,
1559
            16, 1, 175, 252, 248, 34, 196, 152, 31, 107, 144, 61, 83, 41, 122, 95, 140, 123, 0, 0,
1560
            242, 0, 0, 0, 96, 0, 0, 0, 112, 137, 0, 0, 1, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 0, 9, 0,
1561
            0, 0, 84, 0, 0, 0, 16, 0, 0, 0, 45, 0, 0, 128, 16, 0, 0, 0, 47, 0, 0, 128, 23, 0, 0, 0,
1562
            48, 0, 0, 128, 26, 0, 0, 0, 49, 0, 0, 128, 30, 0, 0, 0, 50, 0, 0, 128, 35, 0, 0, 0, 51,
1563
            0, 0, 128, 38, 0, 0, 0, 52, 0, 0, 128, 40, 0, 0, 0, 62, 0, 0, 128, 44, 0, 0, 0, 66, 0,
1564
            0, 128,
1565
        ];
1566
1567
        let offset = PdbInternalSectionOffset {
1568
            section: 0x0001,
1569
            offset: 0x8990, // XXX: section and first line record at 0x0980
1570
        };
1571
1572
        let line_program = LineProgram::parse(data).expect("parse line program");
1573
        let line = line_program
1574
            .lines_for_symbol(offset)
1575
            .next()
1576
            .expect("get line");
1577
1578
        let expected = Some(LineInfo {
1579
            offset: PdbInternalSectionOffset {
1580
                section: 0x1,
1581
                offset: 0x8980,
1582
            },
1583
            length: Some(0),
1584
            file_index: FileIndex(0x0),
1585
            line_start: 45,
1586
            line_end: 45,
1587
            column_start: None,
1588
            column_end: None,
1589
            kind: LineInfoKind::Statement,
1590
        });
1591
1592
        assert_eq!(expected, line);
1593
    }
1594
1595
    #[test]
1596
    fn test_parse_inlinee_lines() {
1597
        let data = &[
1598
            0, 0, 0, 0, 254, 18, 0, 0, 104, 1, 0, 0, 24, 0, 0, 0, 253, 18, 0, 0, 104, 1, 0, 0, 28,
1599
            0, 0, 0,
1600
        ];
1601
1602
        let inlinee_lines = DebugInlineeLinesSubsection::parse(data).expect("parse inlinee lines");
1603
        assert!(!inlinee_lines.header.has_extra_files());
1604
1605
        let lines: Vec<_> = inlinee_lines
1606
            .lines()
1607
            .collect()
1608
            .expect("collect inlinee lines");
1609
1610
        let expected = [
1611
            InlineeSourceLine {
1612
                inlinee: IdIndex(0x12FE),
1613
                file_id: FileIndex(0x168),
1614
                line: 24,
1615
                extra_files: &[],
1616
            },
1617
            InlineeSourceLine {
1618
                inlinee: IdIndex(0x12FD),
1619
                file_id: FileIndex(0x168),
1620
                line: 28,
1621
                extra_files: &[],
1622
            },
1623
        ];
1624
1625
        assert_eq!(lines, expected);
1626
    }
1627
1628
    #[test]
1629
    fn test_parse_inlinee_lines_with_files() {
1630
        let data = &[
1631
            1, 0, 0, 0, 235, 102, 9, 0, 232, 37, 0, 0, 19, 0, 0, 0, 1, 0, 0, 0, 216, 26, 0, 0, 240,
1632
            163, 7, 0, 176, 44, 0, 0, 120, 0, 0, 0, 1, 0, 0, 0, 120, 3, 0, 0,
1633
        ];
1634
1635
        let inlinee_lines = DebugInlineeLinesSubsection::parse(data).expect("parse inlinee lines");
1636
        assert!(inlinee_lines.header.has_extra_files());
1637
1638
        let lines: Vec<_> = inlinee_lines
1639
            .lines()
1640
            .collect()
1641
            .expect("collect inlinee lines");
1642
1643
        let expected = [
1644
            InlineeSourceLine {
1645
                inlinee: IdIndex(0x966EB),
1646
                file_id: FileIndex(0x25e8),
1647
                line: 19,
1648
                extra_files: &[216, 26, 0, 0],
1649
            },
1650
            InlineeSourceLine {
1651
                inlinee: IdIndex(0x7A3F0),
1652
                file_id: FileIndex(0x2cb0),
1653
                line: 120,
1654
                extra_files: &[120, 3, 0, 0],
1655
            },
1656
        ];
1657
1658
        assert_eq!(lines, expected)
1659
    }
1660
1661
    #[test]
1662
    fn test_inlinee_lines() {
1663
        // Obtained from a PDB compiling Breakpad's crash_generation_client.obj
1664
1665
        // S_GPROC32: [0001:00000120], Cb: 00000054
1666
        //   S_INLINESITE: Parent: 0000009C, End: 00000318, Inlinee:             0x1173
1667
        //     S_INLINESITE: Parent: 00000190, End: 000001EC, Inlinee:             0x1180
1668
        //     BinaryAnnotations:    CodeLengthAndCodeOffset 2 3f  CodeLengthAndCodeOffset 3 9
1669
        let inline_site = InlineSiteSymbol {
1670
            parent: Some(SymbolIndex(0x190)),
1671
            end: SymbolIndex(0x1ec),
1672
            inlinee: IdIndex(0x1180),
1673
            invocations: None,
1674
            annotations: BinaryAnnotations::new(&[12, 2, 63, 12, 3, 9, 0, 0]),
1675
        };
1676
1677
        // Inline site from corresponding DEBUG_S_INLINEELINES subsection:
1678
        let inlinee_line = InlineeSourceLine {
1679
            inlinee: IdIndex(0x1180),
1680
            file_id: FileIndex(0x270),
1681
            line: 341,
1682
            extra_files: &[],
1683
        };
1684
1685
        // Parent offset from procedure root:
1686
        // S_GPROC32: [0001:00000120]
1687
        let parent_offset = PdbInternalSectionOffset {
1688
            offset: 0x120,
1689
            section: 0x1,
1690
        };
1691
1692
        let iter = InlineeLineIterator::new(parent_offset, &inline_site, inlinee_line);
1693
        let lines: Vec<_> = iter.collect().expect("collect inlinee lines");
1694
1695
        let expected = [
1696
            LineInfo {
1697
                offset: PdbInternalSectionOffset {
1698
                    section: 0x1,
1699
                    offset: 0x015f,
1700
                },
1701
                length: Some(2),
1702
                file_index: FileIndex(0x270),
1703
                line_start: 341,
1704
                line_end: 342,
1705
                column_start: None,
1706
                column_end: None,
1707
                kind: LineInfoKind::Statement,
1708
            },
1709
            LineInfo {
1710
                offset: PdbInternalSectionOffset {
1711
                    section: 0x1,
1712
                    offset: 0x0168,
1713
                },
1714
                length: Some(3),
1715
                file_index: FileIndex(0x270),
1716
                line_start: 341,
1717
                line_end: 342,
1718
                column_start: None,
1719
                column_end: None,
1720
                kind: LineInfoKind::Statement,
1721
            },
1722
        ];
1723
1724
        assert_eq!(lines, expected);
1725
    }
1726
1727
    #[test]
1728
    fn test_inlinee_lines_length() {
1729
        // Obtained from xul.pdb:
1730
        // https://symbols.mozilla.org/xul.pdb/5DCA9FFE1E8BC7FE4C4C44205044422E1/xul.pd_
1731
        //
1732
        // 1. Rename to `xul.pdb.cab` and extract with `cabextract`
1733
        // 2. Get procedure at SymbolIndex(0x3e3c7f4)
1734
        // 3. Get inlinee   at SymbolIndex(0x3e51b04)
1735
1736
        let inline_site = InlineSiteSymbol {
1737
            parent: Some(SymbolIndex(0x03e5_14dc)),
1738
            end: SymbolIndex(0x03e5_1bd0),
1739
            inlinee: IdIndex(0xeb476),
1740
            invocations: None,
1741
            annotations: BinaryAnnotations::new(&[6, 38, 3, 186, 32, 11, 71, 11, 36, 4, 5, 0]),
1742
        };
1743
1744
        // Binary annotations:
1745
        //   ChangeLineOffset(19),
1746
        //   ChangeCodeOffset(14880),
1747
        //   ChangeCodeOffsetAndLineOffset(7, 2),
1748
        //   ChangeCodeOffsetAndLineOffset(4, 1),
1749
        //   ChangeCodeLength(5),
1750
1751
        let inlinee_line = InlineeSourceLine {
1752
            inlinee: IdIndex(0xeb476),
1753
            file_id: FileIndex(0x590),
1754
            line: 499,
1755
            extra_files: &[],
1756
        };
1757
1758
        let parent_offset = PdbInternalSectionOffset {
1759
            section: 0x1,
1760
            offset: 0x0453_f100,
1761
        };
1762
1763
        let iter = InlineeLineIterator::new(parent_offset, &inline_site, inlinee_line);
1764
        let lines: Vec<_> = iter.collect().expect("collect inlinee lines");
1765
1766
        let expected = [
1767
            LineInfo {
1768
                offset: PdbInternalSectionOffset {
1769
                    section: 0x1,
1770
                    offset: 0x0454_2b20,
1771
                },
1772
                length: Some(7),
1773
                file_index: FileIndex(0x590),
1774
                line_start: 518,
1775
                line_end: 519,
1776
                column_start: None,
1777
                column_end: None,
1778
                kind: LineInfoKind::Statement,
1779
            },
1780
            LineInfo {
1781
                offset: PdbInternalSectionOffset {
1782
                    section: 0x1,
1783
                    offset: 0x0454_2b27,
1784
                },
1785
                length: Some(4),
1786
                file_index: FileIndex(0x590),
1787
                line_start: 520,
1788
                line_end: 521,
1789
                column_start: None,
1790
                column_end: None,
1791
                kind: LineInfoKind::Statement,
1792
            },
1793
            LineInfo {
1794
                offset: PdbInternalSectionOffset {
1795
                    section: 0x1,
1796
                    offset: 0x0454_2b2b,
1797
                },
1798
                length: Some(5),
1799
                file_index: FileIndex(0x590),
1800
                line_start: 521,
1801
                line_end: 522,
1802
                column_start: None,
1803
                column_end: None,
1804
                kind: LineInfoKind::Statement,
1805
            },
1806
        ];
1807
1808
        assert_eq!(lines, expected);
1809
    }
1810
1811
    #[repr(align(4))]
1812
    struct Align4<T>(T);
1813
1814
    /// Aligned data for parsing cross module imports.
1815
    ///
1816
    /// When parsing them from the file, alignment is validated during ruintime using
1817
    /// `cast_aligned`. If alignment is validated, it throws an error.
1818
    const CROSS_MODULE_IMPORT_DATA: Align4<[u8; 76]> = Align4([
1819
        // module 0
1820
        189, 44, 0, 0, // module name 2CBD
1821
        14, 0, 0, 0, // 14 imports (all IDs, no Types)
1822
        171, 19, 0, 128, // 800013AB
1823
        37, 20, 0, 128, // 80001425
1824
        161, 19, 0, 128, // 800013A1
1825
        90, 20, 0, 128, // 8000145A
1826
        159, 19, 0, 128, // 8000139F
1827
        55, 20, 0, 128, // 80001437
1828
        109, 17, 0, 128, // 8000116D
1829
        238, 17, 0, 128, // 800011EE
1830
        246, 19, 0, 128, // 800013F6
1831
        69, 20, 0, 128, // 80001445
1832
        104, 19, 0, 128, // 80001368
1833
        148, 20, 0, 128, // 80001494
1834
        195, 20, 0, 128, // 800014C3
1835
        219, 20, 0, 128, // 800014DB
1836
        // module 1
1837
        21, 222, 0, 0, // module name DE15
1838
        1, 0, 0, 0, // 1 import (id)
1839
        96, 22, 0, 128, // 80001660
1840
    ]);
1841
1842
    #[test]
1843
    fn test_parse_cross_section_imports() {
1844
        let sec = DebugCrossScopeImportsSubsection::new(&CROSS_MODULE_IMPORT_DATA.0);
1845
1846
        let modules: Vec<_> = sec.modules().collect().expect("collect imports");
1847
        assert_eq!(modules.len(), 2);
1848
1849
        let module = modules[0];
1850
        assert_eq!(module.get(0), Some(Local(IdIndex(0x8000_13AB))));
1851
        assert_eq!(module.get(13), Some(Local(IdIndex(0x8000_14DB))));
1852
        assert_eq!(module.get::<IdIndex>(14), None);
1853
    }
1854
1855
    #[test]
1856
    fn test_resolve_cross_module_import() {
1857
        let sec = DebugCrossScopeImportsSubsection::new(&CROSS_MODULE_IMPORT_DATA.0);
1858
1859
        let imports = CrossModuleImports::from_section(sec).expect("parse section");
1860
        let cross_ref = imports
1861
            .resolve_import(IdIndex(0x8000_000A))
1862
            .expect("resolve import");
1863
1864
        let expected = CrossModuleRef(
1865
            // The module index is 0x000 = 1st module.
1866
            ModuleRef(StringRef(0x2CBD)),
1867
            // The import index is 0x0000A = 11th element.
1868
            Local(IdIndex(0x8000_1368)),
1869
        );
1870
1871
        assert_eq!(cross_ref, expected);
1872
    }
1873
1874
    #[test]
1875
    fn test_resolve_cross_module_import2() {
1876
        let sec = DebugCrossScopeImportsSubsection::new(&CROSS_MODULE_IMPORT_DATA.0);
1877
1878
        let imports = CrossModuleImports::from_section(sec).expect("parse section");
1879
        let cross_ref = imports
1880
            .resolve_import(IdIndex(0x8010_0000))
1881
            .expect("resolve import");
1882
1883
        let expected = CrossModuleRef(
1884
            // The module index is 0x001 = 2nd module.
1885
            ModuleRef(StringRef(0xDE15)),
1886
            // The import index is 0x00001 = 1st element.
1887
            Local(IdIndex(0x8000_1660)),
1888
        );
1889
1890
        assert_eq!(cross_ref, expected);
1891
    }
1892
1893
    const CROSS_MODULE_EXPORT_DATA: Align4<[u8; 32]> = Align4([
1894
        31, 16, 0, 0, 12, 16, 0, 0, // 101F -> 100C
1895
        32, 16, 0, 0, 79, 34, 0, 0, // 1020 -> 224F
1896
        92, 17, 0, 128, 97, 17, 0, 0, // 8000115C -> 1161
1897
        109, 17, 0, 128, 98, 17, 0, 0, // 8000116D -> 1162
1898
    ]);
1899
1900
    #[test]
1901
    fn test_iter_cross_module_exports() {
1902
        let section = DebugCrossScopeExportsSubsection::parse(&CROSS_MODULE_EXPORT_DATA.0)
1903
            .expect("parse exports");
1904
        let exports = CrossModuleExports::from_section(section).expect("parse section");
1905
1906
        let exports: Vec<_> = exports.exports().collect().expect("collect exports");
1907
1908
        let expected = [
1909
            CrossModuleExport::Type(Local(TypeIndex(0x101F)), TypeIndex(0x100C)),
1910
            CrossModuleExport::Type(Local(TypeIndex(0x1020)), TypeIndex(0x224F)),
1911
            CrossModuleExport::Id(Local(IdIndex(0x8000_115C)), IdIndex(0x1161)),
1912
            CrossModuleExport::Id(Local(IdIndex(0x8000_116D)), IdIndex(0x1162)),
1913
        ];
1914
1915
        assert_eq!(exports, expected);
1916
    }
1917
1918
    #[test]
1919
    fn test_resolve_cross_module_ref() {
1920
        let section = DebugCrossScopeExportsSubsection::parse(&CROSS_MODULE_EXPORT_DATA.0)
1921
            .expect("parse exports");
1922
        let exports = CrossModuleExports::from_section(section).expect("parse section");
1923
1924
        let type_index = exports
1925
            .resolve_import(Local(TypeIndex(0x101F)))
1926
            .expect("resolve type");
1927
        assert_eq!(type_index, Some(TypeIndex(0x100C)));
1928
1929
        let id_index = exports
1930
            .resolve_import(Local(IdIndex(0x8000_115C)))
1931
            .expect("resolve id");
1932
        assert_eq!(id_index, Some(IdIndex(0x1161)));
1933
1934
        let missing_index = exports
1935
            .resolve_import(Local(TypeIndex(0xFEED)))
1936
            .expect("resolve missing");
1937
        assert_eq!(missing_index, None);
1938
    }
1939
}