mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-12 06:45:09 +00:00
Reorganize files; start RSO support; config & split updates
This commit is contained in:
@@ -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);
|
||||
@@ -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(§ion.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 = §ion.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(§ion.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
107
src/analysis/mod.rs
Normal 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(§ion.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 = §ion.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(§ion.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
117
src/analysis/pass.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
@@ -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),
|
||||
@@ -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()))?;
|
||||
|
||||
509
src/cmd/dol.rs
509
src/cmd/dol.rs
@@ -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(§ion.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(§ion.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(())
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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))?;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
41
src/cmd/rso.rs
Normal 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(())
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
|
||||
10
src/main.rs
10
src/main.rs
@@ -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 {
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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(¤t_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"
|
||||
)
|
||||
}
|
||||
@@ -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}")?;
|
||||
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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()?;
|
||||
|
||||
112
src/util/elf.rs
112
src/util/elf.rs
@@ -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
57
src/util/file.rs
Normal 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)
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
|
||||
@@ -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
65
src/util/nested.rs
Normal 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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
222
src/util/rso.rs
Normal 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
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user