From 228d53dcd56d9e7e4c757a2c8486fa94150e92d3 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Fri, 18 Apr 2025 16:03:09 -0600 Subject: [PATCH] More improvements for relocation handling --- src/ld_so/dso.rs | 173 ++++++++++++++++++++++++++--------------------- 1 file changed, 97 insertions(+), 76 deletions(-) diff --git a/src/ld_so/dso.rs b/src/ld_so/dso.rs index 649efe7e46..c14f40bc37 100644 --- a/src/ld_so/dso.rs +++ b/src/ld_so/dso.rs @@ -166,9 +166,9 @@ unsafe impl Sync for Dynamic<'_> {} #[derive(Debug)] struct Relocation { offset: usize, - addend: usize, + addend: Option, sym: SymbolIndex, - kind: u32, + kind: RelocationKind, } #[cfg(target_pointer_width = "32")] @@ -176,9 +176,9 @@ impl From<&Rela> for Relocation { fn from(reloc: &Rela) -> Self { Self { offset: reloc.r_offset(NativeEndian) as usize, - addend: reloc.r_addend(NativeEndian) as usize, + addend: Some(reloc.r_addend(NativeEndian) as usize), sym: SymbolIndex(reloc.r_sym(NativeEndian) as usize), - kind: reloc.r_type(NativeEndian), + kind: RelocationKind::new(reloc.r_type(NativeEndian)), } } } @@ -189,9 +189,9 @@ impl From<&Rela> for Relocation { let is_mips64el = cfg!(all(target_arch = "mips64", target_endian = "little")); Self { offset: reloc.r_offset(NativeEndian) as usize, - addend: reloc.r_addend(NativeEndian) as usize, + addend: Some(reloc.r_addend(NativeEndian) as usize), sym: SymbolIndex(reloc.r_sym(NativeEndian, is_mips64el) as usize), - kind: reloc.r_type(NativeEndian, is_mips64el), + kind: RelocationKind::new(reloc.r_type(NativeEndian, is_mips64el)), } } } @@ -200,41 +200,47 @@ impl From<&Rel> for Relocation { fn from(reloc: &Rel) -> Self { Self { offset: reloc.r_offset(NativeEndian) as usize, - addend: 0, + addend: None, sym: SymbolIndex(reloc.r_sym(NativeEndian) as usize), - kind: reloc.r_type(NativeEndian), + kind: RelocationKind::new(reloc.r_type(NativeEndian)), } } } +// This is matched up to REL_* constants used by musl for ease of comparison #[allow(dead_code)] #[derive(Clone, Copy, Debug)] -pub enum R { +pub enum RelocationKind { COPY, - DIRECT, DTPMOD, DTPOFF, - GLOB_DAT, + GOT, IRELATIVE, - JUMP_SLOT, + OFFSET, + PLT, RELATIVE, - PC, + SYMBOLIC, + TLSDESC, TPOFF, UNKNOWN(u32), } -impl R { +impl RelocationKind { #[cfg(target_arch = "aarch64")] pub fn new(kind: u32) -> Self { //WARNING: Only use R_AARCH64_* constants here! match kind { - elf::R_AARCH64_COPY => R::COPY, - elf::R_AARCH64_ABS64 => R::DIRECT, - elf::R_AARCH64_GLOB_DAT => R::GLOB_DAT, - elf::R_AARCH64_IRELATIVE => R::IRELATIVE, - elf::R_AARCH64_JUMP_SLOT => R::JUMP_SLOT, - elf::R_AARCH64_RELATIVE => R::RELATIVE, - _ => R::UNKNOWN(kind), + elf::R_AARCH64_COPY => Self::COPY, + elf::R_AARCH64_TLS_DTPMOD => Self::DTPMOD, + elf::R_AARCH64_TLS_DTPREL => Self::DTPOFF, + elf::R_AARCH64_GLOB_DAT => Self::GOT, + elf::R_AARCH64_IRELATIVE => Self::IRELATIVE, + elf::R_AARCH64_JUMP_SLOT => Self::PLT, + elf::R_AARCH64_RELATIVE => Self::RELATIVE, + elf::R_AARCH64_ABS64 => Self::SYMBOLIC, + elf::R_AARCH64_TLSDESC => Self::TLSDESC, + elf::R_AARCH64_TLS_TPREL => Self::TPOFF, + _ => Self::UNKNOWN(kind), } } @@ -242,15 +248,16 @@ impl R { pub fn new(kind: u32) -> Self { //WARNING: Only use R_RISCV_* constants here! match kind { - elf::R_RISCV_COPY => R::COPY, - elf::R_RISCV_64 => R::DIRECT, - elf::R_RISCV_TLS_DTPMOD64 => R::DTPMOD, - elf::R_RISCV_TLS_DTPREL64 => R::DTPOFF, - elf::R_RISCV_IRELATIVE => R::IRELATIVE, - elf::R_RISCV_JUMP_SLOT => R::JUMP_SLOT, - elf::R_RISCV_RELATIVE => R::RELATIVE, - elf::R_RISCV_TLS_TPREL64 => R::TPOFF, - _ => R::UNKNOWN(kind), + elf::R_RISCV_COPY => Self::COPY, + elf::R_RISCV_TLS_DTPMOD64 => Self::DTPMOD, + elf::R_RISCV_TLS_DTPREL64 => Self::DTPOFF, + elf::R_RISCV_IRELATIVE => Self::IRELATIVE, + elf::R_RISCV_JUMP_SLOT => Self::PLT, + elf::R_RISCV_RELATIVE => Self::RELATIVE, + elf::R_RISCV_64 => Self::SYMBOLIC, + //TODO: not defined, should be 12: elf::R_RISCV_TLSDESC => Self::TLSDESC, + elf::R_RISCV_TLS_TPREL64 => Self::TPOFF, + _ => Self::UNKNOWN(kind), } } @@ -258,17 +265,18 @@ impl R { pub fn new(kind: u32) -> Self { //WARNING: Only use R_386_* constants here! match kind { - elf::R_386_COPY => R::COPY, - elf::R_386_32 => R::DIRECT, - elf::R_386_TLS_DTPMOD32 => R::DTPMOD, - elf::R_386_TLS_DTPOFF32 => R::DTPOFF, - elf::R_386_GLOB_DAT => R::GLOB_DAT, - elf::R_386_IRELATIVE => R::IRELATIVE, - elf::R_386_JMP_SLOT => R::JUMP_SLOT, - elf::R_386_PC32 => R::PC, - elf::R_386_RELATIVE => R::RELATIVE, - elf::R_386_TLS_TPOFF32 => R::TPOFF, - _ => R::UNKNOWN(kind), + elf::R_386_COPY => Self::COPY, + elf::R_386_TLS_DTPMOD32 => Self::DTPMOD, + elf::R_386_TLS_DTPOFF32 => Self::DTPOFF, + elf::R_386_GLOB_DAT => Self::GOT, + elf::R_386_IRELATIVE => Self::IRELATIVE, + elf::R_386_JMP_SLOT => Self::PLT, + elf::R_386_PC32 => Self::OFFSET, + elf::R_386_RELATIVE => Self::RELATIVE, + elf::R_386_32 => Self::SYMBOLIC, + elf::R_386_TLS_DESC => Self::TLSDESC, + elf::R_386_TLS_TPOFF => Self::TPOFF, + _ => Self::UNKNOWN(kind), } } @@ -276,16 +284,17 @@ impl R { pub fn new(kind: u32) -> Self { //WARNING: Only use R_X86_64_* constants here! match kind { - elf::R_X86_64_COPY => R::COPY, - elf::R_X86_64_64 => R::DIRECT, - elf::R_X86_64_DTPMOD64 => R::DTPMOD, - elf::R_X86_64_DTPOFF64 => R::DTPOFF, - elf::R_X86_64_GLOB_DAT => R::GLOB_DAT, - elf::R_X86_64_IRELATIVE => R::IRELATIVE, - elf::R_X86_64_JUMP_SLOT => R::JUMP_SLOT, - elf::R_X86_64_RELATIVE => R::RELATIVE, - elf::R_X86_64_TPOFF64 => R::TPOFF, - _ => R::UNKNOWN(kind), + elf::R_X86_64_COPY => Self::COPY, + elf::R_X86_64_DTPMOD64 => Self::DTPMOD, + elf::R_X86_64_DTPOFF64 => Self::DTPOFF, + elf::R_X86_64_GLOB_DAT => Self::GOT, + elf::R_X86_64_IRELATIVE => Self::IRELATIVE, + elf::R_X86_64_JUMP_SLOT => Self::PLT, + elf::R_X86_64_RELATIVE => Self::RELATIVE, + elf::R_X86_64_64 => Self::SYMBOLIC, + elf::R_X86_64_TLSDESC => Self::TLSDESC, + elf::R_X86_64_TPOFF64 => Self::TPOFF, + _ => Self::UNKNOWN(kind), } } } @@ -809,12 +818,11 @@ impl DSO { fn static_relocate(&self, global_scope: &Scope, reloc: Relocation) -> object::Result<()> { let b = self.mmap.as_ptr() as usize; - let r = R::new(reloc.kind); let (sym, my_sym) = if reloc.sym.0 > 0 { let name = self.dynamic.symbol_name(reloc.sym).unwrap(); let lookup_scopes = [global_scope, self.scope()]; - let sym = if matches!(r, R::COPY) { + let sym = if matches!(reloc.kind, RelocationKind::COPY) { lookup_scopes .iter() .find_map(|scope| scope._get_sym(name, 1)) @@ -833,50 +841,60 @@ impl DSO { .map(|(sym, obj)| (sym.as_ptr() as usize, obj.tls_offset)) .unwrap_or((0, 0)); - let a = reloc.addend; let ptr = if self.pie { (b + reloc.offset) as *mut u8 } else { reloc.offset as *mut u8 }; let p = ptr as usize; + let a = match reloc.addend { + Some(some) => some, + None => match reloc.kind { + RelocationKind::COPY | RelocationKind::GOT | RelocationKind::PLT => 0, + _ => unsafe { *(ptr as *mut usize) }, + }, + }; //TODO: support different sizes? let set_usize = |value| unsafe { *(ptr as *mut usize) = value; }; - match r { - R::DIRECT => set_usize(s + a), - R::DTPMOD => set_usize(self.tls_module_id), - R::DTPOFF => { + match reloc.kind { + RelocationKind::DTPMOD => set_usize(self.tls_module_id), + RelocationKind::DTPOFF => { if s != 0 { set_usize(s - b); } else { set_usize(s); } } - R::GLOB_DAT => set_usize(s), - R::PC => set_usize((s + a).wrapping_sub(p)), - R::RELATIVE => set_usize(b + a), - R::TPOFF => { + RelocationKind::GOT => set_usize(s), + RelocationKind::OFFSET => set_usize((s + a).wrapping_sub(p)), + RelocationKind::RELATIVE => set_usize(b + a), + RelocationKind::SYMBOLIC => set_usize(s + a), + RelocationKind::TPOFF => { if reloc.sym.0 > 0 { - let (sym, _) = sym.as_ref().expect("R::TPOFF called without valid symbol"); + let (sym, _) = sym + .as_ref() + .expect("RelocationKind::TPOFF called without valid symbol"); set_usize((sym.value + a).wrapping_sub(t)); } else { set_usize(a.wrapping_sub(t)); } } - R::IRELATIVE => unsafe { + RelocationKind::IRELATIVE => unsafe { let f: unsafe extern "C" fn() -> usize = core::mem::transmute(b + a); set_usize(f()); }, - R::COPY => unsafe { - let (sym, obj) = sym.as_ref().expect("R::COPY called without valid symbol"); - let my_sym = my_sym.expect("R::COPY called without valid symbol"); + RelocationKind::COPY => unsafe { + let (sym, obj) = sym + .as_ref() + .expect("RelocationKind::COPY called without valid symbol"); + let my_sym = my_sym.expect("RelocationKind::COPY called without valid symbol"); assert!( sym.size == my_sym.st_size(NativeEndian) as usize, - "R::COPY failed: I was trying to use the symbol {} from {} for {} but they had different sizes. Please consider relinking.", + "RelocationKind::COPY failed: I was trying to use the symbol {} from {} for {} but they had different sizes. Please consider relinking.", sym.name, obj.name, self.name @@ -884,7 +902,7 @@ impl DSO { // SAFETY: Both the source and destination have the same size. ptr::copy_nonoverlapping(sym.as_ptr() as *const u8, ptr, sym.size); }, - _ => unimplemented!("relocation type {:?}", r), + _ => unimplemented!("relocation type {:?}", reloc.kind), } Ok(()) @@ -924,17 +942,16 @@ impl DSO { reloc.offset as *mut usize }; - let r = R::new(reloc.kind); - match (r, resolve) { - (R::JUMP_SLOT, Resolve::Lazy) if self.pie => unsafe { + match (reloc.kind, resolve) { + (RelocationKind::PLT, Resolve::Lazy) if self.pie => unsafe { *ptr += object_base_addr; }, - (R::JUMP_SLOT, Resolve::Lazy) => { + (RelocationKind::PLT, Resolve::Lazy) => { // NOP. } - (R::JUMP_SLOT, Resolve::Now) => { + (RelocationKind::PLT, Resolve::Now) => { let name = self.dynamic.symbol_name(reloc.sym).unwrap(); let resolved = resolve_sym(name, &[global_scope, self.scope()]) @@ -942,12 +959,16 @@ impl DSO { .unwrap_or_else(|| panic!("unresolved symbol: {name}")); unsafe { - *ptr = resolved + reloc.addend; + *ptr = resolved + reloc.addend.unwrap_or(0); } } _ => { - unimplemented!("relocation type {:?} with resolve {:?}", r, resolve) + unimplemented!( + "relocation type {:?} with resolve {:?}", + reloc.kind, + resolve + ) } } }