1 use crate::debug::Compilation;
2 use crate::FunctionAddressMap;
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.
79 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 
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.into_iter());
188     (fn_start, fn_end, FuncLookup { index, ranges })
189 }
190 
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> {
246     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);
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> {
309     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);
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> {
376     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);
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 {
462     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.into_iter());
490         AddressTransform { map, func }
491     }
492 
493     #[cfg(test)]
494     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         let mut translations = wasmtime_environ::PrimaryMap::new();
502         let mut translation = wasmtime_environ::ModuleTranslation::default();
503         translation.debuginfo.wasm_file = wasm_file;
504         translation
505             .module
506             .push_function(wasmtime_environ::ModuleInternedTypeIndex::from_u32(0));
507         translations.push(translation);
508 
509         let mut dummy_obj = object::write::Object::new(
510             object::BinaryFormat::Elf,
511             object::Architecture::Wasm32,
512             object::Endianness::Little,
513         );
514         let dummy_symbol = dummy_obj.add_file_symbol(Vec::new());
515         let func_lookup = move |_, f| (dummy_symbol, module_map[f]);
516         let tunables = wasmtime_environ::Tunables::default_host();
517         let compile = Compilation::new(
518             &*cranelift_codegen::isa::lookup(target_lexicon::Triple::host())
519                 .unwrap()
520                 .finish(cranelift_codegen::settings::Flags::new(
521                     cranelift_codegen::settings::builder(),
522                 ))
523                 .unwrap(),
524             &translations,
525             &func_lookup,
526             None,
527             &tunables,
528         );
529         Self::new(&compile, StaticModuleIndex::from_u32(0))
530     }
531 
532     fn find_func(&self, addr: u64) -> Option<&FuncTransform> {
533         // TODO check if we need to include end address
534         let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) {
535             Ok(i) => &self.func[i].1,
536             Err(i) => {
537                 if i > 0 {
538                     &self.func[i - 1].1
539                 } else {
540                     return None;
541                 }
542             }
543         };
544         if addr >= func.start {
545             return Some(func);
546         }
547         None
548     }
549 
550     pub fn find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex> {
551         self.find_func(addr).map(|f| f.index)
552     }
553 
554     fn translate_raw(&self, addr: u64) -> Option<(usize, GeneratedAddress)> {
555         if addr == 0 {
556             // It's normally 0 for debug info without the linked code.
557             return None;
558         }
559         if let Some(func) = self.find_func(addr) {
560             let map = &self.map[func.index];
561             if addr == func.end {
562                 // Clamp last address to the end to extend translation to the end
563                 // of the function.
564                 return Some((map.symbol, map.len));
565             }
566             let first_result = TransformRangeStartIter::new(func, addr).next();
567             first_result.map(|(address, _)| (map.symbol, address))
568         } else {
569             // Address was not found: function was not compiled?
570             None
571         }
572     }
573 
574     pub fn can_translate_address(&self, addr: u64) -> bool {
575         self.translate(addr).is_some()
576     }
577 
578     pub fn translate(&self, addr: u64) -> Option<write::Address> {
579         self.translate_raw(addr)
580             .map(|(symbol, address)| write::Address::Symbol {
581                 symbol,
582                 addend: address as i64,
583             })
584     }
585 
586     pub fn translate_ranges_raw<'a>(
587         &'a self,
588         start: u64,
589         end: u64,
590     ) -> Option<(usize, impl Iterator<Item = (usize, usize)> + 'a)> {
591         if start == 0 {
592             // It's normally 0 for debug info without the linked code.
593             return None;
594         }
595         if let Some(func) = self.find_func(start) {
596             let result = TransformRangeIter::new(func, start, end);
597             let symbol = self.map[func.index].symbol;
598             return Some((symbol, result));
599         }
600         // Address was not found: function was not compiled?
601         None
602     }
603 
604     pub fn translate_ranges<'a>(
605         &'a self,
606         start: u64,
607         end: u64,
608     ) -> impl Iterator<Item = (write::Address, u64)> + 'a {
609         enum TranslateRangesResult<'a> {
610             Empty,
611             Raw {
612                 symbol: usize,
613                 it: Box<dyn Iterator<Item = (usize, usize)> + 'a>,
614             },
615         }
616         impl<'a> Iterator for TranslateRangesResult<'a> {
617             type Item = (write::Address, u64);
618             fn next(&mut self) -> Option<Self::Item> {
619                 match self {
620                     TranslateRangesResult::Empty => None,
621                     TranslateRangesResult::Raw { symbol, it } => match it.next() {
622                         Some((start, end)) => {
623                             debug_assert!(start < end);
624                             Some((
625                                 write::Address::Symbol {
626                                     symbol: *symbol,
627                                     addend: start as i64,
628                                 },
629                                 (end - start) as u64,
630                             ))
631                         }
632                         None => None,
633                     },
634                 }
635             }
636         }
637 
638         match self.translate_ranges_raw(start, end) {
639             Some((symbol, ranges)) => TranslateRangesResult::Raw {
640                 symbol,
641                 it: Box::new(ranges),
642             },
643             None => TranslateRangesResult::Empty,
644         }
645     }
646 
647     pub fn map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap> {
648         &self.map
649     }
650 
651     pub fn func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress) {
652         let map = &self.map[index];
653         (map.offset, map.offset + map.len)
654     }
655 
656     pub fn func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress) {
657         let map = &self.map[index];
658         (map.wasm_start, map.wasm_end)
659     }
660 }
661 
662 #[cfg(test)]
663 mod tests {
664     use super::{build_function_lookup, get_wasm_code_offset, AddressTransform};
665     use crate::{CompiledFunctionMetadata, FunctionAddressMap};
666     use cranelift_entity::PrimaryMap;
667     use gimli::write::Address;
668     use std::mem;
669     use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo};
670 
671     #[test]
672     fn test_get_wasm_code_offset() {
673         let offset = get_wasm_code_offset(FilePos::new(3), 1);
674         assert_eq!(2, offset);
675         let offset = get_wasm_code_offset(FilePos::new(16), 0xF000_0000);
676         assert_eq!(0x1000_0010, offset);
677         let offset = get_wasm_code_offset(FilePos::new(1), 0x20_8000_0000);
678         assert_eq!(0x8000_0001, offset);
679     }
680 
681     fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap {
682         FunctionAddressMap {
683             instructions: vec![
684                 InstructionAddressMap {
685                     srcloc: FilePos::new(wasm_offset + 2),
686                     code_offset: 5,
687                 },
688                 InstructionAddressMap {
689                     srcloc: FilePos::default(),
690                     code_offset: 8,
691                 },
692                 InstructionAddressMap {
693                     srcloc: FilePos::new(wasm_offset + 7),
694                     code_offset: 15,
695                 },
696                 InstructionAddressMap {
697                     srcloc: FilePos::default(),
698                     code_offset: 23,
699                 },
700             ]
701             .into(),
702             start_srcloc: FilePos::new(wasm_offset),
703             end_srcloc: FilePos::new(wasm_offset + 10),
704             body_offset: 0,
705             body_len: 30,
706         }
707     }
708 
709     #[test]
710     fn test_build_function_lookup_simple() {
711         let input = create_simple_func(11);
712         let (start, end, lookup) = build_function_lookup(&input, 1);
713         assert_eq!(10, start);
714         assert_eq!(20, end);
715 
716         assert_eq!(1, lookup.index.len());
717         let index_entry = lookup.index.into_iter().next().unwrap();
718         assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry);
719         assert_eq!(1, lookup.ranges.len());
720         let range = &lookup.ranges[0];
721         assert_eq!(10, range.wasm_start);
722         assert_eq!(20, range.wasm_end);
723         assert_eq!(0, range.gen_start);
724         assert_eq!(30, range.gen_end);
725         let positions = &range.positions;
726         assert_eq!(2, positions.len());
727         assert_eq!(12, positions[0].wasm_pos);
728         assert_eq!(5, positions[0].gen_start);
729         assert_eq!(8, positions[0].gen_end);
730         assert_eq!(17, positions[1].wasm_pos);
731         assert_eq!(15, positions[1].gen_start);
732         assert_eq!(23, positions[1].gen_end);
733     }
734 
735     #[test]
736     fn test_build_function_lookup_two_ranges() {
737         let mut input = create_simple_func(11);
738         // append instruction with same srcloc as input.instructions[0]
739         let mut list = Vec::from(mem::take(&mut input.instructions));
740         list.push(InstructionAddressMap {
741             srcloc: FilePos::new(11 + 2),
742             code_offset: 23,
743         });
744         list.push(InstructionAddressMap {
745             srcloc: FilePos::default(),
746             code_offset: 26,
747         });
748         input.instructions = list.into();
749         let (start, end, lookup) = build_function_lookup(&input, 1);
750         assert_eq!(10, start);
751         assert_eq!(20, end);
752 
753         assert_eq!(2, lookup.index.len());
754         let index_entries = Vec::from_iter(lookup.index.into_iter());
755         assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]);
756         assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]);
757         assert_eq!(2, lookup.ranges.len());
758 
759         let range = &lookup.ranges[0];
760         assert_eq!(10, range.wasm_start);
761         assert_eq!(17, range.wasm_end);
762         assert_eq!(0, range.gen_start);
763         assert_eq!(23, range.gen_end);
764         let positions = &range.positions;
765         assert_eq!(2, positions.len());
766         assert_eq!(12, positions[0].wasm_pos);
767         assert_eq!(5, positions[0].gen_start);
768         assert_eq!(8, positions[0].gen_end);
769         assert_eq!(17, positions[1].wasm_pos);
770         assert_eq!(15, positions[1].gen_start);
771         assert_eq!(23, positions[1].gen_end);
772 
773         let range = &lookup.ranges[1];
774         assert_eq!(12, range.wasm_start);
775         assert_eq!(20, range.wasm_end);
776         assert_eq!(23, range.gen_start);
777         assert_eq!(30, range.gen_end);
778         let positions = &range.positions;
779         assert_eq!(1, positions.len());
780         assert_eq!(12, positions[0].wasm_pos);
781         assert_eq!(23, positions[0].gen_start);
782         assert_eq!(26, positions[0].gen_end);
783     }
784 
785     #[test]
786     fn test_addr_translate() {
787         // Ignore this test if cranelift doesn't support the native platform.
788         if cranelift_native::builder().is_err() {
789             return;
790         }
791         let func = CompiledFunctionMetadata {
792             address_map: create_simple_func(11),
793             ..Default::default()
794         };
795         let input = PrimaryMap::from_iter([&func]);
796         let at = AddressTransform::mock(
797             &input,
798             WasmFileInfo {
799                 path: None,
800                 code_section_offset: 1,
801                 imported_func_count: 0,
802                 funcs: Vec::new(),
803             },
804         );
805 
806         let addr = at.translate(10);
807         assert_eq!(
808             Some(Address::Symbol {
809                 symbol: 0,
810                 addend: 0,
811             }),
812             addr
813         );
814 
815         let addr = at.translate(20);
816         assert_eq!(
817             Some(Address::Symbol {
818                 symbol: 0,
819                 addend: 30,
820             }),
821             addr
822         );
823 
824         let addr = at.translate(0);
825         assert_eq!(None, addr);
826 
827         let addr = at.translate(12);
828         assert_eq!(
829             Some(Address::Symbol {
830                 symbol: 0,
831                 addend: 5,
832             }),
833             addr
834         );
835 
836         let addr = at.translate(18);
837         assert_eq!(
838             Some(Address::Symbol {
839                 symbol: 0,
840                 addend: 23,
841             }),
842             addr
843         );
844     }
845 }
846