1 use super::address_transform::AddressTransform; 2 use super::dbi_log; 3 use crate::debug::transform::debug_transform_logging::{ 4 dbi_log_enabled, log_get_value_loc, log_get_value_name, log_get_value_ranges, 5 }; 6 use crate::debug::ModuleMemoryOffset; 7 use crate::translate::get_vmctx_value_label; 8 use anyhow::{Context, Error, Result}; 9 use core::fmt; 10 use cranelift_codegen::ir::ValueLabel; 11 use cranelift_codegen::isa::TargetIsa; 12 use cranelift_codegen::LabelValueLoc; 13 use cranelift_codegen::ValueLabelsRanges; 14 use gimli::{write, Expression, Operation, Reader, ReaderOffset}; 15 use itertools::Itertools; 16 use std::cmp::PartialEq; 17 use std::collections::{HashMap, HashSet}; 18 use std::hash::{Hash, Hasher}; 19 use std::rc::Rc; 20 21 #[derive(Debug)] 22 pub struct FunctionFrameInfo<'a> { 23 pub value_ranges: &'a ValueLabelsRanges, 24 pub memory_offset: ModuleMemoryOffset, 25 } 26 27 struct ExpressionWriter(write::EndianVec<gimli::RunTimeEndian>); 28 29 enum VmctxBase { 30 Reg(u16), 31 OnStack, 32 } 33 34 impl ExpressionWriter { 35 fn new() -> Self { 36 let endian = gimli::RunTimeEndian::Little; 37 let writer = write::EndianVec::new(endian); 38 ExpressionWriter(writer) 39 } 40 41 fn write_op(&mut self, op: gimli::DwOp) -> write::Result<()> { 42 self.write_u8(op.0) 43 } 44 45 fn write_op_reg(&mut self, reg: u16) -> write::Result<()> { 46 if reg < 32 { 47 self.write_u8(gimli::constants::DW_OP_reg0.0 + reg as u8) 48 } else { 49 self.write_op(gimli::constants::DW_OP_regx)?; 50 self.write_uleb128(reg.into()) 51 } 52 } 53 54 fn write_op_breg(&mut self, reg: u16) -> write::Result<()> { 55 if reg < 32 { 56 self.write_u8(gimli::constants::DW_OP_breg0.0 + reg as u8) 57 } else { 58 self.write_op(gimli::constants::DW_OP_bregx)?; 59 self.write_uleb128(reg.into()) 60 } 61 } 62 63 fn write_u8(&mut self, b: u8) -> write::Result<()> { 64 write::Writer::write_u8(&mut self.0, b) 65 } 66 67 fn write_u32(&mut self, b: u32) -> write::Result<()> { 68 write::Writer::write_u32(&mut self.0, b) 69 } 70 71 fn write_uleb128(&mut self, i: u64) -> write::Result<()> { 72 write::Writer::write_uleb128(&mut self.0, i) 73 } 74 75 fn write_sleb128(&mut self, i: i64) -> write::Result<()> { 76 write::Writer::write_sleb128(&mut self.0, i) 77 } 78 79 fn into_vec(self) -> Vec<u8> { 80 self.0.into_vec() 81 } 82 83 fn gen_address_of_memory_base_pointer( 84 &mut self, 85 vmctx: VmctxBase, 86 memory_base: &ModuleMemoryOffset, 87 ) -> write::Result<()> { 88 match *memory_base { 89 ModuleMemoryOffset::Defined(offset) => match vmctx { 90 VmctxBase::Reg(reg) => { 91 self.write_op_breg(reg)?; 92 self.write_sleb128(offset.into())?; 93 } 94 VmctxBase::OnStack => { 95 self.write_op(gimli::constants::DW_OP_consts)?; 96 self.write_sleb128(offset.into())?; 97 self.write_op(gimli::constants::DW_OP_plus)?; 98 } 99 }, 100 ModuleMemoryOffset::Imported { 101 offset_to_vm_memory_definition, 102 offset_to_memory_base, 103 } => { 104 match vmctx { 105 VmctxBase::Reg(reg) => { 106 self.write_op_breg(reg)?; 107 self.write_sleb128(offset_to_vm_memory_definition.into())?; 108 } 109 VmctxBase::OnStack => { 110 if offset_to_vm_memory_definition > 0 { 111 self.write_op(gimli::constants::DW_OP_consts)?; 112 self.write_sleb128(offset_to_vm_memory_definition.into())?; 113 } 114 self.write_op(gimli::constants::DW_OP_plus)?; 115 } 116 } 117 self.write_op(gimli::constants::DW_OP_deref)?; 118 if offset_to_memory_base > 0 { 119 self.write_op(gimli::constants::DW_OP_consts)?; 120 self.write_sleb128(offset_to_memory_base.into())?; 121 self.write_op(gimli::constants::DW_OP_plus)?; 122 } 123 } 124 ModuleMemoryOffset::None => return Err(write::Error::InvalidAttributeValue), 125 } 126 Ok(()) 127 } 128 } 129 130 #[derive(Debug, Clone, PartialEq)] 131 enum CompiledExpressionPart { 132 // Untranslated DWARF expression. 133 Code(Vec<u8>), 134 // The wasm-local DWARF operator. The label points to `ValueLabel`. 135 // The trailing field denotes that the operator was last in sequence, 136 // and it is the DWARF location (not a pointer). 137 Local { 138 label: ValueLabel, 139 trailing: bool, 140 }, 141 // Dereference is needed. 142 Deref, 143 // Jumping in the expression. 144 Jump { 145 conditionally: bool, 146 target: JumpTargetMarker, 147 }, 148 // Floating landing pad. 149 LandingPad(JumpTargetMarker), 150 } 151 152 #[derive(Debug, Clone, PartialEq)] 153 pub struct CompiledExpression { 154 parts: Vec<CompiledExpressionPart>, 155 need_deref: bool, 156 } 157 158 impl CompiledExpression { 159 pub fn vmctx() -> CompiledExpression { 160 CompiledExpression::from_label(get_vmctx_value_label()) 161 } 162 163 pub fn from_label(label: ValueLabel) -> CompiledExpression { 164 CompiledExpression { 165 parts: vec![CompiledExpressionPart::Local { 166 label, 167 trailing: true, 168 }], 169 need_deref: false, 170 } 171 } 172 } 173 174 fn translate_loc( 175 loc: LabelValueLoc, 176 isa: &dyn TargetIsa, 177 add_stack_value: bool, 178 ) -> Result<Option<Vec<u8>>> { 179 Ok(match loc { 180 LabelValueLoc::Reg(r) => { 181 let machine_reg = isa.map_regalloc_reg_to_dwarf(r)?; 182 let mut writer = ExpressionWriter::new(); 183 if add_stack_value { 184 writer.write_op_reg(machine_reg)?; 185 } else { 186 writer.write_op_breg(machine_reg)?; 187 writer.write_sleb128(0)?; 188 } 189 Some(writer.into_vec()) 190 } 191 LabelValueLoc::CFAOffset(off) => { 192 let mut writer = ExpressionWriter::new(); 193 writer.write_op(gimli::constants::DW_OP_fbreg)?; 194 writer.write_sleb128(off)?; 195 if !add_stack_value { 196 writer.write_op(gimli::constants::DW_OP_deref)?; 197 } 198 return Ok(Some(writer.into_vec())); 199 } 200 }) 201 } 202 203 fn append_memory_deref( 204 buf: &mut Vec<u8>, 205 frame_info: &FunctionFrameInfo, 206 vmctx_loc: LabelValueLoc, 207 isa: &dyn TargetIsa, 208 ) -> Result<bool> { 209 let mut writer = ExpressionWriter::new(); 210 let vmctx_base = match vmctx_loc { 211 LabelValueLoc::Reg(r) => VmctxBase::Reg(isa.map_regalloc_reg_to_dwarf(r)?), 212 LabelValueLoc::CFAOffset(off) => { 213 writer.write_op(gimli::constants::DW_OP_fbreg)?; 214 writer.write_sleb128(off)?; 215 writer.write_op(gimli::constants::DW_OP_deref)?; 216 VmctxBase::OnStack 217 } 218 }; 219 writer.gen_address_of_memory_base_pointer(vmctx_base, &frame_info.memory_offset)?; 220 writer.write_op(gimli::constants::DW_OP_deref)?; 221 writer.write_op(gimli::constants::DW_OP_swap)?; 222 writer.write_op(gimli::constants::DW_OP_const4u)?; 223 writer.write_u32(0xffff_ffff)?; 224 writer.write_op(gimli::constants::DW_OP_and)?; 225 writer.write_op(gimli::constants::DW_OP_plus)?; 226 buf.extend(writer.into_vec()); 227 Ok(true) 228 } 229 230 pub struct BuiltCompiledExpression<TIter> { 231 pub expressions: TIter, 232 pub covers_entire_scope: bool, 233 } 234 235 impl CompiledExpression { 236 pub fn is_simple(&self) -> bool { 237 if let [CompiledExpressionPart::Code(_)] = self.parts.as_slice() { 238 true 239 } else { 240 self.parts.is_empty() 241 } 242 } 243 244 pub fn build(&self) -> Option<write::Expression> { 245 if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { 246 return Some(write::Expression::raw(code.to_vec())); 247 } 248 // locals found, not supported 249 None 250 } 251 252 pub fn build_with_locals<'a>( 253 &'a self, 254 scope: &'a [(u64, u64)], // wasm ranges 255 addr_tr: &'a AddressTransform, 256 frame_info: Option<&'a FunctionFrameInfo>, 257 isa: &'a dyn TargetIsa, 258 ) -> BuiltCompiledExpression< 259 impl Iterator<Item = Result<(write::Address, u64, write::Expression)>> + use<'a>, 260 > { 261 enum BuildWithLocalsResult<'a> { 262 Empty, 263 Simple( 264 Box<dyn Iterator<Item = (write::Address, u64)> + 'a>, 265 Vec<u8>, 266 ), 267 Ranges(Box<dyn Iterator<Item = Result<(usize, usize, usize, Vec<u8>)>> + 'a>), 268 } 269 impl Iterator for BuildWithLocalsResult<'_> { 270 type Item = Result<(write::Address, u64, write::Expression)>; 271 fn next(&mut self) -> Option<Self::Item> { 272 match self { 273 BuildWithLocalsResult::Empty => None, 274 BuildWithLocalsResult::Simple(it, code) => it 275 .next() 276 .map(|(addr, len)| Ok((addr, len, write::Expression::raw(code.to_vec())))), 277 BuildWithLocalsResult::Ranges(it) => it.next().map(|r| { 278 r.map(|(symbol, start, end, code_buf)| { 279 ( 280 write::Address::Symbol { 281 symbol, 282 addend: start as i64, 283 }, 284 (end - start) as u64, 285 write::Expression::raw(code_buf), 286 ) 287 }) 288 }), 289 } 290 } 291 } 292 293 if scope.is_empty() { 294 return BuiltCompiledExpression { 295 expressions: BuildWithLocalsResult::Empty, 296 covers_entire_scope: false, 297 }; 298 } 299 300 // If it a simple DWARF code, no need in locals processing. Just translate 301 // the scope ranges. 302 if let [CompiledExpressionPart::Code(code)] = self.parts.as_slice() { 303 return BuiltCompiledExpression { 304 expressions: BuildWithLocalsResult::Simple( 305 Box::new(scope.iter().flat_map(move |(wasm_start, wasm_end)| { 306 addr_tr.translate_ranges(*wasm_start, *wasm_end) 307 })), 308 code.clone(), 309 ), 310 covers_entire_scope: false, 311 }; 312 } 313 314 let vmctx_label = get_vmctx_value_label(); 315 316 // Some locals are present, preparing and divided ranges based on the scope 317 // and frame_info data. 318 let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info, isa); 319 for p in self.parts.iter() { 320 match p { 321 CompiledExpressionPart::Code(_) 322 | CompiledExpressionPart::Jump { .. } 323 | CompiledExpressionPart::LandingPad { .. } => (), 324 CompiledExpressionPart::Local { label, .. } => ranges_builder.process_label(*label), 325 CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label), 326 } 327 } 328 if self.need_deref { 329 ranges_builder.process_label(vmctx_label); 330 } 331 332 let ranges = ranges_builder.into_ranges(); 333 let expressions = BuildWithLocalsResult::Ranges(Box::new( 334 ranges 335 .ranges 336 .into_iter() 337 .map( 338 move |CachedValueLabelRange { 339 func_index, 340 start, 341 end, 342 label_location, 343 }| { 344 // build expression 345 let mut code_buf = Vec::new(); 346 let mut jump_positions = Vec::new(); 347 let mut landing_positions = HashMap::new(); 348 349 macro_rules! deref { 350 () => { 351 if let (Some(vmctx_loc), Some(frame_info)) = 352 (label_location.get(&vmctx_label), frame_info) 353 { 354 if !append_memory_deref( 355 &mut code_buf, 356 frame_info, 357 *vmctx_loc, 358 isa, 359 )? { 360 return Ok(None); 361 } 362 } else { 363 return Ok(None); 364 } 365 }; 366 } 367 for part in &self.parts { 368 match part { 369 CompiledExpressionPart::Code(c) => { 370 code_buf.extend_from_slice(c.as_slice()) 371 } 372 CompiledExpressionPart::LandingPad(marker) => { 373 landing_positions.insert(marker.clone(), code_buf.len()); 374 } 375 CompiledExpressionPart::Jump { 376 conditionally, 377 target, 378 } => { 379 code_buf.push( 380 match conditionally { 381 true => gimli::constants::DW_OP_bra, 382 false => gimli::constants::DW_OP_skip, 383 } 384 .0, 385 ); 386 code_buf.push(!0); 387 code_buf.push(!0); // these will be relocated below 388 jump_positions.push((target.clone(), code_buf.len())); 389 } 390 CompiledExpressionPart::Local { label, trailing } => { 391 let loc = 392 *label_location.get(&label).context("label_location")?; 393 if let Some(expr) = translate_loc(loc, isa, *trailing)? { 394 code_buf.extend_from_slice(&expr) 395 } else { 396 return Ok(None); 397 } 398 } 399 CompiledExpressionPart::Deref => deref!(), 400 } 401 } 402 if self.need_deref { 403 deref!(); 404 } 405 406 for (marker, new_from) in jump_positions { 407 // relocate jump targets 408 let new_to = landing_positions[&marker]; 409 let new_diff = new_to as isize - new_from as isize; 410 // FIXME: use encoding? LittleEndian for now... 411 code_buf[new_from - 2..new_from] 412 .copy_from_slice(&(new_diff as i16).to_le_bytes()); 413 } 414 Ok(Some((func_index, start, end, code_buf))) 415 }, 416 ) 417 .filter_map(Result::transpose), 418 )); 419 420 BuiltCompiledExpression { 421 expressions, 422 covers_entire_scope: ranges.covers_entire_scope, 423 } 424 } 425 } 426 427 fn is_old_expression_format(buf: &[u8]) -> bool { 428 // Heuristic to detect old variable expression format without DW_OP_fbreg: 429 // DW_OP_plus_uconst op must be present, but not DW_OP_fbreg. 430 if buf.contains(&(gimli::constants::DW_OP_fbreg.0)) { 431 // Stop check if DW_OP_fbreg exist. 432 return false; 433 } 434 buf.contains(&(gimli::constants::DW_OP_plus_uconst.0)) 435 } 436 437 pub fn compile_expression<R>( 438 expr: &Expression<R>, 439 encoding: gimli::Encoding, 440 frame_base: Option<&CompiledExpression>, 441 ) -> Result<Option<CompiledExpression>, Error> 442 where 443 R: Reader, 444 { 445 // Bail when `frame_base` is complicated. 446 if let Some(expr) = frame_base { 447 if expr.parts.iter().any(|p| match p { 448 CompiledExpressionPart::Jump { .. } => true, 449 _ => false, 450 }) { 451 return Ok(None); 452 } 453 } 454 455 // jump_targets key is offset in buf starting from the end 456 // (see also `unread_bytes` below) 457 let mut jump_targets: HashMap<u64, JumpTargetMarker> = HashMap::new(); 458 let mut pc = expr.0.clone(); 459 460 let buf = expr.0.to_slice()?; 461 let mut parts = Vec::new(); 462 macro_rules! push { 463 ($part:expr) => {{ 464 let part = $part; 465 if let (CompiledExpressionPart::Code(cc2), Some(CompiledExpressionPart::Code(cc1))) = 466 (&part, parts.last_mut()) 467 { 468 cc1.extend_from_slice(cc2); 469 } else { 470 parts.push(part) 471 } 472 }}; 473 } 474 let mut need_deref = false; 475 if is_old_expression_format(&buf) && frame_base.is_some() { 476 // Still supporting old DWARF variable expressions without fbreg. 477 parts.extend_from_slice(&frame_base.unwrap().parts); 478 if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() { 479 *trailing = false; 480 } 481 need_deref = frame_base.unwrap().need_deref; 482 } 483 let mut code_chunk = Vec::new(); 484 macro_rules! flush_code_chunk { 485 () => { 486 if !code_chunk.is_empty() { 487 push!(CompiledExpressionPart::Code(code_chunk)); 488 code_chunk = Vec::new(); 489 let _ = code_chunk; // suppresses warning for final flush 490 } 491 }; 492 } 493 494 // Find all landing pads by scanning bytes, do not care about 495 // false location at this moment. 496 // Looks hacky but it is fast; does not need to be really exact. 497 if buf.len() > 2 { 498 for i in 0..buf.len() - 2 { 499 let op = buf[i]; 500 if op == gimli::constants::DW_OP_bra.0 || op == gimli::constants::DW_OP_skip.0 { 501 // TODO fix for big-endian 502 let offset = i16::from_le_bytes([buf[i + 1], buf[i + 2]]); 503 let origin = i + 3; 504 // Discarding out-of-bounds jumps (also some of falsely detected ops) 505 if (offset >= 0 && offset as usize + origin <= buf.len()) 506 || (offset < 0 && -offset as usize <= origin) 507 { 508 let target = buf.len() as isize - origin as isize - offset as isize; 509 jump_targets.insert(target as u64, JumpTargetMarker::new()); 510 } 511 } 512 } 513 } 514 515 while !pc.is_empty() { 516 let unread_bytes = pc.len().into_u64(); 517 if let Some(marker) = jump_targets.get(&unread_bytes) { 518 flush_code_chunk!(); 519 parts.push(CompiledExpressionPart::LandingPad(marker.clone())); 520 } 521 522 need_deref = true; 523 524 let pos = pc.offset_from(&expr.0).into_u64() as usize; 525 let op = Operation::parse(&mut pc, encoding)?; 526 match op { 527 Operation::FrameOffset { offset } => { 528 // Expand DW_OP_fbreg into frame location and DW_OP_plus_uconst. 529 if frame_base.is_some() { 530 // Add frame base expressions. 531 flush_code_chunk!(); 532 parts.extend_from_slice(&frame_base.unwrap().parts); 533 } 534 if let Some(CompiledExpressionPart::Local { trailing, .. }) = parts.last_mut() { 535 // Reset local trailing flag. 536 *trailing = false; 537 } 538 // Append DW_OP_plus_uconst part. 539 let mut writer = ExpressionWriter::new(); 540 writer.write_op(gimli::constants::DW_OP_plus_uconst)?; 541 writer.write_uleb128(offset as u64)?; 542 code_chunk.extend(writer.into_vec()); 543 continue; 544 } 545 Operation::Drop { .. } 546 | Operation::Pick { .. } 547 | Operation::Swap { .. } 548 | Operation::Rot { .. } 549 | Operation::Nop { .. } 550 | Operation::UnsignedConstant { .. } 551 | Operation::SignedConstant { .. } 552 | Operation::ConstantIndex { .. } 553 | Operation::PlusConstant { .. } 554 | Operation::Abs { .. } 555 | Operation::And { .. } 556 | Operation::Or { .. } 557 | Operation::Xor { .. } 558 | Operation::Shl { .. } 559 | Operation::Plus { .. } 560 | Operation::Minus { .. } 561 | Operation::Div { .. } 562 | Operation::Mod { .. } 563 | Operation::Mul { .. } 564 | Operation::Neg { .. } 565 | Operation::Not { .. } 566 | Operation::Lt { .. } 567 | Operation::Gt { .. } 568 | Operation::Le { .. } 569 | Operation::Ge { .. } 570 | Operation::Eq { .. } 571 | Operation::Ne { .. } 572 | Operation::TypedLiteral { .. } 573 | Operation::Convert { .. } 574 | Operation::Reinterpret { .. } 575 | Operation::Piece { .. } => (), 576 Operation::Bra { target } | Operation::Skip { target } => { 577 flush_code_chunk!(); 578 let arc_to = (pc.len().into_u64() as isize - target as isize) as u64; 579 let marker = match jump_targets.get(&arc_to) { 580 Some(m) => m.clone(), 581 None => { 582 // Marker not found: probably out of bounds. 583 return Ok(None); 584 } 585 }; 586 push!(CompiledExpressionPart::Jump { 587 conditionally: match op { 588 Operation::Bra { .. } => true, 589 _ => false, 590 }, 591 target: marker, 592 }); 593 continue; 594 } 595 Operation::StackValue => { 596 need_deref = false; 597 598 // Find extra stack_value, that follow wasm-local operators, 599 // and mark such locals with special flag. 600 if let (Some(CompiledExpressionPart::Local { trailing, .. }), true) = 601 (parts.last_mut(), code_chunk.is_empty()) 602 { 603 *trailing = true; 604 continue; 605 } 606 } 607 Operation::Deref { .. } => { 608 flush_code_chunk!(); 609 push!(CompiledExpressionPart::Deref); 610 // Don't re-enter the loop here (i.e. continue), because the 611 // DW_OP_deref still needs to be kept. 612 } 613 Operation::WasmLocal { index } => { 614 flush_code_chunk!(); 615 let label = ValueLabel::from_u32(index); 616 push!(CompiledExpressionPart::Local { 617 label, 618 trailing: false, 619 }); 620 continue; 621 } 622 Operation::Shr { .. } | Operation::Shra { .. } => { 623 // Insert value normalisation part. 624 // The semantic value is 32 bits (TODO: check unit) 625 // but the target architecture is 64-bits. So we'll 626 // clean out the upper 32 bits (in a sign-correct way) 627 // to avoid contamination of the result with randomness. 628 let mut writer = ExpressionWriter::new(); 629 writer.write_op(gimli::constants::DW_OP_plus_uconst)?; 630 writer.write_uleb128(32)?; // increase shift amount 631 writer.write_op(gimli::constants::DW_OP_swap)?; 632 writer.write_op(gimli::constants::DW_OP_const1u)?; 633 writer.write_u8(32)?; 634 writer.write_op(gimli::constants::DW_OP_shl)?; 635 writer.write_op(gimli::constants::DW_OP_swap)?; 636 code_chunk.extend(writer.into_vec()); 637 // Don't re-enter the loop here (i.e. continue), because the 638 // DW_OP_shr* still needs to be kept. 639 } 640 Operation::Address { .. } 641 | Operation::AddressIndex { .. } 642 | Operation::Call { .. } 643 | Operation::Register { .. } 644 | Operation::RegisterOffset { .. } 645 | Operation::CallFrameCFA 646 | Operation::PushObjectAddress 647 | Operation::TLS 648 | Operation::ImplicitValue { .. } 649 | Operation::ImplicitPointer { .. } 650 | Operation::EntryValue { .. } 651 | Operation::ParameterRef { .. } => { 652 return Ok(None); 653 } 654 Operation::WasmGlobal { index: _ } | Operation::WasmStack { index: _ } => { 655 // TODO support those two 656 return Ok(None); 657 } 658 } 659 let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize]; 660 code_chunk.extend_from_slice(chunk); 661 } 662 663 flush_code_chunk!(); 664 if let Some(marker) = jump_targets.get(&0) { 665 parts.push(CompiledExpressionPart::LandingPad(marker.clone())); 666 } 667 668 Ok(Some(CompiledExpression { parts, need_deref })) 669 } 670 671 #[derive(Debug, Clone)] 672 struct CachedValueLabelRange { 673 func_index: usize, 674 start: usize, 675 end: usize, 676 label_location: HashMap<ValueLabel, LabelValueLoc>, 677 } 678 679 struct BuiltRangeSummary<'a> { 680 range: &'a CachedValueLabelRange, 681 isa: &'a dyn TargetIsa, 682 } 683 684 impl<'a> fmt::Debug for BuiltRangeSummary<'a> { 685 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 686 let range = self.range; 687 write!(f, "[")?; 688 let mut is_first = true; 689 for (value, value_loc) in &range.label_location { 690 if !is_first { 691 write!(f, ", ")?; 692 } else { 693 is_first = false; 694 } 695 write!( 696 f, 697 "{:?}:{:?}", 698 log_get_value_name(*value), 699 log_get_value_loc(*value_loc, self.isa) 700 )?; 701 } 702 write!(f, "]@[{}..{})", range.start, range.end)?; 703 Ok(()) 704 } 705 } 706 707 struct ValueLabelRangesBuilder<'a, 'b> { 708 isa: &'a dyn TargetIsa, 709 ranges: Vec<CachedValueLabelRange>, 710 frame_info: Option<&'a FunctionFrameInfo<'b>>, 711 processed_labels: HashSet<ValueLabel>, 712 covers_entire_scope: bool, 713 } 714 715 struct BuiltValueLabelRanges<TIter> { 716 ranges: TIter, 717 covers_entire_scope: bool, 718 } 719 720 impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> { 721 pub fn new( 722 scope: &[(u64, u64)], // wasm ranges 723 addr_tr: &'a AddressTransform, 724 frame_info: Option<&'a FunctionFrameInfo<'b>>, 725 isa: &'a dyn TargetIsa, 726 ) -> Self { 727 let mut ranges = Vec::new(); 728 for (wasm_start, wasm_end) in scope { 729 if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(*wasm_start, *wasm_end) { 730 ranges.extend(tr.into_iter().map(|(start, end)| CachedValueLabelRange { 731 func_index, 732 start, 733 end, 734 label_location: HashMap::new(), 735 })); 736 } 737 } 738 ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start)); 739 740 dbi_log!( 741 "Building ranges for values in scope: {}\n{:?}", 742 ranges 743 .iter() 744 .map(|r| format!("[{}..{})", r.start, r.end)) 745 .join(" "), 746 log_get_value_ranges(frame_info.map(|f| f.value_ranges), isa) 747 ); 748 ValueLabelRangesBuilder { 749 isa, 750 ranges, 751 frame_info, 752 processed_labels: HashSet::new(), 753 covers_entire_scope: true, 754 } 755 } 756 757 fn process_label(&mut self, label: ValueLabel) { 758 if self.processed_labels.contains(&label) { 759 return; 760 } 761 dbi_log!("Intersecting with {:?}", log_get_value_name(label)); 762 self.processed_labels.insert(label); 763 764 let value_ranges = match self.frame_info.and_then(|fi| fi.value_ranges.get(&label)) { 765 Some(value_ranges) => value_ranges, 766 None => { 767 return; 768 } 769 }; 770 771 let ranges = &mut self.ranges; 772 for value_range in value_ranges { 773 let range_start = value_range.start as usize; 774 let range_end = value_range.end as usize; 775 let loc = value_range.loc; 776 if range_start == range_end { 777 continue; 778 } 779 assert!(range_start < range_end); 780 781 // Find acceptable scope of ranges to intersect with. 782 let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) { 783 Ok(i) => i, 784 Err(i) => { 785 if i > 0 && range_start < ranges[i - 1].end { 786 i - 1 787 } else { 788 i 789 } 790 } 791 }; 792 let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) { 793 Ok(i) | Err(i) => i, 794 }; 795 // Starting from the end, intersect (range_start..range_end) with 796 // self.ranges array. 797 for i in (i..j).rev() { 798 if range_end <= ranges[i].start || ranges[i].end <= range_start { 799 continue; 800 } 801 if range_end < ranges[i].end { 802 // Cutting some of the range from the end. 803 let mut tail = ranges[i].clone(); 804 ranges[i].end = range_end; 805 tail.start = range_end; 806 ranges.insert(i + 1, tail); 807 self.covers_entire_scope = false; 808 } 809 assert!(ranges[i].end <= range_end); 810 if range_start <= ranges[i].start { 811 ranges[i].label_location.insert(label, loc); 812 continue; 813 } 814 // Cutting some of the range from the start. 815 let mut tail = ranges[i].clone(); 816 ranges[i].end = range_start; 817 tail.start = range_start; 818 tail.label_location.insert(label, loc); 819 ranges.insert(i + 1, tail); 820 self.covers_entire_scope = false; 821 } 822 } 823 } 824 825 pub fn into_ranges( 826 self, 827 ) -> BuiltValueLabelRanges<impl Iterator<Item = CachedValueLabelRange> + use<>> { 828 // Ranges with not-enough labels are discarded. 829 let processed_labels_len = self.processed_labels.len(); 830 let is_valid_range = 831 move |r: &CachedValueLabelRange| r.label_location.len() == processed_labels_len; 832 833 if dbi_log_enabled!() { 834 dbi_log!("Built ranges:"); 835 for range in self.ranges.iter().filter(|r| is_valid_range(*r)) { 836 dbi_log!( 837 "{:?}", 838 BuiltRangeSummary { 839 range, 840 isa: self.isa 841 } 842 ); 843 } 844 dbi_log!(""); 845 } 846 847 BuiltValueLabelRanges { 848 ranges: self.ranges.into_iter().filter(is_valid_range), 849 covers_entire_scope: self.covers_entire_scope, 850 } 851 } 852 } 853 854 /// Marker for tracking incoming jumps. 855 /// Different when created new, and the same when cloned. 856 #[derive(Clone, Eq)] 857 struct JumpTargetMarker(Rc<u32>); 858 859 impl JumpTargetMarker { 860 fn new() -> JumpTargetMarker { 861 // Create somewhat unique hash data -- using part of 862 // the pointer of the RcBox. 863 let mut rc = Rc::new(0); 864 let hash_data = rc.as_ref() as *const u32 as usize as u32; 865 *Rc::get_mut(&mut rc).unwrap() = hash_data; 866 JumpTargetMarker(rc) 867 } 868 } 869 870 impl PartialEq for JumpTargetMarker { 871 fn eq(&self, other: &JumpTargetMarker) -> bool { 872 Rc::ptr_eq(&self.0, &other.0) 873 } 874 } 875 876 impl Hash for JumpTargetMarker { 877 fn hash<H: Hasher>(&self, hasher: &mut H) { 878 hasher.write_u32(*self.0); 879 } 880 } 881 impl std::fmt::Debug for JumpTargetMarker { 882 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { 883 write!( 884 f, 885 "JumpMarker<{:08x}>", 886 self.0.as_ref() as *const u32 as usize 887 ) 888 } 889 } 890 891 #[cfg(test)] 892 #[expect(trivial_numeric_casts, reason = "macro-generated code")] 893 mod tests { 894 use super::{ 895 compile_expression, AddressTransform, CompiledExpression, CompiledExpressionPart, 896 FunctionFrameInfo, JumpTargetMarker, ValueLabel, ValueLabelsRanges, 897 }; 898 use crate::CompiledFunctionMetadata; 899 use cranelift_codegen::{isa::lookup, settings::Flags}; 900 use gimli::{constants, Encoding, EndianSlice, Expression, RunTimeEndian}; 901 use target_lexicon::triple; 902 use wasmtime_environ::FilePos; 903 904 macro_rules! dw_op { 905 (DW_OP_WASM_location) => { 906 0xed 907 }; 908 ($i:literal) => { 909 $i 910 }; 911 ($d:ident) => { 912 constants::$d.0 as u8 913 }; 914 ($e:expr) => { 915 $e as u8 916 }; 917 } 918 919 macro_rules! expression { 920 ($($t:tt),*) => { 921 Expression(EndianSlice::new( 922 &[$(dw_op!($t)),*], 923 RunTimeEndian::Little, 924 )) 925 } 926 } 927 928 fn find_jump_targets<'a>(ce: &'a CompiledExpression) -> Vec<&'a JumpTargetMarker> { 929 ce.parts 930 .iter() 931 .filter_map(|p| { 932 if let CompiledExpressionPart::LandingPad(t) = p { 933 Some(t) 934 } else { 935 None 936 } 937 }) 938 .collect::<Vec<_>>() 939 } 940 941 static DWARF_ENCODING: Encoding = Encoding { 942 address_size: 4, 943 format: gimli::Format::Dwarf32, 944 version: 4, 945 }; 946 947 #[test] 948 fn test_debug_expression_jump_target() { 949 let m1 = JumpTargetMarker::new(); 950 let m2 = JumpTargetMarker::new(); 951 assert!(m1 != m2); 952 assert!(m1 == m1.clone()); 953 954 // Internal hash_data test (theoretically can fail intermittently). 955 assert!(m1.0 != m2.0); 956 } 957 958 #[test] 959 fn test_debug_parse_expressions() { 960 use cranelift_entity::EntityRef; 961 962 let (val1, val3, val20) = (ValueLabel::new(1), ValueLabel::new(3), ValueLabel::new(20)); 963 964 let e = expression!(DW_OP_WASM_location, 0x0, 20, DW_OP_stack_value); 965 let ce = compile_expression(&e, DWARF_ENCODING, None) 966 .expect("non-error") 967 .expect("expression"); 968 assert_eq!( 969 ce, 970 CompiledExpression { 971 parts: vec![CompiledExpressionPart::Local { 972 label: val20, 973 trailing: true 974 }], 975 need_deref: false, 976 } 977 ); 978 979 let e = expression!( 980 DW_OP_WASM_location, 981 0x0, 982 1, 983 DW_OP_plus_uconst, 984 0x10, 985 DW_OP_stack_value 986 ); 987 let ce = compile_expression(&e, DWARF_ENCODING, None) 988 .expect("non-error") 989 .expect("expression"); 990 assert_eq!( 991 ce, 992 CompiledExpression { 993 parts: vec![ 994 CompiledExpressionPart::Local { 995 label: val1, 996 trailing: false 997 }, 998 CompiledExpressionPart::Code(vec![35, 16, 159]) 999 ], 1000 need_deref: false, 1001 } 1002 ); 1003 1004 let e = expression!(DW_OP_WASM_location, 0x0, 3, DW_OP_stack_value); 1005 let fe = compile_expression(&e, DWARF_ENCODING, None).expect("non-error"); 1006 let e = expression!(DW_OP_fbreg, 0x12); 1007 let ce = compile_expression(&e, DWARF_ENCODING, fe.as_ref()) 1008 .expect("non-error") 1009 .expect("expression"); 1010 assert_eq!( 1011 ce, 1012 CompiledExpression { 1013 parts: vec![ 1014 CompiledExpressionPart::Local { 1015 label: val3, 1016 trailing: false 1017 }, 1018 CompiledExpressionPart::Code(vec![35, 18]) 1019 ], 1020 need_deref: true, 1021 } 1022 ); 1023 1024 let e = expression!( 1025 DW_OP_WASM_location, 1026 0x0, 1027 1, 1028 DW_OP_plus_uconst, 1029 5, 1030 DW_OP_deref, 1031 DW_OP_stack_value 1032 ); 1033 let ce = compile_expression(&e, DWARF_ENCODING, None) 1034 .expect("non-error") 1035 .expect("expression"); 1036 assert_eq!( 1037 ce, 1038 CompiledExpression { 1039 parts: vec![ 1040 CompiledExpressionPart::Local { 1041 label: val1, 1042 trailing: false 1043 }, 1044 CompiledExpressionPart::Code(vec![35, 5]), 1045 CompiledExpressionPart::Deref, 1046 CompiledExpressionPart::Code(vec![6, 159]) 1047 ], 1048 need_deref: false, 1049 } 1050 ); 1051 1052 let e = expression!( 1053 DW_OP_WASM_location, 1054 0x0, 1055 1, 1056 DW_OP_lit16, 1057 DW_OP_shra, 1058 DW_OP_stack_value 1059 ); 1060 let ce = compile_expression(&e, DWARF_ENCODING, None) 1061 .expect("non-error") 1062 .expect("expression"); 1063 assert_eq!( 1064 ce, 1065 CompiledExpression { 1066 parts: vec![ 1067 CompiledExpressionPart::Local { 1068 label: val1, 1069 trailing: false 1070 }, 1071 CompiledExpressionPart::Code(vec![64, 35, 32, 22, 8, 32, 36, 22, 38, 159]) 1072 ], 1073 need_deref: false, 1074 } 1075 ); 1076 1077 let e = expression!( 1078 DW_OP_lit1, 1079 DW_OP_dup, 1080 DW_OP_WASM_location, 1081 0x0, 1082 1, 1083 DW_OP_and, 1084 DW_OP_bra, 1085 5, 1086 0, // --> pointer 1087 DW_OP_swap, 1088 DW_OP_shr, 1089 DW_OP_skip, 1090 2, 1091 0, // --> done 1092 // pointer: 1093 DW_OP_plus, 1094 DW_OP_deref, 1095 // done: 1096 DW_OP_stack_value 1097 ); 1098 let ce = compile_expression(&e, DWARF_ENCODING, None) 1099 .expect("non-error") 1100 .expect("expression"); 1101 let targets = find_jump_targets(&ce); 1102 assert_eq!(targets.len(), 2); 1103 assert_eq!( 1104 ce, 1105 CompiledExpression { 1106 parts: vec![ 1107 CompiledExpressionPart::Code(vec![49, 18]), 1108 CompiledExpressionPart::Local { 1109 label: val1, 1110 trailing: false 1111 }, 1112 CompiledExpressionPart::Code(vec![26]), 1113 CompiledExpressionPart::Jump { 1114 conditionally: true, 1115 target: targets[0].clone(), 1116 }, 1117 CompiledExpressionPart::Code(vec![22, 35, 32, 22, 8, 32, 36, 22, 37]), 1118 CompiledExpressionPart::Jump { 1119 conditionally: false, 1120 target: targets[1].clone(), 1121 }, 1122 CompiledExpressionPart::LandingPad(targets[0].clone()), // capture from 1123 CompiledExpressionPart::Code(vec![34]), 1124 CompiledExpressionPart::Deref, 1125 CompiledExpressionPart::Code(vec![6]), 1126 CompiledExpressionPart::LandingPad(targets[1].clone()), // capture to 1127 CompiledExpressionPart::Code(vec![159]) 1128 ], 1129 need_deref: false, 1130 } 1131 ); 1132 1133 let e = expression!( 1134 DW_OP_lit1, 1135 DW_OP_dup, 1136 DW_OP_bra, 1137 2, 1138 0, // --> target 1139 DW_OP_deref, 1140 DW_OP_lit0, 1141 // target: 1142 DW_OP_stack_value 1143 ); 1144 let ce = compile_expression(&e, DWARF_ENCODING, None) 1145 .expect("non-error") 1146 .expect("expression"); 1147 let targets = find_jump_targets(&ce); 1148 assert_eq!(targets.len(), 1); 1149 assert_eq!( 1150 ce, 1151 CompiledExpression { 1152 parts: vec![ 1153 CompiledExpressionPart::Code(vec![49, 18]), 1154 CompiledExpressionPart::Jump { 1155 conditionally: true, 1156 target: targets[0].clone(), 1157 }, 1158 CompiledExpressionPart::Deref, 1159 CompiledExpressionPart::Code(vec![6, 48]), 1160 CompiledExpressionPart::LandingPad(targets[0].clone()), // capture to 1161 CompiledExpressionPart::Code(vec![159]) 1162 ], 1163 need_deref: false, 1164 } 1165 ); 1166 1167 let e = expression!( 1168 DW_OP_lit1, 1169 /* loop */ DW_OP_dup, 1170 DW_OP_lit25, 1171 DW_OP_ge, 1172 DW_OP_bra, 1173 5, 1174 0, // --> done 1175 DW_OP_plus_uconst, 1176 1, 1177 DW_OP_skip, 1178 (-11_i8), 1179 (!0), // --> loop 1180 /* done */ DW_OP_stack_value 1181 ); 1182 let ce = compile_expression(&e, DWARF_ENCODING, None) 1183 .expect("non-error") 1184 .expect("expression"); 1185 let targets = find_jump_targets(&ce); 1186 assert_eq!(targets.len(), 2); 1187 assert_eq!( 1188 ce, 1189 CompiledExpression { 1190 parts: vec![ 1191 CompiledExpressionPart::Code(vec![49]), 1192 CompiledExpressionPart::LandingPad(targets[0].clone()), 1193 CompiledExpressionPart::Code(vec![18, 73, 42]), 1194 CompiledExpressionPart::Jump { 1195 conditionally: true, 1196 target: targets[1].clone(), 1197 }, 1198 CompiledExpressionPart::Code(vec![35, 1]), 1199 CompiledExpressionPart::Jump { 1200 conditionally: false, 1201 target: targets[0].clone(), 1202 }, 1203 CompiledExpressionPart::LandingPad(targets[1].clone()), 1204 CompiledExpressionPart::Code(vec![159]) 1205 ], 1206 need_deref: false, 1207 } 1208 ); 1209 1210 let e = expression!(DW_OP_WASM_location, 0x0, 1, DW_OP_plus_uconst, 5); 1211 let ce = compile_expression(&e, DWARF_ENCODING, None) 1212 .expect("non-error") 1213 .expect("expression"); 1214 assert_eq!( 1215 ce, 1216 CompiledExpression { 1217 parts: vec![ 1218 CompiledExpressionPart::Local { 1219 label: val1, 1220 trailing: false 1221 }, 1222 CompiledExpressionPart::Code(vec![35, 5]) 1223 ], 1224 need_deref: true, 1225 } 1226 ); 1227 } 1228 1229 fn create_mock_address_transform() -> AddressTransform { 1230 use crate::FunctionAddressMap; 1231 use cranelift_entity::PrimaryMap; 1232 use wasmtime_environ::InstructionAddressMap; 1233 use wasmtime_environ::WasmFileInfo; 1234 1235 let mut module_map = PrimaryMap::new(); 1236 let code_section_offset: u32 = 100; 1237 let func = CompiledFunctionMetadata { 1238 address_map: FunctionAddressMap { 1239 instructions: vec![ 1240 InstructionAddressMap { 1241 srcloc: FilePos::new(code_section_offset + 12), 1242 code_offset: 5, 1243 }, 1244 InstructionAddressMap { 1245 srcloc: FilePos::default(), 1246 code_offset: 8, 1247 }, 1248 InstructionAddressMap { 1249 srcloc: FilePos::new(code_section_offset + 17), 1250 code_offset: 15, 1251 }, 1252 InstructionAddressMap { 1253 srcloc: FilePos::default(), 1254 code_offset: 23, 1255 }, 1256 ] 1257 .into(), 1258 start_srcloc: FilePos::new(code_section_offset + 10), 1259 end_srcloc: FilePos::new(code_section_offset + 20), 1260 body_offset: 0, 1261 body_len: 30, 1262 }, 1263 ..Default::default() 1264 }; 1265 module_map.push(&func); 1266 let fi = WasmFileInfo { 1267 code_section_offset: code_section_offset.into(), 1268 funcs: Vec::new(), 1269 imported_func_count: 0, 1270 path: None, 1271 }; 1272 AddressTransform::mock(&module_map, fi) 1273 } 1274 1275 fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) { 1276 use cranelift_codegen::{LabelValueLoc, ValueLocRange}; 1277 use cranelift_entity::EntityRef; 1278 use std::collections::HashMap; 1279 let mut value_ranges = HashMap::new(); 1280 let value_0 = ValueLabel::new(0); 1281 let value_1 = ValueLabel::new(1); 1282 let value_2 = ValueLabel::new(2); 1283 value_ranges.insert( 1284 value_0, 1285 vec![ValueLocRange { 1286 loc: LabelValueLoc::CFAOffset(0), 1287 start: 0, 1288 end: 25, 1289 }], 1290 ); 1291 value_ranges.insert( 1292 value_1, 1293 vec![ValueLocRange { 1294 loc: LabelValueLoc::CFAOffset(0), 1295 start: 5, 1296 end: 30, 1297 }], 1298 ); 1299 value_ranges.insert( 1300 value_2, 1301 vec![ 1302 ValueLocRange { 1303 loc: LabelValueLoc::CFAOffset(0), 1304 start: 0, 1305 end: 10, 1306 }, 1307 ValueLocRange { 1308 loc: LabelValueLoc::CFAOffset(0), 1309 start: 20, 1310 end: 30, 1311 }, 1312 ], 1313 ); 1314 (value_ranges, (value_0, value_1, value_2)) 1315 } 1316 1317 #[test] 1318 fn test_debug_value_range_builder() { 1319 use super::ValueLabelRangesBuilder; 1320 use crate::debug::ModuleMemoryOffset; 1321 1322 // Ignore this test if cranelift doesn't support the native platform. 1323 if cranelift_native::builder().is_err() { 1324 return; 1325 } 1326 1327 let isa = lookup(triple!("x86_64")) 1328 .expect("expect x86_64 ISA") 1329 .finish(Flags::new(cranelift_codegen::settings::builder())) 1330 .expect("Creating ISA"); 1331 1332 let addr_tr = create_mock_address_transform(); 1333 let (value_ranges, value_labels) = create_mock_value_ranges(); 1334 let fi = FunctionFrameInfo { 1335 memory_offset: ModuleMemoryOffset::None, 1336 value_ranges: &value_ranges, 1337 }; 1338 1339 // No value labels, testing if entire function range coming through. 1340 let builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi), isa.as_ref()); 1341 let ranges = builder.into_ranges().ranges.collect::<Vec<_>>(); 1342 assert_eq!(ranges.len(), 1); 1343 assert_eq!(ranges[0].func_index, 0); 1344 assert_eq!(ranges[0].start, 0); 1345 assert_eq!(ranges[0].end, 30); 1346 1347 // Two labels ([email protected] and [email protected]), their common lifetime intersect at 5..25. 1348 let mut builder = 1349 ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi), isa.as_ref()); 1350 builder.process_label(value_labels.0); 1351 builder.process_label(value_labels.1); 1352 let ranges = builder.into_ranges().ranges.collect::<Vec<_>>(); 1353 assert_eq!(ranges.len(), 1); 1354 assert_eq!(ranges[0].start, 5); 1355 assert_eq!(ranges[0].end, 25); 1356 1357 // Adds val2 with complex lifetime @0..10 and @20..30 to the previous test, and 1358 // also narrows range. 1359 let mut builder = 1360 ValueLabelRangesBuilder::new(&[(11, 17)], &addr_tr, Some(&fi), isa.as_ref()); 1361 builder.process_label(value_labels.0); 1362 builder.process_label(value_labels.1); 1363 builder.process_label(value_labels.2); 1364 let ranges = builder.into_ranges().ranges.collect::<Vec<_>>(); 1365 // Result is two ranges @5..10 and @20..23 1366 assert_eq!(ranges.len(), 2); 1367 assert_eq!(ranges[0].start, 5); 1368 assert_eq!(ranges[0].end, 10); 1369 assert_eq!(ranges[1].start, 20); 1370 assert_eq!(ranges[1].end, 23); 1371 } 1372 } 1373