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