1 use std::path::Path;
2 use std::sync::Once;
3 
4 use pdb::{FallibleIterator, PdbInternalRva, PdbInternalSectionOffset, Rva};
5 
6 // This test is intended to cover OMAP address translation:
7 //   https://github.com/willglynn/pdb/issues/17
8 
9 static DOWNLOADED: Once = Once::new();
open_file() -> std::fs::File10 fn open_file() -> std::fs::File {
11     let path = "fixtures/symbol_server/3844dbb920174967be7aa4a2c20430fa2-ntkrnlmp.pdb";
12     let url = "https://msdl.microsoft.com/download/symbols/ntkrnlmp.pdb/3844dbb920174967be7aa4a2c20430fa2/ntkrnlmp.pdb";
13 
14     DOWNLOADED.call_once(|| {
15         if !Path::new(path).exists() {
16             let mut response = reqwest::get(url).expect("GET request");
17             let mut destination = std::fs::File::create(path).expect("create PDB");
18             response.copy_to(&mut destination).expect("download");
19         }
20     });
21 
22     std::fs::File::open(path).expect("open PDB")
23 }
24 
25 #[test]
test_omap_section_zero()26 fn test_omap_section_zero() {
27     // https://github.com/willglynn/pdb/issues/87
28 
29     let mut pdb = pdb::PDB::open(open_file()).expect("opening pdb");
30 
31     let address = pdb::PdbInternalSectionOffset {
32         offset: 0,
33         section: 0x1234,
34     };
35 
36     let address_map = pdb.address_map().expect("address map");
37 
38     assert_eq!(address.to_rva(&address_map), None);
39 }
40 
41 #[test]
test_omap_symbol()42 fn test_omap_symbol() {
43     let mut pdb = pdb::PDB::open(open_file()).expect("opening pdb");
44 
45     let global_symbols = pdb.global_symbols().expect("global_symbols");
46 
47     // find the target symbol
48     let target_symbol = {
49         let target_name = pdb::RawString::from("NtWaitForSingleObject");
50         let mut iter = global_symbols.iter();
51         iter.find(|sym| {
52             let matches = sym
53                 .parse()
54                 .ok()
55                 .and_then(|d| d.name())
56                 .map_or(false, |n| n == target_name);
57             Ok(matches)
58         })
59         .expect("iterate symbols")
60         .expect("find target symbol")
61     };
62 
63     // extract the PublicSymbol data
64     let pubsym = match target_symbol.parse().expect("parse symbol") {
65         pdb::SymbolData::Public(pubsym) => pubsym,
66         _ => panic!("expected public symbol"),
67     };
68 
69     // ensure the symbol has the correct location
70     assert_eq!(
71         pubsym.offset,
72         PdbInternalSectionOffset {
73             section: 0xc,
74             offset: 0x0004_aeb0,
75         }
76     );
77 
78     // translate the segment offset to an RVA
79     let address_map = pdb.address_map().expect("address map");
80     assert_eq!(pubsym.offset.to_rva(&address_map), Some(Rva(0x0037_68c0)));
81     assert_eq!(
82         Rva(0x0037_68c0).to_internal_offset(&address_map),
83         Some(pubsym.offset)
84     );
85 }
86 
87 #[test]
test_omap_range()88 fn test_omap_range() {
89     let mut pdb = pdb::PDB::open(open_file()).expect("opening pdb");
90     let address_map = pdb.address_map().expect("address map");
91 
92     // Range partially covered by OMAPs
93     // [
94     //   OMAPRecord {
95     //       source_address: 0x000010aa,
96     //       target_address: 0x00015de6
97     //   },
98     //   OMAPRecord {
99     //       source_address: 0x000010bd,
100     //       target_address: 0x00000000
101     //   },
102     //   OMAPRecord {
103     //       source_address: 0x000010c4,
104     //       target_address: 0x0002da00
105     //   },
106     //   OMAPRecord {
107     //       source_address: 0x000010c8,
108     //       target_address: 0x0002da04
109     //   },
110     // ]
111     let start = PdbInternalRva(0x10b0);
112     let end = PdbInternalRva(0x10c6);
113 
114     assert_eq!(
115         address_map.rva_ranges(start..end).collect::<Vec<_>>(),
116         vec![
117             Rva(0x15dec)..Rva(0x15df9), // 0x10aa - 0x10bd
118             // 0x10bd - 0x10c4 omitted due to missing target address
119             Rva(0x2da00)..Rva(0x2da02), // 0x10c4 - 0x10c6
120         ],
121     );
122 
123     // Range starting outside OMAPs
124     // [
125     //   OMAPRecord {
126     //       source_address: 0x00001000,
127     //       target_address: 0x00000000
128     //   },
129     //   OMAPRecord {
130     //       source_address: 0x00001008,
131     //       target_address: 0x00015d44
132     //   },
133     // ]
134     let start = PdbInternalRva(0x0);
135     let end = PdbInternalRva(0x1010);
136     assert_eq!(
137         address_map.rva_ranges(start..end).collect::<Vec<_>>(),
138         vec![Rva(0x15d44)..Rva(0x15d4c)],
139     );
140 
141     // Range ending outside OMAPs
142     // [
143     //   OMAPRecord {
144     //       source_address: 0x005e40e0,
145     //       target_address: 0x005e50e0
146     //   },
147     //   OMAPRecord {
148     //       source_address: 0x005e5000,
149     //       target_address: 0x00000000
150     //   },
151     //   OMAPRecord {
152     //       source_address: 0x005e70c0,
153     //       target_address: 0x00000000
154     //   }
155     // ]
156     let start = PdbInternalRva(0x5e_4fe0);
157     let end = PdbInternalRva(0x5e_8000);
158     assert_eq!(
159         address_map.rva_ranges(start..end).collect::<Vec<_>>(),
160         vec![Rva(0x005e_5fe0)..Rva(0x5e_6000)],
161     );
162 
163     // Range fully before OMAPs
164     let start = PdbInternalRva(0x0);
165     let end = PdbInternalRva(0x100);
166     assert_eq!(
167         address_map.rva_ranges(start..end).collect::<Vec<_>>(),
168         vec![],
169     );
170 
171     // Range fully after OMAPs
172     let start = PdbInternalRva(0x005e_8000);
173     let end = PdbInternalRva(0x005e_9000);
174     assert_eq!(
175         address_map.rva_ranges(start..end).collect::<Vec<_>>(),
176         vec![], // last record targets 0, thus the range is omitted
177     );
178 }
179