mod insn;
use goblin::elf::{Elf, SectionHeader};
use int_enum::IntEnum;
use crate::{
BIT, BIT_U64, ModuleErr, Result,
arch::{Ptr, aarch64::insn::*, get_rela_sym_idx, get_rela_type},
loader::*,
};
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct ModuleArchSpecific {}
#[repr(u32)]
#[derive(Debug, Clone, Copy, IntEnum, PartialEq, Eq)]
#[allow(non_camel_case_types)]
pub enum ArchRelocationType {
R_ARM_NONE = 0,
R_AARCH64_NONE = 256,
R_AARCH64_ABS64 = 257,
R_AARCH64_ABS32 = 258,
R_AARCH64_ABS16 = 259,
R_AARCH64_PREL64 = 260,
R_AARCH64_PREL32 = 261,
R_AARCH64_PREL16 = 262,
R_AARCH64_MOVW_UABS_G0 = 263,
R_AARCH64_MOVW_UABS_G0_NC = 264,
R_AARCH64_MOVW_UABS_G1 = 265,
R_AARCH64_MOVW_UABS_G1_NC = 266,
R_AARCH64_MOVW_UABS_G2 = 267,
R_AARCH64_MOVW_UABS_G2_NC = 268,
R_AARCH64_MOVW_UABS_G3 = 269,
R_AARCH64_MOVW_SABS_G0 = 270,
R_AARCH64_MOVW_SABS_G1 = 271,
R_AARCH64_MOVW_SABS_G2 = 272,
R_AARCH64_LD_PREL_LO19 = 273,
R_AARCH64_ADR_PREL_LO21 = 274,
R_AARCH64_ADR_PREL_PG_HI21 = 275,
R_AARCH64_ADR_PREL_PG_HI21_NC = 276,
R_AARCH64_ADD_ABS_LO12_NC = 277,
R_AARCH64_LDST8_ABS_LO12_NC = 278,
R_AARCH64_TSTBR14 = 279,
R_AARCH64_CONDBR19 = 280,
R_AARCH64_JUMP26 = 282,
R_AARCH64_CALL26 = 283,
R_AARCH64_LDST16_ABS_LO12_NC = 284,
R_AARCH64_LDST32_ABS_LO12_NC = 285,
R_AARCH64_LDST64_ABS_LO12_NC = 286,
R_AARCH64_LDST128_ABS_LO12_NC = 299,
R_AARCH64_MOVW_PREL_G0 = 287,
R_AARCH64_MOVW_PREL_G0_NC = 288,
R_AARCH64_MOVW_PREL_G1 = 289,
R_AARCH64_MOVW_PREL_G1_NC = 290,
R_AARCH64_MOVW_PREL_G2 = 291,
R_AARCH64_MOVW_PREL_G2_NC = 292,
R_AARCH64_MOVW_PREL_G3 = 293,
R_AARCH64_RELATIVE = 1027,
}
type Arm64RelTy = ArchRelocationType;
const fn do_reloc(op: Aarch64RelocOp, location: Ptr, address: u64) -> u64 {
match op {
Aarch64RelocOp::RELOC_OP_ABS => address,
Aarch64RelocOp::RELOC_OP_PREL => address.wrapping_sub(location.0),
Aarch64RelocOp::RELOC_OP_PAGE => (address & !0xfff).wrapping_sub(location.0 & !0xfff),
Aarch64RelocOp::RELOC_OP_NONE => 0,
}
}
fn is_forbidden_offset_for_adrp(address: u64) -> bool {
((address & 0xfff) >= 0xff8) && false
}
impl ArchRelocationType {
fn reloc_data(
&self,
op: Aarch64RelocOp,
location: Ptr,
address: u64,
len: usize,
) -> Result<bool> {
let s_addr = do_reloc(op, location, address) as i64;
match len {
16 => {
location.write::<i16>(s_addr as i16);
match op {
Aarch64RelocOp::RELOC_OP_ABS => Ok(s_addr < 0 || s_addr > u16::MAX as i64),
Aarch64RelocOp::RELOC_OP_PREL => {
Ok(s_addr < i16::MIN as i64 || s_addr > i16::MAX as i64)
}
_ => {
unreachable!("Unsupported operation for AArch64 16-bit relocation")
}
}
}
32 => {
location.write::<i32>(s_addr as i32);
match op {
Aarch64RelocOp::RELOC_OP_ABS => Ok(s_addr < 0 || s_addr > u32::MAX as i64),
Aarch64RelocOp::RELOC_OP_PREL => {
Ok(s_addr < i32::MIN as i64 || s_addr > i32::MAX as i64)
}
_ => {
unreachable!("Unsupported operation for AArch64 32-bit relocation")
}
}
}
64 => {
location.write::<u64>(s_addr as u64);
Ok(false)
}
_ => unreachable!("Unsupported length for AArch64 relocation"),
}
}
fn reloc_insn_movw(
&self,
op: Aarch64RelocOp,
location: Ptr,
address: u64,
lsb: i32,
imm_type: Aarch64InsnMovwImmType,
) -> Result<bool> {
let mut insn = location.read::<u32>();
let s_addr = do_reloc(op, location, address) as i64;
let mut imm = (s_addr >> lsb) as u64;
if imm_type == Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ {
insn &= !(3 << 29);
if s_addr >= 0 {
insn |= 2 << 29;
} else {
imm = !imm;
}
}
insn = aarch64_insn_encode_immediate(Aarch64InsnImmType::AARCH64_INSN_IMM_16, insn, imm);
location.write::<u32>(insn);
if imm > u16::MAX as u64 {
Ok(true)
} else {
Ok(false)
}
}
fn reloc_insn_imm(
&self,
op: Aarch64RelocOp,
location: Ptr,
address: u64,
lsb: i32,
len: i32,
imm_type: Aarch64InsnImmType,
) -> Result<bool> {
let mut insn = location.read::<u32>();
let mut s_addr = do_reloc(op, location, address) as i64;
s_addr >>= lsb;
let imm_mask = (BIT_U64!(lsb + len) - 1) >> lsb;
let imm = (s_addr as u64) & imm_mask;
insn = aarch64_insn_encode_immediate(imm_type, insn, imm);
location.write::<u32>(insn);
s_addr = (s_addr & !((imm_mask >> 1) as i64)) >> (len - 1);
if (s_addr + 1) as u64 >= 2 {
Ok(true)
} else {
Ok(false)
}
}
fn reloc_insn_adrp(&self, location: Ptr, address: u64) -> Result<bool> {
if !is_forbidden_offset_for_adrp(location.0) {
return self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_PAGE,
location,
address,
12,
21,
Aarch64InsnImmType::AARCH64_INSN_IMM_ADR,
);
}
let ovf = self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address & !0xfff,
0,
21,
Aarch64InsnImmType::AARCH64_INSN_IMM_ADR,
)?;
if !ovf {
let mut insn = location.read::<u32>();
insn &= !BIT!(31); location.write::<u32>(insn);
Ok(false)
} else {
log::error!("ADR out of range for veneer emission");
Err(ModuleErr::ENOEXEC)
}
}
fn apply_relocation(&self, location: u64, address: u64) -> Result<()> {
let mut check_overflow = true;
let location = Ptr(location);
let ovf = match self {
Arm64RelTy::R_ARM_NONE | Arm64RelTy::R_AARCH64_NONE => false,
Arm64RelTy::R_AARCH64_ABS64 => {
check_overflow = false;
self.reloc_data(Aarch64RelocOp::RELOC_OP_ABS, location, address, 64)?
}
Arm64RelTy::R_AARCH64_ABS32 => {
self.reloc_data(Aarch64RelocOp::RELOC_OP_ABS, location, address, 32)?
}
Arm64RelTy::R_AARCH64_ABS16 => {
self.reloc_data(Aarch64RelocOp::RELOC_OP_ABS, location, address, 16)?
}
Arm64RelTy::R_AARCH64_PREL64 => {
check_overflow = false;
self.reloc_data(Aarch64RelocOp::RELOC_OP_PREL, location, address, 64)?
}
Arm64RelTy::R_AARCH64_PREL32 => {
self.reloc_data(Aarch64RelocOp::RELOC_OP_PREL, location, address, 32)?
}
Arm64RelTy::R_AARCH64_PREL16 => {
self.reloc_data(Aarch64RelocOp::RELOC_OP_PREL, location, address, 16)?
}
Arm64RelTy::R_AARCH64_MOVW_UABS_G0_NC | Arm64RelTy::R_AARCH64_MOVW_UABS_G0 => {
if *self == Arm64RelTy::R_AARCH64_MOVW_UABS_G0_NC {
check_overflow = false;
}
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
0,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVKZ,
)?
}
Arm64RelTy::R_AARCH64_MOVW_UABS_G1_NC | Arm64RelTy::R_AARCH64_MOVW_UABS_G1 => {
if *self == Arm64RelTy::R_AARCH64_MOVW_UABS_G1_NC {
check_overflow = false;
}
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
16,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVKZ,
)?
}
Arm64RelTy::R_AARCH64_MOVW_UABS_G2_NC | Arm64RelTy::R_AARCH64_MOVW_UABS_G2 => {
if *self == Arm64RelTy::R_AARCH64_MOVW_UABS_G2_NC {
check_overflow = false;
}
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
32,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVKZ,
)?
}
Arm64RelTy::R_AARCH64_MOVW_UABS_G3 => {
check_overflow = false;
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
48,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVKZ,
)?
}
Arm64RelTy::R_AARCH64_MOVW_SABS_G0 => self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
0,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ,
)?,
Arm64RelTy::R_AARCH64_MOVW_SABS_G1 => self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
16,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ,
)?,
Arm64RelTy::R_AARCH64_MOVW_SABS_G2 => self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
32,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ,
)?,
Arm64RelTy::R_AARCH64_MOVW_PREL_G0_NC | Arm64RelTy::R_AARCH64_MOVW_PREL_G0 => {
let mut imm_type = Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ;
if *self == Arm64RelTy::R_AARCH64_MOVW_PREL_G0_NC {
check_overflow = false;
imm_type = Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVKZ;
}
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
0,
imm_type,
)?
}
Arm64RelTy::R_AARCH64_MOVW_PREL_G1_NC | Arm64RelTy::R_AARCH64_MOVW_PREL_G1 => {
let mut imm_type = Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ;
if *self == Arm64RelTy::R_AARCH64_MOVW_PREL_G1_NC {
check_overflow = false;
imm_type = Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVKZ;
}
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
16,
imm_type,
)?
}
Arm64RelTy::R_AARCH64_MOVW_PREL_G2_NC | Arm64RelTy::R_AARCH64_MOVW_PREL_G2 => {
let mut imm_type = Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ;
if *self == Arm64RelTy::R_AARCH64_MOVW_PREL_G2_NC {
check_overflow = false;
imm_type = Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVKZ;
}
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
32,
imm_type,
)?
}
Arm64RelTy::R_AARCH64_MOVW_PREL_G3 => {
check_overflow = false;
self.reloc_insn_movw(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
48,
Aarch64InsnMovwImmType::AARCH64_INSN_IMM_MOVNZ,
)?
}
Arm64RelTy::R_AARCH64_LD_PREL_LO19 => self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
2,
19,
Aarch64InsnImmType::AARCH64_INSN_IMM_19,
)?,
Arm64RelTy::R_AARCH64_ADR_PREL_LO21 => self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
0,
21,
Aarch64InsnImmType::AARCH64_INSN_IMM_ADR,
)?,
Arm64RelTy::R_AARCH64_ADR_PREL_PG_HI21_NC | Arm64RelTy::R_AARCH64_ADR_PREL_PG_HI21 => {
if *self == Arm64RelTy::R_AARCH64_ADR_PREL_PG_HI21_NC {
check_overflow = false;
}
self.reloc_insn_adrp(location, address)?
}
Arm64RelTy::R_AARCH64_ADD_ABS_LO12_NC | Arm64RelTy::R_AARCH64_LDST8_ABS_LO12_NC => {
check_overflow = false;
self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
0,
12,
Aarch64InsnImmType::AARCH64_INSN_IMM_12,
)?
}
Arm64RelTy::R_AARCH64_LDST16_ABS_LO12_NC => {
check_overflow = false;
self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
1,
11,
Aarch64InsnImmType::AARCH64_INSN_IMM_12,
)?
}
Arm64RelTy::R_AARCH64_LDST32_ABS_LO12_NC => {
check_overflow = false;
self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
2,
10,
Aarch64InsnImmType::AARCH64_INSN_IMM_12,
)?
}
Arm64RelTy::R_AARCH64_LDST64_ABS_LO12_NC => {
check_overflow = false;
self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
3,
9,
Aarch64InsnImmType::AARCH64_INSN_IMM_12,
)?
}
Arm64RelTy::R_AARCH64_LDST128_ABS_LO12_NC => {
check_overflow = false;
self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_ABS,
location,
address,
4,
8,
Aarch64InsnImmType::AARCH64_INSN_IMM_12,
)?
}
Arm64RelTy::R_AARCH64_TSTBR14 => self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
2,
14,
Aarch64InsnImmType::AARCH64_INSN_IMM_14,
)?,
Arm64RelTy::R_AARCH64_CONDBR19 => self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
2,
19,
Aarch64InsnImmType::AARCH64_INSN_IMM_19,
)?,
Arm64RelTy::R_AARCH64_JUMP26 | Arm64RelTy::R_AARCH64_CALL26 => {
let ovf = self.reloc_insn_imm(
Aarch64RelocOp::RELOC_OP_PREL,
location,
address,
2,
26,
Aarch64InsnImmType::AARCH64_INSN_IMM_26,
)?;
if ovf {
unimplemented!(
"Veneer emission for out-of-range AArch64 JUMP26/CALL26 not implemented"
);
}
ovf
}
_ => {
log::error!("Relocation type {:?} not implemented yet", self);
return Err(ModuleErr::ENOEXEC);
}
};
if check_overflow && ovf {
log::error!("Overflow detected during relocation type {:?}", self);
return Err(ModuleErr::ENOEXEC);
}
Ok(())
}
}
pub struct ArchRelocate;
#[allow(unused_assignments)]
impl ArchRelocate {
pub fn apply_relocate_add<H: KernelModuleHelper>(
rela_list: &[goblin::elf64::reloc::Rela],
rel_section: &SectionHeader,
sechdrs: &[SectionHeader],
load_info: &ModuleLoadInfo,
module: &ModuleOwner<H>,
) -> Result<()> {
for rela in rela_list {
let rel_type = get_rela_type(rela.r_info);
let sym_idx = get_rela_sym_idx(rela.r_info);
let location = sechdrs[rel_section.sh_info as usize].sh_addr + rela.r_offset;
let (sym, sym_name) = &load_info.syms[sym_idx];
let reloc_type = Arm64RelTy::try_from(rel_type).map_err(|_| {
log::error!(
"[{:?}]: Invalid relocation type: {}",
module.name(),
rel_type
);
ModuleErr::ENOEXEC
})?;
let target_addr = sym.st_value.wrapping_add(rela.r_addend as u64);
log::info!(
"[{:?}]: Applying relocation {:?} at location {:#x} with target addr {:#x}",
module.name(),
reloc_type,
location,
target_addr
);
let res = reloc_type.apply_relocation(location, target_addr);
match res {
Err(e) => {
log::error!("[{:?}]: ({}) {:?}", module.name(), sym_name, e);
return Err(e);
}
Ok(_) => { }
}
}
Ok(())
}
}
pub fn module_frob_arch_sections<H: KernelModuleHelper>(
elf: &mut Elf,
owner: &mut ModuleOwner<H>,
) -> Result<()> {
Ok(())
}