use goblin::elf::{Elf, SectionHeader};
use int_enum::IntEnum;
use crate::{
ModuleErr, Result,
arch::{Ptr, get_rela_sym_idx, get_rela_type},
loader::{KernelModuleHelper, ModuleLoadInfo, ModuleOwner},
};
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct ModuleArchSpecific {}
#[repr(u32)]
#[derive(Debug, Clone, Copy, IntEnum)]
#[allow(non_camel_case_types)]
pub enum ArchRelocationType {
R_X86_64_NONE = 0,
R_X86_64_64 = 1,
R_X86_64_PC32 = 2,
R_X86_64_GOT32 = 3,
R_X86_64_PLT32 = 4,
R_X86_64_COPY = 5,
R_X86_64_GLOB_DAT = 6,
R_X86_64_JUMP_SLOT = 7,
R_X86_64_RELATIVE = 8,
R_X86_64_GOTPCREL = 9,
R_X86_64_32 = 10,
R_X86_64_32S = 11,
R_X86_64_16 = 12,
R_X86_64_PC16 = 13,
R_X86_64_8 = 14,
R_X86_64_PC8 = 15,
R_X86_64_PC64 = 24,
}
type X64RelTy = ArchRelocationType;
impl ArchRelocationType {
fn apply_relocation(&self, location: u64, mut target_addr: u64) -> Result<()> {
let size;
let location = Ptr(location);
let overflow = || {
log::error!(
"overflow in relocation type {:?}, target address {:#x}",
self,
target_addr
);
log::error!("module likely not compiled with -mcmodel=kernel");
ModuleErr::ENOEXEC
};
match self {
X64RelTy::R_X86_64_NONE => return Ok(()),
X64RelTy::R_X86_64_64 => {
size = 8;
}
X64RelTy::R_X86_64_32 => {
if target_addr != target_addr as u32 as u64 {
return Err(overflow());
}
size = 4;
}
X64RelTy::R_X86_64_32S => {
if (target_addr as i64) != ((target_addr as i32) as i64) {
return Err(overflow());
}
size = 4;
}
X64RelTy::R_X86_64_PC32 | X64RelTy::R_X86_64_PLT32 => {
target_addr = target_addr.wrapping_sub(location.0);
size = 4;
}
X64RelTy::R_X86_64_PC64 => {
target_addr = target_addr.wrapping_sub(location.0);
size = 8;
}
_ => {
log::error!("x86/modules: Unsupported relocation type: {:?}", self);
return Err(ModuleErr::ENOEXEC);
}
}
if location.as_slice::<u8>(size).iter().any(|&b| b != 0) {
log::error!(
"x86/modules: Invalid relocation target, existing value is nonzero for type {:?}, loc: {:#x}, value: {:#x}",
self,
location.0,
target_addr
);
return Err(ModuleErr::ENOEXEC);
} else {
match size {
4 => location.write::<u32>(target_addr as u32),
8 => location.write::<u64>(target_addr),
_ => unreachable!(),
}
}
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 = ArchRelocationType::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(())
}