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