pdb/modi/
c13.rs

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