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. 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!(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 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> { 246 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); 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> { 309 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); 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> { 376 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); 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 { 462 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)] 494 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 let mut translations = wasmtime_environ::PrimaryMap::new(); 502 let mut translation = wasmtime_environ::ModuleTranslation::default(); 503 translation.debuginfo.wasm_file = wasm_file; 504 translation 505 .module 506 .push_function(wasmtime_environ::ModuleInternedTypeIndex::from_u32(0)); 507 translations.push(translation); 508 509 let mut dummy_obj = object::write::Object::new( 510 object::BinaryFormat::Elf, 511 object::Architecture::Wasm32, 512 object::Endianness::Little, 513 ); 514 let dummy_symbol = dummy_obj.add_file_symbol(Vec::new()); 515 let func_lookup = move |_, f| (dummy_symbol, module_map[f]); 516 let tunables = wasmtime_environ::Tunables::default_host(); 517 let compile = Compilation::new( 518 &*cranelift_codegen::isa::lookup(target_lexicon::Triple::host()) 519 .unwrap() 520 .finish(cranelift_codegen::settings::Flags::new( 521 cranelift_codegen::settings::builder(), 522 )) 523 .unwrap(), 524 &translations, 525 &func_lookup, 526 None, 527 &tunables, 528 ); 529 Self::new(&compile, StaticModuleIndex::from_u32(0)) 530 } 531 532 fn find_func(&self, addr: u64) -> Option<&FuncTransform> { 533 // TODO check if we need to include end address 534 let func = match self.func.binary_search_by(|entry| entry.0.cmp(&addr)) { 535 Ok(i) => &self.func[i].1, 536 Err(i) => { 537 if i > 0 { 538 &self.func[i - 1].1 539 } else { 540 return None; 541 } 542 } 543 }; 544 if addr >= func.start { 545 return Some(func); 546 } 547 None 548 } 549 550 pub fn find_func_index(&self, addr: u64) -> Option<DefinedFuncIndex> { 551 self.find_func(addr).map(|f| f.index) 552 } 553 554 fn translate_raw(&self, addr: u64) -> Option<(usize, GeneratedAddress)> { 555 const TOMBSTONE: u64 = u32::MAX as u64; 556 if addr == 0 || addr == TOMBSTONE { 557 // Addresses for unlinked code may be left as 0 or replaced 558 // with -1, depending on the linker used. 559 return None; 560 } 561 if let Some(func) = self.find_func(addr) { 562 let map = &self.map[func.index]; 563 if addr == func.end { 564 // Clamp last address to the end to extend translation to the end 565 // of the function. 566 return Some((map.symbol, map.len)); 567 } 568 let first_result = TransformRangeStartIter::new(func, addr).next(); 569 first_result.map(|(address, _)| (map.symbol, address)) 570 } else { 571 // Address was not found: function was not compiled? 572 None 573 } 574 } 575 576 pub fn can_translate_address(&self, addr: u64) -> bool { 577 self.translate(addr).is_some() 578 } 579 580 pub fn translate(&self, addr: u64) -> Option<write::Address> { 581 self.translate_raw(addr) 582 .map(|(symbol, address)| write::Address::Symbol { 583 symbol, 584 addend: address as i64, 585 }) 586 } 587 588 pub fn translate_ranges_raw<'a>( 589 &'a self, 590 start: u64, 591 end: u64, 592 ) -> Option<(usize, impl Iterator<Item = (usize, usize)> + 'a)> { 593 if start == 0 { 594 // It's normally 0 for debug info without the linked code. 595 return None; 596 } 597 if let Some(func) = self.find_func(start) { 598 let result = TransformRangeIter::new(func, start, end); 599 let symbol = self.map[func.index].symbol; 600 return Some((symbol, result)); 601 } 602 // Address was not found: function was not compiled? 603 None 604 } 605 606 pub fn translate_ranges<'a>( 607 &'a self, 608 start: u64, 609 end: u64, 610 ) -> impl Iterator<Item = (write::Address, u64)> + 'a { 611 enum TranslateRangesResult<'a> { 612 Empty, 613 Raw { 614 symbol: usize, 615 it: Box<dyn Iterator<Item = (usize, usize)> + 'a>, 616 }, 617 } 618 impl<'a> Iterator for TranslateRangesResult<'a> { 619 type Item = (write::Address, u64); 620 fn next(&mut self) -> Option<Self::Item> { 621 match self { 622 TranslateRangesResult::Empty => None, 623 TranslateRangesResult::Raw { symbol, it } => match it.next() { 624 Some((start, end)) => { 625 debug_assert!(start < end); 626 Some(( 627 write::Address::Symbol { 628 symbol: *symbol, 629 addend: start as i64, 630 }, 631 (end - start) as u64, 632 )) 633 } 634 None => None, 635 }, 636 } 637 } 638 } 639 640 match self.translate_ranges_raw(start, end) { 641 Some((symbol, ranges)) => TranslateRangesResult::Raw { 642 symbol, 643 it: Box::new(ranges), 644 }, 645 None => TranslateRangesResult::Empty, 646 } 647 } 648 649 pub fn map(&self) -> &PrimaryMap<DefinedFuncIndex, FunctionMap> { 650 &self.map 651 } 652 653 pub fn func_range(&self, index: DefinedFuncIndex) -> (GeneratedAddress, GeneratedAddress) { 654 let map = &self.map[index]; 655 (map.offset, map.offset + map.len) 656 } 657 658 pub fn func_source_range(&self, index: DefinedFuncIndex) -> (WasmAddress, WasmAddress) { 659 let map = &self.map[index]; 660 (map.wasm_start, map.wasm_end) 661 } 662 } 663 664 #[cfg(test)] 665 mod tests { 666 use super::{AddressTransform, build_function_lookup, get_wasm_code_offset}; 667 use crate::{CompiledFunctionMetadata, FunctionAddressMap}; 668 use cranelift_entity::PrimaryMap; 669 use gimli::write::Address; 670 use std::mem; 671 use wasmtime_environ::{FilePos, InstructionAddressMap, WasmFileInfo}; 672 673 #[test] 674 fn test_get_wasm_code_offset() { 675 let offset = get_wasm_code_offset(FilePos::new(3), 1); 676 assert_eq!(2, offset); 677 let offset = get_wasm_code_offset(FilePos::new(16), 0xF000_0000); 678 assert_eq!(0x1000_0010, offset); 679 let offset = get_wasm_code_offset(FilePos::new(1), 0x20_8000_0000); 680 assert_eq!(0x8000_0001, offset); 681 } 682 683 fn create_simple_func(wasm_offset: u32) -> FunctionAddressMap { 684 FunctionAddressMap { 685 instructions: vec![ 686 InstructionAddressMap { 687 srcloc: FilePos::new(wasm_offset + 2), 688 code_offset: 5, 689 }, 690 InstructionAddressMap { 691 srcloc: FilePos::default(), 692 code_offset: 8, 693 }, 694 InstructionAddressMap { 695 srcloc: FilePos::new(wasm_offset + 7), 696 code_offset: 15, 697 }, 698 InstructionAddressMap { 699 srcloc: FilePos::default(), 700 code_offset: 23, 701 }, 702 ] 703 .into(), 704 start_srcloc: FilePos::new(wasm_offset), 705 end_srcloc: FilePos::new(wasm_offset + 10), 706 body_offset: 0, 707 body_len: 30, 708 } 709 } 710 711 #[test] 712 fn test_build_function_lookup_simple() { 713 let input = create_simple_func(11); 714 let (start, end, lookup) = build_function_lookup(&input, 1); 715 assert_eq!(10, start); 716 assert_eq!(20, end); 717 718 assert_eq!(1, lookup.index.len()); 719 let index_entry = lookup.index.into_iter().next().unwrap(); 720 assert_eq!((10u64, vec![0].into_boxed_slice()), index_entry); 721 assert_eq!(1, lookup.ranges.len()); 722 let range = &lookup.ranges[0]; 723 assert_eq!(10, range.wasm_start); 724 assert_eq!(20, range.wasm_end); 725 assert_eq!(0, range.gen_start); 726 assert_eq!(30, range.gen_end); 727 let positions = &range.positions; 728 assert_eq!(2, positions.len()); 729 assert_eq!(12, positions[0].wasm_pos); 730 assert_eq!(5, positions[0].gen_start); 731 assert_eq!(8, positions[0].gen_end); 732 assert_eq!(17, positions[1].wasm_pos); 733 assert_eq!(15, positions[1].gen_start); 734 assert_eq!(23, positions[1].gen_end); 735 } 736 737 #[test] 738 fn test_build_function_lookup_two_ranges() { 739 let mut input = create_simple_func(11); 740 // append instruction with same srcloc as input.instructions[0] 741 let mut list = Vec::from(mem::take(&mut input.instructions)); 742 list.push(InstructionAddressMap { 743 srcloc: FilePos::new(11 + 2), 744 code_offset: 23, 745 }); 746 list.push(InstructionAddressMap { 747 srcloc: FilePos::default(), 748 code_offset: 26, 749 }); 750 input.instructions = list.into(); 751 let (start, end, lookup) = build_function_lookup(&input, 1); 752 assert_eq!(10, start); 753 assert_eq!(20, end); 754 755 assert_eq!(2, lookup.index.len()); 756 let index_entries = Vec::from_iter(lookup.index); 757 assert_eq!((10u64, vec![0].into_boxed_slice()), index_entries[0]); 758 assert_eq!((12u64, vec![0, 1].into_boxed_slice()), index_entries[1]); 759 assert_eq!(2, lookup.ranges.len()); 760 761 let range = &lookup.ranges[0]; 762 assert_eq!(10, range.wasm_start); 763 assert_eq!(17, range.wasm_end); 764 assert_eq!(0, range.gen_start); 765 assert_eq!(23, range.gen_end); 766 let positions = &range.positions; 767 assert_eq!(2, positions.len()); 768 assert_eq!(12, positions[0].wasm_pos); 769 assert_eq!(5, positions[0].gen_start); 770 assert_eq!(8, positions[0].gen_end); 771 assert_eq!(17, positions[1].wasm_pos); 772 assert_eq!(15, positions[1].gen_start); 773 assert_eq!(23, positions[1].gen_end); 774 775 let range = &lookup.ranges[1]; 776 assert_eq!(12, range.wasm_start); 777 assert_eq!(20, range.wasm_end); 778 assert_eq!(23, range.gen_start); 779 assert_eq!(30, range.gen_end); 780 let positions = &range.positions; 781 assert_eq!(1, positions.len()); 782 assert_eq!(12, positions[0].wasm_pos); 783 assert_eq!(23, positions[0].gen_start); 784 assert_eq!(26, positions[0].gen_end); 785 } 786 787 #[test] 788 fn test_addr_translate() { 789 // Ignore this test if cranelift doesn't support the native platform. 790 if cranelift_native::builder().is_err() { 791 return; 792 } 793 let func = CompiledFunctionMetadata { 794 address_map: create_simple_func(11), 795 ..Default::default() 796 }; 797 let input = PrimaryMap::from_iter([&func]); 798 let at = AddressTransform::mock( 799 &input, 800 WasmFileInfo { 801 path: None, 802 code_section_offset: 1, 803 imported_func_count: 0, 804 funcs: Vec::new(), 805 }, 806 ); 807 808 let addr = at.translate(10); 809 assert_eq!( 810 Some(Address::Symbol { 811 symbol: 0, 812 addend: 0, 813 }), 814 addr 815 ); 816 817 let addr = at.translate(20); 818 assert_eq!( 819 Some(Address::Symbol { 820 symbol: 0, 821 addend: 30, 822 }), 823 addr 824 ); 825 826 let addr = at.translate(0); 827 assert_eq!(None, addr); 828 829 let addr = at.translate(12); 830 assert_eq!( 831 Some(Address::Symbol { 832 symbol: 0, 833 addend: 5, 834 }), 835 addr 836 ); 837 838 let addr = at.translate(18); 839 assert_eq!( 840 Some(Address::Symbol { 841 symbol: 0, 842 addend: 23, 843 }), 844 addr 845 ); 846 } 847 } 848