Reorganize files; start RSO support; config & split updates

This commit is contained in:
2023-01-27 23:15:52 -05:00
parent 827e0806be
commit 830f7b172f
171 changed files with 2926 additions and 1010 deletions

View File

@@ -1,22 +1,15 @@
use std::{
collections::{btree_map::Entry, BTreeMap, BTreeSet},
num::NonZeroU32,
ops::Range,
};
use std::collections::{BTreeMap, BTreeSet};
use anyhow::{anyhow, bail, ensure, Context, Result};
use fixedbitset::FixedBitSet;
use flagset::FlagSet;
use ppc750cl::{Argument, Ins, Opcode, GPR};
use anyhow::{anyhow, bail, Result};
use crate::util::{
executor::{disassemble, ExecCbData, ExecCbResult, Executor},
obj::{
ObjInfo, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
ObjSymbolKind,
use crate::{
analysis::{
executor::{ExecCbData, ExecCbResult, Executor},
skip_alignment,
slices::{FunctionSlices, TailCallResult},
vm::{BranchTarget, GprValue, StepResult, VM},
},
slices::{FunctionSlices, TailCallResult},
vm::{BranchTarget, GprValue, StepResult, VM},
obj::{ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind},
};
#[derive(Debug, Default)]
@@ -336,139 +329,14 @@ impl AnalyzerState {
}
}
pub trait AnalysisPass {
fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()>;
}
pub struct FindTRKInterruptVectorTable {}
pub const TRK_TABLE_HEADER: &str = "Metrowerks Target Resident Kernel for PowerPC";
pub const TRK_TABLE_SIZE: u32 = 0x1F34; // always?
// TRK_MINNOW_DOLPHIN.a __exception.s
impl AnalysisPass for FindTRKInterruptVectorTable {
fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> {
for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end == 0) {
let (section, data) = match obj.section_data(start, 0) {
Ok((section, data)) => (section, data),
Err(_) => continue,
};
if data.starts_with(TRK_TABLE_HEADER.as_bytes())
&& data[TRK_TABLE_HEADER.as_bytes().len()] == 0
{
log::info!("Found gTRKInterruptVectorTable @ {:#010X}", start);
state.known_symbols.insert(start, ObjSymbol {
name: "gTRKInterruptVectorTable".to_string(),
demangled_name: None,
address: start as u64,
section: Some(section.index),
size: 0,
size_known: true,
flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)),
kind: ObjSymbolKind::Unknown,
});
let end = start + TRK_TABLE_SIZE;
state.known_symbols.insert(end, ObjSymbol {
name: "gTRKInterruptVectorTableEnd".to_string(),
demangled_name: None,
address: end as u64,
section: Some(section.index),
size: 0,
size_known: true,
flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)),
kind: ObjSymbolKind::Unknown,
});
return Ok(());
}
}
log::info!("gTRKInterruptVectorTable not found");
Ok(())
}
}
pub struct FindSaveRestSleds {}
const SLEDS: [([u8; 4], &'static str, &'static str); 4] = [
([0xd9, 0xcb, 0xff, 0x70], "__save_fpr", "_savefpr_"),
([0xc9, 0xcb, 0xff, 0x70], "__restore_fpr", "_restfpr_"),
([0x91, 0xcb, 0xff, 0xb8], "__save_gpr", "_savegpr_"),
([0x81, 0xcb, 0xff, 0xb8], "__restore_gpr", "_restgpr_"),
];
// Runtime.PPCEABI.H.a runtime.c
impl AnalysisPass for FindSaveRestSleds {
fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> {
const SLED_SIZE: usize = 19 * 4; // registers 14-31 + blr
let mut clear_ranges: Vec<Range<u32>> = vec![];
for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end != 0) {
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);
clear_ranges.push(start + 4..start + SLED_SIZE as u32);
state.known_symbols.insert(start, ObjSymbol {
name: func.to_string(),
demangled_name: None,
address: start as u64,
section: Some(section.index),
size: SLED_SIZE as u64,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
kind: ObjSymbolKind::Function,
});
for i in 14..=31 {
let addr = start + (i - 14) * 4;
state.known_symbols.insert(addr, ObjSymbol {
name: format!("{}{}", label, i),
demangled_name: None,
address: addr as u64,
section: Some(section.index),
size: 0,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
kind: ObjSymbolKind::Unknown,
});
}
}
}
}
for range in clear_ranges {
for addr in range.step_by(4) {
state.function_entries.remove(&addr);
state.function_bounds.remove(&addr);
state.function_slices.remove(&addr);
}
}
Ok(())
}
}
fn skip_alignment(obj: &ObjInfo, mut addr: u32, end: u32) -> Option<u32> {
let mut data = match obj.section_data(addr, end) {
Ok((_, data)) => data,
Err(_) => return None,
};
loop {
if data.is_empty() {
break None;
}
if data[0..4] == [0u8; 4] {
addr += 4;
data = &data[4..];
} else {
break Some(addr);
}
}
}
/// Execute VM from entry point following branches and function calls
/// until SDA bases are initialized (__init_registers)
pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
let mut executor = Executor::new(obj);
executor.push(obj.entry as u32, VM::new(), false);
let result =
executor.run(obj, |ExecCbData { executor, vm, result, section, ins, block_start }| {
let result = executor.run(
obj,
|ExecCbData { executor, vm, result, section: _, ins, block_start: _ }| {
match result {
StepResult::Continue | StepResult::LoadStore { .. } => {
return Ok(ExecCbResult::Continue);
@@ -499,7 +367,8 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
}
Ok(ExecCbResult::EndBlock)
})?;
},
)?;
match result {
Some((sda2_base, sda_base)) => {
obj.sda2_base = Some(sda2_base);

View File

@@ -1,26 +1,15 @@
use std::{collections::BTreeSet, num::NonZeroU32};
use anyhow::{Context, Result};
use anyhow::Result;
use fixedbitset::FixedBitSet;
use ppc750cl::Ins;
use crate::util::{
use crate::{
analysis::{
disassemble,
vm::{StepResult, VM},
},
obj::{ObjInfo, ObjSection, ObjSectionKind},
vm::{StepResult, VM},
};
pub fn disassemble(section: &ObjSection, address: u32) -> Option<Ins> {
read_u32(&section.data, address, section.address as u32).map(|code| Ins::new(code, address))
}
pub fn read_u32(data: &[u8], address: u32, section_address: u32) -> Option<u32> {
let offset = (address - section_address) as usize;
if data.len() < offset + 4 {
return None;
}
Some(u32::from_be_bytes(data[offset..offset + 4].try_into().unwrap()))
}
/// Space-efficient implementation for tracking visited code addresses
struct VisitedAddresses {
inner: Vec<FixedBitSet>,
@@ -155,67 +144,3 @@ impl Executor {
self.visited.contains(section, address)
}
}
fn is_valid_jump_table_addr(obj: &ObjInfo, addr: u32) -> bool {
matches!(obj.section_at(addr), Ok(section) if section.kind != ObjSectionKind::Bss)
}
fn get_jump_table_entries(
obj: &ObjInfo,
addr: u32,
size: Option<NonZeroU32>,
from: u32,
function_start: u32,
function_end: u32,
) -> Result<(Vec<u32>, u32)> {
let section = obj.section_at(addr).with_context(|| {
format!("Failed to get jump table entries @ {:#010X} size {:?}", addr, size)
})?;
let offset = (addr as u64 - section.address) as usize;
if let Some(size) = size.map(|n| n.get()) {
log::debug!(
"Located jump table @ {:#010X} with entry count {} (from {:#010X})",
addr,
size / 4,
from
);
let jt_data = &section.data[offset..offset + size as usize];
let entries =
jt_data.chunks_exact(4).map(|c| u32::from_be_bytes(c.try_into().unwrap())).collect();
Ok((entries, size))
} else {
let mut entries = Vec::new();
let mut cur_addr = addr;
while let Some(value) = read_u32(&section.data, cur_addr, section.address as u32) {
if value < function_start || value >= function_end {
break;
}
entries.push(value);
cur_addr += 4;
}
let size = cur_addr - addr;
log::debug!(
"Guessed jump table @ {:#010X} with entry count {} (from {:#010X})",
addr,
size / 4,
from
);
Ok((entries, size))
}
}
pub fn uniq_jump_table_entries(
obj: &ObjInfo,
addr: u32,
size: Option<NonZeroU32>,
from: u32,
function_start: u32,
function_end: u32,
) -> Result<(BTreeSet<u32>, u32)> {
if !is_valid_jump_table_addr(obj, addr) {
return Ok((BTreeSet::new(), 0));
}
let (entries, size) =
get_jump_table_entries(obj, addr, size, from, function_start, function_end)?;
Ok((BTreeSet::from_iter(entries.iter().cloned().filter(|&addr| addr != 0)), size))
}

107
src/analysis/mod.rs Normal file
View File

@@ -0,0 +1,107 @@
use std::{collections::BTreeSet, num::NonZeroU32};
use anyhow::{Context, Result};
use ppc750cl::Ins;
use crate::obj::{ObjInfo, ObjSection, ObjSectionKind};
pub mod cfa;
pub mod executor;
pub mod pass;
pub mod slices;
pub mod tracker;
pub mod vm;
pub fn disassemble(section: &ObjSection, address: u32) -> Option<Ins> {
read_u32(&section.data, address, section.address as u32).map(|code| Ins::new(code, address))
}
pub fn read_u32(data: &[u8], address: u32, section_address: u32) -> Option<u32> {
let offset = (address - section_address) as usize;
if data.len() < offset + 4 {
return None;
}
Some(u32::from_be_bytes(data[offset..offset + 4].try_into().unwrap()))
}
fn is_valid_jump_table_addr(obj: &ObjInfo, addr: u32) -> bool {
matches!(obj.section_at(addr), Ok(section) if section.kind != ObjSectionKind::Bss)
}
fn get_jump_table_entries(
obj: &ObjInfo,
addr: u32,
size: Option<NonZeroU32>,
from: u32,
function_start: u32,
function_end: u32,
) -> Result<(Vec<u32>, u32)> {
let section = obj.section_at(addr).with_context(|| {
format!("Failed to get jump table entries @ {:#010X} size {:?}", addr, size)
})?;
let offset = (addr as u64 - section.address) as usize;
if let Some(size) = size.map(|n| n.get()) {
log::trace!(
"Located jump table @ {:#010X} with entry count {} (from {:#010X})",
addr,
size / 4,
from
);
let jt_data = &section.data[offset..offset + size as usize];
let entries =
jt_data.chunks_exact(4).map(|c| u32::from_be_bytes(c.try_into().unwrap())).collect();
Ok((entries, size))
} else {
let mut entries = Vec::new();
let mut cur_addr = addr;
while let Some(value) = read_u32(&section.data, cur_addr, section.address as u32) {
if value < function_start || value >= function_end {
break;
}
entries.push(value);
cur_addr += 4;
}
let size = cur_addr - addr;
log::debug!(
"Guessed jump table @ {:#010X} with entry count {} (from {:#010X})",
addr,
size / 4,
from
);
Ok((entries, size))
}
}
pub fn uniq_jump_table_entries(
obj: &ObjInfo,
addr: u32,
size: Option<NonZeroU32>,
from: u32,
function_start: u32,
function_end: u32,
) -> Result<(BTreeSet<u32>, u32)> {
if !is_valid_jump_table_addr(obj, addr) {
return Ok((BTreeSet::new(), 0));
}
let (entries, size) =
get_jump_table_entries(obj, addr, size, from, function_start, function_end)?;
Ok((BTreeSet::from_iter(entries.iter().cloned().filter(|&addr| addr != 0)), size))
}
pub fn skip_alignment(obj: &ObjInfo, mut addr: u32, end: u32) -> Option<u32> {
let mut data = match obj.section_data(addr, end) {
Ok((_, data)) => data,
Err(_) => return None,
};
loop {
if data.is_empty() {
break None;
}
if data[0..4] == [0u8; 4] {
addr += 4;
data = &data[4..];
} else {
break Some(addr);
}
}
}

117
src/analysis/pass.rs Normal file
View File

@@ -0,0 +1,117 @@
use std::ops::Range;
use anyhow::Result;
use flagset::FlagSet;
use crate::{
analysis::cfa::AnalyzerState,
obj::{ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind},
};
pub trait AnalysisPass {
fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()>;
}
pub struct FindTRKInterruptVectorTable {}
pub const TRK_TABLE_HEADER: &str = "Metrowerks Target Resident Kernel for PowerPC";
pub const TRK_TABLE_SIZE: u32 = 0x1F34; // always?
// TRK_MINNOW_DOLPHIN.a __exception.s
impl AnalysisPass for FindTRKInterruptVectorTable {
fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> {
for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end == 0) {
let (section, data) = match obj.section_data(start, 0) {
Ok((section, data)) => (section, data),
Err(_) => continue,
};
if data.starts_with(TRK_TABLE_HEADER.as_bytes())
&& data[TRK_TABLE_HEADER.as_bytes().len()] == 0
{
log::info!("Found gTRKInterruptVectorTable @ {:#010X}", start);
state.known_symbols.insert(start, ObjSymbol {
name: "gTRKInterruptVectorTable".to_string(),
demangled_name: None,
address: start as u64,
section: Some(section.index),
size: 0,
size_known: true,
flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)),
kind: ObjSymbolKind::Unknown,
});
let end = start + TRK_TABLE_SIZE;
state.known_symbols.insert(end, ObjSymbol {
name: "gTRKInterruptVectorTableEnd".to_string(),
demangled_name: None,
address: end as u64,
section: Some(section.index),
size: 0,
size_known: true,
flags: ObjSymbolFlagSet(FlagSet::from(ObjSymbolFlags::Global)),
kind: ObjSymbolKind::Unknown,
});
return Ok(());
}
}
log::info!("gTRKInterruptVectorTable not found");
Ok(())
}
}
pub struct FindSaveRestSleds {}
const SLEDS: [([u8; 4], &'static str, &'static str); 4] = [
([0xd9, 0xcb, 0xff, 0x70], "__save_fpr", "_savefpr_"),
([0xc9, 0xcb, 0xff, 0x70], "__restore_fpr", "_restfpr_"),
([0x91, 0xcb, 0xff, 0xb8], "__save_gpr", "_savegpr_"),
([0x81, 0xcb, 0xff, 0xb8], "__restore_gpr", "_restgpr_"),
];
// Runtime.PPCEABI.H.a runtime.c
impl AnalysisPass for FindSaveRestSleds {
fn execute(state: &mut AnalyzerState, obj: &ObjInfo) -> Result<()> {
const SLED_SIZE: usize = 19 * 4; // registers 14-31 + blr
let mut clear_ranges: Vec<Range<u32>> = vec![];
for (&start, _) in state.function_bounds.iter().filter(|&(_, &end)| end != 0) {
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);
clear_ranges.push(start + 4..start + SLED_SIZE as u32);
state.known_symbols.insert(start, ObjSymbol {
name: func.to_string(),
demangled_name: None,
address: start as u64,
section: Some(section.index),
size: SLED_SIZE as u64,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
kind: ObjSymbolKind::Function,
});
for i in 14..=31 {
let addr = start + (i - 14) * 4;
state.known_symbols.insert(addr, ObjSymbol {
name: format!("{}{}", label, i),
demangled_name: None,
address: addr as u64,
section: Some(section.index),
size: 0,
size_known: true,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
kind: ObjSymbolKind::Unknown,
});
}
}
}
}
for range in clear_ranges {
for addr in range.step_by(4) {
state.function_entries.remove(&addr);
state.function_bounds.remove(&addr);
state.function_slices.remove(&addr);
}
}
Ok(())
}
}

View File

