cranelift_jit/compiled_blob.rs
1use cranelift_codegen::binemit::Reloc;
2use cranelift_module::ModuleReloc;
3use cranelift_module::ModuleRelocTarget;
4
5/// Reads a 32bit instruction at `iptr`, and writes it again after
6/// being altered by `modifier`
7unsafe fn modify_inst32(iptr: *mut u32, modifier: impl FnOnce(u32) -> u32) {
8 let inst = iptr.read_unaligned();
9 let new_inst = modifier(inst);
10 iptr.write_unaligned(new_inst);
11}
12
13#[derive(Clone)]
14pub(crate) struct CompiledBlob {
15 pub(crate) ptr: *mut u8,
16 pub(crate) size: usize,
17 pub(crate) relocs: Vec<ModuleReloc>,
18 #[cfg(feature = "wasmtime-unwinder")]
19 pub(crate) exception_data: Option<Vec<u8>>,
20}
21
22unsafe impl Send for CompiledBlob {}
23
24impl CompiledBlob {
25 pub(crate) fn perform_relocations(
26 &self,
27 get_address: impl Fn(&ModuleRelocTarget) -> *const u8,
28 ) {
29 use std::ptr::write_unaligned;
30
31 for &ModuleReloc {
32 kind,
33 offset,
34 ref name,
35 addend,
36 } in &self.relocs
37 {
38 debug_assert!((offset as usize) < self.size);
39 let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) };
40 match kind {
41 Reloc::Abs4 => {
42 let base = get_address(name);
43 let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
44 unsafe {
45 write_unaligned(at as *mut u32, u32::try_from(what as usize).unwrap())
46 };
47 }
48 Reloc::Abs8 => {
49 let base = get_address(name);
50 let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
51 unsafe {
52 write_unaligned(at as *mut u64, u64::try_from(what as usize).unwrap())
53 };
54 }
55 Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
56 let base = get_address(name);
57 let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
58 let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();
59 unsafe { write_unaligned(at as *mut i32, pcrel) };
60 }
61 Reloc::X86GOTPCRel4 => {
62 panic!("GOT relocation shouldn't be generated when !is_pic");
63 }
64 Reloc::X86CallPLTRel4 => {
65 panic!("PLT relocation shouldn't be generated when !is_pic");
66 }
67 Reloc::S390xPCRel32Dbl | Reloc::S390xPLTRel32Dbl => {
68 let base = get_address(name);
69 let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
70 let pcrel = i32::try_from(((what as isize) - (at as isize)) >> 1).unwrap();
71 unsafe { write_unaligned(at as *mut i32, pcrel) };
72 }
73 Reloc::Arm64Call => {
74 let base = get_address(name);
75 // The instruction is 32 bits long.
76 let iptr = at as *mut u32;
77 // The offset encoded in the `bl` instruction is the
78 // number of bytes divided by 4.
79 let diff = ((base as isize) - (at as isize)) >> 2;
80 // Sign propagating right shift disposes of the
81 // included bits, so the result is expected to be
82 // either all sign bits or 0, depending on if the original
83 // value was negative or positive.
84 assert!((diff >> 26 == -1) || (diff >> 26 == 0));
85 // The lower 26 bits of the `bl` instruction form the
86 // immediate offset argument.
87 let chop = 32 - 26;
88 let imm26 = (diff as u32) << chop >> chop;
89 unsafe { modify_inst32(iptr, |inst| inst | imm26) };
90 }
91 Reloc::Aarch64AdrGotPage21 => {
92 panic!("GOT relocation shouldn't be generated when !is_pic");
93 }
94 Reloc::Aarch64Ld64GotLo12Nc => {
95 panic!("GOT relocation shouldn't be generated when !is_pic");
96 }
97 Reloc::RiscvCallPlt => {
98 // A R_RISCV_CALL_PLT relocation expects auipc+jalr instruction pair.
99 // It is the equivalent of two relocations:
100 // 1. R_RISCV_PCREL_HI20 on the `auipc`
101 // 2. R_RISCV_PCREL_LO12_I on the `jalr`
102
103 let base = get_address(name);
104 let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
105 let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap() as u32;
106
107 // See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses
108 // for a better explanation of the following code.
109 //
110 // Unlike the regular symbol relocations, here both "sub-relocations" point to the same address.
111 //
112 // `pcrel` is a signed value (+/- 2GiB range), when splitting it into two parts, we need to
113 // ensure that `hi20` is close enough to `pcrel` to be able to add `lo12` to it and still
114 // get a valid address.
115 //
116 // `lo12` is also a signed offset (+/- 2KiB range) relative to the `hi20` value.
117 //
118 // `hi20` should also be shifted right to be the "true" value. But we also need it
119 // left shifted for the `lo12` calculation and it also matches the instruction encoding.
120 let hi20 = pcrel.wrapping_add(0x800) & 0xFFFFF000;
121 let lo12 = pcrel.wrapping_sub(hi20) & 0xFFF;
122
123 unsafe {
124 // Do a R_RISCV_PCREL_HI20 on the `auipc`
125 let auipc_addr = at as *mut u32;
126 modify_inst32(auipc_addr, |auipc| (auipc & 0xFFF) | hi20);
127
128 // Do a R_RISCV_PCREL_LO12_I on the `jalr`
129 let jalr_addr = at.offset(4) as *mut u32;
130 modify_inst32(jalr_addr, |jalr| (jalr & 0xFFFFF) | (lo12 << 20));
131 }
132 }
133 _ => unimplemented!(),
134 }
135 }
136 }
137}