From e2ccb97f9b84f8b7ea607ac9cdfd5725a7263bff Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Wed, 23 Aug 2023 16:16:21 +0700 Subject: [PATCH] Resolves symbol for non-PLT relocations (#308) --- src/elf/src/reloc.rs | 1 + src/kernel/src/rtld/mod.rs | 49 ++++++++++--- src/kernel/src/rtld/resolver.rs | 120 ++++++++++++++++++++++++++++++-- 3 files changed, 153 insertions(+), 17 deletions(-) diff --git a/src/elf/src/reloc.rs b/src/elf/src/reloc.rs index c60b907cb..9abc429ed 100644 --- a/src/elf/src/reloc.rs +++ b/src/elf/src/reloc.rs @@ -46,6 +46,7 @@ pub struct Relocation { impl Relocation { pub const R_X86_64_NONE: u32 = 0; pub const R_X86_64_64: u32 = 1; + 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_DTPMOD64: u32 = 16; diff --git a/src/kernel/src/rtld/mod.rs b/src/kernel/src/rtld/mod.rs index 2749d5898..085f50cde 100644 --- a/src/kernel/src/rtld/mod.rs +++ b/src/kernel/src/rtld/mod.rs @@ -11,7 +11,7 @@ use gmtx::{GroupMutex, GroupMutexReadGuard, MutexGroup}; use std::fs::File; use std::num::NonZeroI32; use std::ops::Deref; -use std::ptr::write_unaligned; +use std::ptr::{read_unaligned, write_unaligned}; use std::sync::Arc; use thiserror::Error; @@ -258,8 +258,9 @@ impl<'a> RuntimeLinker<'a> { // TODO: Check what the PS4 actually doing. let list = self.list.read(); + let mains = self.mains.read(); let resolver = SymbolResolver::new( - &list, + &mains, self.app.sdk_ver() >= 0x5000000 || self.flags.contains(LinkerFlags::UNK2), ); @@ -288,7 +289,7 @@ impl<'a> RuntimeLinker<'a> { // Apply relocations. let mut relocated = md.relocated_mut(); - self.relocate_rela(md, mem.as_mut(), &mut relocated)?; + self.relocate_rela(md, mem.as_mut(), &mut relocated, resolver)?; if !md.flags().contains(ModuleFlags::UNK4) { self.relocate_plt(md, mem.as_mut(), &mut relocated, resolver)?; @@ -298,11 +299,12 @@ impl<'a> RuntimeLinker<'a> { } /// See `reloc_non_plt` on the PS4 kernel for a reference. - fn relocate_rela( + fn relocate_rela<'b>( &self, - md: &Arc, + md: &'b Arc, mem: &mut [u8], relocated: &mut [bool], + resolver: &SymbolResolver<'b>, ) -> Result<(), RelocateError> { let info = md.file_info().unwrap(); // Let it panic because the PS4 assume it is available. let addr = mem.as_ptr() as usize; @@ -318,24 +320,51 @@ impl<'a> RuntimeLinker<'a> { let offset = base + reloc.offset(); let target = &mut mem[offset..(offset + 8)]; let addend = reloc.addend(); + let sym = reloc.symbol(); + let symflags = ResolveFlags::empty(); let value = match reloc.ty() { Relocation::R_X86_64_NONE => break, Relocation::R_X86_64_64 => { - // TODO: Resolve symbol. - continue; + // TODO: Apply checks from reloc_non_plt. + let (md, sym) = match resolver.resolve_with_local(md, sym, symflags) { + Some((md, sym)) => (md, md.symbol(sym).unwrap()), + None => continue, + }; + + // TODO: Apply checks from reloc_non_plt. + let mem = md.memory(); + + (mem.addr() + mem.base() + sym.value()).wrapping_add_signed(addend) + } + Relocation::R_X86_64_GLOB_DAT => { + // TODO: Apply checks from reloc_non_plt. + let (md, sym) = match resolver.resolve_with_local(md, sym, symflags) { + Some((md, sym)) => (md, md.symbol(sym).unwrap()), + None => continue, + }; + + // TODO: Apply checks from reloc_non_plt. + let mem = md.memory(); + + mem.addr() + mem.base() + sym.value() } Relocation::R_X86_64_RELATIVE => { // TODO: Apply checks from reloc_non_plt. (addr + base).wrapping_add_signed(addend) } Relocation::R_X86_64_DTPMOD64 => { - // TODO: Resolve symbol. - continue; + // TODO: Apply checks from reloc_non_plt. + let value: usize = match resolver.resolve_with_local(md, sym, symflags) { + Some((md, _)) => md.tls_index().try_into().unwrap(), + None => continue, + }; + + unsafe { read_unaligned(target.as_ptr() as *const usize) + value } } v => return Err(RelocateError::UnsupportedRela(md.path().to_owned(), v)), }; - // Write the value. + // TODO: Check what relocate_text_or_data_segment on the PS4 is doing. unsafe { write_unaligned(target.as_mut_ptr() as *mut usize, value) }; relocated[i] = true; diff --git a/src/kernel/src/rtld/resolver.rs b/src/kernel/src/rtld/resolver.rs index 42308e9e1..0f46f8ffc 100644 --- a/src/kernel/src/rtld/resolver.rs +++ b/src/kernel/src/rtld/resolver.rs @@ -1,18 +1,18 @@ use super::Module; use bitflags::bitflags; -use elf::{LibraryInfo, ModuleInfo, Symbol}; +use elf::{LibraryInfo, Symbol}; use std::sync::Arc; /// An object to resolve a symbol from loaded (S)ELF. pub struct SymbolResolver<'a> { - mods: &'a [Arc], + mains: &'a [Arc], new_algorithm: bool, } impl<'a> SymbolResolver<'a> { - pub fn new(mods: &'a [Arc], new_algorithm: bool) -> Self { + pub fn new(mains: &'a [Arc], new_algorithm: bool) -> Self { Self { - mods, + mains, new_algorithm, } } @@ -47,7 +47,7 @@ impl<'a> SymbolResolver<'a> { ( Some(sym.name()), None, - mi, + mi.map(|i| i.name()), li, Self::hash(Some(sym.name()), li.map(|i| i.name()), mi.map(|i| i.name())), ) @@ -80,13 +80,117 @@ impl<'a> SymbolResolver<'a> { refmod: &'a Arc, name: Option<&str>, decoded_name: Option<&str>, - symmod: Option<&ModuleInfo>, + symmod: Option<&str>, symlib: Option<&LibraryInfo>, hash: u64, flags: ResolveFlags, ) -> Option<(&'a Arc, usize)> { - // TODO: Resolve from global. // TODO: Resolve from DAGs. + self.resolve_from_global(refmod, name, decoded_name, symmod, symlib, hash, flags) + } + + /// See `symlook_global` on the PS4 for a reference. + pub fn resolve_from_global( + &self, + refmod: &'a Arc, + name: Option<&str>, + decoded_name: Option<&str>, + symmod: Option<&str>, + symlib: Option<&LibraryInfo>, + hash: u64, + flags: ResolveFlags, + ) -> Option<(&'a Arc, usize)> { + // TODO: Resolve from list_global. + self.resolve_from_list( + refmod, + name, + decoded_name, + symmod, + symlib, + hash, + flags, + &self.mains, + ) + } + + /// See `symlook_list` on the PS4 for a reference. + pub fn resolve_from_list( + &self, + refmod: &'a Arc, + name: Option<&str>, + decoded_name: Option<&str>, + symmod: Option<&str>, + symlib: Option<&LibraryInfo>, + hash: u64, + flags: ResolveFlags, + list: &'a [Arc], + ) -> Option<(&'a Arc, usize)> { + // Get module name. + let symmod = if !flags.contains(ResolveFlags::UNK2) { + symmod + } else if let Some(v) = decoded_name { + v.rfind('#').map(|i| &v[(i + 1)..]) + } else { + None + }; + + // TODO: Handle LinkerFlags::UNK2. + for md in list { + // TODO: Implement DoneList. + if let Some(name) = symmod { + // TODO: This will be much simpler if we can make sure the module ID is unique. + let mut found = false; + + for info in md.modules() { + if info.id() == 0 { + if info.name() == name { + found = true; + break; + } + } + } + + if !found { + continue; + } + } + + // Lookup from the module. + let (md, index) = match self.resolve_from_module( + refmod, + name, + decoded_name, + symmod, + symlib, + hash, + flags, + md, + ) { + Some(v) => v, + None => continue, + }; + + if md.symbol(index).unwrap().binding() != Symbol::STB_WEAK { + return Some((md, index)); + } + } + + None + } + + /// See `symlook_obj` on the PS4 for a reference. + pub fn resolve_from_module( + &self, + refmod: &'a Arc, + name: Option<&str>, + decoded_name: Option<&str>, + symmod: Option<&str>, + symlib: Option<&LibraryInfo>, + hash: u64, + flags: ResolveFlags, + md: &'a Arc, + ) -> Option<(&'a Arc, usize)> { + // TODO: Implement symlook_obj. None } @@ -159,7 +263,9 @@ impl<'a> SymbolResolver<'a> { bitflags! { /// Flags to control behavior of [`SymbolResolver`]. + #[derive(Clone, Copy)] pub struct ResolveFlags: u32 { const UNK1 = 0x00000001; + const UNK2 = 0x00000100; } }