1 // Copyright 2018 pdb Developers
2 //
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
7 
8 //! Utilities for translating addresses between PDB offsets and _Relative Virtual Addresses_ (RVAs).
9 
10 use std::cmp::{self, Ordering};
11 use std::fmt;
12 use std::iter::FusedIterator;
13 use std::mem;
14 use std::ops::Range;
15 
16 use crate::common::*;
17 use crate::msf::Stream;
18 use crate::pe::ImageSectionHeader;
19 
20 /// A address translation record from an `OMAPTable`.
21 ///
22 /// This record applies to the half-open interval [ `record.source_address`,
23 /// `next_record.source_address` ).
24 #[repr(C)]
25 #[derive(Clone, Copy, Eq, PartialEq)]
26 pub(crate) struct OMAPRecord {
27     source_address: u32,
28     target_address: u32,
29 }
30 
31 impl OMAPRecord {
32     /// Create a new OMAP record for the given mapping.
new(source_address: u32, target_address: u32) -> Self33     pub fn new(source_address: u32, target_address: u32) -> Self {
34         OMAPRecord {
35             source_address: source_address.to_le(),
36             target_address: target_address.to_le(),
37         }
38     }
39 
40     /// Returns the address in the source space.
41     #[inline]
source_address(self) -> u3242     pub fn source_address(self) -> u32 {
43         u32::from_le(self.source_address)
44     }
45 
46     /// Returns the start of the mapped portion in the target address space.
47     #[inline]
target_address(self) -> u3248     pub fn target_address(self) -> u32 {
49         u32::from_le(self.target_address)
50     }
51 
52     /// Translate the given address into the target address space.
53     #[inline]
translate(self, address: u32) -> u3254     fn translate(self, address: u32) -> u32 {
55         // This method is only to be used internally by the OMAP iterator and lookups. The caller
56         // must verify that the record is valid to translate an address.
57         debug_assert!(self.source_address() <= address);
58         (address - self.source_address()) + self.target_address()
59     }
60 }
61 
62 impl fmt::Debug for OMAPRecord {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result63     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64         f.debug_struct("OMAPRecord")
65             .field(
66                 "source_address",
67                 &format_args!("{:#010x}", self.source_address()),
68             )
69             .field(
70                 "target_address",
71                 &format_args!("{:#010x}", self.target_address()),
72             )
73             .finish()
74     }
75 }
76 
77 impl PartialOrd for OMAPRecord {
78     #[inline]
partial_cmp(&self, other: &Self) -> Option<Ordering>79     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80         self.source_address().partial_cmp(&other.source_address())
81     }
82 }
83 
84 impl Ord for OMAPRecord {
85     #[inline]
cmp(&self, other: &Self) -> Ordering86     fn cmp(&self, other: &Self) -> Ordering {
87         self.source_address().cmp(&other.source_address())
88     }
89 }
90 
91 /// PDBs can contain OMAP tables, which translate relative virtual addresses (RVAs) from one address
92 /// space into another.
93 ///
94 /// For more information on the pratical use of OMAPs, see the [module level documentation] and
95 /// [`AddressMap`]. A PDB can contain two OMAPs:
96 ///
97 ///  - `omap_from_src`: A mapping from the original address space to the transformed address space
98 ///    of an optimized binary. Use `PDB::omap_from_src` to obtain an instance of this OMAP. Also,
99 ///    `PdbInternalRva::rva` performs this conversion in a safe manner.
100 ///  - `omap_to_src`: A mapping from the transformed address space back into the original address
101 ///    space of the unoptimized binary. Use `PDB::omap_to_src` to obtain an instace of this OMAP.
102 ///    Also, `Rva::original_rva` performs this conversion in a safe manner.
103 ///
104 /// # Structure
105 ///
106 /// OMAP tables are dense arrays, sequentially storing `OMAPRecord` structs sorted by source
107 /// address.
108 ///
109 /// Each record applies to a range of addresses: i.e. record N indicates that addresses in the
110 /// half-open interval [ `record[n].source_address`, `record[n+1].source_address` ) were relocated
111 /// to a starting address of `record[n].target_address`. If `target_address` is zero, the `lookup()`
112 /// will return None, since this indicates a non-existent location in the target address space.
113 ///
114 /// Given that the table is sorted, lookups by source address can be efficiently serviced using a
115 /// binary search directly against the underlying data without secondary data structures. This is
116 /// not the most cache efficient data structure (especially given that half of each cache line is
117 /// storing target addresses), but given that OMAP tables are an uncommon PDBs feature, the obvious
118 /// binary search implementation seems appropriate.
119 ///
120 /// [module level documentation]: self
121 pub(crate) struct OMAPTable<'s> {
122     stream: Stream<'s>,
123 }
124 
125 impl<'s> OMAPTable<'s> {
parse(stream: Stream<'s>) -> Result<Self>126     pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> {
127         match cast_aligned::<OMAPRecord>(stream.as_slice()) {
128             Some(_) => Ok(OMAPTable { stream }),
129             None => Err(Error::InvalidStreamLength("OMAP")),
130         }
131     }
132 
133     /// Returns a direct view onto the records stored in this OMAP table.
134     #[inline]
records(&self) -> &[OMAPRecord]135     pub fn records(&self) -> &[OMAPRecord] {
136         // alignment is checked during parsing, unwrap is safe.
137         cast_aligned(self.stream.as_slice()).unwrap()
138     }
139 
140     /// Look up `source_address` to yield a target address.
lookup(&self, source_address: u32) -> Option<u32>141     pub fn lookup(&self, source_address: u32) -> Option<u32> {
142         let records = self.records();
143 
144         let index = match records.binary_search_by_key(&source_address, |r| r.source_address()) {
145             Ok(i) => i,
146             Err(0) => return None,
147             Err(i) => i - 1,
148         };
149 
150         let record = records[index];
151 
152         // As a special case, `target_address` can be zero, which indicates that the
153         // `source_address` does not exist in the target address space.
154         if record.target_address() == 0 {
155             return None;
156         }
157 
158         Some(record.translate(source_address))
159     }
160 
161     /// Look up a the range `start..end` and iterate all mapped sub-ranges.
lookup_range(&self, range: Range<u32>) -> RangeIter<'_>162     pub fn lookup_range(&self, range: Range<u32>) -> RangeIter<'_> {
163         let Range { start, end } = range;
164         if end <= start {
165             return RangeIter::empty();
166         }
167 
168         let records = self.records();
169         let (record, next) = match records.binary_search_by_key(&start, |r| r.source_address()) {
170             Ok(i) => (records[i], &records[i + 1..]),
171             // Insert a dummy record no indicate that the range before the first record is invalid.
172             // The range might still overlap with the first record however, so attempt regular
173             // iteration.
174             Err(0) => (OMAPRecord::new(0, 0), records),
175             Err(i) => (records[i - 1], &records[i..]),
176         };
177 
178         RangeIter {
179             records: next.iter(),
180             record,
181             addr: start,
182             end,
183         }
184     }
185 }
186 
187 impl fmt::Debug for OMAPTable<'_> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result188     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189         f.debug_tuple("OMAPTable").field(&self.records()).finish()
190     }
191 }
192 
193 /// An iterator over mapped target ranges in an OMAP.
194 pub(crate) struct RangeIter<'t> {
195     /// Iterator over subsequent OMAP records.
196     records: std::slice::Iter<'t, OMAPRecord>,
197     /// The record that spans the current start address.
198     record: OMAPRecord,
199     /// The start address of the current subrange.
200     addr: u32,
201     /// The final end address of the (last sub-)range.
202     end: u32,
203 }
204 
205 impl<'t> RangeIter<'t> {
206     /// Creates a `RangeIter` that does not yield any ranges.
empty() -> Self207     pub fn empty() -> Self {
208         RangeIter {
209             records: [].iter(),
210             record: OMAPRecord::new(0, 0),
211             addr: 0,
212             end: 0,
213         }
214     }
215 
216     /// Creates a `RangeIter` that only yields the specified range.
identity(range: Range<u32>) -> Self217     pub fn identity(range: Range<u32>) -> Self {
218         // Declare the range `start..` as valid with an identity mapping. We cannot use `0..` here
219         // since the target must be a non-zero value to be recognized as valid mapping. Since there
220         // are no further records, a single subrange `start..end` will be considered.
221         RangeIter {
222             records: [].iter(),
223             record: OMAPRecord::new(range.start, range.start),
224             addr: range.start,
225             end: range.end,
226         }
227     }
228 }
229 
230 impl Default for RangeIter<'_> {
default() -> Self231     fn default() -> Self {
232         Self::empty()
233     }
234 }
235 
236 impl Iterator for RangeIter<'_> {
237     type Item = Range<u32>;
238 
next(&mut self) -> Option<Self::Item>239     fn next(&mut self) -> Option<Self::Item> {
240         while self.addr < self.end {
241             // Pull the next record from the list. Since the current record is only valid up to the
242             // next one, this will determine the end of the current sub slice. If there are no more
243             // records, create an unmapped dummy record starting at the end of the source range.
244             let next_record = match self.records.next() {
245                 Some(record) => *record,
246                 None => OMAPRecord::new(self.end, 0),
247             };
248 
249             // Calculate the bounds of the current subrange and write it back for the next
250             // iteration. Likewise, remember the next record as address translation base.
251             let subrange_end = cmp::min(next_record.source_address(), self.end);
252             let subrange_start = mem::replace(&mut self.addr, subrange_end);
253             let last_record = mem::replace(&mut self.record, next_record);
254 
255             // Check for the validity of this sub-range or skip it silently:
256             //  2. The sub range covered by the last OMAP record might be empty. This can be an
257             //     artifact of a dummy record used when creating a new iterator.
258             //  3. A `target_address` of zero indicates an unmapped address range.
259             if subrange_start >= subrange_end || last_record.target_address() == 0 {
260                 continue;
261             }
262 
263             let translated_start = last_record.translate(subrange_start);
264             let translated_end = last_record.translate(subrange_end);
265             return Some(translated_start..translated_end);
266         }
267 
268         None
269     }
270 }
271 
272 impl FusedIterator for RangeIter<'_> {}
273 
274 /// Iterator over [`Rva`] ranges returned by [`AddressMap::rva_ranges`].
275 pub struct RvaRangeIter<'t>(RangeIter<'t>);
276 
277 impl Iterator for RvaRangeIter<'_> {
278     type Item = Range<Rva>;
279 
next(&mut self) -> Option<Self::Item>280     fn next(&mut self) -> Option<Self::Item> {
281         self.0.next().map(|range| Rva(range.start)..Rva(range.end))
282     }
283 }
284 
285 impl FusedIterator for RvaRangeIter<'_> {}
286 
287 /// Iterator over [`PdbInternalRva`] ranges returned by [`AddressMap::internal_rva_ranges`].
288 pub struct PdbInternalRvaRangeIter<'t>(RangeIter<'t>);
289 
290 impl Iterator for PdbInternalRvaRangeIter<'_> {
291     type Item = Range<PdbInternalRva>;
292 
next(&mut self) -> Option<Self::Item>293     fn next(&mut self) -> Option<Self::Item> {
294         self.0
295             .next()
296             .map(|range| PdbInternalRva(range.start)..PdbInternalRva(range.end))
297     }
298 }
299 
300 impl FusedIterator for PdbInternalRvaRangeIter<'_> {}
301 
302 /// A mapping between addresses and offsets used in the PDB and PE file.
303 ///
304 /// To obtain an instace of this address map, call `PDB::address_map`. It will determine the correct
305 /// translation mode and read all internal state from the PDB. Then use the conversion methods on
306 /// the address and offset types to translate addresses.
307 ///
308 /// # Background
309 ///
310 /// Addresses in PDBs are stored as offsets into sections of the PE file. The `AddressMap` contains
311 /// the PE's section headers to translate between the offsets and virtual addresses relative to the
312 /// image base (RVAs).
313 ///
314 /// Additionally, Microsoft has been reordering the Windows system and application binaries to
315 /// optimize them for paging reduction, using a toolset reported to be derived from and/or built on
316 /// top of the [Vulcan research project]. Relatively little else is known about the tools or the
317 /// methods they use. Looking at Windows system binaries like `ntoskrnl.exe`, it is apparent that
318 /// their layout has been rearranged, and their respective symbol files contain _OMAP_ re-mapping
319 /// information. The [Microsoft Binary Technologies Projects] may be involved in this.
320 ///
321 /// The internals of this transformation are not well understood. According to [1997 reference
322 /// material]:
323 ///
324 /// > Yet another form of debug information is relatively new and undocumented, except for a few
325 /// > obscure references in `WINNT.H` and the Win32 SDK help. This type of information is known as
326 /// > OMAP. Apparently, as part of Microsoft's internal build procedure, small fragments of code in
327 /// > EXEs and DLLs are moved around to put the most commonly used code at the beginning of the code
328 /// > section. This presumably keeps the process memory working set as small as possible. However,
329 /// > when shifting around the blocks of code, the corresponding debug information isn't updated.
330 /// > Instead, OMAP information is created. It lets symbol table code translate between the original
331 /// > address in a symbol table and the modified address where the variable or line of code really
332 /// > exists in memory.
333 ///
334 /// # Usage
335 ///
336 /// To aid with translating addresses and offsets, this module exposes `AddressMap`, a helper that
337 /// contains all information to apply the correct translation of any kind of address or offset to
338 /// another. Due to the rearranging optimizations, there are four types involved:
339 ///
340 ///  - [`Rva`]: A _Relative Virtual Address_ in the actual binary. This address directly corresponds
341 ///    to instruction pointers seen in stack traces and symbol addresses reported by debuggers.
342 ///  - [`PdbInternalRva`]: An RVA as it would have appeared before the optimization. These RVAs are
343 ///    used in some places and can be converted to an `Rva` in the actual address space.
344 ///  - [`SectionOffset`]: An offset into a section of the actual binary. A `section` member of _n_
345 ///    refers to section _n - 1_, which makes a section number of _0_ a null pointer.
346 ///  - [`PdbInternalSectionOffset`]: An offset into a section of the original binary. These offsets
347 ///    are used throughout the PDB and can be converted to either `SectionOffset`, or directly to
348 ///    `Rva` in the actual address space.
349 ///
350 /// For binaries that have not been optimized that way, the `PdbInternal*` values are effectively
351 /// equal to their regular counterparts and the conversion between the two are no-ops. Address
352 /// translation still has to assume different address spaces, which is why there is no direct
353 /// conversion without an `AddressMap`.
354 ///
355 /// # Example
356 ///
357 /// ```rust
358 /// # use pdb::{Rva, FallibleIterator};
359 /// #
360 /// # fn test() -> pdb::Result<()> {
361 /// # let source = std::fs::File::open("fixtures/self/foo.pdb")?;
362 /// let mut pdb = pdb::PDB::open(source)?;
363 ///
364 /// // Compute the address map once and reuse it
365 /// let address_map = pdb.address_map()?;
366 ///
367 /// # let symbol_table = pdb.global_symbols()?;
368 /// # let symbol = symbol_table.iter().next()?.unwrap();
369 /// # match symbol.parse() { Ok(pdb::SymbolData::Public(pubsym)) => {
370 /// // Obtain some section offset, eg from a symbol, and convert it
371 /// match pubsym.offset.to_rva(&address_map) {
372 ///     Some(rva) => {
373 ///         println!("symbol is at {}", rva);
374 /// #       assert_eq!(rva, Rva(26048));
375 ///     }
376 ///     None => {
377 ///         println!("symbol refers to eliminated code");
378 /// #       panic!("symbol should exist");
379 ///     }
380 /// }
381 /// # } _ => unreachable!() }
382 /// # Ok(())
383 /// # }
384 /// # test().unwrap()
385 /// ```
386 ///
387 /// [Vulcan research project]: https://research.microsoft.com/pubs/69850/tr-2001-50.pdf
388 /// [Microsoft Binary Technologies Projects]: https://microsoft.com/windows/cse/bit_projects.mspx
389 /// [1997 reference material]: https://www.microsoft.com/msj/0597/hood0597.aspx
390 #[derive(Debug, Default)]
391 pub struct AddressMap<'s> {
392     pub(crate) original_sections: Vec<ImageSectionHeader>,
393     pub(crate) transformed_sections: Option<Vec<ImageSectionHeader>>,
394     pub(crate) transformed_to_original: Option<OMAPTable<'s>>,
395     pub(crate) original_to_transformed: Option<OMAPTable<'s>>,
396 }
397 
398 impl<'s> AddressMap<'s> {
399     /// Resolves actual ranges in the executable's address space.
400     ///
401     /// The given internal address range might be split up into multiple ranges in the executable.
402     /// This iterator traverses all mapped ranges in the order of the PDB-internal mapping. All
403     /// empty or eliminated ranges are skipped. Thus, the iterator might be empty even for non-empty
404     /// ranges.
rva_ranges(&self, range: Range<PdbInternalRva>) -> RvaRangeIter<'_>405     pub fn rva_ranges(&self, range: Range<PdbInternalRva>) -> RvaRangeIter<'_> {
406         RvaRangeIter(match self.original_to_transformed {
407             Some(ref omap) => omap.lookup_range(range.start.0..range.end.0),
408             None => RangeIter::identity(range.start.0..range.end.0),
409         })
410     }
411 
412     /// Resolves actual ranges in the executable's address space.
413     ///
414     /// The given address range might correspond to multiple ranges in the PDB-internal address
415     /// space. This iterator traverses all mapped ranges in the order of the actual RVA mapping.
416     /// This iterator might be empty even for non-empty ranges if no corresponding original range
417     /// can be found.
internal_rva_ranges(&self, range: Range<Rva>) -> PdbInternalRvaRangeIter<'_>418     pub fn internal_rva_ranges(&self, range: Range<Rva>) -> PdbInternalRvaRangeIter<'_> {
419         PdbInternalRvaRangeIter(match self.transformed_to_original {
420             Some(ref omap) => omap.lookup_range(range.start.0..range.end.0),
421             None => RangeIter::identity(range.start.0..range.end.0),
422         })
423     }
424 }
425 
get_section_offset(sections: &[ImageSectionHeader], address: u32) -> Option<(u16, u32)>426 fn get_section_offset(sections: &[ImageSectionHeader], address: u32) -> Option<(u16, u32)> {
427     // Section headers are sorted by virtual_address, so we only need to iterate until we exceed
428     // the desired address. Since the number of section headers is relatively low, a sequential
429     // search is the fastest option here.
430     let (index, section) = sections
431         .iter()
432         .take_while(|s| s.virtual_address <= address)
433         .enumerate()
434         .find(|(_, s)| address < s.virtual_address + s.size_of_raw_data)?;
435 
436     Some((index as u16 + 1, address - section.virtual_address))
437 }
438 
get_virtual_address(sections: &[ImageSectionHeader], section: u16, offset: u32) -> Option<u32>439 fn get_virtual_address(sections: &[ImageSectionHeader], section: u16, offset: u32) -> Option<u32> {
440     (section as usize)
441         .checked_sub(1)
442         .and_then(|i| sections.get(i))
443         .map(|section| section.virtual_address + offset)
444 }
445 
446 impl Rva {
447     /// Resolves a PDB-internal Relative Virtual Address.
448     ///
449     /// This address is not necessarily compatible with the executable's address space and should
450     /// therefore not be used for debugging purposes.
to_internal_rva(self, translator: &AddressMap<'_>) -> Option<PdbInternalRva>451     pub fn to_internal_rva(self, translator: &AddressMap<'_>) -> Option<PdbInternalRva> {
452         match translator.transformed_to_original {
453             Some(ref omap) => omap.lookup(self.0).map(PdbInternalRva),
454             None => Some(PdbInternalRva(self.0)),
455         }
456     }
457 
458     /// Resolves the section offset in the PE headers.
459     ///
460     /// This is an offset into PE section headers of the executable. To retrieve section offsets
461     /// used in the PDB, use [`to_internal_offset`](Self::to_internal_offset) instead.
to_section_offset(self, translator: &AddressMap<'_>) -> Option<SectionOffset>462     pub fn to_section_offset(self, translator: &AddressMap<'_>) -> Option<SectionOffset> {
463         let (section, offset) = match translator.transformed_sections {
464             Some(ref sections) => get_section_offset(sections, self.0)?,
465             None => get_section_offset(&translator.original_sections, self.0)?,
466         };
467 
468         Some(SectionOffset { section, offset })
469     }
470 
471     /// Resolves the PDB internal section offset.
472     ///
473     /// This is the offset value used in the PDB file. To index into the actual PE section headers,
474     /// use [`to_section_offset`](Self::to_section_offset) instead.
to_internal_offset( self, translator: &AddressMap<'_>, ) -> Option<PdbInternalSectionOffset>475     pub fn to_internal_offset(
476         self,
477         translator: &AddressMap<'_>,
478     ) -> Option<PdbInternalSectionOffset> {
479         self.to_internal_rva(translator)?
480             .to_internal_offset(translator)
481     }
482 }
483 
484 impl PdbInternalRva {
485     /// Resolves an actual Relative Virtual Address in the executable's address space.
to_rva(self, translator: &AddressMap<'_>) -> Option<Rva>486     pub fn to_rva(self, translator: &AddressMap<'_>) -> Option<Rva> {
487         match translator.original_to_transformed {
488             Some(ref omap) => omap.lookup(self.0).map(Rva),
489             None => Some(Rva(self.0)),
490         }
491     }
492 
493     /// Resolves the section offset in the PE headers.
494     ///
495     /// This is an offset into PE section headers of the executable. To retrieve section offsets
496     /// used in the PDB, use [`to_internal_offset`](Self::to_internal_offset) instead.
to_section_offset(self, translator: &AddressMap<'_>) -> Option<SectionOffset>497     pub fn to_section_offset(self, translator: &AddressMap<'_>) -> Option<SectionOffset> {
498         self.to_rva(translator)?.to_section_offset(translator)
499     }
500 
501     /// Resolves the PDB internal section offset.
502     ///
503     /// This is the offset value used in the PDB file. To index into the actual PE section headers,
504     /// use [`to_section_offset`](Self::to_section_offset) instead.
to_internal_offset( self, translator: &AddressMap<'_>, ) -> Option<PdbInternalSectionOffset>505     pub fn to_internal_offset(
506         self,
507         translator: &AddressMap<'_>,
508     ) -> Option<PdbInternalSectionOffset> {
509         let (section, offset) = get_section_offset(&translator.original_sections, self.0)?;
510         Some(PdbInternalSectionOffset { section, offset })
511     }
512 }
513 
514 impl SectionOffset {
515     /// Resolves an actual Relative Virtual Address in the executable's address space.
to_rva(self, translator: &AddressMap<'_>) -> Option<Rva>516     pub fn to_rva(self, translator: &AddressMap<'_>) -> Option<Rva> {
517         let address = match translator.transformed_sections {
518             Some(ref sections) => get_virtual_address(sections, self.section, self.offset)?,
519             None => get_virtual_address(&translator.original_sections, self.section, self.offset)?,
520         };
521 
522         Some(Rva(address))
523     }
524 
525     /// Resolves a PDB-internal Relative Virtual Address.
526     ///
527     /// This address is not necessarily compatible with the executable's address space and should
528     /// therefore not be used for debugging purposes.
to_internal_rva(self, translator: &AddressMap<'_>) -> Option<PdbInternalRva>529     pub fn to_internal_rva(self, translator: &AddressMap<'_>) -> Option<PdbInternalRva> {
530         self.to_rva(translator)?.to_internal_rva(translator)
531     }
532 
533     /// Resolves the PDB internal section offset.
to_internal_offset( self, translator: &AddressMap<'_>, ) -> Option<PdbInternalSectionOffset>534     pub fn to_internal_offset(
535         self,
536         translator: &AddressMap<'_>,
537     ) -> Option<PdbInternalSectionOffset> {
538         if translator.transformed_sections.is_none() {
539             // Fast path to avoid section table lookups
540             let Self { section, offset } = self;
541             return Some(PdbInternalSectionOffset { section, offset });
542         }
543 
544         self.to_internal_rva(translator)?
545             .to_internal_offset(translator)
546     }
547 }
548 
549 impl PdbInternalSectionOffset {
550     /// Resolves an actual Relative Virtual Address in the executable's address space.
to_rva(self, translator: &AddressMap<'_>) -> Option<Rva>551     pub fn to_rva(self, translator: &AddressMap<'_>) -> Option<Rva> {
552         self.to_internal_rva(translator)?.to_rva(translator)
553     }
554 
555     /// Resolves a PDB-internal Relative Virtual Address.
556     ///
557     /// This address is not necessarily compatible with the executable's address space and should
558     /// therefore not be used for debugging purposes.
to_internal_rva(self, translator: &AddressMap<'_>) -> Option<PdbInternalRva>559     pub fn to_internal_rva(self, translator: &AddressMap<'_>) -> Option<PdbInternalRva> {
560         get_virtual_address(&translator.original_sections, self.section, self.offset)
561             .map(PdbInternalRva)
562     }
563 
564     /// Resolves the section offset in the PE headers.
to_section_offset(self, translator: &AddressMap<'_>) -> Option<SectionOffset>565     pub fn to_section_offset(self, translator: &AddressMap<'_>) -> Option<SectionOffset> {
566         if translator.transformed_sections.is_none() {
567             // Fast path to avoid section table lookups
568             let Self { section, offset } = self;
569             return Some(SectionOffset { section, offset });
570         }
571 
572         self.to_rva(translator)?.to_section_offset(translator)
573     }
574 }
575 
576 #[cfg(test)]
577 mod tests {
578     use super::*;
579 
580     use std::mem;
581 
582     #[test]
test_omap_record()583     fn test_omap_record() {
584         assert_eq!(mem::size_of::<OMAPRecord>(), 8);
585         assert_eq!(mem::align_of::<OMAPRecord>(), 4);
586     }
587 
588     #[test]
test_get_virtual_address()589     fn test_get_virtual_address() {
590         let sections = vec![ImageSectionHeader {
591             virtual_address: 0x1000_0000,
592             ..Default::default()
593         }];
594 
595         assert_eq!(get_virtual_address(&sections, 1, 0x1234), Some(0x1000_1234));
596         assert_eq!(get_virtual_address(&sections, 2, 0x1234), None);
597 
598         // https://github.com/willglynn/pdb/issues/87
599         assert_eq!(get_virtual_address(&sections, 0, 0x1234), None);
600     }
601 }
602