1 use crate::CompiledFunctionsMetadata;
2 use gimli::write;
3 use std::collections::BTreeMap;
4 use std::iter::FromIterator;
5 use wasmtime_cranelift_shared::FunctionAddressMap;
6 use wasmtime_environ::{DefinedFuncIndex, EntityRef, FilePos, PrimaryMap, WasmFileInfo};
7 
8 pub type GeneratedAddress = usize;
9 pub type WasmAddress = u64;
10 
11 /// Contains mapping of the generated address to its original
12 /// source location.
13 #[derive(Debug)]
14 pub struct AddressMap {
15     pub generated: GeneratedAddress,
16     pub wasm: WasmAddress,
17 }
18 
19 /// Information about generated function code: its body start,
20 /// length, and instructions addresses.
21 #[derive(Debug)]
22 pub struct FunctionMap {
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     funcs: &CompiledFunctionsMetadata,
193     code_section_offset: u64,
194 ) -> PrimaryMap<DefinedFuncIndex, FunctionMap> {
195     let mut map = PrimaryMap::new();
196     for (_, metadata) in funcs {
197         let ft = &metadata.address_map;
198         let mut fn_map = Vec::new();
199         for t in ft.instructions.iter() {
200             if t.srcloc.file_offset().is_none() {
201                 continue;
202             }
203             let offset = get_wasm_code_offset(t.srcloc, code_section_offset);
204             fn_map.push(AddressMap {
205                 generated: t.code_offset as usize,
206                 wasm: offset,
207             });
208         }
209 
210         if cfg!(debug_assertions) {
211             // fn_map is sorted by the generated field -- see FunctionAddressMap::instructions.
212             for i in 1..fn_map.len() {
213                 assert!(fn_map[i - 1].generated <= fn_map[i].generated);
214             }
215         }
216 
217         map.push(FunctionMap {
218             offset: ft.body_offset,
219             len: ft.body_len as usize,
220             wasm_start: get_wasm_code_offset(ft.start_srcloc, code_section_offset),
221             wasm_end: get_wasm_code_offset(ft.end_srcloc, code_section_offset),
222             addresses: fn_map.into_boxed_slice(),
223         });
224     }
225     map
226 }
227 
228 // Utility iterator to find all ranges starts for specific Wasm address.
229 // The iterator returns generated addresses sorted by RangeIndex.
230 struct TransformRangeStartIter<'a> {
231     addr: WasmAddress,
232     indices: &'a [RangeIndex],
233     ranges: &'a [Range],
234 }
235 
236 impl<'a> TransformRangeStartIter<'a> {
237     fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
238         let found = match func
239             .lookup
240             .index
241             .binary_search_by(|entry| entry.0.cmp(&addr))
242         {
243             Ok(i) => Some(&func.lookup.index[i].1),
244             Err(i) => {
245                 if i > 0 {
246                     Some(&func.lookup.index[i - 1].1)
247                 } else {
248                     None
249                 }
250             }
251         };
252         if let Some(range_indices) = found {
253             TransformRangeStartIter {
254                 addr,
255                 indices: range_indices,
256                 ranges: &func.lookup.ranges,
257             }
258         } else {
259             unreachable!();
260         }
261     }
262 }
263 
264 impl<'a> Iterator for TransformRangeStartIter<'a> {
265     type Item = (GeneratedAddress, RangeIndex);
266     fn next(&mut self) -> Option<Self::Item> {
267         if let Some((first, tail)) = self.indices.split_first() {
268             let range_index = *first;
269             let range = &self.ranges[range_index];
270             self.indices = tail;
271             let address = match range
272                 .positions
273                 .binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
274             {
275                 Ok(i) => range.positions[i].gen_start,
276                 Err(i) => {
277                     if i == 0 {
278                         range.gen_start
279                     } else {
280                         range.positions[i - 1].gen_end
281                     }
282                 }
283             };
284             Some((address, range_index))
285         } else {
286             None
287         }
288     }
289 }
290 
291 // Utility iterator to find all ranges ends for specific Wasm address.
292 // The iterator returns generated addresses sorted by RangeIndex.
293 struct TransformRangeEndIter<'a> {
294     addr: WasmAddress,
295     indices: &'a [RangeIndex],
296     ranges: &'a [Range],
297 }
298 
299 impl<'a> TransformRangeEndIter<'a> {
300     fn new(func: &'a FuncTransform, addr: WasmAddress) -> Self {
301         let found = match func
302             .lookup
303             .index
304             .binary_search_by(|entry| entry.0.cmp(&addr))
305         {
306             Ok(i) => Some(&func.lookup.index[i].1),
307             Err(i) => {
308                 if i > 0 {
309                     Some(&func.lookup.index[i - 1].1)
310                 } else {
311                     None
312                 }
313             }
314         };
315         if let Some(range_indices) = found {
316             TransformRangeEndIter {
317                 addr,
318                 indices: range_indices,
319                 ranges: &func.lookup.ranges,
320             }
321         } else {
322             unreachable!();
323         }
324     }
325 }
326 
327 impl<'a> Iterator for TransformRangeEndIter<'a> {
328     type Item = (GeneratedAddress, RangeIndex);
329     fn next(&mut self) -> Option<Self::Item> {
330         while let Some((first, tail)) = self.indices.split_first() {
331             let range_index = *first;
332             let range = &self.ranges[range_index];
333             self.indices = tail;
334             if range.wasm_start >= self.addr {
335                 continue;
336             }
337             let address = match range
338                 .positions
339                 .binary_search_by(|a| a.wasm_pos.cmp(&self.addr))
340             {
341                 Ok(i) => range.positions[i].gen_end,
342                 Err(i) => {
343                     if i == range.positions.len() {
344                         range.gen_end
345                     } else {
346                         range.positions[i].gen_start
347                     }
348                 }
349             };
350             return Some((address, range_index));
351         }
352         None
353     }
354 }
355 
356 // Utility iterator to iterate by translated function ranges.
357 pub struct TransformRangeIter<'a> {
358     func: &'a FuncTransform,
359     start_it: TransformRangeStartIter<'a>,
360     end_it: TransformRangeEndIter<'a>,
361     last_start: Option<(GeneratedAddress, RangeIndex)>,
362     last_end: Option<(GeneratedAddress, RangeIndex)>,
363     last_item: Option<(GeneratedAddress, GeneratedAddress)>,
364 }
365 
366 impl<'a> TransformRangeIter<'a> {
367     fn new(func: &'a FuncTransform, start: WasmAddress, end: WasmAddress) -> Self {
368         let mut start_it = TransformRangeStartIter::new(func, start);
369         let last_start = start_it.next();
370         let mut end_it = TransformRangeEndIter::new(func, end);
371         let last_end = end_it.next();
372         TransformRangeIter {
373             func,
374             start_it,
375             end_it,
376             last_start,
377             last_end,
378             last_item: None,
379         }
380     }
381 }
382 
383 impl<'a> Iterator for TransformRangeIter<'a> {
384     type Item = (GeneratedAddress, GeneratedAddress);
385     fn next(&mut self) -> Option<Self::Item> {
386         loop {
387             // Merge TransformRangeStartIter and TransformRangeEndIter data using
388             // FuncLookup index's field propery to be sorted by RangeIndex.
389             let (start, end, range_index): (
390                 Option<GeneratedAddress>,
391                 Option<GeneratedAddress>,
392                 RangeIndex,
393             ) = {
394                 match (self.last_start.as_ref(), self.last_end.as_ref()) {
395                     (Some((s, sri)), Some((e, eri))) => {
396                         if sri == eri {
397                             // Start and end RangeIndex matched.
398                             (Some(*s), Some(*e), *sri)
399                         } else if sri < eri {
400                             (Some(*s), None, *sri)
401                         } else {
402                             (None, Some(*e), *eri)
403                         }
404                     }
405                     (Some((s, sri)), None) => (Some(*s), None, *sri),
406                     (None, Some((e, eri))) => (None, Some(*e), *eri),
407                     (None, None) => {
408                         // Reached ends for start and end iterators.
409                         return None;
410                     }
411                 }
412             };
413             let range_start = match start {
414                 Some(range_start) => {
415                     // Consume start iterator.
416                     self.last_start = self.start_it.next();
417                     range_start
418                 }
419                 None => {
420                     let range = &self.func.lookup.ranges[range_index];
421                     range.gen_start
422                 }
423             };
424             let range_end = match end {
425                 Some(range_end) => {
426                     // Consume end iterator.
427                     self.last_end = self.end_it.next();
428                     range_end
429                 }
430                 None => {
431                     let range = &self.func.lookup.ranges[range_index];
432                     range.gen_end
433                 }
434             };
435 
436             if cfg!(debug_assertions) {
437                 match self.last_item.replace((range_start, range_end)) {
438                     Some((_, last_end)) => debug_assert!(last_end <= range_start),
439                     None => (),
440                 }
441             }
442 
443             if range_start < range_end {
444                 return Some((range_start, range_end));
445             }
446             // Throw away empty ranges.
447             debug_assert!(range_start == range_end);
448         }
449     }
450 }
451 
452 impl AddressTransform {
453     pub fn new(funcs: &CompiledFunctionsMetadata, wasm_file: &WasmFileInfo) -> Self {
454         let code_section_offset = wasm_file.code_section_offset;
455 
456         let mut func = BTreeMap::new();
457         for (i, metadata) in funcs {
458             let (fn_start, fn_end, lookup) =
459                 build_function_lookup(&metadata.address_map, code_section_offset);
460 
461             func.insert(
462                 fn_start,
463                 FuncTransform {
464                     start: fn_start,
465                     end: fn_end,
466                     index: i,
467                     lookup,
468                 },
469             );
470         }
471 
472         let map = build_function_addr_map(funcs, code_section_offset);
473         let func = Vec::from_iter(func.into_iter());
474         AddressTransform { map, func }
475     }
476 
477     fn find_func(&self, addr: u64) -> Option<&FuncTransform> {
478         // TODO check if we need to include end address
479         let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) {
480             Ok(i) => &self.func[i].1,
481             Err(i) => {
482                 if i > 0 {
483                     &self.func[i - 1].1
484                 } else {
485                     return None;
486                 }
487             }
488         };
489         if addr >= func.start {
490             return Some(func);
491         }
492         None
493     }
494 
495     pub fn find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex> {
496         self.find_func(addr).map(|f| f.index)
497     }
498 
499     pub fn translate_raw(&self, addr: u64) -> Option<(DefinedFuncIndex, GeneratedAddress)> {
500         if addr == 0 {
501             // It's normally 0 for debug info without the linked code.
502             return None;
503         }
504         if let Some(func) = self.find_func(addr) {
505             if addr == func.end {
506                 // Clamp last address to the end to extend translation to the end
507                 // of the function.
508                 let map = &self.map[func.index];
509                 return Some((func.index, map.len));
510             }
511             let first_result = TransformRangeStartIter::new(func, addr).next();
512             first_result.map(|(address, _)| (func.index, address))
513         } else {
514             // Address was not found: function was not compiled?
515             None
516         }
517     }
518 
519     pub fn can_translate_address(&self, addr: u64) -> bool {
520         self.translate(addr).is_some()
521     }
522 
523     pub fn translate(&self, addr: u64) -> Option<write::Address> {
524         self.translate_raw(addr)
525             .map(|(func_index, address)| write::Address::Symbol {
526                 symbol: func_index.index(),
527                 addend: address as i64,
528             })
529     }
530 
531     pub fn translate_ranges_raw<'a>(
532         &'a self,
533         start: u64,
534         end: u64,
535     ) -> Option<(DefinedFuncIndex, impl Iterator<Item = (usize, usize)> + 'a)> {
536         if start == 0 {
537             // It's normally 0 for debug info without the linked code.
538             return None;
539         }
540         if let Some(func) = self.find_func(start) {
541             let result = TransformRangeIter::new(func, start, end);
542             return Some((func.index, result));
543         }
544         // Address was not found: function was not compiled?
545         None
546     }
547 
548     pub fn translate_ranges<'a>(
549         &'a self,
550         start: u64,
551         end: u64,
552     ) -> impl Iterator<Item = (write::Address, u64)> + 'a {
553         enum TranslateRangesResult<'a> {
554             Empty,
555             Raw {
556                 symbol: usize,
557                 it: Box<dyn Iterator<Item = (usize, usize)> + 'a>,
558             },
559         }
560         impl<'a> Iterator for TranslateRangesResult<'a> {
561             type Item = (write::Address, u64);
562             fn next(&mut self) -> Option<Self::Item> {
563                 match self {
564                     TranslateRangesResult::Empty => None,
565                     TranslateRangesResult::Raw { symbol, it } => match it.next() {
566                         Some((start, end)) => {
567                             debug_assert!(start < end);
568                             Some((
569                                 write::Address::Symbol {
570                                     symbol: *symbol,
571                                     addend: start as i64,
572                                 },
573                                 (end - start) as u64,
574                             ))
575                         }
576                         None => None,
577                     },
578                 }
579             }
580         }
581 
582         match self.translate_ranges_raw(start, end) {
583             Some((func_index, ranges)) => TranslateRangesResult::Raw {
584                 symbol: func_index.index(),
585                 it: Box::new(ranges),
586             },
587             None => TranslateRangesResult::Empty,
588         }
589     }
590 
591     pub fn map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap> {
592         &self.map
593     }
594 
595     pub fn func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress) {
596         let map = &self.map[index];
597         (map.offset, map.offset + map.len)
598     }
599 
600     pub fn func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress) {
601         let map = &self.map[index];
602         (map.wasm_start, map.wasm_end)
603     }
604 }
605 
606 #[cfg(test)]
607 mod tests {
608     use super::{build_function_lookup, get_wasm_code_offset, AddressTransform};
609     use cranelift_entity::PrimaryMap;
610     use gimli::write::Address;
611     use std::iter::FromIterator;
612     use std::mem;
613     use wasmtime_cranelift_shared::{CompiledFunctionMetadata, FunctionAddressMap};
614     use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo};
615 
616     #[test]
617     fn test_get_wasm_code_offset() {
618         let offset = get_wasm_code_offset(FilePos::new(3), 1);
619         assert_eq!(2, offset);
620         let offset = get_wasm_code_offset(FilePos::new(16), 0xF000_0000);
621         assert_eq!(0x1000_0010, offset);
622         let offset = get_wasm_code_offset(FilePos::new(1), 0x20_8000_0000);
623         assert_eq!(0x8000_0001, offset);
624     }
625 
626     fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap {
627         FunctionAddressMap {
628             instructions: vec![
629                 InstructionAddressMap {
630                     srcloc: FilePos::new(wasm_offset + 2),
631                     code_offset: 5,
632                 },
633                 InstructionAddressMap {
634                     srcloc: FilePos::default(),
635                     code_offset: 8,
636                 },
637                 InstructionAddressMap {
638                     srcloc: FilePos::new(wasm_offset + 7),
639                     code_offset: 15,
640                 },
641                 InstructionAddressMap {
642                     srcloc: FilePos::default(),
643                     code_offset: 23,
644                 },
645             ]
646             .into(),
647             start_srcloc: FilePos::new(wasm_offset),
648             end_srcloc: FilePos::new(wasm_offset + 10),
649             body_offset: 0,
650             body_len: 30,
651         }
652     }
653 
654     #[test]
655     fn test_build_function_lookup_simple() {
656         let input = create_simple_func(11);
657         let (start, end, lookup) = build_function_lookup(&input, 1);
658         assert_eq!(10, start);
659         assert_eq!(20, end);
660 
661         assert_eq!(1, lookup.index.len());
662         let index_entry = lookup.index.into_iter().next().unwrap();
663         assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry);
664         assert_eq!(1, lookup.ranges.len());
665         let range = &lookup.ranges[0];
666         assert_eq!(10, range.wasm_start);
667         assert_eq!(20, range.wasm_end);
668         assert_eq!(0, range.gen_start);
669         assert_eq!(30, range.gen_end);
670         let positions = &range.positions;
671         assert_eq!(2, positions.len());
672         assert_eq!(12, positions[0].wasm_pos);
673         assert_eq!(5, positions[0].gen_start);
674         assert_eq!(8, positions[0].gen_end);
675         assert_eq!(17, positions[1].wasm_pos);
676         assert_eq!(15, positions[1].gen_start);
677         assert_eq!(23, positions[1].gen_end);
678     }
679 
680     #[test]
681     fn test_build_function_lookup_two_ranges() {
682         let mut input = create_simple_func(11);
683         // append instruction with same srcloc as input.instructions[0]
684         let mut list = Vec::from(mem::take(&mut input.instructions));
685         list.push(InstructionAddressMap {
686             srcloc: FilePos::new(11 + 2),
687             code_offset: 23,
688         });
689         list.push(InstructionAddressMap {
690             srcloc: FilePos::default(),
691             code_offset: 26,
692         });
693         input.instructions = list.into();
694         let (start, end, lookup) = build_function_lookup(&input, 1);
695         assert_eq!(10, start);
696         assert_eq!(20, end);
697 
698         assert_eq!(2, lookup.index.len());
699         let index_entries = Vec::from_iter(lookup.index.into_iter());
700         assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]);
701         assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]);
702         assert_eq!(2, lookup.ranges.len());
703 
704         let range = &lookup.ranges[0];
705         assert_eq!(10, range.wasm_start);
706         assert_eq!(17, range.wasm_end);
707         assert_eq!(0, range.gen_start);
708         assert_eq!(23, range.gen_end);
709         let positions = &range.positions;
710         assert_eq!(2, positions.len());
711         assert_eq!(12, positions[0].wasm_pos);
712         assert_eq!(5, positions[0].gen_start);
713         assert_eq!(8, positions[0].gen_end);
714         assert_eq!(17, positions[1].wasm_pos);
715         assert_eq!(15, positions[1].gen_start);
716         assert_eq!(23, positions[1].gen_end);
717 
718         let range = &lookup.ranges[1];
719         assert_eq!(12, range.wasm_start);
720         assert_eq!(20, range.wasm_end);
721         assert_eq!(23, range.gen_start);
722         assert_eq!(30, range.gen_end);
723         let positions = &range.positions;
724         assert_eq!(1, positions.len());
725         assert_eq!(12, positions[0].wasm_pos);
726         assert_eq!(23, positions[0].gen_start);
727         assert_eq!(26, positions[0].gen_end);
728     }
729 
730     #[test]
731     fn test_addr_translate() {
732         let func = CompiledFunctionMetadata {
733             address_map: create_simple_func(11),
734             ..Default::default()
735         };
736         let input = PrimaryMap::from_iter([&func]);
737         let at = AddressTransform::new(
738             &input,
739             &WasmFileInfo {
740                 path: None,
741                 code_section_offset: 1,
742                 imported_func_count: 0,
743                 funcs: Vec::new(),
744             },
745         );
746 
747         let addr = at.translate(10);
748         assert_eq!(
749             Some(Address::Symbol {
750                 symbol: 0,
751                 addend: 0,
752             }),
753             addr
754         );
755 
756         let addr = at.translate(20);
757         assert_eq!(
758             Some(Address::Symbol {
759                 symbol: 0,
760                 addend: 30,
761             }),
762             addr
763         );
764 
765         let addr = at.translate(0);
766         assert_eq!(None, addr);
767 
768         let addr = at.translate(12);
769         assert_eq!(
770             Some(Address::Symbol {
771                 symbol: 0,
772                 addend: 5,
773             }),
774             addr
775         );
776 
777         let addr = at.translate(18);
778         assert_eq!(
779             Some(Address::Symbol {
780                 symbol: 0,
781                 addend: 23,
782             }),
783             addr
784         );
785     }
786 }
787