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