Skip to content

Commit

Permalink
Resolves symbol for non-PLT relocations (#308)
Browse files Browse the repository at this point in the history
  • Loading branch information
ultimaweapon authored Aug 23, 2023
1 parent 0f23515 commit e2ccb97
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 17 deletions.
1 change: 1 addition & 0 deletions src/elf/src/reloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
49 changes: 39 additions & 10 deletions src/kernel/src/rtld/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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),
);

Expand Down Expand Up @@ -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)?;
Expand All @@ -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<Module>,
md: &'b Arc<Module>,
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;
Expand All @@ -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;
Expand Down
120 changes: 113 additions & 7 deletions src/kernel/src/rtld/resolver.rs
Original file line number Diff line number Diff line change
@@ -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<Module>],
mains: &'a [Arc<Module>],
new_algorithm: bool,
}

impl<'a> SymbolResolver<'a> {
pub fn new(mods: &'a [Arc<Module>], new_algorithm: bool) -> Self {
pub fn new(mains: &'a [Arc<Module>], new_algorithm: bool) -> Self {
Self {
mods,
mains,
new_algorithm,
}
}
Expand Down Expand Up @@ -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())),
)
Expand Down Expand Up @@ -80,13 +80,117 @@ impl<'a> SymbolResolver<'a> {
refmod: &'a Arc<Module>,
name: Option<&str>,
decoded_name: Option<&str>,
symmod: Option<&ModuleInfo>,
symmod: Option<&str>,
symlib: Option<&LibraryInfo>,
hash: u64,
flags: ResolveFlags,
) -> Option<(&'a Arc<Module>, 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<Module>,
name: Option<&str>,
decoded_name: Option<&str>,
symmod: Option<&str>,
symlib: Option<&LibraryInfo>,
hash: u64,
flags: ResolveFlags,
) -> Option<(&'a Arc<Module>, 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<Module>,
name: Option<&str>,
decoded_name: Option<&str>,
symmod: Option<&str>,
symlib: Option<&LibraryInfo>,
hash: u64,
flags: ResolveFlags,
list: &'a [Arc<Module>],
) -> Option<(&'a Arc<Module>, 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<Module>,
name: Option<&str>,
decoded_name: Option<&str>,
symmod: Option<&str>,
symlib: Option<&LibraryInfo>,
hash: u64,
flags: ResolveFlags,
md: &'a Arc<Module>,
) -> Option<(&'a Arc<Module>, usize)> {
// TODO: Implement symlook_obj.
None
}

Expand Down Expand Up @@ -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;
}
}

0 comments on commit e2ccb97

Please sign in to comment.