mirror of
https://github.com/obhq/obliteration.git
synced 2024-11-23 11:19:56 +00:00
Moves symbol resolver into a dedicated struct (#303)
This commit is contained in:
parent
73cb89d6a7
commit
42fbf587e1
@ -13,7 +13,7 @@ You can download binaries from the latest commits [here](https://github.com/obhq
|
||||
|
||||
## Screenshots
|
||||
|
||||
![Game list](screenshots/game-list.png)
|
||||
![Screenshots](screenshots.png)
|
||||
|
||||
Thanks to [VocalFan](https://github.com/VocalFan) for the awesome icon!
|
||||
|
||||
|
BIN
screenshots.png
Normal file
BIN
screenshots.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 372 KiB |
Binary file not shown.
Before Width: | Height: | Size: 175 KiB |
@ -1,11 +1,12 @@
|
||||
pub use mem::*;
|
||||
pub use module::*;
|
||||
|
||||
use self::resolver::{ResolveFlags, SymbolResolver};
|
||||
use crate::errno::{Errno, EINVAL, ENOEXEC};
|
||||
use crate::fs::{Fs, VPath, VPathBuf};
|
||||
use crate::memory::{MmapError, MprotectError, Protections};
|
||||
use bitflags::bitflags;
|
||||
use elf::{DynamicFlags, Elf, FileInfo, FileType, ReadProgramError, Relocation, Symbol};
|
||||
use elf::{DynamicFlags, Elf, FileType, ReadProgramError, Relocation};
|
||||
use std::fs::File;
|
||||
use std::num::NonZeroI32;
|
||||
use std::ops::Deref;
|
||||
@ -15,6 +16,7 @@ use thiserror::Error;
|
||||
|
||||
mod mem;
|
||||
mod module;
|
||||
mod resolver;
|
||||
|
||||
/// An implementation of
|
||||
/// https://github.com/freebsd/freebsd-src/blob/release/9.1.0/libexec/rtld-elf/rtld.c.
|
||||
@ -202,8 +204,14 @@ impl<'a> RuntimeLinker<'a> {
|
||||
/// No other threads may access the memory of all loaded modules.
|
||||
pub unsafe fn relocate(&self) -> Result<(), RelocateError> {
|
||||
// TODO: Check what the PS4 actually doing.
|
||||
let list = self.list.read().unwrap();
|
||||
let resolver = SymbolResolver::new(
|
||||
&list,
|
||||
self.app.sdk_ver() >= 0x5000000 || self.flags.contains(LinkerFlags::UNK2),
|
||||
);
|
||||
|
||||
for m in self.list.read().unwrap().deref() {
|
||||
self.relocate_single(m)?;
|
||||
self.relocate_single(m, &resolver)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -213,7 +221,11 @@ impl<'a> RuntimeLinker<'a> {
|
||||
///
|
||||
/// # Safety
|
||||
/// No other thread may access the module memory.
|
||||
unsafe fn relocate_single(&self, md: &Arc<Module>) -> Result<(), RelocateError> {
|
||||
unsafe fn relocate_single<'b>(
|
||||
&self,
|
||||
md: &'b Arc<Module>,
|
||||
resolver: &SymbolResolver<'b>,
|
||||
) -> Result<(), RelocateError> {
|
||||
// Unprotect the memory.
|
||||
let mut mem = match md.memory().unprotect() {
|
||||
Ok(v) => v,
|
||||
@ -226,7 +238,7 @@ impl<'a> RuntimeLinker<'a> {
|
||||
self.relocate_rela(md, mem.as_mut(), &mut relocated)?;
|
||||
|
||||
if !md.flags().contains(ModuleFlags::UNK4) {
|
||||
self.relocate_plt(md, mem.as_mut(), &mut relocated)?;
|
||||
self.relocate_plt(md, mem.as_mut(), &mut relocated, resolver)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -280,11 +292,12 @@ impl<'a> RuntimeLinker<'a> {
|
||||
}
|
||||
|
||||
/// See `reloc_jmplots` on the PS4 for a reference.
|
||||
fn relocate_plt(
|
||||
fn relocate_plt<'b>(
|
||||
&self,
|
||||
md: &Arc<Module>,
|
||||
md: &'b Arc<Module>,
|
||||
mem: &mut [u8],
|
||||
relocated: &mut [bool],
|
||||
resolver: &SymbolResolver<'b>,
|
||||
) -> Result<(), RelocateError> {
|
||||
// Do nothing if not a dynamic module.
|
||||
let info = match md.file_info() {
|
||||
@ -312,7 +325,7 @@ impl<'a> RuntimeLinker<'a> {
|
||||
}
|
||||
|
||||
// Resolve symbol.
|
||||
let sym = match self.resolve_symbol(md, reloc.symbol(), info) {
|
||||
let sym = match resolver.resolve_with_local(md, reloc.symbol(), ResolveFlags::UNK1) {
|
||||
Some((m, s)) => {
|
||||
m.memory().addr() + m.memory().base() + m.symbol(s).unwrap().value()
|
||||
}
|
||||
@ -331,41 +344,6 @@ impl<'a> RuntimeLinker<'a> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn resolve_symbol(
|
||||
&self,
|
||||
md: &Arc<Module>,
|
||||
index: usize,
|
||||
info: &FileInfo,
|
||||
) -> Option<(Arc<Module>, usize)> {
|
||||
// Check if symbol index is valid.
|
||||
let sym = md.symbols().get(index)?;
|
||||
|
||||
if index >= info.nchains() {
|
||||
return None;
|
||||
}
|
||||
|
||||
if self.app.sdk_ver() >= 0x5000000 || self.flags.contains(LinkerFlags::UNK2) {
|
||||
// Get library and module.
|
||||
let (li, mi) = match sym.decode_name() {
|
||||
Some(v) => (
|
||||
md.libraries().iter().find(|&l| l.id() == v.1),
|
||||
md.modules().iter().find(|&m| m.id() == v.2),
|
||||
),
|
||||
None => (None, None),
|
||||
};
|
||||
} else {
|
||||
todo!("resolve symbol with SDK version < 0x5000000");
|
||||
}
|
||||
|
||||
// Return this symbol if the binding is local. The reason we don't check this in the
|
||||
// first place is because we want to maintain the same behavior as the PS4.
|
||||
if sym.binding() == Symbol::STB_LOCAL {
|
||||
return Some((md.clone(), index));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
|
165
src/kernel/src/rtld/resolver.rs
Normal file
165
src/kernel/src/rtld/resolver.rs
Normal file
@ -0,0 +1,165 @@
|
||||
use super::Module;
|
||||
use bitflags::bitflags;
|
||||
use elf::{LibraryInfo, ModuleInfo, Symbol};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// An object to resolve a symbol from loaded (S)ELF.
|
||||
pub struct SymbolResolver<'a> {
|
||||
mods: &'a [Arc<Module>],
|
||||
new_algorithm: bool,
|
||||
}
|
||||
|
||||
impl<'a> SymbolResolver<'a> {
|
||||
pub fn new(mods: &'a [Arc<Module>], new_algorithm: bool) -> Self {
|
||||
Self {
|
||||
mods,
|
||||
new_algorithm,
|
||||
}
|
||||
}
|
||||
|
||||
/// See `find_symdef` on the PS4 for a reference.
|
||||
pub fn resolve_with_local(
|
||||
&self,
|
||||
md: &'a Arc<Module>,
|
||||
index: usize,
|
||||
flags: ResolveFlags,
|
||||
) -> Option<(&'a Arc<Module>, usize)> {
|
||||
// Check if symbol index is valid.
|
||||
let sym = md.symbols().get(index)?;
|
||||
let data = md.file_info().unwrap();
|
||||
|
||||
if index >= data.nchains() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Get symbol information.
|
||||
let (name, decoded_name, symmod, symlib, hash) = if self.new_algorithm {
|
||||
// Get library and module.
|
||||
let (li, mi) = match sym.decode_name() {
|
||||
Some(v) => (
|
||||
md.libraries().iter().find(|&l| l.id() == v.1),
|
||||
md.modules().iter().find(|&m| m.id() == v.2),
|
||||
),
|
||||
None => (None, None),
|
||||
};
|
||||
|
||||
// Calculate symbol hash.
|
||||
(
|
||||
Some(sym.name()),
|
||||
None,
|
||||
mi,
|
||||
li,
|
||||
Self::hash(Some(sym.name()), li.map(|i| i.name()), mi.map(|i| i.name())),
|
||||
)
|
||||
} else {
|
||||
todo!("resolve symbol with SDK version < 0x5000000");
|
||||
};
|
||||
|
||||
// Return this symbol if the binding is local. The reason we don't check this in the
|
||||
// first place is because we want to maintain the same behavior as the PS4.
|
||||
if sym.binding() == Symbol::STB_LOCAL {
|
||||
return Some((md, index));
|
||||
} else if sym.ty() == Symbol::STT_SECTION {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Lookup from global list if the symbol is not local.
|
||||
if let Some(v) = self.resolve(md, name, decoded_name, symmod, symlib, hash, flags) {
|
||||
return Some(v);
|
||||
} else if sym.binding() == Symbol::STB_WEAK {
|
||||
// TODO: Return sym_zero on obj_main.
|
||||
todo!("resolving weak symbol");
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// See `symlook_default` on the PS4 for a reference.
|
||||
pub fn resolve(
|
||||
&self,
|
||||
refmod: &'a Arc<Module>,
|
||||
name: Option<&str>,
|
||||
decoded_name: Option<&str>,
|
||||
symmod: Option<&ModuleInfo>,
|
||||
symlib: Option<&LibraryInfo>,
|
||||
hash: u64,
|
||||
flags: ResolveFlags,
|
||||
) -> Option<(&'a Arc<Module>, usize)> {
|
||||
// TODO: Resolve from global.
|
||||
// TODO: Resolve from DAGs.
|
||||
None
|
||||
}
|
||||
|
||||
pub fn hash(name: Option<&str>, libname: Option<&str>, modname: Option<&str>) -> u64 {
|
||||
let mut h: u64 = 0;
|
||||
let mut t: u64 = 0;
|
||||
let mut l: i32 = -1;
|
||||
let mut c = |b: u8| {
|
||||
t = (b as u64) + (h << 4);
|
||||
h = t & 0xf0000000;
|
||||
h = ((h >> 24) ^ t) & !h;
|
||||
};
|
||||
|
||||
// Hash symbol name.
|
||||
if let Some(v) = name {
|
||||
for b in v.bytes() {
|
||||
c(b);
|
||||
|
||||
if b == b'#' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
l = 0;
|
||||
}
|
||||
|
||||
// Hash library name.
|
||||
let v = match libname {
|
||||
Some(v) => v,
|
||||
None => return h,
|
||||
};
|
||||
|
||||
if l == 0 {
|
||||
// This seems like a bug in the PS4 because it hash on # two times.
|
||||
c(b'#');
|
||||
}
|
||||
|
||||
l = 0;
|
||||
|
||||
for b in v.bytes() {
|
||||
c(b);
|
||||
|
||||
if b == b'#' {
|
||||
l = 0x23; // #
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Hash module name.
|
||||
let v = match modname {
|
||||
Some(v) => v,
|
||||
None => return h,
|
||||
};
|
||||
|
||||
if l == 0 {
|
||||
c(b'#');
|
||||
}
|
||||
|
||||
for b in v.bytes() {
|
||||
c(b);
|
||||
|
||||
if b == b'#' {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
h
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// Flags to control behavior of [`SymbolResolver`].
|
||||
pub struct ResolveFlags: u32 {
|
||||
const UNK1 = 0x00000001;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user