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