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