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