wasmparser/readers/core/
reloc.rs

1use crate::{BinaryReader, FromReader, Result, SectionLimited};
2use core::ops::Range;
3
4/// Reader for relocation entries within a `reloc.*` section.
5pub 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)]
10pub struct RelocSectionReader<'a> {
11    section: u32,
12    range: Range<usize>,
13    entries: SectionLimited<'a, RelocationEntry>,
14}
15
16impl<'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
45macro_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
67back_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
201impl<'a> FromReader<'a> for RelocationType {
202    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
203        let num = reader.read_u8()?;
204        num.try_into().or_else(|_| {
205            Err(BinaryReader::invalid_leading_byte_error(
206                num,
207                "RelocEntryType",
208                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)]
216pub 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)]
228pub 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
242impl 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
250impl 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
283impl<'a> FromReader<'a> for RelocationEntry {
284    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
285        let ty = RelocationType::from_reader(reader)?;
286        let offset = reader.read_var_u32()?;
287        let index = reader.read_var_u32()?;
288        let addend = 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}