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