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