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