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