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