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