From eb7c3e9d9f9fa748302c4b8fc90211dd81b4815f Mon Sep 17 00:00:00 2001 From: Luke Street Date: Mon, 10 Jun 2024 00:43:00 -0600 Subject: [PATCH] Detect _savevr/_restvr + check in RELs Some games include a copy of MW runtime.c in RELs. Easy to check. Some games also include _savevr/ _restvr for AltiVec. This is weird but we can handle it as well. --- src/analysis/cfa.rs | 37 ++++++++++++++++++++++++++++++++++--- src/analysis/pass.rs | 41 +++++++++++++++++++++++------------------ src/cmd/dol.rs | 1 + src/util/split.rs | 1 + 4 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/analysis/cfa.rs b/src/analysis/cfa.rs index 09d3982..04fbd04 100644 --- a/src/analysis/cfa.rs +++ b/src/analysis/cfa.rs @@ -115,7 +115,7 @@ pub struct AnalyzerState { pub sda_bases: Option<(u32, u32)>, pub functions: BTreeMap, pub jump_tables: BTreeMap, - pub known_symbols: BTreeMap, + pub known_symbols: BTreeMap>, pub known_sections: BTreeMap, } @@ -196,8 +196,39 @@ impl AnalyzerState { false, )?; } - for (&_addr, symbol) in &self.known_symbols { - obj.add_symbol(symbol.clone(), true)?; + for (&_addr, symbols) in &self.known_symbols { + for symbol in symbols { + // Remove overlapping symbols + if symbol.size > 0 { + let end = symbol.address + symbol.size; + let overlapping = obj + .symbols + .for_section_range( + symbol.section.unwrap(), + symbol.address as u32 + 1..end as u32, + ) + .filter(|(_, s)| s.kind == symbol.kind) + .map(|(a, _)| a) + .collect_vec(); + for index in overlapping { + let existing = &obj.symbols[index]; + let symbol = ObjSymbol { + name: format!("__DELETED_{}", existing.name), + kind: ObjSymbolKind::Unknown, + size: 0, + flags: ObjSymbolFlagSet( + ObjSymbolFlags::RelocationIgnore + | ObjSymbolFlags::NoWrite + | ObjSymbolFlags::NoExport + | ObjSymbolFlags::Stripped, + ), + ..existing.clone() + }; + obj.symbols.replace(index, symbol)?; + } + } + obj.add_symbol(symbol.clone(), true)?; + } } Ok(()) } diff --git a/src/analysis/pass.rs b/src/analysis/pass.rs index 6a527b6..038355a 100644 --- a/src/analysis/pass.rs +++ b/src/analysis/pass.rs @@ -35,7 +35,7 @@ impl AnalysisPass for FindTRKInterruptVectorTable { && data[TRK_TABLE_HEADER.as_bytes().len()] == 0 { log::debug!("Found gTRKInterruptVectorTable @ {:#010X}", start); - state.known_symbols.insert(start, ObjSymbol { + state.known_symbols.entry(start).or_default().push(ObjSymbol { name: "gTRKInterruptVectorTable".to_string(), address: start.address as u64, section: Some(start.section), @@ -44,7 +44,7 @@ impl AnalysisPass for FindTRKInterruptVectorTable { ..Default::default() }); let end = start + TRK_TABLE_SIZE; - state.known_symbols.insert(end, ObjSymbol { + state.known_symbols.entry(end).or_default().push(ObjSymbol { name: "gTRKInterruptVectorTableEnd".to_string(), address: end.address as u64, section: Some(start.section), @@ -63,42 +63,45 @@ impl AnalysisPass for FindTRKInterruptVectorTable { pub struct FindSaveRestSleds {} -const SLEDS: [([u8; 8], &str, &str); 4] = [ - ([0xd9, 0xcb, 0xff, 0x70, 0xd9, 0xeb, 0xff, 0x78], "__save_fpr", "_savefpr_"), - ([0xc9, 0xcb, 0xff, 0x70, 0xc9, 0xeb, 0xff, 0x78], "__restore_fpr", "_restfpr_"), - ([0x91, 0xcb, 0xff, 0xb8, 0x91, 0xeb, 0xff, 0xbc], "__save_gpr", "_savegpr_"), - ([0x81, 0xcb, 0xff, 0xb8, 0x81, 0xeb, 0xff, 0xbc], "__restore_gpr", "_restgpr_"), +#[allow(clippy::type_complexity)] +const SLEDS: [([u8; 8], &str, &str, u32, u32, u32); 6] = [ + ([0xd9, 0xcb, 0xff, 0x70, 0xd9, 0xeb, 0xff, 0x78], "__save_fpr", "_savefpr_", 14, 32, 4), + ([0xc9, 0xcb, 0xff, 0x70, 0xc9, 0xeb, 0xff, 0x78], "__restore_fpr", "_restfpr_", 14, 32, 4), + ([0x91, 0xcb, 0xff, 0xb8, 0x91, 0xeb, 0xff, 0xbc], "__save_gpr", "_savegpr_", 14, 32, 4), + ([0x81, 0xcb, 0xff, 0xb8, 0x81, 0xeb, 0xff, 0xbc], "__restore_gpr", "_restgpr_", 14, 32, 4), + ([0x39, 0x80, 0xff, 0x40, 0x7e, 0x8c, 0x01, 0xce], "_savevr", "_savev", 20, 32, 8), + ([0x39, 0x80, 0xff, 0x40, 0x7e, 0x8c, 0x00, 0xce], "_restorevr", "_restv", 20, 32, 8), ]; // Runtime.PPCEABI.H.a runtime.c impl AnalysisPass for FindSaveRestSleds { fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> { - const SLED_SIZE: usize = 19 * 4; // registers 14-31 + blr for (section_index, section) in obj.sections.by_kind(ObjSectionKind::Code) { - for (needle, func, label) in &SLEDS { - let Some(pos) = memmem::find(§ion.data, needle) else { + for (needle, func, label, reg_start, reg_end, step_size) in SLEDS { + let Some(pos) = memmem::find(§ion.data, &needle) else { continue; }; let start = SectionAddress::new(section_index, section.address as u32 + pos as u32); log::debug!("Found {} @ {:#010X}", func, start); + let sled_size = (reg_end - reg_start) * step_size + 4 /* blr */; state.functions.insert(start, FunctionInfo { analyzed: false, - end: Some(start + SLED_SIZE as u32), + end: Some(start + sled_size), slices: None, }); - state.known_symbols.insert(start, ObjSymbol { + state.known_symbols.entry(start).or_default().push(ObjSymbol { name: func.to_string(), address: start.address as u64, section: Some(start.section), - size: SLED_SIZE as u64, + size: sled_size as u64, size_known: true, flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()), kind: ObjSymbolKind::Function, ..Default::default() }); - for i in 14..=31 { - let addr = start + (i - 14) * 4; - state.known_symbols.insert(addr, ObjSymbol { + for i in reg_start..reg_end { + let addr = start + (i - reg_start) * step_size; + state.known_symbols.entry(addr).or_default().push(ObjSymbol { name: format!("{}{}", label, i), address: addr.address as u64, section: Some(start.section), @@ -185,8 +188,9 @@ impl AnalysisPass for FindRelCtorsDtors { possible_sections[1].0 ); let ctors_section_index = possible_sections[0].0; + let ctors_address = SectionAddress::new(ctors_section_index, 0); state.known_sections.insert(ctors_section_index, ".ctors".to_string()); - state.known_symbols.insert(SectionAddress::new(ctors_section_index, 0), ObjSymbol { + state.known_symbols.entry(ctors_address).or_default().push(ObjSymbol { name: "_ctors".to_string(), section: Some(ctors_section_index), size_known: true, @@ -195,8 +199,9 @@ impl AnalysisPass for FindRelCtorsDtors { }); let dtors_section_index = possible_sections[1].0; + let dtors_address = SectionAddress::new(dtors_section_index, 0); state.known_sections.insert(dtors_section_index, ".dtors".to_string()); - state.known_symbols.insert(SectionAddress::new(dtors_section_index, 0), ObjSymbol { + state.known_symbols.entry(dtors_address).or_default().push(ObjSymbol { name: "_dtors".to_string(), section: Some(dtors_section_index), size_known: true, diff --git a/src/cmd/dol.rs b/src/cmd/dol.rs index 42f8540..e1fcd60 100644 --- a/src/cmd/dol.rs +++ b/src/cmd/dol.rs @@ -1056,6 +1056,7 @@ fn load_analyze_rel(config: &ProjectConfig, module_config: &ModuleConfig) -> Res debug!("Analyzing module {}", module_obj.module_id); if !config.quick_analysis { let mut state = AnalyzerState::default(); + FindSaveRestSleds::execute(&mut state, &module_obj)?; state.detect_functions(&module_obj)?; FindRelCtorsDtors::execute(&mut state, &module_obj)?; FindRelRodataData::execute(&mut state, &module_obj)?; diff --git a/src/util/split.rs b/src/util/split.rs index 18dff1b..e4711d0 100644 --- a/src/util/split.rs +++ b/src/util/split.rs @@ -365,6 +365,7 @@ fn create_gap_splits(obj: &mut ObjInfo) -> Result<()> { let symbols = obj .symbols .for_section_range(section_index, current_address.address..split_start.address) + .filter(|&(_, s)| !s.flags.is_stripped()) .collect_vec(); let mut existing_symbols = HashSet::new(); for &(_, symbol) in &symbols {