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