1 | use crate::{BinaryReader, FromReader, Result, SectionLimited}; |
2 | use core::ops::Range; |
3 | |
4 | /// Reader for relocation entries within a `reloc.*` section. |
5 | pub type RelocationEntryReader<'a> = SectionLimited<'a, RelocationEntry>; |
6 | |
7 | /// Reader for reloc.* sections as defined by |
8 | /// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#relocation-sections>. |
9 | #[derive (Debug, Clone)] |
10 | pub struct RelocSectionReader<'a> { |
11 | section: u32, |
12 | range: Range<usize>, |
13 | entries: SectionLimited<'a, RelocationEntry>, |
14 | } |
15 | |
16 | impl<'a> RelocSectionReader<'a> { |
17 | /// Creates a new reader for a `reloc.*` section starting at |
18 | /// `original_position` within the wasm file. |
19 | pub fn new(mut reader: BinaryReader<'a>) -> Result<Self> { |
20 | let range = reader.range().clone(); |
21 | let section = reader.read_var_u32()?; |
22 | Ok(Self { |
23 | section, |
24 | range, |
25 | entries: SectionLimited::new(reader.shrink())?, |
26 | }) |
27 | } |
28 | |
29 | /// Index of section to which the relocations apply. |
30 | pub fn section_index(&self) -> u32 { |
31 | self.section |
32 | } |
33 | |
34 | /// The byte range of the entire section. |
35 | pub fn range(&self) -> Range<usize> { |
36 | self.range.clone() |
37 | } |
38 | |
39 | /// The relocation entries. |
40 | pub fn entries(&self) -> SectionLimited<'a, RelocationEntry> { |
41 | self.entries.clone() |
42 | } |
43 | } |
44 | |
45 | macro_rules! back_to_enum { |
46 | ($(#[$meta:meta])+ $vis:vis enum $name:ident { |
47 | $($(#[$vmeta:meta])* $vname:ident $(= $val:expr)?,)* |
48 | }) => { |
49 | $(#[$meta])* |
50 | $vis enum $name { |
51 | $($(#[$vmeta])* $vname $(= $val)?,)* |
52 | } |
53 | |
54 | impl TryFrom<u8> for $name { |
55 | type Error = (); |
56 | |
57 | fn try_from(v: u8) -> Result<Self, Self::Error> { |
58 | match v { |
59 | $(x if x == $name::$vname as u8 => Ok($name::$vname),)* |
60 | _ => Err(()), |
61 | } |
62 | } |
63 | } |
64 | } |
65 | } |
66 | |
67 | back_to_enum! { |
68 | |
69 | /// Relocation entry type. Each entry type corresponds to one of the |
70 | /// `R_WASM_*` constants defined at |
71 | /// <https://github.com/llvm/llvm-project/blob/main/llvm/include/llvm/BinaryFormat/WasmRelocs.def> |
72 | /// and |
73 | /// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#relocation-sections>. |
74 | #[derive (Debug, PartialEq, Eq, Hash, Clone, Copy)] |
75 | #[repr (u8)] |
76 | pub enum RelocationType { |
77 | /// A function index encoded as a 5-byte varuint32. Used for the |
78 | /// immediate argument of a call instruction. (since LLVM 10.0) |
79 | FunctionIndexLeb = 0, |
80 | |
81 | /// A function table index encoded as a 5-byte varint32. Used to refer |
82 | /// to the immediate argument of a i32.const instruction, e.g. taking |
83 | /// the address of a function. (since LLVM 10.0) |
84 | TableIndexSleb = 1, |
85 | |
86 | /// A function table index encoded as a uint32, e.g. taking the address |
87 | /// of a function in a static data initializer. (since LLVM 10.0) |
88 | TableIndexI32 = 2, |
89 | |
90 | /// A linear memory index encoded as a 5-byte varuint32. Used for the |
91 | /// immediate argument of a load or store instruction, e.g. directly |
92 | /// loading from or storing to a C++ global. (since LLVM 10.0) |
93 | MemoryAddrLeb = 3, |
94 | |
95 | /// A linear memory index encoded as a 5-byte varint32. Used for the |
96 | /// immediate argument of a i32.const instruction, e.g. taking the |
97 | /// address of a C++ global. (since LLVM 10.0) |
98 | MemoryAddrSleb = 4, |
99 | |
100 | /// A linear memory index encoded as a uint32, e.g. taking the address |
101 | /// of a C++ global in a static data initializer. (since LLVM 10.0) |
102 | MemoryAddrI32 = 5, |
103 | |
104 | /// A type index encoded as a 5-byte varuint32, e.g. the type immediate |
105 | /// in a call_indirect. (since LLVM 10.0) |
106 | TypeIndexLeb = 6, |
107 | |
108 | /// A global index encoded as a 5-byte varuint32, e.g. the index |
109 | /// immediate in a get_global. (since LLVM 10.0) |
110 | GlobalIndexLeb = 7, |
111 | |
112 | /// A byte offset within code section for the specific function encoded |
113 | /// as a uint32. The offsets start at the actual function code excluding |
114 | /// its size field. (since LLVM 10.0) |
115 | FunctionOffsetI32 = 8, |
116 | |
117 | /// A byte offset from start of the specified section encoded as a |
118 | /// uint32. (since LLVM 10.0) |
119 | SectionOffsetI32 = 9, |
120 | |
121 | /// An event index encoded as a 5-byte varuint32. Used for the immediate |
122 | /// argument of a throw and if_except instruction. (since LLVM 10.0) |
123 | EventIndexLeb = 10, |
124 | |
125 | /// A memory address relative to the __memory_base wasm global. Used in |
126 | /// position independent code (-fPIC) where absolute memory addresses |
127 | /// are not known at link time. |
128 | MemoryAddrRelSleb = 11, |
129 | |
130 | /// A function address (table index) relative to the __table_base wasm |
131 | /// global. Used in position indepenent code (-fPIC) where absolute |
132 | /// function addresses are not known at link time. |
133 | TableIndexRelSleb = 12, |
134 | |
135 | /// A global index encoded as uint32. (since LLVM 11.0) |
136 | GlobalIndexI32 = 13, |
137 | |
138 | /// The 64-bit counterpart of `MemoryAddrLeb`. A 64-bit linear memory |
139 | /// index encoded as a 10-byte varuint64, Used for the immediate |
140 | /// argument of a load or store instruction on a 64-bit linear memory |
141 | /// array. (since LLVM 11.0) |
142 | MemoryAddrLeb64 = 14, |
143 | |
144 | /// The 64-bit counterpart of `MemoryAddrSleb`. A 64-bit linear memory |
145 | /// index encoded as a 10-byte varint64. Used for the immediate argument |
146 | /// of a i64.const instruction. (since LLVM 11.0) |
147 | MemoryAddrSleb64 = 15, |
148 | |
149 | /// The 64-bit counterpart of `MemoryAddrI32`. A 64-bit linear memory |
150 | /// index encoded as a uint64, e.g. taking the 64-bit address of a C++ |
151 | /// global in a static data initializer. (since LLVM 11.0) |
152 | MemoryAddrI64 = 16, |
153 | |
154 | /// The 64-bit counterpart of `MemoryAddrRelSleb`. |
155 | MemoryAddrRelSleb64 = 17, |
156 | |
157 | /// The 64-bit counterpart of `TableIndexSleb`. A function table index |
158 | /// encoded as a 10-byte varint64. Used to refer to the immediate |
159 | /// argument of a i64.const instruction, e.g. taking the address of a |
160 | /// function in Wasm64. (in LLVM 12.0) |
161 | TableIndexSleb64 = 18, |
162 | |
163 | /// The 64-bit counterpart of `TableIndexI32`. A function table index |
164 | /// encoded as a uint64, e.g. taking the address of a function in a |
165 | /// static data initializer. (in LLVM 12.0) |
166 | TableIndexI64 = 19, |
167 | |
168 | /// A table number encoded as a 5-byte varuint32. Used for the table |
169 | /// immediate argument in the table.* instructions. (in LLVM 12.0) |
170 | TableNumberLeb = 20, |
171 | |
172 | /// An offset from the __tls_base symbol encoded as a 5-byte varint32. |
173 | /// Used for PIC case to avoid absolute relocation. (in LLVM 12.0) |
174 | MemoryAddrTlsSleb = 21, |
175 | |
176 | /// The 64-bit counterpart of `FunctionOffsetI32`. A byte offset within |
177 | /// code section for the specific function encoded as a uint64. (in LLVM |
178 | /// 12.0) |
179 | FunctionOffsetI64 = 22, |
180 | |
181 | /// A byte offset between the relocating address and a linear memory |
182 | /// index encoded as a uint32. Used for pointer-relative addressing. (in |
183 | /// LLVM 13.0) |
184 | MemoryAddrLocrelI32 = 23, |
185 | |
186 | /// The 64-bit counterpart of `TableIndexRelSleb`. A function table |
187 | /// index encoded as a 10-byte varint64. (in LLVM 13.0) |
188 | TableIndexRelSleb64 = 24, |
189 | |
190 | /// The 64-bit counterpart of `MemoryAddrTlsSleb`. (in LLVM 13.0) |
191 | MemoryAddrTlsSleb64 = 25, |
192 | |
193 | /// A function index encoded as a uint32. Used in custom sections for |
194 | /// function annotations (`__attribute__((annotate(<name>)))`) (in LLVM |
195 | /// 17.0) |
196 | FunctionIndexI32 = 26, |
197 | } |
198 | |
199 | } |
200 | |
201 | impl<'a> FromReader<'a> for RelocationType { |
202 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
203 | let num: u8 = reader.read_u8()?; |
204 | num.try_into().or_else(|_| { |
205 | Err(BinaryReader::invalid_leading_byte_error( |
206 | byte:num, |
207 | desc:"RelocEntryType" , |
208 | offset:reader.original_position() - 1, |
209 | )) |
210 | }) |
211 | } |
212 | } |
213 | |
214 | /// Indicates the kind of addend that applies to a relocation entry. |
215 | #[derive (Debug, PartialEq, Eq, Hash, Clone, Copy)] |
216 | pub enum RelocAddendKind { |
217 | /// Relocation entry does not include an addend. |
218 | None, |
219 | /// Relocation entry includes a 32-bit addend. |
220 | Addend32, |
221 | /// Relocation entry includes a 64-bit addend. |
222 | Addend64, |
223 | } |
224 | |
225 | /// Single relocation entry within a `reloc.*` section, as defined at |
226 | /// <https://github.com/WebAssembly/tool-conventions/blob/main/Linking.md#relocation-sections>. |
227 | #[derive (Debug, PartialEq, Eq, Hash, Clone, Copy)] |
228 | pub struct RelocationEntry { |
229 | /// Relocation entry type. |
230 | pub ty: RelocationType, |
231 | /// Offset in bytes from the start of the section indicated by |
232 | /// `RelocSectionReader::section` targetted by this relocation. |
233 | pub offset: u32, |
234 | /// Index in the symbol table contained in the linking section that |
235 | /// corresponds to the value at `offset`. |
236 | pub index: u32, |
237 | /// Addend to add to the address, or `0` if not applicable. The value must |
238 | /// be consistent with the `self.ty.addend_kind()`. |
239 | pub addend: i64, |
240 | } |
241 | |
242 | impl RelocationEntry { |
243 | /// Byte range relative to the start of the section indicated by |
244 | /// `RelocSectionReader::section` targetted by this relocation. |
245 | pub fn relocation_range(&self) -> Range<usize> { |
246 | (self.offset as usize)..(self.offset as usize + self.ty.extent()) |
247 | } |
248 | } |
249 | |
250 | impl RelocationType { |
251 | /// Indicates if this relocation type has an associated `RelocEntry::addend`. |
252 | pub const fn addend_kind(self: Self) -> RelocAddendKind { |
253 | use RelocationType::*; |
254 | match self { |
255 | MemoryAddrLeb | MemoryAddrSleb | MemoryAddrI32 | FunctionOffsetI32 |
256 | | SectionOffsetI32 | MemoryAddrLocrelI32 | MemoryAddrRelSleb | MemoryAddrTlsSleb => { |
257 | RelocAddendKind::Addend32 |
258 | } |
259 | MemoryAddrRelSleb64 | MemoryAddrTlsSleb64 | MemoryAddrLeb64 | MemoryAddrSleb64 |
260 | | MemoryAddrI64 | FunctionOffsetI64 => RelocAddendKind::Addend64, |
261 | _ => RelocAddendKind::None, |
262 | } |
263 | } |
264 | |
265 | /// Indicates the number of bytes that this relocation type targets. |
266 | pub const fn extent(self) -> usize { |
267 | use RelocationType::*; |
268 | match self { |
269 | FunctionIndexLeb | TableIndexSleb | MemoryAddrLeb | MemoryAddrSleb | TypeIndexLeb |
270 | | GlobalIndexLeb | EventIndexLeb | MemoryAddrRelSleb | TableIndexRelSleb |
271 | | TableNumberLeb | MemoryAddrTlsSleb => 5, |
272 | MemoryAddrLeb64 | MemoryAddrSleb64 | TableIndexSleb64 | TableIndexRelSleb64 |
273 | | MemoryAddrRelSleb64 | MemoryAddrTlsSleb64 => 10, |
274 | |
275 | TableIndexI32 | MemoryAddrI32 | FunctionOffsetI32 | SectionOffsetI32 |
276 | | GlobalIndexI32 | MemoryAddrLocrelI32 | FunctionIndexI32 => 4, |
277 | |
278 | MemoryAddrI64 | TableIndexI64 | FunctionOffsetI64 => 8, |
279 | } |
280 | } |
281 | } |
282 | |
283 | impl<'a> FromReader<'a> for RelocationEntry { |
284 | fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> { |
285 | let ty: RelocationType = RelocationType::from_reader(reader)?; |
286 | let offset: u32 = reader.read_var_u32()?; |
287 | let index: u32 = reader.read_var_u32()?; |
288 | let addend: i64 = match ty.addend_kind() { |
289 | RelocAddendKind::None => 0, |
290 | RelocAddendKind::Addend32 => reader.read_var_i32()? as i64, |
291 | RelocAddendKind::Addend64 => reader.read_var_i64()?, |
292 | }; |
293 | Ok(RelocationEntry { |
294 | ty, |
295 | offset, |
296 | index, |
297 | addend, |
298 | }) |
299 | } |
300 | } |
301 | |