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
104impl fmt::Display for RelocationKind {
105    /// Display trait implementation drops the arch, since its used in contexts where the arch is
106    /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
107    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108        match *self {
109            Self::Abs4 => write!(f, "Abs4"),
110            Self::Abs8 => write!(f, "Abs8"),
111            Self::X86PCRel4 => write!(f, "PCRel4"),
112            Self::X86PCRel8 => write!(f, "PCRel8"),
113            Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
114            Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
115            Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
116            Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
117            Self::Arm64Movw0 => write!(f, "Arm64MovwG0"),
118            Self::Arm64Movw1 => write!(f, "Arm64MovwG1"),
119            Self::Arm64Movw2 => write!(f, "Arm64MovwG2"),
120            Self::Arm64Movw3 => write!(f, "Arm64MovwG3"),
121            Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
122            Self::RiscvPCRelHi20 => write!(f, "RiscvPCRelHi20"),
123            Self::RiscvPCRelLo12I => write!(f, "RiscvPCRelLo12I"),
124            Self::LArchAbsHi20 => write!(f, "LArchAbsHi20"),
125            Self::LArchAbsLo12 => write!(f, "LArchAbsLo12"),
126            Self::LArchAbs64Hi12 => write!(f, "LArchAbs64Hi12"),
127            Self::LArchAbs64Lo20 => write!(f, "LArchAbs64Lo20"),
128            Self::LArchCall36 => write!(f, "LArchCall36"),
129            Self::LArchPCAlaHi20 => write!(f, "LArchPCAlaHi20"),
130            Self::LArchPCAlaLo12 => write!(f, "LArchPCAlaLo12"),
131            Self::LArchPCAla64Hi12 => write!(f, "LArchPCAla64Hi12"),
132            Self::LArchPCAla64Lo20 => write!(f, "LArchPCAla64Lo20"),
133            Self::Aarch64AdrPrelLo21 => write!(f, "Aarch64AdrPrelLo21"),
134            Self::Aarch64AdrPrelPgHi21 => write!(f, "Aarch64AdrPrelPgHi21"),
135            Self::Aarch64AddAbsLo12Nc => write!(f, "Aarch64AddAbsLo12Nc"),
136            Self::Aarch64Ldst128AbsLo12Nc => write!(f, "Aarch64Ldst128AbsLo12Nc"),
137            Self::Aarch64Ldst64AbsLo12Nc => write!(f, "Aarch64Ldst64AbsLo12Nc"),
138            // Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
139        }
140    }
141}
142
143/// A record of a relocation to perform.
144#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
145#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
146#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
147#[rkyv(derive(Debug), compare(PartialEq))]
148pub struct Relocation {
149    /// The relocation kind.
150    pub kind: RelocationKind,
151    /// Relocation target.
152    pub reloc_target: RelocationTarget,
153    /// The offset where to apply the relocation.
154    pub offset: CodeOffset,
155    /// The addend to add to the relocation value.
156    pub addend: Addend,
157}
158
159/// Any struct that acts like a `Relocation`.
160#[allow(missing_docs)]
161pub trait RelocationLike {
162    fn kind(&self) -> RelocationKind;
163    fn reloc_target(&self) -> RelocationTarget;
164    fn offset(&self) -> CodeOffset;
165    fn addend(&self) -> Addend;
166
167    /// Given a function start address, provide the relocation relative
168    /// to that address.
169    ///
170    /// The function returns the relocation address and the delta.
171    ///
172    // # Nomenclature (from [1]@5.7.3.3)
173    //
174    // * S (when used on its own) is the address of the symbol.
175    // * A is the addend for the relocation.
176    // * P is the address of the place being relocated (derived from r_offset).
177    // * X is the result of a relocation operation, before any masking or bit-selection operation is applied
178    // * 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.)
179    //
180    // [1]: https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst
181    fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
182        match self.kind() {
183            RelocationKind::Abs8
184            | RelocationKind::Arm64Movw0
185            | RelocationKind::Arm64Movw1
186            | RelocationKind::Arm64Movw2
187            | RelocationKind::Arm64Movw3
188            | RelocationKind::RiscvPCRelLo12I
189            | RelocationKind::Aarch64Ldst128AbsLo12Nc
190            | RelocationKind::Aarch64Ldst64AbsLo12Nc
191            | RelocationKind::LArchAbsHi20
192            | RelocationKind::LArchAbsLo12
193            | RelocationKind::LArchAbs64Lo20
194            | RelocationKind::LArchAbs64Hi12
195            | RelocationKind::LArchPCAlaLo12 => {
196                let reloc_address = start + self.offset() as usize;
197                let reloc_addend = self.addend() as isize;
198                let reloc_abs = target_func_address
199                    .checked_add(reloc_addend as u64)
200                    .unwrap();
201                (reloc_address, reloc_abs)
202            }
203            RelocationKind::X86PCRel4 => {
204                let reloc_address = start + self.offset() as usize;
205                let reloc_addend = self.addend() as isize;
206                let reloc_delta_u32 = (target_func_address as u32)
207                    .wrapping_sub(reloc_address as u32)
208                    .checked_add(reloc_addend as u32)
209                    .unwrap();
210                (reloc_address, reloc_delta_u32 as u64)
211            }
212            RelocationKind::X86PCRel8 => {
213                let reloc_address = start + self.offset() as usize;
214                let reloc_addend = self.addend() as isize;
215                let reloc_delta = target_func_address
216                    .wrapping_sub(reloc_address as u64)
217                    .checked_add(reloc_addend as u64)
218                    .unwrap();
219                (reloc_address, reloc_delta)
220            }
221            RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
222                let reloc_address = start + self.offset() as usize;
223                let reloc_addend = self.addend() as isize;
224                let reloc_delta_u32 = (target_func_address as u32)
225                    .wrapping_sub(reloc_address as u32)
226                    .wrapping_add(reloc_addend as u32);
227                (reloc_address, reloc_delta_u32 as u64)
228            }
229            RelocationKind::Aarch64AdrPrelLo21 => {
230                let s = target_func_address;
231                let p = start + self.offset() as usize;
232                let a = self.addend() as u64;
233
234                (p, s.wrapping_add(a).wrapping_sub(p as u64))
235            }
236
237            RelocationKind::Aarch64AddAbsLo12Nc => {
238                let s = target_func_address;
239                let p = start + self.offset() as usize;
240                let a = self.addend() as u64;
241
242                (p, s.wrapping_add(a))
243            }
244            RelocationKind::Arm64Call
245            | RelocationKind::RiscvCall
246            | RelocationKind::RiscvPCRelHi20 => {
247                let reloc_address = start + self.offset() as usize;
248                let reloc_addend = self.addend() as isize;
249                let reloc_delta_u32 = target_func_address
250                    .wrapping_sub(reloc_address as u64)
251                    .wrapping_add(reloc_addend as u64);
252                (reloc_address, reloc_delta_u32)
253            }
254            RelocationKind::Aarch64AdrPrelPgHi21 => {
255                let reloc_address = start + self.offset() as usize;
256                let reloc_addend = self.addend() as isize;
257                let target_page =
258                    (target_func_address.wrapping_add(reloc_addend as u64) & !(0xFFF)) as usize;
259                let pc_page = reloc_address & !(0xFFF);
260                (reloc_address, target_page.wrapping_sub(pc_page) as u64)
261            }
262            RelocationKind::LArchCall36 => {
263                let reloc_address = start + self.offset() as usize;
264                let reloc_addend = self.addend() as isize;
265                let reloc_delta = target_func_address
266                    .wrapping_sub(reloc_address as u64)
267                    .wrapping_add(reloc_addend as u64);
268                (
269                    reloc_address,
270                    reloc_delta.wrapping_add((reloc_delta & 0x20000) << 1),
271                )
272            }
273            RelocationKind::LArchPCAlaHi20 => {
274                let reloc_address = start + self.offset() as usize;
275                let reloc_addend = self.addend() as isize;
276                let target_page = (target_func_address
277                    .wrapping_add(reloc_addend as u64)
278                    .wrapping_add(0x800)
279                    & !(0xFFF)) as usize;
280                let pc_page = reloc_address & !(0xFFF);
281                (reloc_address, target_page.wrapping_sub(pc_page) as u64)
282            }
283            RelocationKind::LArchPCAla64Hi12 | RelocationKind::LArchPCAla64Lo20 => {
284                let reloc_address = start + self.offset() as usize;
285                let reloc_addend = self.addend() as isize;
286                let reloc_offset = match self.kind() {
287                    RelocationKind::LArchPCAla64Lo20 => 8,
288                    RelocationKind::LArchPCAla64Hi12 => 12,
289                    _ => 0,
290                };
291                let target_func_address = target_func_address.wrapping_add(reloc_addend as u64);
292                let target_page = (target_func_address & !(0xFFF)) as usize;
293                let pc_page = (reloc_address - reloc_offset) & !(0xFFF);
294                let mut reloc_delta = target_page.wrapping_sub(pc_page) as u64;
295                reloc_delta = reloc_delta
296                    .wrapping_add((target_func_address & 0x800) << 1)
297                    .wrapping_sub((target_func_address & 0x800) << 21);
298                reloc_delta = reloc_delta.wrapping_add((reloc_delta & 0x80000000) << 1);
299                (reloc_address, reloc_delta)
300            }
301            _ => panic!("Relocation kind unsupported"),
302        }
303    }
304}
305
306impl RelocationLike for Relocation {
307    fn kind(&self) -> RelocationKind {
308        self.kind
309    }
310
311    fn reloc_target(&self) -> RelocationTarget {
312        self.reloc_target
313    }
314
315    fn offset(&self) -> CodeOffset {
316        self.offset
317    }
318
319    fn addend(&self) -> Addend {
320        self.addend
321    }
322}
323
324impl RelocationLike for ArchivedRelocation {
325    fn kind(&self) -> RelocationKind {
326        rkyv::deserialize::<_, String>(&self.kind).unwrap()
327    }
328
329    fn reloc_target(&self) -> RelocationTarget {
330        rkyv::deserialize::<_, String>(&self.reloc_target).unwrap()
331    }
332
333    fn offset(&self) -> CodeOffset {
334        self.offset.into()
335    }
336
337    fn addend(&self) -> Addend {
338        self.addend.into()
339    }
340}
341
342/// Destination function. Can be either user function or some special one, like `memory.grow`.
343#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
344#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
345#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq)]
346#[rkyv(derive(Debug), compare(PartialEq))]
347#[repr(u8)]
348pub enum RelocationTarget {
349    /// A relocation to a function defined locally in the wasm (not an imported one).
350    LocalFunc(LocalFunctionIndex),
351    /// A compiler-generated libcall.
352    LibCall(LibCall),
353    /// Custom sections generated by the compiler
354    CustomSection(SectionIndex),
355}
356
357/// Relocations to apply to function bodies.
358pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;