From b0db7bf54ea32513cc11562dc66c54fdddbe6d4e Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Mon, 1 Apr 2024 10:25:21 +0700 Subject: [PATCH] Moves obj_list, list_main and list_global out of RTLD (#790) --- .vscode/settings.json | 1 + src/gmtx/src/guard.rs | 58 ++++++++-- src/gmtx/src/lib.rs | 9 +- src/kernel/src/main.rs | 4 +- src/kernel/src/process/binary/mod.rs | 41 ++++++- src/kernel/src/rtld/mod.rs | 153 ++++++++++----------------- src/kernel/src/rtld/resolver.rs | 21 ++-- 7 files changed, 161 insertions(+), 126 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index a9f7b97d3..78290e746 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -10,6 +10,7 @@ "lldb.launch.initCommands": [ "settings set target.x86-disassembly-flavor intel" ], + "rust-analyzer.imports.granularity.group": "module", "[rust]": { "editor.defaultFormatter": "rust-lang.rust-analyzer", "editor.formatOnSave": true diff --git a/src/gmtx/src/guard.rs b/src/gmtx/src/guard.rs index c122f1c22..03258954f 100644 --- a/src/gmtx/src/guard.rs +++ b/src/gmtx/src/guard.rs @@ -1,5 +1,6 @@ use crate::{GroupGuard, Gutex}; use std::fmt::{Display, Formatter}; +use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; /// RAII structure used to release the shared read access of a lock when dropped. @@ -40,19 +41,64 @@ unsafe impl<'a, T: Sync> Sync for GutexReadGuard<'a, T> {} /// RAII structure used to release the exclusive write access of a lock when dropped. #[allow(dead_code)] pub struct GutexWriteGuard<'a, T> { - mtx: &'a Gutex, + active: *mut usize, + value: *mut T, lock: GroupGuard<'a>, } impl<'a, T> GutexWriteGuard<'a, T> { - pub(crate) fn new(lock: GroupGuard<'a>, mtx: &'a Gutex) -> Self { - Self { lock, mtx } + /// # Safety + /// `active` and `value` must be protected by `lock`. + pub(crate) unsafe fn new(active: *mut usize, value: *mut T, lock: GroupGuard<'a>) -> Self { + Self { + active, + value, + lock, + } + } + + pub fn map &mut O>(self, f: F) -> GutexWriteGuard<'a, O> { + let active = self.active; + let value = f(unsafe { &mut *self.value }); + let lock = GroupGuard { + group: self.lock.group, + phantom: PhantomData, + }; + + std::mem::forget(self); + + GutexWriteGuard { + active, + value, + lock, + } + } +} + +impl<'a, T> GutexWriteGuard<'a, Option> { + pub fn ok_or(self, err: E) -> Result, E> { + match unsafe { (*self.value).as_mut() } { + Some(v) => { + let v = GutexWriteGuard { + active: self.active, + value: v as *mut T, + lock: GroupGuard { + group: self.lock.group, + phantom: PhantomData, + }, + }; + + std::mem::forget(self); + Ok(v) + } + None => Err(err), + } } } impl<'a, T> Drop for GutexWriteGuard<'a, T> { fn drop(&mut self) { - unsafe { *self.mtx.active.get() = 0 }; + unsafe { *self.active = 0 }; } } @@ -60,13 +106,13 @@ impl<'a, T> Deref for GutexWriteGuard<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { - unsafe { &*self.mtx.value.get() } + unsafe { &*self.value } } } impl<'a, T> DerefMut for GutexWriteGuard<'a, T> { fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.mtx.value.get() } + unsafe { &mut *self.value } } } diff --git a/src/gmtx/src/lib.rs b/src/gmtx/src/lib.rs index 09ae2a01c..46a96b70d 100644 --- a/src/gmtx/src/lib.rs +++ b/src/gmtx/src/lib.rs @@ -87,6 +87,7 @@ impl Gutex { let lock = self.group.lock(); let active = self.active.get(); + // SAFETY: This is safe because we own the lock that protect both active and value. unsafe { if *active != 0 { panic!( @@ -95,9 +96,9 @@ impl Gutex { } *active = usize::MAX; - } - GutexWriteGuard::new(lock, self) + GutexWriteGuard::new(&mut *active, &mut *self.value.get(), lock) + } } } @@ -211,8 +212,8 @@ unsafe impl Sync for GutexGroup {} /// An RAII object used to release the lock on [`GutexGroup`]. This type cannot be send because it /// will cause data race on the group when dropping if more than one [`GroupGuard`] are active. struct GroupGuard<'a> { - group: &'a GutexGroup, - phantom: PhantomData>, // For !Send and !Sync. + pub group: &'a GutexGroup, + pub phantom: PhantomData>, // For !Send and !Sync. } impl<'a> GroupGuard<'a> { diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index 2a994cee3..40f863aa5 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -426,7 +426,7 @@ fn run() -> Result<(), KernelError> { info!("Loading {path}."); - let libkernel = ld + let (libkernel, _) = ld .load(path, flags, false, true, &main) .map_err(|e| KernelError::FailedToLoadLibkernel(e.into()))?; @@ -440,7 +440,7 @@ fn run() -> Result<(), KernelError> { info!("Loading {path}."); - let libc = ld + let (libc, _) = ld .load(path, flags, false, true, &main) .map_err(|e| KernelError::FailedToLoadLibSceLibcInternal(e.into()))?; diff --git a/src/kernel/src/process/binary/mod.rs b/src/kernel/src/process/binary/mod.rs index 28a7122b2..3569e8e1a 100644 --- a/src/kernel/src/process/binary/mod.rs +++ b/src/kernel/src/process/binary/mod.rs @@ -7,15 +7,52 @@ use std::sync::Arc; /// that field. #[derive(Debug)] pub struct Binaries { - app: Arc, // obj_main + list: Vec>, // obj_list + obj_tail + mains: Vec>, // list_main + globals: Vec>, // list_global + app: Arc, // obj_main } impl Binaries { pub fn new(app: Arc) -> Self { - Self { app } + Self { + list: vec![app.clone()], + mains: vec![app.clone()], + globals: Vec::new(), + app, + } + } + + /// The returned iterator will never be empty and the first item is always the application + /// itself. + pub fn list(&self) -> impl ExactSizeIterator> { + self.list.iter() + } + + /// The returned iterator will never be empty and the first item is always the application + /// itself. + pub fn mains(&self) -> impl Iterator> { + self.mains.iter() + } + + pub fn globals(&self) -> impl Iterator> { + self.globals.iter() } pub fn app(&self) -> &Arc { &self.app } + + pub fn push(&mut self, md: Arc, main: bool) { + if main { + self.list.push(md.clone()); + self.mains.push(md); + } else { + self.list.push(md); + } + } + + pub fn push_global(&mut self, md: Arc) { + self.globals.push(md); + } } diff --git a/src/kernel/src/rtld/mod.rs b/src/kernel/src/rtld/mod.rs index 8a3119b1a..504197e95 100644 --- a/src/kernel/src/rtld/mod.rs +++ b/src/kernel/src/rtld/mod.rs @@ -13,7 +13,7 @@ use crate::syscalls::{SysErr, SysIn, SysOut, Syscalls}; use crate::vm::{MemoryUpdateError, MmapError, Protections}; use bitflags::bitflags; use elf::{DynamicFlags, Elf, FileType, ReadProgramError, Relocation, Symbol}; -use gmtx::{Gutex, GutexGroup}; +use gmtx::{Gutex, GutexGroup, GutexWriteGuard}; use macros::Errno; use sha1::{Digest, Sha1}; use std::borrow::Cow; @@ -36,10 +36,7 @@ pub struct RuntimeLinker { fs: Arc, ee: Arc, // TODO: Move all fields after this to proc. - list: Gutex>>, // obj_list + obj_tail kernel: Gutex>>, // obj_kernel - mains: Gutex>>, // list_main - globals: Gutex>>, // list_global tls: Gutex, flags: Gutex, } @@ -57,10 +54,7 @@ impl RuntimeLinker { let ld = Arc::new(Self { fs: fs.clone(), ee: ee.clone(), - list: gg.spawn(Vec::new()), kernel: gg.spawn(None), - mains: gg.spawn(Vec::new()), - globals: gg.spawn(Vec::new()), tls: gg.spawn(TlsAlloc { max_index: 1, last_offset: 0, @@ -157,16 +151,12 @@ impl RuntimeLinker { // TODO: Apply logic from aio_proc_rundown_exec. // TODO: Apply logic from gs_is_event_handler_process_exec. let app = Arc::new(app); - let mut list = self.list.write(); - let mut mains = self.mains.write(); let mut bin = td.proc().bin_mut(); if bin.is_some() { todo!("multiple exec on the same process"); } - list.push(app.clone()); - mains.push(app.clone()); *bin = Some(Binaries::new(app.clone())); *self.flags.write() = flags; @@ -176,25 +166,31 @@ impl RuntimeLinker { /// See `load_object`, `do_load_object` and `self_load_shared_object` on the PS4 for a /// reference. - pub fn load( + pub fn load<'a>( &self, path: impl AsRef, _: LoadFlags, force: bool, main: bool, - td: &VThread, - ) -> Result, LoadError> { + td: &'a VThread, + ) -> Result<(Arc, GutexWriteGuard<'a, Binaries>), LoadError> { + // Check if module with the same path already loaded. let path = path.as_ref(); + let mut bin = td.proc().bin_mut().ok_or(LoadError::NoExe)?; + let loaded = bin.list().skip(1).find(|m| m.path() == path); + + if let Some(v) = loaded { + return Ok((v.clone(), bin)); + } - // Check if already loaded. + // Check if module with the same base name already loaded. let name = path.file_name().unwrap().to_owned(); - let mut bin = td.proc().bin_mut(); - let bin = bin.as_mut().ok_or(LoadError::NoExe)?; - let mut list = self.list.write(); if !force { - if let Some(m) = list.iter().skip(1).find(|m| m.names().contains(&name)) { - return Ok(m.clone()); + let loaded = bin.list().skip(1).find(|m| m.names().contains(&name)); + + if let Some(v) = loaded { + return Ok((v.clone(), bin)); } } @@ -238,7 +234,7 @@ impl RuntimeLinker { loop { // Check if the current value has been used. - if !list.iter().any(|m| m.tls_index() == index) { + if !bin.list().any(|m| m.tls_index() == index) { break; } @@ -284,13 +280,9 @@ impl RuntimeLinker { // Add to list. let module = entry.data().clone().downcast::().unwrap(); - list.push(module.clone()); - - if main { - self.mains.write().push(module.clone()); - } + bin.push(module.clone(), main); - Ok(module) + Ok((module, bin)) } /// See `init_dag` on the PS4 for a reference. @@ -332,11 +324,8 @@ impl RuntimeLinker { } // Setup resolver. - let mains = self.mains.read(); - let globals = self.globals.read(); let resolver = SymbolResolver::new( - &mains, - &globals, + bin, bin.app().sdk_ver() >= 0x5000000 || self.flags.read().contains(LinkerFlags::HAS_ASAN), ); @@ -354,7 +343,7 @@ impl RuntimeLinker { lib, SymbolResolver::hash(Some(name.as_ref()), lib, mname), flags | ResolveFlags::UNK3 | ResolveFlags::UNK4, - &dags, + dags.deref(), ) }; @@ -403,8 +392,7 @@ impl RuntimeLinker { let out: *mut usize = i.args[2].into(); // Get target module. - let list = self.list.read(); - let md = match list.iter().find(|m| m.id() == handle) { + let md = match bin.list().find(|m| m.id() == handle) { Some(v) => v, None => return Err(SysErr::Raw(ESRCH)), }; @@ -443,37 +431,24 @@ impl RuntimeLinker { } // Copy module ID. - let list = self.list.read(); + let list = bin.list(); + let len = list.len(); - if list.len() > max { + if len > max { return Err(SysErr::Raw(ENOMEM)); } - for (i, m) in list.iter().enumerate() { + for (i, m) in list.enumerate() { unsafe { *buf.add(i) = m.id() }; } // Set copied. - unsafe { *copied = list.len() }; - - info!("Copied {} module IDs for dynamic linking.", list.len()); + unsafe { *copied = len }; Ok(SysOut::ZERO) } fn sys_dynlib_load_prx(self: &Arc, td: &VThread, i: &SysIn) -> Result { - // Check if application is a dynamic SELF. - let mut bin_guard = td.proc().bin_mut(); - let bin = bin_guard.as_mut().ok_or(SysErr::Raw(EPERM))?; - - let sdk_ver = bin.app().sdk_ver(); - - if bin.app().file_info().is_none() { - return Err(SysErr::Raw(EPERM)); - } - - drop(bin_guard); - // Not sure what is this. Maybe kernel only flags? let mut flags: u32 = i.args[1].try_into().unwrap(); @@ -495,31 +470,18 @@ impl RuntimeLinker { info!("Loading {name} with {flags:#x}."); - // Start locking from here and keep the lock until we finished. - let mut globals = self.globals.write(); - - // Check if already loaded. - let list = self.list.read(); - let md = match list.iter().skip(1).find(|m| m.path() == name) { - Some(v) => v.clone(), - None => { - // Drop list lock first because load is going to acquire the write lock on it. - drop(list); - - // TODO: Refactor this for readability. - self.load( - name, - LoadFlags::from_bits_retain(((flags & 1) << 5) + ((flags >> 10) & 0x40) + 2), - true, // TODO: This hard-coded because we don't support relative path yet. - false, - td, - )? - } - }; + // TODO: Refactor this for readability. + let (md, mut bin) = self.load( + name, + LoadFlags::from_bits_retain(((flags & 1) << 5) + ((flags >> 10) & 0x40) + 2), + true, // TODO: This hard-coded because we don't support relative path yet. + false, + td, + )?; // Add to global list if it is not in the list yet. - if !globals.iter().any(|m| Arc::ptr_eq(m, &md)) { - globals.push(md.clone()); + if !bin.globals().any(|m| Arc::ptr_eq(m, &md)) { + bin.push_global(md.clone()); } // The PS4 checking on the refcount to see if it need to do relocation. We can't do the same @@ -545,21 +507,21 @@ impl RuntimeLinker { drop(mf); // init_dag need to lock this. // Initialize DAG and relocate the module. - let list = self.list.read(); - let mains = self.mains.read(); let resolver = SymbolResolver::new( - &mains, - &globals, - sdk_ver >= 0x5000000 || self.flags.read().contains(LinkerFlags::HAS_ASAN), + &bin, + bin.app().sdk_ver() >= 0x5000000 + || self.flags.read().contains(LinkerFlags::HAS_ASAN), ); self.init_dag(&md); - if unsafe { self.relocate(&md, &list, &resolver).is_err() } { + if unsafe { self.relocate(&md, bin.list(), &resolver).is_err() } { todo!("sys_dynlib_load_prx with location failed"); } } + drop(bin); + // Print the module. let mut log = info!(); writeln!(log, "Module {} is loaded with ID = {}.", name, md.id()).unwrap(); @@ -634,19 +596,15 @@ impl RuntimeLinker { return Err(SysErr::Raw(EINVAL)); } - // Starting locking from here until relocation is completed to prevent the other thread - // hijack our current states. - let list = self.list.read(); - - for md in list.deref() { + // Initialize module DAG. + for md in bin.list() { self.init_dag(md); } // Initialize TLS. - let mains = self.mains.read(); let mut tls = self.tls.write(); - for md in mains.deref() { + for md in bin.mains() { // Skip if already initialized. let mut flags = md.flags_mut(); @@ -680,16 +638,14 @@ impl RuntimeLinker { drop(tls); // Do relocation. - let globals = self.globals.read(); let resolver = SymbolResolver::new( - &mains, - &globals, + bin, bin.app().sdk_ver() >= 0x5000000 || self.flags.read().contains(LinkerFlags::HAS_ASAN), ); info!("Relocating initial modules."); - unsafe { self.relocate(bin.app(), &list, &resolver) }?; + unsafe { self.relocate(bin.app(), bin.list(), &resolver) }?; // TODO: Apply the remaining logics from the PS4. Ok(SysOut::ZERO) @@ -699,10 +655,10 @@ impl RuntimeLinker { /// /// # Safety /// No other threads may access the memory of all loaded modules. - unsafe fn relocate( + unsafe fn relocate<'a>( &self, md: &Arc, - list: &[Arc], + list: impl Iterator>, resolver: &SymbolResolver, ) -> Result<(), RelocateError> { // TODO: Implement flags & 0x800. @@ -945,8 +901,7 @@ impl RuntimeLinker { } // Lookup the module. - let modules = self.list.read(); - let md = match modules.iter().find(|m| m.id() == handle) { + let md = match bin.list().find(|m| m.id() == handle) { Some(v) => v, None => return Err(SysErr::Raw(ESRCH)), }; @@ -1061,10 +1016,8 @@ impl RuntimeLinker { return Err(SysErr::Raw(EINVAL)); } - let list = self.list.read(); - - let module = list - .iter() + let module = bin + .list() .find(|m| m.id() == handle) .ok_or(SysErr::Raw(ESRCH))?; diff --git a/src/kernel/src/rtld/resolver.rs b/src/kernel/src/rtld/resolver.rs index 848d2ac93..bf83f8d7f 100644 --- a/src/kernel/src/rtld/resolver.rs +++ b/src/kernel/src/rtld/resolver.rs @@ -1,23 +1,20 @@ use super::Module; +use crate::process::Binaries; use bitflags::bitflags; use elf::Symbol; use std::borrow::Cow; +use std::ops::Deref; use std::sync::Arc; /// An object to resolve a symbol from loaded (S)ELF. pub struct SymbolResolver<'a> { - mains: &'a [Arc], - globals: &'a [Arc], + bin: &'a Binaries, new_algorithm: bool, } impl<'a> SymbolResolver<'a> { - pub fn new(mains: &'a [Arc], globals: &'a [Arc], new_algorithm: bool) -> Self { - Self { - mains, - globals, - new_algorithm, - } + pub fn new(bin: &'a Binaries, new_algorithm: bool) -> Self { + Self { bin, new_algorithm } } /// See `find_symdef` on the PS4 for a reference. @@ -131,13 +128,13 @@ impl<'a> SymbolResolver<'a> { symlib, hash, flags, - self.mains, + self.bin.mains(), ) { result = Some(v); } // Resolve from list_global. - for md in self.globals { + for md in self.bin.globals() { if let Some((ref md, sym)) = result { if md.symbol(sym).unwrap().binding() != Symbol::STB_WEAK { break; @@ -152,7 +149,7 @@ impl<'a> SymbolResolver<'a> { symlib, hash, flags, - &md.dag_static(), + md.dag_static().deref(), ) { if result.is_none() || md.symbol(sym).unwrap().binding() != Symbol::STB_WEAK { result = Some((md, sym)); @@ -173,7 +170,7 @@ impl<'a> SymbolResolver<'a> { symlib: Option<&str>, hash: u64, flags: ResolveFlags, - list: &'a [Arc], + list: impl IntoIterator>, ) -> Option<(Arc, usize)> { // Get module name. let symmod = if !flags.contains(ResolveFlags::UNK2) {