#![allow(missing_docs)]
use super::section::SectionIndex;
use crate::{Addend, CodeOffset};
use rkyv::{Archive, Deserialize as RkyvDeserialize, Serialize as RkyvSerialize};
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
use wasmer_types::{FunctionIndex, LibCall, LocalFunctionIndex, entity::PrimaryMap};
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Copy, Clone, Debug, PartialEq, Eq)]
#[rkyv(derive(Debug), compare(PartialEq))]
#[repr(u8)]
pub enum RelocationKind {
Abs4,
Abs8,
PCRel4,
PCRel8,
X86CallPCRel4,
X86CallPLTRel4,
X86GOTPCRel4,
Aarch64AdrPrelLo21,
Aarch64AdrPrelPgHi21,
Aarch64AddAbsLo12Nc,
Aarch64Ldst128AbsLo12Nc,
Aarch64Ldst64AbsLo12Nc,
Arm32Call,
Arm64Call,
Arm64Movw0,
Arm64Movw1,
Arm64Movw2,
Arm64Movw3,
RiscvPCRelHi20,
RiscvPCRelLo12I,
RiscvCall,
LArchAbsHi20,
LArchAbsLo12,
LArchAbs64Hi12,
LArchAbs64Lo20,
LArchCall36,
LArchPCAlaHi20,
LArchPCAlaLo12,
LArchPCAla64Hi12,
LArchPCAla64Lo20,
ElfX86_64TlsGd,
MachoArm64RelocUnsigned,
MachoArm64RelocSubtractor,
MachoArm64RelocBranch26,
MachoArm64RelocPage21,
MachoArm64RelocPageoff12,
MachoArm64RelocGotLoadPage21,
MachoArm64RelocGotLoadPageoff12,
MachoArm64RelocPointerToGot,
MachoArm64RelocTlvpLoadPage21,
MachoArm64RelocTlvpLoadPageoff12,
MachoArm64RelocAddend,
MachoX86_64RelocUnsigned,
MachoX86_64RelocSigned,
MachoX86_64RelocBranch,
MachoX86_64RelocGotLoad,
MachoX86_64RelocGot,
MachoX86_64RelocSubtractor,
MachoX86_64RelocSigned1,
MachoX86_64RelocSigned2,
MachoX86_64RelocSigned4,
MachoX86_64RelocTlv,
Abs6Bits,
Abs,
Abs2,
Add,
Add2,
Add4,
Add8,
Sub6Bits,
Sub,
Sub2,
Sub4,
Sub8,
}
impl RelocationKind {
pub fn needs_got(&self) -> bool {
matches!(
self,
Self::MachoArm64RelocGotLoadPage21
| Self::MachoArm64RelocGotLoadPageoff12
| Self::MachoArm64RelocPointerToGot
| Self::MachoX86_64RelocGotLoad
| Self::MachoX86_64RelocGot
)
}
}
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Clone, PartialEq, Eq)]
#[rkyv(derive(Debug), compare(PartialEq))]
pub struct Relocation {
pub kind: RelocationKind,
pub reloc_target: RelocationTarget,
pub offset: CodeOffset,
pub addend: Addend,
}
#[allow(missing_docs)]
pub trait RelocationLike {
fn kind(&self) -> RelocationKind;
fn reloc_target(&self) -> RelocationTarget;
fn offset(&self) -> CodeOffset;
fn addend(&self) -> Addend;
fn for_address(&self, start: usize, target_func_address: u64) -> (usize, u64) {
match self.kind() {
RelocationKind::Abs6Bits
| RelocationKind::Abs
| RelocationKind::Abs2
| RelocationKind::Abs4
| RelocationKind::Abs8
| RelocationKind::Arm64Movw0
| RelocationKind::Arm64Movw1
| RelocationKind::Arm64Movw2
| RelocationKind::Arm64Movw3
| RelocationKind::RiscvPCRelLo12I
| RelocationKind::Aarch64Ldst128AbsLo12Nc
| RelocationKind::Aarch64Ldst64AbsLo12Nc
| RelocationKind::MachoArm64RelocUnsigned
| RelocationKind::MachoX86_64RelocUnsigned
| RelocationKind::MachoArm64RelocSubtractor
| RelocationKind::MachoX86_64RelocSubtractor
| RelocationKind::LArchAbsHi20
| RelocationKind::LArchAbsLo12
| RelocationKind::LArchAbs64Lo20
| RelocationKind::LArchAbs64Hi12
| RelocationKind::LArchPCAlaLo12
| RelocationKind::Add
| RelocationKind::Add2
| RelocationKind::Add4
| RelocationKind::Add8
| RelocationKind::Sub6Bits
| RelocationKind::Sub
| RelocationKind::Sub2
| RelocationKind::Sub4
| RelocationKind::Sub8 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let reloc_abs = target_func_address
.checked_add(reloc_addend as u64)
.unwrap();
(reloc_address, reloc_abs)
}
RelocationKind::PCRel4 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let reloc_delta_u32 = (target_func_address as u32)
.wrapping_sub(reloc_address as u32)
.checked_add(reloc_addend as u32)
.unwrap();
(reloc_address, reloc_delta_u32 as u64)
}
RelocationKind::PCRel8 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let reloc_delta = target_func_address
.wrapping_sub(reloc_address as u64)
.checked_add(reloc_addend as u64)
.unwrap();
(reloc_address, reloc_delta)
}
RelocationKind::X86CallPCRel4 | RelocationKind::X86CallPLTRel4 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let reloc_delta_u32 = (target_func_address as u32)
.wrapping_sub(reloc_address as u32)
.wrapping_add(reloc_addend as u32);
(reloc_address, reloc_delta_u32 as u64)
}
RelocationKind::Aarch64AdrPrelLo21 => {
let s = target_func_address;
let p = start + self.offset() as usize;
let a = self.addend() as u64;
(p, s.wrapping_add(a).wrapping_sub(p as u64))
}
RelocationKind::Aarch64AddAbsLo12Nc => {
let s = target_func_address;
let p = start + self.offset() as usize;
let a = self.addend() as u64;
(p, s.wrapping_add(a))
}
RelocationKind::Arm64Call
| RelocationKind::RiscvCall
| RelocationKind::RiscvPCRelHi20 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let reloc_delta_u32 = target_func_address
.wrapping_sub(reloc_address as u64)
.wrapping_add(reloc_addend as u64);
(reloc_address, reloc_delta_u32)
}
RelocationKind::Aarch64AdrPrelPgHi21
| RelocationKind::MachoArm64RelocGotLoadPage21
| RelocationKind::MachoArm64RelocPage21 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let target_page =
(target_func_address.wrapping_add(reloc_addend as u64) & !(0xFFF)) as usize;
let pc_page = reloc_address & !(0xFFF);
(reloc_address, target_page.wrapping_sub(pc_page) as u64)
}
RelocationKind::MachoArm64RelocGotLoadPageoff12
| RelocationKind::MachoArm64RelocPageoff12 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let target_offset =
(target_func_address.wrapping_add(reloc_addend as u64) & (0xFFF)) as usize;
(reloc_address, target_offset as u64)
}
RelocationKind::LArchCall36 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let reloc_delta = target_func_address
.wrapping_sub(reloc_address as u64)
.wrapping_add(reloc_addend as u64);
(
reloc_address,
reloc_delta.wrapping_add((reloc_delta & 0x20000) << 1),
)
}
RelocationKind::LArchPCAlaHi20 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let target_page = (target_func_address
.wrapping_add(reloc_addend as u64)
.wrapping_add(0x800)
& !(0xFFF)) as usize;
let pc_page = reloc_address & !(0xFFF);
(reloc_address, target_page.wrapping_sub(pc_page) as u64)
}
RelocationKind::LArchPCAla64Hi12 | RelocationKind::LArchPCAla64Lo20 => {
let reloc_address = start + self.offset() as usize;
let reloc_addend = self.addend() as isize;
let reloc_offset = match self.kind() {
RelocationKind::LArchPCAla64Lo20 => 8,
RelocationKind::LArchPCAla64Hi12 => 12,
_ => 0,
};
let target_func_address = target_func_address.wrapping_add(reloc_addend as u64);
let target_page = (target_func_address & !(0xFFF)) as usize;
let pc_page = (reloc_address - reloc_offset) & !(0xFFF);
let mut reloc_delta = target_page.wrapping_sub(pc_page) as u64;
reloc_delta = reloc_delta
.wrapping_add((target_func_address & 0x800) << 1)
.wrapping_sub((target_func_address & 0x800) << 21);
reloc_delta = reloc_delta.wrapping_add((reloc_delta & 0x80000000) << 1);
(reloc_address, reloc_delta)
}
RelocationKind::MachoArm64RelocPointerToGot => {
let reloc_address = start + self.offset() as usize;
let reloc_delta =
(target_func_address as isize).wrapping_sub(reloc_address as isize);
(reloc_address, reloc_delta as u64)
}
_ => panic!("Relocation kind unsupported"),
}
}
}
impl RelocationLike for Relocation {
fn kind(&self) -> RelocationKind {
self.kind
}
fn reloc_target(&self) -> RelocationTarget {
self.reloc_target
}
fn offset(&self) -> CodeOffset {
self.offset
}
fn addend(&self) -> Addend {
self.addend
}
}
impl RelocationLike for ArchivedRelocation {
fn kind(&self) -> RelocationKind {
rkyv::deserialize::<_, String>(&self.kind).unwrap()
}
fn reloc_target(&self) -> RelocationTarget {
rkyv::deserialize::<_, String>(&self.reloc_target).unwrap()
}
fn offset(&self) -> CodeOffset {
self.offset.into()
}
fn addend(&self) -> Addend {
self.addend.into()
}
}
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "artifact-size", derive(loupe::MemoryUsage))]
#[derive(RkyvSerialize, RkyvDeserialize, Archive, Debug, Copy, Clone, PartialEq, Eq, Hash)]
#[rkyv(derive(Debug, Hash, PartialEq, Eq), compare(PartialEq))]
#[repr(u8)]
pub enum RelocationTarget {
LocalFunc(LocalFunctionIndex),
DynamicTrampoline(FunctionIndex),
LibCall(LibCall),
CustomSection(SectionIndex),
}
pub type Relocations = PrimaryMap<LocalFunctionIndex, Vec<Relocation>>;