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