hc_wasmer_types/compilation/
relocation.rs

1//! Relocation is the process of assigning load addresses for position-dependent
2//! code and data of a program and adjusting the code and data to reflect the
3//! assigned addresses.
4//!
5//! [Learn more](https://en.wikipedia.org/wiki/Relocation_(computing)).
6//!
7//! Each time a `Compiler` compiles a WebAssembly function (into machine code),
8//! it also attaches if there are any relocations that need to be patched into
9//! the generated machine code, so a given frontend (JIT or native) can
10//! do the corresponding work to run it.
11
12use super::section::SectionIndex;
13use crate::entity::PrimaryMap;
14use crate::lib::std::fmt;
15use crate::lib::std::vec::Vec;
16use crate::{Addend, CodeOffset};
17use crate::{LibCall, LocalFunctionIndex};
18use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
19#[cfg(feature = "enable-serde")]
20use serde::{Deserialize, Serialize};
21
22/// Relocation kinds for every ISA.
23#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
24#[derive(
25    RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Copy, Clone, Debug, PartialEq, Eq,
26)]
27#[archive(as = "Self")]
28#[repr(u8)]
29pub enum RelocationKind {
30    /// absolute 4-byte
31    Abs4,
32    /// absolute 8-byte
33    Abs8,
34    /// x86 PC-relative 4-byte
35    X86PCRel4,
36    /// x86 PC-relative 8-byte
37    X86PCRel8,
38    /// x86 call to PC-relative 4-byte
39    X86CallPCRel4,
40    /// x86 call to PLT-relative 4-byte
41    X86CallPLTRel4,
42    /// x86 GOT PC-relative 4-byte
43    X86GOTPCRel4,
44    /// Arm32 call target
45    Arm32Call,
46    /// Arm64 call target
47    Arm64Call,
48    /// Arm64 movk/z part 0
49    Arm64Movw0,
50    /// Arm64 movk/z part 1
51    Arm64Movw1,
52    /// Arm64 movk/z part 2
53    Arm64Movw2,
54    /// Arm64 movk/z part 3
55    Arm64Movw3,
56    /// RISC-V PC-relative high 20bit
57    RiscvPCRelHi20,
58    /// RISC-V PC-relative low 12bit, I-type
59    RiscvPCRelLo12I,
60    /// RISC-V call target
61    RiscvCall,
62    /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
63    ElfX86_64TlsGd,
64    // /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
65    // MachOX86_64Tlv,
66}
67
68impl fmt::Display for RelocationKind {
69    /// Display trait implementation drops the arch, since its used in contexts where the arch is
70    /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        match *self {
73            Self::Abs4 => write!(f, "Abs4"),
74            Self::Abs8 => write!(f, "Abs8"),
75            Self::X86PCRel4 => write!(f, "PCRel4"),
76            Self::X86PCRel8 => write!(f, "PCRel8"),
77            Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
78            Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
79            Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
80            Self::Arm32Call | Self::Arm64Call | Self::RiscvCall => write!(f, "Call"),
81            Self::Arm64Movw0 => write!(f, "Arm64MovwG0"),
82            Self::Arm64Movw1 => write!(f, "Arm64MovwG1"),
83            Self::Arm64Movw2 => write!(f, "Arm64MovwG2"),
84            Self::Arm64Movw3 => write!(f, "Arm64MovwG3"),
85            Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
86            Self::RiscvPCRelHi20 => write!(f, "RiscvPCRelHi20"),
87            Self::RiscvPCRelLo12I => write!(f, "RiscvPCRelLo12I"),
88            // Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
89        }
90    }
91}
92
93/// A record of a relocation to perform.
94#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
95#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
96#[archive_attr(derive(rkyv::CheckBytes, Debug))]
97pub struct Relocation {
98    /// The relocation kind.
99    pub kind: RelocationKind,
100    /// Relocation target.
101    pub reloc_target: RelocationTarget,
102    /// The offset where to apply the relocation.
103    pub offset: CodeOffset,
104    /// The addend to add to the relocation value.
105    pub addend: Addend,
106}
107
108/// Any struct that acts like a `Relocation`.
109#[allow(missing_docs)]
110pub trait RelocationLike {
111    fn kind(&self) -> RelocationKind;
112    fn reloc_target(&self) -> RelocationTarget;
113    fn offset(&self) -> CodeOffset;
114    fn addend(&self) -> Addend;
115
116    /// Given a function start address, provide the relocation relative
117    /// to that address.
118    ///
119    /// The function returns the relocation address and the delta.
120    fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
121        match self.kind() {
122            RelocationKind::Abs8
123            | RelocationKind::Arm64Movw0
124            | RelocationKind::Arm64Movw1
125            | RelocationKind::Arm64Movw2
126            | RelocationKind::Arm64Movw3
127            | RelocationKind::RiscvPCRelLo12I => {
128                let reloc_address = start + self.offset() as usize;
129                let reloc_addend = self.addend() as isize;
130                let reloc_abs = target_func_address
131                    .checked_add(reloc_addend as u64)
132                    .unwrap();
133                (reloc_address, reloc_abs)
134            }
135            RelocationKind::X86PCRel4 => {
136                let reloc_address = start + self.offset() as usize;
137                let reloc_addend = self.addend() as isize;
138                let reloc_delta_u32 = (target_func_address as u32)
139                    .wrapping_sub(reloc_address as u32)
140                    .checked_add(reloc_addend as u32)
141                    .unwrap();
142                (reloc_address, reloc_delta_u32 as u64)
143            }
144            RelocationKind::X86PCRel8 => {
145                let reloc_address = start + self.offset() as usize;
146                let reloc_addend = self.addend() as isize;
147                let reloc_delta = target_func_address
148                    .wrapping_sub(reloc_address as u64)
149                    .checked_add(reloc_addend as u64)
150                    .unwrap();
151                (reloc_address, reloc_delta)
152            }
153            RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
154                let reloc_address = start + self.offset() as usize;
155                let reloc_addend = self.addend() as isize;
156                let reloc_delta_u32 = (target_func_address as u32)
157                    .wrapping_sub(reloc_address as u32)
158                    .wrapping_add(reloc_addend as u32);
159                (reloc_address, reloc_delta_u32 as u64)
160            }
161            RelocationKind::Arm64Call
162            | RelocationKind::RiscvCall
163            | RelocationKind::RiscvPCRelHi20 => {
164                let reloc_address = start + self.offset() as usize;
165                let reloc_addend = self.addend() as isize;
166                let reloc_delta_u32 = target_func_address
167                    .wrapping_sub(reloc_address as u64)
168                    .wrapping_add(reloc_addend as u64);
169                (reloc_address, reloc_delta_u32)
170            }
171            _ => panic!("Relocation kind unsupported"),
172        }
173    }
174}
175
176impl RelocationLike for Relocation {
177    fn kind(&self) -> RelocationKind {
178        self.kind
179    }
180
181    fn reloc_target(&self) -> RelocationTarget {
182        self.reloc_target
183    }
184
185    fn offset(&self) -> CodeOffset {
186        self.offset
187    }
188
189    fn addend(&self) -> Addend {
190        self.addend
191    }
192}
193
194impl RelocationLike for ArchivedRelocation {
195    fn kind(&self) -> RelocationKind {
196        self.kind
197    }
198
199    fn reloc_target(&self) -> RelocationTarget {
200        self.reloc_target
201    }
202
203    fn offset(&self) -> CodeOffset {
204        self.offset
205    }
206
207    fn addend(&self) -> Addend {
208        self.addend
209    }
210}
211
212/// Destination function. Can be either user function or some special one, like `memory.grow`.
213#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
214#[derive(
215    RkyvSerialize, RkyvDeserialize, Archive, rkyv::CheckBytes, Debug, Copy, Clone, PartialEq, Eq,
216)]
217#[archive(as = "Self")]
218#[repr(u8)]
219pub enum RelocationTarget {
220    /// A relocation to a function defined locally in the wasm (not an imported one).
221    LocalFunc(LocalFunctionIndex),
222    /// A compiler-generated libcall.
223    LibCall(LibCall),
224    /// Custom sections generated by the compiler
225    CustomSection(SectionIndex),
226}
227
228/// Relocations to apply to function bodies.
229pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;