From f8c7631de0afbd9d72ec14441fceae70601d0a36 Mon Sep 17 00:00:00 2001 From: Putta Khunchalee Date: Tue, 25 Jul 2023 16:36:23 +0700 Subject: [PATCH] Revises dynamic data parser (#272) --- src/elf/src/dynamic.rs | 973 +++++++------------------------- src/elf/src/info.rs | 387 +++++++------ src/elf/src/lib.rs | 37 +- src/elf/src/library.rs | 38 ++ src/elf/src/module.rs | 19 + src/fs/src/lib.rs | 7 - src/kernel/src/ee/native/mod.rs | 2 +- src/kernel/src/log/entry.rs | 13 +- src/kernel/src/main.rs | 17 +- src/kernel/src/process/mod.rs | 8 +- src/kernel/src/rtld/mod.rs | 20 +- src/kernel/src/rtld/module.rs | 118 +++- src/kernel/src/syscalls/mod.rs | 4 +- src/system/Cargo.toml | 3 - src/system/src/lib.rs | 90 --- 15 files changed, 590 insertions(+), 1146 deletions(-) create mode 100644 src/elf/src/library.rs create mode 100644 src/elf/src/module.rs diff --git a/src/elf/src/dynamic.rs b/src/elf/src/dynamic.rs index 700d9df88..7e5192d57 100644 --- a/src/elf/src/dynamic.rs +++ b/src/elf/src/dynamic.rs @@ -1,809 +1,216 @@ use bitflags::bitflags; use byteorder::{ByteOrder, LE}; -use std::collections::HashMap; use std::fmt::{Display, Formatter}; -use std::ops::Index; -use std::slice::SliceIndex; -use thiserror::Error; -/// Contains data required for dynamic linking. -pub struct DynamicLinking { - needed: Vec, - pltrelsz: usize, - pltgot: u64, - relasz: usize, - relaent: usize, - pltrel: u64, - fingerprint: u64, - flags: Option, - filename: u64, - module_info: ModuleInfo, - dependencies: HashMap, - libraries: HashMap, - symbols: Vec, - symbol_lookup_table: Vec, - jmprel: usize, - rela: usize, - data: DynlibData, +/// An iterator over the `PT_DYNAMIC`. +pub struct DynamicEntries<'a> { + next: &'a [u8], } -impl DynamicLinking { - pub const DT_NULL: i64 = 0; - pub const DT_NEEDED: i64 = 1; - pub const DT_PLTRELSZ: i64 = 2; - pub const DT_PLTGOT: i64 = 3; - pub const DT_RELA: i64 = 7; - pub const DT_RELASZ: i64 = 8; - pub const DT_RELAENT: i64 = 9; - pub const DT_STRSZ: i64 = 10; - pub const DT_SYMENT: i64 = 11; - pub const DT_INIT: i64 = 12; - pub const DT_FINI: i64 = 13; - pub const DT_SONAME: i64 = 14; - pub const DT_SYMBOLIC: i64 = 16; - pub const DT_PLTREL: i64 = 20; - pub const DT_DEBUG: i64 = 21; - pub const DT_TEXTREL: i64 = 22; - pub const DT_INIT_ARRAY: i64 = 25; - pub const DT_FINI_ARRAY: i64 = 26; - pub const DT_INIT_ARRAYSZ: i64 = 27; - pub const DT_FINI_ARRAYSZ: i64 = 28; - pub const DT_FLAGS: i64 = 30; - pub const DT_PREINIT_ARRAY: i64 = 32; - pub const DT_PREINIT_ARRAYSZ: i64 = 33; - pub const DT_SCE_FINGERPRINT: i64 = 0x61000007; - pub const DT_SCE_FILENAME: i64 = 0x61000009; - pub const DT_SCE_MODULE_INFO: i64 = 0x6100000d; - pub const DT_SCE_NEEDED_MODULE: i64 = 0x6100000f; - pub const DT_SCE_MODULE_ATTR: i64 = 0x61000011; - pub const DT_SCE_EXPORT_LIB: i64 = 0x61000013; - pub const DT_SCE_IMPORT_LIB: i64 = 0x61000015; - pub const DT_SCE_EXPORT_LIB_ATTR: i64 = 0x61000017; - pub const DT_SCE_IMPORT_LIB_ATTR: i64 = 0x61000019; - pub const DT_SCE_HASH: i64 = 0x61000025; - pub const DT_SCE_PLTGOT: i64 = 0x61000027; - pub const DT_SCE_JMPREL: i64 = 0x61000029; - pub const DT_SCE_PLTREL: i64 = 0x6100002b; - pub const DT_SCE_PLTRELSZ: i64 = 0x6100002d; - pub const DT_SCE_RELA: i64 = 0x6100002f; - pub const DT_SCE_RELASZ: i64 = 0x61000031; - pub const DT_SCE_RELAENT: i64 = 0x61000033; - pub const DT_SCE_STRTAB: i64 = 0x61000035; - pub const DT_SCE_STRSZ: i64 = 0x61000037; - pub const DT_SCE_SYMTAB: i64 = 0x61000039; - pub const DT_SCE_SYMENT: i64 = 0x6100003b; - pub const DT_SCE_HASHSZ: i64 = 0x6100003d; - pub const DT_SCE_SYMTABSZ: i64 = 0x6100003f; - - pub(super) fn parse(data: Vec, dynlib: Vec) -> Result { - // Simple check to see if data valid. - if data.len() % 16 != 0 { - return Err(ParseError::InvalidDataSize); - } - - // Find the offset of data tables. - let mut strtab: Option = None; - let mut strsz: Option = None; - - for data in data.chunks_exact(16) { - // Read fields. - let tag = LE::read_i64(&data); - let value = &data[8..]; - - // Parse entry. - match tag { - Self::DT_SCE_STRTAB => strtab = Some(LE::read_u64(value)), - Self::DT_STRSZ | Self::DT_SCE_STRSZ => strsz = Some(LE::read_u64(value)), - _ => {} - } - } - - // Check string table. - let strtab = strtab.ok_or(ParseError::NoStrtab)? as usize; - let strsz = strsz.ok_or(ParseError::NoStrsz)? as usize; - - if strtab + strsz > dynlib.len() { - return Err(ParseError::InvalidStrtab); - } - - let dynlib = DynlibData { - data: dynlib, - strtab, - strsz, - }; - - // Get data tables. - let parse_module_info = |value: &[u8]| -> Option { - let name = LE::read_u32(&value) as usize; - let version_minor = value[4]; - let version_major = value[5]; - let id = LE::read_u16(&value[6..]); - - Some(ModuleInfo { - id, - name: dynlib.str(name)?, - version_major, - version_minor, - }) - }; - - // Parse entries. - let mut needed: Vec = Vec::new(); - let mut pltrelsz: Option = None; - let mut pltgot: Option = None; - let mut relasz: Option = None; - let mut relaent: Option = None; - let mut syment: Option = None; - let mut pltrel: Option = None; - let mut fingerprint: Option = None; - let mut flags: Option = None; - let mut filename: Option = None; - let mut module_info: Option = None; - let mut dependencies: HashMap = HashMap::new(); - let mut libraries: HashMap = HashMap::new(); - let mut hash: Option = None; - let mut jmprel: Option = None; - let mut rela: Option = None; - let mut symtab: Option = None; - let mut hashsz: Option = None; - let mut symtabsz: Option = None; - - for (index, data) in data.chunks_exact(16).enumerate() { - use std::collections::hash_map::Entry; - - // Read fields. - let tag = LE::read_i64(&data); - let value = &data[8..]; - - // Parse entry. - match tag { - Self::DT_NULL => break, - Self::DT_NEEDED => match dynlib.str(LE::read_u64(value) as usize) { - Some(v) => needed.push(v), - None => return Err(ParseError::InvalidNeeded(index)), - }, - Self::DT_PLTRELSZ | Self::DT_SCE_PLTRELSZ => pltrelsz = Some(LE::read_u64(value)), - Self::DT_PLTGOT | Self::DT_SCE_PLTGOT => pltgot = Some(LE::read_u64(value)), - Self::DT_RELASZ | Self::DT_SCE_RELASZ => relasz = Some(LE::read_u64(value)), - Self::DT_RELAENT | Self::DT_SCE_RELAENT => relaent = Some(LE::read_u64(value)), - Self::DT_STRSZ | Self::DT_SCE_STRSZ => {} - Self::DT_SYMENT | Self::DT_SCE_SYMENT => syment = Some(LE::read_u64(value)), - Self::DT_INIT => {} - Self::DT_FINI => {} - Self::DT_SONAME => {} - Self::DT_SYMBOLIC => {} - Self::DT_PLTREL | Self::DT_SCE_PLTREL => pltrel = Some(LE::read_u64(value)), - Self::DT_DEBUG => {} - Self::DT_TEXTREL => {} - Self::DT_INIT_ARRAY => {} - Self::DT_FINI_ARRAY => {} - Self::DT_INIT_ARRAYSZ => {} - Self::DT_FINI_ARRAYSZ => {} - Self::DT_FLAGS => flags = Some(ModuleFlags::from_bits_retain(LE::read_u64(value))), - Self::DT_PREINIT_ARRAY => {} - Self::DT_PREINIT_ARRAYSZ => {} - Self::DT_SCE_FINGERPRINT => fingerprint = Some(LE::read_u64(value)), - Self::DT_SCE_FILENAME => filename = Some(LE::read_u64(value)), - Self::DT_SCE_MODULE_INFO => { - module_info = Some(match parse_module_info(value) { - Some(v) if v.id == 0 => v, - _ => return Err(ParseError::InvalidModuleInfo), - }); - } - Self::DT_SCE_NEEDED_MODULE => match parse_module_info(value) { - Some(v) if v.id != 0 => match dependencies.entry(v.id) { - Entry::Occupied(_) => { - return Err(ParseError::DuplicatedNeededModule(index)); - } - Entry::Vacant(e) => { - e.insert(v); - } - }, - _ => return Err(ParseError::InvalidNeededModule(index)), - }, - Self::DT_SCE_MODULE_ATTR => {} - Self::DT_SCE_EXPORT_LIB | Self::DT_SCE_IMPORT_LIB => { - // Parse the value. - let name = LE::read_u32(value) as usize; - let version = LE::read_u16(&value[4..]); - let id = LE::read_u16(&value[6..]); - let is_export = tag == Self::DT_SCE_EXPORT_LIB; - let info = LibraryInfo { - id, - name: match dynlib.str(name) { - Some(v) => v, - None => { - return Err(if is_export { - ParseError::InvalidExport(index) - } else { - ParseError::InvalidImport(index) - }) - } - }, - version, - is_export, - }; - - // Store the info. - match libraries.entry(id) { - Entry::Occupied(_) => return Err(ParseError::DuplicatedLibrary(index)), - Entry::Vacant(e) => e.insert(info), - }; - } - Self::DT_SCE_EXPORT_LIB_ATTR => {} - Self::DT_SCE_IMPORT_LIB_ATTR => {} - Self::DT_SCE_HASH => hash = Some(LE::read_u64(value)), - Self::DT_SCE_JMPREL => jmprel = Some(LE::read_u64(value)), - Self::DT_SCE_RELA => rela = Some(LE::read_u64(value)), - Self::DT_SCE_STRTAB => {} - Self::DT_SCE_SYMTAB => symtab = Some(LE::read_u64(value)), - Self::DT_SCE_HASHSZ => hashsz = Some(LE::read_u64(value)), - Self::DT_SCE_SYMTABSZ => symtabsz = Some(LE::read_u64(value)), - _ => return Err(ParseError::UnknownTag(tag)), - } - } - - // Check symbol hash table. - let hash = hash.ok_or(ParseError::NoHash)? as usize; - let hashsz = hashsz.ok_or(ParseError::NoHashsz)? as usize; - - if hashsz < 8 || hashsz % 4 != 0 { - return Err(ParseError::InvalidHashsz); - } - - // Parse symbol hash table. - let mut symbol_lookup_table: Vec = vec![0u32; hashsz / 4]; - - match dynlib.get(hash..(hash + hashsz)) { - Some(v) => LE::read_u32_into(v, &mut symbol_lookup_table), - None => return Err(ParseError::InvalidHash), - } - - // Check size of symbol entry. - let syment = match syment { - Some(v) => { - if v != 24 { - // sizeof(Elf64_Sym) - return Err(ParseError::InvalidSyment); - } - - v as usize - } - None => return Err(ParseError::NoSyment), - }; - - // Parse symbol table. - let symbols = match (symtab, symtabsz) { - (Some(offset), Some(size)) => { - let offset = offset as usize; - let size = size as usize; - - // Check if size valid. - if size % syment != 0 { - return Err(ParseError::InvalidSymtabsz); - } - - // Get the table. - let table = match dynlib.get(offset..(offset + size)) { - Some(v) => v, - None => return Err(ParseError::InvalidSymtab), - }; - - // Parse the table. - let mut symbols: Vec = Vec::with_capacity(size / syment); - - for (i, e) in table.chunks(syment).enumerate() { - let name = LE::read_u32(e) as usize; - let info = e[4]; - let value = LE::read_u64(&e[8..]) as usize; - - symbols.push(SymbolInfo { - name: match dynlib.str(name) { - Some(v) => v, - None => return Err(ParseError::InvalidSymbol(i)), - }, - info, - value, - }) - } - - symbols - } - (None, _) => return Err(ParseError::NoSymtab), - (_, None) => return Err(ParseError::NoSymtabsz), - }; - - let parsed = Self { - needed, - pltrelsz: pltrelsz.ok_or(ParseError::NoPltrelsz)? as usize, - pltgot: pltgot.ok_or(ParseError::NoPltgot)?, - relasz: relasz.ok_or(ParseError::NoRelasz)? as usize, - relaent: relaent.ok_or(ParseError::NoRelaent)? as usize, - pltrel: pltrel.ok_or(ParseError::NoPltrel)?, - fingerprint: fingerprint.ok_or(ParseError::NoFingerprint)?, - flags: match flags { - Some(v) => { - if v.is_empty() { - None - } else { - Some(v) - } - } - None => return Err(ParseError::NoFlags), - }, - filename: filename.ok_or(ParseError::NoFilename)?, - module_info: module_info.ok_or(ParseError::NoModuleInfo)?, - dependencies, - libraries, - symbols, - symbol_lookup_table, - jmprel: jmprel.ok_or(ParseError::NoJmprel)? as usize, - rela: rela.ok_or(ParseError::NoRela)? as usize, - data: dynlib, - }; - - // Check values. - if parsed.relaent != 24 { - // sizeof(Elf64_Rela) - return Err(ParseError::InvalidRelaent); - } else if parsed.pltrel != DynamicLinking::DT_RELA as _ { - return Err(ParseError::InvalidPltrel); - } - - Ok(parsed) - } - - pub fn needed(&self) -> &[String] { - self.needed.as_ref() - } - - pub fn flags(&self) -> Option { - self.flags - } - - pub fn module_info(&self) -> &ModuleInfo { - &self.module_info - } - - pub fn dependencies(&self) -> &HashMap { - &self.dependencies - } - - pub fn libraries(&self) -> &HashMap { - &self.libraries - } - - pub fn symbols(&self) -> &[SymbolInfo] { - self.symbols.as_ref() +impl<'a> DynamicEntries<'a> { + pub(crate) fn new(next: &'a [u8]) -> Self { + Self { next } } +} - pub fn relocation_entries(&self) -> RelocationEntries<'_> { - RelocationEntries { - relaent: self.relaent, - next: &self.data[self.rela..(self.rela + self.relasz)], - } - } +impl<'a> Iterator for DynamicEntries<'a> { + type Item = (DynamicTag, [u8; 8]); - pub fn plt_relocation(&self) -> RelocationEntries<'_> { - RelocationEntries { - relaent: self.relaent, - next: &self.data[self.jmprel..(self.jmprel + self.pltrelsz)], + fn next(&mut self) -> Option { + // Check if all entries has been read. + if self.next.is_empty() { + return None; } - } - - pub fn lookup_symbol(&self, hash: u32, name: &str) -> Option<&SymbolInfo> { - // Get hash table. - let bucket_count = self.symbol_lookup_table[0] as usize; - let chain_count = self.symbol_lookup_table[1] as usize; - let buckets = &self.symbol_lookup_table[2..]; - let chains = &buckets[bucket_count..]; - - // Lookup. - let mut index = buckets[hash as usize % bucket_count] as usize; - - while index != 0 { - if index >= chain_count { - return None; - } - - // Parse symbol name. - let sym = &self.symbols[index]; - let (sym_name, lib_id, mod_id) = match sym.decode_name() { - Some(v) => v, - None => { - index = chains[index] as usize; - continue; - } - }; - - if mod_id != 0 { - index = chains[index] as usize; - continue; - } - - // Get target library. - let lib = match self.libraries.get(&lib_id) { - Some(v) => v, - None => panic!("Unexpected library ID: {lib_id}"), - }; - if !lib.is_export() { - panic!("The target library is not an exported library."); - } - - // Check if matched. - if name == format!("{sym_name}#{}#{}", lib.name, self.module_info.name) { - return Some(sym); - } - - index = chains[index] as usize; + // Read the entry. + let tag = LE::read_i64(self.next); + let value = self.next[8..16].try_into().unwrap(); + + // Move to next entry. + self.next = &self.next[16..]; + + Some((DynamicTag(tag), value)) + } +} + +/// Tag of each dynamic entry. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct DynamicTag(i64); + +impl DynamicTag { + pub const DT_NULL: Self = Self(0); + pub const DT_NEEDED: Self = Self(1); + pub const DT_PLTRELSZ: Self = Self(2); + pub const DT_PLTGOT: Self = Self(3); + pub const DT_HASH: Self = Self(4); + pub const DT_STRTAB: Self = Self(5); + pub const DT_SYMTAB: Self = Self(6); + pub const DT_RELA: Self = Self(7); + pub const DT_RELASZ: Self = Self(8); + pub const DT_RELAENT: Self = Self(9); + pub const DT_STRSZ: Self = Self(10); + pub const DT_SYMENT: Self = Self(11); + pub const DT_INIT: Self = Self(12); + pub const DT_FINI: Self = Self(13); + pub const DT_SONAME: Self = Self(14); + pub const DT_RPATH: Self = Self(15); + pub const DT_SYMBOLIC: Self = Self(16); + pub const DT_REL: Self = Self(17); + pub const DT_RELSZ: Self = Self(18); + pub const DT_RELENT: Self = Self(19); + pub const DT_PLTREL: Self = Self(20); + pub const DT_DEBUG: Self = Self(21); + pub const DT_TEXTREL: Self = Self(22); + pub const DT_JMPREL: Self = Self(23); + pub const DT_BIND_NOW: Self = Self(24); + pub const DT_INIT_ARRAY: Self = Self(25); + pub const DT_FINI_ARRAY: Self = Self(26); + pub const DT_INIT_ARRAYSZ: Self = Self(27); + pub const DT_FINI_ARRAYSZ: Self = Self(28); + pub const DT_RUNPATH: Self = Self(29); + pub const DT_FLAGS: Self = Self(30); + pub const DT_ENCODING: Self = Self(31); + pub const DT_PREINIT_ARRAY: Self = Self(32); + pub const DT_PREINIT_ARRAYSZ: Self = Self(33); + pub const DT_SCE_UNK1: Self = Self(0x60000005); + pub const DT_SCE_FINGERPRINT: Self = Self(0x61000007); + pub const DT_SCE_UNK2: Self = Self(0x61000008); + pub const DT_SCE_UNK3: Self = Self(0x6100000a); + pub const DT_SCE_UNK4: Self = Self(0x6100000b); + pub const DT_SCE_UNK5: Self = Self(0x6100000c); + pub const DT_SCE_UNK6: Self = Self(0x6100000e); + pub const DT_SCE_FILENAME: Self = Self(0x61000009); + pub const DT_SCE_MODULE_INFO: Self = Self(0x6100000d); + pub const DT_SCE_NEEDED_MODULE: Self = Self(0x6100000f); + pub const DT_SCE_UNK7: Self = Self(0x61000010); + pub const DT_SCE_MODULE_ATTR: Self = Self(0x61000011); + pub const DT_SCE_UNK8: Self = Self(0x61000012); + pub const DT_SCE_EXPORT_LIB: Self = Self(0x61000013); + pub const DT_SCE_UNK9: Self = Self(0x61000014); + pub const DT_SCE_IMPORT_LIB: Self = Self(0x61000015); + pub const DT_SCE_UNK10: Self = Self(0x61000016); + pub const DT_SCE_EXPORT_LIB_ATTR: Self = Self(0x61000017); + pub const DT_SCE_UNK11: Self = Self(0x61000018); + pub const DT_SCE_IMPORT_LIB_ATTR: Self = Self(0x61000019); + pub const DT_SCE_UNK12: Self = Self(0x6100001a); + pub const DT_SCE_UNK13: Self = Self(0x6100001b); + pub const DT_SCE_UNK14: Self = Self(0x6100001c); + pub const DT_SCE_UNK15: Self = Self(0x6100001d); + pub const DT_SCE_UNK16: Self = Self(0x6100001e); + pub const DT_SCE_UNK17: Self = Self(0x6100001f); + pub const DT_SCE_UNK18: Self = Self(0x61000020); + pub const DT_SCE_UNK19: Self = Self(0x61000021); + pub const DT_SCE_UNK20: Self = Self(0x61000022); + pub const DT_SCE_UNK21: Self = Self(0x61000023); + pub const DT_SCE_UNK22: Self = Self(0x61000024); + pub const DT_SCE_HASH: Self = Self(0x61000025); + pub const DT_SCE_UNK23: Self = Self(0x61000026); + pub const DT_SCE_PLTGOT: Self = Self(0x61000027); + pub const DT_SCE_UNK24: Self = Self(0x61000028); + pub const DT_SCE_JMPREL: Self = Self(0x61000029); + pub const DT_SCE_UNK25: Self = Self(0x6100002a); + pub const DT_SCE_PLTREL: Self = Self(0x6100002b); + pub const DT_SCE_UNK26: Self = Self(0x6100002c); + pub const DT_SCE_PLTRELSZ: Self = Self(0x6100002d); + pub const DT_SCE_UNK27: Self = Self(0x6100002e); + pub const DT_SCE_RELA: Self = Self(0x6100002f); + pub const DT_SCE_UNK28: Self = Self(0x61000030); + pub const DT_SCE_RELASZ: Self = Self(0x61000031); + pub const DT_SCE_UNK29: Self = Self(0x61000032); + pub const DT_SCE_RELAENT: Self = Self(0x61000033); + pub const DT_SCE_UNK30: Self = Self(0x61000034); + pub const DT_SCE_STRTAB: Self = Self(0x61000035); + pub const DT_SCE_UNK31: Self = Self(0x61000036); + pub const DT_SCE_STRSZ: Self = Self(0x61000037); + pub const DT_SCE_UNK32: Self = Self(0x61000038); + pub const DT_SCE_SYMTAB: Self = Self(0x61000039); + pub const DT_SCE_UNK33: Self = Self(0x6100003a); + pub const DT_SCE_SYMENT: Self = Self(0x6100003b); + pub const DT_SCE_UNK34: Self = Self(0x6100003c); + pub const DT_SCE_HASHSZ: Self = Self(0x6100003d); + pub const DT_SCE_UNK35: Self = Self(0x6100003e); + pub const DT_SCE_SYMTABSZ: Self = Self(0x6100003f); + pub const DT_SCE_UNK36: Self = Self(0x6ffffff9); + pub const DT_SCE_UNK37: Self = Self(0x6ffffffb); +} + +impl Display for DynamicTag { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match *self { + Self::DT_NULL => f.write_str("DT_NULL"), + Self::DT_NEEDED => f.write_str("DT_NEEDED"), + Self::DT_PLTRELSZ => f.write_str("DT_PLTRELSZ"), + Self::DT_PLTGOT => f.write_str("DT_PLTGOT"), + Self::DT_HASH => f.write_str("DT_HASH"), + Self::DT_STRTAB => f.write_str("DT_STRTAB"), + Self::DT_SYMTAB => f.write_str("DT_SYMTAB"), + Self::DT_RELA => f.write_str("DT_RELA"), + Self::DT_RELASZ => f.write_str("DT_RELASZ"), + Self::DT_RELAENT => f.write_str("DT_RELAENT"), + Self::DT_STRSZ => f.write_str("DT_STRSZ"), + Self::DT_SYMENT => f.write_str("DT_SYMENT"), + Self::DT_INIT => f.write_str("DT_INIT"), + Self::DT_FINI => f.write_str("DT_FINI"), + Self::DT_SONAME => f.write_str("DT_SONAME"), + Self::DT_RPATH => f.write_str("DT_RPATH"), + Self::DT_SYMBOLIC => f.write_str("DT_SYMBOLIC"), + Self::DT_REL => f.write_str("DT_REL"), + Self::DT_RELSZ => f.write_str("DT_RELSZ"), + Self::DT_RELENT => f.write_str("DT_RELENT"), + Self::DT_PLTREL => f.write_str("DT_PLTREL"), + Self::DT_DEBUG => f.write_str("DT_DEBUG"), + Self::DT_TEXTREL => f.write_str("DT_TEXTREL"), + Self::DT_JMPREL => f.write_str("DT_JMPREL"), + Self::DT_BIND_NOW => f.write_str("DT_BIND_NOW"), + Self::DT_INIT_ARRAY => f.write_str("DT_INIT_ARRAY"), + Self::DT_FINI_ARRAY => f.write_str("DT_FINI_ARRAY"), + Self::DT_INIT_ARRAYSZ => f.write_str("DT_INIT_ARRAYSZ"), + Self::DT_FINI_ARRAYSZ => f.write_str("DT_FINI_ARRAYSZ"), + Self::DT_RUNPATH => f.write_str("DT_RUNPATH"), + Self::DT_FLAGS => f.write_str("DT_FLAGS"), + Self::DT_ENCODING => f.write_str("DT_ENCODING"), + Self::DT_PREINIT_ARRAY => f.write_str("DT_PREINIT_ARRAY"), + Self::DT_PREINIT_ARRAYSZ => f.write_str("DT_PREINIT_ARRAYSZ"), + Self::DT_SCE_FINGERPRINT => f.write_str("DT_SCE_FINGERPRINT"), + Self::DT_SCE_FILENAME => f.write_str("DT_SCE_FILENAME"), + Self::DT_SCE_MODULE_INFO => f.write_str("DT_SCE_MODULE_INFO"), + Self::DT_SCE_NEEDED_MODULE => f.write_str("DT_SCE_NEEDED_MODULE"), + Self::DT_SCE_MODULE_ATTR => f.write_str("DT_SCE_MODULE_ATTR"), + Self::DT_SCE_EXPORT_LIB => f.write_str("DT_SCE_EXPORT_LIB"), + Self::DT_SCE_IMPORT_LIB => f.write_str("DT_SCE_IMPORT_LIB"), + Self::DT_SCE_EXPORT_LIB_ATTR => f.write_str("DT_SCE_EXPORT_LIB_ATTR"), + Self::DT_SCE_IMPORT_LIB_ATTR => f.write_str("DT_SCE_IMPORT_LIB_ATTR"), + Self::DT_SCE_HASH => f.write_str("DT_SCE_HASH"), + Self::DT_SCE_PLTGOT => f.write_str("DT_SCE_PLTGOT"), + Self::DT_SCE_JMPREL => f.write_str("DT_SCE_JMPREL"), + Self::DT_SCE_PLTREL => f.write_str("DT_SCE_PLTREL"), + Self::DT_SCE_PLTRELSZ => f.write_str("DT_SCE_PLTRELSZ"), + Self::DT_SCE_RELA => f.write_str("DT_SCE_RELA"), + Self::DT_SCE_RELASZ => f.write_str("DT_SCE_RELASZ"), + Self::DT_SCE_RELAENT => f.write_str("DT_SCE_RELAENT"), + Self::DT_SCE_STRTAB => f.write_str("DT_SCE_STRTAB"), + Self::DT_SCE_STRSZ => f.write_str("DT_SCE_STRSZ"), + Self::DT_SCE_SYMTAB => f.write_str("DT_SCE_SYMTAB"), + Self::DT_SCE_SYMENT => f.write_str("DT_SCE_SYMENT"), + Self::DT_SCE_HASHSZ => f.write_str("DT_SCE_HASHSZ"), + Self::DT_SCE_SYMTABSZ => f.write_str("DT_SCE_SYMTABSZ"), + v => write!(f, "{:#018x}", v.0), } - - None } } bitflags! { - /// Contains flags for a module. - #[derive(Clone, Copy)] - pub struct ModuleFlags: u64 { + /// Contains flags for the module. + #[derive(Debug, Clone, Copy)] + pub struct DynamicFlags: u64 { const DF_SYMBOLIC = 0x02; // Not used in PS4. const DF_TEXTREL = 0x04; const DF_BIND_NOW = 0x08; // Not used in PS4. } } -impl Display for ModuleFlags { +impl Display for DynamicFlags { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } - -/// Contains information about the module. -pub struct ModuleInfo { - id: u16, - name: String, - version_major: u8, - version_minor: u8, -} - -impl ModuleInfo { - pub fn id(&self) -> u16 { - self.id - } - - pub fn name(&self) -> &str { - self.name.as_ref() - } - - pub fn version_major(&self) -> u8 { - self.version_major - } - - pub fn version_minor(&self) -> u8 { - self.version_minor - } -} - -/// Contains information about the library in the module. -pub struct LibraryInfo { - id: u16, - name: String, - version: u16, - is_export: bool, -} - -impl LibraryInfo { - /// Gets the ID of this library. - pub fn id(&self) -> u16 { - self.id - } - - /// Gets the name of this library. - pub fn name(&self) -> &str { - self.name.as_ref() - } - - pub fn version(&self) -> u16 { - self.version - } - - pub fn is_export(&self) -> bool { - self.is_export - } -} - -/// Contains information about a symbol in the SELF. -pub struct SymbolInfo { - name: String, - info: u8, - value: usize, -} - -impl SymbolInfo { - /// Local symbol, not visible outside obj file containing def. - pub const STB_LOCAL: u8 = 0; - - /// Global symbol, visible to all object files being combined. - pub const STB_GLOBAL: u8 = 1; - - /// Weak symbol, like global but lower-precedence. - pub const STB_WEAK: u8 = 2; - - pub fn name(&self) -> &str { - self.name.as_ref() - } - - pub fn binding(&self) -> u8 { - self.info >> 4 - } - - pub fn value(&self) -> usize { - self.value - } - - pub fn decode_name(&self) -> Option<(&str, u16, u16)> { - // Extract local name. - let (name, remain) = match self.name.find('#') { - Some(v) => (&self.name[..v], &self.name[(v + 1)..]), - None => return None, - }; - - if name.is_empty() { - return None; - } - - // Extract library ID and module ID. - let (lib_id, mod_id) = match remain.find('#') { - Some(v) => (&remain[..v], &remain[(v + 1)..]), - None => return None, - }; - - if lib_id.is_empty() || mod_id.is_empty() { - return None; - } - - // Decode module ID and library ID. - let mod_id = Self::decode_id(mod_id)?; - let lib_id = Self::decode_id(lib_id)?; - - Some((name, lib_id, mod_id)) - } - - fn decode_id(v: &str) -> Option { - if v.len() > 3 { - return None; - } - - let s = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"; - let mut r = 0u32; - - for c in v.chars() { - r <<= 6; - r |= s.find(c)? as u32; - } - - if r > u16::MAX as _ { - None - } else { - Some(r as u16) - } - } -} - -/// An iterator over the relocation entry of the SELF. -pub struct RelocationEntries<'a> { - relaent: usize, - next: &'a [u8], -} - -impl<'a> Iterator for RelocationEntries<'a> { - type Item = RelocationInfo; - - fn next(&mut self) -> Option { - if self.next.is_empty() { - return None; - } - - // FIXME: Handle invalid data instead of panic. - let offset = LE::read_u64(self.next) as usize; - let info = LE::read_u64(&self.next[8..]); - let addend = LE::read_u64(&self.next[16..]) as usize; - - self.next = &self.next[self.relaent..]; - - Some(RelocationInfo { - offset, - info, - addend, - }) - } -} - -/// Contains information required to relocate a specific address. -pub struct RelocationInfo { - offset: usize, - info: u64, - addend: usize, -} - -impl RelocationInfo { - pub const R_X86_64_NONE: u32 = 0; - pub const R_X86_64_64: u32 = 1; - pub const R_X86_64_PC32: u32 = 2; - 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; - pub const R_X86_64_DTPOFF64: u32 = 17; - pub const R_X86_64_TPOFF64: u32 = 18; - pub const R_X86_64_DTPOFF32: u32 = 21; - pub const R_X86_64_TPOFF32: u32 = 23; - - pub fn ty(&self) -> u32 { - (self.info & 0x00000000ffffffff) as u32 - } - - pub fn symbol(&self) -> usize { - (self.info >> 32) as usize - } - - pub fn offset(&self) -> usize { - self.offset - } - - pub fn addend(&self) -> usize { - self.addend - } -} - -/// Encapsulate a PT_SCE_DYNLIBDATA. -struct DynlibData { - data: Vec, - strtab: usize, - strsz: usize, -} - -impl DynlibData { - fn str(&self, offset: usize) -> Option { - let raw = match self.data[self.strtab..(self.strtab + self.strsz)].get(offset..) { - Some(v) => match v.iter().position(|&b| b == 0) { - Some(i) => &v[..i], - None => return None, - }, - None => return None, - }; - - match std::str::from_utf8(raw) { - Ok(v) => Some(v.to_owned()), - Err(_) => None, - } - } - - fn get>(&self, index: I) -> Option<&I::Output> { - self.data.get(index) - } -} - -impl Index for DynlibData -where - I: SliceIndex<[u8]>, -{ - type Output = >::Output; - - fn index(&self, index: I) -> &Self::Output { - self.data.index(index) - } -} - -/// Represents an error for [`DynamicLinking::parse()`]. -#[derive(Debug, Error)] -pub enum ParseError { - #[error("invalid data size")] - InvalidDataSize, - - #[error("entry {0} is not a valid DT_NEEDED")] - InvalidNeeded(usize), - - #[error("entry DT_PLTRELSZ or DT_SCE_PLTRELSZ does not exist")] - NoPltrelsz, - - #[error("entry DT_PLTGOT or DT_SCE_PLTGOT does not exist")] - NoPltgot, - - #[error("entry DT_RELASZ or DT_SCE_RELASZ does not exist")] - NoRelasz, - - #[error("entry DT_RELAENT or DT_SCE_RELAENT does not exist")] - NoRelaent, - - #[error("entry DT_STRSZ or DT_SCE_STRSZ does not exist")] - NoStrsz, - - #[error("entry DT_SYMENT or DT_SCE_SYMENT does not exist")] - NoSyment, - - #[error("entry DT_PLTREL or DT_SCE_PLTREL does not exist")] - NoPltrel, - - #[error("entry DT_SCE_FINGERPRINT does not exist")] - NoFingerprint, - - #[error("entry DT_FLAGS does not exist")] - NoFlags, - - #[error("entry DT_SCE_FILENAME does not exist")] - NoFilename, - - #[error("entry DT_SCE_MODULE_INFO does not exist")] - NoModuleInfo, - - #[error("entry DT_SCE_HASH does not exist")] - NoHash, - - #[error("entry DT_SCE_HASH has invalid value")] - InvalidHash, - - #[error("entry DT_SCE_JMPREL does not exist")] - NoJmprel, - - #[error("entry DT_SCE_RELA does not exist")] - NoRela, - - #[error("entry DT_SCE_STRTAB does not exist")] - NoStrtab, - - #[error("entry DT_SCE_SYMTAB does not exist")] - NoSymtab, - - #[error("entry DT_SCE_SYMTAB has invalid value")] - InvalidSymtab, - - #[error("entry DT_SCE_HASHSZ does not exist")] - NoHashsz, - - #[error("entry DT_SCE_HASHSZ has invalid value")] - InvalidHashsz, - - #[error("entry DT_SCE_SYMTABSZ does not exist")] - NoSymtabsz, - - #[error("entry DT_SCE_SYMTABSZ has invalid value")] - InvalidSymtabsz, - - #[error("unknown tag {0:#018x}")] - UnknownTag(i64), - - #[error("entry DT_RELAENT or DT_SCE_RELAENT has invalid value")] - InvalidRelaent, - - #[error("entry DT_SYMENT or DT_SCE_SYMENT has invalid value")] - InvalidSyment, - - #[error("entry DT_PLTREL or DT_SCE_PLTREL has value other than DT_RELA")] - InvalidPltrel, - - #[error("entry DT_SCE_STRTAB has invalid value")] - InvalidStrtab, - - #[error("entry DT_SCE_MODULE_INFO has invalid value")] - InvalidModuleInfo, - - #[error("entry {0} is not a valid DT_SCE_NEEDED_MODULE")] - InvalidNeededModule(usize), - - #[error("duplicated needed module on entry {0}")] - DuplicatedNeededModule(usize), - - #[error("entry {0} is not a valid DT_SCE_EXPORT_LIB")] - InvalidExport(usize), - - #[error("entry {0} is not a valid DT_SCE_IMPORT_LIB")] - InvalidImport(usize), - - #[error("duplicated library on entry {0}")] - DuplicatedLibrary(usize), - - #[error("Symbol entry {0} is not valid")] - InvalidSymbol(usize), -} diff --git a/src/elf/src/info.rs b/src/elf/src/info.rs index 1e2f6f478..3da63affe 100644 --- a/src/elf/src/info.rs +++ b/src/elf/src/info.rs @@ -1,4 +1,4 @@ -use crate::Relocations; +use crate::{DynamicEntries, DynamicTag, LibraryFlags, LibraryInfo, ModuleInfo, Relocations}; use byteorder::{ByteOrder, LE}; use thiserror::Error; @@ -6,6 +6,8 @@ use thiserror::Error; pub struct FileInfo { data: Vec, comment: Vec, + dynoff: usize, + dynsize: usize, pltrelsz: usize, relasz: usize, strsz: usize, @@ -19,101 +21,6 @@ pub struct FileInfo { } impl FileInfo { - pub const DT_NULL: i64 = 0; - pub const DT_NEEDED: i64 = 1; - pub const DT_PLTRELSZ: i64 = 2; - pub const DT_PLTGOT: i64 = 3; - pub const DT_HASH: i64 = 4; - pub const DT_STRTAB: i64 = 5; - pub const DT_SYMTAB: i64 = 6; - pub const DT_RELA: i64 = 7; - pub const DT_RELASZ: i64 = 8; - pub const DT_RELAENT: i64 = 9; - pub const DT_STRSZ: i64 = 10; - pub const DT_SYMENT: i64 = 11; - pub const DT_INIT: i64 = 12; - pub const DT_FINI: i64 = 13; - pub const DT_SONAME: i64 = 14; - pub const DT_RPATH: i64 = 15; - pub const DT_SYMBOLIC: i64 = 16; - pub const DT_REL: i64 = 17; - pub const DT_RELSZ: i64 = 18; - pub const DT_RELENT: i64 = 19; - pub const DT_PLTREL: i64 = 20; - pub const DT_DEBUG: i64 = 21; - pub const DT_TEXTREL: i64 = 22; - pub const DT_JMPREL: i64 = 23; - pub const DT_BIND_NOW: i64 = 24; - pub const DT_INIT_ARRAY: i64 = 25; - pub const DT_FINI_ARRAY: i64 = 26; - pub const DT_INIT_ARRAYSZ: i64 = 27; - pub const DT_FINI_ARRAYSZ: i64 = 28; - pub const DT_RUNPATH: i64 = 29; - pub const DT_FLAGS: i64 = 30; - pub const DT_ENCODING: i64 = 31; - pub const DT_PREINIT_ARRAY: i64 = 32; - pub const DT_PREINIT_ARRAYSZ: i64 = 33; - pub const DT_SCE_UNK1: i64 = 0x60000005; - pub const DT_SCE_FINGERPRINT: i64 = 0x61000007; - pub const DT_SCE_UNK2: i64 = 0x61000008; - pub const DT_SCE_UNK3: i64 = 0x6100000a; - pub const DT_SCE_UNK4: i64 = 0x6100000b; - pub const DT_SCE_UNK5: i64 = 0x6100000c; - pub const DT_SCE_UNK6: i64 = 0x6100000e; - pub const DT_SCE_FILENAME: i64 = 0x61000009; - pub const DT_SCE_MODULE_INFO: i64 = 0x6100000d; - pub const DT_SCE_NEEDED_MODULE: i64 = 0x6100000f; - pub const DT_SCE_UNK7: i64 = 0x61000010; - pub const DT_SCE_MODULE_ATTR: i64 = 0x61000011; - pub const DT_SCE_UNK8: i64 = 0x61000012; - pub const DT_SCE_EXPORT_LIB: i64 = 0x61000013; - pub const DT_SCE_UNK9: i64 = 0x61000014; - pub const DT_SCE_IMPORT_LIB: i64 = 0x61000015; - pub const DT_SCE_UNK10: i64 = 0x61000016; - pub const DT_SCE_EXPORT_LIB_ATTR: i64 = 0x61000017; - pub const DT_SCE_UNK11: i64 = 0x61000018; - pub const DT_SCE_IMPORT_LIB_ATTR: i64 = 0x61000019; - pub const DT_SCE_UNK12: i64 = 0x6100001a; - pub const DT_SCE_UNK13: i64 = 0x6100001b; - pub const DT_SCE_UNK14: i64 = 0x6100001c; - pub const DT_SCE_UNK15: i64 = 0x6100001d; - pub const DT_SCE_UNK16: i64 = 0x6100001e; - pub const DT_SCE_UNK17: i64 = 0x6100001f; - pub const DT_SCE_UNK18: i64 = 0x61000020; - pub const DT_SCE_UNK19: i64 = 0x61000021; - pub const DT_SCE_UNK20: i64 = 0x61000022; - pub const DT_SCE_UNK21: i64 = 0x61000023; - pub const DT_SCE_UNK22: i64 = 0x61000024; - pub const DT_SCE_HASH: i64 = 0x61000025; - pub const DT_SCE_UNK23: i64 = 0x61000026; - pub const DT_SCE_PLTGOT: i64 = 0x61000027; - pub const DT_SCE_UNK24: i64 = 0x61000028; - pub const DT_SCE_JMPREL: i64 = 0x61000029; - pub const DT_SCE_UNK25: i64 = 0x6100002a; - pub const DT_SCE_PLTREL: i64 = 0x6100002b; - pub const DT_SCE_UNK26: i64 = 0x6100002c; - pub const DT_SCE_PLTRELSZ: i64 = 0x6100002d; - pub const DT_SCE_UNK27: i64 = 0x6100002e; - pub const DT_SCE_RELA: i64 = 0x6100002f; - pub const DT_SCE_UNK28: i64 = 0x61000030; - pub const DT_SCE_RELASZ: i64 = 0x61000031; - pub const DT_SCE_UNK29: i64 = 0x61000032; - pub const DT_SCE_RELAENT: i64 = 0x61000033; - pub const DT_SCE_UNK30: i64 = 0x61000034; - pub const DT_SCE_STRTAB: i64 = 0x61000035; - pub const DT_SCE_UNK31: i64 = 0x61000036; - pub const DT_SCE_STRSZ: i64 = 0x61000037; - pub const DT_SCE_UNK32: i64 = 0x61000038; - pub const DT_SCE_SYMTAB: i64 = 0x61000039; - pub const DT_SCE_UNK33: i64 = 0x6100003a; - pub const DT_SCE_SYMENT: i64 = 0x6100003b; - pub const DT_SCE_UNK34: i64 = 0x6100003c; - pub const DT_SCE_HASHSZ: i64 = 0x6100003d; - pub const DT_SCE_UNK35: i64 = 0x6100003e; - pub const DT_SCE_SYMTABSZ: i64 = 0x6100003f; - pub const DT_SCE_UNK36: i64 = 0x6ffffff9; - pub const DT_SCE_UNK37: i64 = 0x6ffffffb; - pub(super) fn parse( data: Vec, comment: Vec, @@ -141,121 +48,124 @@ impl FileInfo { // Let it panic if the dynamic size is not correct because the PS4 also does not check for // this. - for entry in data[dynoff..(dynoff + dynsize)].chunks(16) { - let tag = LE::read_i64(entry); - let value = &entry[8..]; - + for (tag, value) in DynamicEntries::new(&data[dynoff..(dynoff + dynsize)]) { match tag { - Self::DT_NULL => break, - Self::DT_NEEDED => {} - Self::DT_PLTRELSZ | Self::DT_SCE_PLTRELSZ => pltrelsz = Some(LE::read_u64(value)), - Self::DT_PLTGOT - | Self::DT_RPATH - | Self::DT_BIND_NOW - | Self::DT_RUNPATH - | Self::DT_ENCODING - | Self::DT_SCE_UNK2 - | Self::DT_SCE_UNK3 - | Self::DT_SCE_UNK4 - | Self::DT_SCE_UNK5 - | Self::DT_SCE_UNK6 - | Self::DT_SCE_UNK7 - | Self::DT_SCE_UNK8 - | Self::DT_SCE_UNK9 - | Self::DT_SCE_UNK10 - | Self::DT_SCE_UNK11 - | Self::DT_SCE_UNK12 - | Self::DT_SCE_UNK13 - | Self::DT_SCE_UNK14 - | Self::DT_SCE_UNK15 - | Self::DT_SCE_UNK16 - | Self::DT_SCE_UNK17 - | Self::DT_SCE_UNK18 - | Self::DT_SCE_UNK19 - | Self::DT_SCE_UNK20 - | Self::DT_SCE_UNK21 - | Self::DT_SCE_UNK22 - | Self::DT_SCE_UNK23 - | Self::DT_SCE_UNK24 - | Self::DT_SCE_UNK25 - | Self::DT_SCE_UNK26 - | Self::DT_SCE_UNK27 - | Self::DT_SCE_UNK28 - | Self::DT_SCE_UNK29 - | Self::DT_SCE_UNK30 - | Self::DT_SCE_UNK31 - | Self::DT_SCE_UNK32 - | Self::DT_SCE_UNK33 - | Self::DT_SCE_UNK34 - | Self::DT_SCE_UNK35 => { + DynamicTag::DT_NULL => break, + DynamicTag::DT_NEEDED => {} + DynamicTag::DT_PLTRELSZ | DynamicTag::DT_SCE_PLTRELSZ => { + pltrelsz = Some(u64::from_le_bytes(value)) + } + DynamicTag::DT_PLTGOT + | DynamicTag::DT_RPATH + | DynamicTag::DT_BIND_NOW + | DynamicTag::DT_RUNPATH + | DynamicTag::DT_ENCODING + | DynamicTag::DT_SCE_UNK2 + | DynamicTag::DT_SCE_UNK3 + | DynamicTag::DT_SCE_UNK4 + | DynamicTag::DT_SCE_UNK5 + | DynamicTag::DT_SCE_UNK6 + | DynamicTag::DT_SCE_UNK7 + | DynamicTag::DT_SCE_UNK8 + | DynamicTag::DT_SCE_UNK9 + | DynamicTag::DT_SCE_UNK10 + | DynamicTag::DT_SCE_UNK11 + | DynamicTag::DT_SCE_UNK12 + | DynamicTag::DT_SCE_UNK13 + | DynamicTag::DT_SCE_UNK14 + | DynamicTag::DT_SCE_UNK15 + | DynamicTag::DT_SCE_UNK16 + | DynamicTag::DT_SCE_UNK17 + | DynamicTag::DT_SCE_UNK18 + | DynamicTag::DT_SCE_UNK19 + | DynamicTag::DT_SCE_UNK20 + | DynamicTag::DT_SCE_UNK21 + | DynamicTag::DT_SCE_UNK22 + | DynamicTag::DT_SCE_UNK23 + | DynamicTag::DT_SCE_UNK24 + | DynamicTag::DT_SCE_UNK25 + | DynamicTag::DT_SCE_UNK26 + | DynamicTag::DT_SCE_UNK27 + | DynamicTag::DT_SCE_UNK28 + | DynamicTag::DT_SCE_UNK29 + | DynamicTag::DT_SCE_UNK30 + | DynamicTag::DT_SCE_UNK31 + | DynamicTag::DT_SCE_UNK32 + | DynamicTag::DT_SCE_UNK33 + | DynamicTag::DT_SCE_UNK34 + | DynamicTag::DT_SCE_UNK35 => { return Err(FileInfoError::UnsupportedTag(tag)); } - Self::DT_HASH - | Self::DT_STRTAB - | Self::DT_SYMTAB - | Self::DT_RELA - | Self::DT_JMPREL - | Self::DT_REL - | Self::DT_RELSZ - | Self::DT_RELENT => { + DynamicTag::DT_HASH + | DynamicTag::DT_STRTAB + | DynamicTag::DT_SYMTAB + | DynamicTag::DT_RELA + | DynamicTag::DT_JMPREL + | DynamicTag::DT_REL + | DynamicTag::DT_RELSZ + | DynamicTag::DT_RELENT => { return Err(FileInfoError::OrbisUnsupported(tag)); } - Self::DT_RELASZ | Self::DT_SCE_RELASZ => relasz = Some(LE::read_u64(value)), - Self::DT_RELAENT | Self::DT_SCE_RELAENT => { - relaent = if LE::read_u64(value) == 24 { + DynamicTag::DT_RELASZ | DynamicTag::DT_SCE_RELASZ => { + relasz = Some(u64::from_le_bytes(value)) + } + DynamicTag::DT_RELAENT | DynamicTag::DT_SCE_RELAENT => { + relaent = if u64::from_le_bytes(value) == 24 { true } else { return Err(FileInfoError::InvalidRelaent); } } - Self::DT_STRSZ | Self::DT_SCE_STRSZ => strsz = Some(LE::read_u64(value)), - Self::DT_SYMENT | Self::DT_SCE_SYMENT => { - syment = if LE::read_u64(value) == 24 { + DynamicTag::DT_STRSZ | DynamicTag::DT_SCE_STRSZ => { + strsz = Some(u64::from_le_bytes(value)) + } + DynamicTag::DT_SYMENT | DynamicTag::DT_SCE_SYMENT => { + syment = if u64::from_le_bytes(value) == 24 { true } else { return Err(FileInfoError::InvalidSyment); } } - Self::DT_INIT => {} - Self::DT_FINI => {} - Self::DT_SONAME => {} - Self::DT_SYMBOLIC => {} - Self::DT_PLTREL | Self::DT_SCE_PLTREL => { - pltrel = if LE::read_u64(value) == 7 { + DynamicTag::DT_INIT => {} + DynamicTag::DT_FINI => {} + DynamicTag::DT_SONAME => {} + DynamicTag::DT_SYMBOLIC => {} + DynamicTag::DT_PLTREL | DynamicTag::DT_SCE_PLTREL => { + pltrel = if u64::from_le_bytes(value) == 7 { true } else { return Err(FileInfoError::InvalidPltrel); } } - Self::DT_DEBUG => {} - Self::DT_TEXTREL => {} - Self::DT_INIT_ARRAY => {} - Self::DT_FINI_ARRAY => {} - Self::DT_INIT_ARRAYSZ => {} - Self::DT_FINI_ARRAYSZ => {} - Self::DT_FLAGS => {} - Self::DT_PREINIT_ARRAY => {} - Self::DT_PREINIT_ARRAYSZ => {} - Self::DT_SCE_UNK1 => {} - Self::DT_SCE_FINGERPRINT => fingerprint = true, - Self::DT_SCE_FILENAME => filename = true, - Self::DT_SCE_MODULE_INFO => module_info = true, - Self::DT_SCE_NEEDED_MODULE => {} - Self::DT_SCE_MODULE_ATTR => {} - Self::DT_SCE_EXPORT_LIB => {} - Self::DT_SCE_IMPORT_LIB => {} - Self::DT_SCE_EXPORT_LIB_ATTR => {} - Self::DT_SCE_IMPORT_LIB_ATTR => {} - Self::DT_SCE_HASH => hash = Some(LE::read_u64(value)), - Self::DT_SCE_PLTGOT => pltgot = true, - Self::DT_SCE_JMPREL => jmprel = Some(LE::read_u64(value)), - Self::DT_SCE_RELA => rela = Some(LE::read_u64(value)), - Self::DT_SCE_STRTAB => strtab = Some(LE::read_u64(value)), - Self::DT_SCE_SYMTAB => symtab = Some(LE::read_u64(value)), - Self::DT_SCE_HASHSZ => hashsz = Some(LE::read_u64(value)), - Self::DT_SCE_SYMTABSZ => symtabsz = Some(LE::read_u64(value)), - Self::DT_SCE_UNK36 => {} - Self::DT_SCE_UNK37 => {} + DynamicTag::DT_DEBUG => {} + DynamicTag::DT_TEXTREL => {} + DynamicTag::DT_INIT_ARRAY => {} + DynamicTag::DT_FINI_ARRAY => {} + DynamicTag::DT_INIT_ARRAYSZ => {} + DynamicTag::DT_FINI_ARRAYSZ => {} + DynamicTag::DT_FLAGS => {} + DynamicTag::DT_PREINIT_ARRAY => {} + DynamicTag::DT_PREINIT_ARRAYSZ => {} + DynamicTag::DT_SCE_UNK1 => {} + DynamicTag::DT_SCE_FINGERPRINT => fingerprint = true, + DynamicTag::DT_SCE_FILENAME => filename = true, + DynamicTag::DT_SCE_MODULE_INFO => module_info = true, + DynamicTag::DT_SCE_NEEDED_MODULE => {} + DynamicTag::DT_SCE_MODULE_ATTR => {} + DynamicTag::DT_SCE_EXPORT_LIB => {} + DynamicTag::DT_SCE_IMPORT_LIB => {} + DynamicTag::DT_SCE_EXPORT_LIB_ATTR => {} + DynamicTag::DT_SCE_IMPORT_LIB_ATTR => {} + DynamicTag::DT_SCE_HASH => hash = Some(u64::from_le_bytes(value)), + DynamicTag::DT_SCE_PLTGOT => pltgot = true, + DynamicTag::DT_SCE_JMPREL => jmprel = Some(u64::from_le_bytes(value)), + DynamicTag::DT_SCE_RELA => rela = Some(u64::from_le_bytes(value)), + DynamicTag::DT_SCE_STRTAB => strtab = Some(u64::from_le_bytes(value)), + DynamicTag::DT_SCE_SYMTAB => symtab = Some(u64::from_le_bytes(value)), + DynamicTag::DT_SCE_HASHSZ => hashsz = Some(u64::from_le_bytes(value)), + DynamicTag::DT_SCE_SYMTABSZ => symtabsz = Some(u64::from_le_bytes(value)), + DynamicTag::DT_SCE_UNK36 => {} + DynamicTag::DT_SCE_UNK37 => {} v => return Err(FileInfoError::UnknownTag(v)), } } @@ -292,6 +202,8 @@ impl FileInfo { Ok(Self { data, comment, + dynoff, + dynsize, pltrelsz: pltrelsz.try_into().unwrap(), relasz: relasz.try_into().unwrap(), strsz: strsz.try_into().unwrap(), @@ -305,6 +217,14 @@ impl FileInfo { }) } + pub fn comment(&self) -> &[u8] { + self.comment.as_ref() + } + + pub fn dynamic(&self) -> DynamicEntries<'_> { + DynamicEntries::new(&self.data[self.dynoff..(self.dynoff + self.dynsize)]) + } + pub fn relocs(&self) -> Relocations<'_> { Relocations::new(&self.data[self.rela..(self.rela + self.relasz)]) } @@ -312,19 +232,65 @@ impl FileInfo { pub fn plt_relocs(&self) -> Relocations<'_> { Relocations::new(&self.data[self.jmprel..(self.jmprel + self.pltrelsz)]) } + + pub fn read_module(&self, data: [u8; 8]) -> Result { + // Load data. + let name = LE::read_u32(&data); + let id = LE::read_u16(&data[6..]); + + // Lookup name. + let name = match self.read_str(name.try_into().unwrap()) { + Ok(v) => v.to_owned(), + Err(e) => return Err(ReadModuleError::InvalidNameOffset(name, e)), + }; + + Ok(ModuleInfo::new(id, name)) + } + + pub fn read_library(&self, data: [u8; 8]) -> Result { + // Load data. + let name = LE::read_u32(&data); + let id = LE::read_u16(&data[6..]); + + // Lookup name. + let name = match self.read_str(name.try_into().unwrap()) { + Ok(v) => v.to_owned(), + Err(e) => return Err(ReadLibraryError::InvalidNameOffset(name, e)), + }; + + Ok(LibraryInfo::new(id, name, LibraryFlags::empty())) + } + + pub fn read_str(&self, offset: usize) -> Result<&str, StringTableError> { + // Get raw string. + let tab = &self.data[self.strtab..(self.strtab + self.strsz)]; + let raw = match tab.get(offset..) { + Some(v) if !v.is_empty() => v, + _ => return Err(StringTableError::InvalidOffset), + }; + + // Find a NULL-terminated. + let raw = match raw.iter().position(|&b| b == 0) { + Some(i) => &raw[..i], + None => return Err(StringTableError::NotCString), + }; + + // Get Rust string. + std::str::from_utf8(raw).map_err(|_| StringTableError::NotUtf8) + } } /// Represents an error for file info parsing. #[derive(Debug, Error)] pub enum FileInfoError { - #[error("unknown tag {0:#018x}")] - UnknownTag(i64), + #[error("unknown tag {0}")] + UnknownTag(DynamicTag), - #[error("tag {0:#018x} is not supported")] - UnsupportedTag(i64), + #[error("tag {0} is not supported")] + UnsupportedTag(DynamicTag), - #[error("Orbis object file does not support tag {0:#018x}")] - OrbisUnsupported(i64), + #[error("Orbis object file does not support tag {0}")] + OrbisUnsupported(DynamicTag), #[error("no DT_PLTRELSZ or DT_SCE_PLTRELSZ")] NoPltrelsz, @@ -386,3 +352,30 @@ pub enum FileInfoError { #[error("no DT_SCE_SYMTABSZ")] NoSymtabsz, } + +/// Represents an error when reading [`ModuleInfo`] is failed. +#[derive(Debug, Error)] +pub enum ReadModuleError { + #[error("name offset {0} is not valid")] + InvalidNameOffset(u32, #[source] StringTableError), +} + +/// Represents an error when reading [`LibraryInfo`] is failed. +#[derive(Debug, Error)] +pub enum ReadLibraryError { + #[error("name offset {0} is not valid")] + InvalidNameOffset(u32, #[source] StringTableError), +} + +/// Represents an error when string table lookup is failed. +#[derive(Debug, Error)] +pub enum StringTableError { + #[error("the offset is not a valid offset in the string table")] + InvalidOffset, + + #[error("the offset is not a C string")] + NotCString, + + #[error("the offset is not a UTF-8 string")] + NotUtf8, +} diff --git a/src/elf/src/lib.rs b/src/elf/src/lib.rs index 29aa2985c..4be1fbb80 100644 --- a/src/elf/src/lib.rs +++ b/src/elf/src/lib.rs @@ -1,5 +1,7 @@ pub use dynamic::*; pub use info::*; +pub use library::*; +pub use module::*; pub use program::*; pub use reloc::*; pub use ty::*; @@ -12,6 +14,8 @@ use thiserror::Error; mod dynamic; mod info; +mod library; +mod module; mod program; mod reloc; mod ty; @@ -37,7 +41,6 @@ pub struct Elf { dynamic: Option, dyndata: Option, tls: Option, - dynamic_linking: Option, proc_param: Option, mod_param: Option, comment: Option, @@ -174,7 +177,6 @@ impl Elf { dynamic: None, dyndata: None, tls: None, - dynamic_linking: None, proc_param: None, mod_param: None, comment: None, @@ -227,13 +229,6 @@ impl Elf { let mut dynoff: usize = dynamic.offset().try_into().unwrap(); let dynsize: usize = dynamic.file_size().try_into().unwrap(); - // Read PT_DYNAMIC. - let mut dynamic = vec![0u8; dynamic.file_size() as usize]; - - if let Err(e) = elf.read_program(i, &mut dynamic) { - return Err(OpenError::ReadDynamicFailed(e)); - } - // Check dynamic data. let i = elf.dyndata.ok_or(OpenError::NoDynData)?; let dyndata = &elf.programs[i]; @@ -244,10 +239,10 @@ impl Elf { // Adjust dynamic offset inside the dynamic data. It looks weird but this is how Sony // actually did. - dynoff -= dyndata.offset() as usize; + dynoff -= TryInto::::try_into(dyndata.offset()).unwrap(); // Read PT_SCE_DYNLIBDATA. - let mut dyndata = vec![0u8; dyndata.file_size() as usize]; + let mut dyndata = vec![0u8; dyndata.file_size().try_into().unwrap()]; if let Err(e) = elf.read_program(i, &mut dyndata) { return Err(OpenError::ReadDynDataFailed(e)); @@ -255,7 +250,7 @@ impl Elf { // Read PT_SCE_COMMENT. let comment = if let Some(i) = elf.comment { - let mut buf = vec![0u8; elf.programs[i].file_size() as usize]; + let mut buf = vec![0u8; elf.programs[i].file_size().try_into().unwrap()]; if elf.read_program(i, &mut buf).is_err() { // This is not an error on the PS4. @@ -268,16 +263,10 @@ impl Elf { }; // Load info. - elf.info = match FileInfo::parse(dyndata.clone(), comment, dynoff, dynsize) { + elf.info = match FileInfo::parse(dyndata, comment, dynoff, dynsize) { Ok(v) => Some(v), Err(e) => return Err(OpenError::ParseFileInfoFailed(e)), }; - - // Parse PT_DYNAMIC & PT_SCE_DYNLIBDATA. - elf.dynamic_linking = match DynamicLinking::parse(dynamic, dyndata) { - Ok(v) => Some(v), - Err(e) => return Err(OpenError::ParseDynamicLinkingFailed(e)), - }; } // Check PT_SCE_RELRO. @@ -344,10 +333,6 @@ impl Elf { self.tls } - pub fn dynamic_linking(&self) -> Option<&DynamicLinking> { - self.dynamic_linking.as_ref() - } - pub fn proc_param(&self) -> Option { self.proc_param } @@ -735,9 +720,6 @@ pub enum OpenError { #[error("PT_DYNAMIC is not valid")] InvalidDynamic, - #[error("cannot read PT_DYNAMIC")] - ReadDynamicFailed(#[source] ReadProgramError), - #[error("no PT_SCE_DYNLIBDATA")] NoDynData, @@ -750,9 +732,6 @@ pub enum OpenError { #[error("cannot parse file info")] ParseFileInfoFailed(#[source] FileInfoError), - #[error("cannot parse PT_DYNAMIC and PT_SCE_DYNLIBDATA")] - ParseDynamicLinkingFailed(#[source] self::dynamic::ParseError), - #[error("PT_SCE_RELRO has invalid address")] InvalidRelroAddr, diff --git a/src/elf/src/library.rs b/src/elf/src/library.rs new file mode 100644 index 000000000..d764c0552 --- /dev/null +++ b/src/elf/src/library.rs @@ -0,0 +1,38 @@ +use bitflags::bitflags; + +/// Contains information about the library. +pub struct LibraryInfo { + id: u16, + name: String, + flags: LibraryFlags, +} + +impl LibraryInfo { + pub(crate) fn new(id: u16, name: String, flags: LibraryFlags) -> Self { + Self { id, name, flags } + } + + pub fn id(&self) -> u16 { + self.id + } + + pub fn name(&self) -> &str { + self.name.as_ref() + } + + pub fn flags(&self) -> LibraryFlags { + self.flags + } + + pub fn flags_mut(&mut self) -> &mut LibraryFlags { + &mut self.flags + } +} + +bitflags! { + /// Flags of [`LibraryInfo`]. + #[derive(Clone, Copy)] + pub struct LibraryFlags: u64 { + const EXPORT = 0x010000; + } +} diff --git a/src/elf/src/module.rs b/src/elf/src/module.rs new file mode 100644 index 000000000..cea18b2b0 --- /dev/null +++ b/src/elf/src/module.rs @@ -0,0 +1,19 @@ +/// Contains information about the module. +pub struct ModuleInfo { + id: u16, + name: String, +} + +impl ModuleInfo { + pub(crate) fn new(id: u16, name: String) -> Self { + Self { id, name } + } + + pub fn id(&self) -> u16 { + self.id + } + + pub fn name(&self) -> &str { + self.name.as_ref() + } +} diff --git a/src/fs/src/lib.rs b/src/fs/src/lib.rs index f47172446..c0016e834 100644 --- a/src/fs/src/lib.rs +++ b/src/fs/src/lib.rs @@ -78,13 +78,6 @@ impl From for FileMode { } } -/// Contains some information for SPRX file. -#[derive(Serialize, Deserialize)] -pub struct ModuleInfo { - pub name: String, - pub path: String, -} - /// Errors for [`create_for()`][Metadata::create_for()]. #[derive(Debug, Error)] pub enum CreateForError { diff --git a/src/kernel/src/ee/native/mod.rs b/src/kernel/src/ee/native/mod.rs index 28a849ffa..07238c6ae 100644 --- a/src/kernel/src/ee/native/mod.rs +++ b/src/kernel/src/ee/native/mod.rs @@ -444,7 +444,7 @@ impl<'a, 'b> ExecutionEngine for NativeEngine<'a, 'b> { let ld = self.rtld.read().unwrap(); let eboot = ld.app(); - if eboot.image().dynamic_linking().is_none() { + if eboot.image().dynamic().is_none() { todo!("A statically linked eboot.bin is not supported yet."); } diff --git a/src/kernel/src/log/entry.rs b/src/kernel/src/log/entry.rs index e994397a5..8f703608d 100644 --- a/src/kernel/src/log/entry.rs +++ b/src/kernel/src/log/entry.rs @@ -97,19 +97,14 @@ impl LogEntry { None => return, }; - // Write file name. + // Do nothing if no file name to write. let file = match file { - Some(v) => { - // Strip 'kernel/src/'. - if v.starts_with("kernel/src/") || v.starts_with("kernel\\src\\") { - &v[11..] - } else { - v - } - } + Some(v) => v, None => return, }; + // Do not strip "kernel/src/" due to we also set a panic hook. That mean the file path may + // be from another crate. write!(stdout, ":{file}").unwrap(); write!(self.plain, ":{file}").unwrap(); diff --git a/src/kernel/src/main.rs b/src/kernel/src/main.rs index 49740f7e4..825db06d9 100644 --- a/src/kernel/src/main.rs +++ b/src/kernel/src/main.rs @@ -319,16 +319,6 @@ fn print_module(log: &mut LogEntry, module: &Module) { writeln!(log, "Image type : {}", image.ty()).unwrap(); - if let Some(dynamic) = image.dynamic_linking() { - let i = dynamic.module_info(); - - writeln!(log, "Module name : {}", i.name()).unwrap(); - - if let Some(f) = dynamic.flags() { - writeln!(log, "Module flags : {f}").unwrap(); - } - } - for (i, p) in image.programs().iter().enumerate() { let offset = p.offset(); let end = offset + p.file_size(); @@ -344,13 +334,12 @@ fn print_module(log: &mut LogEntry, module: &Module) { .unwrap(); } - if let Some(dynamic) = image.dynamic_linking() { - for n in dynamic.needed() { - writeln!(log, "Needed : {n}").unwrap(); - } + for n in module.needed() { + writeln!(log, "Needed : {}", n.name()).unwrap(); } // Runtime info. + writeln!(log, "Module flags : {}", module.flags()).unwrap(); writeln!(log, "TLS index : {}", module.tls_index()).unwrap(); // Memory. diff --git a/src/kernel/src/process/mod.rs b/src/kernel/src/process/mod.rs index 2098999af..aa4166f33 100644 --- a/src/kernel/src/process/mod.rs +++ b/src/kernel/src/process/mod.rs @@ -83,7 +83,7 @@ impl VProc { // TODO: Invoke signotify at the end. } - v => return Err(SigmaskError::InvalidArgument(v)), + v => return Err(SigmaskError::InvalidHow(v)), } // TODO: Check if we need to invoke reschedule_signals. @@ -94,14 +94,14 @@ impl VProc { /// Represents an error when [`VProc::sigmask()`] is failed. #[derive(Debug, Error)] pub enum SigmaskError { - #[error("Invalid argument in sigmask: {0}")] - InvalidArgument(i32), + #[error("{0} is not a valid how")] + InvalidHow(i32), } impl Errno for SigmaskError { fn errno(&self) -> NonZeroI32 { match self { - Self::InvalidArgument(_) => EINVAL, + Self::InvalidHow(_) => EINVAL, } } } diff --git a/src/kernel/src/rtld/mod.rs b/src/kernel/src/rtld/mod.rs index 87224e248..88bf4a394 100644 --- a/src/kernel/src/rtld/mod.rs +++ b/src/kernel/src/rtld/mod.rs @@ -5,7 +5,7 @@ use crate::errno::{Errno, ENOEXEC}; use crate::fs::path::{VPath, VPathBuf}; use crate::fs::Fs; use crate::memory::{MemoryManager, MmapError, MprotectError}; -use elf::{Elf, FileInfo, FileType, ReadProgramError, Relocation}; +use elf::{DynamicFlags, Elf, FileInfo, FileType, ReadProgramError, Relocation}; use std::fs::File; use std::num::NonZeroI32; use thiserror::Error; @@ -49,11 +49,11 @@ impl<'a> RuntimeLinker<'a> { // Check image type. match elf.ty() { FileType::ET_EXEC | FileType::ET_SCE_EXEC | FileType::ET_SCE_REPLAY_EXEC => { - if elf.dynamic_linking().is_none() { - todo!("A statically linked eboot.bin is not supported yet."); + if elf.info().is_none() { + todo!("a statically linked eboot.bin is not supported yet."); } } - FileType::ET_SCE_DYNEXEC if elf.dynamic_linking().is_some() => {} + FileType::ET_SCE_DYNEXEC if elf.dynamic().is_some() => {} _ => return Err(RuntimeLinkerError::InvalidExe(file.into_vpath())), } @@ -264,6 +264,18 @@ pub enum MapError { #[error("cannot protect the memory")] ProtectMemoryFailed(#[source] MprotectError), + + #[error("cannot read DT_NEEDED from dynamic entry {0}")] + ReadNeededFailed(usize, #[source] elf::StringTableError), + + #[error("{0} is obsolete")] + ObsoleteFlags(DynamicFlags), + + #[error("cannot read module info from dynamic entry {0}")] + ReadModuleInfoFailed(usize, #[source] elf::ReadModuleError), + + #[error("cannot read libraru info from dynamic entry {0}")] + ReadLibraryInfoFailed(usize, #[source] elf::ReadLibraryError), } /// Represents an error for (S)ELF loading. diff --git a/src/kernel/src/rtld/module.rs b/src/kernel/src/rtld/module.rs index 10dff87cf..7492c8b17 100644 --- a/src/kernel/src/rtld/module.rs +++ b/src/kernel/src/rtld/module.rs @@ -1,7 +1,8 @@ use super::{MapError, Memory}; use crate::memory::MemoryManager; use bitflags::bitflags; -use elf::Elf; +use elf::{DynamicFlags, DynamicTag, Elf, FileInfo, LibraryFlags, LibraryInfo, ModuleInfo}; +use std::fmt::{Display, Formatter}; use std::fs::File; /// An implementation of @@ -12,6 +13,9 @@ pub struct Module<'a> { tls_index: u32, proc_param: Option<(usize, usize)>, flags: ModuleFlags, + needed: Vec, + modules: Vec, + libraries: Vec, image: Elf, memory: Memory<'a>, } @@ -33,12 +37,18 @@ impl<'a> Module<'a> { .map_err(|e| MapError::ReadProgramFailed(prog, e)) })?; + // Initialize PLT relocation. + if let Some(i) = image.info() { + Self::init_plt(&mut memory, base, i); + } + // Apply memory protection. if let Err(e) = memory.protect() { return Err(MapError::ProtectMemoryFailed(e)); } - Ok(Self { + // Parse dynamic info. + let mut module = Self { id, entry: image.entry_addr().map(|v| base + v), tls_index, @@ -47,9 +57,16 @@ impl<'a> Module<'a> { (base + p.addr(), p.file_size().try_into().unwrap()) }), flags: ModuleFlags::empty(), + needed: Vec::new(), + modules: Vec::new(), + libraries: Vec::new(), image, memory, - }) + }; + + module.digest_dynamic()?; + + Ok(module) } pub fn id(&self) -> u32 { @@ -76,6 +93,10 @@ impl<'a> Module<'a> { &mut self.flags } + pub fn needed(&self) -> &[NeededModule] { + self.needed.as_ref() + } + pub fn image(&self) -> &Elf { &self.image } @@ -83,6 +104,78 @@ impl<'a> Module<'a> { pub fn memory(&self) -> &Memory<'a> { &self.memory } + + /// See `dynlib_initialize_pltgot_each` on the PS4 for a reference. + fn init_plt(mem: &mut Memory, base: usize, info: &FileInfo) { + let mem = mem.as_mut(); + + for (i, reloc) in info.plt_relocs().enumerate() { + // TODO: Apply the same checks from dynlib_initialize_pltgot_each. + let offset = base + reloc.offset(); + let dst = &mut mem[offset..(offset + 8)]; + + // SAFETY: This is safe because dst is forced to be valid by the above statement. + let i: u64 = i.try_into().unwrap(); + + // Not sure why Sony initialize each PLT relocation to 0xeffffffe????????. + unsafe { std::ptr::write_unaligned(dst.as_mut_ptr() as _, i | 0xeffffffe00000000) }; + } + } + + /// See `digest_dynamic` on the PS4 for a reference. + fn digest_dynamic(&mut self) -> Result<(), MapError> { + let info = match self.image.info() { + Some(v) => v, + None => return Ok(()), // Do nothing if not a dynamic module. + }; + + // TODO: Implement the remaining tags. + for (i, (tag, value)) in info.dynamic().enumerate() { + match tag { + DynamicTag::DT_NULL => break, + DynamicTag::DT_NEEDED => { + let name = u64::from_le_bytes(value); + + match info.read_str(name.try_into().unwrap()) { + Ok(v) => self.needed.push(NeededModule { name: v.to_owned() }), + Err(e) => return Err(MapError::ReadNeededFailed(i, e)), + } + } + DynamicTag::DT_FLAGS => { + let flags = DynamicFlags::from_bits_retain(u64::from_le_bytes(value)); + + if flags.contains(DynamicFlags::DF_SYMBOLIC) { + return Err(MapError::ObsoleteFlags(DynamicFlags::DF_SYMBOLIC)); + } else if flags.contains(DynamicFlags::DF_BIND_NOW) { + return Err(MapError::ObsoleteFlags(DynamicFlags::DF_BIND_NOW)); + } else if flags.contains(DynamicFlags::DF_TEXTREL) { + self.flags |= ModuleFlags::TEXT_REL; + } + } + DynamicTag::DT_SCE_MODULE_INFO | DynamicTag::DT_SCE_NEEDED_MODULE => { + match info.read_module(value) { + Ok(v) => self.modules.push(v), + Err(e) => return Err(MapError::ReadModuleInfoFailed(i, e)), + } + } + DynamicTag::DT_SCE_EXPORT_LIB | DynamicTag::DT_SCE_IMPORT_LIB => { + let mut info = match info.read_library(value) { + Ok(v) => v, + Err(e) => return Err(MapError::ReadLibraryInfoFailed(i, e)), + }; + + if tag == DynamicTag::DT_SCE_EXPORT_LIB { + *info.flags_mut() |= LibraryFlags::EXPORT; + } + + self.libraries.push(info); + } + _ => continue, + } + } + + Ok(()) + } } bitflags! { @@ -90,5 +183,24 @@ bitflags! { #[derive(Clone, Copy, PartialEq)] pub struct ModuleFlags: u16 { const MAIN_PROG = 0x0001; + const TEXT_REL = 0x0002; + const INIT_SCANNED = 0x0010; + } +} + +impl Display for ModuleFlags { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +/// An implementation of `Needed_Entry`. +pub struct NeededModule { + name: String, +} + +impl NeededModule { + pub fn name(&self) -> &str { + self.name.as_ref() } } diff --git a/src/kernel/src/syscalls/mod.rs b/src/kernel/src/syscalls/mod.rs index 6280af24f..81f68e67d 100644 --- a/src/kernel/src/syscalls/mod.rs +++ b/src/kernel/src/syscalls/mod.rs @@ -56,7 +56,7 @@ impl<'a, 'b: 'a> Syscalls<'a, 'b> { ), 592 => self.dynlib_get_list(i.args[0].into(), i.args[1].into(), i.args[2].into()), 598 => self.get_proc_param(i.args[0].into(), i.args[1].into()), - 599 => self.relocate_process(), + 599 => self.dynlib_process_needed_and_relocate(), _ => todo!("syscall {} at {:#018x} on {}", i.id, i.offset, i.module), }; @@ -200,7 +200,7 @@ impl<'a, 'b: 'a> Syscalls<'a, 'b> { Ok(Output::ZERO) } - unsafe fn relocate_process(&self) -> Result { + unsafe fn dynlib_process_needed_and_relocate(&self) -> Result { // Check if application is dynamic linking. let ld = self.ld.read().unwrap(); let app = ld.app().image(); diff --git a/src/system/Cargo.toml b/src/system/Cargo.toml index 1c25c1674..5675dc1d7 100644 --- a/src/system/Cargo.toml +++ b/src/system/Cargo.toml @@ -4,9 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -elf = { path = "../elf" } error = { path = "../error" } -fs = { path = "../fs" } ftp = { path = "../ftp" } -serde_yaml = "0.9" thiserror = "1.0" diff --git a/src/system/src/lib.rs b/src/system/src/lib.rs index ce1fdacf9..855f11776 100644 --- a/src/system/src/lib.rs +++ b/src/system/src/lib.rs @@ -1,6 +1,4 @@ -use elf::Elf; use error::Error; -use fs::ModuleInfo; use ftp::FtpClient; use std::borrow::Cow; use std::collections::VecDeque; @@ -65,7 +63,6 @@ pub unsafe extern "C" fn system_download( // Download the whole system directory. let to = CStr::from_ptr(to); let to = Path::new(to.to_str().unwrap()); - let mut modules: Vec = Vec::new(); let mut dirs = VecDeque::from([(String::from("/system"), to.join("system"))]); while let Some((remote, local)) = dirs.pop_front() { @@ -98,27 +95,12 @@ pub unsafe extern "C" fn system_download( if let Err(e) = download_file(&mut ftp, &remote, &local, item.len(), status) { return Error::new(&e); } - - // Get module info. - match get_module_info(local, remote) { - Ok(v) => { - if let Some(v) = v { - modules.push(v); - } - } - Err(e) => return Error::new(&e), - }; } ItemType::Directory => dirs.push_back((remote, local)), } } } - // Write module database. - if let Err(e) = write_module_db(to.join("modules.yml"), modules) { - return Error::new(&e); - } - null_mut() } @@ -191,69 +173,6 @@ where Ok(()) } -fn get_module_info(local: PathBuf, remote: String) -> Result, DownloadError> { - use elf::OpenError; - - // Check file path. - if !remote.starts_with("/system/common/lib/") && !remote.starts_with("/system/priv/lib/") { - return Ok(None); - } - - // Check file name. - if let Some(ext) = local.extension() { - if ext == "elf" || ext == "self" { - return Ok(None); - } - } - - // Open the file. - let file = match File::open(&local) { - Ok(v) => v, - Err(e) => return Err(DownloadError::OpenFileFailed(local, e)), - }; - - // Open the SELF. - let elf = match Elf::open(local.to_string_lossy(), file) { - Ok(v) => v, - Err(e) => match e { - OpenError::ReadHeaderFailed(i) if i.kind() == std::io::ErrorKind::UnexpectedEof => { - return Ok(None); - } - OpenError::InvalidElfMagic => return Ok(None), - v => return Err(DownloadError::OpenSelfFailed(local, v)), - }, - }; - - // Check if (S)ELF has dynamic linking info. - let dynamic = match elf.dynamic_linking() { - Some(v) => v, - None => return Ok(None), - }; - - // Store the module information. - let info = dynamic.module_info(); - - Ok(Some(ModuleInfo { - name: info.name().into(), - path: remote, - })) -} - -fn write_module_db(path: PathBuf, modules: Vec) -> Result<(), DownloadError> { - // Open the file. - let file = match File::create(&path) { - Ok(v) => v, - Err(e) => return Err(DownloadError::CreateFileFailed(path, e)), - }; - - // Write the file. - if let Err(e) = serde_yaml::to_writer(file, &modules) { - return Err(DownloadError::WriteModuleDbFailed(path, e)); - } - - Ok(()) -} - #[derive(Debug, Error)] pub enum DownloadError { #[error("cannot connect to the FTP server")] @@ -291,13 +210,4 @@ pub enum DownloadError { #[error("cannot close {0}")] CloseRemoteFailed(String, #[source] ftp::retrieve::CloseError), - - #[error("cannot open {0}")] - OpenFileFailed(PathBuf, #[source] std::io::Error), - - #[error("cannot open SELF from {0}")] - OpenSelfFailed(PathBuf, #[source] elf::OpenError), - - #[error("cannot write a module database to {0}")] - WriteModuleDbFailed(PathBuf, #[source] serde_yaml::Error), }