1 //! Data structures to provide transformation of the source 2 3 use crate::InstructionAddressMap; 4 use crate::obj::ELF_WASMTIME_ADDRMAP; 5 use crate::prelude::*; 6 use object::write::{Object, StandardSegment}; 7 use object::{LittleEndian, SectionKind, U32}; 8 use std::ops::Range; 9 10 /// Builder for the address map section of a wasmtime compilation image. 11 /// 12 /// This builder is used to conveniently built the `ELF_WASMTIME_ADDRMAP` 13 /// section by compilers, and provides utilities to directly insert the results 14 /// into an `Object`. 15 #[derive(Default)] 16 pub struct AddressMapSection { 17 offsets: Vec<U32<LittleEndian>>, 18 positions: Vec<U32<LittleEndian>>, 19 last_offset: u32, 20 } 21 22 impl AddressMapSection { 23 /// Pushes a new set of instruction mapping information for a function added 24 /// in the executable. 25 /// 26 /// The `func` argument here is the range of the function, relative to the 27 /// start of the text section in the executable. The `instrs` provided are 28 /// the descriptors for instructions in the function and their various 29 /// mappings back to original source positions. 30 /// 31 /// This is required to be called for `func` values that are strictly 32 /// increasing in addresses (e.g. as the object is built). Additionally the 33 /// `instrs` map must be sorted based on code offset in the native text 34 /// section. push(&mut self, func: Range<u64>, instrs: &[InstructionAddressMap])35 pub fn push(&mut self, func: Range<u64>, instrs: &[InstructionAddressMap]) { 36 // NB: for now this only supports <=4GB text sections in object files. 37 // Alternative schemes will need to be created for >32-bit offsets to 38 // avoid making this section overly large. 39 let func_start = u32::try_from(func.start).unwrap(); 40 let func_end = u32::try_from(func.end).unwrap(); 41 42 self.offsets.reserve(instrs.len()); 43 self.positions.reserve(instrs.len()); 44 let mut last_srcloc = None; 45 for map in instrs { 46 // Sanity-check to ensure that functions are pushed in-order, otherwise 47 // the `offsets` array won't be sorted which is our goal. 48 let pos = func_start + map.code_offset; 49 assert!(pos >= self.last_offset); 50 self.last_offset = pos; 51 52 // Drop duplicate instruction mappings that match what was 53 // previously pushed into the array since the representation used 54 // here will naturally cover `pos` with the previous entry. 55 let srcloc = map.srcloc.file_offset().unwrap_or(u32::MAX); 56 if Some(srcloc) == last_srcloc { 57 continue; 58 } 59 last_srcloc = Some(srcloc); 60 61 self.offsets.push(U32::new(LittleEndian, pos)); 62 self.positions.push(U32::new(LittleEndian, srcloc)); 63 } 64 self.last_offset = func_end; 65 } 66 67 /// Finishes encoding this section into the `Object` provided. append_to(self, obj: &mut Object)68 pub fn append_to(self, obj: &mut Object) { 69 let section = obj.add_section( 70 obj.segment_name(StandardSegment::Data).to_vec(), 71 ELF_WASMTIME_ADDRMAP.as_bytes().to_vec(), 72 SectionKind::ReadOnlyData, 73 ); 74 75 // NB: this matches the encoding expected by `lookup` below. 76 let amt = u32::try_from(self.offsets.len()).unwrap(); 77 obj.append_section_data(section, &amt.to_le_bytes(), 1); 78 obj.append_section_data(section, object::bytes_of_slice(&self.offsets), 1); 79 obj.append_section_data(section, object::bytes_of_slice(&self.positions), 1); 80 } 81 } 82