6 months of occasional work I guess

This commit is contained in:
2023-07-21 17:59:07 -04:00
parent f1b4afa885
commit 0fa0aafaea
44 changed files with 4688 additions and 1817 deletions

View File

@@ -1,6 +1,6 @@
use std::collections::{BTreeMap, BTreeSet};
use anyhow::{anyhow, bail, Result};
use anyhow::{bail, Context, Result};
use crate::{
analysis::{
@@ -9,7 +9,7 @@ use crate::{
slices::{FunctionSlices, TailCallResult},
vm::{BranchTarget, GprValue, StepResult, VM},
},
obj::{ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind},
obj::{ObjInfo, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind},
};
#[derive(Debug, Default)]
@@ -29,102 +29,66 @@ impl AnalyzerState {
if end == 0 {
continue;
}
if let Some(existing_symbol) = obj
.symbols
.iter_mut()
.find(|sym| sym.address == start as u64 && sym.kind == ObjSymbolKind::Function)
{
let new_size = (end - start) as u64;
if !existing_symbol.size_known || existing_symbol.size == 0 {
existing_symbol.size = new_size;
existing_symbol.size_known = true;
} else if existing_symbol.size != new_size {
log::warn!(
"Conflicting size for {}: was {:#X}, now {:#X}",
existing_symbol.name,
existing_symbol.size,
new_size
);
}
continue;
}
let section = obj
.sections
.iter()
.find(|section| {
(start as u64) >= section.address
&& (end as u64) <= section.address + section.size
})
.ok_or_else(|| {
anyhow!("Failed to locate section for function {:#010X}-{:#010X}", start, end)
})?;
obj.symbols.push(ObjSymbol {
name: format!("fn_{:08X}", start),
demangled_name: None,
address: start as u64,
section: Some(section.index),
size: (end - start) as u64,
size_known: true,
flags: Default::default(),
kind: ObjSymbolKind::Function,
});
let section_index =
obj.section_for(start..end).context("Failed to locate section for function")?.index;
obj.add_symbol(
ObjSymbol {
name: format!("fn_{:08X}", start),
demangled_name: None,
address: start as u64,
section: Some(section_index),
size: (end - start) as u64,
size_known: true,
flags: Default::default(),
kind: ObjSymbolKind::Function,
align: None,
data_kind: Default::default(),
},
false,
)?;
}
for (&addr, &size) in &self.jump_tables {
let section = obj
.sections
.iter()
.find(|section| {
(addr as u64) >= section.address
&& ((addr + size) as u64) <= section.address + section.size
})
.ok_or_else(|| anyhow!("Failed to locate section for jump table"))?;
if let Some(existing_symbol) = obj
.symbols
.iter_mut()
.find(|sym| sym.address == addr as u64 && sym.kind == ObjSymbolKind::Object)
{
let new_size = size as u64;
if !existing_symbol.size_known || existing_symbol.size == 0 {
existing_symbol.size = new_size;
existing_symbol.size_known = true;
// existing_symbol.flags.0 &= ObjSymbolFlags::Global;
// existing_symbol.flags.0 |= ObjSymbolFlags::Local;
} else if existing_symbol.size != new_size {
log::warn!(
"Conflicting size for {}: was {:#X}, now {:#X}",
existing_symbol.name,
existing_symbol.size,
new_size
);
}
continue;
}
obj.symbols.push(ObjSymbol {
name: format!("jumptable_{:08X}", addr),
demangled_name: None,
address: addr as u64,
section: Some(section.index),
size: size as u64,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local.into()),
kind: ObjSymbolKind::Object,
});
let section_index = obj
.section_for(addr..addr + size)
.context("Failed to locate section for jump table")?
.index;
obj.add_symbol(
ObjSymbol {
name: format!("jumptable_{:08X}", addr),
demangled_name: None,
address: addr as u64,
section: Some(section_index),
size: size as u64,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local.into()),
kind: ObjSymbolKind::Object,
align: None,
data_kind: Default::default(),
},
false,
)?;
}
for (&_addr, symbol) in &self.known_symbols {
if let Some(existing_symbol) = obj
.symbols
.iter_mut()
.find(|e| symbol.address == e.address && symbol.kind == e.kind)
{
*existing_symbol = symbol.clone();
continue;
}
obj.symbols.push(symbol.clone());
obj.add_symbol(symbol.clone(), true)?;
}
Ok(())
}
pub fn detect_functions(&mut self, obj: &ObjInfo) -> Result<()> {
// Apply known functions from extab
for (&addr, &size) in &obj.known_functions {
self.function_entries.insert(addr);
self.function_bounds.insert(addr, addr + size);
}
// Apply known functions from symbols
for (_, symbol) in obj.symbols.by_kind(ObjSymbolKind::Function) {
self.function_entries.insert(symbol.address as u32);
if symbol.size_known {
self.function_bounds
.insert(symbol.address as u32, (symbol.address + symbol.size) as u32);
}
}
// Process known functions first
let known_functions = self.function_entries.clone();
for addr in known_functions {
@@ -189,6 +153,7 @@ impl AnalyzerState {
)?;
}
}
TailCallResult::Error(e) => return Err(e),
}
}
if slices.can_finalize() {
@@ -249,13 +214,11 @@ impl AnalyzerState {
match self.first_unbounded_function() {
Some(addr) => {
log::trace!("Processing {:#010X}", addr);
self.process_function_at(&obj, addr)?;
self.process_function_at(obj, addr)?;
}
None => {
if !self.finalize_functions(obj, false)? {
if !self.detect_new_functions(obj)? {
break;
}
if !self.finalize_functions(obj, false)? && !self.detect_new_functions(obj)? {
break;
}
}
}
@@ -291,9 +254,6 @@ impl AnalyzerState {
fn process_function(&mut self, obj: &ObjInfo, start: u32) -> Result<Option<FunctionSlices>> {
let mut slices = FunctionSlices::default();
let function_end = self.function_bounds.get(&start).cloned();
if start == 0x801FC300 {
log::info!("Processing TRKExceptionHandler");
}
Ok(match slices.analyze(obj, start, start, function_end, &self.function_entries)? {
true => Some(slices),
false => None,
@@ -302,27 +262,56 @@ impl AnalyzerState {
fn detect_new_functions(&mut self, obj: &ObjInfo) -> Result<bool> {
let mut found_new = false;
let mut iter = self.function_bounds.iter().peekable();
while let (Some((&first_begin, &first_end)), Some(&(&second_begin, &second_end))) =
(iter.next(), iter.peek())
{
if first_end == 0 || first_end > second_begin {
for section in &obj.sections {
if section.kind != ObjSectionKind::Code {
continue;
}
let addr = match skip_alignment(obj, first_end, second_begin) {
Some(addr) => addr,
None => continue,
};
if second_begin > addr && self.function_entries.insert(addr) {
log::trace!(
"Trying function @ {:#010X} (from {:#010X}-{:#010X} <-> {:#010X}-{:#010X})",
addr,
first_begin,
first_end,
second_begin,
second_end,
);
found_new = true;
let section_start = section.address as u32;
let section_end = (section.address + section.size) as u32;
let mut iter = self.function_bounds.range(section_start..section_end).peekable();
loop {
match (iter.next(), iter.peek()) {
(Some((&first_begin, &first_end)), Some(&(&second_begin, &second_end))) => {
if first_end == 0 || first_end > second_begin {
continue;
}
let addr = match skip_alignment(obj, first_end, second_begin) {
Some(addr) => addr,
None => continue,
};
if second_begin > addr && self.function_entries.insert(addr) {
log::trace!(
"Trying function @ {:#010X} (from {:#010X}-{:#010X} <-> {:#010X}-{:#010X})",
addr,
first_begin,
first_end,
second_begin,
second_end,
);
found_new = true;
}
}
(Some((&last_begin, &last_end)), None) => {
if last_end > 0 && last_end < section_end {
let addr = match skip_alignment(obj, last_end, section_end) {
Some(addr) => addr,
None => continue,
};
if addr < section_end && self.function_entries.insert(addr) {
log::debug!(
"Trying function @ {:#010X} (from {:#010X}-{:#010X} <-> {:#010X})",
addr,
last_begin,
last_end,
section_end,
);
found_new = true;
}
}
}
_ => break,
}
}
}
Ok(found_new)
@@ -342,19 +331,15 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
return Ok(ExecCbResult::Continue);
}
StepResult::Illegal => bail!("Illegal instruction @ {:#010X}", ins.addr),
StepResult::Jump(target) => match target {
BranchTarget::Address(addr) => {
StepResult::Jump(target) => {
if let BranchTarget::Address(addr) = target {
return Ok(ExecCbResult::Jump(addr));
}
_ => {}
},
}
StepResult::Branch(branches) => {
for branch in branches {
match branch.target {
BranchTarget::Address(addr) => {
executor.push(addr, branch.vm, false);
}
_ => {}
if let BranchTarget::Address(addr) = branch.target {
executor.push(addr, branch.vm, false);
}
}
}

View File

@@ -7,7 +7,9 @@ use crate::obj::{ObjInfo, ObjSection, ObjSectionKind};
pub mod cfa;
pub mod executor;
pub mod objects;
pub mod pass;
pub mod signatures;
pub mod slices;
pub mod tracker;
pub mod vm;

155
src/analysis/objects.rs Normal file
View File

@@ -0,0 +1,155 @@
use anyhow::Result;
use crate::obj::{
split::is_linker_generated_label, ObjDataKind, ObjInfo, ObjSectionKind, ObjSymbolKind,
};
pub fn detect_object_boundaries(obj: &mut ObjInfo) -> Result<()> {
for section in obj.sections.iter().filter(|s| s.kind != ObjSectionKind::Code) {
let section_start = section.address as u32;
let section_end = (section.address + section.size) as u32;
let mut replace_symbols = vec![];
for (idx, symbol) in obj.symbols.for_range(section_start..section_end) {
let mut symbol = symbol.clone();
if is_linker_generated_label(&symbol.name) {
continue;
}
let expected_size = match symbol.data_kind {
ObjDataKind::Byte => 1,
ObjDataKind::Byte2 => 2,
ObjDataKind::Byte4 | ObjDataKind::Float => 4,
ObjDataKind::Byte8 | ObjDataKind::Double => 8,
_ => 0,
};
if !symbol.size_known {
let next_addr = obj
.symbols
.for_range(symbol.address as u32 + 1..section_end)
.next()
.map_or(section_end, |(_, symbol)| symbol.address as u32);
let new_size = next_addr - symbol.address as u32;
log::debug!("Guessed {} size {:#X}", symbol.name, new_size);
symbol.size = match (new_size, expected_size) {
(..=4, 1) => expected_size,
(2 | 4, 2) => expected_size,
(..=8, 1 | 2 | 4) => {
// alignment to double
if obj.symbols.at_address(next_addr).any(|(_, sym)| sym.data_kind == ObjDataKind::Double)
// If we're at a TU boundary, we can assume it's just padding
|| obj.splits.contains_key(&(symbol.address as u32 + new_size))
{
expected_size
} else {
new_size
}
}
_ => new_size,
} as u64;
symbol.size_known = true;
}
symbol.kind = ObjSymbolKind::Object;
if expected_size > 1 && symbol.size as u32 % expected_size != 0 {
symbol.data_kind = ObjDataKind::Unknown;
}
replace_symbols.push((idx, symbol));
}
for (idx, symbol) in replace_symbols {
obj.symbols.replace(idx, symbol)?;
}
}
Ok(())
}
pub fn detect_strings(obj: &mut ObjInfo) -> Result<()> {
let mut symbols_set = Vec::<(usize, ObjDataKind, usize)>::new();
for section in obj
.sections
.iter()
.filter(|s| matches!(s.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData))
{
enum StringResult {
None,
String { length: usize, terminated: bool },
WString { length: usize, str: String },
}
pub const fn trim_zeroes_end(mut bytes: &[u8]) -> &[u8] {
while let [rest @ .., last] = bytes {
if *last == 0 {
bytes = rest;
} else {
break;
}
}
bytes
}
fn is_string(data: &[u8]) -> StringResult {
let bytes = trim_zeroes_end(data);
if bytes.iter().all(|&c| c.is_ascii_graphic() || c.is_ascii_whitespace()) {
return StringResult::String {
length: bytes.len(),
terminated: data.len() > bytes.len(),
};
}
if bytes.len() % 2 == 0 && data.len() >= bytes.len() + 2 {
// Found at least 2 bytes of trailing 0s, check UTF-16
let mut ok = true;
let mut str = String::new();
for n in std::char::decode_utf16(
bytes.chunks_exact(2).map(|c| u16::from_be_bytes(c.try_into().unwrap())),
) {
match n {
Ok(c) if c.is_ascii_graphic() || c.is_ascii_whitespace() => {
str.push(c);
}
_ => {
ok = false;
break;
}
}
}
if ok {
return StringResult::WString { length: bytes.len(), str };
}
}
StringResult::None
}
for (symbol_idx, symbol) in obj
.symbols
.for_section(section)
.filter(|(_, sym)| sym.data_kind == ObjDataKind::Unknown)
{
let (_section, data) =
obj.section_data(symbol.address as u32, (symbol.address + symbol.size) as u32)?;
match is_string(data) {
StringResult::None => {}
StringResult::String { length, terminated } => {
if length > 0 {
let str = String::from_utf8_lossy(&data[..length]);
log::debug!("Found string '{}' @ {}", str, symbol.name);
symbols_set.push((
symbol_idx,
ObjDataKind::String,
if terminated { length + 1 } else { length },
));
}
}
StringResult::WString { length, str } => {
if length > 0 {
log::debug!("Found wide string '{}' @ {}", str, symbol.name);
symbols_set.push((symbol_idx, ObjDataKind::String16, length + 2));
}
}
}
}
}
for (symbol_idx, data_kind, size) in symbols_set {
let mut symbol = obj.symbols.at(symbol_idx).clone();
log::debug!("Setting {} ({:#010X}) to size {:#X}", symbol.name, symbol.address, size);
symbol.data_kind = data_kind;
symbol.size = size as u64;
symbol.size_known = true;
obj.symbols.replace(symbol_idx, symbol)?;
}
Ok(())
}

View File

@@ -38,6 +38,8 @@ impl AnalysisPass for FindTRKInterruptVectorTable {
size_known: true,
flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)),
kind: ObjSymbolKind::Unknown,
align: None,
data_kind: Default::default(),
});
let end = start + TRK_TABLE_SIZE;
state.known_symbols.insert(end, ObjSymbol {
@@ -49,19 +51,21 @@ impl AnalysisPass for FindTRKInterruptVectorTable {
size_known: true,
flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)),
kind: ObjSymbolKind::Unknown,
align: None,
data_kind: Default::default(),
});
return Ok(());
}
}
log::info!("gTRKInterruptVectorTable not found");
log::debug!("gTRKInterruptVectorTable not found");
Ok(())
}
}
pub struct FindSaveRestSleds {}
const SLEDS: [([u8; 4], &'static str, &'static str); 4] = [
const SLEDS: [([u8; 4], &str, &str); 4] = [
([0xd9, 0xcb, 0xff, 0x70], "__save_fpr", "_savefpr_"),
([0xc9, 0xcb, 0xff, 0x70], "__restore_fpr", "_restfpr_"),
([0x91, 0xcb, 0xff, 0xb8], "__save_gpr", "_savegpr_"),
@@ -77,7 +81,7 @@ impl AnalysisPass for FindSaveRestSleds {
let (section, data) = obj.section_data(start, 0)?;
for (needle, func, label) in &SLEDS {
if data.starts_with(needle) {
log::info!("Found {} @ {:#010X}", func, start);
log::debug!("Found {} @ {:#010X}", func, start);
clear_ranges.push(start + 4..start + SLED_SIZE as u32);
state.known_symbols.insert(start, ObjSymbol {
name: func.to_string(),
@@ -88,6 +92,8 @@ impl AnalysisPass for FindSaveRestSleds {
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
kind: ObjSymbolKind::Function,
align: None,
data_kind: Default::default(),
});
for i in 14..=31 {
let addr = start + (i - 14) * 4;
@@ -100,6 +106,8 @@ impl AnalysisPass for FindSaveRestSleds {
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
kind: ObjSymbolKind::Unknown,
align: None,
data_kind: Default::default(),
});
}
}

365
src/analysis/signatures.rs Normal file
View File

@@ -0,0 +1,365 @@
use anyhow::{anyhow, Result};
use crate::{
analysis::{cfa::AnalyzerState, read_u32},
obj::{
signatures::{
apply_signature, check_signatures, check_signatures_str, parse_signatures,
FunctionSignature,
},
ObjInfo, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
},
};
const SIGNATURES: &[(&str, &str)] = &[
("__init_registers", include_str!("../../assets/signatures/__init_registers.yml")),
("__init_hardware", include_str!("../../assets/signatures/__init_hardware.yml")),
("__init_data", include_str!("../../assets/signatures/__init_data.yml")),
("__set_debug_bba", include_str!("../../assets/signatures/__set_debug_bba.yml")),
("__OSPSInit", include_str!("../../assets/signatures/__OSPSInit.yml")),
("__OSFPRInit", include_str!("../../assets/signatures/__OSFPRInit.yml")),
("__OSCacheInit", include_str!("../../assets/signatures/__OSCacheInit.yml")),
("DMAErrorHandler", include_str!("../../assets/signatures/DMAErrorHandler.yml")),
("DBInit", include_str!("../../assets/signatures/DBInit.yml")),
("OSInit", include_str!("../../assets/signatures/OSInit.yml")),
("__OSThreadInit", include_str!("../../assets/signatures/__OSThreadInit.yml")),
("__OSInitIPCBuffer", include_str!("../../assets/signatures/__OSInitIPCBuffer.yml")),
("EXIInit", include_str!("../../assets/signatures/EXIInit.yml")),
("EXIGetID", include_str!("../../assets/signatures/EXIGetID.yml")),
("exit", include_str!("../../assets/signatures/exit.yml")),
("_ExitProcess", include_str!("../../assets/signatures/_ExitProcess.yml")),
("__fini_cpp", include_str!("../../assets/signatures/__fini_cpp.yml")),
// ("__destroy_global_chain", include_str!("../../assets/signatures/__destroy_global_chain.yml")),
("InitMetroTRK", include_str!("../../assets/signatures/InitMetroTRK.yml")),
("InitMetroTRKCommTable", include_str!("../../assets/signatures/InitMetroTRKCommTable.yml")),
("OSExceptionInit", include_str!("../../assets/signatures/OSExceptionInit.yml")),
(
"OSDefaultExceptionHandler",
include_str!("../../assets/signatures/OSDefaultExceptionHandler.yml"),
),
("__OSUnhandledException", include_str!("../../assets/signatures/__OSUnhandledException.yml")),
("OSDisableScheduler", include_str!("../../assets/signatures/OSDisableScheduler.yml")),
("__OSReschedule", include_str!("../../assets/signatures/__OSReschedule.yml")),
("__OSInitSystemCall", include_str!("../../assets/signatures/__OSInitSystemCall.yml")),
("OSInitAlarm", include_str!("../../assets/signatures/OSInitAlarm.yml")),
("__OSInitAlarm", include_str!("../../assets/signatures/__OSInitAlarm.yml")),
("__OSEVStart", include_str!("../../assets/signatures/OSExceptionVector.yml")),
("__OSDBINTSTART", include_str!("../../assets/signatures/__OSDBIntegrator.yml")),
("__OSDBJUMPSTART", include_str!("../../assets/signatures/__OSDBJump.yml")),
("SIInit", include_str!("../../assets/signatures/SIInit.yml")),
("SIGetType", include_str!("../../assets/signatures/SIGetType.yml")),
("SISetSamplingRate", include_str!("../../assets/signatures/SISetSamplingRate.yml")),
("SISetXY", include_str!("../../assets/signatures/SISetXY.yml")),
("VIGetTvFormat", include_str!("../../assets/signatures/VIGetTvFormat.yml")),
("DVDInit", include_str!("../../assets/signatures/DVDInit.yml")),
(
"DVDSetAutoFatalMessaging",
include_str!("../../assets/signatures/DVDSetAutoFatalMessaging.yml"),
),
("OSSetArenaLo", include_str!("../../assets/signatures/OSSetArenaLo.yml")),
("OSSetArenaHi", include_str!("../../assets/signatures/OSSetArenaHi.yml")),
("OSSetMEM1ArenaLo", include_str!("../../assets/signatures/OSSetMEM1ArenaLo.yml")),
("OSSetMEM1ArenaHi", include_str!("../../assets/signatures/OSSetMEM1ArenaHi.yml")),
("OSSetMEM2ArenaLo", include_str!("../../assets/signatures/OSSetMEM2ArenaLo.yml")),
("OSSetMEM2ArenaHi", include_str!("../../assets/signatures/OSSetMEM2ArenaHi.yml")),
("__OSInitAudioSystem", include_str!("../../assets/signatures/__OSInitAudioSystem.yml")),
(
"__OSInitMemoryProtection",
include_str!("../../assets/signatures/__OSInitMemoryProtection.yml"),
),
// ("BATConfig", include_str!("../../assets/signatures/BATConfig.yml")), TODO
("ReportOSInfo", include_str!("../../assets/signatures/ReportOSInfo.yml")),
("__check_pad3", include_str!("../../assets/signatures/__check_pad3.yml")),
("OSResetSystem", include_str!("../../assets/signatures/OSResetSystem.yml")),
("OSReturnToMenu", include_str!("../../assets/signatures/OSReturnToMenu.yml")),
("__OSReturnToMenu", include_str!("../../assets/signatures/__OSReturnToMenu.yml")),
("__OSShutdownDevices", include_str!("../../assets/signatures/__OSShutdownDevices.yml")),
("__OSInitSram", include_str!("../../assets/signatures/__OSInitSram.yml")),
("__OSSyncSram", include_str!("../../assets/signatures/__OSSyncSram.yml")),
(
"__OSGetExceptionHandler",
include_str!("../../assets/signatures/__OSGetExceptionHandler.yml"),
),
(
"OSRegisterResetFunction",
include_str!("../../assets/signatures/OSRegisterResetFunction.yml"),
),
(
"OSRegisterShutdownFunction",
include_str!("../../assets/signatures/OSRegisterShutdownFunction.yml"),
),
(
"DecrementerExceptionHandler",
include_str!("../../assets/signatures/DecrementerExceptionHandler.yml"),
),
(
"DecrementerExceptionCallback",
include_str!("../../assets/signatures/DecrementerExceptionCallback.yml"),
),
("__OSInterruptInit", include_str!("../../assets/signatures/__OSInterruptInit.yml")),
("__OSContextInit", include_str!("../../assets/signatures/__OSContextInit.yml")),
("OSSwitchFPUContext", include_str!("../../assets/signatures/OSSwitchFPUContext.yml")),
("OSReport", include_str!("../../assets/signatures/OSReport.yml")),
("TRK_main", include_str!("../../assets/signatures/TRK_main.yml")),
("TRKNubWelcome", include_str!("../../assets/signatures/TRKNubWelcome.yml")),
("TRKInitializeNub", include_str!("../../assets/signatures/TRKInitializeNub.yml")),
(
"TRKInitializeIntDrivenUART",
include_str!("../../assets/signatures/TRKInitializeIntDrivenUART.yml"),
),
("TRKEXICallBack", include_str!("../../assets/signatures/TRKEXICallBack.yml")),
("TRKLoadContext", include_str!("../../assets/signatures/TRKLoadContext.yml")),
("TRKInterruptHandler", include_str!("../../assets/signatures/TRKInterruptHandler.yml")),
("TRKExceptionHandler", include_str!("../../assets/signatures/TRKExceptionHandler.yml")),
("TRKSaveExtended1Block", include_str!("../../assets/signatures/TRKSaveExtended1Block.yml")),
("TRKNubMainLoop", include_str!("../../assets/signatures/TRKNubMainLoop.yml")),
("TRKTargetContinue", include_str!("../../assets/signatures/TRKTargetContinue.yml")),
("TRKSwapAndGo", include_str!("../../assets/signatures/TRKSwapAndGo.yml")),
(
"TRKRestoreExtended1Block",
include_str!("../../assets/signatures/TRKRestoreExtended1Block.yml"),
),
(
"TRKInterruptHandlerEnableInterrupts",
include_str!("../../assets/signatures/TRKInterruptHandlerEnableInterrupts.yml"),
),
("memset", include_str!("../../assets/signatures/memset.yml")),
(
"__msl_runtime_constraint_violation_s",
include_str!("../../assets/signatures/__msl_runtime_constraint_violation_s.yml"),
),
("ClearArena", include_str!("../../assets/signatures/ClearArena.yml")),
("IPCCltInit", include_str!("../../assets/signatures/IPCCltInit.yml")),
("__OSInitSTM", include_str!("../../assets/signatures/__OSInitSTM.yml")),
("IOS_Open", include_str!("../../assets/signatures/IOS_Open.yml")),
("__ios_Ipc2", include_str!("../../assets/signatures/__ios_Ipc2.yml")),
("IPCiProfQueueReq", include_str!("../../assets/signatures/IPCiProfQueueReq.yml")),
("SCInit", include_str!("../../assets/signatures/SCInit.yml")),
("SCReloadConfFileAsync", include_str!("../../assets/signatures/SCReloadConfFileAsync.yml")),
("NANDPrivateOpenAsync", include_str!("../../assets/signatures/NANDPrivateOpenAsync.yml")),
("nandIsInitialized", include_str!("../../assets/signatures/nandIsInitialized.yml")),
("nandOpen", include_str!("../../assets/signatures/nandOpen.yml")),
("nandGenerateAbsPath", include_str!("../../assets/signatures/nandGenerateAbsPath.yml")),
("nandGetHeadToken", include_str!("../../assets/signatures/nandGetHeadToken.yml")),
("ISFS_OpenAsync", include_str!("../../assets/signatures/ISFS_OpenAsync.yml")),
("nandConvertErrorCode", include_str!("../../assets/signatures/nandConvertErrorCode.yml")),
(
"NANDLoggingAddMessageAsync",
include_str!("../../assets/signatures/NANDLoggingAddMessageAsync.yml"),
),
(
"__NANDPrintErrorMessage",
include_str!("../../assets/signatures/__NANDPrintErrorMessage.yml"),
),
("__OSInitNet", include_str!("../../assets/signatures/__OSInitNet.yml")),
("__DVDCheckDevice", include_str!("../../assets/signatures/__DVDCheckDevice.yml")),
("__OSInitPlayTime", include_str!("../../assets/signatures/__OSInitPlayTime.yml")),
("__OSStartPlayRecord", include_str!("../../assets/signatures/__OSStartPlayRecord.yml")),
("NANDInit", include_str!("../../assets/signatures/NANDInit.yml")),
("ISFS_OpenLib", include_str!("../../assets/signatures/ISFS_OpenLib.yml")),
("ESP_GetTitleId", include_str!("../../assets/signatures/ESP_GetTitleId.yml")),
(
"NANDSetAutoErrorMessaging",
include_str!("../../assets/signatures/NANDSetAutoErrorMessaging.yml"),
),
("__DVDFSInit", include_str!("../../assets/signatures/__DVDFSInit.yml")),
("__DVDClearWaitingQueue", include_str!("../../assets/signatures/__DVDClearWaitingQueue.yml")),
("__DVDInitWA", include_str!("../../assets/signatures/__DVDInitWA.yml")),
("__DVDLowSetWAType", include_str!("../../assets/signatures/__DVDLowSetWAType.yml")),
("__fstLoad", include_str!("../../assets/signatures/__fstLoad.yml")),
("DVDReset", include_str!("../../assets/signatures/DVDReset.yml")),
("DVDLowReset", include_str!("../../assets/signatures/DVDLowReset.yml")),
("DVDReadDiskID", include_str!("../../assets/signatures/DVDReadDiskID.yml")),
("stateReady", include_str!("../../assets/signatures/stateReady.yml")),
("DVDLowWaitCoverClose", include_str!("../../assets/signatures/DVDLowWaitCoverClose.yml")),
("__DVDStoreErrorCode", include_str!("../../assets/signatures/__DVDStoreErrorCode.yml")),
("DVDLowStopMotor", include_str!("../../assets/signatures/DVDLowStopMotor.yml")),
("DVDGetDriveStatus", include_str!("../../assets/signatures/DVDGetDriveStatus.yml")),
("printf", include_str!("../../assets/signatures/printf.yml")),
("sprintf", include_str!("../../assets/signatures/sprintf.yml")),
("vprintf", include_str!("../../assets/signatures/vprintf.yml")),
("vsprintf", include_str!("../../assets/signatures/vsprintf.yml")),
("vsnprintf", include_str!("../../assets/signatures/vsnprintf.yml")),
("__pformatter", include_str!("../../assets/signatures/__pformatter.yml")),
("longlong2str", include_str!("../../assets/signatures/longlong2str.yml")),
("__mod2u", include_str!("../../assets/signatures/__mod2u.yml")),
("__FileWrite", include_str!("../../assets/signatures/__FileWrite.yml")),
("fwrite", include_str!("../../assets/signatures/fwrite.yml")),
("__fwrite", include_str!("../../assets/signatures/__fwrite.yml")),
("__stdio_atexit", include_str!("../../assets/signatures/__stdio_atexit.yml")),
("__StringWrite", include_str!("../../assets/signatures/__StringWrite.yml")),
];
const POST_SIGNATURES: &[(&str, &str)] = &[
("RSOStaticLocateObject", include_str!("../../assets/signatures/RSOStaticLocateObject.yml")),
// ("GXInit", include_str!("../../assets/signatures/GXInit.yml")),
("__register_fragment", include_str!("../../assets/signatures/__register_fragment.yml")),
];
pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> {
let entry = obj.entry as u32;
if let Some(signature) =
check_signatures_str(obj, entry, include_str!("../../assets/signatures/__start.yml"))?
{
apply_signature(obj, entry, &signature)?;
}
for &(name, sig_str) in SIGNATURES {
if let Some((_, symbol)) = obj.symbols.by_name(name)? {
let addr = symbol.address as u32;
if let Some(signature) = check_signatures_str(obj, addr, sig_str)? {
apply_signature(obj, addr, &signature)?;
}
}
}
if let Some((_, symbol)) = obj.symbols.by_name("__init_user")? {
// __init_user can be overridden, but we can still look for __init_cpp from it
let mut analyzer = AnalyzerState::default();
analyzer.process_function_at(obj, symbol.address as u32)?;
for addr in analyzer.function_entries {
if let Some(signature) = check_signatures_str(
obj,
addr,
include_str!("../../assets/signatures/__init_cpp.yml"),
)? {
apply_signature(obj, addr, &signature)?;
break;
}
}
}
if let Some((_, symbol)) = obj.symbols.by_name("_ctors")? {
// First entry of ctors is __init_cpp_exceptions
let section = obj.section_at(symbol.address as u32)?;
let target = read_u32(&section.data, symbol.address as u32, section.address as u32)
.ok_or_else(|| anyhow!("Failed to read _ctors data"))?;
if target != 0 {
if let Some(signature) = check_signatures_str(
obj,
target,
include_str!("../../assets/signatures/__init_cpp_exceptions.yml"),
)? {
let address = symbol.address;
let section_index = section.index;
apply_signature(obj, target, &signature)?;
obj.add_symbol(
ObjSymbol {
name: "__init_cpp_exceptions_reference".to_string(),
demangled_name: None,
address,
section: Some(section_index),
size: 4,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local.into()),
kind: ObjSymbolKind::Object,
align: None,
data_kind: Default::default(),
},
true,
)?;
if obj.split_for(address as u32).is_none() {
obj.add_split(address as u32, ObjSplit {
unit: "__init_cpp_exceptions.cpp".to_string(),
end: address as u32 + 4,
align: None,
common: false,
});
}
}
}
}
if let Some((_, symbol)) = obj.symbols.by_name("_dtors")? {
let section = obj.section_at(symbol.address as u32)?;
let address = symbol.address;
let section_address = section.address;
let section_index = section.index;
// First entry of dtors is __destroy_global_chain
let target = read_u32(&section.data, address as u32, section_address as u32)
.ok_or_else(|| anyhow!("Failed to read _dtors data"))?;
let target2 = read_u32(&section.data, address as u32 + 4, section_address as u32)
.ok_or_else(|| anyhow!("Failed to read _dtors data"))?;
let mut target_ok = false;
let mut target2_ok = false;
if target != 0 {
if let Some(signature) = check_signatures_str(
obj,
target,
include_str!("../../assets/signatures/__destroy_global_chain.yml"),
)? {
apply_signature(obj, target, &signature)?;
obj.add_symbol(
ObjSymbol {
name: "__destroy_global_chain_reference".to_string(),
demangled_name: None,
address,
section: Some(section_index),
size: 4,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local.into()),
kind: ObjSymbolKind::Object,
align: None,
data_kind: Default::default(),
},
true,
)?;
target_ok = true;
}
}
// Second entry of dtors is __fini_cpp_exceptions
if target2 != 0 {
if let Some(signature) = check_signatures_str(
obj,
target2,
include_str!("../../assets/signatures/__fini_cpp_exceptions.yml"),
)? {
apply_signature(obj, target2, &signature)?;
obj.add_symbol(
ObjSymbol {
name: "__fini_cpp_exceptions_reference".to_string(),
demangled_name: None,
address: address + 4,
section: Some(section_index),
size: 4,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Local.into()),
kind: ObjSymbolKind::Object,
align: None,
data_kind: Default::default(),
},
true,
)?;
target2_ok = true;
}
}
if target_ok && target2_ok && obj.split_for(address as u32).is_none() {
obj.add_split(address as u32, ObjSplit {
unit: "__init_cpp_exceptions.cpp".to_string(),
end: address as u32 + 8,
align: None,
common: false,
});
}
}
Ok(())
}
pub fn apply_signatures_post(obj: &mut ObjInfo) -> Result<()> {
log::info!("Checking post CFA signatures...");
for &(_name, sig_str) in POST_SIGNATURES {
let signatures = parse_signatures(sig_str)?;
let mut iter = obj.symbols.by_kind(ObjSymbolKind::Function);
let opt = loop {
let Some((_, symbol)) = iter.next() else {
break Option::<(u32, FunctionSignature)>::None;
};
if let Some(signature) = check_signatures(obj, symbol.address as u32, &signatures)? {
break Some((symbol.address as u32, signature));
}
};
if let Some((addr, signature)) = opt {
drop(iter);
apply_signature(obj, addr, &signature)?;
break;
}
}
log::info!("Done!");
Ok(())
}

View File

@@ -35,6 +35,7 @@ pub enum TailCallResult {
Not,
Is,
Possible,
Error(anyhow::Error),
}
type BlockRange = Range<u32>;
@@ -137,7 +138,7 @@ impl FunctionSlices {
.with_context(|| format!("While processing {:#010X}", function_start))?;
self.check_epilogue(section, ins)
.with_context(|| format!("While processing {:#010X}", function_start))?;
if !self.has_conditional_blr && is_conditional_blr(&ins) {
if !self.has_conditional_blr && is_conditional_blr(ins) {
self.has_conditional_blr = true;
}
if !self.has_rfi && ins.op == Opcode::Rfi {
@@ -351,13 +352,15 @@ impl FunctionSlices {
}
let end = self.end();
if let Ok(section) = obj.section_at(end) {
// FIXME this is real bad
if !self.has_conditional_blr {
if let Some(ins) = disassemble(&section, end - 4) {
if ins.op == Opcode::B {
if self.function_references.contains(&ins.branch_dest().unwrap()) {
for (_, branches) in &self.branches {
match (obj.section_at(end), obj.section_at(end - 4)) {
(Ok(section), Ok(other_section)) if section.index == other_section.index => {
// FIXME this is real bad
if !self.has_conditional_blr {
if let Some(ins) = disassemble(section, end - 4) {
if ins.op == Opcode::B
&& self.function_references.contains(&ins.branch_dest().unwrap())
{
for branches in self.branches.values() {
if branches.len() > 1
&& branches.contains(self.blocks.last_key_value().unwrap().0)
{
@@ -367,29 +370,28 @@ impl FunctionSlices {
}
}
}
}
// MWCC optimization sometimes leaves an unreachable blr
// after generating a conditional blr in the function.
if self.has_conditional_blr {
if matches!(disassemble(&section, end - 4), Some(ins) if !ins.is_blr())
&& matches!(disassemble(&section, end), Some(ins) if ins.is_blr())
// MWCC optimization sometimes leaves an unreachable blr
// after generating a conditional blr in the function.
if self.has_conditional_blr
&& matches!(disassemble(section, end - 4), Some(ins) if !ins.is_blr())
&& matches!(disassemble(section, end), Some(ins) if ins.is_blr())
&& !known_functions.contains(&end)
{
log::trace!("Found trailing blr @ {:#010X}, merging with function", end);
self.blocks.insert(end, end + 4);
}
}
// Some functions with rfi also include a trailing nop
if self.has_rfi {
if matches!(disassemble(&section, end), Some(ins) if is_nop(&ins))
// Some functions with rfi also include a trailing nop
if self.has_rfi
&& matches!(disassemble(section, end), Some(ins) if is_nop(&ins))
&& !known_functions.contains(&end)
{
log::trace!("Found trailing nop @ {:#010X}, merging with function", end);
self.blocks.insert(end, end + 4);
}
}
_ => {}
}
self.finalized = true;
@@ -417,6 +419,14 @@ impl FunctionSlices {
if addr < function_start {
return TailCallResult::Is;
}
// If the jump target is in a different section, known tail call.
let section = match obj.section_at(function_start) {
Ok(section) => section,
Err(e) => return TailCallResult::Error(e),
};
if !section.contains(addr) {
return TailCallResult::Is;
}
// If the jump target has 0'd padding before it, known tail call.
if matches!(obj.section_data(addr - 4, addr), Ok((_, data)) if data == [0u8; 4]) {
return TailCallResult::Is;
@@ -428,15 +438,16 @@ impl FunctionSlices {
}
// If jump target is known to be a function, or there's a function in between
// this and the jump target, known tail call.
log::trace!("Checking {:#010X}..={:#010X}", function_start + 4, addr);
if self.function_references.range(function_start + 4..=addr).next().is_some()
|| known_functions.range(function_start + 4..=addr).next().is_some()
{
return TailCallResult::Is;
}
// Perform CFA on jump target to determine more
let mut slices = FunctionSlices::default();
slices.function_references = self.function_references.clone();
let mut slices = FunctionSlices {
function_references: self.function_references.clone(),
..Default::default()
};
if let Ok(result) =
slices.analyze(obj, addr, function_start, Some(function_end), known_functions)
{

View File

@@ -1,5 +1,5 @@
use std::{
collections::{BTreeMap, BTreeSet},
collections::{btree_map::Entry, BTreeMap, BTreeSet},
mem::take,
};
@@ -12,8 +12,10 @@ use crate::{
uniq_jump_table_entries,
vm::{is_store_op, BranchTarget, GprValue, StepResult, VM},
},
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolKind},
util::nested::NestedVec,
obj::{
ObjDataKind, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
},
};
#[derive(Debug, Copy, Clone)]
@@ -99,80 +101,38 @@ impl Tracker {
Ok(())
}
// fn update_stack_address(&mut self, addr: u32) {
// if let Some(db_stack_addr) = self.db_stack_addr {
// if db_stack_addr == addr {
// return;
// }
// }
// if let Some(stack_addr) = self.stack_address {
// if stack_addr != addr {
// log::error!("Stack address overridden from {:#010X} to {:#010X}", stack_addr, addr);
// return;
// }
// }
// log::debug!("Located stack address: {:08X}", addr);
// self.stack_address = Some(addr);
// let db_stack_addr = addr + 0x2000;
// self.db_stack_addr = Some(db_stack_addr);
// self.arena_lo = Some((db_stack_addr + 0x1F) & !0x1F);
// // __ArenaHi is fixed (until it isn't?)
// self.arena_hi = Some(0x81700000);
// log::debug!("_stack_addr: {:#010X}", addr);
// log::debug!("_stack_end: {:#010X}", self.stack_end.unwrap());
// log::debug!("_db_stack_addr: {:#010X}", db_stack_addr);
// log::debug!("__ArenaLo: {:#010X}", self.arena_lo.unwrap());
// log::debug!("__ArenaHi: {:#010X}", self.arena_hi.unwrap());
// }
fn process_code(&mut self, obj: &ObjInfo) -> Result<()> {
let mut symbol_map = BTreeMap::new();
self.process_function_by_address(obj, obj.entry as u32)?;
for section in obj.sections.iter().filter(|s| s.kind == ObjSectionKind::Code) {
symbol_map.append(&mut obj.build_symbol_map(section.index)?);
}
self.process_function_by_address(obj, &symbol_map, obj.entry as u32)?;
'outer: for (&addr, symbols) in &symbol_map {
if self.processed_functions.contains(&addr) {
continue;
}
self.processed_functions.insert(addr);
for &symbol_idx in symbols {
let symbol = &obj.symbols[symbol_idx];
if symbol.kind == ObjSymbolKind::Function && symbol.size_known {
self.process_function(obj, symbol)?;
continue 'outer;
for (_, symbol) in obj
.symbols
.for_range(section.address as u32..(section.address + section.size) as u32)
.filter(|(_, symbol)| symbol.kind == ObjSymbolKind::Function && symbol.size_known)
{
let addr = symbol.address as u32;
if !self.processed_functions.insert(addr) {
continue;
}
self.process_function(obj, symbol)?;
}
}
// Special handling for gTRKInterruptVectorTable
// TODO
// if let (Some(trk_interrupt_table), Some(trk_interrupt_vector_table_end)) = (
// obj.symbols.iter().find(|sym| sym.name == "gTRKInterruptVectorTable"),
// obj.symbols.iter().find(|sym| sym.name == "gTRKInterruptVectorTableEnd"),
// ) {}
Ok(())
}
fn process_function_by_address(
&mut self,
obj: &ObjInfo,
symbol_map: &BTreeMap<u32, Vec<usize>>,
addr: u32,
) -> Result<()> {
fn process_function_by_address(&mut self, obj: &ObjInfo, addr: u32) -> Result<()> {
if self.processed_functions.contains(&addr) {
return Ok(());
}
self.processed_functions.insert(addr);
if let Some(symbols) = symbol_map.get(&addr) {
for &symbol_idx in symbols {
let symbol = &obj.symbols[symbol_idx];
if symbol.kind == ObjSymbolKind::Function && symbol.size_known {
self.process_function(obj, symbol)?;
return Ok(());
}
}
if let Some((_, symbol)) = obj
.symbols
.at_address(addr)
.find(|(_, symbol)| symbol.kind == ObjSymbolKind::Function && symbol.size_known)
{
self.process_function(obj, symbol)?;
} else {
log::warn!("Failed to locate function symbol @ {:#010X}", addr);
}
log::warn!("Failed to locate function symbol @ {:#010X}", addr);
Ok(())
}
@@ -189,12 +149,9 @@ impl Tracker {
match result {
StepResult::Continue => {
// if ins.addr == 0x8000ed0c || ins.addr == 0x8000ed08 || ins.addr == 0x8000ca50 {
// println!("ok");
// }
match ins.op {
// addi rD, rA, SIMM
Opcode::Addi | Opcode::Addic | Opcode::Addic_ => {
// addi rD, rA, SIMM
let source = ins.field_rA();
let target = ins.field_rD();
if let GprValue::Constant(value) = vm.gpr[target].value {
@@ -224,8 +181,8 @@ impl Tracker {
}
}
}
// ori rA, rS, UIMM
Opcode::Ori => {
// ori rA, rS, UIMM
let target = ins.field_rA();
if let GprValue::Constant(value) = vm.gpr[target].value {
if self.is_valid_address(obj, ins.addr, value) {
@@ -416,6 +373,11 @@ impl Tracker {
if self.ignore_addresses.contains(&addr) {
return false;
}
if let Some((&start, &end)) = obj.blocked_ranges.range(..=from).last() {
if from >= start && from < end {
return false;
}
}
if self.known_relocations.contains(&from) {
return true;
}
@@ -432,11 +394,9 @@ impl Tracker {
// if addr > 0x80000000 && addr < 0x80003100 {
// return true;
// }
for section in &obj.sections {
if addr >= section.address as u32 && addr <= (section.address + section.size) as u32 {
// References to code sections will never be unaligned
return section.kind != ObjSectionKind::Code || addr & 3 == 0;
}
if let Ok(section) = obj.section_at(addr) {
// References to code sections will never be unaligned
return section.kind != ObjSectionKind::Code || addr & 3 == 0;
}
false
}
@@ -451,16 +411,16 @@ impl Tracker {
return None;
}
// HACK for RSOStaticLocateObject
for section in &obj.sections {
if addr == section.address as u32 {
let name = format!("_f_{}", section.name.trim_start_matches('.'));
return Some(generate_special_symbol(obj, addr, &name));
}
}
// for section in &obj.sections {
// if addr == section.address as u32 {
// let name = format!("_f_{}", section.name.trim_start_matches('.'));
// return generate_special_symbol(obj, addr, &name).ok();
// }
// }
let mut check_symbol = |opt: Option<u32>, name: &str| -> Option<usize> {
if let Some(value) = opt {
if addr == value {
return Some(generate_special_symbol(obj, value, name));
return generate_special_symbol(obj, value, name).ok();
}
}
None
@@ -475,11 +435,22 @@ impl Tracker {
}
pub fn apply(&self, obj: &mut ObjInfo, replace: bool) -> Result<()> {
fn apply_section_name(section: &mut ObjSection, name: &str) {
let module_id = if let Some((_, b)) = section.name.split_once(':') {
b.parse::<u32>().unwrap_or(0)
} else {
0
};
let new_name =
if module_id == 0 { name.to_string() } else { format!("{}:{}", name, module_id) };
log::debug!("Renaming {} to {}", section.name, new_name);
section.name = new_name;
}
for section in &mut obj.sections {
if !section.section_known {
if section.kind == ObjSectionKind::Code {
log::info!("Renaming {} to .text", section.name);
section.name = ".text".to_string();
apply_section_name(section, ".text");
continue;
}
let start = section.address as u32;
@@ -487,39 +458,32 @@ impl Tracker {
if self.sda_to.range(start..end).next().is_some() {
if self.stores_to.range(start..end).next().is_some() {
if section.kind == ObjSectionKind::Bss {
log::info!("Renaming {} to .sbss", section.name);
section.name = ".sbss".to_string();
apply_section_name(section, ".sbss");
} else {
log::info!("Renaming {} to .sdata", section.name);
section.name = ".sdata".to_string();
apply_section_name(section, ".sdata");
}
} else if section.kind == ObjSectionKind::Bss {
log::info!("Renaming {} to .sbss2", section.name);
section.name = ".sbss2".to_string();
apply_section_name(section, ".sbss2");
} else {
log::info!("Renaming {} to .sdata2", section.name);
section.name = ".sdata2".to_string();
apply_section_name(section, ".sdata2");
section.kind = ObjSectionKind::ReadOnlyData;
}
} else if self.hal_to.range(start..end).next().is_some() {
if section.kind == ObjSectionKind::Bss {
log::info!("Renaming {} to .bss", section.name);
section.name = ".bss".to_string();
apply_section_name(section, ".bss");
} else if self.stores_to.range(start..end).next().is_some() {
log::info!("Renaming {} to .data", section.name);
section.name = ".data".to_string();
apply_section_name(section, ".data");
} else {
log::info!("Renaming {} to .rodata", section.name);
section.name = ".rodata".to_string();
apply_section_name(section, ".rodata");
section.kind = ObjSectionKind::ReadOnlyData;
}
}
}
}
let mut symbol_maps = Vec::new();
let mut relocation_maps = Vec::new();
for section in &obj.sections {
symbol_maps.push(obj.build_symbol_map(section.index)?);
relocation_maps.push(section.build_relocation_map()?);
}
for (addr, reloc) in &self.relocations {
@@ -533,6 +497,18 @@ impl Tracker {
Relocation::Rel24(v) => (ObjRelocKind::PpcRel24, v),
Relocation::Absolute(v) => (ObjRelocKind::Absolute, v),
};
let data_kind = self
.data_types
.get(&target)
.map(|dt| match dt {
DataKind::Unknown => ObjDataKind::Unknown,
DataKind::Word => ObjDataKind::Byte4,
DataKind::Half => ObjDataKind::Byte2,
DataKind::Byte => ObjDataKind::Byte,
DataKind::Float => ObjDataKind::Float,
DataKind::Double => ObjDataKind::Double,
})
.unwrap_or_default();
let (target_symbol, addend) =
if let Some(symbol) = self.special_symbol(obj, target, reloc_kind) {
(symbol, 0)
@@ -544,16 +520,15 @@ impl Tracker {
None => continue,
};
// Try to find a previous sized symbol that encompasses the target
let sym_map = &mut symbol_maps[target_section.index];
let target_symbol = {
let mut result = None;
for (_addr, symbol_idxs) in sym_map.range(..=target).rev() {
for (_addr, symbol_idxs) in obj.symbols.indexes_for_range(..=target).rev() {
let symbol_idx = if symbol_idxs.len() == 1 {
symbol_idxs.first().cloned().unwrap()
} else {
let mut symbol_idxs = symbol_idxs.clone();
let mut symbol_idxs = symbol_idxs.to_vec();
symbol_idxs.sort_by_key(|&symbol_idx| {
let symbol = &obj.symbols[symbol_idx];
let symbol = obj.symbols.at(symbol_idx);
let mut rank = match symbol.kind {
ObjSymbolKind::Function | ObjSymbolKind::Object => {
match reloc_kind {
@@ -589,7 +564,7 @@ impl Tracker {
None => continue,
}
};
let symbol = &obj.symbols[symbol_idx];
let symbol = obj.symbols.at(symbol_idx);
if symbol.address == target as u64 {
result = Some(symbol_idx);
break;
@@ -604,12 +579,20 @@ impl Tracker {
result
};
if let Some(symbol_idx) = target_symbol {
let symbol = &obj.symbols[symbol_idx];
(symbol_idx, target as i64 - symbol.address as i64)
let symbol = obj.symbols.at(symbol_idx);
let symbol_address = symbol.address;
// TODO meh
if data_kind != ObjDataKind::Unknown
&& symbol.data_kind == ObjDataKind::Unknown
&& symbol_address as u32 == target
{
obj.symbols
.replace(symbol_idx, ObjSymbol { data_kind, ..symbol.clone() })?;
}
(symbol_idx, target as i64 - symbol_address as i64)
} else {
// Create a new label
let symbol_idx = obj.symbols.len();
obj.symbols.push(ObjSymbol {
let symbol_idx = obj.symbols.add_direct(ObjSymbol {
name: format!("lbl_{:08X}", target),
demangled_name: None,
address: target as u64,
@@ -618,8 +601,9 @@ impl Tracker {
size_known: false,
flags: Default::default(),
kind: Default::default(),
});
sym_map.nested_push(target, symbol_idx);
align: None,
data_kind,
})?;
(symbol_idx, 0)
}
};
@@ -636,25 +620,35 @@ impl Tracker {
reloc
),
};
match section.relocations.iter_mut().find(|r| r.address as u32 == addr) {
Some(v) => {
let iter_symbol = &obj.symbols[v.target_symbol];
let reloc_symbol = &obj.symbols[reloc.target_symbol];
if iter_symbol.address as i64 + v.addend
!= reloc_symbol.address as i64 + reloc.addend
{
bail!(
"Conflicting relocations (target {:#010X}): {:#010X?} != {:#010X?}",
target,
v,
reloc
);
}
if replace {
*v = reloc;
let reloc_map = &mut relocation_maps[section.index];
match reloc_map.entry(addr) {
Entry::Vacant(e) => {
e.insert(section.relocations.len());
section.relocations.push(reloc);
}
Entry::Occupied(e) => {
let reloc_symbol = obj.symbols.at(reloc.target_symbol);
if reloc_symbol.name != "_unresolved" {
let v = &mut section.relocations[*e.get()];
let iter_symbol = obj.symbols.at(v.target_symbol);
if iter_symbol.address as i64 + v.addend
!= reloc_symbol.address as i64 + reloc.addend
{
bail!(
"Conflicting relocations (target {:#010X}): {:#010X?} ({}) != {:#010X?} ({})",
target,
v,
iter_symbol.name,
reloc,
reloc_symbol.name
);
}
if replace {
*v = reloc;
}
}
}
None => section.relocations.push(reloc),
}
}
Ok(())
@@ -716,17 +710,16 @@ fn data_kind_from_op(op: Opcode) -> DataKind {
}
}
fn generate_special_symbol(obj: &mut ObjInfo, addr: u32, name: &str) -> usize {
if let Some((symbol_idx, _)) =
obj.symbols.iter().enumerate().find(|&(_, symbol)| symbol.name == name)
{
return symbol_idx;
}
let symbol_idx = obj.symbols.len();
obj.symbols.push(ObjSymbol {
name: name.to_string(),
address: addr as u64,
..Default::default()
});
symbol_idx
fn generate_special_symbol(obj: &mut ObjInfo, addr: u32, name: &str) -> Result<usize> {
obj.add_symbol(
ObjSymbol {
name: name.to_string(),
address: addr as u64,
size: 0,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
..Default::default()
},
true,
)
}

View File

@@ -162,8 +162,8 @@ impl VM {
Opcode::Illegal => {
return StepResult::Illegal;
}
// add rD, rA, rB
Opcode::Add => {
// add rD, rA, rB
let left = self.gpr[ins.field_rA()].value;
let right = self.gpr[ins.field_rB()].value;
let value = match (left, right) {
@@ -174,8 +174,8 @@ impl VM {
};
self.gpr[ins.field_rD()].set_direct(value);
}
// addis rD, rA, SIMM
Opcode::Addis => {
// addis rD, rA, SIMM
let left = if ins.field_rA() == 0 {
GprValue::Constant(0)
} else {
@@ -194,10 +194,10 @@ impl VM {
self.gpr[ins.field_rD()].set_direct(value);
}
}
// addi rD, rA, SIMM
// addic rD, rA, SIMM
// addic. rD, rA, SIMM
Opcode::Addi | Opcode::Addic | Opcode::Addic_ => {
// addi rD, rA, SIMM
// addic rD, rA, SIMM
// addic. rD, rA, SIMM
let left = if ins.field_rA() == 0 && ins.op == Opcode::Addi {
GprValue::Constant(0)
} else {
@@ -216,8 +216,8 @@ impl VM {
self.gpr[ins.field_rD()].set_lo(value, ins.addr, self.gpr[ins.field_rA()]);
}
}
// ori rA, rS, UIMM
Opcode::Ori => {
// ori rA, rS, UIMM
let value = match self.gpr[ins.field_rS()].value {
GprValue::Constant(value) => {
GprValue::Constant(value | ins.field_uimm() as u32)
@@ -226,8 +226,8 @@ impl VM {
};
self.gpr[ins.field_rA()].set_lo(value, ins.addr, self.gpr[ins.field_rS()]);
}
// or rA, rS, rB
Opcode::Or => {
// or rA, rS, rB
if ins.field_rS() == ins.field_rB() {
// Register copy
self.gpr[ins.field_rA()] = self.gpr[ins.field_rS()];
@@ -428,11 +428,8 @@ impl VM {
}
_ => {
for field in ins.defs() {
match field.argument() {
Some(Argument::GPR(GPR(reg))) => {
self.gpr[reg as usize].set_direct(GprValue::Unknown);
}
_ => {}
if let Some(Argument::GPR(GPR(reg))) = field.argument() {
self.gpr[reg as usize].set_direct(GprValue::Unknown);
}
}
}