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 return Ok(None); 652 } 653 Operation::WasmGlobal { index: _ } | Operation::WasmStack { index: _ } => { 654 // TODO support those two 655 return Ok(None); 656 } 657 } 658 let chunk = &buf[pos..pc.offset_from(&expr.0).into_u64() as usize]; 659 code_chunk.extend_from_slice(chunk); 660 } 661 662 flush_code_chunk!(); 663 if let Some(marker) = jump_targets.get(&0) { 664 parts.push(CompiledExpressionPart::LandingPad(marker.clone())); 665 } 666 667 Ok(Some(CompiledExpression { parts, need_deref })) 668 } 669 670 #[derive(Debug, Clone)] 671 struct CachedValueLabelRange { 672 func_index: usize, 673 start: usize, 674 end: usize, 675 label_location: HashMap<ValueLabel, LabelValueLoc>, 676 } 677 678 struct BuiltRangeSummary<'a> { 679 range: &'a CachedValueLabelRange, 680 isa: &'a dyn TargetIsa, 681 } 682 683 impl<'a> fmt::Debug for BuiltRangeSummary<'a> { 684 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 685 let range = self.range; 686 write!(f, "[")?; 687 let mut is_first = true; 688 for (value, value_loc) in &range.label_location { 689 if !is_first { 690 write!(f, ", ")?; 691 } else { 692 is_first = false; 693 } 694 write!( 695 f, 696 "{:?}:{:?}", 697 log_get_value_name(*value), 698 log_get_value_loc(*value_loc, self.isa) 699 )?; 700 } 701 write!(f, "]@[{}..{})", range.start, range.end)?; 702 Ok(()) 703 } 704 } 705 706 struct ValueLabelRangesBuilder<'a, 'b> { 707 isa: &'a dyn TargetIsa, 708 ranges: Vec<CachedValueLabelRange>, 709 frame_info: Option<&'a FunctionFrameInfo<'b>>, 710 processed_labels: HashSet<ValueLabel>, 711 covers_entire_scope: bool, 712 } 713 714 struct BuiltValueLabelRanges<TIter> { 715 ranges: TIter, 716 covers_entire_scope: bool, 717 } 718 719 impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> { 720 pub fn new( 721 scope: &[(u64, u64)], // wasm ranges 722 addr_tr: &'a AddressTransform, 723 frame_info: Option<&'a FunctionFrameInfo<'b>>, 724 isa: &'a dyn TargetIsa, 725 ) -> Self { 726 let mut ranges = Vec::new(); 727 for (wasm_start, wasm_end) in scope { 728 if let Some((func_index, tr)) = addr_tr.translate_ranges_raw(*wasm_start, *wasm_end) { 729 ranges.extend(tr.into_iter().map(|(start, end)| CachedValueLabelRange { 730 func_index, 731 start, 732 end, 733 label_location: HashMap::new(), 734 })); 735 } 736 } 737 ranges.sort_unstable_by(|a, b| a.start.cmp(&b.start)); 738 739 dbi_log!( 740 "Building ranges for values in scope: {}\n{:?}", 741 ranges 742 .iter() 743 .map(|r| format!("[{}..{})", r.start, r.end)) 744 .join(" "), 745 log_get_value_ranges(frame_info.map(|f| f.value_ranges), isa) 746 ); 747 ValueLabelRangesBuilder { 748 isa, 749 ranges, 750 frame_info, 751 processed_labels: HashSet::new(), 752 covers_entire_scope: true, 753 } 754 } 755 756 fn process_label(&mut self, label: ValueLabel) { 757 if self.processed_labels.contains(&label) { 758 return; 759 } 760 dbi_log!("Intersecting with {:?}", log_get_value_name(label)); 761 self.processed_labels.insert(label); 762 763 let value_ranges = match self.frame_info.and_then(|fi| fi.value_ranges.get(&label)) { 764 Some(value_ranges) => value_ranges, 765 None => { 766 return; 767 } 768 }; 769 770 let ranges = &mut self.ranges; 771 for value_range in value_ranges { 772 let range_start = value_range.start as usize; 773 let range_end = value_range.end as usize; 774 let loc = value_range.loc; 775 if range_start == range_end { 776 continue; 777 } 778 assert!(range_start < range_end); 779 780 // Find acceptable scope of ranges to intersect with. 781 let i = match ranges.binary_search_by(|s| s.start.cmp(&range_start)) { 782 Ok(i) => i, 783 Err(i) => { 784 if i > 0 && range_start < ranges[i - 1].end { 785 i - 1 786 } else { 787 i 788 } 789 } 790 }; 791 let j = match ranges.binary_search_by(|s| s.start.cmp(&range_end)) { 792 Ok(i) | Err(i) => i, 793 }; 794 // Starting from the end, intersect (range_start..range_end) with 795 // self.ranges array. 796 for i in (i..j).rev() { 797 if range_end <= ranges[i].start || ranges[i].end <= range_start { 798 continue; 799 } 800 if range_end < ranges[i].end { 801 // Cutting some of the range from the end. 802 let mut tail = ranges[i].clone(); 803 ranges[i].end = range_end; 804 tail.start = range_end; 805 ranges.insert(i + 1, tail); 806 self.covers_entire_scope = false; 807 } 808 assert!(ranges[i].end <= range_end); 809 if range_start <= ranges[i].start { 810 ranges[i].label_location.insert(label, loc); 811 continue; 812 } 813 // Cutting some of the range from the start. 814 let mut tail = ranges[i].clone(); 815 ranges[i].end = range_start; 816 tail.start = range_start; 817 tail.label_location.insert(label, loc); 818 ranges.insert(i + 1, tail); 819 self.covers_entire_scope = false; 820 } 821 } 822 } 823 824 pub fn into_ranges( 825 self, 826 ) -> BuiltValueLabelRanges<impl Iterator<Item = CachedValueLabelRange> + use<>> { 827 // Ranges with not-enough labels are discarded. 828 let processed_labels_len = self.processed_labels.len(); 829 let is_valid_range = 830 move |r: &CachedValueLabelRange| r.label_location.len() == processed_labels_len; 831 832 if dbi_log_enabled!() { 833 dbi_log!("Built ranges:"); 834 for range in self.ranges.iter().filter(|r| is_valid_range(*r)) { 835 dbi_log!( 836 "{:?}", 837 BuiltRangeSummary { 838 range, 839 isa: self.isa 840 } 841 ); 842 } 843 dbi_log!(""); 844 } 845 846 BuiltValueLabelRanges { 847 ranges: self.ranges.into_iter().filter(is_valid_range), 848 covers_entire_scope: self.covers_entire_scope, 849 } 850 } 851 } 852 853 /// Marker for tracking incoming jumps. 854 /// Different when created new, and the same when cloned. 855 #[derive(Clone, Eq)] 856 struct JumpTargetMarker(Rc<u32>); 857 858 impl JumpTargetMarker { 859 fn new() -> JumpTargetMarker { 860 // Create somewhat unique hash data -- using part of 861 // the pointer of the RcBox. 862 let mut rc = Rc::new(0); 863 let hash_data = rc.as_ref() as *const u32 as usize as u32; 864 *Rc::get_mut(&mut rc).unwrap() = hash_data; 865 JumpTargetMarker(rc) 866 } 867 } 868 869 impl PartialEq for JumpTargetMarker { 870 fn eq(&self, other: &JumpTargetMarker) -> bool { 871 Rc::ptr_eq(&self.0, &other.0) 872 } 873 } 874 875 impl Hash for JumpTargetMarker { 876 fn hash<H: Hasher>(&self, hasher: &mut H) { 877 hasher.write_u32(*self.0); 878 } 879 } 880 impl std::fmt::Debug for JumpTargetMarker { 881 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> { 882 write!( 883 f, 884 "JumpMarker<{:08x}>", 885 self.0.as_ref() as *const u32 as usize 886 ) 887 } 888 } 889 890 #[cfg(test)] 891 #[expect(trivial_numeric_casts, reason = "macro-generated code")] 892 mod tests { 893 use super::{ 894 AddressTransform, CompiledExpression, CompiledExpressionPart, FunctionFrameInfo, 895 JumpTargetMarker, ValueLabel, ValueLabelsRanges, compile_expression, 896 }; 897 use crate::CompiledFunctionMetadata; 898 use cranelift_codegen::{isa::lookup, settings::Flags}; 899 use gimli::{Encoding, EndianSlice, Expression, RunTimeEndian, constants}; 900 use target_lexicon::triple; 901 use wasmtime_environ::FilePos; 902 903 macro_rules! dw_op { 904 (DW_OP_WASM_location) => { 905 0xed 906 }; 907 ($i:literal) => { 908 $i 909 }; 910 ($d:ident) => { 911 constants::$d.0 as u8 912 }; 913 ($e:expr) => { 914 $e as u8 915 }; 916 } 917 918 macro_rules! expression { 919 ($($t:tt),*) => { 920 Expression(EndianSlice::new( 921 &[$(dw_op!($t)),*], 922 RunTimeEndian::Little, 923 )) 924 } 925 } 926 927 fn find_jump_targets<'a>(ce: &'a CompiledExpression) -> Vec<&'a JumpTargetMarker> { 928 ce.parts 929 .iter() 930 .filter_map(|p| { 931 if let CompiledExpressionPart::LandingPad(t) = p { 932 Some(t) 933 } else { 934 None 935 } 936 }) 937 .collect::<Vec<_>>() 938 } 939 940 static DWARF_ENCODING: Encoding = Encoding { 941 address_size: 4, 942 format: gimli::Format::Dwarf32, 943 version: 4, 944 }; 945 946 #[test] 947 fn test_debug_expression_jump_target() { 948 let m1 = JumpTargetMarker::new(); 949 let m2 = JumpTargetMarker::new(); 950 assert!(m1 != m2); 951 assert!(m1 == m1.clone()); 952 953 // Internal hash_data test (theoretically can fail intermittently). 954 assert!(m1.0 != m2.0); 955 } 956 957 #[test] 958 fn test_debug_parse_expressions() { 959 use cranelift_entity::EntityRef; 960 961 let (val1, val3, val20) = (ValueLabel::new(1), ValueLabel::new(3), ValueLabel::new(20)); 962 963 let e = expression!(DW_OP_WASM_location, 0x0, 20, DW_OP_stack_value); 964 let ce = compile_expression(&e, DWARF_ENCODING, None) 965 .expect("non-error") 966 .expect("expression"); 967 assert_eq!( 968 ce, 969 CompiledExpression { 970 parts: vec![CompiledExpressionPart::Local { 971 label: val20, 972 trailing: true 973 }], 974 need_deref: false, 975 } 976 ); 977 978 let e = expression!( 979 DW_OP_WASM_location, 980 0x0, 981 1, 982 DW_OP_plus_uconst, 983 0x10, 984 DW_OP_stack_value 985 ); 986 let ce = compile_expression(&e, DWARF_ENCODING, None) 987 .expect("non-error") 988 .expect("expression"); 989 assert_eq!( 990 ce, 991 CompiledExpression { 992 parts: vec![ 993 CompiledExpressionPart::Local { 994 label: val1, 995 trailing: false 996 }, 997 CompiledExpressionPart::Code(vec![35, 16, 159]) 998 ], 999 need_deref: false, 1000 } 1001 ); 1002 1003 let e = expression!(DW_OP_WASM_location, 0x0, 3, DW_OP_stack_value); 1004 let fe = compile_expression(&e, DWARF_ENCODING, None).expect("non-error"); 1005 let e = expression!(DW_OP_fbreg, 0x12); 1006 let ce = compile_expression(&e, DWARF_ENCODING, fe.as_ref()) 1007 .expect("non-error") 1008 .expect("expression"); 1009 assert_eq!( 1010 ce, 1011 CompiledExpression { 1012 parts: vec![ 1013 CompiledExpressionPart::Local { 1014 label: val3, 1015 trailing: false 1016 }, 1017 CompiledExpressionPart::Code(vec![35, 18]) 1018 ], 1019 need_deref: true, 1020 } 1021 ); 1022 1023 let e = expression!( 1024 DW_OP_WASM_location, 1025 0x0, 1026 1, 1027 DW_OP_plus_uconst, 1028 5, 1029 DW_OP_deref, 1030 DW_OP_stack_value 1031 ); 1032 let ce = compile_expression(&e, DWARF_ENCODING, None) 1033 .expect("non-error") 1034 .expect("expression"); 1035 assert_eq!( 1036 ce, 1037 CompiledExpression { 1038 parts: vec![ 1039 CompiledExpressionPart::Local { 1040 label: val1, 1041 trailing: false 1042 }, 1043 CompiledExpressionPart::Code(vec![35, 5]), 1044 CompiledExpressionPart::Deref, 1045 CompiledExpressionPart::Code(vec![6, 159]) 1046 ], 1047 need_deref: false, 1048 } 1049 ); 1050 1051 let e = expression!( 1052 DW_OP_WASM_location, 1053 0x0, 1054 1, 1055 DW_OP_lit16, 1056 DW_OP_shra, 1057 DW_OP_stack_value 1058 ); 1059 let ce = compile_expression(&e, DWARF_ENCODING, None) 1060 .expect("non-error") 1061 .expect("expression"); 1062 assert_eq!( 1063 ce, 1064 CompiledExpression { 1065 parts: vec![ 1066 CompiledExpressionPart::Local { 1067 label: val1, 1068 trailing: false 1069 }, 1070 CompiledExpressionPart::Code(vec![64, 35, 32, 22, 8, 32, 36, 22, 38, 159]) 1071 ], 1072 need_deref: false, 1073 } 1074 ); 1075 1076 let e = expression!( 1077 DW_OP_lit1, 1078 DW_OP_dup, 1079 DW_OP_WASM_location, 1080 0x0, 1081 1, 1082 DW_OP_and, 1083 DW_OP_bra, 1084 5, 1085 0, // --> pointer 1086 DW_OP_swap, 1087 DW_OP_shr, 1088 DW_OP_skip, 1089 2, 1090 0, // --> done 1091 // pointer: 1092 DW_OP_plus, 1093 DW_OP_deref, 1094 // done: 1095 DW_OP_stack_value 1096 ); 1097 let ce = compile_expression(&e, DWARF_ENCODING, None) 1098 .expect("non-error") 1099 .expect("expression"); 1100 let targets = find_jump_targets(&ce); 1101 assert_eq!(targets.len(), 2); 1102 assert_eq!( 1103 ce, 1104 CompiledExpression { 1105 parts: vec![ 1106 CompiledExpressionPart::Code(vec![49, 18]), 1107 CompiledExpressionPart::Local { 1108 label: val1, 1109 trailing: false 1110 }, 1111 CompiledExpressionPart::Code(vec![26]), 1112 CompiledExpressionPart::Jump { 1113 conditionally: true, 1114 target: targets[0].clone(), 1115 }, 1116 CompiledExpressionPart::Code(vec![22, 35, 32, 22, 8, 32, 36, 22, 37]), 1117 CompiledExpressionPart::Jump { 1118 conditionally: false, 1119 target: targets[1].clone(), 1120 }, 1121 CompiledExpressionPart::LandingPad(targets[0].clone()), // capture from 1122 CompiledExpressionPart::Code(vec![34]), 1123 CompiledExpressionPart::Deref, 1124 CompiledExpressionPart::Code(vec![6]), 1125 CompiledExpressionPart::LandingPad(targets[1].clone()), // capture to 1126 CompiledExpressionPart::Code(vec![159]) 1127 ], 1128 need_deref: false, 1129 } 1130 ); 1131 1132 let e = expression!( 1133 DW_OP_lit1, 1134 DW_OP_dup, 1135 DW_OP_bra, 1136 2, 1137 0, // --> target 1138 DW_OP_deref, 1139 DW_OP_lit0, 1140 // target: 1141 DW_OP_stack_value 1142 ); 1143 let ce = compile_expression(&e, DWARF_ENCODING, None) 1144 .expect("non-error") 1145 .expect("expression"); 1146 let targets = find_jump_targets(&ce); 1147 assert_eq!(targets.len(), 1); 1148 assert_eq!( 1149 ce, 1150 CompiledExpression { 1151 parts: vec![ 1152 CompiledExpressionPart::Code(vec![49, 18]), 1153 CompiledExpressionPart::Jump { 1154 conditionally: true, 1155 target: targets[0].clone(), 1156 }, 1157 CompiledExpressionPart::Deref, 1158 CompiledExpressionPart::Code(vec![6, 48]), 1159 CompiledExpressionPart::LandingPad(targets[0].clone()), // capture to 1160 CompiledExpressionPart::Code(vec![159]) 1161 ], 1162 need_deref: false, 1163 } 1164 ); 1165 1166 let e = expression!( 1167 DW_OP_lit1, 1168 /* loop */ DW_OP_dup, 1169 DW_OP_lit25, 1170 DW_OP_ge, 1171 DW_OP_bra, 1172 5, 1173 0, // --> done 1174 DW_OP_plus_uconst, 1175 1, 1176 DW_OP_skip, 1177 (-11_i8), 1178 (!0), // --> loop 1179 /* done */ DW_OP_stack_value 1180 ); 1181 let ce = compile_expression(&e, DWARF_ENCODING, None) 1182 .expect("non-error") 1183 .expect("expression"); 1184 let targets = find_jump_targets(&ce); 1185 assert_eq!(targets.len(), 2); 1186 assert_eq!( 1187 ce, 1188 CompiledExpression { 1189 parts: vec![ 1190 CompiledExpressionPart::Code(vec![49]), 1191 CompiledExpressionPart::LandingPad(targets[0].clone()), 1192 CompiledExpressionPart::Code(vec![18, 73, 42]), 1193 CompiledExpressionPart::Jump { 1194 conditionally: true, 1195 target: targets[1].clone(), 1196 }, 1197 CompiledExpressionPart::Code(vec![35, 1]), 1198 CompiledExpressionPart::Jump { 1199 conditionally: false, 1200 target: targets[0].clone(), 1201 }, 1202 CompiledExpressionPart::LandingPad(targets[1].clone()), 1203 CompiledExpressionPart::Code(vec![159]) 1204 ], 1205 need_deref: false, 1206 } 1207 ); 1208 1209 let e = expression!(DW_OP_WASM_location, 0x0, 1, DW_OP_plus_uconst, 5); 1210 let ce = compile_expression(&e, DWARF_ENCODING, None) 1211 .expect("non-error") 1212 .expect("expression"); 1213 assert_eq!( 1214 ce, 1215 CompiledExpression { 1216 parts: vec![ 1217 CompiledExpressionPart::Local { 1218 label: val1, 1219 trailing: false 1220 }, 1221 CompiledExpressionPart::Code(vec![35, 5]) 1222 ], 1223 need_deref: true, 1224 } 1225 ); 1226 } 1227 1228 fn create_mock_address_transform() -> AddressTransform { 1229 use crate::FunctionAddressMap; 1230 use cranelift_entity::PrimaryMap; 1231 use wasmtime_environ::InstructionAddressMap; 1232 use wasmtime_environ::WasmFileInfo; 1233 1234 let mut module_map = PrimaryMap::new(); 1235 let code_section_offset: u32 = 100; 1236 let func = CompiledFunctionMetadata { 1237 address_map: FunctionAddressMap { 1238 instructions: vec![ 1239 InstructionAddressMap { 1240 srcloc: FilePos::new(code_section_offset + 12), 1241 code_offset: 5, 1242 }, 1243 InstructionAddressMap { 1244 srcloc: FilePos::default(), 1245 code_offset: 8, 1246 }, 1247 InstructionAddressMap { 1248 srcloc: FilePos::new(code_section_offset + 17), 1249 code_offset: 15, 1250 }, 1251 InstructionAddressMap { 1252 srcloc: FilePos::default(), 1253 code_offset: 23, 1254 }, 1255 ] 1256 .into(), 1257 start_srcloc: FilePos::new(code_section_offset + 10), 1258 end_srcloc: FilePos::new(code_section_offset + 20), 1259 body_offset: 0, 1260 body_len: 30, 1261 }, 1262 ..Default::default() 1263 }; 1264 module_map.push(&func); 1265 let fi = WasmFileInfo { 1266 code_section_offset: code_section_offset.into(), 1267 funcs: Vec::new(), 1268 imported_func_count: 0, 1269 path: None, 1270 }; 1271 AddressTransform::mock(&module_map, fi) 1272 } 1273 1274 fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) { 1275 use cranelift_codegen::{LabelValueLoc, ValueLocRange}; 1276 use cranelift_entity::EntityRef; 1277 use std::collections::HashMap; 1278 let mut value_ranges = HashMap::new(); 1279 let value_0 = ValueLabel::new(0); 1280 let value_1 = ValueLabel::new(1); 1281 let value_2 = ValueLabel::new(2); 1282 value_ranges.insert( 1283 value_0, 1284 vec![ValueLocRange { 1285 loc: LabelValueLoc::CFAOffset(0), 1286 start: 0, 1287 end: 25, 1288 }], 1289 ); 1290 value_ranges.insert( 1291 value_1, 1292 vec![ValueLocRange { 1293 loc: LabelValueLoc::CFAOffset(0), 1294 start: 5, 1295 end: 30, 1296 }], 1297 ); 1298 value_ranges.insert( 1299 value_2, 1300 vec![ 1301 ValueLocRange { 1302 loc: LabelValueLoc::CFAOffset(0), 1303 start: 0, 1304 end: 10, 1305 }, 1306 ValueLocRange { 1307 loc: LabelValueLoc::CFAOffset(0), 1308 start: 20, 1309 end: 30, 1310 }, 1311 ], 1312 ); 1313 (value_ranges, (value_0, value_1, value_2)) 1314 } 1315 1316 #[test] 1317 fn test_debug_value_range_builder() { 1318 use super::ValueLabelRangesBuilder; 1319 use crate::debug::ModuleMemoryOffset; 1320 1321 // Ignore this test if cranelift doesn't support the native platform. 1322 if cranelift_native::builder().is_err() { 1323 return; 1324 } 1325 1326 let isa = lookup(triple!("x86_64")) 1327 .expect("expect x86_64 ISA") 1328 .finish(Flags::new(cranelift_codegen::settings::builder())) 1329 .expect("Creating ISA"); 1330 1331 let addr_tr = create_mock_address_transform(); 1332 let (value_ranges, value_labels) = create_mock_value_ranges(); 1333 let fi = FunctionFrameInfo { 1334 memory_offset: ModuleMemoryOffset::None, 1335 value_ranges: &value_ranges, 1336 }; 1337 1338 // No value labels, testing if entire function range coming through. 1339 let builder = ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi), isa.as_ref()); 1340 let ranges = builder.into_ranges().ranges.collect::<Vec<_>>(); 1341 assert_eq!(ranges.len(), 1); 1342 assert_eq!(ranges[0].func_index, 0); 1343 assert_eq!(ranges[0].start, 0); 1344 assert_eq!(ranges[0].end, 30); 1345 1346 // Two labels ([email protected] and [email protected]), their common lifetime intersect at 5..25. 1347 let mut builder = 1348 ValueLabelRangesBuilder::new(&[(10, 20)], &addr_tr, Some(&fi), isa.as_ref()); 1349 builder.process_label(value_labels.0); 1350 builder.process_label(value_labels.1); 1351 let ranges = builder.into_ranges().ranges.collect::<Vec<_>>(); 1352 assert_eq!(ranges.len(), 1); 1353 assert_eq!(ranges[0].start, 5); 1354 assert_eq!(ranges[0].end, 25); 1355 1356 // Adds val2 with complex lifetime @0..10 and @20..30 to the previous test, and 1357 // also narrows range. 1358 let mut builder = 1359 ValueLabelRangesBuilder::new(&[(11, 17)], &addr_tr, Some(&fi), isa.as_ref()); 1360 builder.process_label(value_labels.0); 1361 builder.process_label(value_labels.1); 1362 builder.process_label(value_labels.2); 1363 let ranges = builder.into_ranges().ranges.collect::<Vec<_>>(); 1364 // Result is two ranges @5..10 and @20..23 1365 assert_eq!(ranges.len(), 2); 1366 assert_eq!(ranges[0].start, 5); 1367 assert_eq!(ranges[0].end, 10); 1368 assert_eq!(ranges[1].start, 20); 1369 assert_eq!(ranges[1].end, 23); 1370 } 1371 } 1372