Skip to content

Instantly share code, notes, and snippets.

@Amanieu
Created August 25, 2021 00:53
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Amanieu/588e3f9d330019c5d39f3ce60e8e0aae to your computer and use it in GitHub Desktop.
Save Amanieu/588e3f9d330019c5d39f3ce60e8e0aae to your computer and use it in GitHub Desktop.
PIE self-relocation in Rust
/// Processes all relocations in the ELF dynamic section
unsafe fn process_pie_relocations(auxv: &mut AuxVec) {
// Locate the PT_DYNAMIC segment in the program headers. If we don't have
// a .dynamic section then it means that we are a static executable and
// don't need any relocations.
let pt_dynamic = auxv
.phdr
.iter()
.filter(|p| p.p_type == linux::PT_DYNAMIC)
.next();
// Calculate our load offset and the address of _DYNAMIC.
let mut dynamic = if let Some(pt_dynamic) = pt_dynamic {
// PIE executables have __executable_start at link address 0.
#[cfg(target_arch = "aarch64")]
asm!("adrp {}, __executable_start", out(reg) auxv.load_offset, options(pure, nomem, nostack, preserves_flags));
#[cfg(target_arch = "riscv64")]
asm!("lla {}, __executable_start", out(reg) auxv.load_offset, options(pure, nomem, nostack));
pt_dynamic.p_vaddr.wrapping_add(auxv.load_offset as u64) as *const linux::Elf64_Dyn
} else {
// Static executables have a load offset of 0.
return;
};
// Find the relocation arrays
let mut rela = (ptr::null(), 0);
loop {
match (*dynamic).d_tag as u32 {
linux::DT_NULL => break,
linux::DT_RELA => {
rela.0 = auxv.load_offset.wrapping_add((*dynamic).d_val as usize)
as *const linux::Elf64_Rela
}
linux::DT_RELASZ => {
rela.1 = (*dynamic).d_val as usize / mem::size_of::<linux::Elf64_Rela>()
}
linux::DT_RELAENT => {
if (*dynamic).d_val as usize != mem::size_of::<linux::Elf64_Rela>() {
early_abort()
}
}
// The linker should only generate RELA entries
linux::DT_RELSZ => early_abort(),
_ => {}
}
dynamic = dynamic.add(1);
}
// Process the relocations. We only support R_*_RELATIVE relocations.
if !rela.0.is_null() {
for r in slice::from_raw_parts(rela.0, rela.1) {
// Due to a linker bug, we may see stray R_RISCV_NONE relocations.
// https://sourceware.org/bugzilla/show_bug.cgi?id=24673
if cfg!(target_arch = "riscv64") && linux::ELF64_R_TYPE(r.r_info) == linux::R_RISCV_NONE
{
continue;
}
#[cfg(target_arch = "aarch64")]
let r_relative = linux::R_AARCH64_RELATIVE;
#[cfg(target_arch = "riscv64")]
let r_relative = linux::R_RISCV_RELATIVE;
if linux::ELF64_R_TYPE(r.r_info) != r_relative {
early_abort();
}
let ptr = auxv.load_offset.wrapping_add(r.r_offset as usize) as *mut usize;
*ptr = auxv.load_offset.wrapping_add(r.r_addend as usize);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment