wasmer_compiler/types/
relocation.rs

1/*
2 * ! Remove me once rkyv generates doc-comments for fields or generates an #[allow(missing_docs)]
3 * on their own.
4 */
5#![allow(missing_docs)]
6
7//! Relocation is the process of assigning load addresses for position-dependent
8//! code and data of a program and adjusting the code and data to reflect the
9//! assigned addresses.
10//!
11//! [Learn more](https://en.wikipedia.org/wiki/Relocation_(computing)).
12//!
13//! Each time a `Compiler` compiles a WebAssembly function (into machine code),
14//! it also attaches if there are any relocations that need to be patched into
15//! the generated machine code, so a given frontend (JIT or native) can
16//! do the corresponding work to run it.
17
18use super::section::SectionIndex;
19use crate::{Addend, CodeOffset};
20use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
21#[cfg(feature = "enable-serde")]
22use serde::{Deserialize, Serialize};
23use wasmer_types::{entity::PrimaryMap, lib::std::fmt, LibCall, LocalFunctionIndex};
24
25/// Relocation kinds for every ISA.
26#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
27#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
28#[derive(RkyvSerialize, RkyvDeserialize, Archive, Copy, Clone, Debug, PartialEq, Eq)]
29#[rkyv(derive(Debug), compare(PartialEq))]
30#[repr(u8)]
31pub enum RelocationKind {
32    /// absolute 4-byte
33    Abs4,
34    /// absolute 8-byte
35    Abs8,
36    /// x86 PC-relative 4-byte
37    X86PCRel4,
38    /// x86 PC-relative 8-byte
39    X86PCRel8,
40    /// x86 call to PC-relative 4-byte
41    X86CallPCRel4,
42    /// x86 call to PLT-relative 4-byte
43    X86CallPLTRel4,
44    /// x86 GOT PC-relative 4-byte
45    X86GOTPCRel4,
46
47    /// R_AARCH64_ADR_PREL_LO21
48    Aarch64AdrPrelLo21,
49
50    /// R_AARCH64_ADR_PREL_PG_HI21
51    Aarch64AdrPrelPgHi21,
52
53    /// R_AARCH64_ADD_ABS_LO12_NC
54    Aarch64AddAbsLo12Nc,
55
56    /// R_AARCH64_LDST128_ABS_LO12_NC
57    Aarch64Ldst128AbsLo12Nc,
58
59    /// R_AARCH64_LDST64_ABS_LO12_NC
60    Aarch64Ldst64AbsLo12Nc,
61
62    /// Arm32 call target
63    Arm32Call,
64    /// Arm64 call target
65    Arm64Call,
66    /// Arm64 movk/z part 0
67    Arm64Movw0,
68    /// Arm64 movk/z part 1
69    Arm64Movw1,
70    /// Arm64 movk/z part 2
71    Arm64Movw2,
72    /// Arm64 movk/z part 3
73    Arm64Movw3,
74    /// RISC-V PC-relative high 20bit
75    RiscvPCRelHi20,
76    /// RISC-V PC-relative low 12bit, I-type
77    RiscvPCRelLo12I,
78    /// RISC-V call target
79    RiscvCall,
80    /// LoongArch absolute high 20bit
81    LArchAbsHi20,
82    /// LoongArch absolute low 12bit
83    LArchAbsLo12,
84    /// LoongArch absolute high 12bit
85    LArchAbs64Hi12,
86    /// LoongArch absolute low 20bit
87    LArchAbs64Lo20,
88    /// LoongArch PC-relative call 38bit
89    LArchCall36,
90    /// LoongArch PC-relative high 20bit
91    LArchPCAlaHi20,
92    /// LoongArch PC-relative low 12bit
93    LArchPCAlaLo12,
94    /// LoongArch PC64-relative high 12bit
95    LArchPCAla64Hi12,
96    /// LoongArch PC64-relative low 20bit
97    LArchPCAla64Lo20,
98    /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
99    ElfX86_64TlsGd,
100    // /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
101    // MachOX86_64Tlv,
102
103    // -- Mach-O-specific relocations
104    //
105    // --- Arm64
106    // (MACHO_ARM64_RELOC_UNSIGNED) for pointers
107    MachoArm64RelocUnsigned,
108    // (MACHO_ARM64_RELOC_SUBTRACTOR) must be followed by a ARM64_RELOC_UNSIGNED
109    MachoArm64RelocSubtractor,
110    // (MACHO_ARM64_RELOC_BRANCH26) a B/BL instruction with 26-bit displacement
111    MachoArm64RelocBranch26,
112    // (MACHO_ARM64_RELOC_PAGE21) pc-rel distance to page of target
113    MachoArm64RelocPage21,
114    // (MACHO_ARM64_RELOC_PAGEOFF12) offset within page, scaled by r_length
115    MachoArm64RelocPageoff12,
116    // (MACHO_ARM64_RELOC_GOT_LOAD_PAGE21) pc-rel distance to page of GOT slot
117    MachoArm64RelocGotLoadPage21,
118    // (MACHO_ARM64_RELOC_GOT_LOAD_PAGEOFF12) offset within page of GOT slot, scaled by r_length
119    MachoArm64RelocGotLoadPageoff12,
120    // (MACHO_ARM64_RELOC_POINTER_TO_GOT) for pointers to GOT slots
121    MachoArm64RelocPointerToGot,
122    // (MACHO_ARM64_RELOC_TLVP_LOAD_PAGE21) pc-rel distance to page of TLVP slot
123    MachoArm64RelocTlvpLoadPage21,
124    // (MACHO_ARM64_RELOC_TLVP_LOAD_PAGEOFF12) offset within page of TLVP slot, scaled by r_length
125    MachoArm64RelocTlvpLoadPageoff12,
126    // (MACHO_ARM64_RELOC_ADDEND) must be followed by PAGE21 or PAGEOFF12
127    MachoArm64RelocAddend,
128
129    // --- X86_64
130    // (MACHO_X86_64_RELOC_UNSIGNED) for absolute addresses
131    MachoX86_64RelocUnsigned,
132    // (MACHO_X86_64_RELOC_SIGNED) for signed 32-bit displacement
133    MachoX86_64RelocSigned,
134    // (MACHO_X86_64_RELOC_BRANCH) a CALL/JMP instruction with 32-bit displacement
135    MachoX86_64RelocBranch,
136    // (MACHO_X86_64_RELOC_GOT_LOAD) a MOVQ load of a GOT entry
137    MachoX86_64RelocGotLoad,
138    // (MACHO_X86_64_RELOC_GOT) other GOT references
139    MachoX86_64RelocGot,
140    // (MACHO_X86_64_RELOC_SUBTRACTOR) must be followed by a X86_64_RELOC_UNSIGNED
141    MachoX86_64RelocSubtractor,
142    // (MACHO_X86_64_RELOC_SIGNED_1) for signed 32-bit displacement with a -1 addend
143    MachoX86_64RelocSigned1,
144    // (MACHO_X86_64_RELOC_SIGNED_2) for signed 32-bit displacement with a -2 addend
145    MachoX86_64RelocSigned2,
146    // (MACHO_X86_64_RELOC_SIGNED_4) for signed 32-bit displacement with a -4 addend
147    MachoX86_64RelocSigned4,
148    // (MACHO_X86_64_RELOC_TLV) for thread local variables
149    MachoX86_64RelocTlv,
150}
151
152impl RelocationKind {
153    pub fn needs_got(&self) -> bool {
154        matches!(
155            self,
156            Self::MachoArm64RelocGotLoadPage21
157                | Self::MachoArm64RelocGotLoadPageoff12
158                | Self::MachoArm64RelocPointerToGot
159                | Self::MachoX86_64RelocGotLoad
160                | Self::MachoX86_64RelocGot
161        )
162    }
163}
164
165impl fmt::Display for RelocationKind {
166    /// Display trait implementation drops the arch, since its used in contexts where the arch is
167    /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
168    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169        match *self {
170            Self::Abs4 => write!(f, "Abs4"),
171            Self::Abs8 => write!(f, "Abs8"),
172            Self::X86PCRel4 => write!(f, "PCRel4"),
173            Self::X86PCRel8 => write!(f, "PCRel8"),
174            Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
175            Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
176            Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
177            Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
178            Self::Arm64Movw0 => write!(f, "Arm64MovwG0"),
179            Self::Arm64Movw1 => write!(f, "Arm64MovwG1"),
180            Self::Arm64Movw2 => write!(f, "Arm64MovwG2"),
181            Self::Arm64Movw3 => write!(f, "Arm64MovwG3"),
182            Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
183            Self::RiscvPCRelHi20 => write!(f, "RiscvPCRelHi20"),
184            Self::RiscvPCRelLo12I => write!(f, "RiscvPCRelLo12I"),
185            Self::LArchAbsHi20 => write!(f, "LArchAbsHi20"),
186            Self::LArchAbsLo12 => write!(f, "LArchAbsLo12"),
187            Self::LArchAbs64Hi12 => write!(f, "LArchAbs64Hi12"),
188            Self::LArchAbs64Lo20 => write!(f, "LArchAbs64Lo20"),
189            Self::LArchCall36 => write!(f, "LArchCall36"),
190            Self::LArchPCAlaHi20 => write!(f, "LArchPCAlaHi20"),
191            Self::LArchPCAlaLo12 => write!(f, "LArchPCAlaLo12"),
192            Self::LArchPCAla64Hi12 => write!(f, "LArchPCAla64Hi12"),
193            Self::LArchPCAla64Lo20 => write!(f, "LArchPCAla64Lo20"),
194            Self::Aarch64AdrPrelLo21 => write!(f, "Aarch64AdrPrelLo21"),
195            Self::Aarch64AdrPrelPgHi21 => write!(f, "Aarch64AdrPrelPgHi21"),
196            Self::Aarch64AddAbsLo12Nc => write!(f, "Aarch64AddAbsLo12Nc"),
197            Self::Aarch64Ldst128AbsLo12Nc => write!(f, "Aarch64Ldst128AbsLo12Nc"),
198            Self::Aarch64Ldst64AbsLo12Nc => write!(f, "Aarch64Ldst64AbsLo12Nc"),
199            Self::MachoArm64RelocUnsigned => write!(f, "MachoArm64RelocUnsigned"),
200            Self::MachoArm64RelocSubtractor => write!(f, "MachoArm64RelocSubtractor"),
201            Self::MachoArm64RelocBranch26 => write!(f, "MachoArm64RelocBranch26"),
202            Self::MachoArm64RelocPage21 => write!(f, "MachoArm64RelocPage21"),
203            Self::MachoArm64RelocPageoff12 => write!(f, "MachoArm64RelocPageoff12"),
204            Self::MachoArm64RelocGotLoadPage21 => write!(f, "MachoArm64RelocGotLoadPage21"),
205            Self::MachoArm64RelocGotLoadPageoff12 => write!(f, "MachoArm64RelocGotLoadPageoff12"),
206            Self::MachoArm64RelocPointerToGot => write!(f, "MachoArm64RelocPointerToGot"),
207            Self::MachoArm64RelocTlvpLoadPage21 => write!(f, "MachoArm64RelocTlvpLoadPage21"),
208            Self::MachoArm64RelocTlvpLoadPageoff12 => write!(f, "MachoArm64RelocTlvpLoadPageoff12"),
209            Self::MachoArm64RelocAddend => write!(f, "MachoArm64RelocAddend"),
210            Self::MachoX86_64RelocUnsigned => write!(f, "MachoX86_64RelocUnsigned"),
211            Self::MachoX86_64RelocSigned => write!(f, "MachoX86_64RelocSigned"),
212            Self::MachoX86_64RelocBranch => write!(f, "MachoX86_64RelocBranch"),
213            Self::MachoX86_64RelocGotLoad => write!(f, "MachoX86_64RelocGotLoad"),
214            Self::MachoX86_64RelocGot => write!(f, "MachoX86_64RelocGot"),
215            Self::MachoX86_64RelocSubtractor => write!(f, "MachoX86_64RelocSubtractor"),
216            Self::MachoX86_64RelocSigned1 => write!(f, "MachoX86_64RelocSigned1"),
217            Self::MachoX86_64RelocSigned2 => write!(f, "MachoX86_64RelocSigned2"),
218            Self::MachoX86_64RelocSigned4 => write!(f, "MachoX86_64RelocSigned4"),
219            Self::MachoX86_64RelocTlv => write!(f, "MachoX86_64RelocTlv"),
220        }
221    }
222}
223
224/// A record of a relocation to perform.
225#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
226#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
227#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
228#[rkyv(derive(Debug), compare(PartialEq))]
229pub struct Relocation {
230    /// The relocation kind.
231    pub kind: RelocationKind,
232    /// Relocation target.
233    pub reloc_target: RelocationTarget,
234    /// The offset where to apply the relocation.
235    pub offset: CodeOffset,
236    /// The addend to add to the relocation value.
237    pub addend: Addend,
238}
239
240/// Any struct that acts like a `Relocation`.
241#[allow(missing_docs)]
242pub trait RelocationLike {
243    fn kind(&self) -> RelocationKind;
244    fn reloc_target(&self) -> RelocationTarget;
245    fn offset(&self) -> CodeOffset;
246    fn addend(&self) -> Addend;
247
248    /// Given a function start address, provide the relocation relative
249    /// to that address.
250    ///
251    /// The function returns the relocation address and the delta.
252    ///
253    // # Nomenclature (from [1]@5.7.3.3)
254    //
255    // * S (when used on its own) is the address of the symbol.
256    // * A is the addend for the relocation.
257    // * P is the address of the place being relocated (derived from r_offset).
258    // * X is the result of a relocation operation, before any masking or bit-selection operation is applied
259    // * Page(expr) is the page address of the expression expr, defined as (expr & ~0xFFF). (This applies even if the machine page size supported by the platform has a different value.)
260    //
261    // [1]: https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
262    fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
263        match self.kind() {
264            RelocationKind::Abs8
265            | RelocationKind::Arm64Movw0
266            | RelocationKind::Arm64Movw1
267            | RelocationKind::Arm64Movw2
268            | RelocationKind::Arm64Movw3
269            | RelocationKind::RiscvPCRelLo12I
270            | RelocationKind::Aarch64Ldst128AbsLo12Nc
271            | RelocationKind::Aarch64Ldst64AbsLo12Nc
272            | RelocationKind::MachoArm64RelocUnsigned
273            | RelocationKind::MachoX86_64RelocUnsigned
274            | RelocationKind::MachoArm64RelocSubtractor
275            | RelocationKind::MachoX86_64RelocSubtractor
276            | RelocationKind::LArchAbsHi20
277            | RelocationKind::LArchAbsLo12
278            | RelocationKind::LArchAbs64Lo20
279            | RelocationKind::LArchAbs64Hi12
280            | RelocationKind::LArchPCAlaLo12 => {
281                let reloc_address = start + self.offset() as usize;
282                let reloc_addend = self.addend() as isize;
283                let reloc_abs = target_func_address
284                    .checked_add(reloc_addend as u64)
285                    .unwrap();
286                (reloc_address, reloc_abs)
287            }
288            RelocationKind::X86PCRel4 => {
289                let reloc_address = start + self.offset() as usize;
290                let reloc_addend = self.addend() as isize;
291                let reloc_delta_u32 = (target_func_address as u32)
292                    .wrapping_sub(reloc_address as u32)
293                    .checked_add(reloc_addend as u32)
294                    .unwrap();
295                (reloc_address, reloc_delta_u32 as u64)
296            }
297            RelocationKind::X86PCRel8 => {
298                let reloc_address = start + self.offset() as usize;
299                let reloc_addend = self.addend() as isize;
300                let reloc_delta = target_func_address
301                    .wrapping_sub(reloc_address as u64)
302                    .checked_add(reloc_addend as u64)
303                    .unwrap();
304                (reloc_address, reloc_delta)
305            }
306            RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
307                let reloc_address = start + self.offset() as usize;
308                let reloc_addend = self.addend() as isize;
309                let reloc_delta_u32 = (target_func_address as u32)
310                    .wrapping_sub(reloc_address as u32)
311                    .wrapping_add(reloc_addend as u32);
312                (reloc_address, reloc_delta_u32 as u64)
313            }
314            RelocationKind::Aarch64AdrPrelLo21 => {
315                let s = target_func_address;
316                let p = start + self.offset() as usize;
317                let a = self.addend() as u64;
318
319                (p, s.wrapping_add(a).wrapping_sub(p as u64))
320            }
321
322            RelocationKind::Aarch64AddAbsLo12Nc => {
323                let s = target_func_address;
324                let p = start + self.offset() as usize;
325                let a = self.addend() as u64;
326
327                (p, s.wrapping_add(a))
328            }
329            RelocationKind::Arm64Call
330            | RelocationKind::RiscvCall
331            | RelocationKind::RiscvPCRelHi20 => {
332                let reloc_address = start + self.offset() as usize;
333                let reloc_addend = self.addend() as isize;
334                let reloc_delta_u32 = target_func_address
335                    .wrapping_sub(reloc_address as u64)
336                    .wrapping_add(reloc_addend as u64);
337                (reloc_address, reloc_delta_u32)
338            }
339            RelocationKind::Aarch64AdrPrelPgHi21
340            | RelocationKind::MachoArm64RelocGotLoadPage21
341            | RelocationKind::MachoArm64RelocPage21 => {
342                let reloc_address = start + self.offset() as usize;
343                let reloc_addend = self.addend() as isize;
344                let target_page =
345                    (target_func_address.wrapping_add(reloc_addend as u64) & !(0xFFF)) as usize;
346                let pc_page = reloc_address & !(0xFFF);
347                (reloc_address, target_page.wrapping_sub(pc_page) as u64)
348            }
349            RelocationKind::MachoArm64RelocGotLoadPageoff12
350            | RelocationKind::MachoArm64RelocPageoff12 => {
351                let reloc_address = start + self.offset() as usize;
352                let reloc_addend = self.addend() as isize;
353                let target_offset =
354                    (target_func_address.wrapping_add(reloc_addend as u64) & (0xFFF)) as usize;
355                (reloc_address, target_offset as u64)
356            }
357            RelocationKind::LArchCall36 => {
358                let reloc_address = start + self.offset() as usize;
359                let reloc_addend = self.addend() as isize;
360                let reloc_delta = target_func_address
361                    .wrapping_sub(reloc_address as u64)
362                    .wrapping_add(reloc_addend as u64);
363                (
364                    reloc_address,
365                    reloc_delta.wrapping_add((reloc_delta & 0x20000) << 1),
366                )
367            }
368            RelocationKind::LArchPCAlaHi20 => {
369                let reloc_address = start + self.offset() as usize;
370                let reloc_addend = self.addend() as isize;
371                let target_page = (target_func_address
372                    .wrapping_add(reloc_addend as u64)
373                    .wrapping_add(0x800)
374                    & !(0xFFF)) as usize;
375                let pc_page = reloc_address & !(0xFFF);
376                (reloc_address, target_page.wrapping_sub(pc_page) as u64)
377            }
378            RelocationKind::LArchPCAla64Hi12 | RelocationKind::LArchPCAla64Lo20 => {
379                let reloc_address = start + self.offset() as usize;
380                let reloc_addend = self.addend() as isize;
381                let reloc_offset = match self.kind() {
382                    RelocationKind::LArchPCAla64Lo20 => 8,
383                    RelocationKind::LArchPCAla64Hi12 => 12,
384                    _ => 0,
385                };
386                let target_func_address = target_func_address.wrapping_add(reloc_addend as u64);
387                let target_page = (target_func_address & !(0xFFF)) as usize;
388                let pc_page = (reloc_address - reloc_offset) & !(0xFFF);
389                let mut reloc_delta = target_page.wrapping_sub(pc_page) as u64;
390                reloc_delta = reloc_delta
391                    .wrapping_add((target_func_address & 0x800) << 1)
392                    .wrapping_sub((target_func_address & 0x800) << 21);
393                reloc_delta = reloc_delta.wrapping_add((reloc_delta & 0x80000000) << 1);
394                (reloc_address, reloc_delta)
395            }
396            RelocationKind::MachoArm64RelocPointerToGot => {
397                let reloc_address = start + self.offset() as usize;
398                let reloc_delta =
399                    (target_func_address as isize).wrapping_sub(reloc_address as isize);
400                (reloc_address, reloc_delta as u64)
401            }
402            _ => panic!("Relocation kind unsupported"),
403        }
404    }
405}
406
407impl RelocationLike for Relocation {
408    fn kind(&self) -> RelocationKind {
409        self.kind
410    }
411
412    fn reloc_target(&self) -> RelocationTarget {
413        self.reloc_target
414    }
415
416    fn offset(&self) -> CodeOffset {
417        self.offset
418    }
419
420    fn addend(&self) -> Addend {
421        self.addend
422    }
423}
424
425impl RelocationLike for ArchivedRelocation {
426    fn kind(&self) -> RelocationKind {
427        rkyv::deserialize::<_, String>(&self.kind).unwrap()
428    }
429
430    fn reloc_target(&self) -> RelocationTarget {
431        rkyv::deserialize::<_, String>(&self.reloc_target).unwrap()
432    }
433
434    fn offset(&self) -> CodeOffset {
435        self.offset.into()
436    }
437
438    fn addend(&self) -> Addend {
439        self.addend.into()
440    }
441}
442
443/// Destination function. Can be either user function or some special one, like `memory.grow`.
444#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
445#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
446#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq, Hash)]
447#[rkyv(derive(Debug, Hash, PartialEq, Eq), compare(PartialEq))]
448#[repr(u8)]
449pub enum RelocationTarget {
450    /// A relocation to a function defined locally in the wasm (not an imported one).
451    LocalFunc(LocalFunctionIndex),
452    /// A compiler-generated libcall.
453    LibCall(LibCall),
454    /// Custom sections generated by the compiler
455    CustomSection(SectionIndex),
456}
457
458/// Relocations to apply to function bodies.
459pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;