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