1 use std::any::Any;
2 
3 use crate::{mach_reloc_to_reloc, mach_trap_to_trap, Relocation};
4 use cranelift_codegen::{
5     ir, ir::UserExternalNameRef, isa::unwind::CfaUnwindInfo, isa::unwind::UnwindInfo, Final,
6     MachBufferFinalized, MachSrcLoc, ValueLabelsRanges,
7 };
8 use wasmtime_environ::{FilePos, InstructionAddressMap, TrapInformation};
9 
10 /// Trait used in the [CompiledFunction] to resolve the locations of
11 /// external name references in a compiled function.
12 pub trait CompiledFuncEnv {
13     fn resolve_user_external_name_ref(&self, external: UserExternalNameRef) -> (u32, u32);
14 }
15 
16 #[derive(Debug, Clone, PartialEq, Eq, Default)]
17 /// Metadata to translate from binary offsets back to the original
18 /// location found in the wasm input.
19 pub struct FunctionAddressMap {
20     /// An array of data for the instructions in this function, indicating where
21     /// each instruction maps back to in the original function.
22     ///
23     /// This array is sorted least-to-greatest by the `code_offset` field.
24     /// Additionally the span of each `InstructionAddressMap` is implicitly the
25     /// gap between it and the next item in the array.
26     pub instructions: Box<[InstructionAddressMap]>,
27 
28     /// Function's initial offset in the source file, specified in bytes from
29     /// the front of the file.
30     pub start_srcloc: FilePos,
31 
32     /// Function's end offset in the source file, specified in bytes from
33     /// the front of the file.
34     pub end_srcloc: FilePos,
35 
36     /// Generated function body offset if applicable, otherwise 0.
37     pub body_offset: usize,
38 
39     /// Generated function body length.
40     pub body_len: u32,
41 }
42 
43 /// The metadata for the compiled function.
44 #[derive(Default)]
45 pub struct CompiledFunctionMetadata {
46     /// The function address map to translate from binary
47     /// back to the original source.
48     pub address_map: FunctionAddressMap,
49     /// The unwind information.
50     pub unwind_info: Option<UnwindInfo>,
51     /// CFA-based unwind information for DWARF debugging support.
52     pub cfa_unwind_info: Option<CfaUnwindInfo>,
53     /// Mapping of value labels and their locations.
54     pub value_labels_ranges: ValueLabelsRanges,
55     /// Allocated stack slots.
56     pub sized_stack_slots: ir::StackSlots,
57     /// Start source location.
58     pub start_srcloc: FilePos,
59     /// End source location.
60     pub end_srcloc: FilePos,
61 }
62 
63 /// Compiled function: machine code body, jump table offsets, and unwind information.
64 pub struct CompiledFunction {
65     /// The machine code buffer for this function.
66     pub buffer: MachBufferFinalized<Final>,
67     /// The environment for the compiled function.
68     env: Box<dyn CompiledFuncEnv + Send + 'static>,
69     /// The alignment for the compiled function.
70     pub alignment: u32,
71     /// The metadata for the compiled function, including unwind information
72     /// the function address map.
73     metadata: CompiledFunctionMetadata,
74 }
75 
76 impl CompiledFunction {
77     /// Creates a [CompiledFunction] from a [cranelift_codegen::MachBufferFinalized<Final>]
78     /// This function uses the information in the machine buffer to derive the traps and relocations
79     /// fields. The compiled function metadata is loaded with the default values.
80     pub fn new<E>(buffer: MachBufferFinalized<Final>, env: E, alignment: u32) -> Self
81     where
82         E: Any + CompiledFuncEnv + Send + 'static,
83     {
84         Self {
85             buffer,
86             env: Box::new(env),
87             alignment,
88             metadata: Default::default(),
89         }
90     }
91 
92     /// Returns an iterator to the function's relocation information.
93     pub fn relocations(&self) -> impl Iterator<Item = Relocation> + '_ {
94         self.buffer.relocs().iter().map(|r| {
95             mach_reloc_to_reloc(r, |external| {
96                 self.env.resolve_user_external_name_ref(external)
97             })
98         })
99     }
100 
101     /// Returns an iterator to the function's trap information.
102     pub fn traps(&self) -> impl Iterator<Item = TrapInformation> + '_ {
103         self.buffer.traps().iter().filter_map(mach_trap_to_trap)
104     }
105 
106     /// Get the function's address map from the metadata.
107     pub fn address_map(&self) -> &FunctionAddressMap {
108         &self.metadata.address_map
109     }
110 
111     /// Create and return the compiled function address map from the original source offset
112     /// and length.
113     pub fn set_address_map(&mut self, offset: u32, length: u32, with_instruction_addresses: bool) {
114         assert!((offset + length) <= u32::max_value());
115         let len = self.buffer.data().len();
116         let srclocs = self
117             .buffer
118             .get_srclocs_sorted()
119             .into_iter()
120             .map(|&MachSrcLoc { start, end, loc }| (loc, start, (end - start)));
121         let instructions = if with_instruction_addresses {
122             collect_address_maps(len as u32, srclocs)
123         } else {
124             Default::default()
125         };
126         let start_srcloc = FilePos::new(offset);
127         let end_srcloc = FilePos::new(offset + length);
128 
129         let address_map = FunctionAddressMap {
130             instructions: instructions.into(),
131             start_srcloc,
132             end_srcloc,
133             body_offset: 0,
134             body_len: len as u32,
135         };
136 
137         self.metadata.address_map = address_map;
138     }
139 
140     /// Get a reference to the unwind information from the
141     /// function's metadata.
142     pub fn unwind_info(&self) -> Option<&UnwindInfo> {
143         self.metadata.unwind_info.as_ref()
144     }
145 
146     /// Get a reference to the compiled function metadata.
147     pub fn metadata(&self) -> &CompiledFunctionMetadata {
148         &self.metadata
149     }
150 
151     /// Set the value labels ranges in the function's metadata.
152     pub fn set_value_labels_ranges(&mut self, ranges: ValueLabelsRanges) {
153         self.metadata.value_labels_ranges = ranges;
154     }
155 
156     /// Set the unwind info in the function's metadata.
157     pub fn set_unwind_info(&mut self, unwind: UnwindInfo) {
158         self.metadata.unwind_info = Some(unwind);
159     }
160 
161     /// Set the CFA-based unwind info in the function's metadata.
162     pub fn set_cfa_unwind_info(&mut self, unwind: CfaUnwindInfo) {
163         self.metadata.cfa_unwind_info = Some(unwind);
164     }
165 
166     /// Set the sized stack slots.
167     pub fn set_sized_stack_slots(&mut self, slots: ir::StackSlots) {
168         self.metadata.sized_stack_slots = slots;
169     }
170 }
171 
172 // Collects an iterator of `InstructionAddressMap` into a `Vec` for insertion
173 // into a `FunctionAddressMap`. This will automatically coalesce adjacent
174 // instructions which map to the same original source position.
175 fn collect_address_maps(
176     code_size: u32,
177     iter: impl IntoIterator<Item = (ir::SourceLoc, u32, u32)>,
178 ) -> Vec<InstructionAddressMap> {
179     let mut iter = iter.into_iter();
180     let (mut cur_loc, mut cur_offset, mut cur_len) = match iter.next() {
181         Some(i) => i,
182         None => return Vec::new(),
183     };
184     let mut ret = Vec::new();
185     for (loc, offset, len) in iter {
186         // If this instruction is adjacent to the previous and has the same
187         // source location then we can "coalesce" it with the current
188         // instruction.
189         if cur_offset + cur_len == offset && loc == cur_loc {
190             cur_len += len;
191             continue;
192         }
193 
194         // Push an entry for the previous source item.
195         ret.push(InstructionAddressMap {
196             srcloc: cvt(cur_loc),
197             code_offset: cur_offset,
198         });
199         // And push a "dummy" entry if necessary to cover the span of ranges,
200         // if any, between the previous source offset and this one.
201         if cur_offset + cur_len != offset {
202             ret.push(InstructionAddressMap {
203                 srcloc: FilePos::default(),
204                 code_offset: cur_offset + cur_len,
205             });
206         }
207         // Update our current location to get extended later or pushed on at
208         // the end.
209         cur_loc = loc;
210         cur_offset = offset;
211         cur_len = len;
212     }
213     ret.push(InstructionAddressMap {
214         srcloc: cvt(cur_loc),
215         code_offset: cur_offset,
216     });
217     if cur_offset + cur_len != code_size {
218         ret.push(InstructionAddressMap {
219             srcloc: FilePos::default(),
220             code_offset: cur_offset + cur_len,
221         });
222     }
223 
224     return ret;
225 
226     fn cvt(loc: ir::SourceLoc) -> FilePos {
227         if loc.is_default() {
228             FilePos::default()
229         } else {
230             FilePos::new(loc.bits())
231         }
232     }
233 }
234