@@ -6,10 +6,14 @@ use std::{
use anyhow::{bail, ensure, Context, Result};
use ppc750cl::{Ins, Opcode};
use crate::util::{
executor::{disassemble, uniq_jump_table_entries, ExecCbData, ExecCbResult, Executor, VMState},
obj::{ObjInfo, ObjSection, ObjSectionKind},
vm::{BranchTarget, StepResult, VM},
use crate::{
analysis::{
disassemble,
executor::{ExecCbData, ExecCbResult, Executor},
uniq_jump_table_entries,
vm::{BranchTarget, StepResult, VM},
},
obj::{ObjInfo, ObjSection},
};
#[derive(Debug, Default, Clone)]
@@ -43,9 +47,6 @@ impl FunctionSlices {
}
pub fn add_block_start(&mut self, addr: u32) -> bool {
if addr == 0xFFFFFFFF {
panic!();
}
// Slice previous block.
if let Some((_, end)) = self.blocks.range_mut(..addr).last() {
let last_end = *end;
@@ -190,7 +191,7 @@ impl FunctionSlices {
self.blocks.insert(block_start, ins.addr + 4);
self.branches.insert(ins.addr, vec![addr]);
if addr == ins.addr {
// pass
// Infinite loop
} else if addr >= function_start
&& matches!(function_end, Some(known_end) if addr < known_end)
{
@@ -200,7 +201,7 @@ impl FunctionSlices {
}
} else if matches!(obj.section_data(ins.addr, ins.addr + 4), Ok((_, data)) if data == [0u8; 4])
{
// If this branch has 0'd padding after it, assume tail call.
// If this branch has zeroed padding after it, assume tail call.
self.function_references.insert(addr);
} else {
self.possible_blocks.insert(addr);
@@ -240,7 +241,7 @@ impl FunctionSlices {
Ok(ExecCbResult::EndBlock)
}
},
StepResult::Branch(mut branches) => {
StepResult::Branch(branches) => {
// End of block
self.blocks.insert(block_start, ins.addr + 4);
@@ -339,18 +340,14 @@ impl FunctionSlices {
ensure!(self.can_finalize(), "Can't finalize");
match (self.prologue, self.epilogue) {
(Some(p), Some(e)) => {
// log::info!("Prologue/epilogue pair: {:#010X} - {:#010X}", p, e);
}
(Some(p), None) => {
// log::info!("{:#010X?}", self);
// bail!("Unpaired prologue {:#010X}", p);
(Some(_), Some(_)) | (None, None) => {}
(Some(_), None) => {
// Likely __noreturn
}
(None, Some(e)) => {
log::info!("{:#010X?}", self);
bail!("Unpaired epilogue {:#010X}", e);
}
(None, None) => {}
}
let end = self.end();
@@ -408,7 +405,6 @@ impl FunctionSlices {
function_end: u32,
known_functions: &BTreeSet<u32>,
) -> TailCallResult {
// log::info!("Determing if {:#010X} is a tail call", addr);
// If jump target is already a known block or within known function bounds, not a tail call.
if self.blocks.contains_key(&addr) || (addr >= function_start && addr < function_end) {
return TailCallResult::Not;

View File

@@ -1,30 +1,21 @@
use std::{
collections::{BTreeMap, BTreeSet, VecDeque},
collections::{BTreeMap, BTreeSet},
mem::take,
};
use anyhow::{bail, Result};
use ppc750cl::{disasm_iter, Argument, Ins, Opcode};
use ppc750cl::Opcode;
use crate::util::{
executor::{uniq_jump_table_entries, ExecCbData, ExecCbResult, Executor},
obj::{
nested_push, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
ObjSymbolKind,
use crate::{
analysis::{
executor::{ExecCbData, ExecCbResult, Executor},
uniq_jump_table_entries,
vm::{is_store_op, BranchTarget, GprValue, StepResult, VM},
},
slices::FunctionSlices,
vm::{is_store_op, BranchTarget, GprValue, StepResult, VM},
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolKind},
util::nested::NestedVec,
};
#[derive(Debug, Eq, PartialEq)]
pub enum Label {
Local,
Global,
Data,
JumpTable,
VTable,
}
#[derive(Debug, Copy, Clone)]
pub enum Relocation {
Ha(u32),
@@ -44,15 +35,14 @@ pub enum DataKind {
Byte,
Float,
Double,
String,
String16,
// String,
// String16,
}
pub struct Tracker {
processed_functions: BTreeSet<u32>,
sda2_base: u32, // r2
sda_base: u32, // r13
labels: BTreeMap<u32, Label>,
pub relocations: BTreeMap<u32, Relocation>,
data_types: BTreeMap<u32, DataKind>,
stack_address: Option<u32>,
@@ -74,7 +64,6 @@ impl Tracker {
processed_functions: Default::default(),
sda2_base: obj.sda2_base.unwrap(),
sda_base: obj.sda_base.unwrap(),
labels: Default::default(),
relocations: Default::default(),
data_types: Default::default(),
stack_address: obj.stack_address,
@@ -101,7 +90,7 @@ impl Tracker {
pub fn process(&mut self, obj: &ObjInfo) -> Result<()> {
log::debug!("Processing code sections");
self.process_code(obj)?;
for (section_index, section) in obj.sections.iter().enumerate() {
for section in &obj.sections {
if matches!(section.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData) {
log::debug!("Processing section {}, address {:#X}", section.index, section.address);
self.process_data(obj, section)?;
@@ -110,31 +99,31 @@ 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 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();
@@ -156,10 +145,11 @@ impl Tracker {
}
}
// Special handling for gTRKInterruptVectorTable
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"),
) {}
// 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(())
}
@@ -194,7 +184,7 @@ impl Tracker {
function_end: u32,
possible_missed_branches: &mut BTreeMap<u32, Box<VM>>,
) -> Result<ExecCbResult<()>> {
let ExecCbData { executor, vm, result, section, ins, block_start } = data;
let ExecCbData { executor, vm, result, section: _, ins, block_start: _ } = data;
let is_function_addr = |addr: u32| addr >= function_start && addr < function_end;
match result {
@@ -236,13 +226,8 @@ impl Tracker {
}
Opcode::Ori => {
// ori rA, rS, UIMM
let source = ins.field_rS();
let target = ins.field_rA();
if let GprValue::Constant(value) = vm.gpr[target].value {
// if target == 1 {
// log::debug!("Stack address written from {:#010X}", ins.addr);
// self.update_stack_address(value);
// }
if self.is_valid_address(obj, ins.addr, value) {
if let (Some(hi_addr), Some(lo_addr)) =
(vm.gpr[target].hi_addr, vm.gpr[target].lo_addr)
@@ -444,18 +429,34 @@ impl Tracker {
{
return true;
}
if addr > 0x80000000 && addr < 0x80003100 {
return true;
}
// 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 {
return true;
// References to code sections will never be unaligned
return section.kind != ObjSectionKind::Code || addr & 3 == 0;
}
}
false
}
fn special_symbol(&self, obj: &mut ObjInfo, addr: u32) -> Option<usize> {
fn special_symbol(
&self,
obj: &mut ObjInfo,
addr: u32,
reloc_kind: ObjRelocKind,
) -> Option<usize> {
if !matches!(reloc_kind, ObjRelocKind::PpcAddr16Ha | ObjRelocKind::PpcAddr16Lo) {
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));
}
}
let mut check_symbol = |opt: Option<u32>, name: &str| -> Option<usize> {
if let Some(value) = opt {
if addr == value {
@@ -532,96 +533,96 @@ impl Tracker {
Relocation::Rel24(v) => (ObjRelocKind::PpcRel24, v),
Relocation::Absolute(v) => (ObjRelocKind::Absolute, v),
};
let (target_symbol, addend) = if let Some(symbol) = self.special_symbol(obj, target) {
(symbol, 0)
} else {
let target_section =
match obj.sections.iter().find(|s| {
let (target_symbol, addend) =
if let Some(symbol) = self.special_symbol(obj, target, reloc_kind) {
(symbol, 0)
} else {
let target_section = match obj.sections.iter().find(|s| {
target >= s.address as u32 && target < (s.address + s.size) as u32
}) {
Some(v) => v,
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() {
let symbol_idx = if symbol_idxs.len() == 1 {
symbol_idxs.first().cloned().unwrap()
} else {
let mut symbol_idxs = symbol_idxs.clone();
symbol_idxs.sort_by_key(|&symbol_idx| {
let symbol = &obj.symbols[symbol_idx];
let mut rank = match symbol.kind {
ObjSymbolKind::Function | ObjSymbolKind::Object => {
match reloc_kind {
// 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() {
let symbol_idx = if symbol_idxs.len() == 1 {
symbol_idxs.first().cloned().unwrap()
} else {
let mut symbol_idxs = symbol_idxs.clone();
symbol_idxs.sort_by_key(|&symbol_idx| {
let symbol = &obj.symbols[symbol_idx];
let mut rank = match symbol.kind {
ObjSymbolKind::Function | ObjSymbolKind::Object => {
match reloc_kind {
ObjRelocKind::PpcAddr16Hi
| ObjRelocKind::PpcAddr16Ha
| ObjRelocKind::PpcAddr16Lo => 1,
ObjRelocKind::Absolute
| ObjRelocKind::PpcRel24
| ObjRelocKind::PpcRel14
| ObjRelocKind::PpcEmbSda21 => 2,
}
}
// Label
ObjSymbolKind::Unknown => match reloc_kind {
ObjRelocKind::PpcAddr16Hi
| ObjRelocKind::PpcAddr16Ha
| ObjRelocKind::PpcAddr16Lo => 1,
ObjRelocKind::Absolute
| ObjRelocKind::PpcRel24
| ObjRelocKind::PpcRel14
| ObjRelocKind::PpcEmbSda21 => 2,
}
| ObjRelocKind::PpcAddr16Lo
if !symbol.name.starts_with("..") =>
{
3
}
_ => 1,
},
ObjSymbolKind::Section => -1,
};
if symbol.size > 0 {
rank += 1;
}
// Label
ObjSymbolKind::Unknown => match reloc_kind {
ObjRelocKind::PpcAddr16Hi
| ObjRelocKind::PpcAddr16Ha
| ObjRelocKind::PpcAddr16Lo
if !symbol.name.starts_with("..") =>
{
3
}
_ => 1,
},
ObjSymbolKind::Section => -1,
};
if symbol.size > 0 {
rank += 1;
-rank
});
match symbol_idxs.first().cloned() {
Some(v) => v,
None => continue,
}
-rank
});
match symbol_idxs.first().cloned() {
Some(v) => v,
None => continue,
}
};
let symbol = &obj.symbols[symbol_idx];
if symbol.address == target as u64 {
result = Some(symbol_idx);
break;
}
if symbol.size > 0 {
if symbol.address + symbol.size > target as u64 {
};
let symbol = &obj.symbols[symbol_idx];
if symbol.address == target as u64 {
result = Some(symbol_idx);
break;
}
if symbol.size > 0 {
if symbol.address + symbol.size > target as u64 {
result = Some(symbol_idx);
}
break;
}
break;
}
result
};
if let Some(symbol_idx) = target_symbol {
let symbol = &obj.symbols[symbol_idx];
(symbol_idx, target as i64 - symbol.address as i64)
} else {
// Create a new label
let symbol_idx = obj.symbols.len();
obj.symbols.push(ObjSymbol {
name: format!("lbl_{:08X}", target),
demangled_name: None,
address: target as u64,
section: Some(target_section.index),
size: 0,
size_known: false,
flags: Default::default(),
kind: Default::default(),
});
sym_map.nested_push(target, symbol_idx);
(symbol_idx, 0)
}
result
};
if let Some(symbol_idx) = target_symbol {
let symbol = &obj.symbols[symbol_idx];
(symbol_idx, target as i64 - symbol.address as i64)
} else {
// Create a new label
let symbol_idx = obj.symbols.len();
obj.symbols.push(ObjSymbol {
name: format!("lbl_{:08X}", target),
demangled_name: None,
address: target as u64,
section: Some(target_section.index),
size: 0,
size_known: false,
flags: Default::default(),
kind: Default::default(),
});
nested_push(sym_map, target, symbol_idx);
(symbol_idx, 0)
}
};
let reloc = ObjReloc { kind: reloc_kind, address: addr as u64, target_symbol, addend };
let section = match obj
.sections
@@ -660,8 +661,6 @@ impl Tracker {
}
}
fn is_branch_with_link(ins: &Ins) -> bool { ins.is_branch() && ins.field_LK() }
fn data_kind_from_op(op: Opcode) -> DataKind {
match op {
Opcode::Lbz => DataKind::Byte,

View File

@@ -2,7 +2,7 @@ use std::num::NonZeroU32;
use ppc750cl::{Argument, Ins, Opcode, GPR};
use crate::util::obj::ObjInfo;
use crate::obj::ObjInfo;
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub enum GprValue {
@@ -684,7 +684,7 @@ mod tests {
fn test_load_indexed_3() {
let mut vm = VM::new();
assert_eq!(vm.step(&Ins::new(0x28000127, 0x800ed458)), StepResult::Continue); // cmplwi r0, 0x127
assert_eq!(vm.cr, Cr {
assert_eq!(vm.cr[0], Cr {
signed: false,
left: GprValue::Unknown,
right: GprValue::Constant(295),

View File

@@ -1,14 +1,16 @@
use std::{
collections::{btree_map::Entry, BTreeMap},
fs::File,
io::{BufRead, BufReader, BufWriter, Write},
io::{BufRead, BufWriter, Write},
path::PathBuf,
};
use anyhow::{anyhow, bail, Context, Result};
use anyhow::{anyhow, bail, Result};
use argh::FromArgs;
use object::{Object, ObjectSymbol, SymbolScope};
use crate::util::file::{buf_reader, map_file};
#[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing static libraries.
#[argh(subcommand, name = "ar")]
@@ -49,10 +51,7 @@ fn create(args: CreateArgs) -> Result<()> {
path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?;
match path_str.strip_prefix('@') {
Some(rsp_file) => {
let reader = BufReader::new(
File::open(rsp_file)
.with_context(|| format!("Failed to open file '{rsp_file}'"))?,
);
let reader = buf_reader(rsp_file)?;
for result in reader.lines() {
let line = result?;
if !line.is_empty() {
@@ -79,11 +78,8 @@ fn create(args: CreateArgs) -> Result<()> {
Entry::Vacant(e) => e.insert(Vec::new()),
Entry::Occupied(_) => bail!("Duplicate file name '{path_str}'"),
};
let object_file = File::open(path)
.with_context(|| format!("Failed to open object file '{}'", path.display()))?;
let map = unsafe { memmap2::MmapOptions::new().map(&object_file) }
.with_context(|| format!("Failed to mmap object file: '{}'", path.display()))?;
let obj = object::File::parse(map.as_ref())?;
let mmap = map_file(path)?;
let obj = object::File::parse(&*mmap)?;
for symbol in obj.symbols() {
if symbol.scope() == SymbolScope::Dynamic {
entries.push(symbol.name_bytes()?.to_vec());
@@ -93,8 +89,13 @@ fn create(args: CreateArgs) -> Result<()> {
// Write archive
let out = BufWriter::new(File::create(&args.out)?);
let mut builder =
ar::GnuBuilder::new(out, identifiers, ar::GnuSymbolTableFormat::Size32, symbol_table)?;
let mut builder = ar::GnuBuilder::new_with_symbol_table(
out,
true,
identifiers,
ar::GnuSymbolTableFormat::Size32,
symbol_table,
)?;
for path in files {
let path_str =
path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?;

View File

@@ -1,30 +1,37 @@
use std::{
collections::BTreeMap,
fs::File,
io::{BufRead, BufReader, BufWriter},
fs,
fs::{DirBuilder, File},
io::{BufRead, BufWriter, Write},
path::{Path, PathBuf},
};
use std::collections::{hash_map, HashMap};
use anyhow::{anyhow, bail, Context, Result};
use argh::FromArgs;
use crate::util::{
cfa::{
locate_sda_bases, AnalysisPass, AnalyzerState, FindSaveRestSleds,
FindTRKInterruptVectorTable,
use crate::{
analysis::{
cfa::AnalyzerState,
pass::{AnalysisPass, FindSaveRestSleds, FindTRKInterruptVectorTable},
read_u32,
tracker::Tracker,
},
config::{parse_symbol_line, write_symbols},
dol::process_dol,
elf::process_elf,
executor::read_u32,
map::process_map,
obj::{
ObjInfo, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags,
ObjSymbolKind,
signatures::{apply_signature, check_signatures, check_signatures_str, parse_signatures},
split::split_obj,
ObjInfo, ObjRelocKind, ObjSectionKind, ObjSymbolKind,
},
util::{
asm::write_asm,
config::{apply_splits, parse_symbol_line, write_symbols},
dol::process_dol,
elf::process_elf,
file::{map_file, map_reader},
map::process_map,
},
sigs::check_signatures,
tracker::Tracker,
};
use crate::util::elf::write_elf;
#[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing DOL files.
@@ -42,7 +49,7 @@ enum SubCommand {
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
/// Disassembles a DOL file.
/// disassembles a DOL file
#[argh(subcommand, name = "disasm")]
pub struct DisasmArgs {
#[argh(option, short = 'm')]
@@ -51,12 +58,18 @@ pub struct DisasmArgs {
#[argh(option, short = 's')]
/// path to symbols file
symbols_file: Option<PathBuf>,
#[argh(option, short = 'p')]
/// path to splits file
splits_file: Option<PathBuf>,
#[argh(option, short = 'e')]
/// ELF file to validate against (debugging only)
elf_file: Option<PathBuf>,
#[argh(positional)]
/// DOL file
dol_file: PathBuf,
#[argh(option, short = 'o')]
/// output file (or directory, if splitting)
out: PathBuf,
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
@@ -76,152 +89,201 @@ pub fn run(args: Args) -> Result<()> {
}
const SIGNATURES: &[(&str, &str)] = &[
("__init_registers", include_str!("../../assets/__init_registers.yml")),
("__init_hardware", include_str!("../../assets/__init_hardware.yml")),
("__init_data", include_str!("../../assets/__init_data.yml")),
("__set_debug_bba", include_str!("../../assets/__set_debug_bba.yml")),
("__OSPSInit", include_str!("../../assets/__OSPSInit.yml")),
("__OSFPRInit", include_str!("../../assets/__OSFPRInit.yml")),
("__OSCacheInit", include_str!("../../assets/__OSCacheInit.yml")),
("DMAErrorHandler", include_str!("../../assets/DMAErrorHandler.yml")),
("DBInit", include_str!("../../assets/DBInit.yml")),
("OSInit", include_str!("../../assets/OSInit.yml")),
("__OSThreadInit", include_str!("../../assets/__OSThreadInit.yml")),
("__OSInitIPCBuffer", include_str!("../../assets/__OSInitIPCBuffer.yml")),
("EXIInit", include_str!("../../assets/EXIInit.yml")),
("EXIGetID", include_str!("../../assets/EXIGetID.yml")),
("exit", include_str!("../../assets/exit.yml")),
("_ExitProcess", include_str!("../../assets/_ExitProcess.yml")),
("__fini_cpp", include_str!("../../assets/__fini_cpp.yml")),
("__destroy_global_chain", include_str!("../../assets/__destroy_global_chain.yml")),
("InitMetroTRK", include_str!("../../assets/InitMetroTRK.yml")),
("InitMetroTRKCommTable", include_str!("../../assets/InitMetroTRKCommTable.yml")),
("OSExceptionInit", include_str!("../../assets/OSExceptionInit.yml")),
("OSDefaultExceptionHandler", include_str!("../../assets/OSDefaultExceptionHandler.yml")),
("__OSUnhandledException", include_str!("../../assets/__OSUnhandledException.yml")),
("OSDisableScheduler", include_str!("../../assets/OSDisableScheduler.yml")),
("__OSReschedule", include_str!("../../assets/__OSReschedule.yml")),
("__OSInitSystemCall", include_str!("../../assets/__OSInitSystemCall.yml")),
("OSInitAlarm", include_str!("../../assets/OSInitAlarm.yml")),
("__OSInitAlarm", include_str!("../../assets/__OSInitAlarm.yml")),
("__OSEVStart", include_str!("../../assets/OSExceptionVector.yml")),
("__OSDBINTSTART", include_str!("../../assets/__OSDBIntegrator.yml")),
("__OSDBJUMPSTART", include_str!("../../assets/__OSDBJump.yml")),
("SIInit", include_str!("../../assets/SIInit.yml")),
("SIGetType", include_str!("../../assets/SIGetType.yml")),
("SISetSamplingRate", include_str!("../../assets/SISetSamplingRate.yml")),
("SISetXY", include_str!("../../assets/SISetXY.yml")),
("VIGetTvFormat", include_str!("../../assets/VIGetTvFormat.yml")),
("DVDInit", include_str!("../../assets/DVDInit.yml")),
("DVDSetAutoFatalMessaging", include_str!("../../assets/DVDSetAutoFatalMessaging.yml")),
("OSSetArenaLo", include_str!("../../assets/OSSetArenaLo.yml")),
("OSSetArenaHi", include_str!("../../assets/OSSetArenaHi.yml")),
("OSSetMEM1ArenaLo", include_str!("../../assets/OSSetMEM1ArenaLo.yml")),
("OSSetMEM1ArenaHi", include_str!("../../assets/OSSetMEM1ArenaHi.yml")),
("OSSetMEM2ArenaLo", include_str!("../../assets/OSSetMEM2ArenaLo.yml")),
("OSSetMEM2ArenaHi", include_str!("../../assets/OSSetMEM2ArenaHi.yml")),
("__OSInitAudioSystem", include_str!("../../assets/__OSInitAudioSystem.yml")),
("__OSInitMemoryProtection", include_str!("../../assets/__OSInitMemoryProtection.yml")),
// ("BATConfig", include_str!("../../assets/BATConfig.yml")), TODO
("ReportOSInfo", include_str!("../../assets/ReportOSInfo.yml")),
("__check_pad3", include_str!("../../assets/__check_pad3.yml")),
("OSResetSystem", include_str!("../../assets/OSResetSystem.yml")),
("OSReturnToMenu", include_str!("../../assets/OSReturnToMenu.yml")),
("__OSReturnToMenu", include_str!("../../assets/__OSReturnToMenu.yml")),
("__OSShutdownDevices", include_str!("../../assets/__OSShutdownDevices.yml")),
("__OSInitSram", include_str!("../../assets/__OSInitSram.yml")),
("__OSSyncSram", include_str!("../../assets/__OSSyncSram.yml")),
("__OSGetExceptionHandler", include_str!("../../assets/__OSGetExceptionHandler.yml")),
("OSRegisterResetFunction", include_str!("../../assets/OSRegisterResetFunction.yml")),
("OSRegisterShutdownFunction", include_str!("../../assets/OSRegisterShutdownFunction.yml")),
("DecrementerExceptionHandler", include_str!("../../assets/DecrementerExceptionHandler.yml")),
("DecrementerExceptionCallback", include_str!("../../assets/DecrementerExceptionCallback.yml")),
("__OSInterruptInit", include_str!("../../assets/__OSInterruptInit.yml")),
("__OSContextInit", include_str!("../../assets/__OSContextInit.yml")),
("OSSwitchFPUContext", include_str!("../../assets/OSSwitchFPUContext.yml")),
("OSReport", include_str!("../../assets/OSReport.yml")),
("TRK_main", include_str!("../../assets/TRK_main.yml")),
("TRKNubWelcome", include_str!("../../assets/TRKNubWelcome.yml")),
("TRKInitializeNub", include_str!("../../assets/TRKInitializeNub.yml")),
("TRKInitializeIntDrivenUART", include_str!("../../assets/TRKInitializeIntDrivenUART.yml")),
("TRKEXICallBack", include_str!("../../assets/TRKEXICallBack.yml")),
("TRKLoadContext", include_str!("../../assets/TRKLoadContext.yml")),
("TRKInterruptHandler", include_str!("../../assets/TRKInterruptHandler.yml")),
("TRKExceptionHandler", include_str!("../../assets/TRKExceptionHandler.yml")),
("TRKSaveExtended1Block", include_str!("../../assets/TRKSaveExtended1Block.yml")),
("TRKNubMainLoop", include_str!("../../assets/TRKNubMainLoop.yml")),
("TRKTargetContinue", include_str!("../../assets/TRKTargetContinue.yml")),
("TRKSwapAndGo", include_str!("../../assets/TRKSwapAndGo.yml")),
("TRKRestoreExtended1Block", include_str!("../../assets/TRKRestoreExtended1Block.yml")),
("__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/TRKInterruptHandlerEnableInterrupts.yml"),
include_str!("../../assets/signatures/TRKInterruptHandlerEnableInterrupts.yml"),
),
("memset", include_str!("../../assets/memset.yml")),
("memset", include_str!("../../assets/signatures/memset.yml")),
(
"__msl_runtime_constraint_violation_s",
include_str!("../../assets/__msl_runtime_constraint_violation_s.yml"),
include_str!("../../assets/signatures/__msl_runtime_constraint_violation_s.yml"),
),
("ClearArena", include_str!("../../assets/ClearArena.yml")),
("IPCCltInit", include_str!("../../assets/IPCCltInit.yml")),
("__OSInitSTM", include_str!("../../assets/__OSInitSTM.yml")),
("IOS_Open", include_str!("../../assets/IOS_Open.yml")),
("__ios_Ipc2", include_str!("../../assets/__ios_Ipc2.yml")),
("IPCiProfQueueReq", include_str!("../../assets/IPCiProfQueueReq.yml")),
("SCInit", include_str!("../../assets/SCInit.yml")),
("SCReloadConfFileAsync", include_str!("../../assets/SCReloadConfFileAsync.yml")),
("NANDPrivateOpenAsync", include_str!("../../assets/NANDPrivateOpenAsync.yml")),
("nandIsInitialized", include_str!("../../assets/nandIsInitialized.yml")),
("nandOpen", include_str!("../../assets/nandOpen.yml")),
("nandGenerateAbsPath", include_str!("../../assets/nandGenerateAbsPath.yml")),
("nandGetHeadToken", include_str!("../../assets/nandGetHeadToken.yml")),
("ISFS_OpenAsync", include_str!("../../assets/ISFS_OpenAsync.yml")),
("nandConvertErrorCode", include_str!("../../assets/nandConvertErrorCode.yml")),
("NANDLoggingAddMessageAsync", include_str!("../../assets/NANDLoggingAddMessageAsync.yml")),
("__NANDPrintErrorMessage", include_str!("../../assets/__NANDPrintErrorMessage.yml")),
("__OSInitNet", include_str!("../../assets/__OSInitNet.yml")),
("__DVDCheckDevice", include_str!("../../assets/__DVDCheckDevice.yml")),
("__OSInitPlayTime", include_str!("../../assets/__OSInitPlayTime.yml")),
("__OSStartPlayRecord", include_str!("../../assets/__OSStartPlayRecord.yml")),
("NANDInit", include_str!("../../assets/NANDInit.yml")),
("ISFS_OpenLib", include_str!("../../assets/ISFS_OpenLib.yml")),
("ESP_GetTitleId", include_str!("../../assets/ESP_GetTitleId.yml")),
("NANDSetAutoErrorMessaging", include_str!("../../assets/NANDSetAutoErrorMessaging.yml")),
("__DVDFSInit", include_str!("../../assets/__DVDFSInit.yml")),
("__DVDClearWaitingQueue", include_str!("../../assets/__DVDClearWaitingQueue.yml")),
("__DVDInitWA", include_str!("../../assets/__DVDInitWA.yml")),
("__DVDLowSetWAType", include_str!("../../assets/__DVDLowSetWAType.yml")),
("__fstLoad", include_str!("../../assets/__fstLoad.yml")),
("DVDReset", include_str!("../../assets/DVDReset.yml")),
("DVDLowReset", include_str!("../../assets/DVDLowReset.yml")),
("DVDReadDiskID", include_str!("../../assets/DVDReadDiskID.yml")),
("stateReady", include_str!("../../assets/stateReady.yml")),
("DVDLowWaitCoverClose", include_str!("../../assets/DVDLowWaitCoverClose.yml")),
("__DVDStoreErrorCode", include_str!("../../assets/__DVDStoreErrorCode.yml")),
("DVDLowStopMotor", include_str!("../../assets/DVDLowStopMotor.yml")),
("DVDGetDriveStatus", include_str!("../../assets/DVDGetDriveStatus.yml")),
("printf", include_str!("../../assets/printf.yml")),
("sprintf", include_str!("../../assets/sprintf.yml")),
("vprintf", include_str!("../../assets/vprintf.yml")),
("vsprintf", include_str!("../../assets/vsprintf.yml")),
("vsnprintf", include_str!("../../assets/vsnprintf.yml")),
("__pformatter", include_str!("../../assets/__pformatter.yml")),
("longlong2str", include_str!("../../assets/longlong2str.yml")),
("__mod2u", include_str!("../../assets/__mod2u.yml")),
("__FileWrite", include_str!("../../assets/__FileWrite.yml")),
("fwrite", include_str!("../../assets/fwrite.yml")),
("__fwrite", include_str!("../../assets/__fwrite.yml")),
("__stdio_atexit", include_str!("../../assets/__stdio_atexit.yml")),
("__StringWrite", include_str!("../../assets/__StringWrite.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")),
];
pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> {
let entry = obj.entry as u32;
check_signatures(obj, entry, include_str!("../../assets/__start.yml"))?;
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.iter().find(|symbol| symbol.name == name) {
let addr = symbol.address as u32;
check_signatures(obj, addr, sig_str)?;
if let Some(signature) = check_signatures_str(obj, addr, sig_str)? {
apply_signature(obj, addr, &signature)?;
}
}
}
if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == "__init_user") {
@@ -229,7 +291,12 @@ pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> {
let mut analyzer = AnalyzerState::default();
analyzer.process_function_at(&obj, symbol.address as u32)?;
for addr in analyzer.function_entries {
if check_signatures(obj, addr, include_str!("../../assets/__init_cpp.yml"))? {
if let Some(signature) = check_signatures_str(
obj,
addr,
include_str!("../../assets/signatures/__init_cpp.yml"),
)? {
apply_signature(obj, addr, &signature)?;
break;
}
}
@@ -240,7 +307,13 @@ pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> {
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 {
check_signatures(obj, target, include_str!("../../assets/__init_cpp_exceptions.yml"))?;
if let Some(signature) = check_signatures_str(
obj,
target,
include_str!("../../assets/signatures/__init_cpp_exceptions.yml"),
)? {
apply_signature(obj, target, &signature)?;
}
}
}
if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == "_dtors") {
@@ -249,12 +322,34 @@ pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> {
let target = read_u32(&section.data, symbol.address as u32 + 4, section.address as u32)
.ok_or_else(|| anyhow!("Failed to read _dtors data"))?;
if target != 0 {
check_signatures(obj, target, include_str!("../../assets/__fini_cpp_exceptions.yml"))?;
if let Some(signature) = check_signatures_str(
obj,
target,
include_str!("../../assets/signatures/__fini_cpp_exceptions.yml"),
)? {
apply_signature(obj, target, &signature)?;
}
}
}
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)?;
for symbol in obj.symbols.iter().filter(|symbol| symbol.kind == ObjSymbolKind::Function) {
let addr = symbol.address as u32;
if let Some(signature) = check_signatures(obj, addr, &signatures)? {
apply_signature(obj, addr, &signature)?;
break;
}
}
}
log::info!("Done!");
Ok(())
}
fn info(args: InfoArgs) -> Result<()> {
let mut obj = process_dol(&args.dol_file)?;
apply_signatures(&mut obj)?;
@@ -288,6 +383,8 @@ fn info(args: InfoArgs) -> Result<()> {
FindSaveRestSleds::execute(&mut state, &obj)?;
state.apply(&mut obj)?;
apply_signatures_post(&mut obj)?;
println!("{}:", obj.name);
println!("Entry point: {:#010X}", obj.entry);
println!("\nSections:");
@@ -329,20 +426,20 @@ fn disasm(args: DisasmArgs) -> Result<()> {
// }
if let Some(map) = &args.map_file {
let mut reader = BufReader::new(
File::open(map)
.with_context(|| format!("Failed to open map file '{}'", map.display()))?,
);
let _entries = process_map(&mut reader)?;
let mmap = map_file(map)?;
let _entries = process_map(map_reader(&mmap))?;
}
if let Some(splits_file) = &args.splits_file {
let map = map_file(splits_file)?;
apply_splits(map_reader(&map), &mut obj)?;
}
let mut state = AnalyzerState::default();
if let Some(symbols_path) = &args.symbols_file {
let mut reader = BufReader::new(File::open(symbols_path).with_context(|| {
format!("Failed to open symbols file '{}'", symbols_path.display())
})?);
for result in reader.lines() {
let map = map_file(symbols_path)?;
for result in map_reader(&map).lines() {
let line = match result {
Ok(line) => line,
Err(e) => bail!("Failed to process symbols file: {e:?}"),
@@ -407,10 +504,66 @@ fn disasm(args: DisasmArgs) -> Result<()> {
log::info!("Applying relocations");
tracker.apply(&mut obj, false)?;
//
// log::info!("Writing disassembly");
// let mut w = BufWriter::new(File::create("out.s")?);
// write_asm(&mut w, &obj)?;
if args.splits_file.is_some() {
log::info!("Splitting {} objects", obj.link_order.len());
let split_objs = split_obj(&obj)?;
// Create out dirs
let asm_dir = args.out.join("asm");
let include_dir = args.out.join("include");
let obj_dir = args.out.join("expected");
DirBuilder::new().recursive(true).create(&include_dir)?;
fs::write(include_dir.join("macros.inc"), include_bytes!("../../assets/macros.inc"))?;
log::info!("Writing object files");
let mut file_map = HashMap::<String, Vec<u8>>::new();
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
let out_obj = write_elf(split_obj)?;
match file_map.entry(unit.clone()) {
hash_map::Entry::Vacant(e) => e.insert(out_obj),
hash_map::Entry::Occupied(_) => bail!("Duplicate file {unit}"),
};
}
let mut rsp_file = BufWriter::new(File::create("rsp")?);
for unit in &obj.link_order {
let object = file_map
.get(unit)
.ok_or_else(|| anyhow!("Failed to find object file for unit '{unit}'"))?;
let out_path = obj_dir.join(unit);
writeln!(rsp_file, "{}", out_path.display())?;
if let Some(parent) = out_path.parent() {
DirBuilder::new().recursive(true).create(parent)?;
}
let mut file = File::create(&out_path)
.with_context(|| format!("Failed to create '{}'", out_path.display()))?;
file.write_all(object)?;
file.flush()?;
}
rsp_file.flush()?;
log::info!("Writing disassembly");
let mut files_out = File::create(args.out.join("link_order.txt"))?;
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
let out_path = asm_dir.join(format!("{}.s", unit.trim_end_matches(".o")));
if let Some(parent) = out_path.parent() {
DirBuilder::new().recursive(true).create(parent)?;
}
let mut w = BufWriter::new(File::create(out_path)?);
write_asm(&mut w, split_obj)?;
w.flush()?;
writeln!(files_out, "{}", unit)?;
}
files_out.flush()?;
} else {
log::info!("Writing disassembly");
let mut w = BufWriter::new(File::create("out.s")?);
write_asm(&mut w, &obj)?;
}
if let Some(symbols_path) = &args.symbols_file {
let mut symbols_writer = BufWriter::new(
@@ -500,6 +653,7 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
);
}
}
return Ok(()); // TODO
for real_section in &real_obj.sections {
let obj_section = match obj.sections.get(real_section.index) {
Some(v) => v,
@@ -562,20 +716,17 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
}
for (&obj_addr, obj_reloc) in &obj_map {
let obj_symbol = &obj.symbols[obj_reloc.target_symbol];
let real_reloc = match real_map.get(&obj_addr) {
Some(v) => v,
None => {
log::warn!(
"Relocation not real @ {:#010X} {:?} to {:#010X}+{:X} ({})",
obj_addr,
obj_reloc.kind,
obj_symbol.address,
obj_reloc.addend,
obj_symbol.demangled_name.as_ref().unwrap_or(&obj_symbol.name)
);
continue;
}
};
if !real_map.contains_key(&obj_addr) {
log::warn!(
"Relocation not real @ {:#010X} {:?} to {:#010X}+{:X} ({})",
obj_addr,
obj_reloc.kind,
obj_symbol.address,
obj_reloc.addend,
obj_symbol.demangled_name.as_ref().unwrap_or(&obj_symbol.name)
);
continue;
}
}
}
Ok(())

View File

@@ -1,9 +1,9 @@
use std::{
collections::{btree_map, btree_map::Entry, hash_map, BTreeMap, HashMap, HashSet},
collections::{btree_map, hash_map, BTreeMap, HashMap},
fs,
fs::{DirBuilder, File},
io::{BufRead, BufReader, BufWriter, Write},
path::{Path, PathBuf},
path::PathBuf,
};
use anyhow::{anyhow, bail, ensure, Context, Result};
@@ -13,18 +13,19 @@ use object::{
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionFlags,
SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection,
};
use ppc750cl::Ins;
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
use crate::util::{
asm::write_asm,
config::write_symbols,
elf::{process_elf, write_elf},
obj::{ObjKind, ObjReloc, ObjRelocKind, ObjSymbolFlagSet, ObjSymbolKind},
sigs::{check_signature, compare_signature, generate_signature, FunctionSignature},
split::split_obj,
tracker::Tracker,
use crate::{
obj::{
signatures::{compare_signature, generate_signature, FunctionSignature},
split::split_obj,
ObjKind,
},
util::{
asm::write_asm,
config::{write_splits, write_symbols},
elf::{process_elf, write_elf},
file::buf_reader,
},
};
#[derive(FromArgs, PartialEq, Debug)]
@@ -91,6 +92,9 @@ pub struct ConfigArgs {
#[argh(positional)]
/// output directory
out_dir: PathBuf,
#[argh(option, short = 'm')]
/// path to obj_files.mk
obj_files: Option<PathBuf>,
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
@@ -120,15 +124,43 @@ pub fn run(args: Args) -> Result<()> {
fn config(args: ConfigArgs) -> Result<()> {
log::info!("Loading {}", args.in_file.display());
let mut obj = process_elf(&args.in_file)?;
let obj = process_elf(&args.in_file)?;
DirBuilder::new().recursive(true).create(&args.out_dir)?;
let symbols_path = args.out_dir.join("symbols.txt");
let mut symbols_writer = BufWriter::new(
File::create(&symbols_path)
.with_context(|| format!("Failed to create '{}'", symbols_path.display()))?,
);
write_symbols(&mut symbols_writer, &obj)?;
{
let symbols_path = args.out_dir.join("symbols.txt");
let mut symbols_writer = BufWriter::new(
File::create(&symbols_path)
.with_context(|| format!("Failed to create '{}'", symbols_path.display()))?,
);
write_symbols(&mut symbols_writer, &obj)?;
}
{
let obj_files = if let Some(path) = &args.obj_files {
Some(
BufReader::new(
File::open(path)
.with_context(|| format!("Failed to open '{}'", path.display()))?,
)
.lines()
.filter(|line| match line {
Ok(line) => line.contains(".o"),
Err(_) => false,
})
.map(|result| result.unwrap())
.collect::<Vec<String>>(),
)
} else {
None
};
let splits_path = args.out_dir.join("splits.txt");
let mut splits_writer = BufWriter::new(
File::create(&splits_path)
.with_context(|| format!("Failed to create '{}'", splits_path.display()))?,
);
write_splits(&mut splits_writer, &obj, obj_files)?;
}
Ok(())
}
@@ -449,10 +481,7 @@ fn signatures(args: SignaturesArgs) -> Result<()> {
path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?;
match path_str.strip_prefix('@') {
Some(rsp_file) => {
let reader = BufReader::new(
File::open(rsp_file)
.with_context(|| format!("Failed to open file '{rsp_file}'"))?,
);
let reader = buf_reader(rsp_file)?;
for result in reader.lines() {
let line = result?;
if !line.is_empty() {
@@ -466,10 +495,10 @@ fn signatures(args: SignaturesArgs) -> Result<()> {
}
}
let mut signatures: HashMap<Vec<u8>, FunctionSignature> = HashMap::new();
let mut signatures: HashMap<String, FunctionSignature> = HashMap::new();
for path in files {
log::info!("Processing {}", path.display());
let (data, signature) = match generate_signature(&path, &args.symbol) {
let signature = match generate_signature(&path, &args.symbol) {
Ok(Some(signature)) => signature,
Ok(None) => continue,
Err(e) => {
@@ -478,13 +507,13 @@ fn signatures(args: SignaturesArgs) -> Result<()> {
}
};
log::info!("Comparing hash {}", signature.hash);
if let Some((_, existing)) = signatures.iter_mut().find(|(a, b)| *a == &data) {
if let Some(existing) = signatures.get_mut(&signature.hash) {
compare_signature(existing, &signature)?;
} else {
signatures.insert(data, signature);
signatures.insert(signature.hash.clone(), signature);
}
}
let mut signatures = signatures.into_iter().map(|(a, b)| b).collect::<Vec<FunctionSignature>>();
let mut signatures = signatures.into_values().collect::<Vec<FunctionSignature>>();
log::info!("{} unique signatures", signatures.len());
signatures.sort_by_key(|s| s.signature.len());
let out =

View File

@@ -1,23 +1,25 @@
use std::{
fs::File,
io::{BufWriter, Seek, SeekFrom, Write},
path::PathBuf,
};
use anyhow::{anyhow, bail, ensure, Context, Result};
use argh::FromArgs;
use memmap2::MmapOptions;
use object::{Architecture, Endianness, Object, ObjectKind, ObjectSection, SectionKind};
use crate::util::file::map_file;
#[derive(FromArgs, PartialEq, Eq, Debug)]
/// Converts an ELF file to a DOL file.
#[argh(subcommand, name = "elf2dol")]
pub struct Args {
#[argh(positional)]
/// path to input ELF
elf_file: String,
elf_file: PathBuf,
#[argh(positional)]
/// path to output DOL
dol_file: String,
dol_file: PathBuf,
}
#[derive(Debug, Clone, Default)]
@@ -42,10 +44,7 @@ const MAX_TEXT_SECTIONS: usize = 7;
const MAX_DATA_SECTIONS: usize = 11;
pub fn run(args: Args) -> Result<()> {
let elf_file = File::open(&args.elf_file)
.with_context(|| format!("Failed to open ELF file '{}'", args.elf_file))?;
let map = unsafe { MmapOptions::new().map(&elf_file) }
.with_context(|| format!("Failed to mmap ELF file: '{}'", args.elf_file))?;
let map = map_file(&args.elf_file)?;
let obj_file = object::read::File::parse(&*map)?;
match obj_file.architecture() {
Architecture::PowerPc => {}
@@ -61,7 +60,7 @@ pub fn run(args: Args) -> Result<()> {
let mut offset = 0x100u32;
let mut out = BufWriter::new(
File::create(&args.dol_file)
.with_context(|| format!("Failed to create DOL file '{}'", args.dol_file))?,
.with_context(|| format!("Failed to create DOL file '{}'", args.dol_file.display()))?,
);
out.seek(SeekFrom::Start(offset as u64))?;

View File

@@ -1,9 +1,12 @@
use std::{fs::File, io::BufReader};
use std::path::PathBuf;
use anyhow::{bail, ensure, Context, Result};
use anyhow::{bail, ensure, Result};
use argh::FromArgs;
use crate::util::map::{process_map, resolve_link_order, SymbolEntry, SymbolRef};
use crate::util::{
file::{map_file, map_reader},
map::{process_map, resolve_link_order, SymbolEntry, SymbolRef},
};
#[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing CodeWarrior maps.
@@ -29,7 +32,7 @@ enum SubCommand {
pub struct EntriesArgs {
#[argh(positional)]
/// path to input map
map_file: String,
map_file: PathBuf,
#[argh(positional)]
/// TU to display entries for
unit: String,
@@ -41,7 +44,7 @@ pub struct EntriesArgs {
pub struct SymbolArgs {
#[argh(positional)]
/// path to input map
map_file: String,
map_file: PathBuf,
#[argh(positional)]
/// symbol to display references for
symbol: String,
@@ -53,7 +56,7 @@ pub struct SymbolArgs {
pub struct OrderArgs {
#[argh(positional)]
/// path to input map
map_file: String,
map_file: PathBuf,
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
@@ -62,7 +65,7 @@ pub struct OrderArgs {
pub struct SlicesArgs {
#[argh(positional)]
/// path to input map
map_file: String,
map_file: PathBuf,
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
@@ -71,7 +74,7 @@ pub struct SlicesArgs {
pub struct SymbolsArgs {
#[argh(positional)]
/// path to input map
map_file: String,
map_file: PathBuf,
}
pub fn run(args: Args) -> Result<()> {
@@ -85,11 +88,8 @@ pub fn run(args: Args) -> Result<()> {
}
fn entries(args: EntriesArgs) -> Result<()> {
let reader = BufReader::new(
File::open(&args.map_file)
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
);
let entries = process_map(reader)?;
let map = map_file(&args.map_file)?;
let entries = process_map(map_reader(&map))?;
match entries.unit_entries.get_vec(&args.unit) {
Some(vec) => {
for symbol_ref in vec {
@@ -109,11 +109,8 @@ fn entries(args: EntriesArgs) -> Result<()> {
}
fn symbol(args: SymbolArgs) -> Result<()> {
let reader = BufReader::new(
File::open(&args.map_file)
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
);
let entries = process_map(reader)?;
let map = map_file(&args.map_file)?;
let entries = process_map(map_reader(&map))?;
let mut opt_ref: Option<(SymbolRef, SymbolEntry)> = None;
for (symbol_ref, entry) in &entries.symbols {
if symbol_ref.name == args.symbol {
@@ -164,11 +161,8 @@ fn symbol(args: SymbolArgs) -> Result<()> {
}
fn order(args: OrderArgs) -> Result<()> {
let reader = BufReader::new(
File::open(&args.map_file)
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
);
let entries = process_map(reader)?;
let map = map_file(&args.map_file)?;
let entries = process_map(map_reader(&map))?;
let order = resolve_link_order(&entries.unit_order)?;
for unit in order {
println!("{unit}");
@@ -177,11 +171,8 @@ fn order(args: OrderArgs) -> Result<()> {
}
fn slices(args: SlicesArgs) -> Result<()> {
let reader = BufReader::new(
File::open(&args.map_file)
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
);
let entries = process_map(reader)?;
let map = map_file(&args.map_file)?;
let entries = process_map(map_reader(&map))?;
let order = resolve_link_order(&entries.unit_order)?;
for unit in order {
let unit_path = if let Some((lib, name)) = unit.split_once(' ') {
@@ -210,11 +201,8 @@ fn slices(args: SlicesArgs) -> Result<()> {
}
fn symbols(args: SymbolsArgs) -> Result<()> {
let reader = BufReader::new(
File::open(&args.map_file)
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
);
let _entries = process_map(reader)?;
let map = map_file(&args.map_file)?;
let _entries = process_map(map_reader(&map))?;
// for (address, symbol) in entries.address_to_symbol {
// if symbol.name.starts_with('@') {
// continue;

View File

@@ -1,3 +1,5 @@
use std::path::PathBuf;
use anyhow::{bail, ensure, Context, Result};
use argh::FromArgs;
use memchr::memmem;
@@ -9,18 +11,19 @@ use memmap2::MmapOptions;
pub struct Args {
#[argh(positional)]
/// path to source binary
binary: String,
binary: PathBuf,
#[argh(positional)]
/// path to build info string
build_info: String,
build_info: PathBuf,
}
const BUILD_STRING_MAX: usize = 35;
const BUILD_STRING_TAG: &str = "!#$MetroidBuildInfo!#$";
pub fn run(args: Args) -> Result<()> {
let build_string = std::fs::read_to_string(&args.build_info)
.with_context(|| format!("Failed to read build info string from '{}'", args.build_info))?;
let build_string = std::fs::read_to_string(&args.build_info).with_context(|| {
format!("Failed to read build info string from '{}'", args.build_info.display())
})?;
let build_string_trim = build_string.trim_end();
let build_string_bytes = build_string_trim.as_bytes();
ensure!(
@@ -28,13 +31,12 @@ pub fn run(args: Args) -> Result<()> {
"Build string '{build_string_trim}' is greater than maximum size of {BUILD_STRING_MAX}"
);
let binary_file = std::fs::File::options()
.read(true)
.write(true)
.open(&args.binary)
.with_context(|| format!("Failed to open binary for writing: '{}'", args.binary))?;
let binary_file =
std::fs::File::options().read(true).write(true).open(&args.binary).with_context(|| {
format!("Failed to open binary for writing: '{}'", args.binary.display())
})?;
let mut map = unsafe { MmapOptions::new().map_mut(&binary_file) }
.with_context(|| format!("Failed to mmap binary: '{}'", args.binary))?;
.with_context(|| format!("Failed to mmap binary: '{}'", args.binary.display()))?;
let start = match memmem::find(&map, BUILD_STRING_TAG.as_bytes()) {
Some(idx) => idx + BUILD_STRING_TAG.as_bytes().len(),
None => bail!("Failed to find build string tag in binary"),

View File

@@ -1,9 +1,10 @@
pub(crate) mod ar;
pub(crate) mod demangle;
pub(crate) mod dol;
pub(crate) mod elf;
pub(crate) mod elf2dol;
pub(crate) mod map;
pub(crate) mod metroidbuildinfo;
pub(crate) mod rel;
pub(crate) mod shasum;
pub mod ar;
pub mod demangle;
pub mod dol;
pub mod elf;
pub mod elf2dol;
pub mod map;
pub mod metroidbuildinfo;
pub mod rel;
pub mod rso;
pub mod shasum;

View File

@@ -1,25 +1,28 @@
use std::{
collections::{btree_map, BTreeMap},
fs::File,
io::{BufWriter, Write},
io::Write,
path::PathBuf,
};
use anyhow::{anyhow, bail, ensure, Context, Result};
use anyhow::{bail, ensure, Context, Result};
use argh::FromArgs;
use crate::{
analysis::{
cfa::AnalyzerState,
pass::{AnalysisPass, FindSaveRestSleds, FindTRKInterruptVectorTable},
tracker::Tracker,
},
cmd::dol::apply_signatures,
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolKind},
util::{
dol::process_dol,
elf::write_elf,
obj::{ObjInfo, ObjSection, ObjSymbol},
nested::{NestedMap, NestedVec},
rel::process_rel,
},
};
use crate::util::cfa::{AnalysisPass, AnalyzerState, FindSaveRestSleds, FindTRKInterruptVectorTable};
use crate::util::obj::{nested_push, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbolKind};
use crate::util::tracker::Tracker;
#[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing REL files.
@@ -111,12 +114,7 @@ fn merge(args: MergeArgs) -> Result<()> {
file_offset: mod_section.file_offset,
section_known: mod_section.section_known,
});
nested_try_insert(
&mut section_map,
module.module_id,
mod_section.elf_index as u32,
offset,
)?;
section_map.nested_insert(module.module_id, mod_section.elf_index as u32, offset)?;
let symbols = module.symbols_for_section(mod_section.index);
for (_, mod_symbol) in symbols {
obj.symbols.push(ObjSymbol {
@@ -159,7 +157,7 @@ fn merge(args: MergeArgs) -> Result<()> {
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_addr).rev() {
for (_addr, symbol_idxs) in sym_map.range(..=target_addr).rev() {
let symbol_idx = if symbol_idxs.len() == 1 {
symbol_idxs.first().cloned().unwrap()
} else {
@@ -183,10 +181,10 @@ fn merge(args: MergeArgs) -> Result<()> {
ObjRelocKind::PpcAddr16Hi
| ObjRelocKind::PpcAddr16Ha
| ObjRelocKind::PpcAddr16Lo
if !symbol.name.starts_with("..") =>
{
3
}
if !symbol.name.starts_with("..") =>
{
3
}
_ => 1,
},
ObjSymbolKind::Section => -1,
@@ -231,7 +229,7 @@ fn merge(args: MergeArgs) -> Result<()> {
flags: Default::default(),
kind: Default::default(),
});
nested_push(sym_map, target_addr, symbol_idx);
sym_map.nested_push(target_addr, symbol_idx);
(symbol_idx, 0)
};
obj.sections[target_section_index].relocations.push(ObjReloc {
@@ -288,25 +286,3 @@ fn merge(args: MergeArgs) -> Result<()> {
file.flush()?;
Ok(())
}
#[inline]
fn nested_try_insert<T1, T2, T3>(
map: &mut BTreeMap<T1, BTreeMap<T2, T3>>,
v1: T1,
v2: T2,
v3: T3,
) -> Result<()>
where
T1: Eq + Ord,
T2: Eq + Ord,
{
let map = match map.entry(v1) {
btree_map::Entry::Occupied(entry) => entry.into_mut(),
btree_map::Entry::Vacant(entry) => entry.insert(Default::default()),
};
match map.entry(v2) {
btree_map::Entry::Occupied(_) => bail!("Entry already exists"),
btree_map::Entry::Vacant(entry) => entry.insert(v3),
};
Ok(())
}

41
src/cmd/rso.rs Normal file
View File

@@ -0,0 +1,41 @@
use std::path::PathBuf;
use anyhow::Result;
use argh::FromArgs;
use crate::util::rso::process_rso;
#[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing RSO files.
#[argh(subcommand, name = "rso")]
pub struct Args {
#[argh(subcommand)]
command: SubCommand,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)]
enum SubCommand {
Info(InfoArgs),
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
/// Views RSO file information.
#[argh(subcommand, name = "info")]
pub struct InfoArgs {
#[argh(positional)]
/// RSO file
rso_file: PathBuf,
}
pub fn run(args: Args) -> Result<()> {
match args.command {
SubCommand::Info(c_args) => info(c_args),
}
}
fn info(args: InfoArgs) -> Result<()> {
let rso = process_rso(&args.rso_file)?;
println!("Read RSO module {}", rso.name);
Ok(())
}

View File

@@ -1,7 +1,7 @@
use std::{
fs::{File, OpenOptions},
io::{BufRead, BufReader, Read},
path::Path,
path::{Path, PathBuf},
};
use anyhow::{anyhow, bail, Context, Result};
@@ -18,17 +18,17 @@ pub struct Args {
check: bool,
#[argh(positional)]
/// path to file
file: String,
file: PathBuf,
#[argh(option, short = 'o')]
/// touch output file on successful check
output: Option<String>,
output: Option<PathBuf>,
}
const DEFAULT_BUF_SIZE: usize = 8192;
pub fn run(args: Args) -> Result<()> {
let file =
File::open(&args.file).with_context(|| format!("Failed to open file '{}'", args.file))?;
let file = File::open(&args.file)
.with_context(|| format!("Failed to open file '{}'", args.file.display()))?;
if args.check {
check(args, file)
} else {
@@ -69,7 +69,8 @@ fn check(args: Args, file: File) -> Result<()> {
std::process::exit(1);
}
if let Some(out_path) = args.output {
touch(&out_path).with_context(|| format!("Failed to touch output file '{out_path}'"))?;
touch(&out_path)
.with_context(|| format!("Failed to touch output file '{}'", out_path.display()))?;
}
Ok(())
}
@@ -79,7 +80,7 @@ fn hash(args: Args, file: File) -> Result<()> {
let mut hash_buf = [0u8; 40];
let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf)
.map_err(|e| anyhow!("Failed to encode hash: {e}"))?;
println!("{} {}", hash_str, args.file);
println!("{} {}", hash_str, args.file.display());
Ok(())
}

View File

@@ -1,8 +1,10 @@
use argh::FromArgs;
mod argh_version;
mod cmd;
mod util;
pub mod analysis;
pub mod argh_version;
pub mod cmd;
pub mod obj;
pub mod util;
#[derive(FromArgs, PartialEq, Debug)]
/// GameCube/Wii decompilation project tools.
@@ -22,6 +24,7 @@ enum SubCommand {
Map(cmd::map::Args),
MetroidBuildInfo(cmd::metroidbuildinfo::Args),
Rel(cmd::rel::Args),
Rso(cmd::rso::Args),
Shasum(cmd::shasum::Args),
}
@@ -38,6 +41,7 @@ fn main() {
SubCommand::Map(c_args) => cmd::map::run(c_args),
SubCommand::MetroidBuildInfo(c_args) => cmd::metroidbuildinfo::run(c_args),
SubCommand::Rel(c_args) => cmd::rel::run(c_args),
SubCommand::Rso(c_args) => cmd::rso::run(c_args),
SubCommand::Shasum(c_args) => cmd::shasum::run(c_args),
};
if let Err(e) = result {

View File

@@ -1,18 +1,18 @@
pub mod signatures;
pub mod split;
use std::{
cmp::min,
collections::{btree_map, BTreeMap},
fmt,
hash::{Hash, Hasher},
};
use std::marker::PhantomData;
use anyhow::{anyhow, bail, Result};
use flagset::{flags, FlagSet};
use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
use serde_yaml::Value;
use serde_repr::{Serialize_repr, Deserialize_repr};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::util::rel::RelReloc;
use crate::util::{nested::NestedVec, rel::RelReloc};
flags! {
#[repr(u8)]
@@ -26,7 +26,7 @@ flags! {
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct ObjSymbolFlagSet(pub(crate) FlagSet<ObjSymbolFlags>);
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
#[allow(clippy::derive_hash_xor_eq)]
impl Hash for ObjSymbolFlagSet {
fn hash<H: Hasher>(&self, state: &mut H) { self.0.bits().hash(state) }
@@ -103,7 +103,8 @@ pub struct ObjInfo {
pub arena_hi: Option<u32>,
// Extracted
pub splits: BTreeMap<u32, String>,
pub splits: BTreeMap<u32, Vec<String>>,
pub named_sections: BTreeMap<u32, String>,
pub link_order: Vec<String>,
// From extab
@@ -146,7 +147,7 @@ impl ObjInfo {
pub fn build_symbol_map(&self, section_idx: usize) -> Result<BTreeMap<u32, Vec<usize>>> {
let mut symbols = BTreeMap::<u32, Vec<usize>>::new();
for (symbol_idx, symbol) in self.symbols_for_section(section_idx) {
nested_push(&mut symbols, symbol.address as u32, symbol_idx);
symbols.nested_push(symbol.address as u32, symbol_idx);
}
Ok(symbols)
}
@@ -187,16 +188,3 @@ impl ObjSection {
Ok(relocations)
}
}
#[inline]
pub fn nested_push<T1, T2>(map: &mut BTreeMap<T1, Vec<T2>>, v1: T1, v2: T2)
where T1: Ord {
match map.entry(v1) {
btree_map::Entry::Occupied(mut e) => {
e.get_mut().push(v2);
}
btree_map::Entry::Vacant(e) => {
e.insert(vec![v2]);
}
}
}

View File

@@ -6,17 +6,16 @@ use std::{
use anyhow::{anyhow, bail, ensure, Result};
use base64::{engine::general_purpose::STANDARD, Engine};
use cwdemangle::{demangle, DemangleOptions};
use ppc750cl::Ins;
use serde::{forward_to_deserialize_any, Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use sha1::{Digest, Sha1};
use crate::util::{
elf::process_elf,
use crate::{
analysis::tracker::{Relocation, Tracker},
array_ref,
obj::{
ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
ObjSymbolFlags, ObjSymbolKind,
ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolKind,
},
tracker::{Relocation, Tracker},
util::elf::process_elf,
};
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
@@ -45,18 +44,6 @@ pub struct FunctionSignature {
pub relocations: Vec<OutReloc>,
}
/// Creates a fixed-size array reference from a slice.
#[macro_export]
macro_rules! array_ref {
($slice:expr, $offset:expr, $size:expr) => {{
#[inline]
fn to_array<T>(slice: &[T]) -> &[T; $size] {
unsafe { &*(slice.as_ptr() as *const [_; $size]) }
}
to_array(&$slice[$offset..$offset + $size])
}};
}
pub fn check_signature(mut data: &[u8], sig: &FunctionSignature) -> Result<bool> {
let sig_data = STANDARD.decode(&sig.signature)?;
// println!(
@@ -79,28 +66,47 @@ pub fn check_signature(mut data: &[u8], sig: &FunctionSignature) -> Result<bool>
Ok(true)
}
pub fn check_signatures(obj: &mut ObjInfo, addr: u32, sig_str: &str) -> Result<bool> {
let signatures: Vec<FunctionSignature> = serde_yaml::from_str(sig_str)?;
pub fn parse_signatures(sig_str: &str) -> Result<Vec<FunctionSignature>> {
Ok(serde_yaml::from_str(sig_str)?)
}
pub fn check_signatures_str(
obj: &ObjInfo,
addr: u32,
sig_str: &str,
) -> Result<Option<FunctionSignature>> {
check_signatures(obj, addr, &parse_signatures(sig_str)?)
}
pub fn check_signatures(
obj: &ObjInfo,
addr: u32,
signatures: &Vec<FunctionSignature>,
) -> Result<Option<FunctionSignature>> {
let (_, data) = obj.section_data(addr, 0)?;
let mut name = None;
for signature in &signatures {
for signature in signatures {
if name.is_none() {
name = Some(signature.symbols[signature.symbol].name.clone());
}
if check_signature(data, signature)? {
log::debug!("Found {} @ {:#010X}", signature.symbols[signature.symbol].name, addr);
apply_signature(obj, addr, signature)?;
return Ok(true);
log::debug!(
"Found {} @ {:#010X} (hash {})",
signature.symbols[signature.symbol].name,
addr,
signature.hash
);
return Ok(Some(signature.clone()));
}
}
if let Some(name) = name {
log::debug!("Didn't find {} @ {:#010X}", name, addr);
}
Ok(false)
// if let Some(name) = name {
// log::debug!("Didn't find {} @ {:#010X}", name, addr);
// }
Ok(None)
}
pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> Result<usize> {
let target_section_index = obj.section_at(target).ok().map(|section| section.index);
let mut target_section_index = obj.section_at(target).ok().map(|section| section.index);
if let Some(target_section_index) = target_section_index {
let target_section = &mut obj.sections[target_section_index];
if !target_section.section_known {
@@ -119,21 +125,26 @@ pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> R
}
}
}
if sig_symbol.kind == ObjSymbolKind::Unknown
&& (sig_symbol.name.starts_with("_f_") || sig_symbol.name.starts_with("_SDA"))
{
// Hack to mark linker generated symbols as ABS
target_section_index = None;
}
let target_symbol_idx = if let Some((symbol_idx, existing)) =
obj.symbols.iter_mut().enumerate().find(|(_, symbol)| {
symbol.address == target as u64
&& symbol.kind == sig_symbol.kind
// HACK to avoid replacing different ABS symbols
// Hack to avoid replacing different ABS symbols
&& (symbol.section.is_some() || symbol.name == sig_symbol.name)
}) {
// TODO apply to existing
log::debug!("Replacing {:?} with {}", existing, sig_symbol.name);
log::debug!("Replacing {:?} with {:?}", existing, sig_symbol);
*existing = ObjSymbol {
name: sig_symbol.name.clone(),
demangled_name: demangle(&sig_symbol.name, &DemangleOptions::default()),
address: target as u64,
section: target_section_index,
size: if existing.size_known { existing.size } else { sig_symbol.size as u64 },
size: if sig_symbol.size == 0 { existing.size } else { sig_symbol.size as u64 },
size_known: existing.size_known || sig_symbol.size != 0,
flags: sig_symbol.flags,
kind: sig_symbol.kind,
@@ -256,10 +267,7 @@ pub fn compare_signature(existing: &mut FunctionSignature, new: &FunctionSignatu
Ok(())
}
pub fn generate_signature(
path: &Path,
symbol_name: &str,
) -> Result<Option<(Vec<u8>, FunctionSignature)>> {
pub fn generate_signature(path: &Path, symbol_name: &str) -> Result<Option<FunctionSignature>> {
let mut out_symbols: Vec<OutSymbol> = Vec::new();
let mut out_relocs: Vec<OutReloc> = Vec::new();
let mut symbol_map: BTreeMap<usize, usize> = BTreeMap::new();
@@ -270,10 +278,17 @@ pub fn generate_signature(
|| obj.stack_address.is_none()
|| obj.stack_end.is_none()
|| obj.db_stack_addr.is_none()
// || obj.arena_hi.is_none()
// || obj.arena_lo.is_none()
{
log::warn!("Failed to locate all abs symbols {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?}", obj.sda2_base, obj.sda_base, obj.stack_address, obj.stack_end, obj.db_stack_addr, obj.arena_hi, obj.arena_lo);
log::warn!(
"Failed to locate all abs symbols {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?}",
obj.sda2_base,
obj.sda_base,
obj.stack_address,
obj.stack_end,
obj.db_stack_addr,
obj.arena_hi,
obj.arena_lo
);
return Ok(None);
}
let mut tracker = Tracker::new(&obj);
@@ -285,7 +300,6 @@ pub fn generate_signature(
if symbol.name != symbol_name && symbol.name != symbol_name.replace("TRK", "TRK_") {
continue;
}
// log::info!("Tracking {}", symbol.name);
tracker.process_function(&obj, symbol)?;
}
tracker.apply(&mut obj, true)?; // true
@@ -370,28 +384,16 @@ pub fn generate_signature(
kind: reloc.kind,
symbol: symbol_idx,
addend: reloc.addend as i32,
// instruction: format!("{}", Ins::new(*ins, addr).simplified()),
});
}
// println!("{}", Ins::new(*ins, addr).simplified());
}
// if out_symbols.is_empty() || out_relocs.is_empty() {
// bail!("Failed to locate any symbols or relocs");
// }
// println!("Data: {:#010X?}", instructions);
let mut data = vec![0u8; instructions.len() * 8];
for (idx, &(ins, pat)) in instructions.iter().enumerate() {
data[idx * 8..idx * 8 + 4].copy_from_slice(&ins.to_be_bytes());
data[idx * 8 + 4..idx * 8 + 8].copy_from_slice(&pat.to_be_bytes());
}
// println!(
// "OK: Data (len {}): {:X?} | SYMBOLS: {:?} | RELOCS: {:?}",
// data.len(),
// data,
// out_symbols,
// out_relocs
// );
let encoded = STANDARD.encode(&data);
let mut hasher = Sha1::new();
hasher.update(&data);
@@ -399,13 +401,13 @@ pub fn generate_signature(
let mut hash_buf = [0u8; 40];
let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf)
.map_err(|e| anyhow!("Failed to encode hash: {e}"))?;
return Ok(Some((data, FunctionSignature {
return Ok(Some(FunctionSignature {
symbol: 0,
hash: hash_str.to_string(),
signature: encoded,
symbols: out_symbols,
relocations: out_relocs,
})));
}));
}
Ok(None)
}

View File

@@ -2,7 +2,7 @@ use std::{cmp::min, collections::HashMap};
use anyhow::{anyhow, bail, ensure, Result};
use crate::util::obj::{
use crate::obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol,
};
@@ -32,6 +32,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
arena_lo: None,
arena_hi: None,
splits: Default::default(),
named_sections: Default::default(),
link_order: vec![],
known_functions: Default::default(),
unresolved_relocations: vec![],
@@ -41,6 +42,9 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
for (section_idx, section) in obj.sections.iter().enumerate() {
let mut current_address = section.address as u32;
let mut section_end = (section.address + section.size) as u32;
// if matches!(section.name.as_str(), "extab" | "extabindex") {
// continue;
// }
// .ctors and .dtors end with a linker-generated null pointer,
// adjust section size appropriately
if matches!(section.name.as_str(), ".ctors" | ".dtors")
@@ -48,7 +52,11 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
{
section_end -= 4;
}
let mut file_iter = obj.splits.range(current_address..).peekable();
let mut file_iter = obj
.splits
.range(current_address..)
.flat_map(|(addr, v)| v.iter().map(move |u| (addr, u)))
.peekable();
// Build address to relocation / address to symbol maps
let relocations = section.build_relocation_map()?;
@@ -72,8 +80,20 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
file_addr
);
let mut file_end = section_end;
if let Some(&(&next_addr, _)) = file_iter.peek() {
file_end = min(next_addr, section_end);
let mut dont_go_forward = false;
if let Some(&(&next_addr, next_unit)) = file_iter.peek() {
if file_addr == next_addr {
log::warn!("Duplicating {} in {unit} and {next_unit}", section.name);
dont_go_forward = true;
file_end = obj
.splits
.range(current_address + 1..)
.next()
.map(|(&addr, _)| addr)
.unwrap_or(section_end);
} else {
file_end = min(next_addr, section_end);
}
}
let file = name_to_obj
@@ -147,8 +167,13 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
..(file_end as u64 - section.address) as usize]
.to_vec(),
};
let name = if let Some(name) = obj.named_sections.get(&current_address) {
name.clone()
} else {
section.name.clone()
};
file.sections.push(ObjSection {
name: section.name.clone(),
name,
kind: section.kind,
address: 0,
size: file_end as u64 - current_address as u64,
@@ -162,7 +187,9 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
section_known: true,
});
current_address = file_end;
if !dont_go_forward {
current_address = file_end;
}
}
}
@@ -186,6 +213,17 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
..Default::default()
});
reloc.target_symbol = out_sym_idx;
if matches!(section.name.as_str(), "extab" | "extabindex") {
log::warn!(
"Extern relocation @ {:#010X} {} ({:#010X} {}): {:#010X} {}",
reloc.address + section.original_address,
section.name,
section.original_address,
out_obj.name,
target_sym.address,
target_sym.demangled_name.as_deref().unwrap_or(&target_sym.name)
);
}
}
}
}
@@ -237,10 +275,73 @@ fn default_section_align(section: &ObjSection) -> u64 {
/// Linker-generated symbols to extern
#[inline]
fn is_skip_symbol(name: &str) -> bool { matches!(name, "_ctors" | "_dtors") }
fn is_skip_symbol(name: &str) -> bool {
matches!(
name,
"_ctors"
| "_dtors"
| "_f_init"
| "_f_init_rom"
| "_e_init"
| "_fextab"
| "_fextab_rom"
| "_eextab"
| "_fextabindex"
| "_fextabindex_rom"
| "_eextabindex"
| "_f_text"
| "_f_text_rom"
| "_e_text"
| "_f_ctors"
| "_f_ctors_rom"
| "_e_ctors"
| "_f_dtors"
| "_f_dtors_rom"
| "_e_dtors"
| "_f_rodata"
| "_f_rodata_rom"
| "_e_rodata"
| "_f_data"
| "_f_data_rom"
| "_e_data"
| "_f_sdata"
| "_f_sdata_rom"
| "_e_sdata"
| "_f_sbss"
| "_f_sbss_rom"
| "_e_sbss"
| "_f_sdata2"
| "_f_sdata2_rom"
| "_e_sdata2"
| "_f_sbss2"
| "_f_sbss2_rom"
| "_e_sbss2"
| "_f_bss"
| "_f_bss_rom"
| "_e_bss"
| "_f_stack"
| "_f_stack_rom"
| "_e_stack"
| "_stack_addr"
| "_stack_end"
| "_db_stack_addr"
| "_db_stack_end"
| "_heap_addr"
| "_heap_end"
| "_nbfunctions"
| "SIZEOF_HEADERS"
| "_SDA_BASE_"
| "_SDA2_BASE_"
| "_ABS_SDA_BASE_"
| "_ABS_SDA2_BASE_"
)
}
/// Linker generated symbols to strip entirely
#[inline]
fn is_linker_symbol(name: &str) -> bool {
matches!(name, "_eti_init_info" | "_rom_copy_info" | "_bss_init_info")
matches!(
name,
"_eti_init_info" | "_rom_copy_info" | "_bss_init_info" | "_ctors$99" | "_dtors$99"
)
}

View File

@@ -7,9 +7,12 @@ use std::{
use anyhow::{anyhow, bail, ensure, Result};
use ppc750cl::{disasm_iter, Argument, Ins, Opcode};
use crate::util::obj::{
nested_push, ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
ObjSymbolFlags, ObjSymbolKind,
use crate::{
obj::{
ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlags,
ObjSymbolKind,
},
util::nested::NestedVec,
};
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
@@ -42,12 +45,12 @@ pub fn write_asm<W: Write>(w: &mut W, obj: &ObjInfo) -> Result<()> {
if symbol.kind == ObjSymbolKind::Section {
continue;
}
nested_push(&mut entries, symbol.address as u32, SymbolEntry {
entries.nested_push(symbol.address as u32, SymbolEntry {
index: symbol_index,
kind: SymbolEntryKind::Start,
});
if symbol.size > 0 {
nested_push(&mut entries, (symbol.address + symbol.size) as u32, SymbolEntry {
entries.nested_push((symbol.address + symbol.size) as u32, SymbolEntry {
index: symbol_index,
kind: SymbolEntryKind::End,
});
@@ -68,7 +71,7 @@ pub fn write_asm<W: Write>(w: &mut W, obj: &ObjInfo) -> Result<()> {
}
// Replace section-relative jump relocations (generated by GCC)
// These aren't possible to express accurately in GNU assembler
// These aren't always possible to express accurately in GNU assembler
if matches!(relocations.get(&ins.addr), Some(reloc) if reloc.addend == 0) {
continue;
}
@@ -452,10 +455,12 @@ fn write_data<W: Write>(
if symbol_kind == ObjSymbolKind::Function {
ensure!(
current_address & 3 == 0 && data.len() & 3 == 0,
"Unaligned code write @ {} {:#010X} size {:#X}",
"Unaligned code write @ {} {:#010X} size {:#X} (next entry: {:?}, reloc: {:?})",
section.name,
current_address,
data.len()
data.len(),
entry,
reloc,
);
write_code_chunk(w, symbols, entries, relocations, section, current_address, data)?;
} else {
@@ -635,7 +640,7 @@ fn write_section_header<W: Write>(
write!(w, ".section {}", section.name)?;
write!(w, ", \"a\", @nobits")?;
}
".ctors" | ".dtors" | "extab" | "extabindex" => {
".ctors" | ".dtors" | ".ctors$10" | ".dtors$10" | ".dtors$15" | "extab" | "extabindex" => {
write!(w, ".section {}", section.name)?;
write!(w, ", \"a\"")?;
}
@@ -671,8 +676,7 @@ fn write_reloc_symbol<W: Write>(
}
fn write_symbol_name<W: Write>(w: &mut W, name: &str) -> std::io::Result<()> {
// TODO more?
if name.contains('@') || name.contains('<') {
if name.contains('@') || name.contains('<') || name.contains('\\') {
write!(w, "\"{name}\"")?;
} else {
write!(w, "{name}")?;

View File

@@ -1,14 +1,17 @@
use std::{io::Write, num::ParseIntError, ops::BitAndAssign};
use std::{
io::{BufRead, Write},
iter,
num::ParseIntError,
str::FromStr,
};
use anyhow::{anyhow, bail, Result};
use cwdemangle::{demangle, DemangleOptions};
use flagset::FlagSet;
use once_cell::sync::Lazy;
use regex::Regex;
use crate::util::obj::{
ObjInfo, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
};
use crate::obj::{ObjInfo, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind};
use crate::util::nested::NestedVec;
fn parse_hex(s: &str) -> Result<u32, ParseIntError> {
u32::from_str_radix(s.trim_start_matches("0x"), 16)
@@ -21,7 +24,7 @@ pub fn parse_symbol_line(line: &str, obj: &ObjInfo) -> Result<Option<ObjSymbol>>
)
.unwrap()
});
static COMMENT_LINE: Lazy<Regex> = Lazy::new(|| Regex::new("^\\s*//.*$").unwrap());
static COMMENT_LINE: Lazy<Regex> = Lazy::new(|| Regex::new("^\\s*(?://|#).*$").unwrap());
if let Some(captures) = SYMBOL_LINE.captures(&line) {
let name = captures["name"].to_string();
@@ -73,6 +76,7 @@ pub fn parse_symbol_line(line: &str, obj: &ObjInfo) -> Result<Option<ObjSymbol>>
}
fn is_skip_symbol(symbol: &ObjSymbol) -> bool {
let _ = symbol;
// symbol.name.starts_with("lbl_")
// || symbol.name.starts_with("func_")
// || symbol.name.starts_with("switch_")
@@ -177,3 +181,135 @@ fn symbol_flags_from_str(s: &str) -> Option<ObjSymbolFlags> {
_ => None,
}
}
pub fn write_splits<W: Write>(
w: &mut W,
obj: &ObjInfo,
obj_files: Option<Vec<String>>,
) -> Result<()> {
let mut obj_files_iter = obj_files.map(|v| v.into_iter());
for unit in &obj.link_order {
let obj_file = if let Some(obj_files_iter) = &mut obj_files_iter {
obj_files_iter.next()
} else {
None
};
log::info!("Processing {} (obj file {:?})", unit, obj_file);
if let Some(obj_file) = obj_file {
let trim_unit = unit
.trim_end_matches("_1")
.trim_end_matches(" (asm)")
.trim_end_matches(".o")
.trim_end_matches(".cpp")
.trim_end_matches(".c");
if !obj_file.contains(trim_unit) {
bail!("Unit mismatch: {} vs {}", unit, obj_file);
}
let trim_obj = obj_file
.trim_end_matches(" \\")
.trim_start_matches("\t$(BUILD_DIR)/")
.trim_start_matches("asm/")
.trim_start_matches("src/");
writeln!(w, "{}:", trim_obj)?;
} else {
writeln!(w, "{}:", unit)?;
}
let mut split_iter = obj.splits.iter()
.flat_map(|(addr, v)| v.iter().map(move |u| (addr, u))).peekable();
while let Some((&addr, it_unit)) = split_iter.next() {
if it_unit != unit {
continue;
}
let end = split_iter.peek().map(|(&addr, _)| addr).unwrap_or(u32::MAX);
let section = obj.section_at(addr)?;
writeln!(w, "\t{:<11} start:{:#010X} end:{:#010X}", section.name, addr, end)?;
// align:{}
}
writeln!(w)?;
}
Ok(())
}
enum SplitLine {
Unit { name: String },
Section { name: String, start: u32, end: u32, align: Option<u32> },
None,
}
fn parse_split_line(line: &str) -> Result<SplitLine> {
static UNIT_LINE: Lazy<Regex> =
Lazy::new(|| Regex::new("^\\s*(?P<name>[^\\s:]+)\\s*:\\s*$").unwrap());
static SECTION_LINE: Lazy<Regex> =
Lazy::new(|| Regex::new("^\\s*(?P<name>\\S+)\\s*(?P<attrs>.*)$").unwrap());
static COMMENT_LINE: Lazy<Regex> = Lazy::new(|| Regex::new("^\\s*(?://|#).*$").unwrap());
if line.is_empty() || COMMENT_LINE.is_match(line) {
Ok(SplitLine::None)
} else if let Some(captures) = UNIT_LINE.captures(&line) {
let name = captures["name"].to_string();
Ok(SplitLine::Unit { name })
} else if let Some(captures) = SECTION_LINE.captures(&line) {
let mut name = captures["name"].to_string();
let mut start: Option<u32> = None;
let mut end: Option<u32> = None;
let mut align: Option<u32> = None;
let attrs = captures["attrs"].split(' ');
for attr in attrs {
if let Some((attr, value)) = attr.split_once(':') {
match attr {
"start" => {
start = Some(parse_hex(&value)?);
}
"end" => {
end = Some(parse_hex(&value)?);
}
"align" => align = Some(u32::from_str(value)?),
"rename" => name = value.to_string(),
_ => bail!("Unknown attribute '{name}'"),
}
} else {
bail!("Unknown attribute '{attr}'")
}
}
if let (Some(start), Some(end)) = (start, end) {
Ok(SplitLine::Section { name, start, end, align })
} else {
Err(anyhow!("Missing attribute: '{line}'"))
}
} else {
Err(anyhow!("Failed to parse line: '{line}'"))
}
}
pub fn apply_splits<R: BufRead>(r: R, obj: &mut ObjInfo) -> Result<()> {
enum SplitState {
None,
Unit(String),
}
let mut state = SplitState::None;
for result in r.lines() {
let line = match result {
Ok(line) => line,
Err(e) => return Err(e.into()),
};
let split_line = parse_split_line(&line)?;
match (&mut state, split_line) {
(SplitState::None | SplitState::Unit(_), SplitLine::Unit { name }) => {
obj.link_order.push(name.clone());
state = SplitState::Unit(name);
}
(SplitState::None, SplitLine::Section { name, .. }) => {
bail!("Section {} defined outside of unit", name);
}
(SplitState::Unit(unit), SplitLine::Section { name, start, end, align }) => {
let _ = end;
let _ = align;
obj.splits.nested_push(start, unit.clone());
obj.named_sections.insert(start, name);
}
_ => {}
}
}
Ok(())
}

View File

@@ -1,14 +1,15 @@
use std::{collections::BTreeMap, fs::File, io::BufReader, path::Path};
use std::{collections::BTreeMap, path::Path};
use anyhow::{anyhow, bail, ensure, Result};
use dol::{Dol, DolSection, DolSectionType};
use crate::util::{
cfa::{locate_sda_bases, AnalyzerState},
use crate::{
analysis::cfa::locate_sda_bases,
obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
ObjSymbolFlags, ObjSymbolKind,
},
util::file::{map_file, map_reader},
};
const MAX_TEXT_SECTIONS: usize = 7;
@@ -28,7 +29,10 @@ pub fn process_dol<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
.and_then(|filename| filename.to_str())
.unwrap_or_default()
.to_string();
let dol = Dol::read_from(BufReader::new(File::open(path)?))?;
let dol = {
let mmap = map_file(path)?;
Dol::read_from(map_reader(&mmap))?
};
let mut obj = ObjInfo {
module_id: 0,
kind: ObjKind::Executable,
@@ -45,6 +49,7 @@ pub fn process_dol<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
arena_lo: None,
arena_hi: None,
splits: Default::default(),
named_sections: Default::default(),
link_order: vec![],
known_functions: Default::default(),
unresolved_relocations: vec![],
@@ -447,7 +452,7 @@ pub fn process_dol<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
}
// Locate _SDA2_BASE_ & _SDA_BASE_
let sda_bases = match locate_sda_bases(&mut obj) {
match locate_sda_bases(&mut obj) {
Ok(true) => {
let sda2_base = obj.sda2_base.unwrap();
let sda_base = obj.sda_base.unwrap();
@@ -478,7 +483,7 @@ pub fn process_dol<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
Err(e) => {
log::warn!("Failed to locate SDA bases: {:?}", e);
}
};
}
Ok(obj)
}

View File

@@ -380,6 +380,7 @@ pub fn read_debug_section<R: BufRead + Seek>(reader: &mut R) -> Result<TagMap> {
Ok(tags)
}
#[allow(unused)]
pub fn read_aranges_section<R: BufRead + Seek>(reader: &mut R) -> Result<()> {
let len = {
let old_pos = reader.stream_position()?;

View File

@@ -1,6 +1,5 @@
use std::{
collections::{btree_map::Entry, hash_map, BTreeMap, HashMap},
fs::File,
io::Cursor,
path::Path,
};
@@ -9,30 +8,32 @@ use anyhow::{anyhow, bail, ensure, Context, Result};
use cwdemangle::demangle;
use flagset::Flags;
use indexmap::IndexMap;
use memmap2::MmapOptions;
use object::{
elf,
elf::{SHF_ALLOC, SHF_EXECINSTR, SHF_WRITE, SHT_NOBITS, SHT_PROGBITS},
write::{
elf::{ProgramHeader, Rel, SectionHeader, SectionIndex, SymbolIndex},
Mangling, SectionId, StringId, SymbolId,
StringId,
},
Architecture, BinaryFormat, Endianness, Object, ObjectKind, ObjectSection, ObjectSymbol,
Relocation, RelocationEncoding, RelocationKind, RelocationTarget, Section, SectionKind, Symbol,
SymbolFlags, SymbolKind, SymbolScope, SymbolSection,
Architecture, Endianness, Object, ObjectKind, ObjectSection, ObjectSymbol, Relocation,
RelocationKind, RelocationTarget, Section, SectionKind, Symbol, SymbolKind, SymbolScope,
SymbolSection,
};
use crate::util::{
dwarf::{
process_address, process_type, read_debug_section, type_string, ud_type, ud_type_string,
AttributeKind, TagKind, TypeKind,
},
use crate::{
obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind,
ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
},
sigs::OutSymbol,
util::{
dwarf::{
process_address, process_type, read_debug_section, type_string, ud_type,
ud_type_string, AttributeKind, TagKind, TypeKind,
},
file::map_file,
},
};
use crate::util::nested::NestedVec;
enum BoundaryState {
/// Looking for a file symbol, any section symbols are queued
@@ -46,11 +47,8 @@ enum BoundaryState {
const ENABLE_DWARF: bool = false;
pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let elf_file = File::open(&path)
.with_context(|| format!("Failed to open ELF file '{}'", path.as_ref().display()))?;
let map = unsafe { MmapOptions::new().map(&elf_file) }
.with_context(|| format!("Failed to mmap ELF file: '{}'", path.as_ref().display()))?;
let obj_file = object::read::File::parse(&*map)?;
let mmap = map_file(path)?;
let obj_file = object::read::File::parse(&*mmap)?;
let architecture = match obj_file.architecture() {
Architecture::PowerPc => ObjArchitecture::PowerPc,
arch => bail!("Unexpected architecture: {arch:?}"),
@@ -105,12 +103,12 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
relocations: vec![],
original_address: 0, // TODO load from abs symbol
file_offset: section.file_range().map(|(v, _)| v).unwrap_or_default(),
section_known: true,
});
}
let mut symbols: Vec<ObjSymbol> = vec![];
let mut symbol_indexes: Vec<Option<usize>> = vec![];
let mut current_file: Option<String> = None;
let mut section_starts = IndexMap::<String, Vec<(u64, String)>>::new();
let mut name_to_index = HashMap::<String, usize>::new(); // for resolving duplicate names
let mut boundary_state = BoundaryState::LookForFile(Default::default());
@@ -170,7 +168,6 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
}
indexmap::map::Entry::Vacant(e) => e.insert(Default::default()),
};
current_file = Some(file_name.clone());
match &mut boundary_state {
BoundaryState::LookForFile(queue) => {
if queue.is_empty() {
@@ -199,10 +196,12 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
queue.push((symbol.address(), section_name));
}
BoundaryState::LookForSections(file_name) => {
let sections = section_starts
.get_mut(file_name)
.ok_or_else(|| anyhow!("Failed to create entry"))?;
sections.push((symbol.address(), section_name));
if section_indexes[section_index.0].is_some() {
let sections = section_starts
.get_mut(file_name)
.ok_or_else(|| anyhow!("Failed to create entry"))?;
sections.push((symbol.address(), section_name));
}
}
BoundaryState::FilesEnded => {
log::warn!(
@@ -216,28 +215,29 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
_ => match symbol.section() {
// Linker generated symbols indicate the end
SymbolSection::Absolute => {
current_file = None;
boundary_state = BoundaryState::FilesEnded;
}
SymbolSection::Section(section_index) => match &mut boundary_state {
BoundaryState::LookForFile(_) => {}
BoundaryState::LookForSections(file_name) => {
let sections = section_starts
.get_mut(file_name)
.ok_or_else(|| anyhow!("Failed to create entry"))?;
let section = obj_file.section_by_index(section_index)?;
let section_name = section.name()?;
if let Some((addr, _)) = sections
.iter_mut()
.find(|(addr, name)| *addr == 0 && name == section_name)
{
// If the section symbol had address 0, determine address
// from first symbol within that section.
*addr = symbol.address();
} else if !sections.iter().any(|(_, name)| name == section_name) {
// Otherwise, if there was no section symbol, assume this
// symbol indicates the section address.
sections.push((symbol.address(), section_name.to_string()));
if section_indexes[section_index.0].is_some() {
let sections = section_starts
.get_mut(file_name)
.ok_or_else(|| anyhow!("Failed to create entry"))?;
let section = obj_file.section_by_index(section_index)?;
let section_name = section.name()?;
if let Some((addr, _)) = sections
.iter_mut()
.find(|(addr, name)| *addr == 0 && name == section_name)
{
// If the section symbol had address 0, determine address
// from first symbol within that section.
*addr = symbol.address();
} else if !sections.iter().any(|(_, name)| name == section_name) {
// Otherwise, if there was no section symbol, assume this
// symbol indicates the section address.
sections.push((symbol.address(), section_name.to_string()));
}
}
}
BoundaryState::FilesEnded => {}
@@ -259,7 +259,7 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
}
let mut link_order = Vec::<String>::new();
let mut splits = BTreeMap::<u32, String>::new();
let mut splits = BTreeMap::<u32, Vec<String>>::new();
if kind == ObjKind::Executable {
// Link order is trivially deduced
for file_name in section_starts.keys() {
@@ -269,7 +269,7 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
// Create a map of address -> file splits
for (file_name, sections) in section_starts {
for (address, _) in sections {
splits.insert(address as u32, file_name.clone());
splits.nested_push(address as u32, file_name.clone());
}
}
@@ -309,6 +309,7 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
arena_lo,
arena_hi,
splits,
named_sections: Default::default(),
link_order,
known_functions: Default::default(),
unresolved_relocations: vec![],
@@ -332,6 +333,7 @@ pub fn write_elf(obj: &ObjInfo) -> Result<Vec<u8>> {
sym: object::write::elf::Sym,
}
writer.reserve_null_section_index();
let mut out_sections: Vec<OutSection> = Vec::with_capacity(obj.sections.len());
for section in &obj.sections {
let name = writer.add_section_name(section.name.as_bytes());
@@ -356,14 +358,15 @@ pub fn write_elf(obj: &ObjInfo) -> Result<Vec<u8>> {
}
}
let symtab = writer.reserve_symtab_section_index();
let shstrtab = writer.reserve_shstrtab_section_index();
let strtab = writer.reserve_strtab_section_index();
writer.reserve_shstrtab_section_index();
writer.reserve_strtab_section_index();
// Add symbols
let mut out_symbols: Vec<OutSymbol> = Vec::with_capacity(obj.symbols.len());
let mut symbol_offset = 0;
let mut num_local = 0;
if !obj.name.is_empty() {
// Add file symbol
let name_index = writer.add_string(obj.name.as_bytes());
let index = writer.reserve_symbol_index(None);
out_symbols.push(OutSymbol {
@@ -435,10 +438,7 @@ pub fn write_elf(obj: &ObjInfo) -> Result<Vec<u8>> {
if sym.st_info >> 4 == elf::STB_LOCAL {
num_local = writer.symbol_count();
}
out_symbols.push(OutSymbol {
index,
sym,
});
out_symbols.push(OutSymbol { index, sym });
}
writer.reserve_file_header();
@@ -540,27 +540,27 @@ pub fn write_elf(obj: &ObjInfo) -> Result<Vec<u8>> {
ObjRelocKind::PpcAddr16Hi => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_HI
},
}
ObjRelocKind::PpcAddr16Ha => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_HA
},
}
ObjRelocKind::PpcAddr16Lo => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_ADDR16_LO
},
}
ObjRelocKind::PpcRel24 => {
r_offset = (r_offset & !3);
r_offset = r_offset & !3;
elf::R_PPC_REL24
},
}
ObjRelocKind::PpcRel14 => {
r_offset = (r_offset & !3);
r_offset = r_offset & !3;
elf::R_PPC_REL14
},
}
ObjRelocKind::PpcEmbSda21 => {
r_offset = (r_offset & !3) + 2;
elf::R_PPC_EMB_SDA21
},
}
};
writer.write_relocation(true, &Rel {
r_offset,

57
src/util/file.rs Normal file
View File

@@ -0,0 +1,57 @@
use std::{
fs::File,
io::{BufReader, Cursor, Read},
path::Path,
};
use anyhow::{Context, Result};
use byteorder::ReadBytesExt;
use memmap2::{Mmap, MmapOptions};
/// Opens a memory mapped file.
pub fn map_file<P: AsRef<Path>>(path: P) -> Result<Mmap> {
let file = File::open(&path)
.with_context(|| format!("Failed to open file '{}'", path.as_ref().display()))?;
let map = unsafe { MmapOptions::new().map(&file) }
.with_context(|| format!("Failed to mmap file: '{}'", path.as_ref().display()))?;
Ok(map)
}
pub type Reader<'a> = Cursor<&'a [u8]>;
/// Creates a reader for the memory mapped file.
#[inline]
pub fn map_reader(mmap: &Mmap) -> Reader { Cursor::new(&*mmap) }
/// Creates a buffered reader around a file (not memory mapped).
pub fn buf_reader<P: AsRef<Path>>(path: P) -> Result<BufReader<File>> {
let file = File::open(&path)
.with_context(|| format!("Failed to open file '{}'", path.as_ref().display()))?;
Ok(BufReader::new(file))
}
/// Reads a string with known size at the specified offset.
pub fn read_string(reader: &mut Reader, off: u64, size: usize) -> Result<String> {
let mut data = vec![0u8; size];
let pos = reader.position();
reader.set_position(off);
reader.read_exact(&mut data)?;
reader.set_position(pos);
Ok(String::from_utf8(data)?)
}
/// Reads a zero-terminated string at the specified offset.
pub fn read_c_string(reader: &mut Reader, off: u64) -> Result<String> {
let pos = reader.position();
reader.set_position(off);
let mut s = String::new();
loop {
let b = reader.read_u8()?;
if b == 0 {
break;
}
s.push(b as char);
}
reader.set_position(pos);
Ok(s)
}

View File

@@ -1,13 +1,13 @@
use std::{
collections::{btree_map, hash_map, BTreeMap, HashMap},
collections::{btree_map, BTreeMap, HashMap},
hash::Hash,
io::BufRead,
};
use anyhow::{bail, ensure, Error, Result};
use cwdemangle::{demangle, DemangleOptions};
use lazy_static::lazy_static;
use multimap::MultiMap;
use once_cell::sync::Lazy;
use regex::{Captures, Regex};
use topological_sort::TopologicalSort;
@@ -168,29 +168,36 @@ pub fn resolve_link_order(section_unit_order: &[(String, Vec<String>)]) -> Resul
Ok(global_unit_order)
}
lazy_static! {
static ref LINK_MAP_START: Regex = Regex::new("^Link map of (?P<entry>.*)$").unwrap();
static ref LINK_MAP_ENTRY: Regex = Regex::new(
"^\\s*(?P<depth>\\d+)] (?P<sym>.*) \\((?P<type>.*),(?P<vis>.*)\\) found in (?P<tu>.*)$",
)
.unwrap();
static ref LINK_MAP_ENTRY_GENERATED: Regex =
Regex::new("^\\s*(?P<depth>\\d+)] (?P<sym>.*) found as linker generated symbol$").unwrap();
static ref LINK_MAP_ENTRY_DUPLICATE: Regex =
Regex::new("^\\s*(?P<depth>\\d+)] >>> UNREFERENCED DUPLICATE (?P<sym>.*)$").unwrap();
static ref SECTION_LAYOUT_START: Regex = Regex::new("^(?P<section>.*) section layout$").unwrap();
static ref SECTION_LAYOUT_SYMBOL: Regex = Regex::new(
"^\\s*(?P<rom_addr>[0-9A-Fa-f]+|UNUSED)\\s+(?P<size>[0-9A-Fa-f]+)\\s+(?P<addr>[0-9A-Fa-f]+|\\.{8})\\s+(?P<align>\\d+)?\\s*(?P<sym>.*?)(?:\\s+\\(entry of (?P<entry_of>.*?)\\))?\\s+(?P<tu>.*)$",
)
.unwrap();
static ref SECTION_LAYOUT_HEADER: Regex = Regex::new(
"^(\\s*Starting\\s+Virtual\\s*|\\s*address\\s+Size\\s+address\\s*|\\s*-----------------------\\s*)$",
)
.unwrap();
static ref MEMORY_MAP_HEADER: Regex = Regex::new("^\\s*Memory map:\\s*$").unwrap();
static ref EXTERN_SYMBOL: Regex = Regex::new("^\\s*>>> SYMBOL NOT FOUND: (.*)$").unwrap();
static ref LINKER_SYMBOLS_HEADER: Regex = Regex::new("^\\s*Linker generated symbols:\\s*$").unwrap();
macro_rules! static_regex {
($name:ident, $str:expr) => {
static $name: Lazy<Regex> = Lazy::new(|| Regex::new($str).unwrap());
};
}
static_regex!(LINK_MAP_START, "^Link map of (?P<entry>.*)$");
static_regex!(
LINK_MAP_ENTRY,
"^\\s*(?P<depth>\\d+)] (?P<sym>.*) \\((?P<type>.*),(?P<vis>.*)\\) found in (?P<tu>.*)$"
);
static_regex!(
LINK_MAP_ENTRY_GENERATED,
"^\\s*(?P<depth>\\d+)] (?P<sym>.*) found as linker generated symbol$"
);
static_regex!(
LINK_MAP_ENTRY_DUPLICATE,
"^\\s*(?P<depth>\\d+)] >>> UNREFERENCED DUPLICATE (?P<sym>.*)$"
);
static_regex!(SECTION_LAYOUT_START, "^(?P<section>.*) section layout$");
static_regex!(
SECTION_LAYOUT_SYMBOL,
"^\\s*(?P<rom_addr>[0-9A-Fa-f]+|UNUSED)\\s+(?P<size>[0-9A-Fa-f]+)\\s+(?P<addr>[0-9A-Fa-f]+|\\.{8})\\s+(?P<align>\\d+)?\\s*(?P<sym>.*?)(?:\\s+\\(entry of (?P<entry_of>.*?)\\))?\\s+(?P<tu>.*)$"
);
static_regex!(
SECTION_LAYOUT_HEADER,
"^(\\s*Starting\\s+Virtual\\s*|\\s*address\\s+Size\\s+address\\s*|\\s*-----------------------\\s*)$"
);
static_regex!(MEMORY_MAP_HEADER, "^\\s*Memory map:\\s*$");
static_regex!(EXTERN_SYMBOL, "^\\s*>>> SYMBOL NOT FOUND: (.*)$");
static_regex!(LINKER_SYMBOLS_HEADER, "^\\s*Linker generated symbols:\\s*$");
#[derive(Default)]
pub struct MapEntries {
@@ -544,25 +551,3 @@ pub fn process_map<R: BufRead>(reader: R) -> Result<MapEntries> {
// entries.unit_order = section_order.unit_order;
Ok(entries)
}
#[inline]
fn nested_try_insert<T1, T2, T3>(
map: &mut HashMap<T1, HashMap<T2, T3>>,
v1: T1,
v2: T2,
v3: T3,
) -> Result<()>
where
T1: Hash + Eq,
T2: Hash + Eq,
{
let map = match map.entry(v1) {
hash_map::Entry::Occupied(entry) => entry.into_mut(),
hash_map::Entry::Vacant(entry) => entry.insert(Default::default()),
};
match map.entry(v2) {
hash_map::Entry::Occupied(_) => bail!("Entry already exists"),
hash_map::Entry::Vacant(entry) => entry.insert(v3),
};
Ok(())
}

View File

@@ -1,15 +1,22 @@
pub(crate) mod asm;
pub(crate) mod cfa;
pub(crate) mod config;
pub(crate) mod dol;
pub(crate) mod dwarf;
pub(crate) mod elf;
pub(crate) mod executor;
pub(crate) mod map;
pub(crate) mod obj;
pub(crate) mod rel;
pub(crate) mod sigs;
pub(crate) mod slices;
pub(crate) mod split;
pub(crate) mod tracker;
pub(crate) mod vm;
pub mod asm;
pub mod config;
pub mod dol;
pub mod dwarf;
pub mod elf;
pub mod file;
pub mod map;
pub mod nested;
pub mod rel;
pub mod rso;
/// Creates a fixed-size array reference from a slice.
#[macro_export]
macro_rules! array_ref {
($slice:expr, $offset:expr, $size:expr) => {{
#[inline]
fn to_array<T>(slice: &[T]) -> &[T; $size] {
unsafe { &*(slice.as_ptr() as *const [_; $size]) }
}
to_array(&$slice[$offset..$offset + $size])
}};
}

65
src/util/nested.rs Normal file
View File

@@ -0,0 +1,65 @@
use std::{
collections::{btree_map, hash_map, BTreeMap, HashMap},
hash::Hash,
};
use anyhow::{bail, Result};
pub trait NestedMap<T1, T2, T3> {
fn nested_insert(&mut self, v1: T1, v2: T2, v3: T3) -> Result<()>;
}
pub trait NestedVec<T1, T2> {
fn nested_push(&mut self, v1: T1, v2: T2);
}
impl<T1, T2, T3> NestedMap<T1, T2, T3> for BTreeMap<T1, BTreeMap<T2, T3>>
where
T1: Eq + Ord,
T2: Eq + Ord,
{
fn nested_insert(&mut self, v1: T1, v2: T2, v3: T3) -> Result<()> {
let inner = match self.entry(v1) {
btree_map::Entry::Occupied(entry) => entry.into_mut(),
btree_map::Entry::Vacant(entry) => entry.insert(Default::default()),
};
match inner.entry(v2) {
btree_map::Entry::Occupied(_) => bail!("Entry already exists"),
btree_map::Entry::Vacant(entry) => entry.insert(v3),
};
Ok(())
}
}
impl<T1, T2, T3> NestedMap<T1, T2, T3> for HashMap<T1, HashMap<T2, T3>>
where
T1: Eq + Hash,
T2: Eq + Hash,
{
fn nested_insert(&mut self, v1: T1, v2: T2, v3: T3) -> Result<()> {
let inner = match self.entry(v1) {
hash_map::Entry::Occupied(entry) => entry.into_mut(),
hash_map::Entry::Vacant(entry) => entry.insert(Default::default()),
};
match inner.entry(v2) {
hash_map::Entry::Occupied(_) => bail!("Entry already exists"),
hash_map::Entry::Vacant(entry) => entry.insert(v3),
};
Ok(())
}
}
impl<T1, T2> NestedVec<T1, T2> for BTreeMap<T1, Vec<T2>>
where T1: Ord
{
fn nested_push(&mut self, v1: T1, v2: T2) {
match self.entry(v1) {
btree_map::Entry::Occupied(mut e) => {
e.get_mut().push(v2);
}
btree_map::Entry::Vacant(e) => {
e.insert(vec![v2]);
}
}
}
}

View File

@@ -1,19 +1,15 @@
use std::{
fs::File,
io::{BufReader, Read, Seek, SeekFrom},
path::Path,
};
use std::{io::Read, path::Path};
use anyhow::{anyhow, bail, ensure, Result};
use byteorder::{BigEndian, ReadBytesExt};
use object::elf::{
R_PPC_ADDR16, R_PPC_ADDR16_HA, R_PPC_ADDR16_HI, R_PPC_ADDR16_LO, R_PPC_ADDR24, R_PPC_ADDR32,
R_PPC_NONE, R_PPC_REL14, R_PPC_REL24, R_PPC_UADDR32,
};
use object::elf;
use crate::util::obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
use crate::{
obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
},
util::file::{map_file, map_reader, read_string},
};
/// Do not relocate anything, but accumulate the offset field for the next relocation offset calculation.
@@ -29,7 +25,9 @@ pub const R_DOLPHIN_END: u32 = 203;
pub const R_DOLPHIN_MRKREF: u32 = 204;
pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let mut reader = BufReader::new(File::open(&path)?);
let mmap = map_file(path)?;
let mut reader = map_reader(&mmap);
let module_id = reader.read_u32::<BigEndian>()?;
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'next' to be 0");
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'prev' to be 0");
@@ -60,7 +58,7 @@ pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let fix_size = if version >= 3 { Some(reader.read_u32::<BigEndian>()?) } else { None };
let mut sections = Vec::with_capacity(num_sections as usize);
reader.seek(SeekFrom::Start(section_info_offset as u64))?;
reader.set_position(section_info_offset as u64);
let mut total_bss_size = 0;
for idx in 0..num_sections {
let offset = reader.read_u32::<BigEndian>()?;
@@ -74,11 +72,11 @@ pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let data = if offset == 0 {
vec![]
} else {
let position = reader.stream_position()?;
reader.seek(SeekFrom::Start(offset as u64))?;
let position = reader.position();
reader.set_position(offset as u64);
let mut data = vec![0u8; size as usize];
reader.read_exact(&mut data)?;
reader.seek(SeekFrom::Start(position))?;
reader.set_position(position);
data
};
@@ -148,8 +146,8 @@ pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let mut unresolved_relocations = Vec::new();
let mut imp_idx = 0;
let imp_end = (imp_offset + imp_size) as u64;
reader.seek(SeekFrom::Start(imp_offset as u64))?;
while reader.stream_position()? < imp_end {
reader.set_position(imp_offset as u64);
while reader.position() < imp_end {
let reloc_module_id = reader.read_u32::<BigEndian>()?;
let reloc_offset = reader.read_u32::<BigEndian>()?;
@@ -165,12 +163,17 @@ pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
if reloc_module_id == module_id {
if let Some(fix_size) = fix_size {
ensure!(fix_size == reloc_offset, "fix_size mismatch: {:#X} != {:#X}", fix_size, reloc_offset);
ensure!(
fix_size == reloc_offset,
"fix_size mismatch: {:#X} != {:#X}",
fix_size,
reloc_offset
);
}
}
let position = reader.stream_position()?;
reader.seek(SeekFrom::Start(reloc_offset as u64))?;
let position = reader.position();
reader.set_position(reloc_offset as u64);
let mut address = 0u32;
let mut section = u8::MAX;
loop {
@@ -179,20 +182,20 @@ pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let target_section = reader.read_u8()?;
let addend = reader.read_u32::<BigEndian>()?;
let kind = match type_id {
R_PPC_NONE => continue,
R_PPC_ADDR32 | R_PPC_UADDR32 => ObjRelocKind::Absolute,
// R_PPC_ADDR24 => ObjRelocKind::PpcAddr24,
// R_PPC_ADDR16 => ObjRelocKind::PpcAddr16,
R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
// R_PPC_ADDR14 => ObjRelocKind::PpcAddr14,
// R_PPC_ADDR14_BRTAKEN => ObjRelocKind::PpcAddr14BrTaken,
// R_PPC_ADDR14_BRNTAKEN => ObjRelocKind::PpcAddr14BrnTaken,
R_PPC_REL24 => ObjRelocKind::PpcRel24,
R_PPC_REL14 => ObjRelocKind::PpcRel14,
// R_PPC_REL14_BRTAKEN => ObjRelocKind::PpcRel14BrTaken,
// R_PPC_REL14_BRNTAKEN => ObjRelocKind::PpcRel14BrnTaken,
elf::R_PPC_NONE => continue,
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 => ObjRelocKind::Absolute,
// elf::R_PPC_ADDR24 => ObjRelocKind::PpcAddr24,
// elf::R_PPC_ADDR16 => ObjRelocKind::PpcAddr16,
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
elf::R_PPC_ADDR16_HI => ObjRelocKind::PpcAddr16Hi,
elf::R_PPC_ADDR16_HA => ObjRelocKind::PpcAddr16Ha,
// elf::R_PPC_ADDR14 => ObjRelocKind::PpcAddr14,
// elf::R_PPC_ADDR14_BRTAKEN => ObjRelocKind::PpcAddr14BrTaken,
// elf::R_PPC_ADDR14_BRNTAKEN => ObjRelocKind::PpcAddr14BrnTaken,
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
// elf::R_PPC_REL14_BRTAKEN => ObjRelocKind::PpcRel14BrTaken,
// elf::R_PPC_REL14_BRNTAKEN => ObjRelocKind::PpcRel14BrnTaken,
R_DOLPHIN_NOP => {
address += offset as u32;
continue;
@@ -216,14 +219,18 @@ pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
addend,
});
}
reader.seek(SeekFrom::Start(position))?;
reader.set_position(position);
}
let name = match name_offset {
0 => String::new(),
_ => read_string(&mut reader, name_offset as u64, name_size as usize)?,
};
Ok(ObjInfo {
module_id,
kind: ObjKind::Relocatable,
architecture: ObjArchitecture::PowerPc,
name: "".to_string(),
name,
symbols,
sections,
entry: 0,
@@ -235,6 +242,7 @@ pub fn process_rel<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
arena_lo: None,
arena_hi: None,
splits: Default::default(),
named_sections: Default::default(),
link_order: vec![],
known_functions: Default::default(),
unresolved_relocations,

222
src/util/rso.rs Normal file
View File

@@ -0,0 +1,222 @@
use std::{io::Read, path::Path};
use anyhow::{anyhow, ensure, Result};
use byteorder::{BigEndian, ReadBytesExt};
use cwdemangle::{demangle, DemangleOptions};
use crate::{
obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
ObjSymbolFlags, ObjSymbolKind,
},
util::file::{map_file, map_reader, read_c_string, read_string},
};
pub fn process_rso<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
let mmap = map_file(path)?;
let mut reader = map_reader(&mmap);
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'next' to be 0");
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'prev' to be 0");
let num_sections = reader.read_u32::<BigEndian>()?;
let section_info_offset = reader.read_u32::<BigEndian>()?;
let name_offset = reader.read_u32::<BigEndian>()?;
let name_size = reader.read_u32::<BigEndian>()?;
let version = reader.read_u32::<BigEndian>()?;
ensure!(version == 1, "Unsupported RSO version {}", version);
let bss_size = reader.read_u32::<BigEndian>()?;
let prolog_section = reader.read_u8()?;
let epilog_section = reader.read_u8()?;
let unresolved_section = reader.read_u8()?;
ensure!(reader.read_u8()? == 0, "Expected 'bssSection' to be 0");
let prolog_offset = reader.read_u32::<BigEndian>()?;
let epilog_offset = reader.read_u32::<BigEndian>()?;
let unresolved_offset = reader.read_u32::<BigEndian>()?;
let internal_rel_offset = reader.read_u32::<BigEndian>()?;
let internal_rel_size = reader.read_u32::<BigEndian>()?;
let external_rel_offset = reader.read_u32::<BigEndian>()?;
let external_rel_size = reader.read_u32::<BigEndian>()?;
let export_table_offset = reader.read_u32::<BigEndian>()?;
let export_table_size = reader.read_u32::<BigEndian>()?;
let export_table_name_offset = reader.read_u32::<BigEndian>()?;
let import_table_offset = reader.read_u32::<BigEndian>()?;
let import_table_size = reader.read_u32::<BigEndian>()?;
let import_table_name_offset = reader.read_u32::<BigEndian>()?;
let mut sections = Vec::with_capacity(num_sections as usize);
reader.set_position(section_info_offset as u64);
let mut total_bss_size = 0;
for idx in 0..num_sections {
let offset = reader.read_u32::<BigEndian>()?;
let size = reader.read_u32::<BigEndian>()?;
log::info!("Section {}: {:#X} {:#X}", idx, offset, size);
if size == 0 {
continue;
}
let exec = (offset & 1) == 1;
let offset = offset & !3;
let data = if offset == 0 {
vec![]
} else {
let position = reader.position();
reader.set_position(offset as u64);
let mut data = vec![0u8; size as usize];
reader.read_exact(&mut data)?;
reader.set_position(position);
data
};
// println!("Section {} offset {:#X} size {:#X}", idx, offset, size);
let index = sections.len();
sections.push(ObjSection {
name: format!(".section{}", idx),
kind: if offset == 0 {
ObjSectionKind::Bss
} else if exec {
ObjSectionKind::Code
} else {
ObjSectionKind::Data
},
address: 0,
size: size as u64,
data,
align: 0,
index,
elf_index: idx as usize,
relocations: vec![],
original_address: 0,
file_offset: offset as u64,
section_known: false,
});
if offset == 0 {
total_bss_size += size;
}
}
ensure!(
total_bss_size == bss_size,
"Mismatched BSS size: {:#X} != {:#X}",
total_bss_size,
bss_size
);
let mut symbols = Vec::new();
let mut add_symbol = |section_idx: u8, offset: u32, name: &str| -> Result<()> {
if section_idx > 0 {
let section = sections
.iter()
.find(|section| section.elf_index == section_idx as usize)
.ok_or_else(|| anyhow!("Failed to locate {name} section {section_idx}"))?;
log::info!("Adding {name} section {section_idx} offset {offset:#X}");
symbols.push(ObjSymbol {
name: name.to_string(),
demangled_name: None,
address: offset as u64,
section: Some(section.index),
size: 0,
size_known: false,
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
kind: ObjSymbolKind::Function,
});
}
Ok(())
};
add_symbol(prolog_section, prolog_offset, "_prolog")?;
add_symbol(epilog_section, epilog_offset, "_epilog")?;
add_symbol(unresolved_section, unresolved_offset, "_unresolved")?;
reader.set_position(external_rel_offset as u64);
while reader.position() < (external_rel_offset + external_rel_size) as u64 {
let offset = reader.read_u32::<BigEndian>()?;
let id_and_type = reader.read_u32::<BigEndian>()?;
let id = (id_and_type & 0xFFFFFF00) >> 8;
let rel_type = id_and_type & 0xFF;
let sym_offset = reader.read_u32::<BigEndian>()?;
log::info!(
"Reloc offset: {:#X}, id: {}, type: {}, sym offset: {:#X}",
offset,
id,
rel_type,
sym_offset
);
}
reader.set_position(export_table_offset as u64);
while reader.position() < (export_table_offset + export_table_size) as u64 {
let name_off = reader.read_u32::<BigEndian>()?;
let name = read_c_string(&mut reader, (export_table_name_offset + name_off) as u64)?;
let sym_off = reader.read_u32::<BigEndian>()?;
let section_idx = reader.read_u32::<BigEndian>()?;
let hash_n = reader.read_u32::<BigEndian>()?;
let calc = symbol_hash(&name);
let demangled_name = demangle(&name, &DemangleOptions::default());
let section = sections
.iter()
.find(|section| section.elf_index == section_idx as usize)
.map(|section| section.index);
log::info!(
"Export: {}, sym off: {:#X}, section: {}, ELF hash: {:#X}, {:#X}",
demangled_name.as_deref().unwrap_or(&name),
sym_off,
section_idx,
hash_n,
calc
);
symbols.push(ObjSymbol {
name,
demangled_name,
address: sym_off as u64,
section,
size: 0,
size_known: false,
flags: Default::default(),
kind: Default::default(),
});
}
reader.set_position(import_table_offset as u64);
while reader.position() < (import_table_offset + import_table_size) as u64 {
let name_off = reader.read_u32::<BigEndian>()?;
let name = read_c_string(&mut reader, (import_table_name_offset + name_off) as u64)?;
let sym_off = reader.read_u32::<BigEndian>()?;
let section_idx = reader.read_u32::<BigEndian>()?;
log::info!("Import: {}, sym off: {}, section: {}", name, sym_off, section_idx);
}
let name = match name_offset {
0 => String::new(),
_ => read_string(&mut reader, name_offset as u64, name_size as usize)?,
};
Ok(ObjInfo {
kind: ObjKind::Relocatable,
architecture: ObjArchitecture::PowerPc,
name,
symbols,
sections,
entry: 0,
sda2_base: None,
sda_base: None,
stack_address: None,
stack_end: None,
db_stack_addr: None,
arena_lo: None,
arena_hi: None,
splits: Default::default(),
named_sections: Default::default(),
link_order: vec![],
known_functions: Default::default(),
module_id: 0,
unresolved_relocations: vec![],
})
}
fn symbol_hash(s: &str) -> u32 {
s.bytes().fold(0u32, |hash, c| {
let mut m = (hash << 4) + c as u32;
let n = m & 0xF0000000;
if n != 0 {
m ^= n >> 24;
}
m & !n
})
}