/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 | | } |