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