From dfb569b883e9afe883cb8a31455e57cbc41d3ac5 Mon Sep 17 00:00:00 2001 From: Luke Street Date: Sat, 7 Oct 2023 02:03:09 -0400 Subject: [PATCH] Add alf support (NVIDIA Shield TV binaries) - Supports .alf files in all places .dol files are accepted. - Adds `hash` and `dhash` to symbols config. --- src/analysis/cfa.rs | 9 +- src/analysis/pass.rs | 36 +--- src/analysis/signatures.rs | 24 +-- src/analysis/tracker.rs | 7 +- src/cmd/alf.rs | 92 ++++++++ src/cmd/dol.rs | 31 +-- src/cmd/mod.rs | 1 + src/cmd/rel.rs | 14 +- src/main.rs | 2 + src/obj/symbols.rs | 7 + src/util/alf.rs | 274 ++++++++++++++++++++++++ src/util/config.rs | 36 ++-- src/util/dol.rs | 424 +++++++++++++++++++++++++------------ src/util/elf.rs | 2 +- src/util/map.rs | 2 +- src/util/mod.rs | 2 + src/util/reader.rs | 249 ++++++++++++++++++++++ src/util/rel.rs | 6 +- src/util/rso.rs | 13 +- src/util/signatures.rs | 3 +- src/util/split.rs | 10 +- 21 files changed, 976 insertions(+), 268 deletions(-) create mode 100644 src/cmd/alf.rs create mode 100644 src/util/alf.rs create mode 100644 src/util/reader.rs diff --git a/src/analysis/cfa.rs b/src/analysis/cfa.rs index 641dcce..97769dd 100644 --- a/src/analysis/cfa.rs +++ b/src/analysis/cfa.rs @@ -144,15 +144,12 @@ impl AnalyzerState { obj.add_symbol( ObjSymbol { name, - demangled_name: None, address: start.address as u64, section: Some(start.section), size: (end.address - start.address) as u64, size_known: true, - flags: Default::default(), kind: ObjSymbolKind::Function, - align: None, - data_kind: Default::default(), + ..Default::default() }, false, )?; @@ -188,15 +185,13 @@ impl AnalyzerState { obj.add_symbol( ObjSymbol { name: format!("jumptable_{}", address_str), - demangled_name: None, address: addr.address as u64, section: Some(addr.section), size: size as u64, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Local.into()), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, false, )?; diff --git a/src/analysis/pass.rs b/src/analysis/pass.rs index d48cbf0..142b9ab 100644 --- a/src/analysis/pass.rs +++ b/src/analysis/pass.rs @@ -37,28 +37,20 @@ impl AnalysisPass for FindTRKInterruptVectorTable { log::debug!("Found gTRKInterruptVectorTable @ {:#010X}", start); state.known_symbols.insert(start, ObjSymbol { name: "gTRKInterruptVectorTable".to_string(), - demangled_name: None, address: start.address as u64, section: Some(start.section), - size: 0, size_known: true, flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)), - kind: ObjSymbolKind::Unknown, - align: None, - data_kind: Default::default(), + ..Default::default() }); let end = start + TRK_TABLE_SIZE; state.known_symbols.insert(end, ObjSymbol { name: "gTRKInterruptVectorTableEnd".to_string(), - demangled_name: None, address: end.address as u64, section: Some(start.section), - size: 0, size_known: true, flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)), - kind: ObjSymbolKind::Unknown, - align: None, - data_kind: Default::default(), + ..Default::default() }); return Ok(()); @@ -96,29 +88,23 @@ impl AnalysisPass for FindSaveRestSleds { }); state.known_symbols.insert(start, ObjSymbol { name: func.to_string(), - demangled_name: None, address: start.address as u64, section: Some(start.section), size: SLED_SIZE as u64, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), kind: ObjSymbolKind::Function, - align: None, - data_kind: Default::default(), + ..Default::default() }); for i in 14..=31 { let addr = start + (i - 14) * 4; state.known_symbols.insert(addr, ObjSymbol { name: format!("{}{}", label, i), - demangled_name: None, address: addr.address as u64, section: Some(start.section), - size: 0, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: ObjSymbolKind::Unknown, - align: None, - data_kind: Default::default(), + ..Default::default() }); } } @@ -202,30 +188,20 @@ impl AnalysisPass for FindRelCtorsDtors { state.known_sections.insert(ctors_section_index, ".ctors".to_string()); state.known_symbols.insert(SectionAddress::new(ctors_section_index, 0), ObjSymbol { name: "_ctors".to_string(), - demangled_name: None, - address: 0, section: Some(ctors_section_index), - size: 0, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: Default::default(), - align: None, - data_kind: Default::default(), + ..Default::default() }); let dtors_section_index = possible_sections[1].0; state.known_sections.insert(dtors_section_index, ".dtors".to_string()); state.known_symbols.insert(SectionAddress::new(dtors_section_index, 0), ObjSymbol { name: "_dtors".to_string(), - demangled_name: None, - address: 0, section: Some(dtors_section_index), - size: 0, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: Default::default(), - align: None, - data_kind: Default::default(), + ..Default::default() }); // Check for duplicate entries in .dtors, indicating __destroy_global_chain_reference diff --git a/src/analysis/signatures.rs b/src/analysis/signatures.rs index 09cc42d..0a0f544 100644 --- a/src/analysis/signatures.rs +++ b/src/analysis/signatures.rs @@ -245,15 +245,13 @@ fn apply_ctors_signatures(obj: &mut ObjInfo) -> Result<()> { obj.symbols.add( ObjSymbol { name: "__init_cpp_exceptions_reference".to_string(), - demangled_name: None, address, section: Some(ctors_section_index), size: 4, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global | ObjSymbolFlags::RelocationIgnore), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; @@ -303,7 +301,6 @@ fn apply_dtors_signatures(obj: &mut ObjInfo) -> Result<()> { obj.add_symbol( ObjSymbol { name: "__destroy_global_chain_reference".to_string(), - demangled_name: None, address, section: Some(dtors_section_index), size: 4, @@ -312,8 +309,7 @@ fn apply_dtors_signatures(obj: &mut ObjInfo) -> Result<()> { ObjSymbolFlags::Global | ObjSymbolFlags::RelocationIgnore, ), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; @@ -334,7 +330,6 @@ fn apply_dtors_signatures(obj: &mut ObjInfo) -> Result<()> { obj.add_symbol( ObjSymbol { name: "__fini_cpp_exceptions_reference".to_string(), - demangled_name: None, address: address + 4, section: Some(dtors_section_index), size: 4, @@ -343,8 +338,7 @@ fn apply_dtors_signatures(obj: &mut ObjInfo) -> Result<()> { ObjSymbolFlags::Global | ObjSymbolFlags::RelocationIgnore, ), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; @@ -452,15 +446,11 @@ pub fn update_ctors_dtors(obj: &mut ObjInfo) -> Result<()> { if let Some((section_index, section)) = obj.sections.by_name(".ctors")? { obj.symbols.add_direct(ObjSymbol { name: "_ctors".to_string(), - demangled_name: None, address: section.address, section: Some(section_index), - size: 0, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: ObjSymbolKind::Unknown, - align: None, - data_kind: Default::default(), + ..Default::default() })?; } } @@ -468,15 +458,11 @@ pub fn update_ctors_dtors(obj: &mut ObjInfo) -> Result<()> { if let Some((section_index, section)) = obj.sections.by_name(".dtors")? { obj.symbols.add_direct(ObjSymbol { name: "_dtors".to_string(), - demangled_name: None, address: section.address, section: Some(section_index), - size: 0, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: ObjSymbolKind::Unknown, - align: None, - data_kind: Default::default(), + ..Default::default() })?; } } diff --git a/src/analysis/tracker.rs b/src/analysis/tracker.rs index 7c502b5..d3f32ba 100644 --- a/src/analysis/tracker.rs +++ b/src/analysis/tracker.rs @@ -710,15 +710,10 @@ impl Tracker { }; let symbol_idx = obj.symbols.add_direct(ObjSymbol { name, - demangled_name: None, address: target.address as u64, section: Some(target.section), - size: 0, - size_known: false, - flags: Default::default(), - kind: Default::default(), - align: None, data_kind, + ..Default::default() })?; (symbol_idx, 0) }; diff --git a/src/cmd/alf.rs b/src/cmd/alf.rs new file mode 100644 index 0000000..8356e3e --- /dev/null +++ b/src/cmd/alf.rs @@ -0,0 +1,92 @@ +use std::{ + io::{stdout, Write}, + path::PathBuf, +}; + +use anyhow::Result; +use argp::FromArgs; + +use crate::{ + cmd, + util::{ + alf::AlfFile, + file::{buf_writer, map_file}, + reader::{Endian, FromReader}, + }, +}; + +#[derive(FromArgs, PartialEq, Debug)] +/// Commands for processing NVIDIA Shield TV alf files. +#[argp(subcommand, name = "alf")] +pub struct Args { + #[argp(subcommand)] + command: SubCommand, +} + +#[derive(FromArgs, PartialEq, Debug)] +#[argp(subcommand)] +enum SubCommand { + Info(InfoArgs), + Hashes(HashesArgs), +} + +#[derive(FromArgs, PartialEq, Debug)] +/// Prints information about an alf file. (Same as `dol info`) +#[argp(subcommand, name = "info")] +pub struct InfoArgs { + #[argp(positional)] + /// alf file + file: PathBuf, +} + +#[derive(FromArgs, PartialEq, Debug)] +/// Extracts symbol hashes from an alf file. +#[argp(subcommand, name = "hashes")] +pub struct HashesArgs { + #[argp(positional)] + /// alf file + alf_file: PathBuf, + #[argp(positional)] + /// output file + output: Option, +} + +pub fn run(args: Args) -> Result<()> { + match args.command { + SubCommand::Info(c_args) => info(c_args), + SubCommand::Hashes(c_args) => hashes(c_args), + } +} + +fn hashes(args: HashesArgs) -> Result<()> { + let alf_file = { + let file = map_file(&args.alf_file)?; + let mut reader = file.as_reader(); + AlfFile::from_reader(&mut reader, Endian::Little)? + }; + let mut w: Box = if let Some(output) = args.output { + Box::new(buf_writer(output)?) + } else { + Box::new(stdout()) + }; + let mut symbols = alf_file.symbols.clone(); + symbols.sort_by_key(|s| s.address); + for symbol in symbols { + writeln!( + w, + "{:#010X} | {} | {:?} | {} | {} | {:#X}", + symbol.address, + symbol.section, + symbol.kind, + symbol.name, + symbol.demangled_name, + symbol.size + )?; + } + w.flush()?; + Ok(()) +} + +fn info(args: InfoArgs) -> Result<()> { + cmd::dol::info(cmd::dol::InfoArgs { dol_file: args.file, selfile: None }) +} diff --git a/src/cmd/dol.rs b/src/cmd/dol.rs index 48c74e0..e66ed57 100644 --- a/src/cmd/dol.rs +++ b/src/cmd/dol.rs @@ -31,9 +31,8 @@ use crate::{ }, cmd::shasum::file_sha1_string, obj::{ - best_match_for_reloc, ObjDataKind, ObjInfo, ObjKind, ObjReloc, ObjRelocKind, - ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, - SymbolIndex, + best_match_for_reloc, ObjInfo, ObjKind, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, + ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, SymbolIndex, }, util::{ asm::write_asm, @@ -79,10 +78,10 @@ enum SubCommand { pub struct InfoArgs { #[argp(positional)] /// DOL file - dol_file: PathBuf, + pub dol_file: PathBuf, #[argp(option, short = 's')] /// optional path to selfile.sel - selfile: Option, + pub selfile: Option, } #[derive(FromArgs, PartialEq, Eq, Debug)] @@ -366,6 +365,8 @@ fn apply_selfile(obj: &mut ObjInfo, buf: &[u8]) -> Result<()> { kind: existing_symbol.kind, align: existing_symbol.align, data_kind: existing_symbol.data_kind, + name_hash: existing_symbol.name_hash, + demangled_name_hash: existing_symbol.demangled_name_hash, })?; } else { log::debug!("Creating symbol {} at {:#010X}", symbol.name, address); @@ -385,7 +386,7 @@ fn apply_selfile(obj: &mut ObjInfo, buf: &[u8]) -> Result<()> { Ok(()) } -fn info(args: InfoArgs) -> Result<()> { +pub fn info(args: InfoArgs) -> Result<()> { let mut obj = { let file = map_file(&args.dol_file)?; process_dol(file.as_slice(), "")? @@ -523,15 +524,10 @@ fn update_symbols(obj: &mut ObjInfo, modules: &ModuleMap<'_>, create_symbols: bo }; obj.symbols.add_direct(ObjSymbol { name, - demangled_name: None, address: rel_reloc.addend as u64, section: Some(target_section_index), - size: 0, - size_known: false, flags: ObjSymbolFlagSet(ObjSymbolFlags::ForceActive.into()), - kind: Default::default(), - align: None, - data_kind: ObjDataKind::Unknown, + ..Default::default() })?; } } @@ -654,14 +650,7 @@ fn resolve_external_relocations( let symbol_idx = obj.symbols.add_direct(ObjSymbol { name: target_symbol.name.clone(), demangled_name: target_symbol.demangled_name.clone(), - address: 0, - section: None, - size: 0, - size_known: false, - flags: Default::default(), - kind: Default::default(), - align: None, - data_kind: Default::default(), + ..Default::default() })?; e.insert(symbol_idx); @@ -1542,6 +1531,8 @@ fn apply(args: ApplyArgs) -> Result<()> { kind: linked_sym.kind, align: linked_sym.align, data_kind: linked_sym.data_kind, + name_hash: linked_sym.name_hash, + demangled_name_hash: linked_sym.demangled_name_hash, })?; } } diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 5af2f18..a02c35b 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,3 +1,4 @@ +pub mod alf; pub mod ar; pub mod demangle; pub mod dol; diff --git a/src/cmd/rel.rs b/src/cmd/rel.rs index 4631daa..8ce4512 100644 --- a/src/cmd/rel.rs +++ b/src/cmd/rel.rs @@ -471,8 +471,10 @@ fn merge(args: MergeArgs) -> Result<()> { size_known: mod_symbol.size_known, flags: mod_symbol.flags, kind: mod_symbol.kind, - align: None, - data_kind: Default::default(), + align: mod_symbol.align, + data_kind: mod_symbol.data_kind, + name_hash: mod_symbol.name_hash, + demangled_name_hash: mod_symbol.demangled_name_hash, })?; } offset += align32(mod_section.size as u32); @@ -506,15 +508,9 @@ fn merge(args: MergeArgs) -> Result<()> { // Create a new label let symbol_idx = obj.symbols.add_direct(ObjSymbol { name: String::new(), - demangled_name: None, address: target_addr as u64, section: Some(target_section_index), - size: 0, - size_known: false, - flags: Default::default(), - kind: Default::default(), - align: None, - data_kind: Default::default(), + ..Default::default() })?; (symbol_idx, 0) }; diff --git a/src/main.rs b/src/main.rs index f7b3332..fe510f3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -74,6 +74,7 @@ struct TopLevel { #[derive(FromArgs, PartialEq, Debug)] #[argp(subcommand)] enum SubCommand { + Alf(cmd::alf::Args), Ar(cmd::ar::Args), Demangle(cmd::demangle::Args), Dol(cmd::dol::Args), @@ -122,6 +123,7 @@ fn main() { }); } result = result.and_then(|_| match args.command { + SubCommand::Alf(c_args) => cmd::alf::run(c_args), SubCommand::Ar(c_args) => cmd::ar::run(c_args), SubCommand::Demangle(c_args) => cmd::demangle::run(c_args), SubCommand::Dol(c_args) => cmd::dol::run(c_args), diff --git a/src/obj/symbols.rs b/src/obj/symbols.rs index a790bc0..114bc98 100644 --- a/src/obj/symbols.rs +++ b/src/obj/symbols.rs @@ -165,6 +165,9 @@ pub struct ObjSymbol { pub kind: ObjSymbolKind, pub align: Option, pub data_kind: ObjDataKind, + /// ALF hashes + pub name_hash: Option, + pub demangled_name_hash: Option, } pub type SymbolIndex = usize; @@ -263,6 +266,8 @@ impl ObjSymbols { ObjDataKind::Unknown => existing.data_kind, kind => kind, }, + name_hash: in_symbol.name_hash.or(existing.name_hash), + demangled_name_hash: in_symbol.demangled_name_hash.or(existing.demangled_name_hash), }; if existing != &new_symbol { log::debug!("Replacing {:?} with {:?}", existing, new_symbol); @@ -282,6 +287,8 @@ impl ObjSymbols { kind: in_symbol.kind, align: in_symbol.align, data_kind: in_symbol.data_kind, + name_hash: in_symbol.name_hash, + demangled_name_hash: in_symbol.demangled_name_hash, })?; target_symbol_idx }; diff --git a/src/util/alf.rs b/src/util/alf.rs new file mode 100644 index 0000000..7b66489 --- /dev/null +++ b/src/util/alf.rs @@ -0,0 +1,274 @@ +use std::{ + io, + io::{Read, Seek, SeekFrom}, +}; + +use anyhow::Result; +use io::{Error, ErrorKind}; + +use crate::{ + obj::{ObjSymbol, ObjSymbolKind}, + util::{ + dol::{DolLike, DolSection, DolSectionKind}, + reader::{ + read_string, read_vec, read_vec_args, struct_size, Endian, FromReader, DYNAMIC_SIZE, + }, + }, +}; + +pub const ALF_MAGIC: [u8; 4] = *b"RBOF"; + +#[derive(Debug, Clone)] +pub struct AlfFile { + pub header: AlfHeader, + pub sections: Vec, + pub symbols: Vec, +} + +impl FromReader for AlfFile { + type Args = (); + + const STATIC_SIZE: usize = DYNAMIC_SIZE; + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let header = AlfHeader::from_reader(reader, e)?; + if !matches!(header.version, 104 | 105) { + return Err(Error::new( + ErrorKind::InvalidData, + format!("unsupported ALF version: {}", header.version), + )); + } + let alf_sections: Vec = read_vec(reader, header.section_count as usize, e)?; + let symtab = + AlfSymTab::from_reader_args(reader, e, AlfVersionArgs { version: header.version })?; + + // Infer section types from data size and symbol typeFs + let mut sections = Vec::with_capacity(alf_sections.len()); + for section in &alf_sections { + let kind = + if section.data_size == 0 { DolSectionKind::Bss } else { DolSectionKind::Data }; + sections.push(DolSection { + address: section.address, + file_offset: section.file_offset, + data_size: section.data_size, + size: section.size, + kind, + index: sections.len(), + }); + } + for sym in &symtab.symbols { + // Section IDs are 1-based + if sym.section == 0 { + return Err(Error::new(ErrorKind::InvalidData, "invalid ALF symbol section")); + } + if sym.kind == AlfSymbolKind::Function { + sections[sym.section as usize - 1].kind = DolSectionKind::Text; + } + } + + Ok(Self { header, sections, symbols: symtab.symbols }) + } +} + +impl DolLike for AlfFile { + fn sections(&self) -> &[DolSection] { &self.sections } + + fn symbols(&self) -> &[AlfSymbol] { &self.symbols } + + fn entry_point(&self) -> u32 { self.header.entry } + + fn has_unified_bss(&self) -> bool { false } +} + +#[derive(Debug, Clone)] +pub struct AlfHeader { + pub version: u32, + pub entry: u32, + pub section_count: u32, +} + +impl FromReader for AlfHeader { + type Args = (); + + const STATIC_SIZE: usize = struct_size([ + 4, // magic + u32::STATIC_SIZE, // version + u32::STATIC_SIZE, // entry + u32::STATIC_SIZE, // section_count + ]); + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + if <[u8; 4]>::from_reader(reader, e)? != ALF_MAGIC { + return Err(Error::new(ErrorKind::InvalidData, "invalid ALF magic")); + } + Ok(Self { + version: <_>::from_reader(reader, e)?, + entry: <_>::from_reader(reader, e)?, + section_count: <_>::from_reader(reader, e)?, + }) + } +} + +#[derive(Debug, Clone)] +pub struct AlfSection { + pub address: u32, + pub data_size: u32, + pub size: u32, + pub file_offset: u32, +} + +impl FromReader for AlfSection { + type Args = (); + + const STATIC_SIZE: usize = struct_size([ + u32::STATIC_SIZE, // address + u32::STATIC_SIZE, // data_size + u32::STATIC_SIZE, // size + ]); + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let result = Self { + address: <_>::from_reader(reader, e)?, + data_size: <_>::from_reader(reader, e)?, + size: <_>::from_reader(reader, e)?, + file_offset: reader.stream_position()? as u32, + }; + reader.seek(SeekFrom::Current(result.data_size as i64))?; + Ok(result) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum AlfSymbolKind { + Function, + Object, +} + +impl FromReader for AlfSymbolKind { + type Args = (); + + const STATIC_SIZE: usize = u32::STATIC_SIZE; + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + match u32::from_reader(reader, e)? { + 0 => Ok(Self::Function), + 1 => Ok(Self::Object), + v => Err(Error::new(ErrorKind::InvalidData, format!("invalid ALF symbol kind: {}", v))), + } + } +} + +#[derive(Debug, Clone)] +pub struct AlfSymbol { + pub name: String, + pub demangled_name: String, + pub address: u32, + pub size: u32, + pub kind: AlfSymbolKind, + pub section: u32, + pub unk: u32, +} + +#[derive(Copy, Clone)] +pub struct AlfVersionArgs { + pub version: u32, +} + +impl FromReader for AlfSymbol { + type Args = AlfVersionArgs; + + const STATIC_SIZE: usize = DYNAMIC_SIZE; + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + Ok(Self { + name: read_string::(reader, e)?, + demangled_name: read_string::(reader, e)?, + address: <_>::from_reader(reader, e)?, + size: <_>::from_reader(reader, e)?, + kind: <_>::from_reader(reader, e)?, + section: <_>::from_reader(reader, e)?, + unk: if args.version >= 105 { <_>::from_reader(reader, e)? } else { 0 }, + }) + } +} + +impl AlfSymbol { + pub fn to_obj_symbol(&self) -> Result { + let kind = match self.kind { + AlfSymbolKind::Function => ObjSymbolKind::Function, + AlfSymbolKind::Object => ObjSymbolKind::Object, + }; + let (name, name_hash) = if self.name.starts_with('#') { + let hash_str = self.name.trim_start_matches('#'); + let hash = u32::from_str_radix(hash_str, 16)?; + let name = match self.kind { + AlfSymbolKind::Function => format!("fn_{:08X}", self.address), + AlfSymbolKind::Object => format!("lbl_{:08X}", self.address), + }; + (name, Some(hash)) + } else { + (self.name.clone(), None) + }; + let (demangled_name, demangled_name_hash) = if self.demangled_name.starts_with('#') { + let hash_str = self.demangled_name.trim_start_matches('#'); + let hash = u32::from_str_radix(hash_str, 16)?; + (None, Some(hash)) + } else { + (Some(self.demangled_name.clone()), None) + }; + Ok(ObjSymbol { + name, + demangled_name, + address: self.address as u64, + section: Some(self.section as usize - 1), + size: self.size as u64, + size_known: true, + flags: Default::default(), + kind, + align: None, + data_kind: Default::default(), + name_hash, + demangled_name_hash, + }) + } +} + +#[derive(Debug)] +pub struct AlfSymTab { + pub symbols: Vec, +} + +impl FromReader for AlfSymTab { + type Args = AlfVersionArgs; + + const STATIC_SIZE: usize = DYNAMIC_SIZE; + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let _size = u32::from_reader(reader, e)?; + let count = u32::from_reader(reader, e)? as usize; + let symbols = read_vec_args(reader, count, e, args)?; + Ok(Self { symbols }) + } +} + +pub const ALF_HASH_SEED: u32 = 0x1505; + +pub fn alf_hash(mut h: u32, s: &str) -> u32 { + for c in s.bytes() { + h *= 33; + h ^= c as u32; + } + h +} diff --git a/src/util/config.rs b/src/util/config.rs index a974566..15fb3cc 100644 --- a/src/util/config.rs +++ b/src/util/config.rs @@ -75,18 +75,8 @@ pub fn parse_symbol_line(line: &str, obj: &mut ObjInfo) -> Result Result { + let hash = parse_hex(value)?; + symbol.name_hash = Some(hash); + if symbol.demangled_name_hash.is_none() { + symbol.demangled_name_hash = Some(hash); + } + } + "dhash" => { + symbol.demangled_name_hash = Some(parse_hex(value)?); + } _ => bail!("Unknown symbol attribute '{name}'"), } } else { @@ -163,7 +163,9 @@ pub fn is_skip_symbol(symbol: &ObjSymbol) -> bool { } pub fn is_auto_symbol(symbol: &ObjSymbol) -> bool { - symbol.name.starts_with("lbl_") || symbol.name.starts_with("fn_") + symbol.name.starts_with("lbl_") + || symbol.name.starts_with("fn_") + || symbol.name.starts_with("jumptable_") } #[inline] @@ -204,6 +206,14 @@ fn write_symbol(w: &mut W, obj: &ObjInfo, symbol: &ObjSymbol) -> Resul if let Some(kind) = symbol_data_kind_to_str(symbol.data_kind) { write!(w, " data:{kind}")?; } + if let Some(hash) = symbol.name_hash { + write!(w, " hash:{:#010X}", hash)?; + } + if let Some(hash) = symbol.demangled_name_hash { + if symbol.name_hash != symbol.demangled_name_hash { + write!(w, " dhash:{:#010X}", hash)?; + } + } if symbol.flags.is_hidden() { write!(w, " hidden")?; } diff --git a/src/util/dol.rs b/src/util/dol.rs index e7b3b2f..d6b4a24 100644 --- a/src/util/dol.rs +++ b/src/util/dol.rs @@ -1,7 +1,10 @@ -use std::{collections::BTreeMap, io::Cursor}; +use std::{ + collections::BTreeMap, + io, + io::{Cursor, Read, Seek}, +}; use anyhow::{anyhow, bail, ensure, Result}; -use dol::{Dol, DolSection, DolSectionType}; use crate::{ analysis::cfa::{locate_sda_bases, SectionAddress}, @@ -9,6 +12,10 @@ use crate::{ ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, }, + util::{ + alf::{AlfFile, AlfSymbol}, + reader::{skip_bytes, Endian, FromReader}, + }, }; const MAX_TEXT_SECTIONS: usize = 7; @@ -17,34 +24,188 @@ const MAX_ROM_COPY_INFO_SIZE: usize = (MAX_TEXT_SECTIONS + MAX_DATA_SECTIONS + 1 const MAX_BSS_INIT_INFO_SIZE: usize = (MAX_DATA_SECTIONS + 1) * 2 * 4; // num sections * 2 entries * u32 const ETI_INIT_INFO_SIZE: usize = 16; // eti_start, eti_end, code_start, code_size -fn read_u32(dol: &Dol, addr: u32) -> Result { - Ok(u32::from_be_bytes(dol.virtual_data_at(addr, 4)?.try_into()?)) +/// Unified trait for DOL and ALF files +pub trait DolLike { + fn sections(&self) -> &[DolSection]; + + fn symbols(&self) -> &[AlfSymbol] { &[] } + + fn entry_point(&self) -> u32; + + fn has_unified_bss(&self) -> bool; + + fn section_by_address(&self, addr: u32) -> Option<&DolSection> { + self.sections() + .iter() + .find(|section| addr >= section.address && addr < section.address + section.size) + } + + fn virtual_data_at<'a>(&self, buf: &'a [u8], addr: u32, size: u32) -> Result<&'a [u8]> { + let section = self + .section_by_address(addr) + .ok_or_else(|| anyhow!("Failed to locate section for address {:#010X}", addr))?; + let offset = addr - section.address; + ensure!( + offset + size <= section.size, + "Invalid virtual data range {:#010X}-{:#010X} (section: {:#010X}-{:#010X})", + addr, + addr + size, + section.address, + section.address + section.size + ); + let offset = section.file_offset as usize + offset as usize; + Ok(&buf[offset..offset + size as usize]) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DolSectionKind { + Text, + Data, + Bss, +} + +#[derive(Debug, Clone)] +pub struct DolSection { + pub address: u32, + pub file_offset: u32, + pub data_size: u32, + pub size: u32, + pub kind: DolSectionKind, + // TODO remove + pub index: usize, +} + +#[derive(Debug, Clone)] +pub struct DolFile { + pub header: DolHeader, + pub sections: Vec, +} + +impl FromReader for DolFile { + type Args = (); + + const STATIC_SIZE: usize = DolHeader::STATIC_SIZE; + + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let header = DolHeader::from_reader(reader, e)?; + let mut sections = Vec::with_capacity(header.text_sizes.len() + header.data_sizes.len()); + for (idx, &size) in header.text_sizes.iter().enumerate() { + if size == 0 { + continue; + } + sections.push(DolSection { + address: header.text_addrs[idx], + file_offset: header.text_offs[idx], + data_size: size, + size, + kind: DolSectionKind::Text, + index: sections.len(), + }); + } + for (idx, &size) in header.data_sizes.iter().enumerate() { + if size == 0 { + continue; + } + sections.push(DolSection { + address: header.data_addrs[idx], + file_offset: header.data_offs[idx], + data_size: size, + size, + kind: DolSectionKind::Data, + index: sections.len(), + }); + } + sections.push(DolSection { + address: header.bss_addr, + file_offset: 0, + data_size: 0, + size: header.bss_size, + kind: DolSectionKind::Bss, + index: sections.len(), + }); + Ok(Self { header, sections }) + } +} + +#[derive(Debug, Clone)] +pub struct DolHeader { + pub text_offs: [u32; MAX_TEXT_SECTIONS], + pub data_offs: [u32; MAX_DATA_SECTIONS], + pub text_addrs: [u32; MAX_TEXT_SECTIONS], + pub data_addrs: [u32; MAX_DATA_SECTIONS], + pub text_sizes: [u32; MAX_TEXT_SECTIONS], + pub data_sizes: [u32; MAX_DATA_SECTIONS], + pub bss_addr: u32, + pub bss_size: u32, + pub entry_point: u32, +} + +impl FromReader for DolHeader { + type Args = (); + + const STATIC_SIZE: usize = 0x100; + + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let result = Self { + text_offs: <_>::from_reader(reader, e)?, + data_offs: <_>::from_reader(reader, e)?, + text_addrs: <_>::from_reader(reader, e)?, + data_addrs: <_>::from_reader(reader, e)?, + text_sizes: <_>::from_reader(reader, e)?, + data_sizes: <_>::from_reader(reader, e)?, + bss_addr: <_>::from_reader(reader, e)?, + bss_size: <_>::from_reader(reader, e)?, + entry_point: <_>::from_reader(reader, e)?, + }; + skip_bytes::<0x1C, _>(reader)?; // padding + Ok(result) + } +} + +impl DolLike for DolFile { + fn sections(&self) -> &[DolSection] { &self.sections } + + fn entry_point(&self) -> u32 { self.header.entry_point } + + fn has_unified_bss(&self) -> bool { true } +} + +fn read_u32(buf: &[u8], dol: &dyn DolLike, addr: u32) -> Result { + Ok(u32::from_be_bytes(dol.virtual_data_at(buf, addr, 4)?.try_into()?)) } pub fn process_dol(buf: &[u8], name: &str) -> Result { - let dol = Dol::read_from(Cursor::new(buf))?; + let mut reader = Cursor::new(buf); + let dol: Box = if buf.len() > 4 && &buf[0..4] == b"RBOF" { + Box::new(AlfFile::from_reader(&mut reader, Endian::Little)?) + } else { + Box::new(DolFile::from_reader(&mut reader, Endian::Big)?) + }; // Locate _rom_copy_info let first_rom_section = dol - .header - .sections + .sections() .iter() - .find(|section| section.kind != DolSectionType::Bss) + .find(|section| section.kind != DolSectionKind::Bss) .ok_or_else(|| anyhow!("Failed to locate first rom section"))?; - let init_section = section_by_address(&dol, dol.header.entry_point) + let init_section = dol + .section_by_address(dol.entry_point()) .ok_or_else(|| anyhow!("Failed to locate .init section"))?; let rom_copy_info_addr = { - let mut addr = init_section.target + init_section.size + let mut addr = init_section.address + init_section.size - MAX_ROM_COPY_INFO_SIZE as u32 - MAX_BSS_INIT_INFO_SIZE as u32; loop { - let value = read_u32(&dol, addr)?; - if value == first_rom_section.target { + let value = read_u32(buf, dol.as_ref(), addr)?; + if value == first_rom_section.address { log::debug!("Found _rom_copy_info @ {addr:#010X}"); break Some(addr); } addr += 4; - if addr >= init_section.target + init_section.size { + if addr >= init_section.address + init_section.size { log::warn!("Failed to locate _rom_copy_info"); break None; } @@ -55,19 +216,19 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { let mut rom_sections = BTreeMap::::new(); let rom_copy_info_end = match rom_copy_info_addr { Some(mut addr) => loop { - let rom = read_u32(&dol, addr)?; - let copy = read_u32(&dol, addr + 4)?; + let rom = read_u32(buf, dol.as_ref(), addr)?; + let copy = read_u32(buf, dol.as_ref(), addr + 4)?; ensure!( rom == copy, "Unsupported section: ROM address {rom:#010X} != copy address {copy:#010X}", ); - let size = read_u32(&dol, addr + 8)?; + let size = read_u32(buf, dol.as_ref(), addr + 8)?; addr += 12; if size == 0 { log::debug!("Found _rom_copy_info end @ {addr:#010X}"); break Some(addr); } - if addr >= init_section.target + init_section.size { + if addr >= init_section.address + init_section.size { log::warn!("Failed to locate _rom_copy_info end"); break None; } @@ -78,20 +239,19 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { // Locate _bss_init_info let bss_section = dol - .header - .sections + .sections() .iter() - .find(|section| section.kind == DolSectionType::Bss) + .find(|section| section.kind == DolSectionKind::Bss) .ok_or_else(|| anyhow!("Failed to locate BSS section"))?; let bss_init_info_addr = match rom_copy_info_end { Some(mut addr) => loop { - let value = read_u32(&dol, addr)?; - if value == bss_section.target { + let value = read_u32(buf, dol.as_ref(), addr)?; + if value == bss_section.address { log::debug!("Found _bss_init_info @ {addr:#010X}"); break Some(addr); } addr += 4; - if addr >= init_section.target + init_section.size { + if addr >= init_section.address + init_section.size { log::warn!("Failed to locate _bss_init_info"); break None; } @@ -103,14 +263,14 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { let mut bss_sections = BTreeMap::::new(); let bss_init_info_end = match bss_init_info_addr { Some(mut addr) => loop { - let rom = read_u32(&dol, addr)?; - let size = read_u32(&dol, addr + 4)?; + let rom = read_u32(buf, dol.as_ref(), addr)?; + let size = read_u32(buf, dol.as_ref(), addr + 4)?; addr += 8; if size == 0 { log::debug!("Found _bss_init_info end @ {addr:#010X}"); break Some(addr); } - if addr >= init_section.target + init_section.size { + if addr >= init_section.address + init_section.size { log::warn!("Failed to locate _bss_init_info end"); break None; } @@ -121,27 +281,27 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { // Locate _eti_init_info let num_text_sections = - dol.header.sections.iter().filter(|section| section.kind == DolSectionType::Text).count(); + dol.sections().iter().filter(|section| section.kind == DolSectionKind::Text).count(); let mut eti_entries: Vec = Vec::new(); let mut eti_init_info_range: Option<(u32, u32)> = None; let mut extab_section: Option = None; let mut extabindex_section: Option = None; 'outer: for dol_section in - dol.header.sections.iter().filter(|section| section.kind == DolSectionType::Data) + dol.sections().iter().filter(|section| section.kind == DolSectionKind::Data) { // Use section size from _rom_copy_info - let dol_section_size = match rom_sections.get(&dol_section.target) { + let dol_section_size = match rom_sections.get(&dol_section.address) { Some(&size) => size, None => dol_section.size, }; - let dol_section_end = dol_section.target + dol_section_size; + let dol_section_end = dol_section.address + dol_section_size; let eti_init_info_addr = { let mut addr = dol_section_end - (ETI_INIT_INFO_SIZE * (num_text_sections + 1)) as u32; loop { - let eti_init_info = read_eti_init_info(&dol, addr)?; + let eti_init_info = read_eti_init_info(buf, dol.as_ref(), addr)?; if validate_eti_init_info( - &dol, + dol.as_ref(), &eti_init_info, dol_section, dol_section_end, @@ -160,7 +320,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { let eti_init_info_end = { let mut addr = eti_init_info_addr; loop { - let eti_init_info = read_eti_init_info(&dol, addr)?; + let eti_init_info = read_eti_init_info(buf, dol.as_ref(), addr)?; addr += 16; if eti_init_info.is_zero() { break; @@ -172,7 +332,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { ); } if !validate_eti_init_info( - &dol, + dol.as_ref(), &eti_init_info, dol_section, dol_section_end, @@ -181,9 +341,9 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { bail!("Invalid _eti_init_info entry: {:#010X?}", eti_init_info); } for addr in (eti_init_info.eti_start..eti_init_info.eti_end).step_by(12) { - let eti_entry = read_eti_entry(&dol, addr)?; + let eti_entry = read_eti_entry(buf, dol.as_ref(), addr)?; let entry_section = - section_by_address(&dol, eti_entry.extab_addr).ok_or_else(|| { + dol.section_by_address(eti_entry.extab_addr).ok_or_else(|| { anyhow!( "Failed to locate section for extab address {:#010X}", eti_entry.extab_addr @@ -216,9 +376,12 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { // Add text and data sections let mut sections = vec![]; - for dol_section in - dol.header.sections.iter().filter(|section| section.kind != DolSectionType::Bss) - { + for dol_section in dol.sections().iter() { + // We'll split .bss later + if dol_section.kind == DolSectionKind::Bss && dol.has_unified_bss() { + continue; + } + let (name, kind, known) = match dol_section.index { idx if idx == init_section.index => (".init".to_string(), ObjSectionKind::Code, true), idx if Some(idx) == extab_section => { @@ -227,79 +390,86 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { idx if Some(idx) == extabindex_section => { ("extabindex".to_string(), ObjSectionKind::ReadOnlyData, true) } - _ if num_text_sections == 2 && dol_section.kind == DolSectionType::Text => { + _ if num_text_sections == 2 && dol_section.kind == DolSectionKind::Text => { (".text".to_string(), ObjSectionKind::Code, true) } idx => match dol_section.kind { - DolSectionType::Text => (format!(".text{idx}"), ObjSectionKind::Code, false), - DolSectionType::Data => (format!(".data{idx}"), ObjSectionKind::Data, false), - DolSectionType::Bss => unreachable!(), + DolSectionKind::Text => (format!(".text{idx}"), ObjSectionKind::Code, false), + DolSectionKind::Data => (format!(".data{idx}"), ObjSectionKind::Data, false), + DolSectionKind::Bss => (format!(".bss{}", idx), ObjSectionKind::Bss, false), }, }; - // Use section size from _rom_copy_info - let size = match rom_sections.get(&dol_section.target) { - Some(&size) => size, - None => { - if !rom_sections.is_empty() { - log::warn!( - "Section {} ({:#010X}) doesn't exist in _rom_copy_info", - dol_section.index, - dol_section.target - ); + let (size, data): (u32, &[u8]) = if kind == ObjSectionKind::Bss { + (dol_section.size, &[]) + } else { + // Use section size from _rom_copy_info + let size = match rom_sections.get(&dol_section.address) { + Some(&size) => size, + None => { + if !rom_sections.is_empty() { + log::warn!( + "Section {} ({:#010X}) doesn't exist in _rom_copy_info", + dol_section.index, + dol_section.address + ); + } + dol_section.size } - dol_section.size - } + }; + (size, dol.virtual_data_at(buf, dol_section.address, size)?) }; sections.push(ObjSection { name, kind, - address: dol_section.target as u64, + address: dol_section.address as u64, size: size as u64, - data: dol.virtual_data_at(dol_section.target, size)?.to_vec(), + data: data.to_vec(), align: 0, elf_index: 0, relocations: Default::default(), original_address: 0, - file_offset: dol_section.offset as u64, + file_offset: dol_section.file_offset as u64, section_known: known, splits: Default::default(), }); } - // Add BSS sections from _bss_init_info - for (idx, (&addr, &size)) in bss_sections.iter().enumerate() { - ensure!( - addr >= bss_section.target - && addr < bss_section.target + bss_section.size - && addr + size <= bss_section.target + bss_section.size, - "Invalid BSS range {:#010X}-{:#010X} (DOL BSS: {:#010X}-{:#010X})", - addr, - addr + size, - bss_section.target, - bss_section.target + bss_section.size - ); + if dol.has_unified_bss() { + // Add BSS sections from _bss_init_info + for (idx, (&addr, &size)) in bss_sections.iter().enumerate() { + ensure!( + addr >= bss_section.address + && addr < bss_section.address + bss_section.size + && addr + size <= bss_section.address + bss_section.size, + "Invalid BSS range {:#010X}-{:#010X} (DOL BSS: {:#010X}-{:#010X})", + addr, + addr + size, + bss_section.address, + bss_section.address + bss_section.size + ); - sections.push(ObjSection { - name: format!(".bss{}", idx), - kind: ObjSectionKind::Bss, - address: addr as u64, - size: size as u64, - data: vec![], - align: 0, - elf_index: 0, - relocations: Default::default(), - original_address: 0, - file_offset: 0, - section_known: false, - splits: Default::default(), - }); + sections.push(ObjSection { + name: format!(".bss{}", idx), + kind: ObjSectionKind::Bss, + address: addr as u64, + size: size as u64, + data: vec![], + align: 0, + elf_index: 0, + relocations: Default::default(), + original_address: 0, + file_offset: 0, + section_known: false, + splits: Default::default(), + }); + } + + // Sort sections by address ascending + sections.sort_by_key(|s| s.address); } - // Sort sections by address ascending - sections.sort_by_key(|s| s.address); - // Apply section indices let mut init_section_index = None; for (idx, section) in sections.iter_mut().enumerate() { @@ -328,7 +498,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { vec![], sections, ); - obj.entry = Some(dol.header.entry_point as u64); + obj.entry = Some(dol.entry_point() as u64); // Generate _rom_copy_info symbol if let (Some(rom_copy_info_addr), Some(rom_copy_info_end)) = @@ -337,15 +507,13 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { obj.add_symbol( ObjSymbol { name: "_rom_copy_info".to_string(), - demangled_name: None, address: rom_copy_info_addr as u64, section: init_section_index, size: (rom_copy_info_end - rom_copy_info_addr) as u64, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; @@ -358,15 +526,13 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { obj.add_symbol( ObjSymbol { name: "_bss_init_info".to_string(), - demangled_name: None, address: bss_init_info_addr as u64, section: init_section_index, size: (bss_init_info_end - bss_init_info_addr) as u64, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; @@ -377,15 +543,13 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { obj.add_symbol( ObjSymbol { name: "_eti_init_info".to_string(), - demangled_name: None, address: eti_init_info_addr as u64, section: extabindex_section, size: (eti_init_info_end - eti_init_info_addr) as u64, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; @@ -424,15 +588,13 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { obj.add_symbol( ObjSymbol { name: format!("@eti_{:08X}", entry.address), - demangled_name: None, address: entry.address as u64, section: Some(extabindex_section_index), size: 12, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Local | ObjSymbolFlags::Hidden), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, false, )?; @@ -451,15 +613,13 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { obj.add_symbol( ObjSymbol { name: format!("@etb_{:08X}", addr), - demangled_name: None, address: addr as u64, section: Some(extab_section_index), size: size as u64, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Local | ObjSymbolFlags::Hidden), kind: ObjSymbolKind::Object, - align: None, - data_kind: Default::default(), + ..Default::default() }, false, )?; @@ -504,30 +664,20 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { obj.add_symbol( ObjSymbol { name: "_SDA2_BASE_".to_string(), - demangled_name: None, address: sda2_base as u64, - section: None, - size: 0, - size_known: false, + size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: ObjSymbolKind::Unknown, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; obj.add_symbol( ObjSymbol { name: "_SDA_BASE_".to_string(), - demangled_name: None, address: sda_base as u64, - section: None, - size: 0, - size_known: false, + size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), - kind: ObjSymbolKind::Unknown, - align: None, - data_kind: Default::default(), + ..Default::default() }, true, )?; @@ -540,6 +690,11 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result { } } + // Apply ALF symbols + for symbol in dol.symbols() { + obj.add_symbol(symbol.to_obj_symbol()?, true)?; + } + Ok(obj) } @@ -566,35 +721,35 @@ struct EtiEntry { extab_addr: u32, } -fn read_eti_init_info(dol: &Dol, addr: u32) -> Result { - let eti_start = read_u32(dol, addr)?; - let eti_end = read_u32(dol, addr + 4)?; - let code_start = read_u32(dol, addr + 8)?; - let code_size = read_u32(dol, addr + 12)?; +fn read_eti_init_info(buf: &[u8], dol: &dyn DolLike, addr: u32) -> Result { + let eti_start = read_u32(buf, dol, addr)?; + let eti_end = read_u32(buf, dol, addr + 4)?; + let code_start = read_u32(buf, dol, addr + 8)?; + let code_size = read_u32(buf, dol, addr + 12)?; Ok(EtiInitInfo { eti_start, eti_end, code_start, code_size }) } -fn read_eti_entry(dol: &Dol, address: u32) -> Result { - let function = read_u32(dol, address)?; - let function_size = read_u32(dol, address + 4)?; - let extab_addr = read_u32(dol, address + 8)?; +fn read_eti_entry(buf: &[u8], dol: &dyn DolLike, address: u32) -> Result { + let function = read_u32(buf, dol, address)?; + let function_size = read_u32(buf, dol, address + 4)?; + let extab_addr = read_u32(buf, dol, address + 8)?; Ok(EtiEntry { address, function, function_size, extab_addr }) } fn validate_eti_init_info( - dol: &Dol, + dol: &dyn DolLike, eti_init_info: &EtiInitInfo, eti_section: &DolSection, eti_section_end: u32, rom_sections: &BTreeMap, ) -> Result { - if eti_init_info.eti_start >= eti_section.target + if eti_init_info.eti_start >= eti_section.address && eti_init_info.eti_start < eti_section_end - && eti_init_info.eti_end >= eti_section.target + && eti_init_info.eti_end >= eti_section.address && eti_init_info.eti_end < eti_section_end { - if let Some(code_section) = section_by_address(dol, eti_init_info.code_start) { - let code_section_size = match rom_sections.get(&code_section.target) { + if let Some(code_section) = dol.section_by_address(eti_init_info.code_start) { + let code_section_size = match rom_sections.get(&code_section.address) { Some(&size) => size, None => code_section.size, }; @@ -605,10 +760,3 @@ fn validate_eti_init_info( } Ok(false) } - -fn section_by_address(dol: &Dol, addr: u32) -> Option<&DolSection> { - dol.header - .sections - .iter() - .find(|section| addr >= section.target && addr < section.target + section.size) -} diff --git a/src/util/elf.rs b/src/util/elf.rs index fc772e5..1110316 100644 --- a/src/util/elf.rs +++ b/src/util/elf.rs @@ -816,7 +816,7 @@ fn to_obj_symbol( _ => bail!("Unsupported symbol kind: {:?}", symbol), }, align, - data_kind: Default::default(), + ..Default::default() }) } diff --git a/src/util/map.rs b/src/util/map.rs index 8461b22..a590ed2 100644 --- a/src/util/map.rs +++ b/src/util/map.rs @@ -688,7 +688,7 @@ fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option ObjSymbolKind::Unknown, }, align: symbol_entry.align, - data_kind: Default::default(), + ..Default::default() }, true, )?; diff --git a/src/util/mod.rs b/src/util/mod.rs index b3e911e..f2800aa 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,5 +1,6 @@ use std::{borrow::Cow, ops::Deref}; +pub mod alf; pub mod asm; pub mod comment; pub mod config; @@ -12,6 +13,7 @@ pub mod lcf; pub mod map; pub mod nested; pub mod rarc; +pub mod reader; pub mod rel; pub mod rso; pub mod signatures; diff --git a/src/util/reader.rs b/src/util/reader.rs new file mode 100644 index 0000000..5d5f0ab --- /dev/null +++ b/src/util/reader.rs @@ -0,0 +1,249 @@ +use std::{ + io, + io::{Error, ErrorKind, Read, Seek, SeekFrom}, +}; + +use io::Write; + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Endian { + Big, + Little, +} + +pub const DYNAMIC_SIZE: usize = 0; + +pub const fn struct_size(fields: [usize; N]) -> usize { + let mut result = 0; + let mut i = 0; + while i < N { + let size = fields[i]; + if size == DYNAMIC_SIZE { + // Dynamically sized + return DYNAMIC_SIZE; + } + result += size; + i += 1; + } + result +} + +#[inline] +pub fn skip_bytes(reader: &mut R) -> io::Result<()> +where R: Read + Seek + ?Sized { + reader.seek(SeekFrom::Current(N as i64))?; + Ok(()) +} + +pub trait FromReader: Sized { + type Args; + + const STATIC_SIZE: usize; + + fn from_reader_args(reader: &mut R, e: Endian, args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized; + + fn from_reader(reader: &mut R, e: Endian) -> io::Result + where + R: Read + Seek + ?Sized, + Self::Args: Default, + { + Self::from_reader_args(reader, e, Default::default()) + } +} + +macro_rules! impl_from_reader { + ($($t:ty),*) => { + $( + impl FromReader for $t { + const STATIC_SIZE: usize = std::mem::size_of::(); + + type Args = (); + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let mut buf = [0u8; Self::STATIC_SIZE]; + reader.read_exact(&mut buf)?; + Ok(match e { + Endian::Big => Self::from_be_bytes(buf), + Endian::Little => Self::from_le_bytes(buf), + }) + } + } + )* + }; +} + +impl_from_reader!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +impl FromReader for [u8; N] { + type Args = (); + + const STATIC_SIZE: usize = N; + + #[inline] + fn from_reader_args(reader: &mut R, _e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let mut buf = [0u8; N]; + reader.read_exact(&mut buf)?; + Ok(buf) + } +} + +impl FromReader for [u32; N] { + type Args = (); + + const STATIC_SIZE: usize = N * u32::STATIC_SIZE; + + #[inline] + fn from_reader_args(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result + where R: Read + Seek + ?Sized { + let mut buf = [0u32; N]; + reader.read_exact(unsafe { + std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, Self::STATIC_SIZE) + })?; + if e == Endian::Big { + for x in buf.iter_mut() { + *x = u32::from_be(*x); + } + } + Ok(buf) + } +} + +#[inline] +pub fn read_bytes(reader: &mut R, count: usize) -> io::Result> +where R: Read + Seek + ?Sized { + let mut buf = vec![0u8; count]; + reader.read_exact(&mut buf)?; + Ok(buf) +} + +#[inline] +pub fn read_vec(reader: &mut R, count: usize, e: Endian) -> io::Result> +where + T: FromReader, + T::Args: Default, + R: Read + Seek + ?Sized, +{ + let mut vec = Vec::with_capacity(count); + for _ in 0..count { + vec.push(T::from_reader(reader, e)?); + } + Ok(vec) +} + +#[inline] +pub fn read_vec_args( + reader: &mut R, + count: usize, + e: Endian, + args: T::Args, +) -> io::Result> +where + T: FromReader, + T::Args: Clone, + R: Read + Seek + ?Sized, +{ + let mut vec = Vec::with_capacity(count); + for _ in 0..count { + vec.push(T::from_reader_args(reader, e, args.clone())?); + } + Ok(vec) +} + +#[inline] +pub fn read_string(reader: &mut R, e: Endian) -> io::Result +where + T: FromReader + TryInto, + T::Args: Default, + R: Read + Seek + ?Sized, +{ + let len = ::from_reader(reader, e)? + .try_into() + .map_err(|_| Error::new(ErrorKind::InvalidData, "invalid string length"))?; + let mut buf = vec![0u8; len]; + reader.read_exact(&mut buf)?; + String::from_utf8(buf).map_err(|e| Error::new(ErrorKind::InvalidData, e)) +} + +pub trait ToWriter: Sized { + fn to_writer(&self, writer: &mut W, e: Endian) -> io::Result<()> + where W: Write + ?Sized; + + fn to_bytes(&self, e: Endian) -> io::Result> { + let mut buf = vec![0u8; self.write_size()]; + self.to_writer(&mut buf.as_mut_slice(), e)?; + Ok(buf) + } + + fn write_size(&self) -> usize; +} + +macro_rules! impl_to_writer { + ($($t:ty),*) => { + $( + impl ToWriter for $t { + fn to_writer(&self, writer: &mut W, e: Endian) -> io::Result<()> + where W: Write + ?Sized { + writer.write_all(&match e { + Endian::Big => self.to_be_bytes(), + Endian::Little => self.to_le_bytes(), + }) + } + + fn to_bytes(&self, e: Endian) -> io::Result> { + Ok(match e { + Endian::Big => self.to_be_bytes(), + Endian::Little => self.to_le_bytes(), + }.to_vec()) + } + + fn write_size(&self) -> usize { + std::mem::size_of::() + } + } + )* + }; +} + +impl_to_writer!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128); + +impl ToWriter for [u8; N] { + fn to_writer(&self, writer: &mut W, _e: Endian) -> io::Result<()> + where W: Write + ?Sized { + writer.write_all(self) + } + + fn write_size(&self) -> usize { N } +} + +impl ToWriter for &[u8] { + fn to_writer(&self, writer: &mut W, _e: Endian) -> io::Result<()> + where W: Write + ?Sized { + writer.write_all(self) + } + + fn write_size(&self) -> usize { self.len() } +} + +impl ToWriter for Vec { + fn to_writer(&self, writer: &mut W, _e: Endian) -> io::Result<()> + where W: Write + ?Sized { + writer.write_all(self) + } + + fn write_size(&self) -> usize { self.len() } +} + +pub fn write_vec(writer: &mut W, vec: &[T], e: Endian) -> io::Result<()> +where + T: ToWriter, + W: Write + ?Sized, +{ + for item in vec { + item.to_writer(writer, e)?; + } + Ok(()) +} diff --git a/src/util/rel.rs b/src/util/rel.rs index 4640d0a..65b1319 100644 --- a/src/util/rel.rs +++ b/src/util/rel.rs @@ -232,15 +232,11 @@ pub fn process_rel(reader: &mut R, name: &str) -> Result<(RelHea } symbols.push(ObjSymbol { name: name.to_string(), - demangled_name: None, address: offset as u64, section: Some(section_index), - size: 0, - size_known: false, flags, kind: ObjSymbolKind::Function, - align: None, - data_kind: Default::default(), + ..Default::default() }); } Ok(()) diff --git a/src/util/rso.rs b/src/util/rso.rs index 1aa6e34..5956bc5 100644 --- a/src/util/rso.rs +++ b/src/util/rso.rs @@ -128,15 +128,11 @@ pub fn process_rso(reader: &mut R) -> Result { log::debug!("Adding {name} section {rel_section_idx} offset {offset:#X}"); symbols.push(ObjSymbol { name: name.to_string(), - demangled_name: None, address: offset as u64, section: Some(section_index), - size: 0, - size_known: false, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), kind: ObjSymbolKind::Function, - align: None, - data_kind: Default::default(), + ..Default::default() }); } Ok(()) @@ -196,12 +192,7 @@ pub fn process_rso(reader: &mut R) -> Result { demangled_name, address: sym_off as u64, section: Some(section), - size: 0, - size_known: false, - flags: Default::default(), - kind: Default::default(), - align: None, - data_kind: Default::default(), + ..Default::default() }); } reader.seek(SeekFrom::Start(import_table_offset as u64))?; diff --git a/src/util/signatures.rs b/src/util/signatures.rs index f5e0270..1e64eee 100644 --- a/src/util/signatures.rs +++ b/src/util/signatures.rs @@ -142,8 +142,7 @@ pub fn apply_symbol( size_known: sig_symbol.size > 0 || sig_symbol.kind == ObjSymbolKind::Unknown, flags: sig_symbol.flags, kind: sig_symbol.kind, - align: None, - data_kind: Default::default(), + ..Default::default() }, false, )?; diff --git a/src/util/split.rs b/src/util/split.rs index 188fd6e..e54fb7e 100644 --- a/src/util/split.rs +++ b/src/util/split.rs @@ -554,7 +554,6 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> { log::debug!("Adding padding symbol {} at {:#010X}", symbol_name, addr); obj.symbols.add_direct(ObjSymbol { name: symbol_name, - demangled_name: None, address: addr as u64, section: Some(section_index), size: next_symbol_address - addr as u64, @@ -568,8 +567,7 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> { ObjSymbolKind::Object } }, - align: None, - data_kind: Default::default(), + ..Default::default() })?; } } @@ -600,7 +598,6 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> { log::debug!("Adding gap symbol {} at {:#010X}", symbol_name, aligned_end); to_add.push(ObjSymbol { name: symbol_name, - demangled_name: None, address: aligned_end as u64, section: Some(section_index), size: next_symbol.address - aligned_end as u64, @@ -616,8 +613,7 @@ fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> { | ObjSectionKind::ReadOnlyData | ObjSectionKind::Bss => ObjSymbolKind::Object, }, - align: None, - data_kind: Default::default(), + ..Default::default() }); } Ordering::Equal => {} @@ -1048,6 +1044,8 @@ pub fn split_obj(obj: &ObjInfo) -> Result> { kind: symbol.kind, align: symbol.align, data_kind: symbol.data_kind, + name_hash: symbol.name_hash, + demangled_name_hash: symbol.demangled_name_hash, })?); }