Add selfile option & load in `dol split`

This commit is contained in:
Luke Street 2023-08-15 22:45:23 -04:00
parent 908e3bb037
commit 23a156a6d5
2 changed files with 179 additions and 12 deletions

View File

@ -8,6 +8,7 @@ use std::{
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use argp::FromArgs; use argp::FromArgs;
use itertools::Itertools;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -35,6 +36,7 @@ use crate::{
lcf::{asm_path_for_unit, generate_ldscript, obj_path_for_unit}, lcf::{asm_path_for_unit, generate_ldscript, obj_path_for_unit},
map::apply_map_file, map::apply_map_file,
rel::process_rel, rel::process_rel,
rso::{process_rso, DOL_SECTION_ABS, DOL_SECTION_NAMES},
}, },
}; };
@ -62,6 +64,9 @@ pub struct InfoArgs {
#[argp(positional)] #[argp(positional)]
/// DOL file /// DOL file
dol_file: PathBuf, dol_file: PathBuf,
#[argp(option, short = 's')]
/// optional path to selfile.sel
selfile: Option<PathBuf>,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
@ -118,6 +123,8 @@ pub struct ProjectConfig {
pub hash: Option<String>, pub hash: Option<String>,
pub splits: Option<PathBuf>, pub splits: Option<PathBuf>,
pub symbols: Option<PathBuf>, pub symbols: Option<PathBuf>,
pub selfile: Option<PathBuf>,
pub selfile_hash: Option<String>,
/// Version of the MW `.comment` section format. /// Version of the MW `.comment` section format.
/// If not present, no `.comment` sections will be written. /// If not present, no `.comment` sections will be written.
pub mw_comment_version: Option<u8>, pub mw_comment_version: Option<u8>,
@ -166,6 +173,86 @@ pub fn run(args: Args) -> Result<()> {
} }
} }
fn apply_selfile(obj: &mut ObjInfo, selfile: &Path) -> Result<()> {
let rso = process_rso(selfile)?;
for symbol in rso.symbols.iter() {
let dol_section_index = match symbol.section {
Some(section) => section,
None => bail!(
"Expected section for symbol '{}' @ {:#010X} in selfile",
symbol.name,
symbol.address
),
};
let (section, address, section_kind) = if dol_section_index == DOL_SECTION_ABS as usize {
(None, symbol.address as u32, None)
} else {
let dol_section_name =
DOL_SECTION_NAMES.get(dol_section_index).and_then(|&opt| opt).ok_or_else(|| {
anyhow!("Can't add symbol for unknown DOL section {}", dol_section_index)
})?;
let dol_section = obj
.sections
.iter()
.find(|section| section.name == dol_section_name)
.ok_or_else(|| anyhow!("Failed to locate DOL section {}", dol_section_name))?;
(
Some(dol_section.index),
dol_section.address as u32 + symbol.address as u32,
Some(dol_section.kind),
)
};
let symbol_kind = match section_kind {
Some(ObjSectionKind::Code) => ObjSymbolKind::Function,
Some(_) => ObjSymbolKind::Object,
None => ObjSymbolKind::Unknown,
};
let existing_symbols = obj.symbols.at_address(address).collect_vec();
let existing_symbol = existing_symbols
.iter()
.find(|(_, s)| s.section == section && s.name == symbol.name)
.cloned()
.or_else(|| {
existing_symbols
.iter()
.find(|(_, s)| s.section == section && s.kind == symbol_kind)
.cloned()
});
if let Some((existing_symbol_idx, existing_symbol)) = existing_symbol {
log::debug!("Mapping symbol {} to {}", symbol.name, existing_symbol.name);
obj.symbols.replace(existing_symbol_idx, ObjSymbol {
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: address as u64,
section,
size: existing_symbol.size,
size_known: existing_symbol.size_known,
flags: ObjSymbolFlagSet(existing_symbol.flags.0 | ObjSymbolFlags::ForceActive),
kind: existing_symbol.kind,
align: existing_symbol.align,
data_kind: existing_symbol.data_kind,
})?;
} else {
log::debug!("Creating symbol {} at {:#010X}", symbol.name, address);
obj.symbols.add(
ObjSymbol {
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
address: address as u64,
section,
flags: ObjSymbolFlagSet(
(ObjSymbolFlags::Global | ObjSymbolFlags::ForceActive).into(),
),
..*symbol
},
false,
)?;
}
}
Ok(())
}
fn info(args: InfoArgs) -> Result<()> { fn info(args: InfoArgs) -> Result<()> {
let mut obj = process_dol(&args.dol_file)?; let mut obj = process_dol(&args.dol_file)?;
apply_signatures(&mut obj)?; apply_signatures(&mut obj)?;
@ -180,6 +267,10 @@ fn info(args: InfoArgs) -> Result<()> {
apply_signatures_post(&mut obj)?; apply_signatures_post(&mut obj)?;
if let Some(selfile) = args.selfile {
apply_selfile(&mut obj, &selfile)?;
}
println!("{}:", obj.name); println!("{}:", obj.name);
println!("Entry point: {:#010X}", obj.entry); println!("Entry point: {:#010X}", obj.entry);
println!("\nSections:"); println!("\nSections:");
@ -342,6 +433,13 @@ fn split(args: SplitArgs) -> Result<()> {
apply_signatures_post(&mut obj)?; apply_signatures_post(&mut obj)?;
if let Some(selfile) = &config.selfile {
if let Some(hash) = &config.selfile_hash {
verify_hash(selfile, hash)?;
}
apply_selfile(&mut obj, &selfile)?;
}
log::info!("Performing relocation analysis"); log::info!("Performing relocation analysis");
let mut tracker = Tracker::new(&obj); let mut tracker = Tracker::new(&obj);
tracker.process(&obj)?; tracker.process(&obj)?;
@ -429,7 +527,8 @@ fn split(args: SplitArgs) -> Result<()> {
DirBuilder::new().recursive(true).create(parent)?; DirBuilder::new().recursive(true).create(parent)?;
} }
let mut w = buf_writer(&out_path)?; let mut w = buf_writer(&out_path)?;
write_asm(&mut w, split_obj)?; write_asm(&mut w, split_obj)
.with_context(|| format!("Failed to write {}", out_path.display()))?;
w.flush()?; w.flush()?;
} }
@ -692,10 +791,50 @@ fn diff(args: DiffArgs) -> Result<()> {
linked_sym.address, linked_sym.address,
); );
} }
break; return Ok(());
} }
} }
// Data diff
for orig_sym in obj.symbols.iter() {
if orig_sym.kind == ObjSymbolKind::Section || orig_sym.section.is_none() {
continue;
}
let (_, linked_sym) = linked_obj
.symbols
.at_address(orig_sym.address as u32)
.find(|(_, sym)| sym.name == orig_sym.name)
.or_else(|| {
linked_obj
.symbols
.at_address(orig_sym.address as u32)
.find(|(_, sym)| sym.kind == orig_sym.kind)
})
.unwrap();
let orig_data = obj
.section_data(orig_sym.address as u32, orig_sym.address as u32 + orig_sym.size as u32)?
.1;
let linked_data = linked_obj
.section_data(
linked_sym.address as u32,
linked_sym.address as u32 + linked_sym.size as u32,
)?
.1;
if orig_data != linked_data {
log::error!(
"Data mismatch for {} (type {:?}, size {:#X}) at {:#010X}",
orig_sym.name,
orig_sym.kind,
orig_sym.size,
orig_sym.address
);
return Ok(());
}
}
log::info!("OK");
Ok(()) Ok(())
} }

