1 use crate::FunctionAddressMap;
2 use crate::debug::Compilation;
3 use gimli::write;
4 use std::collections::BTreeMap;
5 use wasmtime_environ::{DefinedFuncIndex, FilePos, PrimaryMap, StaticModuleIndex};
6 
7 pub type GeneratedAddress = usize;
8 pub type WasmAddress = u64;
9 
10 /// Contains mapping of the generated address to its original
11 /// source location.
12 #[derive(Debug)]
13 pub struct AddressMap {
14     pub generated: GeneratedAddress,
15     pub wasm: WasmAddress,
16 }
17 
18 /// Information about generated function code: its body start,
19 /// length, and instructions addresses.
20 #[derive(Debug)]
21 pub struct FunctionMap {
22     pub symbol: usize,
23     pub offset: GeneratedAddress,
24     pub len: GeneratedAddress,
25     pub wasm_start: WasmAddress,
26     pub wasm_end: WasmAddress,
27     pub addresses: Box<[AddressMap]>,
28 }
29 
30 /// Mapping of the source location to its generated code range.
31 #[derive(Debug)]
32 struct Position {
33     wasm_pos: WasmAddress,
34     gen_start: GeneratedAddress,
35     gen_end: GeneratedAddress,
36 }
37 
38 /// Mapping of continuous range of source location to its generated
39 /// code. The positions are always in ascending order for search.
40 #[derive(Debug)]
41 struct Range {
42     wasm_start: WasmAddress,
43     wasm_end: WasmAddress,
44     gen_start: GeneratedAddress,
45     gen_end: GeneratedAddress,
46     positions: Box<[Position]>,
47 }
48 
49 type RangeIndex = usize;
50 
51 /// Helper function address lookup data. Contains ranges start positions
52 /// index and ranges data. The multiple ranges can include the same
53 /// original source position. The index (B-Tree) uses range start
54 /// position as a key. The index values reference the ranges array.
55 /// The item are ordered RangeIndex.
56 #[derive(Debug)]
57 struct FuncLookup {
58     index: Vec<(WasmAddress, Box<[RangeIndex]>)>,
59     ranges: Box<[Range]>,
60 }
61 
62 /// Mapping of original functions to generated code locations/ranges.
63 #[derive(Debug)]
64 struct FuncTransform {
65     start: WasmAddress,
66     end: WasmAddress,
67     index: DefinedFuncIndex,
68     lookup: FuncLookup,
69 }
70 
71 /// Module functions mapping to generated code.
72 #[derive(Debug)]
73 pub struct AddressTransform {
74     map: PrimaryMap<DefinedFuncIndex, FunctionMap>,
75     func: Vec<(WasmAddress, FuncTransform)>,
76 }
77 
78 /// Returns a wasm bytecode offset in the code section from SourceLoc.
get_wasm_code_offset(loc: FilePos, code_section_offset: u64) -> WasmAddress79 fn get_wasm_code_offset(loc: FilePos, code_section_offset: u64) -> WasmAddress {
80     // Code section size <= 4GB, allow wrapped SourceLoc to recover the overflow.
81     loc.file_offset()
82         .unwrap()
83         .wrapping_sub(code_section_offset as u32) as WasmAddress
84 }
85 
build_function_lookup( ft: &FunctionAddressMap, code_section_offset: u64, ) -> (WasmAddress, WasmAddress, FuncLookup)86 fn build_function_lookup(
87     ft: &FunctionAddressMap,
88     code_section_offset: u64,
89 ) -> (WasmAddress, WasmAddress, FuncLookup) {
90     assert!(code_section_offset <= ft.start_srcloc.file_offset().unwrap().into());
91     let fn_start = get_wasm_code_offset(ft.start_srcloc, code_section_offset);
92     let fn_end = get_wasm_code_offset(ft.end_srcloc, code_section_offset);
93     assert!(fn_start <= fn_end);
94 
95     // Build ranges of continuous source locations. The new ranges starts when
96     // non-descending order is interrupted. Assuming the same origin location can
97     // be present in multiple ranges.
98     let mut range_wasm_start = fn_start;
99     let mut range_gen_start = ft.body_offset;
100     let mut last_wasm_pos = range_wasm_start;
101     let mut ranges = Vec::new();
102     let mut ranges_index = BTreeMap::new();
103     let mut current_range = Vec::new();
104     let mut last_gen_inst_empty = false;
105     for (i, t) in ft.instructions.iter().enumerate() {
106         if t.srcloc.file_offset().is_none() {
107             continue;
108         }
109 
110         let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
111         assert!(fn_start <= offset);
112         assert!(offset <= fn_end);
113 
114         let inst_gen_start = t.code_offset as usize;
115         let inst_gen_end = match ft.instructions.get(i + 1) {
116             Some(i) => i.code_offset as usize,
117             None => ft.body_len as usize,
118         };
119 
120         if last_wasm_pos > offset {
121             // Start new range.
122             ranges_index.insert(range_wasm_start, ranges.len());
123             ranges.push(Range {
124                 wasm_start: range_wasm_start,
125                 wasm_end: last_wasm_pos,
126                 gen_start: range_gen_start,
127                 gen_end: inst_gen_start,
128                 positions: current_range.into_boxed_slice(),
129             });
130             range_wasm_start = offset;
131             range_gen_start = inst_gen_start;
132             current_range = Vec::new();
133             last_gen_inst_empty = false;
134         }
135         if last_gen_inst_empty && current_range.last().unwrap().gen_start == inst_gen_start {
136             // It is possible that previous inst_gen_start == inst_gen_end, so
137             // make an attempt to merge all such positions with current one.
138             if inst_gen_start < inst_gen_end {
139                 let last = current_range.last_mut().unwrap();
140                 last.gen_end = inst_gen_end;
141                 last_gen_inst_empty = false;
142             }
143         } else {
144             // Continue existing range: add new wasm->generated code position.
145             current_range.push(Position {
146                 wasm_pos: offset,
147                 gen_start: inst_gen_start,
148                 gen_end: inst_gen_end,
149             });
150             // Track if last position was empty (see if-branch above).
151             last_gen_inst_empty = inst_gen_start == inst_gen_end;
152         }
153         last_wasm_pos = offset;
154     }
155     let last_gen_addr = ft.body_offset + ft.body_len as usize;
156     ranges_index.insert(range_wasm_start, ranges.len());
157     ranges.push(Range {
158         wasm_start: range_wasm_start,
159         wasm_end: fn_end,
160         gen_start: range_gen_start,
161         gen_end: last_gen_addr,
162         positions: current_range.into_boxed_slice(),
163     });
164 
165     // Making ranges lookup faster by building index: B-tree with every range
166     // start position that maps into list of active ranges at this position.
167     let ranges = ranges.into_boxed_slice();
168     let mut active_ranges = Vec::new();
169     let mut index = BTreeMap::new();
170     let mut last_wasm_pos = None;
171     for (wasm_start, range_index) in ranges_index {
172         if Some(wasm_start) == last_wasm_pos {
173             active_ranges.push(range_index);
174             continue;
175         }
176         if let Some(position) = last_wasm_pos {
177             let mut sorted_ranges = active_ranges.clone();
178             sorted_ranges.sort();
179             index.insert(position, sorted_ranges.into_boxed_slice());
180         }
181         active_ranges.retain(|r| ranges[*r].wasm_end.cmp(&wasm_start) != std::cmp::Ordering::Less);
182         active_ranges.push(range_index);
183         last_wasm_pos = Some(wasm_start);
184     }
185     active_ranges.sort();
186     index.insert(last_wasm_pos.unwrap(), active_ranges.into_boxed_slice());
187     let index = Vec::from_iter(index);
188     (fn_start, fn_end, FuncLookup { index, ranges })
189 }
190 
build_function_addr_map( compilation: &Compilation<'_>, module: StaticModuleIndex, ) -> PrimaryMap<DefinedFuncIndex, FunctionMap>191 fn build_function_addr_map(
192     compilation: &Compilation<'_>,
193     module: StaticModuleIndex,
194 ) -> PrimaryMap<DefinedFuncIndex, FunctionMap> {
195     let mut map = PrimaryMap::new();
196     for idx in compilation.translations[module]
197         .module
198         .defined_func_indices()
199     {
200         let (symbol, metadata) = compilation.function(module, idx);
201         let code_section_offset = compilation.translations[module]
202             .debuginfo
203             .wasm_file
204             .code_section_offset;
205         let ft = &metadata.address_map;
206         let mut fn_map = Vec::new();
207         for t in ft.instructions.iter() {
208             if t.srcloc.file_offset().is_none() {
209                 continue;
210             }
211             let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
212             fn_map.push(AddressMap {
213                 generated: t.code_offset as usize,
214                 wasm: offset,
215             });
216         }
217 
218         if cfg!(debug_assertions) {
219             // fn_map is sorted by the generated field -- see FunctionAddressMap::instructions.
220             for i in 1..fn_map.len() {
221                 assert!(fn_map[i - 1].generated <= fn_map[i].generated);
222             }
223         }
224 
225         map.push(FunctionMap {
226             symbol,
227             offset: ft.body_offset,
228             len: ft.body_len as usize,
229             wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset),
230             wasm_end: get_wasm_code_offset(ft.end_srcloc, code_section_offset),
231             addresses: fn_map.into_boxed_slice(),
232         });
233     }
234     map
235 }
236 
237 // Utility iterator to find all ranges starts for specific Wasm address.
238 // The iterator returns generated addresses sorted by RangeIndex.
239 struct TransformRangeStartIter<'a> {
240     addr: WasmAddress,
241     indices: &'a [RangeIndex],
242     ranges: &'a [Range],
243 }
244 
245 impl<'a> TransformRangeStartIter<'a> {
new(func: &'a FuncTransform, addr: WasmAddress) -> Self246     fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
247         let found = match func
248             .lookup
249             .index
250             .binary_search_by(|entry| entry.0.cmp(&addr))
251         {
252             Ok(i) => Some(&func.lookup.index[i].1),
253             Err(i) => {
254                 if i > 0 {
255                     Some(&func.lookup.index[i - 1].1)
256                 } else {
257                     None
258                 }
259             }
260         };
261         if let Some(range_indices) = found {
262             TransformRangeStartIter {
263                 addr,
264                 indices: range_indices,
265                 ranges: &func.lookup.ranges,
266             }
267         } else {
268             unreachable!();
269         }
270     }
271 }
272 
273 impl<'a> Iterator for TransformRangeStartIter<'a> {
274     type Item = (GeneratedAddress, RangeIndex);
next(&mut self) -> Option<Self::Item>275     fn next(&mut self) -> Option<Self::Item> {
276         if let Some((first, tail)) = self.indices.split_first() {
277             let range_index = *first;
278             let range = &self.ranges[range_index];
279             self.indices = tail;
280             let address = match range
281                 .positions
282                 .binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
283             {
284                 Ok(i) => range.positions[i].gen_start,
285                 Err(i) => {
286                     if i == 0 {
287                         range.gen_start
288                     } else {
289                         range.positions[i - 1].gen_end
290                     }
291                 }
292             };
293             Some((address, range_index))
294         } else {
295             None
296         }
297     }
298 }
299 
300 // Utility iterator to find all ranges ends for specific Wasm address.
301 // The iterator returns generated addresses sorted by RangeIndex.
302 struct TransformRangeEndIter<'a> {
303     addr: WasmAddress,
304     indices: &'a [RangeIndex],
305     ranges: &'a [Range],
306 }
307 
308 impl<'a> TransformRangeEndIter<'a> {
new(func: &'a FuncTransform, addr: WasmAddress) -> Self309     fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
310         let found = match func
311             .lookup
312             .index
313             .binary_search_by(|entry| entry.0.cmp(&addr))
314         {
315             Ok(i) => Some(&func.lookup.index[i].1),
316             Err(i) => {
317                 if i > 0 {
318                     Some(&func.lookup.index[i - 1].1)
319                 } else {
320                     None
321                 }
322             }
323         };
324         if let Some(range_indices) = found {
325             TransformRangeEndIter {
326                 addr,
327                 indices: range_indices,
328                 ranges: &func.lookup.ranges,
329             }
330         } else {
331             unreachable!();
332         }
333     }
334 }
335 
336 impl<'a> Iterator for TransformRangeEndIter<'a> {
337     type Item = (GeneratedAddress, RangeIndex);
next(&mut self) -> Option<Self::Item>338     fn next(&mut self) -> Option<Self::Item> {
339         while let Some((first, tail)) = self.indices.split_first() {
340             let range_index = *first;
341             let range = &self.ranges[range_index];
342             self.indices = tail;
343             if range.wasm_start >= self.addr {
344                 continue;
345             }
346             let address = match range
347                 .positions
348                 .binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
349             {
350                 Ok(i) => range.positions[i].gen_end,
351                 Err(i) => {
352                     if i == range.positions.len() {
353                         range.gen_end
354                     } else {
355                         range.positions[i].gen_start
356                     }
357                 }
358             };
359             return Some((address, range_index));
360         }
361         None
362     }
363 }
364 
365 // Utility iterator to iterate by translated function ranges.
366 struct TransformRangeIter<'a> {
367     func: &'a FuncTransform,
368     start_it: TransformRangeStartIter<'a>,
369     end_it: TransformRangeEndIter<'a>,
370     last_start: Option<(GeneratedAddress, RangeIndex)>,
371     last_end: Option<(GeneratedAddress, RangeIndex)>,
372     last_item: Option<(GeneratedAddress, GeneratedAddress)>,
373 }
374 
375 impl<'a> TransformRangeIter<'a> {
new(func: &'a FuncTransform, start: WasmAddress, end: WasmAddress) -> Self376     fn new(func: &'a FuncTransform, start: WasmAddress, end: WasmAddress) -> Self {
377         let mut start_it = TransformRangeStartIter::new(func, start);
378         let last_start = start_it.next();
379         let mut end_it = TransformRangeEndIter::new(func, end);
380         let last_end = end_it.next();
381         TransformRangeIter {
382             func,
383             start_it,
384             end_it,
385             last_start,
386             last_end,
387             last_item: None,
388         }
389     }
390 }
391 
392 impl<'a> Iterator for TransformRangeIter<'a> {
393     type Item = (GeneratedAddress, GeneratedAddress);
next(&mut self) -> Option<Self::Item>394     fn next(&mut self) -> Option<Self::Item> {
395         loop {
396             // Merge TransformRangeStartIter and TransformRangeEndIter data using
397             // FuncLookup index's field property to be sorted by RangeIndex.
398             let (start, end, range_index): (
399                 Option<GeneratedAddress>,
400                 Option<GeneratedAddress>,
401                 RangeIndex,
402             ) = {
403                 match (self.last_start.as_ref(), self.last_end.as_ref()) {
404                     (Some((s, sri)), Some((e, eri))) => {
405                         if sri == eri {
406                             // Start and end RangeIndex matched.
407                             (Some(*s), Some(*e), *sri)
408                         } else if sri < eri {
409                             (Some(*s), None, *sri)
410                         } else {
411                             (None, Some(*e), *eri)
412                         }
413                     }
414                     (Some((s, sri)), None) => (Some(*s), None, *sri),
415                     (None, Some((e, eri))) => (None, Some(*e), *eri),
416                     (None, None) => {
417                         // Reached ends for start and end iterators.
418                         return None;
419                     }
420                 }
421             };
422             let range_start = match start {
423                 Some(range_start) => {
424                     // Consume start iterator.
425                     self.last_start = self.start_it.next();
426                     range_start
427                 }
428                 None => {
429                     let range = &self.func.lookup.ranges[range_index];
430                     range.gen_start
431                 }
432             };
433             let range_end = match end {
434                 Some(range_end) => {
435                     // Consume end iterator.
436                     self.last_end = self.end_it.next();
437                     range_end
438                 }
439                 None => {
440                     let range = &self.func.lookup.ranges[range_index];
441                     range.gen_end
442                 }
443             };
444 
445             if cfg!(debug_assertions) {
446                 match self.last_item.replace((range_start, range_end)) {
447                     Some((_, last_end)) => debug_assert!(last_end <= range_start),
448                     None => (),
449                 }
450             }
451 
452             if range_start < range_end {
453                 return Some((range_start, range_end));
454             }
455             // Throw away empty ranges.
456             debug_assert!(range_start == range_end);
457         }
458     }
459 }
460 
461 impl AddressTransform {
new(compilation: &Compilation<'_>, module: StaticModuleIndex) -> Self462     pub fn new(compilation: &Compilation<'_>, module: StaticModuleIndex) -> Self {
463         let mut func = BTreeMap::new();
464         let code_section_offset = compilation.translations[module]
465             .debuginfo
466             .wasm_file
467             .code_section_offset;
468 
469         for idx in compilation.translations[module]
470             .module
471             .defined_func_indices()
472         {
473             let (_, metadata) = compilation.function(module, idx);
474             let (fn_start, fn_end, lookup) =
475                 build_function_lookup(&metadata.address_map, code_section_offset);
476 
477             func.insert(
478                 fn_start,
479                 FuncTransform {
480                     start: fn_start,
481                     end: fn_end,
482                     index: idx,
483                     lookup,
484                 },
485             );
486         }
487 
488         let map = build_function_addr_map(compilation, module);
489         let func = Vec::from_iter(func);
490         AddressTransform { map, func }
491     }
492 
493     #[cfg(test)]
mock( module_map: &wasmtime_environ::PrimaryMap< wasmtime_environ::DefinedFuncIndex, &crate::CompiledFunctionMetadata, >, wasm_file: wasmtime_environ::WasmFileInfo, ) -> Self494     pub fn mock(
495         module_map: &wasmtime_environ::PrimaryMap<
496             wasmtime_environ::DefinedFuncIndex,
497             &crate::CompiledFunctionMetadata,
498         >,
499         wasm_file: wasmtime_environ::WasmFileInfo,
500     ) -> Self {
501         use cranelift_entity::EntityRef;
502 
503         let mut translations = wasmtime_environ::PrimaryMap::new();
504         let mut translation = wasmtime_environ::ModuleTranslation::new(StaticModuleIndex::new(0));
505         translation.debuginfo.wasm_file = wasm_file;
506         translation
507             .module
508             .push_function(wasmtime_environ::ModuleInternedTypeIndex::from_u32(0));
509         translations.push(translation);
510 
511         let mut dummy_obj = object::write::Object::new(
512             object::BinaryFormat::Elf,
513             object::Architecture::Wasm32,
514             object::Endianness::Little,
515         );
516         let dummy_symbol = dummy_obj.add_file_symbol(Vec::new());
517         let func_lookup = move |_, f| (dummy_symbol, module_map[f]);
518         let tunables = wasmtime_environ::Tunables::default_host();
519         let compile = Compilation::new(
520             &*cranelift_codegen::isa::lookup(target_lexicon::Triple::host())
521                 .unwrap()
522                 .finish(cranelift_codegen::settings::Flags::new(
523                     cranelift_codegen::settings::builder(),
524                 ))
525                 .unwrap(),
526             &translations,
527             &func_lookup,
528             None,
529             &tunables,
530         );
531         Self::new(&compile, StaticModuleIndex::from_u32(0))
532     }
533 
find_func(&self, addr: u64) -> Option<&FuncTransform>534     fn find_func(&self, addr: u64) -> Option<&FuncTransform> {
535         // TODO check if we need to include end address
536         let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) {
537             Ok(i) => &self.func[i].1,
538             Err(i) => {
539                 if i > 0 {
540                     &self.func[i - 1].1
541                 } else {
542                     return None;
543                 }
544             }
545         };
546         if addr >= func.start {
547             return Some(func);
548         }
549         None
550     }
551 
find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex>552     pub fn find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex> {
553         self.find_func(addr).map(|f| f.index)
554     }
555 
translate_raw(&self, addr: u64) -> Option<(usize, GeneratedAddress)>556     fn translate_raw(&self, addr: u64) -> Option<(usize, GeneratedAddress)> {
557         const TOMBSTONE: u64 = u32::MAX as u64;
558         if addr == 0 || addr == TOMBSTONE {
559             // Addresses for unlinked code may be left as 0 or replaced
560             // with -1, depending on the linker used.
561             return None;
562         }
563         if let Some(func) = self.find_func(addr) {
564             let map = &self.map[func.index];
565             if addr == func.end {
566                 // Clamp last address to the end to extend translation to the end
567                 // of the function.
568                 return Some((map.symbol, map.len));
569             }
570             let first_result = TransformRangeStartIter::new(func, addr).next();
571             first_result.map(|(address, _)| (map.symbol, address))
572         } else {
573             // Address was not found: function was not compiled?
574             None
575         }
576     }
577 
can_translate_address(&self, addr: u64) -> bool578     pub fn can_translate_address(&self, addr: u64) -> bool {
579         self.translate(addr).is_some()
580     }
581 
translate(&self, addr: u64) -> Option<write::Address>582     pub fn translate(&self, addr: u64) -> Option<write::Address> {
583         self.translate_raw(addr)
584             .map(|(symbol, address)| write::Address::Symbol {
585                 symbol,
586                 addend: address as i64,
587             })
588     }
589 
translate_ranges_raw<'a>( &'a self, start: u64, end: u64, ) -> Option<(usize, impl Iterator<Item = (usize, usize)> + 'a)>590     pub fn translate_ranges_raw<'a>(
591         &'a self,
592         start: u64,
593         end: u64,
594     ) -> Option<(usize, impl Iterator<Item = (usize, usize)> + 'a)> {
595         if start == 0 {
596             // It's normally 0 for debug info without the linked code.
597             return None;
598         }
599         if let Some(func) = self.find_func(start) {
600             let result = TransformRangeIter::new(func, start, end);
601             let symbol = self.map[func.index].symbol;
602             return Some((symbol, result));
603         }
604         // Address was not found: function was not compiled?
605         None
606     }
607 
translate_ranges<'a>( &'a self, start: u64, end: u64, ) -> impl Iterator<Item = (write::Address, u64)> + 'a608     pub fn translate_ranges<'a>(
609         &'a self,
610         start: u64,
611         end: u64,
612     ) -> impl Iterator<Item = (write::Address, u64)> + 'a {
613         enum TranslateRangesResult<'a> {
614             Empty,
615             Raw {
616                 symbol: usize,
617                 it: Box<dyn Iterator<Item = (usize, usize)> + 'a>,
618             },
619         }
620         impl<'a> Iterator for TranslateRangesResult<'a> {
621             type Item = (write::Address, u64);
622             fn next(&mut self) -> Option<Self::Item> {
623                 match self {
624                     TranslateRangesResult::Empty => None,
625                     TranslateRangesResult::Raw { symbol, it } => match it.next() {
626                         Some((start, end)) => {
627                             debug_assert!(start < end);
628                             Some((
629                                 write::Address::Symbol {
630                                     symbol: *symbol,
631                                     addend: start as i64,
632                                 },
633                                 (end - start) as u64,
634                             ))
635                         }
636                         None => None,
637                     },
638                 }
639             }
640         }
641 
642         match self.translate_ranges_raw(start, end) {
643             Some((symbol, ranges)) => TranslateRangesResult::Raw {
644                 symbol,
645                 it: Box::new(ranges),
646             },
647             None => TranslateRangesResult::Empty,
648         }
649     }
650 
map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap>651     pub fn map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap> {
652         &self.map
653     }
654 
func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress)655     pub fn func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress) {
656         let map = &self.map[index];
657         (map.offset, map.offset + map.len)
658     }
659 
func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress)660     pub fn func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress) {
661         let map = &self.map[index];
662         (map.wasm_start, map.wasm_end)
663     }
664 }
665 
666 #[cfg(test)]
667 mod tests {
668     use super::{AddressTransform, build_function_lookup, get_wasm_code_offset};
669     use crate::{CompiledFunctionMetadata, FunctionAddressMap};
670     use cranelift_entity::PrimaryMap;
671     use gimli::write::Address;
672     use std::mem;
673     use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo};
674 
675     #[test]
test_get_wasm_code_offset()676     fn test_get_wasm_code_offset() {
677         let offset = get_wasm_code_offset(FilePos::new(3), 1);
678         assert_eq!(2, offset);
679         let offset = get_wasm_code_offset(FilePos::new(16), 0xF000_0000);
680         assert_eq!(0x1000_0010, offset);
681         let offset = get_wasm_code_offset(FilePos::new(1), 0x20_8000_0000);
682         assert_eq!(0x8000_0001, offset);
683     }
684 
create_simple_func(wasm_offset: u32) -> FunctionAddressMap685     fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap {
686         FunctionAddressMap {
687             instructions: vec![
688                 InstructionAddressMap {
689                     srcloc: FilePos::new(wasm_offset + 2),
690                     code_offset: 5,
691                 },
692                 InstructionAddressMap {
693                     srcloc: FilePos::default(),
694                     code_offset: 8,
695                 },
696                 InstructionAddressMap {
697                     srcloc: FilePos::new(wasm_offset + 7),
698                     code_offset: 15,
699                 },
700                 InstructionAddressMap {
701                     srcloc: FilePos::default(),
702                     code_offset: 23,
703                 },
704             ]
705             .into(),
706             start_srcloc: FilePos::new(wasm_offset),
707             end_srcloc: FilePos::new(wasm_offset + 10),
708             body_offset: 0,
709             body_len: 30,
710         }
711     }
712 
713     #[test]
test_build_function_lookup_simple()714     fn test_build_function_lookup_simple() {
715         let input = create_simple_func(11);
716         let (start, end, lookup) = build_function_lookup(&input, 1);
717         assert_eq!(10, start);
718         assert_eq!(20, end);
719 
720         assert_eq!(1, lookup.index.len());
721         let index_entry = lookup.index.into_iter().next().unwrap();
722         assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry);
723         assert_eq!(1, lookup.ranges.len());
724         let range = &lookup.ranges[0];
725         assert_eq!(10, range.wasm_start);
726         assert_eq!(20, range.wasm_end);
727         assert_eq!(0, range.gen_start);
728         assert_eq!(30, range.gen_end);
729         let positions = &range.positions;
730         assert_eq!(2, positions.len());
731         assert_eq!(12, positions[0].wasm_pos);
732         assert_eq!(5, positions[0].gen_start);
733         assert_eq!(8, positions[0].gen_end);
734         assert_eq!(17, positions[1].wasm_pos);
735         assert_eq!(15, positions[1].gen_start);
736         assert_eq!(23, positions[1].gen_end);
737     }
738 
739     #[test]
test_build_function_lookup_two_ranges()740     fn test_build_function_lookup_two_ranges() {
741         let mut input = create_simple_func(11);
742         // append instruction with same srcloc as input.instructions[0]
743         let mut list = Vec::from(mem::take(&mut input.instructions));
744         list.push(InstructionAddressMap {
745             srcloc: FilePos::new(11 + 2),
746             code_offset: 23,
747         });
748         list.push(InstructionAddressMap {
749             srcloc: FilePos::default(),
750             code_offset: 26,
751         });
752         input.instructions = list.into();
753         let (start, end, lookup) = build_function_lookup(&input, 1);
754         assert_eq!(10, start);
755         assert_eq!(20, end);
756 
757         assert_eq!(2, lookup.index.len());
758         let index_entries = Vec::from_iter(lookup.index);
759         assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]);
760         assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]);
761         assert_eq!(2, lookup.ranges.len());
762 
763         let range = &lookup.ranges[0];
764         assert_eq!(10, range.wasm_start);
765         assert_eq!(17, range.wasm_end);
766         assert_eq!(0, range.gen_start);
767         assert_eq!(23, range.gen_end);
768         let positions = &range.positions;
769         assert_eq!(2, positions.len());
770         assert_eq!(12, positions[0].wasm_pos);
771         assert_eq!(5, positions[0].gen_start);
772         assert_eq!(8, positions[0].gen_end);
773         assert_eq!(17, positions[1].wasm_pos);
774         assert_eq!(15, positions[1].gen_start);
775         assert_eq!(23, positions[1].gen_end);
776 
777         let range = &lookup.ranges[1];
778         assert_eq!(12, range.wasm_start);
779         assert_eq!(20, range.wasm_end);
780         assert_eq!(23, range.gen_start);
781         assert_eq!(30, range.gen_end);
782         let positions = &range.positions;
783         assert_eq!(1, positions.len());
784         assert_eq!(12, positions[0].wasm_pos);
785         assert_eq!(23, positions[0].gen_start);
786         assert_eq!(26, positions[0].gen_end);
787     }
788 
789     #[test]
test_addr_translate()790     fn test_addr_translate() {
791         // Ignore this test if cranelift doesn't support the native platform.
792         if cranelift_native::builder().is_err() {
793             return;
794         }
795         let func = CompiledFunctionMetadata {
796             address_map: create_simple_func(11),
797             ..Default::default()
798         };
799         let input = PrimaryMap::from_iter([&func]);
800         let at = AddressTransform::mock(
801             &input,
802             WasmFileInfo {
803                 path: None,
804                 code_section_offset: 1,
805                 imported_func_count: 0,
806                 funcs: Vec::new(),
807             },
808         );
809 
810         let addr = at.translate(10);
811         assert_eq!(
812             Some(Address::Symbol {
813                 symbol: 0,
814                 addend: 0,
815             }),
816             addr
817         );
818 
819         let addr = at.translate(20);
820         assert_eq!(
821             Some(Address::Symbol {
822                 symbol: 0,
823                 addend: 30,
824             }),
825             addr
826         );
827 
828         let addr = at.translate(0);
829         assert_eq!(None, addr);
830 
831         let addr = at.translate(12);
832         assert_eq!(
833             Some(Address::Symbol {
834                 symbol: 0,
835                 addend: 5,
836             }),
837             addr
838         );
839 
840         let addr = at.translate(18);
841         assert_eq!(
842             Some(Address::Symbol {
843                 symbol: 0,
844                 addend: 23,
845             }),
846             addr
847         );
848     }
849 }
850