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