Path: blob/main/cranelift/jit/src/compiled_blob.rs
1692 views
use cranelift_codegen::binemit::Reloc;1use cranelift_module::ModuleReloc;2use cranelift_module::ModuleRelocTarget;34/// Reads a 32bit instruction at `iptr`, and writes it again after5/// being altered by `modifier`6unsafe fn modify_inst32(iptr: *mut u32, modifier: impl FnOnce(u32) -> u32) {7let inst = iptr.read_unaligned();8let new_inst = modifier(inst);9iptr.write_unaligned(new_inst);10}1112#[derive(Clone)]13pub(crate) struct CompiledBlob {14pub(crate) ptr: *mut u8,15pub(crate) size: usize,16pub(crate) relocs: Vec<ModuleReloc>,17#[cfg(feature = "wasmtime-unwinder")]18pub(crate) exception_data: Option<Vec<u8>>,19}2021unsafe impl Send for CompiledBlob {}2223impl CompiledBlob {24pub(crate) fn perform_relocations(25&self,26get_address: impl Fn(&ModuleRelocTarget) -> *const u8,27) {28use std::ptr::write_unaligned;2930for (31i,32&ModuleReloc {33kind,34offset,35ref name,36addend,37},38) in self.relocs.iter().enumerate()39{40debug_assert!((offset as usize) < self.size);41let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) };42match kind {43Reloc::Abs4 => {44let base = get_address(name);45let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };46unsafe {47write_unaligned(at as *mut u32, u32::try_from(what as usize).unwrap())48};49}50Reloc::Abs8 => {51let base = get_address(name);52let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };53unsafe {54write_unaligned(at as *mut u64, u64::try_from(what as usize).unwrap())55};56}57Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {58let base = get_address(name);59let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };60let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();61unsafe { write_unaligned(at as *mut i32, pcrel) };62}63Reloc::X86GOTPCRel4 => {64panic!("GOT relocation shouldn't be generated when !is_pic");65}66Reloc::X86CallPLTRel4 => {67panic!("PLT relocation shouldn't be generated when !is_pic");68}69Reloc::S390xPCRel32Dbl | Reloc::S390xPLTRel32Dbl => {70let base = get_address(name);71let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };72let pcrel = i32::try_from(((what as isize) - (at as isize)) >> 1).unwrap();73unsafe { write_unaligned(at as *mut i32, pcrel) };74}75Reloc::Arm64Call => {76let base = get_address(name);77// The instruction is 32 bits long.78let iptr = at as *mut u32;79// The offset encoded in the `bl` instruction is the80// number of bytes divided by 4.81let diff = ((base as isize) - (at as isize)) >> 2;82// Sign propagating right shift disposes of the83// included bits, so the result is expected to be84// either all sign bits or 0, depending on if the original85// value was negative or positive.86assert!((diff >> 26 == -1) || (diff >> 26 == 0));87// The lower 26 bits of the `bl` instruction form the88// immediate offset argument.89let chop = 32 - 26;90let imm26 = (diff as u32) << chop >> chop;91unsafe { modify_inst32(iptr, |inst| inst | imm26) };92}93Reloc::Aarch64AdrGotPage21 => {94panic!("GOT relocation shouldn't be generated when !is_pic");95}96Reloc::Aarch64Ld64GotLo12Nc => {97panic!("GOT relocation shouldn't be generated when !is_pic");98}99Reloc::Aarch64AdrPrelPgHi21 => {100let base = get_address(name);101let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };102let get_page = |x| x & (!0xfff);103let pcrel = i32::try_from(get_page(what as isize) - get_page(at as isize))104.unwrap()105.cast_unsigned();106let iptr = at as *mut u32;107let hi21 = pcrel >> 12;108let lo = (hi21 & 0x3) << 29;109let hi = (hi21 & 0x1ffffc) << 3;110unsafe { modify_inst32(iptr, |inst| inst | lo | hi) };111}112Reloc::Aarch64AddAbsLo12Nc => {113let base = get_address(name);114let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };115let iptr = at as *mut u32;116let imm12 = (what.addr() as u32 & 0xfff) << 10;117unsafe { modify_inst32(iptr, |inst| inst | imm12) };118}119Reloc::RiscvCallPlt => {120// A R_RISCV_CALL_PLT relocation expects auipc+jalr instruction pair.121// It is the equivalent of two relocations:122// 1. R_RISCV_PCREL_HI20 on the `auipc`123// 2. R_RISCV_PCREL_LO12_I on the `jalr`124125let base = get_address(name);126let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };127let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap() as u32;128129// See https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses130// for a better explanation of the following code.131//132// Unlike the regular symbol relocations, here both "sub-relocations" point to the same address.133//134// `pcrel` is a signed value (+/- 2GiB range), when splitting it into two parts, we need to135// ensure that `hi20` is close enough to `pcrel` to be able to add `lo12` to it and still136// get a valid address.137//138// `lo12` is also a signed offset (+/- 2KiB range) relative to the `hi20` value.139//140// `hi20` should also be shifted right to be the "true" value. But we also need it141// left shifted for the `lo12` calculation and it also matches the instruction encoding.142let hi20 = pcrel.wrapping_add(0x800) & 0xFFFFF000;143let lo12 = pcrel.wrapping_sub(hi20) & 0xFFF;144145unsafe {146// Do a R_RISCV_PCREL_HI20 on the `auipc`147let auipc_addr = at as *mut u32;148modify_inst32(auipc_addr, |auipc| (auipc & 0xFFF) | hi20);149150// Do a R_RISCV_PCREL_LO12_I on the `jalr`151let jalr_addr = at.offset(4) as *mut u32;152modify_inst32(jalr_addr, |jalr| (jalr & 0xFFFFF) | (lo12 << 20));153}154}155Reloc::PulleyPcRel => {156let base = get_address(name);157let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };158let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();159let at = at as *mut i32;160unsafe {161at.write_unaligned(at.read_unaligned().wrapping_add(pcrel));162}163}164165// See <https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-elf.adoc#pc-relative-symbol-addresses>166// for why `0x800` is added here.167Reloc::RiscvPCRelHi20 => {168let base = get_address(name);169let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };170let pcrel = i32::try_from((what as isize) - (at as isize) + 0x800)171.unwrap()172.cast_unsigned();173let at = at as *mut u32;174unsafe {175modify_inst32(at, |i| i | (pcrel & 0xfffff000));176}177}178179// The target of this relocation is the `auipc` preceding this180// instruction which should be `RiscvPCRelHi20`, and the actual181// target that we're relocating against is the target of that182// relocation. Assume for now that the previous relocation is183// the target of this relocation, and then use that.184Reloc::RiscvPCRelLo12I => {185let prev_reloc = &self.relocs[i - 1];186assert_eq!(prev_reloc.kind, Reloc::RiscvPCRelHi20);187let lo_target = get_address(name);188let hi_address =189unsafe { self.ptr.offset(isize::try_from(prev_reloc.offset).unwrap()) };190assert_eq!(lo_target, hi_address);191let hi_target = get_address(&prev_reloc.name);192let pcrel = i32::try_from((hi_target as isize) - (hi_address as isize))193.unwrap()194.cast_unsigned();195let at = at as *mut u32;196unsafe {197modify_inst32(at, |i| i | ((pcrel & 0xfff) << 20));198}199}200201other => unimplemented!("unimplemented reloc {other:?}"),202}203}204}205}206207208