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