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