View File

@ -12,6 +12,26 @@ use crate::{
util::file::{map_file, map_reader, read_c_string, read_string}, util::file::{map_file, map_reader, read_c_string, read_string},
}; };
/// For RSO references to the DOL, the sections are hardcoded.
pub const DOL_SECTION_NAMES: [Option<&str>; 14] = [
None, // s_null
Some(".init"),
Some(".text"),
Some(".ctors"),
Some(".dtors"),
Some(".rodata"),
Some(".data"),
Some(".bss"),
Some(".sdata"),
Some(".sdata2"),
None, // s_zero
Some(".sbss"),
Some(".sbss2"),
None, // s_zero2
];
/// ABS symbol section index.
pub const DOL_SECTION_ABS: u32 = 65521;
pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> { pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let mmap = map_file(path)?; let mmap = map_file(path)?;
let mut reader = map_reader(&mmap); let mut reader = map_reader(&mmap);
@ -49,7 +69,7 @@ pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
for idx in 0..num_sections { for idx in 0..num_sections {
let offset = reader.read_u32::<BigEndian>()?; let offset = reader.read_u32::<BigEndian>()?;
let size = reader.read_u32::<BigEndian>()?; let size = reader.read_u32::<BigEndian>()?;
log::info!("Section {}: {:#X} {:#X}", idx, offset, size); log::debug!("Section {}: offset {:#X}, size {:#X}", idx, offset, size);
if size == 0 { if size == 0 {
continue; continue;
} }
@ -108,7 +128,7 @@ pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
.iter() .iter()
.find(|section| section.elf_index == section_idx as usize) .find(|section| section.elf_index == section_idx as usize)
.ok_or_else(|| anyhow!("Failed to locate {name} section {section_idx}"))?; .ok_or_else(|| anyhow!("Failed to locate {name} section {section_idx}"))?;
log::info!("Adding {name} section {section_idx} offset {offset:#X}"); log::debug!("Adding {name} section {section_idx} offset {offset:#X}");
symbols.push(ObjSymbol { symbols.push(ObjSymbol {
name: name.to_string(), name: name.to_string(),
demangled_name: None, demangled_name: None,
@ -135,7 +155,7 @@ pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let id = (id_and_type & 0xFFFFFF00) >> 8; let id = (id_and_type & 0xFFFFFF00) >> 8;
let rel_type = id_and_type & 0xFF; let rel_type = id_and_type & 0xFF;
let sym_offset = reader.read_u32::<BigEndian>()?; let sym_offset = reader.read_u32::<BigEndian>()?;
log::info!( log::debug!(
"Reloc offset: {:#X}, id: {}, type: {}, sym offset: {:#X}", "Reloc offset: {:#X}, id: {}, type: {}, sym offset: {:#X}",
offset, offset,
id, id,
@ -152,24 +172,32 @@ pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let section_idx = reader.read_u32::<BigEndian>()?; let section_idx = reader.read_u32::<BigEndian>()?;
let hash_n = reader.read_u32::<BigEndian>()?; let hash_n = reader.read_u32::<BigEndian>()?;
let calc = symbol_hash(&name); let calc = symbol_hash(&name);
ensure!(
hash_n == calc,
"Mismatched calculated hash for symbol {}: {:#X} != {:#X}",
name,
hash_n,
calc
);
let demangled_name = demangle(&name, &DemangleOptions::default()); let demangled_name = demangle(&name, &DemangleOptions::default());
let section = sections let section = sections
.iter() .iter()
.find(|section| section.elf_index == section_idx as usize) .find(|section| section.elf_index == section_idx as usize)
.map(|section| section.index); .map(|section| section.index)
log::info!( // HACK: selfiles won't have any sections
"Export: {}, sym off: {:#X}, section: {}, ELF hash: {:#X}, {:#X}", .unwrap_or(section_idx as usize);
log::debug!(
"Export: {}, sym off: {:#X}, section: {}, ELF hash: {:#X}",
demangled_name.as_deref().unwrap_or(&name), demangled_name.as_deref().unwrap_or(&name),
sym_off, sym_off,
section_idx, section_idx,
hash_n, hash_n
calc
); );
symbols.push(ObjSymbol { symbols.push(ObjSymbol {
name, name,
demangled_name, demangled_name,
address: sym_off as u64, address: sym_off as u64,
section, section: Some(section),
size: 0, size: 0,
size_known: false, size_known: false,
flags: Default::default(), flags: Default::default(),
@ -184,7 +212,7 @@ pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let name = read_c_string(&mut reader, (import_table_name_offset + name_off) as u64)?; let name = read_c_string(&mut reader, (import_table_name_offset + name_off) as u64)?;
let sym_off = reader.read_u32::<BigEndian>()?; let sym_off = reader.read_u32::<BigEndian>()?;
let section_idx = reader.read_u32::<BigEndian>()?; let section_idx = reader.read_u32::<BigEndian>()?;
log::info!("Import: {}, sym off: {}, section: {}", name, sym_off, section_idx); log::debug!("Import: {}, sym off: {}, section: {}", name, sym_off, section_idx);
} }
let name = match name_offset { let name = match name_offset {