unc_vm_compiler/
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 crate::lib::std::fmt;
13use crate::lib::std::vec::Vec;
14use crate::section::SectionIndex;
15use crate::{Addend, CodeOffset, JumpTable};
16use unc_vm_types::entity::PrimaryMap;
17use unc_vm_types::LocalFunctionIndex;
18use unc_vm_vm::libcalls::LibCall;
19
20/// Relocation kinds for every ISA.
21#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Copy, Clone, Debug, PartialEq, Eq)]
22pub enum RelocationKind {
23    /// absolute 4-byte
24    Abs4,
25    /// absolute 8-byte
26    Abs8,
27    /// x86 PC-relative 4-byte
28    X86PCRel4,
29    /// x86 PC-relative 8-byte
30    X86PCRel8,
31    /// x86 PC-relative 4-byte offset to trailing rodata
32    X86PCRelRodata4,
33    /// x86 call to PC-relative 4-byte
34    X86CallPCRel4,
35    /// x86 call to PLT-relative 4-byte
36    X86CallPLTRel4,
37    /// x86 GOT PC-relative 4-byte
38    X86GOTPCRel4,
39    /// Arm32 call target
40    Arm32Call,
41    /// Arm64 call target
42    Arm64Call,
43    /// Arm64 movk/z part 0
44    Arm64Movw0,
45    /// Arm64 movk/z part 1
46    Arm64Movw1,
47    /// Arm64 movk/z part 2
48    Arm64Movw2,
49    /// Arm64 movk/z part 3
50    Arm64Movw3,
51    // /// RISC-V call target
52    // RiscvCall,
53    /// Elf x86_64 32 bit signed PC relative offset to two GOT entries for GD symbol.
54    ElfX86_64TlsGd,
55    // /// Mach-O x86_64 32 bit signed PC relative offset to a `__thread_vars` entry.
56    // MachOX86_64Tlv,
57}
58
59impl fmt::Display for RelocationKind {
60    /// Display trait implementation drops the arch, since its used in contexts where the arch is
61    /// already unambiguous, e.g. clif syntax with isa specified. In other contexts, use Debug.
62    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
63        match *self {
64            Self::Abs4 => write!(f, "Abs4"),
65            Self::Abs8 => write!(f, "Abs8"),
66            Self::X86PCRel4 => write!(f, "PCRel4"),
67            Self::X86PCRel8 => write!(f, "PCRel8"),
68            Self::X86PCRelRodata4 => write!(f, "PCRelRodata4"),
69            Self::X86CallPCRel4 => write!(f, "CallPCRel4"),
70            Self::X86CallPLTRel4 => write!(f, "CallPLTRel4"),
71            Self::X86GOTPCRel4 => write!(f, "GOTPCRel4"),
72            Self::Arm32Call | Self::Arm64Call => write!(f, "Call"),
73            Self::Arm64Movw0 => write!(f, "Arm64MovwG0"),
74            Self::Arm64Movw1 => write!(f, "Arm64MovwG1"),
75            Self::Arm64Movw2 => write!(f, "Arm64MovwG2"),
76            Self::Arm64Movw3 => write!(f, "Arm64MovwG3"),
77            Self::ElfX86_64TlsGd => write!(f, "ElfX86_64TlsGd"),
78            // Self::MachOX86_64Tlv => write!(f, "MachOX86_64Tlv"),
79        }
80    }
81}
82
83/// A record of a relocation to perform.
84#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, PartialEq, Eq)]
85pub struct Relocation {
86    /// The relocation kind.
87    pub kind: RelocationKind,
88    /// Relocation target.
89    pub reloc_target: RelocationTarget,
90    /// The offset where to apply the relocation.
91    pub offset: CodeOffset,
92    /// The addend to add to the relocation value.
93    pub addend: Addend,
94}
95
96/// Destination function. Can be either user function or some special one, like `memory.grow`.
97#[derive(rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Copy, Clone, PartialEq, Eq)]
98pub enum RelocationTarget {
99    /// A relocation to a function defined locally in the wasm (not an imported one).
100    LocalFunc(LocalFunctionIndex),
101    /// A compiler-generated libcall.
102    LibCall(LibCall),
103    /// Jump table index.
104    JumpTable(LocalFunctionIndex, JumpTable),
105    /// Custom sections generated by the compiler
106    CustomSection(SectionIndex),
107}
108
109impl Relocation {
110    /// Given a function start address, provide the relocation relative
111    /// to that address.
112    ///
113    /// The function returns the relocation address and the delta.
114    pub fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
115        match self.kind {
116            RelocationKind::Abs8
117            | RelocationKind::Arm64Movw0
118            | RelocationKind::Arm64Movw1
119            | RelocationKind::Arm64Movw2
120            | RelocationKind::Arm64Movw3 => {
121                let reloc_address = start + self.offset as usize;
122                let reloc_addend = self.addend as isize;
123                let reloc_abs = target_func_address.checked_add(reloc_addend as u64).unwrap();
124                (reloc_address, reloc_abs)
125            }
126            RelocationKind::X86PCRel4 => {
127                let reloc_address = start + self.offset as usize;
128                let reloc_addend = self.addend as isize;
129                let reloc_delta_u32 = (target_func_address as u32)
130                    .wrapping_sub(reloc_address as u32)
131                    .checked_add(reloc_addend as u32)
132                    .unwrap();
133                (reloc_address, reloc_delta_u32 as u64)
134            }
135            RelocationKind::X86PCRel8 => {
136                let reloc_address = start + self.offset as usize;
137                let reloc_addend = self.addend as isize;
138                let reloc_delta = target_func_address
139                    .wrapping_sub(reloc_address as u64)
140                    .checked_add(reloc_addend as u64)
141                    .unwrap();
142                (reloc_address, reloc_delta)
143            }
144            RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
145                let reloc_address = start + self.offset as usize;
146                let reloc_addend = self.addend as isize;
147                let reloc_delta_u32 = (target_func_address as u32)
148                    .wrapping_sub(reloc_address as u32)
149                    .wrapping_add(reloc_addend as u32);
150                (reloc_address, reloc_delta_u32 as u64)
151            }
152            RelocationKind::Arm64Call => {
153                let reloc_address = start + self.offset as usize;
154                let reloc_addend = self.addend as isize;
155                let reloc_delta_u32 = target_func_address
156                    .wrapping_sub(reloc_address as u64)
157                    .wrapping_add(reloc_addend as u64);
158                (reloc_address, reloc_delta_u32)
159            }
160            // RelocationKind::X86PCRelRodata4 => {
161            //     (start, target_func_address)
162            // }
163            _ => panic!("Relocation kind unsupported"),
164        }
165    }
166}
167
168/// Relocations to apply to function bodies.
169pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;