use crate::elf::elf_types::ElfMachine;
pub const R_X86_64_NONE: u32 = 0;
pub const R_X86_64_64: u32 = 1;
pub const R_X86_64_PC32: u32 = 2;
pub const R_X86_64_GOT32: u32 = 3;
pub const R_X86_64_PLT32: u32 = 4;
pub const R_X86_64_COPY: u32 = 5;
pub const R_X86_64_GLOB_DAT: u32 = 6;
pub const R_X86_64_JUMP_SLOT: u32 = 7;
pub const R_X86_64_RELATIVE: u32 = 8;
pub const R_X86_64_GOTPCREL: u32 = 9;
pub const R_X86_64_32: u32 = 10;
pub const R_X86_64_32S: u32 = 11;
pub const R_X86_64_16: u32 = 12;
pub const R_X86_64_PC16: u32 = 13;
pub const R_X86_64_8: u32 = 14;
pub const R_X86_64_PC8: u32 = 15;
pub const R_X86_64_PC64: u32 = 24;
pub const R_X86_64_GOTOFF64: u32 = 25;
pub const R_X86_64_GOTPC32: u32 = 26;
pub const R_X86_64_SIZE32: u32 = 32;
pub const R_X86_64_SIZE64: u32 = 33;
pub const R_X86_64_GOTPC32_TLSDESC: u32 = 34;
pub const R_X86_64_TLSDESC_CALL: u32 = 35;
pub const R_X86_64_TLSDESC: u32 = 36;
pub const R_AARCH64_NONE: u32 = 0;
pub const R_AARCH64_ABS64: u32 = 257;
pub const R_AARCH64_ABS32: u32 = 258;
pub const R_AARCH64_ABS16: u32 = 259;
pub const R_AARCH64_PREL64: u32 = 260;
pub const R_AARCH64_PREL32: u32 = 261;
pub const R_AARCH64_PREL16: u32 = 262;
pub const R_AARCH64_ADR_PREL_PG_HI21: u32 = 275;
pub const R_AARCH64_ADD_ABS_LO12_NC: u32 = 277;
pub const R_AARCH64_LDST8_ABS_LO12_NC: u32 = 278;
pub const R_AARCH64_TSTBR14: u32 = 279;
pub const R_AARCH64_CONDBR19: u32 = 280;
pub const R_AARCH64_JUMP26: u32 = 282;
pub const R_AARCH64_CALL26: u32 = 283;
pub const R_AARCH64_LDST64_ABS_LO12_NC: u32 = 286;
pub const R_AARCH64_ADR_GOT_PAGE: u32 = 311;
pub const R_AARCH64_LD64_GOT_LO12_NC: u32 = 312;
pub const R_ARM_NONE: u32 = 0;
pub const R_ARM_ABS32: u32 = 2;
pub const R_ARM_REL32: u32 = 3;
pub const R_ARM_THM_CALL: u32 = 10;
pub const R_ARM_CALL: u32 = 28;
pub const R_ARM_JUMP24: u32 = 29;
pub const R_ARM_THM_JUMP24: u32 = 30;
pub const R_ARM_PREL31: u32 = 42;
pub const R_ARM_MOVW_ABS_NC: u32 = 43;
pub const R_ARM_MOVT_ABS: u32 = 44;
pub const R_ARM_THM_MOVW_ABS_NC: u32 = 47;
pub const R_ARM_THM_MOVT_ABS: u32 = 48;
pub const R_RISCV_NONE: u32 = 0;
pub const R_RISCV_32: u32 = 1;
pub const R_RISCV_64: u32 = 2;
pub const R_RISCV_RELATIVE: u32 = 3;
pub const R_RISCV_COPY: u32 = 4;
pub const R_RISCV_JUMP_SLOT: u32 = 5;
pub const R_RISCV_BRANCH: u32 = 16;
pub const R_RISCV_JAL: u32 = 17;
pub const R_RISCV_CALL: u32 = 18;
pub const R_RISCV_CALL_PLT: u32 = 19;
pub const R_RISCV_GOT_HI20: u32 = 20;
pub const R_RISCV_TLS_GOT_HI20: u32 = 21;
pub const R_RISCV_TLS_GD_HI20: u32 = 22;
pub const R_RISCV_PCREL_HI20: u32 = 23;
pub const R_RISCV_PCREL_LO12_I: u32 = 24;
pub const R_RISCV_PCREL_LO12_S: u32 = 25;
pub const R_RISCV_HI20: u32 = 26;
pub const R_RISCV_LO12_I: u32 = 27;
pub const R_RISCV_LO12_S: u32 = 28;
pub const R_RISCV_TPREL_HI20: u32 = 29;
pub const R_RISCV_TPREL_LO12_I: u32 = 30;
pub const R_RISCV_TPREL_LO12_S: u32 = 31;
pub const R_RISCV_TPREL_ADD: u32 = 32;
pub const R_RISCV_ADD8: u32 = 33;
pub const R_RISCV_ADD16: u32 = 34;
pub const R_RISCV_ADD32: u32 = 35;
pub const R_RISCV_ADD64: u32 = 36;
pub const R_RISCV_SUB8: u32 = 37;
pub const R_RISCV_SUB16: u32 = 38;
pub const R_RISCV_SUB32: u32 = 39;
pub const R_RISCV_SUB64: u32 = 40;
pub const R_RISCV_ALIGN: u32 = 43;
pub const R_RISCV_RVC_BRANCH: u32 = 44;
pub const R_RISCV_RVC_JUMP: u32 = 45;
pub const R_RISCV_RELAX: u32 = 51;
pub const R_RISCV_SET6: u32 = 54;
pub const R_RISCV_SET8: u32 = 55;
pub const R_RISCV_SET16: u32 = 56;
pub const R_RISCV_SET32: u32 = 57;
pub const R_RISCV_32_PCREL: u32 = 58;
pub struct ElfRelocationHandler;
impl ElfRelocationHandler {
pub fn type_name(machine: ElfMachine, rel_type: u32) -> &'static str {
match machine {
ElfMachine::X86_64 => Self::x86_64_type_name(rel_type),
ElfMachine::AArch64 => Self::aarch64_type_name(rel_type),
ElfMachine::ARM => Self::arm_type_name(rel_type),
ElfMachine::RiscV => Self::riscv_type_name(rel_type),
ElfMachine::X86 => Self::x86_type_name(rel_type),
_ => "R_UNKNOWN",
}
}
fn x86_64_type_name(t: u32) -> &'static str {
match t {
R_X86_64_NONE => "R_X86_64_NONE",
R_X86_64_64 => "R_X86_64_64",
R_X86_64_PC32 => "R_X86_64_PC32",
R_X86_64_GOT32 => "R_X86_64_GOT32",
R_X86_64_PLT32 => "R_X86_64_PLT32",
R_X86_64_COPY => "R_X86_64_COPY",
R_X86_64_GLOB_DAT => "R_X86_64_GLOB_DAT",
R_X86_64_JUMP_SLOT => "R_X86_64_JUMP_SLOT",
R_X86_64_RELATIVE => "R_X86_64_RELATIVE",
R_X86_64_GOTPCREL => "R_X86_64_GOTPCREL",
R_X86_64_32 => "R_X86_64_32",
R_X86_64_32S => "R_X86_64_32S",
R_X86_64_16 => "R_X86_64_16",
R_X86_64_PC16 => "R_X86_64_PC16",
R_X86_64_8 => "R_X86_64_8",
R_X86_64_PC8 => "R_X86_64_PC8",
R_X86_64_PC64 => "R_X86_64_PC64",
R_X86_64_GOTOFF64 => "R_X86_64_GOTOFF64",
R_X86_64_GOTPC32 => "R_X86_64_GOTPC32",
R_X86_64_SIZE32 => "R_X86_64_SIZE32",
R_X86_64_SIZE64 => "R_X86_64_SIZE64",
R_X86_64_GOTPC32_TLSDESC => "R_X86_64_GOTPC32_TLSDESC",
R_X86_64_TLSDESC_CALL => "R_X86_64_TLSDESC_CALL",
R_X86_64_TLSDESC => "R_X86_64_TLSDESC",
_ => "R_X86_64_UNKNOWN",
}
}
fn aarch64_type_name(t: u32) -> &'static str {
match t {
R_AARCH64_NONE => "R_AARCH64_NONE",
R_AARCH64_ABS64 => "R_AARCH64_ABS64",
R_AARCH64_ABS32 => "R_AARCH64_ABS32",
R_AARCH64_ABS16 => "R_AARCH64_ABS16",
R_AARCH64_PREL64 => "R_AARCH64_PREL64",
R_AARCH64_PREL32 => "R_AARCH64_PREL32",
R_AARCH64_PREL16 => "R_AARCH64_PREL16",
R_AARCH64_ADR_PREL_PG_HI21 => "R_AARCH64_ADR_PREL_PG_HI21",
R_AARCH64_ADD_ABS_LO12_NC => "R_AARCH64_ADD_ABS_LO12_NC",
R_AARCH64_LDST8_ABS_LO12_NC => "R_AARCH64_LDST8_ABS_LO12_NC",
R_AARCH64_TSTBR14 => "R_AARCH64_TSTBR14",
R_AARCH64_CONDBR19 => "R_AARCH64_CONDBR19",
R_AARCH64_JUMP26 => "R_AARCH64_JUMP26",
R_AARCH64_CALL26 => "R_AARCH64_CALL26",
R_AARCH64_LDST64_ABS_LO12_NC => "R_AARCH64_LDST64_ABS_LO12_NC",
R_AARCH64_ADR_GOT_PAGE => "R_AARCH64_ADR_GOT_PAGE",
R_AARCH64_LD64_GOT_LO12_NC => "R_AARCH64_LD64_GOT_LO12_NC",
_ => "R_AARCH64_UNKNOWN",
}
}
fn arm_type_name(t: u32) -> &'static str {
match t {
R_ARM_NONE => "R_ARM_NONE",
R_ARM_ABS32 => "R_ARM_ABS32",
R_ARM_REL32 => "R_ARM_REL32",
R_ARM_THM_CALL => "R_ARM_THM_CALL",
R_ARM_CALL => "R_ARM_CALL",
R_ARM_JUMP24 => "R_ARM_JUMP24",
R_ARM_THM_JUMP24 => "R_ARM_THM_JUMP24",
R_ARM_PREL31 => "R_ARM_PREL31",
R_ARM_MOVW_ABS_NC => "R_ARM_MOVW_ABS_NC",
R_ARM_MOVT_ABS => "R_ARM_MOVT_ABS",
R_ARM_THM_MOVW_ABS_NC => "R_ARM_THM_MOVW_ABS_NC",
R_ARM_THM_MOVT_ABS => "R_ARM_THM_MOVT_ABS",
_ => "R_ARM_UNKNOWN",
}
}
fn riscv_type_name(t: u32) -> &'static str {
match t {
R_RISCV_NONE => "R_RISCV_NONE",
R_RISCV_32 => "R_RISCV_32",
R_RISCV_64 => "R_RISCV_64",
R_RISCV_RELATIVE => "R_RISCV_RELATIVE",
R_RISCV_COPY => "R_RISCV_COPY",
R_RISCV_JUMP_SLOT => "R_RISCV_JUMP_SLOT",
R_RISCV_BRANCH => "R_RISCV_BRANCH",
R_RISCV_JAL => "R_RISCV_JAL",
R_RISCV_CALL => "R_RISCV_CALL",
R_RISCV_CALL_PLT => "R_RISCV_CALL_PLT",
R_RISCV_GOT_HI20 => "R_RISCV_GOT_HI20",
R_RISCV_TLS_GOT_HI20 => "R_RISCV_TLS_GOT_HI20",
R_RISCV_TLS_GD_HI20 => "R_RISCV_TLS_GD_HI20",
R_RISCV_PCREL_HI20 => "R_RISCV_PCREL_HI20",
R_RISCV_PCREL_LO12_I => "R_RISCV_PCREL_LO12_I",
R_RISCV_PCREL_LO12_S => "R_RISCV_PCREL_LO12_S",
R_RISCV_HI20 => "R_RISCV_HI20",
R_RISCV_LO12_I => "R_RISCV_LO12_I",
R_RISCV_LO12_S => "R_RISCV_LO12_S",
R_RISCV_TPREL_HI20 => "R_RISCV_TPREL_HI20",
R_RISCV_TPREL_LO12_I => "R_RISCV_TPREL_LO12_I",
R_RISCV_TPREL_LO12_S => "R_RISCV_TPREL_LO12_S",
R_RISCV_TPREL_ADD => "R_RISCV_TPREL_ADD",
R_RISCV_ADD8 => "R_RISCV_ADD8",
R_RISCV_ADD16 => "R_RISCV_ADD16",
R_RISCV_ADD32 => "R_RISCV_ADD32",
R_RISCV_ADD64 => "R_RISCV_ADD64",
R_RISCV_SUB8 => "R_RISCV_SUB8",
R_RISCV_SUB16 => "R_RISCV_SUB16",
R_RISCV_SUB32 => "R_RISCV_SUB32",
R_RISCV_SUB64 => "R_RISCV_SUB64",
R_RISCV_ALIGN => "R_RISCV_ALIGN",
R_RISCV_RVC_BRANCH => "R_RISCV_RVC_BRANCH",
R_RISCV_RVC_JUMP => "R_RISCV_RVC_JUMP",
R_RISCV_RELAX => "R_RISCV_RELAX",
R_RISCV_SET6 => "R_RISCV_SET6",
R_RISCV_SET8 => "R_RISCV_SET8",
R_RISCV_SET16 => "R_RISCV_SET16",
R_RISCV_SET32 => "R_RISCV_SET32",
R_RISCV_32_PCREL => "R_RISCV_32_PCREL",
_ => "R_RISCV_UNKNOWN",
}
}
fn x86_type_name(t: u32) -> &'static str {
match t {
0 => "R_386_NONE",
1 => "R_386_32",
2 => "R_386_PC32",
3 => "R_386_GOT32",
4 => "R_386_PLT32",
5 => "R_386_COPY",
6 => "R_386_GLOB_DAT",
7 => "R_386_JUMP_SLOT",
8 => "R_386_RELATIVE",
9 => "R_386_GOTOFF",
10 => "R_386_GOTPC",
_ => "R_386_UNKNOWN",
}
}
pub fn is_pc_relative(machine: ElfMachine, rel_type: u32) -> bool {
match machine {
ElfMachine::X86_64 => Self::x86_64_is_pc_relative(rel_type),
ElfMachine::AArch64 => Self::aarch64_is_pc_relative(rel_type),
ElfMachine::ARM => Self::arm_is_pc_relative(rel_type),
ElfMachine::RiscV => Self::riscv_is_pc_relative(rel_type),
ElfMachine::X86 => Self::x86_is_pc_relative(rel_type),
_ => false,
}
}
fn x86_64_is_pc_relative(t: u32) -> bool {
matches!(
t,
R_X86_64_PC32
| R_X86_64_PC16
| R_X86_64_PC8
| R_X86_64_PC64
| R_X86_64_GOTPCREL
| R_X86_64_GOTPC32
| R_X86_64_PLT32
| R_X86_64_GOTPC32_TLSDESC
| R_X86_64_TLSDESC_CALL
)
}
fn aarch64_is_pc_relative(t: u32) -> bool {
matches!(
t,
R_AARCH64_PREL64
| R_AARCH64_PREL32
| R_AARCH64_PREL16
| R_AARCH64_CALL26
| R_AARCH64_JUMP26
| R_AARCH64_ADR_PREL_PG_HI21
| R_AARCH64_CONDBR19
| R_AARCH64_TSTBR14
| R_AARCH64_ADR_GOT_PAGE
)
}
fn arm_is_pc_relative(t: u32) -> bool {
matches!(
t,
R_ARM_REL32
| R_ARM_CALL
| R_ARM_JUMP24
| R_ARM_THM_CALL
| R_ARM_THM_JUMP24
| R_ARM_PREL31
)
}
fn riscv_is_pc_relative(t: u32) -> bool {
matches!(
t,
R_RISCV_BRANCH
| R_RISCV_JAL
| R_RISCV_CALL
| R_RISCV_CALL_PLT
| R_RISCV_PCREL_HI20
| R_RISCV_PCREL_LO12_I
| R_RISCV_PCREL_LO12_S
| R_RISCV_RVC_BRANCH
| R_RISCV_RVC_JUMP
| R_RISCV_32_PCREL
)
}
fn x86_is_pc_relative(t: u32) -> bool {
matches!(t, 2 | 4 | 10) }
pub fn relocation_size(machine: ElfMachine, rel_type: u32) -> u8 {
match machine {
ElfMachine::X86_64 => Self::x86_64_relocation_size(rel_type),
ElfMachine::AArch64 => Self::aarch64_relocation_size(rel_type),
ElfMachine::ARM => Self::arm_relocation_size(rel_type),
ElfMachine::RiscV => Self::riscv_relocation_size(rel_type),
ElfMachine::X86 => Self::x86_relocation_size(rel_type),
_ => 0,
}
}
fn x86_64_relocation_size(t: u32) -> u8 {
match t {
R_X86_64_64 | R_X86_64_PC64 | R_X86_64_GOTOFF64 | R_X86_64_SIZE64 => 8,
R_X86_64_32 | R_X86_64_32S | R_X86_64_PC32 | R_X86_64_GOT32 | R_X86_64_PLT32
| R_X86_64_GOTPCREL | R_X86_64_GOTPC32 | R_X86_64_SIZE32 => 4,
R_X86_64_16 | R_X86_64_PC16 => 2,
R_X86_64_8 | R_X86_64_PC8 => 1,
_ => 0,
}
}
fn aarch64_relocation_size(t: u32) -> u8 {
match t {
R_AARCH64_ABS64 | R_AARCH64_PREL64 => 8,
R_AARCH64_ABS32 | R_AARCH64_PREL32 => 4,
R_AARCH64_ABS16 | R_AARCH64_PREL16 => 2,
R_AARCH64_CALL26
| R_AARCH64_JUMP26
| R_AARCH64_ADR_PREL_PG_HI21
| R_AARCH64_ADD_ABS_LO12_NC
| R_AARCH64_LDST8_ABS_LO12_NC
| R_AARCH64_LDST64_ABS_LO12_NC
| R_AARCH64_CONDBR19
| R_AARCH64_TSTBR14
| R_AARCH64_ADR_GOT_PAGE
| R_AARCH64_LD64_GOT_LO12_NC => 4,
_ => 0,
}
}
fn arm_relocation_size(t: u32) -> u8 {
match t {
R_ARM_ABS32 | R_ARM_REL32 => 4,
R_ARM_CALL | R_ARM_JUMP24 | R_ARM_THM_CALL | R_ARM_THM_JUMP24 => 4,
R_ARM_MOVW_ABS_NC | R_ARM_MOVT_ABS | R_ARM_THM_MOVW_ABS_NC | R_ARM_THM_MOVT_ABS => 4,
R_ARM_PREL31 => 4,
_ => 0,
}
}
fn riscv_relocation_size(t: u32) -> u8 {
match t {
R_RISCV_64 | R_RISCV_ADD64 | R_RISCV_SUB64 => 8,
R_RISCV_32 | R_RISCV_ADD32 | R_RISCV_SUB32 | R_RISCV_32_PCREL | R_RISCV_SET32 => 4,
R_RISCV_ADD16 | R_RISCV_SUB16 | R_RISCV_SET16 => 2,
R_RISCV_ADD8 | R_RISCV_SUB8 | R_RISCV_SET8 => 1,
R_RISCV_BRANCH | R_RISCV_JAL | R_RISCV_CALL | R_RISCV_CALL_PLT | R_RISCV_PCREL_HI20
| R_RISCV_PCREL_LO12_I | R_RISCV_PCREL_LO12_S | R_RISCV_HI20 | R_RISCV_LO12_I
| R_RISCV_LO12_S | R_RISCV_GOT_HI20 | R_RISCV_TPREL_HI20 | R_RISCV_TPREL_LO12_I
| R_RISCV_TPREL_LO12_S | R_RISCV_TPREL_ADD | R_RISCV_TLS_GOT_HI20
| R_RISCV_TLS_GD_HI20 => 4,
R_RISCV_RVC_BRANCH | R_RISCV_RVC_JUMP => 2,
_ => 0,
}
}
fn x86_relocation_size(t: u32) -> u8 {
match t {
1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 10 => 4, 0 => 0,
_ => 0,
}
}
pub fn apply_relocation(
machine: ElfMachine,
rel_type: u32,
buf: &mut [u8],
offset: u64,
symbol_value: u64,
addend: i64,
pc_offset: u64,
) -> Result<(), String> {
match machine {
ElfMachine::X86_64 => {
Self::apply_x86_64(rel_type, buf, offset, symbol_value, addend, pc_offset)
}
ElfMachine::AArch64 => {
Self::apply_aarch64(rel_type, buf, offset, symbol_value, addend, pc_offset)
}
ElfMachine::ARM => {
Self::apply_arm(rel_type, buf, offset, symbol_value, addend, pc_offset)
}
ElfMachine::RiscV => {
Self::apply_riscv(rel_type, buf, offset, symbol_value, addend, pc_offset)
}
ElfMachine::X86 => {
Self::apply_x86(rel_type, buf, offset, symbol_value, addend, pc_offset)
}
_ => Err(format!("Unsupported machine for relocation: {:?}", machine)),
}
}
fn apply_x86_64(
rel_type: u32,
buf: &mut [u8],
offset: u64,
symbol_value: u64,
addend: i64,
pc_offset: u64,
) -> Result<(), String> {
let off = offset as usize;
let value = (symbol_value as i64).wrapping_add(addend);
match rel_type {
R_X86_64_NONE => Ok(()),
R_X86_64_64 => {
let result = value as u64;
Self::check_bounds(buf, off, 8)?;
buf[off..off + 8].copy_from_slice(&result.to_le_bytes());
Ok(())
}
R_X86_64_PC32 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let truncated = result as i32;
if truncated as i64 != result {
return Err(format!(
"R_X86_64_PC32 relocation overflow: value={}, pc={}",
value, pc
));
}
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(truncated as u32).to_le_bytes());
Ok(())
}
R_X86_64_32 => {
let result = value as u64;
if result > 0xFFFFFFFF {
return Err(format!(
"R_X86_64_32 relocation overflow: value 0x{:X} does not fit in 32 bits",
result
));
}
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(result as u32).to_le_bytes());
Ok(())
}
R_X86_64_32S => {
let value = ((symbol_value as u32 as i32) as i64).wrapping_add(addend);
if value < i32::MIN as i64 || value > i32::MAX as i64 {
return Err(format!(
"R_X86_64_32S relocation overflow: value {} does not fit in i32",
value
));
}
let truncated = value as i32;
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(truncated as u32).to_le_bytes());
Ok(())
}
R_X86_64_16 => {
let result = value as u64;
if result > 0xFFFF {
return Err(format!(
"R_X86_64_16 relocation overflow: value 0x{:X}",
result
));
}
Self::check_bounds(buf, off, 2)?;
buf[off..off + 2].copy_from_slice(&(result as u16).to_le_bytes());
Ok(())
}
R_X86_64_PC16 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let truncated = result as i16;
if truncated as i64 != result {
return Err(format!("R_X86_64_PC16 relocation overflow"));
}
Self::check_bounds(buf, off, 2)?;
buf[off..off + 2].copy_from_slice(&(truncated as u16).to_le_bytes());
Ok(())
}
R_X86_64_8 => {
let result = value as u64;
if result > 0xFF {
return Err(format!(
"R_X86_64_8 relocation overflow: value 0x{:X}",
result
));
}
Self::check_bounds(buf, off, 1)?;
buf[off] = result as u8;
Ok(())
}
R_X86_64_PC8 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let truncated = result as i8;
if truncated as i64 != result {
return Err(format!("R_X86_64_PC8 relocation overflow"));
}
Self::check_bounds(buf, off, 1)?;
buf[off] = truncated as u8;
Ok(())
}
R_X86_64_PC64 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
Self::check_bounds(buf, off, 8)?;
buf[off..off + 8].copy_from_slice(&(result as u64).to_le_bytes());
Ok(())
}
R_X86_64_GOTPCREL | R_X86_64_GOTPC32 | R_X86_64_PLT32 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let truncated = result as i32;
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(truncated as u32).to_le_bytes());
Ok(())
}
_ => Err(format!("Unsupported X86-64 relocation type: {}", rel_type)),
}
}
fn apply_aarch64(
rel_type: u32,
buf: &mut [u8],
offset: u64,
symbol_value: u64,
addend: i64,
pc_offset: u64,
) -> Result<(), String> {
let off = offset as usize;
let value = (symbol_value as i64).wrapping_add(addend);
match rel_type {
R_AARCH64_NONE => Ok(()),
R_AARCH64_ABS64 => {
Self::check_bounds(buf, off, 8)?;
buf[off..off + 8].copy_from_slice(&(value as u64).to_le_bytes());
Ok(())
}
R_AARCH64_ABS32 => {
let result = value as u64;
if result > 0xFFFFFFFF {
return Err(format!(
"R_AARCH64_ABS32 relocation overflow: 0x{:X}",
result
));
}
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(result as u32).to_le_bytes());
Ok(())
}
R_AARCH64_ABS16 => {
let result = value as u64;
if result > 0xFFFF {
return Err(format!(
"R_AARCH64_ABS16 relocation overflow: 0x{:X}",
result
));
}
Self::check_bounds(buf, off, 2)?;
buf[off..off + 2].copy_from_slice(&(result as u16).to_le_bytes());
Ok(())
}
R_AARCH64_PREL64 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
Self::check_bounds(buf, off, 8)?;
buf[off..off + 8].copy_from_slice(&(result as u64).to_le_bytes());
Ok(())
}
R_AARCH64_PREL32 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let truncated = result as i32;
if truncated as i64 != result {
return Err(format!("R_AARCH64_PREL32 relocation overflow"));
}
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(truncated as u32).to_le_bytes());
Ok(())
}
R_AARCH64_PREL16 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let truncated = result as i16;
if truncated as i64 != result {
return Err(format!("R_AARCH64_PREL16 relocation overflow"));
}
Self::check_bounds(buf, off, 2)?;
buf[off..off + 2].copy_from_slice(&(truncated as u16).to_le_bytes());
Ok(())
}
R_AARCH64_CALL26 | R_AARCH64_JUMP26 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
if result & 0x3 != 0 {
return Err(format!(
"R_AARCH64_CALL26/JUMP26 target not 4-byte aligned: offset={}",
result
));
}
let offset_28 = result >> 2;
if offset_28 < -(1 << 27) || offset_28 >= (1 << 27) {
return Err(format!(
"R_AARCH64_CALL26/JUMP26 relocation out of range: offset={}",
result
));
}
let imm26 = (offset_28 as u32) & 0x03FFFFFF;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0xFC000000) | imm26;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_AARCH64_ADR_PREL_PG_HI21 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let page = result >> 12;
if page < -(1 << 20) || page >= (1 << 20) {
return Err(format!(
"R_AARCH64_ADR_PREL_PG_HI21 relocation out of range: offset={}",
result
));
}
let immhi = ((page as u32) >> 2) & 0x7FFFF;
let immlo = (page as u32) & 0x3;
let encoded = (immlo << 29) | (immhi << 5);
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0x9F00001F) | encoded;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
_ => Err(format!("Unsupported AArch64 relocation type: {}", rel_type)),
}
}
fn apply_arm(
rel_type: u32,
buf: &mut [u8],
offset: u64,
symbol_value: u64,
addend: i64,
pc_offset: u64,
) -> Result<(), String> {
let off = offset as usize;
let value = (symbol_value as i64).wrapping_add(addend);
match rel_type {
R_ARM_NONE => Ok(()),
R_ARM_ABS32 => {
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(value as u32).to_le_bytes());
Ok(())
}
R_ARM_REL32 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(result as u32).to_le_bytes());
Ok(())
}
R_ARM_CALL | R_ARM_JUMP24 => {
let pc = pc_offset as i64 + 8;
let result = value.wrapping_sub(pc);
if result & 0x3 != 0 {
return Err(format!("R_ARM_CALL target not 4-byte aligned"));
}
let offset_24 = result >> 2;
if offset_24 < -(1 << 23) || offset_24 >= (1 << 23) {
return Err(format!("R_ARM_CALL relocation out of range"));
}
let imm24 = (offset_24 as u32) & 0x00FFFFFF;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0xFF000000) | imm24;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_ARM_MOVW_ABS_NC => {
let imm16 = (value as u32) & 0xFFFF;
let imm4 = (value as u32 >> 16) & 0xF;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0xFFF0F000) | (imm4 << 16) | imm16;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_ARM_MOVT_ABS => {
let imm16 = ((value as u32) >> 16) & 0xFFFF;
let imm4 = ((value as u32) >> 28) & 0xF;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0xFFF0F000) | (imm4 << 16) | imm16;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
_ => Err(format!("Unsupported ARM32 relocation type: {}", rel_type)),
}
}
fn apply_riscv(
rel_type: u32,
buf: &mut [u8],
offset: u64,
symbol_value: u64,
addend: i64,
pc_offset: u64,
) -> Result<(), String> {
let off = offset as usize;
let value = (symbol_value as i64).wrapping_add(addend);
match rel_type {
R_RISCV_NONE => Ok(()),
R_RISCV_32 => {
let result = value as u32;
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&result.to_le_bytes());
Ok(())
}
R_RISCV_64 => {
Self::check_bounds(buf, off, 8)?;
buf[off..off + 8].copy_from_slice(&(value as u64).to_le_bytes());
Ok(())
}
R_RISCV_BRANCH => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
if result & 0x1 != 0 {
return Err(format!("R_RISCV_BRANCH target not 2-byte aligned"));
}
let offset_12 = result >> 1;
if offset_12 < -(1 << 11) || offset_12 >= (1 << 11) {
return Err(format!("R_RISCV_BRANCH relocation out of range"));
}
let imm = offset_12 as u32 & 0xFFF;
let encoded = ((imm >> 12) & 0x1) << 31
| ((imm >> 5) & 0x3F) << 25
| ((imm >> 1) & 0xF) << 8
| ((imm >> 11) & 0x1) << 7;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0x1FFF07F) | encoded;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_RISCV_JAL => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
if result & 0x1 != 0 {
return Err(format!("R_RISCV_JAL target not 2-byte aligned"));
}
let offset_20 = result >> 1;
if offset_20 < -(1 << 19) || offset_20 >= (1 << 19) {
return Err(format!("R_RISCV_JAL relocation out of range"));
}
let imm = offset_20 as u32 & 0xFFFFF;
let encoded = ((imm >> 20) & 0x1) << 31
| ((imm >> 1) & 0x3FF) << 21
| ((imm >> 11) & 0x1) << 20
| ((imm >> 12) & 0xFF) << 12;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0x000FFFFF) | encoded;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_RISCV_PCREL_HI20 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
let imm20 = ((result as u32) & 0xFFFFF000) as i32;
let encoded = (imm20 as u32) & 0xFFFFF000;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0x00000FFF) | encoded;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_RISCV_PCREL_LO12_I | R_RISCV_LO12_I => {
let imm12 = (value as u32) & 0xFFF;
let encoded = imm12 << 20;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0x000FFFFF) | encoded;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_RISCV_HI20 => {
let imm20 = ((value as u32) & 0xFFFFF000) as u32;
Self::check_bounds(buf, off, 4)?;
let insn = u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let new_insn = (insn & 0x00000FFF) | imm20;
buf[off..off + 4].copy_from_slice(&new_insn.to_le_bytes());
Ok(())
}
R_RISCV_ADD32 => {
let existing =
u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let result = existing.wrapping_add(value as u32);
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&result.to_le_bytes());
Ok(())
}
R_RISCV_ADD64 => {
let existing = u64::from_le_bytes([
buf[off],
buf[off + 1],
buf[off + 2],
buf[off + 3],
buf[off + 4],
buf[off + 5],
buf[off + 6],
buf[off + 7],
]);
let result = existing.wrapping_add(value as u64);
Self::check_bounds(buf, off, 8)?;
buf[off..off + 8].copy_from_slice(&result.to_le_bytes());
Ok(())
}
R_RISCV_SUB32 => {
let existing =
u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]]);
let result = existing.wrapping_sub(value as u32);
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&result.to_le_bytes());
Ok(())
}
R_RISCV_SUB64 => {
let existing = u64::from_le_bytes([
buf[off],
buf[off + 1],
buf[off + 2],
buf[off + 3],
buf[off + 4],
buf[off + 5],
buf[off + 6],
buf[off + 7],
]);
let result = existing.wrapping_sub(value as u64);
Self::check_bounds(buf, off, 8)?;
buf[off..off + 8].copy_from_slice(&result.to_le_bytes());
Ok(())
}
_ => Err(format!("Unsupported RISC-V relocation type: {}", rel_type)),
}
}
fn apply_x86(
rel_type: u32,
buf: &mut [u8],
offset: u64,
symbol_value: u64,
addend: i64,
pc_offset: u64,
) -> Result<(), String> {
let off = offset as usize;
let value = (symbol_value as i64).wrapping_add(addend);
match rel_type {
0 => Ok(()), 1 => {
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(value as u32).to_le_bytes());
Ok(())
}
2 => {
let pc = pc_offset as i64;
let result = value.wrapping_sub(pc);
Self::check_bounds(buf, off, 4)?;
buf[off..off + 4].copy_from_slice(&(result as u32).to_le_bytes());
Ok(())
}
_ => Err(format!("Unsupported X86 relocation type: {}", rel_type)),
}
}
fn check_bounds(buf: &[u8], offset: usize, size: usize) -> Result<(), String> {
if offset + size > buf.len() {
Err(format!(
"Relocation write out of bounds: offset={}, size={}, buf_len={}",
offset,
size,
buf.len()
))
} else {
Ok(())
}
}
pub fn is_static_relocation(machine: ElfMachine, rel_type: u32) -> bool {
!Self::is_plt_relocation(machine, rel_type) && !Self::is_dynamic_only(machine, rel_type)
}
fn is_dynamic_only(machine: ElfMachine, rel_type: u32) -> bool {
match machine {
ElfMachine::X86_64 => matches!(
rel_type,
R_X86_64_COPY
| R_X86_64_GLOB_DAT
| R_X86_64_JUMP_SLOT
| R_X86_64_RELATIVE
| R_X86_64_TLSDESC
),
ElfMachine::AArch64 => false, ElfMachine::ARM => false,
ElfMachine::RiscV => {
matches!(
rel_type,
R_RISCV_COPY | R_RISCV_JUMP_SLOT | R_RISCV_RELATIVE
)
}
ElfMachine::X86 => matches!(rel_type, 5 | 6 | 7 | 8), _ => false,
}
}
pub fn is_plt_relocation(machine: ElfMachine, rel_type: u32) -> bool {
match machine {
ElfMachine::X86_64 => {
matches!(rel_type, R_X86_64_PLT32 | R_X86_64_JUMP_SLOT)
}
ElfMachine::AArch64 => {
rel_type == 1026
}
ElfMachine::ARM => {
matches!(rel_type, 13 | 20) }
ElfMachine::RiscV => {
matches!(rel_type, R_RISCV_JUMP_SLOT | R_RISCV_CALL_PLT)
}
ElfMachine::X86 => {
matches!(rel_type, 4 | 7) }
_ => false,
}
}
pub fn is_got_relocation(machine: ElfMachine, rel_type: u32) -> bool {
match machine {
ElfMachine::X86_64 => matches!(
rel_type,
R_X86_64_GOT32
| R_X86_64_GOTPCREL
| R_X86_64_GOTOFF64
| R_X86_64_GOTPC32
| R_X86_64_GLOB_DAT
| R_X86_64_GOTPC32_TLSDESC
),
ElfMachine::AArch64 => {
matches!(
rel_type,
R_AARCH64_ADR_GOT_PAGE | R_AARCH64_LD64_GOT_LO12_NC
)
}
ElfMachine::ARM => {
matches!(rel_type, 24 | 26 | 29)
}
ElfMachine::RiscV => {
matches!(rel_type, R_RISCV_GOT_HI20)
}
ElfMachine::X86 => {
matches!(rel_type, 3 | 6 | 9) }
_ => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn read_u16_le(buf: &[u8], off: usize) -> u16 {
u16::from_le_bytes([buf[off], buf[off + 1]])
}
fn read_u32_le(buf: &[u8], off: usize) -> u32 {
u32::from_le_bytes([buf[off], buf[off + 1], buf[off + 2], buf[off + 3]])
}
fn read_u64_le(buf: &[u8], off: usize) -> u64 {
u64::from_le_bytes([
buf[off],
buf[off + 1],
buf[off + 2],
buf[off + 3],
buf[off + 4],
buf[off + 5],
buf[off + 6],
buf[off + 7],
])
}
#[test]
fn test_type_name_x86_64() {
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::X86_64, R_X86_64_NONE),
"R_X86_64_NONE"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::X86_64, R_X86_64_64),
"R_X86_64_64"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::X86_64, R_X86_64_PC32),
"R_X86_64_PC32"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::X86_64, R_X86_64_GOTPCREL),
"R_X86_64_GOTPCREL"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::X86_64, 999),
"R_X86_64_UNKNOWN"
);
}
#[test]
fn test_type_name_aarch64() {
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::AArch64, R_AARCH64_NONE),
"R_AARCH64_NONE"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::AArch64, R_AARCH64_ABS64),
"R_AARCH64_ABS64"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::AArch64, R_AARCH64_CALL26),
"R_AARCH64_CALL26"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::AArch64, 999),
"R_AARCH64_UNKNOWN"
);
}
#[test]
fn test_type_name_arm32() {
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::ARM, R_ARM_NONE),
"R_ARM_NONE"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::ARM, R_ARM_ABS32),
"R_ARM_ABS32"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::ARM, R_ARM_CALL),
"R_ARM_CALL"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::ARM, 999),
"R_ARM_UNKNOWN"
);
}
#[test]
fn test_type_name_riscv() {
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::RiscV, R_RISCV_NONE),
"R_RISCV_NONE"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::RiscV, R_RISCV_64),
"R_RISCV_64"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::RiscV, R_RISCV_BRANCH),
"R_RISCV_BRANCH"
);
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::RiscV, 999),
"R_RISCV_UNKNOWN"
);
}
#[test]
fn test_type_name_unknown_machine() {
assert_eq!(
ElfRelocationHandler::type_name(ElfMachine::Unknown(0xDEAD), 1),
"R_UNKNOWN"
);
}
#[test]
fn test_is_pc_relative_x86_64() {
let m = ElfMachine::X86_64;
assert!(!ElfRelocationHandler::is_pc_relative(m, R_X86_64_NONE));
assert!(!ElfRelocationHandler::is_pc_relative(m, R_X86_64_64));
assert!(!ElfRelocationHandler::is_pc_relative(m, R_X86_64_32));
assert!(ElfRelocationHandler::is_pc_relative(m, R_X86_64_PC32));
assert!(ElfRelocationHandler::is_pc_relative(m, R_X86_64_PC64));
assert!(ElfRelocationHandler::is_pc_relative(m, R_X86_64_PC8));
assert!(ElfRelocationHandler::is_pc_relative(m, R_X86_64_PC16));
assert!(ElfRelocationHandler::is_pc_relative(m, R_X86_64_GOTPCREL));
assert!(ElfRelocationHandler::is_pc_relative(m, R_X86_64_PLT32));
assert!(ElfRelocationHandler::is_pc_relative(m, R_X86_64_GOTPC32));
}
#[test]
fn test_is_pc_relative_aarch64() {
let m = ElfMachine::AArch64;
assert!(!ElfRelocationHandler::is_pc_relative(m, R_AARCH64_ABS64));
assert!(!ElfRelocationHandler::is_pc_relative(m, R_AARCH64_ABS32));
assert!(ElfRelocationHandler::is_pc_relative(m, R_AARCH64_PREL64));
assert!(ElfRelocationHandler::is_pc_relative(m, R_AARCH64_PREL32));
assert!(ElfRelocationHandler::is_pc_relative(m, R_AARCH64_CALL26));
assert!(ElfRelocationHandler::is_pc_relative(m, R_AARCH64_JUMP26));
assert!(ElfRelocationHandler::is_pc_relative(
m,
R_AARCH64_ADR_PREL_PG_HI21
));
assert!(ElfRelocationHandler::is_pc_relative(m, R_AARCH64_CONDBR19));
assert!(ElfRelocationHandler::is_pc_relative(m, R_AARCH64_TSTBR14));
assert!(ElfRelocationHandler::is_pc_relative(
m,
R_AARCH64_ADR_GOT_PAGE
));
}
#[test]
fn test_is_pc_relative_arm32() {
let m = ElfMachine::ARM;
assert!(!ElfRelocationHandler::is_pc_relative(m, R_ARM_ABS32));
assert!(!ElfRelocationHandler::is_pc_relative(m, R_ARM_MOVW_ABS_NC));
assert!(ElfRelocationHandler::is_pc_relative(m, R_ARM_REL32));
assert!(ElfRelocationHandler::is_pc_relative(m, R_ARM_CALL));
assert!(ElfRelocationHandler::is_pc_relative(m, R_ARM_JUMP24));
assert!(ElfRelocationHandler::is_pc_relative(m, R_ARM_THM_CALL));
assert!(ElfRelocationHandler::is_pc_relative(m, R_ARM_PREL31));
}
#[test]
fn test_is_pc_relative_riscv() {
let m = ElfMachine::RiscV;
assert!(!ElfRelocationHandler::is_pc_relative(m, R_RISCV_64));
assert!(!ElfRelocationHandler::is_pc_relative(m, R_RISCV_32));
assert!(ElfRelocationHandler::is_pc_relative(m, R_RISCV_BRANCH));
assert!(ElfRelocationHandler::is_pc_relative(m, R_RISCV_JAL));
assert!(ElfRelocationHandler::is_pc_relative(m, R_RISCV_CALL));
assert!(ElfRelocationHandler::is_pc_relative(m, R_RISCV_PCREL_HI20));
assert!(ElfRelocationHandler::is_pc_relative(
m,
R_RISCV_PCREL_LO12_I
));
assert!(ElfRelocationHandler::is_pc_relative(m, R_RISCV_32_PCREL));
assert!(ElfRelocationHandler::is_pc_relative(m, R_RISCV_RVC_BRANCH));
}
#[test]
fn test_relocation_size_x86_64() {
let m = ElfMachine::X86_64;
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_64), 8);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_PC64), 8);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_32), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_PC32), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_32S), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_16), 2);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_8), 1);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_X86_64_NONE), 0);
}
#[test]
fn test_relocation_size_aarch64() {
let m = ElfMachine::AArch64;
assert_eq!(ElfRelocationHandler::relocation_size(m, R_AARCH64_ABS64), 8);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_AARCH64_ABS32), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_AARCH64_ABS16), 2);
assert_eq!(
ElfRelocationHandler::relocation_size(m, R_AARCH64_CALL26),
4
);
assert_eq!(
ElfRelocationHandler::relocation_size(m, R_AARCH64_JUMP26),
4
);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_AARCH64_NONE), 0);
}
#[test]
fn test_relocation_size_arm32() {
let m = ElfMachine::ARM;
assert_eq!(ElfRelocationHandler::relocation_size(m, R_ARM_ABS32), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_ARM_CALL), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_ARM_NONE), 0);
}
#[test]
fn test_relocation_size_riscv() {
let m = ElfMachine::RiscV;
assert_eq!(ElfRelocationHandler::relocation_size(m, R_RISCV_64), 8);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_RISCV_32), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_RISCV_BRANCH), 4);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_RISCV_JAL), 4);
assert_eq!(
ElfRelocationHandler::relocation_size(m, R_RISCV_RVC_BRANCH),
2
);
assert_eq!(ElfRelocationHandler::relocation_size(m, R_RISCV_NONE), 0);
}
#[test]
fn test_apply_x86_64_64() {
let mut buf = vec![0u8; 8];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_64,
&mut buf,
0,
0x123456789ABCDEF0,
0,
0,
)
.unwrap();
assert_eq!(read_u64_le(&buf, 0), 0x123456789ABCDEF0);
}
#[test]
fn test_apply_x86_64_64_with_addend() {
let mut buf = vec![0u8; 8];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_64,
&mut buf,
0,
0x1000,
8,
0,
)
.unwrap();
assert_eq!(read_u64_le(&buf, 0), 0x1008);
}
#[test]
fn test_apply_x86_64_pc32() {
let mut buf = vec![0xCCu8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_PC32,
&mut buf,
0,
0x2000,
-4,
0x1000,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0) as i32, 0x0FFC);
}
#[test]
fn test_apply_x86_64_32() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_32,
&mut buf,
0,
0xDEADBEEF,
0,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0), 0xDEADBEEF);
}
#[test]
fn test_apply_x86_64_32_overflow() {
let mut buf = vec![0u8; 4];
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_32,
&mut buf,
0,
0x1_0000_0000,
0,
0,
);
assert!(result.is_err());
}
#[test]
fn test_apply_x86_64_32s() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_32S,
&mut buf,
0,
0x1000,
-4,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0) as i32, 0x0FFCi32);
}
#[test]
fn test_apply_x86_64_32s_overflow() {
let mut buf = vec![0u8; 4];
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_32S,
&mut buf,
0,
0x80000000,
0,
0,
);
assert!(result.is_ok());
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_32S,
&mut buf,
0,
0x7FFFFFFF,
1,
0,
);
assert!(result.is_err());
}
#[test]
fn test_apply_x86_64_8() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_8,
&mut buf,
0,
0xAB,
0,
0,
)
.unwrap();
assert_eq!(buf[0], 0xAB);
}
#[test]
fn test_apply_x86_64_16() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_16,
&mut buf,
0,
0xBEEF,
0,
0,
)
.unwrap();
assert_eq!(read_u16_le(&buf, 0), 0xBEEF);
}
#[test]
fn test_apply_x86_64_none() {
let mut buf = vec![0x42u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_NONE,
&mut buf,
0,
0,
0,
0,
)
.unwrap();
assert_eq!(buf[0], 0x42);
}
#[test]
fn test_apply_x86_64_unsupported() {
let mut buf = vec![0u8; 4];
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
999, &mut buf,
0,
0,
0,
0,
);
assert!(result.is_err());
}
#[test]
fn test_apply_aarch64_abs64() {
let mut buf = vec![0u8; 8];
ElfRelocationHandler::apply_relocation(
ElfMachine::AArch64,
R_AARCH64_ABS64,
&mut buf,
0,
0xAABBCCDD11223344,
0,
0,
)
.unwrap();
assert_eq!(read_u64_le(&buf, 0), 0xAABBCCDD11223344);
}
#[test]
fn test_apply_aarch64_abs32() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::AArch64,
R_AARCH64_ABS32,
&mut buf,
0,
0xCAFEBABE,
0,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0), 0xCAFEBABE);
}
#[test]
fn test_apply_aarch64_call26() {
let mut buf = vec![0u8; 4];
buf[0..4].copy_from_slice(&0x94000000u32.to_le_bytes());
ElfRelocationHandler::apply_relocation(
ElfMachine::AArch64,
R_AARCH64_CALL26,
&mut buf,
0,
0x1000,
0,
0,
)
.unwrap();
let insn = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(insn, 0x94000000 | 0x400);
}
#[test]
fn test_apply_arm_abs32() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::ARM,
R_ARM_ABS32,
&mut buf,
0,
0xDEADBEEF,
0,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0), 0xDEADBEEF);
}
#[test]
fn test_apply_arm_call() {
let mut buf = vec![0u8; 4];
buf[0..4].copy_from_slice(&0xEB000000u32.to_le_bytes());
ElfRelocationHandler::apply_relocation(
ElfMachine::ARM,
R_ARM_CALL,
&mut buf,
0,
0x1000,
0,
0,
)
.unwrap();
let insn = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(insn, 0xEB000000 | 0x3FE);
}
#[test]
fn test_apply_riscv_64() {
let mut buf = vec![0u8; 8];
ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_64,
&mut buf,
0,
0xFEEDFACEDEADBEEF,
0,
0,
)
.unwrap();
assert_eq!(read_u64_le(&buf, 0), 0xFEEDFACEDEADBEEF);
}
#[test]
fn test_apply_riscv_32() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_32,
&mut buf,
0,
0xCAFEBABE,
0,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0), 0xCAFEBABE);
}
#[test]
fn test_apply_riscv_branch() {
let mut buf = vec![0u8; 4];
buf[0..4].copy_from_slice(&0x00000063u32.to_le_bytes());
ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_BRANCH,
&mut buf,
0,
0x800,
0,
0,
)
.unwrap();
let insn = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(insn, 0x00000063 | 0x40000000);
}
#[test]
fn test_is_static_relocation() {
let m = ElfMachine::X86_64;
assert!(ElfRelocationHandler::is_static_relocation(m, R_X86_64_64));
assert!(ElfRelocationHandler::is_static_relocation(m, R_X86_64_PC32));
assert!(!ElfRelocationHandler::is_static_relocation(
m,
R_X86_64_JUMP_SLOT
));
assert!(!ElfRelocationHandler::is_static_relocation(
m,
R_X86_64_COPY
));
assert!(!ElfRelocationHandler::is_static_relocation(
m,
R_X86_64_GLOB_DAT
));
}
#[test]
fn test_is_plt_relocation() {
let m = ElfMachine::X86_64;
assert!(ElfRelocationHandler::is_plt_relocation(m, R_X86_64_PLT32));
assert!(ElfRelocationHandler::is_plt_relocation(
m,
R_X86_64_JUMP_SLOT
));
assert!(!ElfRelocationHandler::is_plt_relocation(m, R_X86_64_64));
assert!(!ElfRelocationHandler::is_plt_relocation(m, R_X86_64_PC32));
let a = ElfMachine::AArch64;
assert!(ElfRelocationHandler::is_plt_relocation(a, 1026));
let r = ElfMachine::RiscV;
assert!(ElfRelocationHandler::is_plt_relocation(
r,
R_RISCV_JUMP_SLOT
));
assert!(ElfRelocationHandler::is_plt_relocation(r, R_RISCV_CALL_PLT));
assert!(!ElfRelocationHandler::is_plt_relocation(r, R_RISCV_64));
}
#[test]
fn test_is_got_relocation() {
let m = ElfMachine::X86_64;
assert!(ElfRelocationHandler::is_got_relocation(m, R_X86_64_GOT32));
assert!(ElfRelocationHandler::is_got_relocation(
m,
R_X86_64_GOTPCREL
));
assert!(ElfRelocationHandler::is_got_relocation(
m,
R_X86_64_GLOB_DAT
));
assert!(ElfRelocationHandler::is_got_relocation(m, R_X86_64_GOTPC32));
assert!(!ElfRelocationHandler::is_got_relocation(m, R_X86_64_64));
assert!(!ElfRelocationHandler::is_got_relocation(m, R_X86_64_PC32));
let a = ElfMachine::AArch64;
assert!(ElfRelocationHandler::is_got_relocation(
a,
R_AARCH64_ADR_GOT_PAGE
));
assert!(ElfRelocationHandler::is_got_relocation(
a,
R_AARCH64_LD64_GOT_LO12_NC
));
assert!(!ElfRelocationHandler::is_got_relocation(a, R_AARCH64_ABS64));
let r = ElfMachine::RiscV;
assert!(ElfRelocationHandler::is_got_relocation(r, R_RISCV_GOT_HI20));
assert!(!ElfRelocationHandler::is_got_relocation(r, R_RISCV_64));
}
#[test]
fn test_apply_out_of_bounds() {
let mut buf = vec![0u8; 2];
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_64,
&mut buf,
0,
0x1000,
0,
0,
);
assert!(result.is_err());
}
#[test]
fn test_apply_unsupported_machine() {
let mut buf = vec![0u8; 8];
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::Unknown(0xFFFF),
1,
&mut buf,
0,
0,
0,
0,
);
assert!(result.is_err());
}
#[test]
fn test_apply_x86_64_8_overflow() {
let mut buf = vec![0u8; 4];
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_8,
&mut buf,
0,
0x100,
0,
0,
);
assert!(result.is_err());
}
#[test]
fn test_apply_x86_64_16_overflow() {
let mut buf = vec![0u8; 4];
let result = ElfRelocationHandler::apply_relocation(
ElfMachine::X86_64,
R_X86_64_16,
&mut buf,
0,
0x10000,
0,
0,
);
assert!(result.is_err());
}
#[test]
fn test_apply_x86_32() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86,
1, &mut buf,
0,
0xBEEF1234,
0,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0), 0xBEEF1234);
}
#[test]
fn test_apply_x86_pc32() {
let mut buf = vec![0u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::X86,
2, &mut buf,
0,
0x2000,
-4,
0x1000,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0) as i32, 0x0FFC);
}
#[test]
fn test_apply_riscv_jal() {
let mut buf = vec![0u8; 4];
buf[0..4].copy_from_slice(&0x0000006Fu32.to_le_bytes());
ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_JAL,
&mut buf,
0,
0x1000,
0,
0,
)
.unwrap();
let insn = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(insn, 0x0000006F | 0x00100000);
}
#[test]
fn test_apply_riscv_pcrel_hi20() {
let mut buf = vec![0u8; 4];
buf[0..4].copy_from_slice(&0x00000017u32.to_le_bytes());
ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_PCREL_HI20,
&mut buf,
0,
0x12345000,
0,
0x1000,
)
.unwrap();
let insn = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
let expected_imm = 0x12344000;
assert_eq!(insn, 0x00000017 | expected_imm);
}
#[test]
fn test_apply_riscv_lo12_i() {
let mut buf = vec![0u8; 4];
buf[0..4].copy_from_slice(&0x00000013u32.to_le_bytes());
ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_LO12_I,
&mut buf,
0,
0xABC,
0,
0,
)
.unwrap();
let insn = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
assert_eq!(insn, 0x00000013 | (0xABC << 20));
}
#[test]
fn test_apply_riscv_add32() {
let mut buf = [0x10u8, 0x00, 0x00, 0x00]; ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_ADD32,
&mut buf,
0,
0x20,
0,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0), 0x30); }
#[test]
fn test_apply_riscv_sub32() {
let mut buf = [0x50u8, 0x00, 0x00, 0x00]; ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_SUB32,
&mut buf,
0,
0x20,
0,
0,
)
.unwrap();
assert_eq!(read_u32_le(&buf, 0), 0x30); }
#[test]
fn test_apply_riscv_none() {
let mut buf = vec![0x42u8; 4];
ElfRelocationHandler::apply_relocation(
ElfMachine::RiscV,
R_RISCV_NONE,
&mut buf,
0,
0,
0,
0,
)
.unwrap();
assert_eq!(buf[0], 0x42); }
}