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