/src/wasmtime/crates/environ/src/address_map.rs
Line | Count | Source |
1 | | //! Data structures to provide transformation of the source |
2 | | |
3 | | use object::{Bytes, LittleEndian, U32Bytes}; |
4 | | use serde_derive::{Deserialize, Serialize}; |
5 | | |
6 | | /// Single source location to generated address mapping. |
7 | | #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] |
8 | | pub struct InstructionAddressMap { |
9 | | /// Where in the source wasm binary this instruction comes from, specified |
10 | | /// in an offset of bytes from the front of the file. |
11 | | pub srcloc: FilePos, |
12 | | |
13 | | /// Offset from the start of the function's compiled code to where this |
14 | | /// instruction is located, or the region where it starts. |
15 | | pub code_offset: u32, |
16 | | } |
17 | | |
18 | | /// A position within an original source file, |
19 | | /// |
20 | | /// This structure is used as a newtype wrapper around a 32-bit integer which |
21 | | /// represents an offset within a file where a wasm instruction or function is |
22 | | /// to be originally found. |
23 | | #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] |
24 | | pub struct FilePos(u32); |
25 | | |
26 | | impl FilePos { |
27 | | /// Create a new file position with the given offset. |
28 | 36.1M | pub fn new(pos: u32) -> FilePos { |
29 | 36.1M | assert!(pos != u32::MAX); |
30 | 36.1M | FilePos(pos) |
31 | 36.1M | } |
32 | | |
33 | | /// Get the null file position. |
34 | 45.8M | pub fn none() -> FilePos { |
35 | 45.8M | FilePos(u32::MAX) |
36 | 45.8M | } |
37 | | |
38 | | /// Is this the null file position? |
39 | | #[inline] |
40 | 16.5M | pub fn is_none(&self) -> bool { |
41 | 16.5M | *self == FilePos::none() |
42 | 16.5M | } |
43 | | |
44 | | /// Returns the offset that this offset was created with. |
45 | | /// |
46 | | /// Note that positions created with `FilePos::none` and the `Default` |
47 | | /// implementation will return `None` here, whereas positions created with |
48 | | /// `FilePos::new` will return `Some`. |
49 | 45.1M | pub fn file_offset(self) -> Option<u32> { |
50 | 45.1M | if self.0 == u32::MAX { |
51 | 12.4M | None |
52 | | } else { |
53 | 32.6M | Some(self.0) |
54 | | } |
55 | 45.1M | } |
56 | | } |
57 | | |
58 | | impl Default for FilePos { |
59 | 24.2M | fn default() -> FilePos { |
60 | 24.2M | FilePos::none() |
61 | 24.2M | } |
62 | | } |
63 | | |
64 | | /// Parse an `ELF_WASMTIME_ADDRMAP` section, returning the slice of code offsets |
65 | | /// and the slice of associated file positions for each offset. |
66 | 16.5M | fn parse_address_map( |
67 | 16.5M | section: &[u8], |
68 | 16.5M | ) -> Option<(&[U32Bytes<LittleEndian>], &[U32Bytes<LittleEndian>])> { |
69 | 16.5M | let mut section = Bytes(section); |
70 | | // NB: this matches the encoding written by `append_to` in the |
71 | | // `compile::address_map` module. |
72 | 16.5M | let count = section.read::<U32Bytes<LittleEndian>>().ok()?; |
73 | 8.42M | let count = usize::try_from(count.get(LittleEndian)).ok()?; |
74 | 8.42M | let (offsets, section) = |
75 | 8.42M | object::slice_from_bytes::<U32Bytes<LittleEndian>>(section.0, count).ok()?; |
76 | 8.42M | let (positions, section) = |
77 | 8.42M | object::slice_from_bytes::<U32Bytes<LittleEndian>>(section, count).ok()?; |
78 | 8.42M | debug_assert!(section.is_empty()); |
79 | 8.42M | Some((offsets, positions)) |
80 | 16.5M | } |
81 | | |
82 | | /// Lookup an `offset` within an encoded address map section, returning the |
83 | | /// original `FilePos` that corresponds to the offset, if found. |
84 | | /// |
85 | | /// This function takes a `section` as its first argument which must have been |
86 | | /// created with `AddressMapSection` above. This is intended to be the raw |
87 | | /// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact. |
88 | | /// |
89 | | /// The `offset` provided is a relative offset from the start of the text |
90 | | /// section of the pc that is being looked up. If `offset` is out of range or |
91 | | /// doesn't correspond to anything in this file then `None` is returned. |
92 | 16.5M | pub fn lookup_file_pos(section: &[u8], offset: usize) -> Option<FilePos> { |
93 | 16.5M | let (offsets, positions) = parse_address_map(section)?; |
94 | | |
95 | | // First perform a binary search on the `offsets` array. This is a sorted |
96 | | // array of offsets within the text section, which is conveniently what our |
97 | | // `offset` also is. Note that we are somewhat unlikely to find a precise |
98 | | // match on the element in the array, so we're largely interested in which |
99 | | // "bucket" the `offset` falls into. |
100 | 8.42M | let offset = u32::try_from(offset).ok()?; |
101 | 70.9M | let index = match offsets.binary_search_by_key(&offset, |v| v.get(LittleEndian)) { |
102 | | // Exact hit! |
103 | 83.9k | Ok(i) => i, |
104 | | |
105 | | // This *would* be at the first slot in the array, so no |
106 | | // instructions cover `pc`. |
107 | 0 | Err(0) => return None, |
108 | | |
109 | | // This would be at the `nth` slot, so we're at the `n-1`th slot. |
110 | 8.34M | Err(n) => n - 1, |
111 | | }; |
112 | | |
113 | | // Using the `index` we found of which bucket `offset` corresponds to we can |
114 | | // lookup the actual `FilePos` value in the `positions` array. |
115 | 8.42M | let pos = positions.get(index)?; |
116 | 8.42M | Some(FilePos(pos.get(LittleEndian))) |
117 | 16.5M | } |
118 | | |
119 | | /// Iterate over the address map contained in the given address map section. |
120 | | /// |
121 | | /// This function takes a `section` as its first argument which must have been |
122 | | /// created with `AddressMapSection` above. This is intended to be the raw |
123 | | /// `ELF_WASMTIME_ADDRMAP` section from the compilation artifact. |
124 | | /// |
125 | | /// The yielded offsets are relative to the start of the text section for this |
126 | | /// map's code object. |
127 | 0 | pub fn iterate_address_map<'a>( |
128 | 0 | section: &'a [u8], |
129 | 0 | ) -> Option<impl Iterator<Item = (u32, FilePos)> + 'a> { |
130 | 0 | let (offsets, positions) = parse_address_map(section)?; |
131 | | |
132 | | Some( |
133 | 0 | offsets |
134 | 0 | .iter() |
135 | 0 | .map(|o| o.get(LittleEndian)) |
136 | 0 | .zip(positions.iter().map(|pos| FilePos(pos.get(LittleEndian)))), |
137 | | ) |
138 | 0 | } |