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