mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-12 14:46:17 +00:00
6 months of occasional work I guess
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
155
src/analysis/objects.rs
Normal 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(())
|
||||
}
|
||||
@@ -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
365
src/analysis/signatures.rs
Normal 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(§ion.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(§ion.data, address as u32, section_address as u32)
|
||||
.ok_or_else(|| anyhow!("Failed to read _dtors data"))?;
|
||||
let target2 = read_u32(§ion.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(())
|
||||
}
|
||||
@@ -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(§ion, 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(§ion, end - 4), Some(ins) if !ins.is_blr())
|
||||
&& matches!(disassemble(§ion, 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(§ion, 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)
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user