Add alf support (NVIDIA Shield TV binaries)

- Supports .alf files in all places .dol files are accepted.
- Adds `hash` and `dhash` to symbols config.
This commit is contained in:
Luke Street 2023-10-07 02:03:09 -04:00
parent db506fb927
commit dfb569b883
21 changed files with 976 additions and 268 deletions

View File

@ -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,
)?;

View File

@ -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

View File

@ -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()
})?;
}
}

View File

@ -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)
};

92
src/cmd/alf.rs Normal file
View File

@ -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<PathBuf>,
}
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<dyn Write> = 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 })
}

View File

@ -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<PathBuf>,
pub selfile: Option<PathBuf>,
}
#[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,
})?;
}
}

View File

@ -1,3 +1,4 @@
pub mod alf;
pub mod ar;
pub mod demangle;
pub mod dol;

View File

@ -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)
};

View File

@ -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),

View File

@ -165,6 +165,9 @@ pub struct ObjSymbol {
pub kind: ObjSymbolKind,
pub align: Option<u32>,
pub data_kind: ObjDataKind,
/// ALF hashes
pub name_hash: Option<u32>,
pub demangled_name_hash: Option<u32>,
}
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
};

274
src/util/alf.rs Normal file
View File

@ -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<DolSection>,
pub symbols: Vec<AlfSymbol>,
}
impl FromReader for AlfFile {
type Args = ();
const STATIC_SIZE: usize = DYNAMIC_SIZE;
#[inline]
fn from_reader_args<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<AlfSection> = 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<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<R>(reader: &mut R, e: Endian, args: Self::Args) -> io::Result<Self>
where R: Read + Seek + ?Sized {
Ok(Self {
name: read_string::<u32, _>(reader, e)?,
demangled_name: read_string::<u32, _>(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<ObjSymbol> {
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<AlfSymbol>,
}
impl FromReader for AlfSymTab {
type Args = AlfVersionArgs;
const STATIC_SIZE: usize = DYNAMIC_SIZE;
#[inline]
fn from_reader_args<R>(reader: &mut R, e: Endian, args: Self::Args) -> io::Result<Self>
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
}

View File

@ -75,18 +75,8 @@ pub fn parse_symbol_line(line: &str, obj: &mut ObjInfo) -> Result<Option<ObjSymb
bail!("Section {} not found", section_name)
};
let demangled_name = demangle(&name, &DemangleOptions::default());
let mut symbol = ObjSymbol {
name,
demangled_name,
address: addr as u64,
section,
size: 0,
size_known: false,
flags: Default::default(),
kind: ObjSymbolKind::Unknown,
align: None,
data_kind: Default::default(),
};
let mut symbol =
ObjSymbol { name, demangled_name, address: addr as u64, section, ..Default::default() };
// TODO move somewhere common
if symbol.name.starts_with("..") {
symbol.flags.0 |= ObjSymbolFlags::ForceActive;
@ -114,6 +104,16 @@ pub fn parse_symbol_line(line: &str, obj: &mut ObjInfo) -> Result<Option<ObjSymb
symbol.data_kind = symbol_data_kind_from_str(value)
.ok_or_else(|| anyhow!("Unknown symbol data type '{}'", value))?;
}
"hash" => {
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: Write>(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")?;
}

View File

@ -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<u32> {
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<DolSection>,
}
impl FromReader for DolFile {
type Args = ();
const STATIC_SIZE: usize = DolHeader::STATIC_SIZE;
fn from_reader_args<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<u32> {
Ok(u32::from_be_bytes(dol.virtual_data_at(buf, addr, 4)?.try_into()?))
}
pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
let dol = Dol::read_from(Cursor::new(buf))?;
let mut reader = Cursor::new(buf);
let dol: Box<dyn DolLike> = 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<ObjInfo> {
let mut rom_sections = BTreeMap::<u32, u32>::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<ObjInfo> {
// 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<ObjInfo> {
let mut bss_sections = BTreeMap::<u32, u32>::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<ObjInfo> {
// 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<EtiEntry> = Vec::new();
let mut eti_init_info_range: Option<(u32, u32)> = None;
let mut extab_section: Option<usize> = None;
let mut extabindex_section: Option<usize> = 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<ObjInfo> {
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<ObjInfo> {
);
}
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<ObjInfo> {
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<ObjInfo> {
// 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,58 +390,64 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
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),
},
};
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.target) {
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.target
dol_section.address
);
}
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(),
});
}
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.target
&& addr < bss_section.target + bss_section.size
&& addr + size <= bss_section.target + bss_section.size,
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.target,
bss_section.target + bss_section.size
bss_section.address,
bss_section.address + bss_section.size
);
sections.push(ObjSection {
@ -299,6 +468,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
// Sort sections by address ascending
sections.sort_by_key(|s| s.address);
}
// Apply section indices
let mut init_section_index = None;
@ -328,7 +498,7 @@ pub fn process_dol(buf: &[u8], name: &str) -> Result<ObjInfo> {
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<ObjInfo> {
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<ObjInfo> {
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<ObjInfo> {
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<ObjInfo> {
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<ObjInfo> {
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<ObjInfo> {
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<ObjInfo> {
}
}
// 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<EtiInitInfo> {
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<EtiInitInfo> {
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<EtiEntry> {
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<EtiEntry> {
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<u32, u32>,
) -> Result<bool> {
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)
}

View File

@ -816,7 +816,7 @@ fn to_obj_symbol(
_ => bail!("Unsupported symbol kind: {:?}", symbol),
},
align,
data_kind: Default::default(),
..Default::default()
})
}

View File

@ -688,7 +688,7 @@ fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option<usi
SymbolKind::NoType => ObjSymbolKind::Unknown,
},
align: symbol_entry.align,
data_kind: Default::default(),
..Default::default()
},
true,
)?;

View File

@ -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;

249
src/util/reader.rs Normal file
View File

@ -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<const N: usize>(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<const N: usize, R>(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<R>(reader: &mut R, e: Endian, args: Self::Args) -> io::Result<Self>
where R: Read + Seek + ?Sized;
fn from_reader<R>(reader: &mut R, e: Endian) -> io::Result<Self>
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::<Self>();
type Args = ();
#[inline]
fn from_reader_args<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<const N: usize> FromReader for [u8; N] {
type Args = ();
const STATIC_SIZE: usize = N;
#[inline]
fn from_reader_args<R>(reader: &mut R, _e: Endian, _args: Self::Args) -> io::Result<Self>
where R: Read + Seek + ?Sized {
let mut buf = [0u8; N];
reader.read_exact(&mut buf)?;
Ok(buf)
}
}
impl<const N: usize> FromReader for [u32; N] {
type Args = ();
const STATIC_SIZE: usize = N * u32::STATIC_SIZE;
#[inline]
fn from_reader_args<R>(reader: &mut R, e: Endian, _args: Self::Args) -> io::Result<Self>
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<R>(reader: &mut R, count: usize) -> io::Result<Vec<u8>>
where R: Read + Seek + ?Sized {
let mut buf = vec![0u8; count];
reader.read_exact(&mut buf)?;
Ok(buf)
}
#[inline]
pub fn read_vec<T, R>(reader: &mut R, count: usize, e: Endian) -> io::Result<Vec<T>>
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<T, R>(
reader: &mut R,
count: usize,
e: Endian,
args: T::Args,
) -> io::Result<Vec<T>>
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<T, R>(reader: &mut R, e: Endian) -> io::Result<String>
where
T: FromReader + TryInto<usize>,
T::Args: Default,
R: Read + Seek + ?Sized,
{
let len = <T>::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<W>(&self, writer: &mut W, e: Endian) -> io::Result<()>
where W: Write + ?Sized;
fn to_bytes(&self, e: Endian) -> io::Result<Vec<u8>> {
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<W>(&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<Vec<u8>> {
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::<Self>()
}
}
)*
};
}
impl_to_writer!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128);
impl<const N: usize> ToWriter for [u8; N] {
fn to_writer<W>(&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<W>(&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<u8> {
fn to_writer<W>(&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<T, W>(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(())
}

View File

@ -232,15 +232,11 @@ pub fn process_rel<R: Read + Seek>(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(())

View File

@ -128,15 +128,11 @@ pub fn process_rso<R: Read + Seek>(reader: &mut R) -> Result<ObjInfo> {
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<R: Read + Seek>(reader: &mut R) -> Result<ObjInfo> {
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))?;

View File

@ -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,
)?;

View File

@ -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<Vec<ObjInfo>> {
kind: symbol.kind,
align: symbol.align,
data_kind: symbol.data_kind,
name_hash: symbol.name_hash,
demangled_name_hash: symbol.demangled_name_hash,
})?);
}