1use crate::{
4 get_libcall_trampoline,
5 types::{
6 relocation::{RelocationKind, RelocationLike, RelocationTarget},
7 section::SectionIndex,
8 },
9 FunctionExtent,
10};
11use std::{
12 collections::{HashMap, HashSet},
13 ptr::{read_unaligned, write_unaligned},
14};
15
16use wasmer_types::{entity::PrimaryMap, LocalFunctionIndex, ModuleInfo};
17use wasmer_vm::{libcalls::function_pointer, SectionBodyPtr};
18
19#[allow(clippy::too_many_arguments)]
20fn apply_relocation(
21 body: usize,
22 r: &impl RelocationLike,
23 allocated_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
24 allocated_sections: &PrimaryMap<SectionIndex, SectionBodyPtr>,
25 libcall_trampolines_sec_idx: SectionIndex,
26 libcall_trampoline_len: usize,
27 riscv_pcrel_hi20s: &mut HashMap<usize, u32>,
28 get_got_address: &dyn Fn(RelocationTarget) -> Option<usize>,
29) {
30 let reloc_target = r.reloc_target();
31
32 let target_func_address: usize = if r.kind().needs_got() && r.addend() == 0 {
37 if let Some(got_address) = get_got_address(reloc_target) {
38 got_address
39 } else {
40 panic!("No GOT entry for reloc target {reloc_target:?}")
41 }
42 } else {
43 match reloc_target {
44 RelocationTarget::LocalFunc(index) => *allocated_functions[index].ptr as usize,
45 RelocationTarget::LibCall(libcall) => {
46 if matches!(
49 r.kind(),
50 RelocationKind::Abs8
51 | RelocationKind::X86PCRel8
52 | RelocationKind::MachoArm64RelocUnsigned
53 | RelocationKind::MachoX86_64RelocUnsigned
54 ) {
55 function_pointer(libcall)
56 } else {
57 get_libcall_trampoline(
58 libcall,
59 allocated_sections[libcall_trampolines_sec_idx].0 as usize,
60 libcall_trampoline_len,
61 )
62 }
63 }
64 RelocationTarget::CustomSection(custom_section) => {
65 *allocated_sections[custom_section] as usize
66 }
67 }
68 };
69
70 let mut macho_aarch64_subtractor_addresses = HashSet::new();
72
73 match r.kind() {
74 RelocationKind::Abs8 => unsafe {
75 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
76 write_unaligned(reloc_address as *mut u64, reloc_delta);
77 },
78 RelocationKind::X86PCRel4 => unsafe {
79 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
80 write_unaligned(reloc_address as *mut u32, reloc_delta as _);
81 },
82 RelocationKind::X86PCRel8 => unsafe {
83 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
84 write_unaligned(reloc_address as *mut u64, reloc_delta);
85 },
86 RelocationKind::X86CallPCRel4 => unsafe {
87 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
88 write_unaligned(reloc_address as *mut u32, reloc_delta as _);
89 },
90 RelocationKind::Arm64Call => unsafe {
91 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
92 if (reloc_delta as i64).abs() >= 0x1000_0000 {
93 panic!(
94 "Relocation to big for {:?} for {:?} with {:x}, current val {:x}",
95 r.kind(),
96 r.reloc_target(),
97 reloc_delta,
98 read_unaligned(reloc_address as *mut u32)
99 )
100 }
101 let reloc_delta = (((reloc_delta / 4) as u32) & 0x3ff_ffff)
102 | (read_unaligned(reloc_address as *mut u32) & 0xfc00_0000);
103 write_unaligned(reloc_address as *mut u32, reloc_delta);
104 },
105 RelocationKind::Arm64Movw0 => unsafe {
106 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
107 let reloc_delta =
108 (((reloc_delta & 0xffff) as u32) << 5) | read_unaligned(reloc_address as *mut u32);
109 write_unaligned(reloc_address as *mut u32, reloc_delta);
110 },
111 RelocationKind::Arm64Movw1 => unsafe {
112 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
113 let reloc_delta = ((((reloc_delta >> 16) & 0xffff) as u32) << 5)
114 | read_unaligned(reloc_address as *mut u32);
115 write_unaligned(reloc_address as *mut u32, reloc_delta);
116 },
117 RelocationKind::Arm64Movw2 => unsafe {
118 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
119 let reloc_delta = ((((reloc_delta >> 32) & 0xffff) as u32) << 5)
120 | read_unaligned(reloc_address as *mut u32);
121 write_unaligned(reloc_address as *mut u32, reloc_delta);
122 },
123 RelocationKind::Arm64Movw3 => unsafe {
124 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
125 let reloc_delta = ((((reloc_delta >> 48) & 0xffff) as u32) << 5)
126 | read_unaligned(reloc_address as *mut u32);
127 write_unaligned(reloc_address as *mut u32, reloc_delta);
128 },
129 RelocationKind::RiscvPCRelHi20 => unsafe {
130 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
131
132 riscv_pcrel_hi20s.insert(reloc_address, reloc_delta as u32);
134
135 let reloc_delta = ((reloc_delta.wrapping_add(0x800) & 0xfffff000) as u32)
136 | read_unaligned(reloc_address as *mut u32);
137 write_unaligned(reloc_address as *mut u32, reloc_delta);
138 },
139 RelocationKind::RiscvPCRelLo12I => unsafe {
140 let (reloc_address, reloc_abs) = r.for_address(body, target_func_address as u64);
141 let reloc_delta = ((riscv_pcrel_hi20s.get(&(reloc_abs as usize)).expect(
142 "R_RISCV_PCREL_LO12_I relocation target must be a symbol with R_RISCV_PCREL_HI20",
143 ) & 0xfff)
144 << 20)
145 | read_unaligned(reloc_address as *mut u32);
146 write_unaligned(reloc_address as *mut u32, reloc_delta);
147 },
148 RelocationKind::RiscvCall => unsafe {
149 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
150 let reloc_delta = ((reloc_delta & 0xfff) << 52)
151 | (reloc_delta.wrapping_add(0x800) & 0xfffff000)
152 | read_unaligned(reloc_address as *mut u64);
153 write_unaligned(reloc_address as *mut u64, reloc_delta);
154 },
155 RelocationKind::LArchAbsHi20 | RelocationKind::LArchPCAlaHi20 => unsafe {
156 let (reloc_address, reloc_abs) = r.for_address(body, target_func_address as u64);
157 let reloc_abs = ((((reloc_abs >> 12) & 0xfffff) as u32) << 5)
158 | read_unaligned(reloc_address as *mut u32);
159 write_unaligned(reloc_address as *mut u32, reloc_abs);
160 },
161 RelocationKind::LArchAbsLo12 | RelocationKind::LArchPCAlaLo12 => unsafe {
162 let (reloc_address, reloc_abs) = r.for_address(body, target_func_address as u64);
163 let reloc_abs =
164 (((reloc_abs & 0xfff) as u32) << 10) | read_unaligned(reloc_address as *mut u32);
165 write_unaligned(reloc_address as *mut u32, reloc_abs);
166 },
167 RelocationKind::LArchAbs64Hi12 | RelocationKind::LArchPCAla64Hi12 => unsafe {
168 let (reloc_address, reloc_abs) = r.for_address(body, target_func_address as u64);
169 let reloc_abs = ((((reloc_abs >> 52) & 0xfff) as u32) << 10)
170 | read_unaligned(reloc_address as *mut u32);
171 write_unaligned(reloc_address as *mut u32, reloc_abs);
172 },
173 RelocationKind::LArchAbs64Lo20 | RelocationKind::LArchPCAla64Lo20 => unsafe {
174 let (reloc_address, reloc_abs) = r.for_address(body, target_func_address as u64);
175 let reloc_abs = ((((reloc_abs >> 32) & 0xfffff) as u32) << 5)
176 | read_unaligned(reloc_address as *mut u32);
177 write_unaligned(reloc_address as *mut u32, reloc_abs);
178 },
179 RelocationKind::LArchCall36 => unsafe {
180 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
181 let reloc_delta1 = ((((reloc_delta >> 18) & 0xfffff) as u32) << 5)
182 | read_unaligned(reloc_address as *mut u32);
183 write_unaligned(reloc_address as *mut u32, reloc_delta1);
184 let reloc_delta2 = ((((reloc_delta >> 2) & 0xffff) as u32) << 10)
185 | read_unaligned((reloc_address + 4) as *mut u32);
186 write_unaligned((reloc_address + 4) as *mut u32, reloc_delta2);
187 },
188 RelocationKind::Aarch64AdrPrelPgHi21 => unsafe {
189 let (reloc_address, delta) = r.for_address(body, target_func_address as u64);
190
191 let delta = delta as isize;
192 assert!(
193 ((-1 << 32)..(1 << 32)).contains(&delta),
194 "can't generate page-relative relocation with ±4GB `adrp` instruction"
195 );
196
197 let op = read_unaligned(reloc_address as *mut u32);
198 let delta = delta >> 12;
199 let immlo = ((delta as u32) & 0b11) << 29;
200 let immhi = (((delta as u32) >> 2) & 0x7ffff) << 5;
201 let mask = !((0x7ffff << 5) | (0b11 << 29));
202 let op = (op & mask) | immlo | immhi;
203
204 write_unaligned(reloc_address as *mut u32, op);
205 },
206 RelocationKind::Aarch64AdrPrelLo21 => unsafe {
207 let (reloc_address, delta) = r.for_address(body, target_func_address as u64);
208
209 let delta = delta as isize;
210 assert!(
211 ((-1 << 20)..(1 << 20)).contains(&delta),
212 "can't generate an ADR_PREL_LO21 relocation with an immediate larger than 20 bits"
213 );
214
215 let op = read_unaligned(reloc_address as *mut u32);
216 let immlo = ((delta as u32) & 0b11) << 29;
217 let immhi = (((delta as u32) >> 2) & 0x7ffff) << 5;
218 let mask = !((0x7ffff << 5) | (0b11 << 29));
219 let op = (op & mask) | immlo | immhi;
220
221 write_unaligned(reloc_address as *mut u32, op);
222 },
223 RelocationKind::Aarch64AddAbsLo12Nc => unsafe {
224 let (reloc_address, delta) = r.for_address(body, target_func_address as u64);
225
226 let delta = delta as isize;
227 let op = read_unaligned(reloc_address as *mut u32);
228 let imm = ((delta as u32) & 0xfff) << 10;
229 let mask = !((0xfff) << 10);
230 let op = (op & mask) | imm;
231
232 write_unaligned(reloc_address as *mut u32, op);
233 },
234 RelocationKind::Aarch64Ldst128AbsLo12Nc => unsafe {
235 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
236 let reloc_delta = ((reloc_delta as u32 & 0xfff) >> 4) << 10
237 | (read_unaligned(reloc_address as *mut u32) & 0xFFC003FF);
238 write_unaligned(reloc_address as *mut u32, reloc_delta);
239 },
240 RelocationKind::Aarch64Ldst64AbsLo12Nc => unsafe {
241 let (reloc_address, reloc_delta) = r.for_address(body, target_func_address as u64);
242 let reloc_delta = ((reloc_delta as u32 & 0xfff) >> 3) << 10
243 | (read_unaligned(reloc_address as *mut u32) & 0xFFC003FF);
244 write_unaligned(reloc_address as *mut u32, reloc_delta);
245 },
246 RelocationKind::MachoArm64RelocSubtractor | RelocationKind::MachoX86_64RelocSubtractor => unsafe {
247 let (reloc_address, reloc_sub) = r.for_address(body, target_func_address as u64);
248 macho_aarch64_subtractor_addresses.insert(reloc_address);
249 write_unaligned(reloc_address as *mut u64, reloc_sub);
250 },
251 RelocationKind::MachoArm64RelocGotLoadPage21
252 | RelocationKind::MachoArm64RelocTlvpLoadPage21 => unsafe {
253 let (reloc_address, _) = r.for_address(body, target_func_address as u64);
254 let target_func_page = target_func_address & !0xfff;
255 let reloc_at_page = reloc_address & !0xfff;
256 let pcrel = (target_func_page as isize)
257 .checked_sub(reloc_at_page as isize)
258 .unwrap();
259 assert!(
260 (-1 << 32) <= (pcrel as i64) && (pcrel as i64) < (1 << 32),
261 "can't reach GOT page with ±4GB `adrp` instruction"
262 );
263 let val = pcrel >> 12;
264
265 let immlo = ((val as u32) & 0b11) << 29;
266 let immhi = (((val as u32) >> 2) & 0x7ffff) << 5;
267 let mask = !((0x7ffff << 5) | (0b11 << 29));
268 let op = read_unaligned(reloc_address as *mut u32);
269 write_unaligned(reloc_address as *mut u32, (op & mask) | immlo | immhi);
270 },
271
272 RelocationKind::MachoArm64RelocPage21 => unsafe {
273 let target_page: u64 =
274 ((target_func_address.wrapping_add(r.addend() as _)) & !0xfff) as u64;
275 let reloc_address = body.wrapping_add(r.offset() as _);
276 let pc_page: u64 = (reloc_address & !0xfff) as u64;
277 let page_delta = target_page - pc_page;
278 let raw_instr = read_unaligned(reloc_address as *mut u32);
279 assert_eq!(
280 (raw_instr & 0xffffffe0),
281 0x90000000,
282 "raw_instr isn't an ADRP instruction"
283 );
284
285 let immlo: u32 = ((page_delta >> 12) & 0x3) as _;
286 let immhi: u32 = ((page_delta >> 14) & 0x7ffff) as _;
287 let fixed_instr = raw_instr | (immlo << 29) | (immhi << 5);
288 write_unaligned(reloc_address as *mut u32, fixed_instr);
289 },
290 RelocationKind::MachoArm64RelocPageoff12 => unsafe {
291 let target_offset: u64 =
292 ((target_func_address.wrapping_add(r.addend() as _)) & 0xfff) as u64;
293
294 let reloc_address = body.wrapping_add(r.offset() as _);
295 let raw_instr = read_unaligned(reloc_address as *mut u32);
296 let imm_shift = {
297 const VEC128_MASK: u32 = 0x04800000;
298
299 const LOAD_STORE_IMM12_MASK: u32 = 0x3b000000;
300 let is_load_store_imm12 = (raw_instr & LOAD_STORE_IMM12_MASK) == 0x39000000;
301
302 if is_load_store_imm12 {
303 let mut implicit_shift = raw_instr >> 30;
304
305 if implicit_shift == 0 && (raw_instr & VEC128_MASK) == VEC128_MASK {
306 implicit_shift = 4;
307 }
308
309 implicit_shift
310 } else {
311 0
312 }
313 };
314
315 assert_eq!(
316 target_offset & ((1 << imm_shift) - 1),
317 0,
318 "PAGEOFF12 target is not aligned"
319 );
320
321 let encoded_imm: u32 = ((target_offset as u32) >> imm_shift) << 10;
322 let fixed_instr: u32 = raw_instr | encoded_imm;
323 write_unaligned(reloc_address as *mut u32, fixed_instr);
324 },
325
326 RelocationKind::MachoArm64RelocGotLoadPageoff12 => unsafe {
327 if r.addend() == 0 {
331 let (reloc_address, _) = r.for_address(body, target_func_address as u64);
332 assert_eq!(target_func_address & 0b111, 0);
333 let val = target_func_address >> 3;
334 let imm9 = ((val & 0x1ff) << 10) as u32;
335 let mask = !(0x1ff << 10);
336 let op = read_unaligned(reloc_address as *mut u32);
337 write_unaligned(reloc_address as *mut u32, (op & mask) | imm9);
338 } else {
339 let fixup_ptr = body + r.offset() as usize;
340 let target_address: usize = target_func_address + r.addend() as usize;
341
342 let raw_instr = read_unaligned(fixup_ptr as *mut u32);
343
344 assert_eq!(
345 raw_instr & 0xfffffc00, 0xf9400000,
346 "raw_instr isn't a 64-bit LDR immediate (bits: {raw_instr:032b}, hex: {raw_instr:x})"
347 );
348
349 let reg: u32 = raw_instr & 0b11111;
350
351 let mut fixup_ldr = 0x91000000 | (reg << 5) | reg;
352 fixup_ldr |= ((target_address & 0xfff) as u32) << 10;
353
354 write_unaligned(fixup_ptr as *mut u32, fixup_ldr);
355 }
356 },
357 RelocationKind::MachoArm64RelocUnsigned | RelocationKind::MachoX86_64RelocUnsigned => unsafe {
358 let (reloc_address, mut reloc_delta) = r.for_address(body, target_func_address as u64);
359
360 if macho_aarch64_subtractor_addresses.contains(&reloc_address) {
361 reloc_delta -= read_unaligned(reloc_address as *mut u64);
362 }
363
364 write_unaligned(reloc_address as *mut u64, reloc_delta);
365 },
366
367 RelocationKind::MachoArm64RelocPointerToGot => unsafe {
368 let at = body + r.offset() as usize;
369 let pcrel = i32::try_from((target_func_address as isize) - (at as isize)).unwrap();
370 write_unaligned(at as *mut i32, pcrel);
371 },
372
373 RelocationKind::MachoArm64RelocBranch26 => unsafe {
374 let fixup_ptr = body + r.offset() as usize;
375 assert_eq!(fixup_ptr & 0x3, 0, "Branch-inst is not 32-bit aligned");
376 let value = i32::try_from((target_func_address as isize) - (fixup_ptr as isize))
377 .unwrap()
378 .wrapping_add(r.addend() as _);
379 assert!(
380 value & 0x3 == 0,
381 "BranchPCRel26 target is not 32-bit aligned"
382 );
383
384 assert!(
385 (-(1 << 27)..=((1 << 27) - 1)).contains(&value),
386 "out of range BranchPCRel26 target"
387 );
388
389 let raw_instr = read_unaligned(fixup_ptr as *mut u32);
390
391 assert_eq!(
392 raw_instr & 0x7fffffff,
393 0x14000000,
394 "RawInstr isn't a B or BR immediate instruction"
395 );
396 let imm: u32 = ((value as u32) & ((1 << 28) - 1)) >> 2;
397 let fixed_instr: u32 = raw_instr | imm;
398
399 write_unaligned(fixup_ptr as *mut u32, fixed_instr);
400 },
401 kind => panic!("Relocation kind unsupported in the current architecture: {kind}"),
402 }
403}
404
405#[allow(clippy::too_many_arguments)]
408pub fn link_module<'a>(
409 _module: &ModuleInfo,
410 allocated_functions: &PrimaryMap<LocalFunctionIndex, FunctionExtent>,
411 function_relocations: impl Iterator<
412 Item = (
413 LocalFunctionIndex,
414 impl Iterator<Item = &'a (impl RelocationLike + 'a)>,
415 ),
416 >,
417 allocated_sections: &PrimaryMap<SectionIndex, SectionBodyPtr>,
418 section_relocations: impl Iterator<
419 Item = (
420 SectionIndex,
421 impl Iterator<Item = &'a (impl RelocationLike + 'a)>,
422 ),
423 >,
424 libcall_trampolines: SectionIndex,
425 trampoline_len: usize,
426 get_got_address: &'a dyn Fn(RelocationTarget) -> Option<usize>,
427) {
428 let mut riscv_pcrel_hi20s: HashMap<usize, u32> = HashMap::new();
429
430 for (i, section_relocs) in section_relocations {
431 let body = *allocated_sections[i] as usize;
432 for r in section_relocs {
433 apply_relocation(
434 body,
435 r,
436 allocated_functions,
437 allocated_sections,
438 libcall_trampolines,
439 trampoline_len,
440 &mut riscv_pcrel_hi20s,
441 get_got_address,
442 );
443 }
444 }
445 for (i, function_relocs) in function_relocations {
446 let body = *allocated_functions[i].ptr as usize;
447 for r in function_relocs {
448 apply_relocation(
449 body,
450 r,
451 allocated_functions,
452 allocated_sections,
453 libcall_trampolines,
454 trampoline_len,
455 &mut riscv_pcrel_hi20s,
456 get_got_address,
457 );
458 }
459 }
460}