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