mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-12 22:56:28 +00:00
6 months of occasional work I guess
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use std::{
|
||||
collections::{btree_map::Entry, BTreeMap},
|
||||
fs::File,
|
||||
io::{BufRead, BufWriter, Write},
|
||||
io::{BufWriter, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
@@ -9,7 +9,7 @@ use anyhow::{anyhow, bail, Result};
|
||||
use argh::FromArgs;
|
||||
use object::{Object, ObjectSymbol, SymbolScope};
|
||||
|
||||
use crate::util::file::{buf_reader, map_file};
|
||||
use crate::util::file::{map_file, process_rsp};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Commands for processing static libraries.
|
||||
@@ -45,25 +45,7 @@ pub fn run(args: Args) -> Result<()> {
|
||||
|
||||
fn create(args: CreateArgs) -> Result<()> {
|
||||
// Process response files (starting with '@')
|
||||
let mut files = Vec::with_capacity(args.files.len());
|
||||
for path in args.files {
|
||||
let path_str =
|
||||
path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?;
|
||||
match path_str.strip_prefix('@') {
|
||||
Some(rsp_file) => {
|
||||
let reader = buf_reader(rsp_file)?;
|
||||
for result in reader.lines() {
|
||||
let line = result?;
|
||||
if !line.is_empty() {
|
||||
files.push(PathBuf::from(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
let files = process_rsp(&args.files)?;
|
||||
|
||||
// Build identifiers & symbol table
|
||||
let mut identifiers = Vec::with_capacity(files.len());
|
||||
|
||||
618
src/cmd/dol.rs
618
src/cmd/dol.rs
@@ -1,11 +1,10 @@
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
collections::{hash_map, BTreeMap, HashMap},
|
||||
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;
|
||||
@@ -13,25 +12,24 @@ use argh::FromArgs;
|
||||
use crate::{
|
||||
analysis::{
|
||||
cfa::AnalyzerState,
|
||||
objects::{detect_object_boundaries, detect_strings},
|
||||
pass::{AnalysisPass, FindSaveRestSleds, FindTRKInterruptVectorTable},
|
||||
read_u32,
|
||||
signatures::{apply_signatures, apply_signatures_post},
|
||||
tracker::Tracker,
|
||||
},
|
||||
obj::{
|
||||
signatures::{apply_signature, check_signatures, check_signatures_str, parse_signatures},
|
||||
split::split_obj,
|
||||
split::{split_obj, update_splits},
|
||||
ObjInfo, ObjRelocKind, ObjSectionKind, ObjSymbolKind,
|
||||
},
|
||||
util::{
|
||||
asm::write_asm,
|
||||
config::{apply_splits, parse_symbol_line, write_symbols},
|
||||
config::{apply_splits, parse_symbol_line, write_splits, write_symbols},
|
||||
dol::process_dol,
|
||||
elf::process_elf,
|
||||
elf::{process_elf, write_elf},
|
||||
file::{map_file, map_reader},
|
||||
map::process_map,
|
||||
lcf::{asm_path_for_unit, generate_ldscript, obj_path_for_unit},
|
||||
},
|
||||
};
|
||||
use crate::util::elf::write_elf;
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Commands for processing DOL files.
|
||||
@@ -44,32 +42,8 @@ pub struct Args {
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommand {
|
||||
Disasm(DisasmArgs),
|
||||
Info(InfoArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// disassembles a DOL file
|
||||
#[argh(subcommand, name = "disasm")]
|
||||
pub struct DisasmArgs {
|
||||
#[argh(option, short = 'm')]
|
||||
/// path to input map
|
||||
map_file: Option<PathBuf>,
|
||||
#[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,
|
||||
Split(SplitArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
@@ -81,301 +55,39 @@ pub struct InfoArgs {
|
||||
dol_file: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Splits a DOL into relocatable objects.
|
||||
#[argh(subcommand, name = "split")]
|
||||
pub struct SplitArgs {
|
||||
#[argh(positional)]
|
||||
/// input file
|
||||
in_file: PathBuf,
|
||||
#[argh(positional)]
|
||||
/// output directory
|
||||
out_dir: PathBuf,
|
||||
#[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>,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Disasm(c_args) => disasm(c_args),
|
||||
SubCommand::Info(c_args) => info(c_args),
|
||||
SubCommand::Split(c_args) => split(c_args),
|
||||
}
|
||||
}
|
||||
|
||||
const SIGNATURES: &[(&str, &str)] = &[
|
||||
("__init_registers", include_str!("../../assets/signatures/__init_registers.yml")),
|
||||
("__init_hardware", include_str!("../../assets/signatures/__init_hardware.yml")),
|
||||
("__init_data", include_str!("../../assets/signatures/__init_data.yml")),
|
||||
("__set_debug_bba", include_str!("../../assets/signatures/__set_debug_bba.yml")),
|
||||
("__OSPSInit", include_str!("../../assets/signatures/__OSPSInit.yml")),
|
||||
("__OSFPRInit", include_str!("../../assets/signatures/__OSFPRInit.yml")),
|
||||
("__OSCacheInit", include_str!("../../assets/signatures/__OSCacheInit.yml")),
|
||||
("DMAErrorHandler", include_str!("../../assets/signatures/DMAErrorHandler.yml")),
|
||||
("DBInit", include_str!("../../assets/signatures/DBInit.yml")),
|
||||
("OSInit", include_str!("../../assets/signatures/OSInit.yml")),
|
||||
("__OSThreadInit", include_str!("../../assets/signatures/__OSThreadInit.yml")),
|
||||
("__OSInitIPCBuffer", include_str!("../../assets/signatures/__OSInitIPCBuffer.yml")),
|
||||
("EXIInit", include_str!("../../assets/signatures/EXIInit.yml")),
|
||||
("EXIGetID", include_str!("../../assets/signatures/EXIGetID.yml")),
|
||||
("exit", include_str!("../../assets/signatures/exit.yml")),
|
||||
("_ExitProcess", include_str!("../../assets/signatures/_ExitProcess.yml")),
|
||||
("__fini_cpp", include_str!("../../assets/signatures/__fini_cpp.yml")),
|
||||
("__destroy_global_chain", include_str!("../../assets/signatures/__destroy_global_chain.yml")),
|
||||
("InitMetroTRK", include_str!("../../assets/signatures/InitMetroTRK.yml")),
|
||||
("InitMetroTRKCommTable", include_str!("../../assets/signatures/InitMetroTRKCommTable.yml")),
|
||||
("OSExceptionInit", include_str!("../../assets/signatures/OSExceptionInit.yml")),
|
||||
(
|
||||
"OSDefaultExceptionHandler",
|
||||
include_str!("../../assets/signatures/OSDefaultExceptionHandler.yml"),
|
||||
),
|
||||
("__OSUnhandledException", include_str!("../../assets/signatures/__OSUnhandledException.yml")),
|
||||
("OSDisableScheduler", include_str!("../../assets/signatures/OSDisableScheduler.yml")),
|
||||
("__OSReschedule", include_str!("../../assets/signatures/__OSReschedule.yml")),
|
||||
("__OSInitSystemCall", include_str!("../../assets/signatures/__OSInitSystemCall.yml")),
|
||||
("OSInitAlarm", include_str!("../../assets/signatures/OSInitAlarm.yml")),
|
||||
("__OSInitAlarm", include_str!("../../assets/signatures/__OSInitAlarm.yml")),
|
||||
("__OSEVStart", include_str!("../../assets/signatures/OSExceptionVector.yml")),
|
||||
("__OSDBINTSTART", include_str!("../../assets/signatures/__OSDBIntegrator.yml")),
|
||||
("__OSDBJUMPSTART", include_str!("../../assets/signatures/__OSDBJump.yml")),
|
||||
("SIInit", include_str!("../../assets/signatures/SIInit.yml")),
|
||||
("SIGetType", include_str!("../../assets/signatures/SIGetType.yml")),
|
||||
("SISetSamplingRate", include_str!("../../assets/signatures/SISetSamplingRate.yml")),
|
||||
("SISetXY", include_str!("../../assets/signatures/SISetXY.yml")),
|
||||
("VIGetTvFormat", include_str!("../../assets/signatures/VIGetTvFormat.yml")),
|
||||
("DVDInit", include_str!("../../assets/signatures/DVDInit.yml")),
|
||||
(
|
||||
"DVDSetAutoFatalMessaging",
|
||||
include_str!("../../assets/signatures/DVDSetAutoFatalMessaging.yml"),
|
||||
),
|
||||
("OSSetArenaLo", include_str!("../../assets/signatures/OSSetArenaLo.yml")),
|
||||
("OSSetArenaHi", include_str!("../../assets/signatures/OSSetArenaHi.yml")),
|
||||
("OSSetMEM1ArenaLo", include_str!("../../assets/signatures/OSSetMEM1ArenaLo.yml")),
|
||||
("OSSetMEM1ArenaHi", include_str!("../../assets/signatures/OSSetMEM1ArenaHi.yml")),
|
||||
("OSSetMEM2ArenaLo", include_str!("../../assets/signatures/OSSetMEM2ArenaLo.yml")),
|
||||
("OSSetMEM2ArenaHi", include_str!("../../assets/signatures/OSSetMEM2ArenaHi.yml")),
|
||||
("__OSInitAudioSystem", include_str!("../../assets/signatures/__OSInitAudioSystem.yml")),
|
||||
(
|
||||
"__OSInitMemoryProtection",
|
||||
include_str!("../../assets/signatures/__OSInitMemoryProtection.yml"),
|
||||
),
|
||||
// ("BATConfig", include_str!("../../assets/signatures/BATConfig.yml")), TODO
|
||||
("ReportOSInfo", include_str!("../../assets/signatures/ReportOSInfo.yml")),
|
||||
("__check_pad3", include_str!("../../assets/signatures/__check_pad3.yml")),
|
||||
("OSResetSystem", include_str!("../../assets/signatures/OSResetSystem.yml")),
|
||||
("OSReturnToMenu", include_str!("../../assets/signatures/OSReturnToMenu.yml")),
|
||||
("__OSReturnToMenu", include_str!("../../assets/signatures/__OSReturnToMenu.yml")),
|
||||
("__OSShutdownDevices", include_str!("../../assets/signatures/__OSShutdownDevices.yml")),
|
||||
("__OSInitSram", include_str!("../../assets/signatures/__OSInitSram.yml")),
|
||||
("__OSSyncSram", include_str!("../../assets/signatures/__OSSyncSram.yml")),
|
||||
(
|
||||
"__OSGetExceptionHandler",
|
||||
include_str!("../../assets/signatures/__OSGetExceptionHandler.yml"),
|
||||
),
|
||||
(
|
||||
"OSRegisterResetFunction",
|
||||
include_str!("../../assets/signatures/OSRegisterResetFunction.yml"),
|
||||
),
|
||||
(
|
||||
"OSRegisterShutdownFunction",
|
||||
include_str!("../../assets/signatures/OSRegisterShutdownFunction.yml"),
|
||||
),
|
||||
(
|
||||
"DecrementerExceptionHandler",
|
||||
include_str!("../../assets/signatures/DecrementerExceptionHandler.yml"),
|
||||
),
|
||||
(
|
||||
"DecrementerExceptionCallback",
|
||||
include_str!("../../assets/signatures/DecrementerExceptionCallback.yml"),
|
||||
),
|
||||
("__OSInterruptInit", include_str!("../../assets/signatures/__OSInterruptInit.yml")),
|
||||
("__OSContextInit", include_str!("../../assets/signatures/__OSContextInit.yml")),
|
||||
("OSSwitchFPUContext", include_str!("../../assets/signatures/OSSwitchFPUContext.yml")),
|
||||
("OSReport", include_str!("../../assets/signatures/OSReport.yml")),
|
||||
("TRK_main", include_str!("../../assets/signatures/TRK_main.yml")),
|
||||
("TRKNubWelcome", include_str!("../../assets/signatures/TRKNubWelcome.yml")),
|
||||
("TRKInitializeNub", include_str!("../../assets/signatures/TRKInitializeNub.yml")),
|
||||
(
|
||||
"TRKInitializeIntDrivenUART",
|
||||
include_str!("../../assets/signatures/TRKInitializeIntDrivenUART.yml"),
|
||||
),
|
||||
("TRKEXICallBack", include_str!("../../assets/signatures/TRKEXICallBack.yml")),
|
||||
("TRKLoadContext", include_str!("../../assets/signatures/TRKLoadContext.yml")),
|
||||
("TRKInterruptHandler", include_str!("../../assets/signatures/TRKInterruptHandler.yml")),
|
||||
("TRKExceptionHandler", include_str!("../../assets/signatures/TRKExceptionHandler.yml")),
|
||||
("TRKSaveExtended1Block", include_str!("../../assets/signatures/TRKSaveExtended1Block.yml")),
|
||||
("TRKNubMainLoop", include_str!("../../assets/signatures/TRKNubMainLoop.yml")),
|
||||
("TRKTargetContinue", include_str!("../../assets/signatures/TRKTargetContinue.yml")),
|
||||
("TRKSwapAndGo", include_str!("../../assets/signatures/TRKSwapAndGo.yml")),
|
||||
(
|
||||
"TRKRestoreExtended1Block",
|
||||
include_str!("../../assets/signatures/TRKRestoreExtended1Block.yml"),
|
||||
),
|
||||
(
|
||||
"TRKInterruptHandlerEnableInterrupts",
|
||||
include_str!("../../assets/signatures/TRKInterruptHandlerEnableInterrupts.yml"),
|
||||
),
|
||||
("memset", include_str!("../../assets/signatures/memset.yml")),
|
||||
(
|
||||
"__msl_runtime_constraint_violation_s",
|
||||
include_str!("../../assets/signatures/__msl_runtime_constraint_violation_s.yml"),
|
||||
),
|
||||
("ClearArena", include_str!("../../assets/signatures/ClearArena.yml")),
|
||||
("IPCCltInit", include_str!("../../assets/signatures/IPCCltInit.yml")),
|
||||
("__OSInitSTM", include_str!("../../assets/signatures/__OSInitSTM.yml")),
|
||||
("IOS_Open", include_str!("../../assets/signatures/IOS_Open.yml")),
|
||||
("__ios_Ipc2", include_str!("../../assets/signatures/__ios_Ipc2.yml")),
|
||||
("IPCiProfQueueReq", include_str!("../../assets/signatures/IPCiProfQueueReq.yml")),
|
||||
("SCInit", include_str!("../../assets/signatures/SCInit.yml")),
|
||||
("SCReloadConfFileAsync", include_str!("../../assets/signatures/SCReloadConfFileAsync.yml")),
|
||||
("NANDPrivateOpenAsync", include_str!("../../assets/signatures/NANDPrivateOpenAsync.yml")),
|
||||
("nandIsInitialized", include_str!("../../assets/signatures/nandIsInitialized.yml")),
|
||||
("nandOpen", include_str!("../../assets/signatures/nandOpen.yml")),
|
||||
("nandGenerateAbsPath", include_str!("../../assets/signatures/nandGenerateAbsPath.yml")),
|
||||
("nandGetHeadToken", include_str!("../../assets/signatures/nandGetHeadToken.yml")),
|
||||
("ISFS_OpenAsync", include_str!("../../assets/signatures/ISFS_OpenAsync.yml")),
|
||||
("nandConvertErrorCode", include_str!("../../assets/signatures/nandConvertErrorCode.yml")),
|
||||
(
|
||||
"NANDLoggingAddMessageAsync",
|
||||
include_str!("../../assets/signatures/NANDLoggingAddMessageAsync.yml"),
|
||||
),
|
||||
(
|
||||
"__NANDPrintErrorMessage",
|
||||
include_str!("../../assets/signatures/__NANDPrintErrorMessage.yml"),
|
||||
),
|
||||
("__OSInitNet", include_str!("../../assets/signatures/__OSInitNet.yml")),
|
||||
("__DVDCheckDevice", include_str!("../../assets/signatures/__DVDCheckDevice.yml")),
|
||||
("__OSInitPlayTime", include_str!("../../assets/signatures/__OSInitPlayTime.yml")),
|
||||
("__OSStartPlayRecord", include_str!("../../assets/signatures/__OSStartPlayRecord.yml")),
|
||||
("NANDInit", include_str!("../../assets/signatures/NANDInit.yml")),
|
||||
("ISFS_OpenLib", include_str!("../../assets/signatures/ISFS_OpenLib.yml")),
|
||||
("ESP_GetTitleId", include_str!("../../assets/signatures/ESP_GetTitleId.yml")),
|
||||
(
|
||||
"NANDSetAutoErrorMessaging",
|
||||
include_str!("../../assets/signatures/NANDSetAutoErrorMessaging.yml"),
|
||||
),
|
||||
("__DVDFSInit", include_str!("../../assets/signatures/__DVDFSInit.yml")),
|
||||
("__DVDClearWaitingQueue", include_str!("../../assets/signatures/__DVDClearWaitingQueue.yml")),
|
||||
("__DVDInitWA", include_str!("../../assets/signatures/__DVDInitWA.yml")),
|
||||
("__DVDLowSetWAType", include_str!("../../assets/signatures/__DVDLowSetWAType.yml")),
|
||||
("__fstLoad", include_str!("../../assets/signatures/__fstLoad.yml")),
|
||||
("DVDReset", include_str!("../../assets/signatures/DVDReset.yml")),
|
||||
("DVDLowReset", include_str!("../../assets/signatures/DVDLowReset.yml")),
|
||||
("DVDReadDiskID", include_str!("../../assets/signatures/DVDReadDiskID.yml")),
|
||||
("stateReady", include_str!("../../assets/signatures/stateReady.yml")),
|
||||
("DVDLowWaitCoverClose", include_str!("../../assets/signatures/DVDLowWaitCoverClose.yml")),
|
||||
("__DVDStoreErrorCode", include_str!("../../assets/signatures/__DVDStoreErrorCode.yml")),
|
||||
("DVDLowStopMotor", include_str!("../../assets/signatures/DVDLowStopMotor.yml")),
|
||||
("DVDGetDriveStatus", include_str!("../../assets/signatures/DVDGetDriveStatus.yml")),
|
||||
("printf", include_str!("../../assets/signatures/printf.yml")),
|
||||
("sprintf", include_str!("../../assets/signatures/sprintf.yml")),
|
||||
("vprintf", include_str!("../../assets/signatures/vprintf.yml")),
|
||||
("vsprintf", include_str!("../../assets/signatures/vsprintf.yml")),
|
||||
("vsnprintf", include_str!("../../assets/signatures/vsnprintf.yml")),
|
||||
("__pformatter", include_str!("../../assets/signatures/__pformatter.yml")),
|
||||
("longlong2str", include_str!("../../assets/signatures/longlong2str.yml")),
|
||||
("__mod2u", include_str!("../../assets/signatures/__mod2u.yml")),
|
||||
("__FileWrite", include_str!("../../assets/signatures/__FileWrite.yml")),
|
||||
("fwrite", include_str!("../../assets/signatures/fwrite.yml")),
|
||||
("__fwrite", include_str!("../../assets/signatures/__fwrite.yml")),
|
||||
("__stdio_atexit", include_str!("../../assets/signatures/__stdio_atexit.yml")),
|
||||
("__StringWrite", include_str!("../../assets/signatures/__StringWrite.yml")),
|
||||
];
|
||||
const POST_SIGNATURES: &[(&str, &str)] = &[
|
||||
("RSOStaticLocateObject", include_str!("../../assets/signatures/RSOStaticLocateObject.yml")),
|
||||
// ("GXInit", include_str!("../../assets/signatures/GXInit.yml")),
|
||||
];
|
||||
|
||||
pub fn apply_signatures(obj: &mut ObjInfo) -> Result<()> {
|
||||
let entry = obj.entry as u32;
|
||||
if let Some(signature) =
|
||||
check_signatures_str(obj, entry, include_str!("../../assets/signatures/__start.yml"))?
|
||||
{
|
||||
apply_signature(obj, entry, &signature)?;
|
||||
}
|
||||
for &(name, sig_str) in SIGNATURES {
|
||||
if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == name) {
|
||||
let addr = symbol.address as u32;
|
||||
if let Some(signature) = check_signatures_str(obj, addr, sig_str)? {
|
||||
apply_signature(obj, addr, &signature)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == "__init_user") {
|
||||
// __init_user can be overridden, but we can still look for __init_cpp from it
|
||||
let mut analyzer = AnalyzerState::default();
|
||||
analyzer.process_function_at(&obj, symbol.address as u32)?;
|
||||
for addr in analyzer.function_entries {
|
||||
if let Some(signature) = check_signatures_str(
|
||||
obj,
|
||||
addr,
|
||||
include_str!("../../assets/signatures/__init_cpp.yml"),
|
||||
)? {
|
||||
apply_signature(obj, addr, &signature)?;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == "_ctors") {
|
||||
// First entry of ctors is __init_cpp_exceptions
|
||||
let section = obj.section_at(symbol.address as u32)?;
|
||||
let target = read_u32(§ion.data, symbol.address as u32, section.address as u32)
|
||||
.ok_or_else(|| anyhow!("Failed to read _ctors data"))?;
|
||||
if target != 0 {
|
||||
if let Some(signature) = check_signatures_str(
|
||||
obj,
|
||||
target,
|
||||
include_str!("../../assets/signatures/__init_cpp_exceptions.yml"),
|
||||
)? {
|
||||
apply_signature(obj, target, &signature)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(symbol) = obj.symbols.iter().find(|symbol| symbol.name == "_dtors") {
|
||||
// Second entry of dtors is __fini_cpp_exceptions
|
||||
let section = obj.section_at(symbol.address as u32)?;
|
||||
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 {
|
||||
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)?;
|
||||
// Apply known functions from extab
|
||||
let mut state = AnalyzerState::default();
|
||||
for (&addr, &size) in &obj.known_functions {
|
||||
state.function_entries.insert(addr);
|
||||
state.function_bounds.insert(addr, addr + size);
|
||||
}
|
||||
for symbol in &obj.symbols {
|
||||
if symbol.kind != ObjSymbolKind::Function {
|
||||
continue;
|
||||
}
|
||||
state.function_entries.insert(symbol.address as u32);
|
||||
if !symbol.size_known {
|
||||
continue;
|
||||
}
|
||||
state.function_bounds.insert(symbol.address as u32, (symbol.address + symbol.size) as u32);
|
||||
}
|
||||
// Also check the start of each code section
|
||||
for section in &obj.sections {
|
||||
if section.kind == ObjSectionKind::Code {
|
||||
state.function_entries.insert(section.address as u32);
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = AnalyzerState::default();
|
||||
state.detect_functions(&obj)?;
|
||||
log::info!("Discovered {} functions", state.function_slices.len());
|
||||
|
||||
@@ -397,9 +109,7 @@ fn info(args: InfoArgs) -> Result<()> {
|
||||
}
|
||||
println!("\nDiscovered symbols:");
|
||||
println!("\t{: >23} | {: <10} | {: <10}", "Name", "Address", "Size");
|
||||
let mut symbols = obj.symbols.clone();
|
||||
symbols.sort_by_key(|sym| sym.address);
|
||||
for symbol in symbols {
|
||||
for (_, symbol) in obj.symbols.for_range(..) {
|
||||
if symbol.name.starts_with('@') || symbol.name.starts_with("fn_") {
|
||||
continue;
|
||||
}
|
||||
@@ -414,83 +124,39 @@ fn info(args: InfoArgs) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn disasm(args: DisasmArgs) -> Result<()> {
|
||||
let mut obj = process_dol(&args.dol_file)?;
|
||||
log::info!("Performing initial control flow analysis");
|
||||
fn split(args: SplitArgs) -> Result<()> {
|
||||
log::info!("Loading {}", args.in_file.display());
|
||||
let mut obj = process_dol(&args.in_file)?;
|
||||
|
||||
// if detect_sda_bases(&mut obj).context("Failed to locate SDA bases")? {
|
||||
// let (sda2_base, sda_base) = obj.sda_bases.unwrap();
|
||||
// log::info!("Found _SDA2_BASE_ @ {:#010X}, _SDA_BASE_ @ {:#010X}", sda2_base, sda_base);
|
||||
// } else {
|
||||
// bail!("Unable to locate SDA bases");
|
||||
// }
|
||||
|
||||
if let Some(map) = &args.map_file {
|
||||
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)?;
|
||||
if let Some(splits_path) = &args.splits_file {
|
||||
if splits_path.is_file() {
|
||||
let map = map_file(splits_path)?;
|
||||
apply_splits(map_reader(&map), &mut obj)?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut state = AnalyzerState::default();
|
||||
|
||||
if let Some(symbols_path) = &args.symbols_file {
|
||||
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:?}"),
|
||||
};
|
||||
if let Some(symbol) = parse_symbol_line(&line, &obj)? {
|
||||
// if symbol.kind == ObjSymbolKind::Function {
|
||||
// state.function_entries.insert(symbol.address as u32);
|
||||
// if symbol.size_known {
|
||||
// state
|
||||
// .function_bounds
|
||||
// .insert(symbol.address as u32, (symbol.address + symbol.size) as u32);
|
||||
// }
|
||||
// }
|
||||
if let Some(existing_symbol) = obj
|
||||
.symbols
|
||||
.iter_mut()
|
||||
.find(|e| e.address == symbol.address && e.kind == symbol.kind)
|
||||
{
|
||||
*existing_symbol = symbol;
|
||||
} else {
|
||||
obj.symbols.push(symbol);
|
||||
if symbols_path.is_file() {
|
||||
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:?}"),
|
||||
};
|
||||
if let Some(symbol) = parse_symbol_line(&line, &mut obj)? {
|
||||
obj.add_symbol(symbol, true)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO move before symbols?
|
||||
log::info!("Performing signature analysis");
|
||||
apply_signatures(&mut obj)?;
|
||||
|
||||
// Apply known functions from extab
|
||||
for (&addr, &size) in &obj.known_functions {
|
||||
state.function_entries.insert(addr);
|
||||
state.function_bounds.insert(addr, addr + size);
|
||||
}
|
||||
for symbol in &obj.symbols {
|
||||
if symbol.kind != ObjSymbolKind::Function {
|
||||
continue;
|
||||
}
|
||||
state.function_entries.insert(symbol.address as u32);
|
||||
if !symbol.size_known {
|
||||
continue;
|
||||
}
|
||||
state.function_bounds.insert(symbol.address as u32, (symbol.address + symbol.size) as u32);
|
||||
}
|
||||
// Also check the start of each code section
|
||||
for section in &obj.sections {
|
||||
if section.kind == ObjSectionKind::Code {
|
||||
state.function_entries.insert(section.address as u32);
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("Detecting function boundaries");
|
||||
state.detect_functions(&obj)?;
|
||||
log::info!("Discovered {} functions", state.function_slices.len());
|
||||
|
||||
@@ -505,78 +171,112 @@ fn disasm(args: DisasmArgs) -> Result<()> {
|
||||
log::info!("Applying relocations");
|
||||
tracker.apply(&mut obj, false)?;
|
||||
|
||||
if args.splits_file.is_some() {
|
||||
log::info!("Detecting object boundaries");
|
||||
detect_object_boundaries(&mut obj)?;
|
||||
|
||||
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)?;
|
||||
}
|
||||
log::info!("Detecting strings");
|
||||
detect_strings(&mut obj)?;
|
||||
|
||||
if let Some(symbols_path) = &args.symbols_file {
|
||||
let mut symbols_writer = BufWriter::new(
|
||||
File::create(&symbols_path)
|
||||
File::create(symbols_path)
|
||||
.with_context(|| format!("Failed to create '{}'", symbols_path.display()))?,
|
||||
);
|
||||
write_symbols(&mut symbols_writer, &obj)?;
|
||||
}
|
||||
|
||||
// (debugging) validate against ELF
|
||||
if let Some(file) = args.elf_file {
|
||||
validate(&obj, &file, &state)?;
|
||||
if let Some(splits_path) = &args.splits_file {
|
||||
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)?;
|
||||
}
|
||||
|
||||
log::info!("Adjusting splits");
|
||||
update_splits(&mut obj)?;
|
||||
|
||||
log::info!("Splitting {} objects", obj.link_order.len());
|
||||
let split_objs = split_obj(&obj)?;
|
||||
|
||||
// Create out dirs
|
||||
let asm_dir = args.out_dir.join("asm");
|
||||
let include_dir = args.out_dir.join("include");
|
||||
let obj_dir = args.out_dir.clone();
|
||||
DirBuilder::new().recursive(true).create(&include_dir)?;
|
||||
fs::write(include_dir.join("macros.inc"), include_str!("../../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(args.out_dir.join("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(obj_path_for_unit(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()?;
|
||||
|
||||
// Generate ldscript.lcf
|
||||
fs::write(args.out_dir.join("ldscript.lcf"), generate_ldscript(&obj)?)?;
|
||||
|
||||
log::info!("Writing disassembly");
|
||||
// let mut files_out = File::create(args.out_dir.join("build.ps1"))?;
|
||||
// writeln!(files_out, "$ErrorActionPreference = 'Stop'")?;
|
||||
// writeln!(
|
||||
// files_out,
|
||||
// "$asflags = '-mgekko', '-I', '{}', '--defsym', 'version=0', '-W', '--strip-local-absolute', '-gdwarf-2'",
|
||||
// include_dir.display()
|
||||
// )?;
|
||||
// writeln!(files_out, "$env:PATH = \"$env:PATH;C:\\devkitPro\\devkitPPC\\bin\"")?;
|
||||
for (unit, split_obj) in obj.link_order.iter().zip(&split_objs) {
|
||||
let out_path = asm_dir.join(asm_path_for_unit(unit));
|
||||
|
||||
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()?;
|
||||
|
||||
// let obj_path = obj_dir.join(obj_path_for_unit(unit));
|
||||
// writeln!(files_out, "Write-Host 'Compiling {}'", obj_path.display())?;
|
||||
// writeln!(
|
||||
// files_out,
|
||||
// "powerpc-eabi-as @asflags -o '{}' '{}'",
|
||||
// obj_path.display(),
|
||||
// out_path.display()
|
||||
// )?;
|
||||
// writeln!(
|
||||
// files_out,
|
||||
// "dtk elf fixup '{}' '{}'",
|
||||
// obj_path.display(),
|
||||
// obj_path.display()
|
||||
// )?;
|
||||
}
|
||||
// files_out.flush()?;
|
||||
|
||||
// (debugging) validate against ELF
|
||||
if let Some(file) = &args.elf_file {
|
||||
validate(&obj, file, &state)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -610,11 +310,7 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
|
||||
if section.kind != ObjSectionKind::Code {
|
||||
continue;
|
||||
}
|
||||
for (_symbol_idx, symbol) in real_obj.symbols_for_section(section.index) {
|
||||
// if symbol.name.starts_with("switch_") {
|
||||
// continue;
|
||||
// }
|
||||
// if symbol.kind == ObjSymbolKind::Function {
|
||||
for (_symbol_idx, symbol) in real_obj.symbols.for_section(section) {
|
||||
real_functions.insert(symbol.address as u32, symbol.name.clone());
|
||||
match state.function_bounds.get(&(symbol.address as u32)) {
|
||||
Some(&end) => {
|
||||
@@ -636,7 +332,6 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
|
||||
);
|
||||
}
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
for (&start, &end) in &state.function_bounds {
|
||||
@@ -653,7 +348,8 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
|
||||
);
|
||||
}
|
||||
}
|
||||
return Ok(()); // TODO
|
||||
// return Ok(()); // TODO
|
||||
|
||||
for real_section in &real_obj.sections {
|
||||
let obj_section = match obj.sections.get(real_section.index) {
|
||||
Some(v) => v,
|
||||
@@ -661,10 +357,11 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
|
||||
};
|
||||
let real_map = real_section.build_relocation_map()?;
|
||||
let obj_map = obj_section.build_relocation_map()?;
|
||||
for (&real_addr, real_reloc) in &real_map {
|
||||
let real_symbol = &real_obj.symbols[real_reloc.target_symbol];
|
||||
for (&real_addr, &real_reloc_idx) in &real_map {
|
||||
let real_reloc = &real_section.relocations[real_reloc_idx];
|
||||
let real_symbol = real_obj.symbols.at(real_reloc.target_symbol);
|
||||
let obj_reloc = match obj_map.get(&real_addr) {
|
||||
Some(v) => v,
|
||||
Some(v) => &obj_section.relocations[*v],
|
||||
None => {
|
||||
// Ignore GCC local jump branches
|
||||
if real_symbol.kind == ObjSymbolKind::Section
|
||||
@@ -688,7 +385,7 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
|
||||
continue;
|
||||
}
|
||||
};
|
||||
let obj_symbol = &obj.symbols[obj_reloc.target_symbol];
|
||||
let obj_symbol = obj.symbols.at(obj_reloc.target_symbol);
|
||||
if real_reloc.kind != obj_reloc.kind {
|
||||
log::warn!(
|
||||
"Relocation type mismatch @ {:#010X}: {:?} != {:?}",
|
||||
@@ -714,8 +411,9 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
|
||||
continue;
|
||||
}
|
||||
}
|
||||
for (&obj_addr, obj_reloc) in &obj_map {
|
||||
let obj_symbol = &obj.symbols[obj_reloc.target_symbol];
|
||||
for (&obj_addr, &obj_reloc_idx) in &obj_map {
|
||||
let obj_reloc = &obj_section.relocations[obj_reloc_idx];
|
||||
let obj_symbol = obj.symbols.at(obj_reloc.target_symbol);
|
||||
if !real_map.contains_key(&obj_addr) {
|
||||
log::warn!(
|
||||
"Relocation not real @ {:#010X} {:?} to {:#010X}+{:X} ({})",
|
||||
|
||||
@@ -11,9 +11,8 @@ use object::{elf, Object, ObjectSection, ObjectSymbol, RelocationKind, Relocatio
|
||||
|
||||
use crate::util::{
|
||||
dwarf::{
|
||||
process_address, process_offset, process_type, process_variable_location,
|
||||
read_debug_section, type_string, ud_type, ud_type_def, ud_type_string, AttributeKind,
|
||||
TagKind, TypeKind,
|
||||
process_address, process_type, process_variable_location, read_debug_section, type_string,
|
||||
ud_type, ud_type_def, ud_type_string, AttributeKind, TagKind,
|
||||
},
|
||||
file::map_file,
|
||||
};
|
||||
@@ -61,13 +60,13 @@ fn dump(args: DumpArgs) -> Result<()> {
|
||||
};
|
||||
let name = String::from_utf8_lossy(e.header().identifier()).to_string();
|
||||
let mut data = vec![0u8; e.header().size() as usize];
|
||||
e.read(&mut data)?;
|
||||
e.read_exact(&mut data)?;
|
||||
let obj_file = object::read::File::parse(&*data)?;
|
||||
let debug_section = match obj_file.section_by_name(".debug") {
|
||||
Some(section) => {
|
||||
log::info!("Processing '{}'", name);
|
||||
section
|
||||
},
|
||||
}
|
||||
None => {
|
||||
log::warn!("Object '{}' missing .debug section", name);
|
||||
continue;
|
||||
@@ -76,7 +75,7 @@ fn dump(args: DumpArgs) -> Result<()> {
|
||||
if let Some(out_path) = &args.out {
|
||||
// TODO make a basename method
|
||||
let name = name.trim_start_matches("D:").replace('\\', "/");
|
||||
let name = name.rsplit_once('/').map(|(a, b)| b).unwrap_or(&name);
|
||||
let name = name.rsplit_once('/').map(|(_, b)| b).unwrap_or(&name);
|
||||
let file_path = out_path.join(format!("{}.txt", name));
|
||||
let mut file = BufWriter::new(File::create(file_path)?);
|
||||
dump_debug_section(&mut file, &obj_file, debug_section)?;
|
||||
|
||||
@@ -2,16 +2,17 @@ use std::{
|
||||
collections::{btree_map, hash_map, BTreeMap, HashMap},
|
||||
fs,
|
||||
fs::{DirBuilder, File},
|
||||
io::{BufRead, BufReader, BufWriter, Write},
|
||||
io::{BufWriter, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use argh::FromArgs;
|
||||
use object::{
|
||||
elf,
|
||||
write::{Mangling, SectionId, SymbolId},
|
||||
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionFlags,
|
||||
SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection,
|
||||
FileFlags, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionFlags,
|
||||
SectionIndex, SectionKind, SymbolFlags, SymbolIndex, SymbolKind, SymbolScope, SymbolSection,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -24,7 +25,7 @@ use crate::{
|
||||
asm::write_asm,
|
||||
config::{write_splits, write_symbols},
|
||||
elf::{process_elf, write_elf},
|
||||
file::buf_reader,
|
||||
file::process_rsp,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -92,9 +93,6 @@ 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)]
|
||||
@@ -137,29 +135,12 @@ fn config(args: ConfigArgs) -> Result<()> {
|
||||
}
|
||||
|
||||
{
|
||||
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)?;
|
||||
write_splits(&mut splits_writer, &obj)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -257,13 +238,22 @@ fn file_name_from_unit(str: &str, suffix: &str) -> String {
|
||||
|
||||
const ASM_SUFFIX: &str = " (asm)";
|
||||
|
||||
// fn fixup(args: FixupArgs) -> Result<()> {
|
||||
// let obj = process_elf(&args.in_file)?;
|
||||
// let out = write_elf(&obj)?;
|
||||
// fs::write(&args.out_file, &out).context("Failed to create output file")?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
fn fixup(args: FixupArgs) -> Result<()> {
|
||||
let in_buf = fs::read(&args.in_file)
|
||||
.with_context(|| format!("Failed to open input file: '{}'", args.in_file.display()))?;
|
||||
let in_file = object::read::File::parse(&*in_buf).context("Failed to parse input ELF")?;
|
||||
let mut out_file =
|
||||
object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness());
|
||||
out_file.set_mangling(Mangling::None);
|
||||
out_file.flags =
|
||||
FileFlags::Elf { os_abi: elf::ELFOSABI_SYSV, abi_version: 0, e_flags: elf::EF_PPC_EMB };
|
||||
out_file.mangling = Mangling::None;
|
||||
|
||||
// Write file symbol first
|
||||
let mut file_symbol_found = false;
|
||||
@@ -317,7 +307,7 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
||||
} else {
|
||||
out_section.set_data(section.uncompressed_data()?.into_owned(), section.align());
|
||||
}
|
||||
if has_section_flags(section.flags(), object::elf::SHF_ALLOC)? {
|
||||
if has_section_flags(section.flags(), elf::SHF_ALLOC)? {
|
||||
// Generate section symbol
|
||||
out_file.section_symbol(section_id);
|
||||
}
|
||||
@@ -398,9 +388,9 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
||||
// This is a hack to avoid replacement with a section symbol
|
||||
// See [`object::write::elf::object::elf_fixup_relocation`]
|
||||
RelocationKind::Absolute => RelocationKind::Elf(if addr & 3 == 0 {
|
||||
object::elf::R_PPC_ADDR32
|
||||
elf::R_PPC_ADDR32
|
||||
} else {
|
||||
object::elf::R_PPC_UADDR32
|
||||
elf::R_PPC_UADDR32
|
||||
}),
|
||||
other => other,
|
||||
};
|
||||
@@ -442,7 +432,9 @@ fn to_write_symbol_section(
|
||||
}
|
||||
}
|
||||
|
||||
fn to_write_symbol_flags(flags: SymbolFlags<SectionIndex>) -> Result<SymbolFlags<SectionId>> {
|
||||
fn to_write_symbol_flags(
|
||||
flags: SymbolFlags<SectionIndex, SymbolIndex>,
|
||||
) -> Result<SymbolFlags<SectionId, SymbolId>> {
|
||||
match flags {
|
||||
SymbolFlags::Elf { st_info, st_other } => Ok(SymbolFlags::Elf { st_info, st_other }),
|
||||
SymbolFlags::None => Ok(SymbolFlags::None),
|
||||
@@ -475,25 +467,7 @@ fn has_section_flags(flags: SectionFlags, flag: u32) -> Result<bool> {
|
||||
|
||||
fn signatures(args: SignaturesArgs) -> Result<()> {
|
||||
// Process response files (starting with '@')
|
||||
let mut files = Vec::with_capacity(args.files.len());
|
||||
for path in args.files {
|
||||
let path_str =
|
||||
path.to_str().ok_or_else(|| anyhow!("'{}' is not valid UTF-8", path.display()))?;
|
||||
match path_str.strip_prefix('@') {
|
||||
Some(rsp_file) => {
|
||||
let reader = buf_reader(rsp_file)?;
|
||||
for result in reader.lines() {
|
||||
let line = result?;
|
||||
if !line.is_empty() {
|
||||
files.push(PathBuf::from(line));
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
files.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
let files = process_rsp(&args.files)?;
|
||||
|
||||
let mut signatures: HashMap<String, FunctionSignature> = HashMap::new();
|
||||
for path in files {
|
||||
|
||||
@@ -65,10 +65,10 @@ pub fn run(args: Args) -> Result<()> {
|
||||
out.seek(SeekFrom::Start(offset as u64))?;
|
||||
|
||||
// Text sections
|
||||
for section in obj_file.sections() {
|
||||
if section.kind() != SectionKind::Text {
|
||||
continue;
|
||||
}
|
||||
for section in
|
||||
obj_file.sections().filter(|s| section_kind(s) == SectionKind::Text && is_alloc(s.flags()))
|
||||
{
|
||||
log::debug!("Processing text section '{}'", section.name().unwrap_or("[error]"));
|
||||
let address = section.address() as u32;
|
||||
let size = align32(section.size() as u32);
|
||||
*header.text_sections.get_mut(header.text_section_count).ok_or_else(|| {
|
||||
@@ -83,10 +83,10 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
|
||||
// Data sections
|
||||
for section in obj_file.sections() {
|
||||
if section.kind() != SectionKind::Data && section.kind() != SectionKind::ReadOnlyData {
|
||||
continue;
|
||||
}
|
||||
for section in
|
||||
obj_file.sections().filter(|s| section_kind(s) == SectionKind::Data && is_alloc(s.flags()))
|
||||
{
|
||||
log::debug!("Processing data section '{}'", section.name().unwrap_or("[error]"));
|
||||
let address = section.address() as u32;
|
||||
let size = align32(section.size() as u32);
|
||||
*header.data_sections.get_mut(header.data_section_count).ok_or_else(|| {
|
||||
@@ -101,10 +101,10 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
|
||||
// BSS sections
|
||||
for section in obj_file.sections() {
|
||||
if section.kind() != SectionKind::UninitializedData {
|
||||
continue;
|
||||
}
|
||||
for section in obj_file
|
||||
.sections()
|
||||
.filter(|s| section_kind(s) == SectionKind::UninitializedData && is_alloc(s.flags()))
|
||||
{
|
||||
let address = section.address() as u32;
|
||||
let size = section.size() as u32;
|
||||
if header.bss_address == 0 {
|
||||
@@ -162,3 +162,28 @@ fn write_aligned<T: Write>(out: &mut T, bytes: &[u8], aligned_size: u32) -> std:
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Some ELF files don't have the proper section kind set (for small data sections in particular)
|
||||
// so we map the section name to the expected section kind when possible.
|
||||
#[inline]
|
||||
fn section_kind(section: &object::Section) -> SectionKind {
|
||||
section
|
||||
.name()
|
||||
.ok()
|
||||
.and_then(|name| match name {
|
||||
".init" | ".text" | ".vmtext" | ".dbgtext" => Some(SectionKind::Text),
|
||||
".ctors" | ".dtors" | ".data" | ".rodata" | ".sdata" | ".sdata2" | "extab"
|
||||
| "extabindex" => Some(SectionKind::Data),
|
||||
".bss" | ".sbss" | ".sbss2" => Some(SectionKind::UninitializedData),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| match section.kind() {
|
||||
SectionKind::ReadOnlyData => SectionKind::Data,
|
||||
kind => kind,
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_alloc(flags: object::SectionFlags) -> bool {
|
||||
matches!(flags, object::SectionFlags::Elf { sh_flags } if sh_flags & object::elf::SHF_ALLOC as u64 != 0)
|
||||
}
|
||||
|
||||
180
src/cmd/map.rs
180
src/cmd/map.rs
@@ -1,11 +1,13 @@
|
||||
#![allow(clippy::needless_borrow)]
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::{bail, ensure, Result};
|
||||
use anyhow::{bail, Result};
|
||||
use argh::FromArgs;
|
||||
use cwdemangle::{demangle, DemangleOptions};
|
||||
|
||||
use crate::util::{
|
||||
file::{map_file, map_reader},
|
||||
map::{process_map, resolve_link_order, SymbolEntry, SymbolRef},
|
||||
map::{process_map, SymbolEntry, SymbolRef},
|
||||
};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
@@ -96,11 +98,8 @@ fn entries(args: EntriesArgs) -> Result<()> {
|
||||
if symbol_ref.name.starts_with('@') {
|
||||
continue;
|
||||
}
|
||||
if let Some(symbol) = entries.symbols.get(symbol_ref) {
|
||||
println!("{}", symbol.demangled.as_ref().unwrap_or(&symbol.name));
|
||||
} else {
|
||||
println!("Symbol not found: {}", symbol_ref.name);
|
||||
}
|
||||
let demangled = demangle(&symbol_ref.name, &DemangleOptions::default());
|
||||
println!("{}", demangled.as_deref().unwrap_or(&symbol_ref.name));
|
||||
}
|
||||
}
|
||||
None => bail!("Failed to find entries for TU '{}' in map", args.unit),
|
||||
@@ -111,98 +110,115 @@ fn entries(args: EntriesArgs) -> Result<()> {
|
||||
fn symbol(args: SymbolArgs) -> Result<()> {
|
||||
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 {
|
||||
ensure!(opt_ref.is_none(), "Symbol '{}' found in multiple TUs", args.symbol);
|
||||
opt_ref = Some((symbol_ref.clone(), entry.clone()));
|
||||
}
|
||||
}
|
||||
match opt_ref {
|
||||
Some((symbol_ref, symbol)) => {
|
||||
println!("Located symbol {}", symbol.demangled.as_ref().unwrap_or(&symbol.name));
|
||||
if let Some(vec) = entries.entry_references.get_vec(&symbol_ref) {
|
||||
println!("\nReferences:");
|
||||
for x in vec {
|
||||
if let Some(reference) = entries.symbols.get(x) {
|
||||
println!(
|
||||
">>> {} ({:?},{:?}) [{}]",
|
||||
reference.demangled.as_ref().unwrap_or(&reference.name),
|
||||
reference.kind,
|
||||
reference.visibility,
|
||||
reference.unit.as_deref().unwrap_or("[generated]")
|
||||
);
|
||||
} else {
|
||||
println!(">>> {} (NOT FOUND)", x.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(vec) = entries.entry_referenced_from.get_vec(&symbol_ref) {
|
||||
println!("\nReferenced from:");
|
||||
for x in vec {
|
||||
if let Some(reference) = entries.symbols.get(x) {
|
||||
println!(
|
||||
">>> {} ({:?}, {:?}) [{}]",
|
||||
reference.demangled.as_ref().unwrap_or(&reference.name),
|
||||
reference.kind,
|
||||
reference.visibility,
|
||||
reference.unit.as_deref().unwrap_or("[generated]")
|
||||
);
|
||||
} else {
|
||||
println!(">>> {} (NOT FOUND)", x.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
println!("\n");
|
||||
}
|
||||
None => bail!("Failed to find symbol '{}' in map", args.symbol),
|
||||
}
|
||||
let opt_ref: Option<(SymbolRef, SymbolEntry)> = None;
|
||||
|
||||
_ = entries;
|
||||
_ = opt_ref;
|
||||
// TODO
|
||||
|
||||
// for (symbol_ref, entry) in &entries.symbols {
|
||||
// if symbol_ref.name == args.symbol {
|
||||
// ensure!(opt_ref.is_none(), "Symbol '{}' found in multiple TUs", args.symbol);
|
||||
// opt_ref = Some((symbol_ref.clone(), entry.clone()));
|
||||
// }
|
||||
// }
|
||||
// match opt_ref {
|
||||
// Some((symbol_ref, symbol)) => {
|
||||
// println!("Located symbol {}", symbol.demangled.as_ref().unwrap_or(&symbol.name));
|
||||
// if let Some(vec) = entries.entry_references.get_vec(&symbol_ref) {
|
||||
// println!("\nReferences:");
|
||||
// for x in vec {
|
||||
// if let Some(reference) = entries.symbols.get(x) {
|
||||
// println!(
|
||||
// ">>> {} ({:?},{:?}) [{}]",
|
||||
// reference.demangled.as_ref().unwrap_or(&reference.name),
|
||||
// reference.kind,
|
||||
// reference.visibility,
|
||||
// reference.unit.as_deref().unwrap_or("[generated]")
|
||||
// );
|
||||
// } else {
|
||||
// println!(">>> {} (NOT FOUND)", x.name);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if let Some(vec) = entries.entry_referenced_from.get_vec(&symbol_ref) {
|
||||
// println!("\nReferenced from:");
|
||||
// for x in vec {
|
||||
// if let Some(reference) = entries.symbols.get(x) {
|
||||
// println!(
|
||||
// ">>> {} ({:?}, {:?}) [{}]",
|
||||
// reference.demangled.as_ref().unwrap_or(&reference.name),
|
||||
// reference.kind,
|
||||
// reference.visibility,
|
||||
// reference.unit.as_deref().unwrap_or("[generated]")
|
||||
// );
|
||||
// } else {
|
||||
// println!(">>> {} (NOT FOUND)", x.name);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// println!("\n");
|
||||
// }
|
||||
// None => bail!("Failed to find symbol '{}' in map", args.symbol),
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn order(args: OrderArgs) -> Result<()> {
|
||||
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}");
|
||||
}
|
||||
|
||||
_ = entries;
|
||||
// TODO
|
||||
|
||||
// let order = resolve_link_order(&entries.unit_order)?;
|
||||
// for unit in order {
|
||||
// println!("{unit}");
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn slices(args: SlicesArgs) -> Result<()> {
|
||||
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(' ') {
|
||||
format!("{}/{}", lib.strip_suffix(".a").unwrap_or(lib), name)
|
||||
} else if let Some(strip) = unit.strip_suffix(".o") {
|
||||
format!("{strip}.c")
|
||||
} else {
|
||||
unit.clone()
|
||||
};
|
||||
println!("{unit_path}:");
|
||||
// let mut ranges = Vec::<(String, Range<u32>)>::new();
|
||||
// match entries.unit_section_ranges.get(&unit) {
|
||||
// Some(sections) => {
|
||||
// for (name, range) in sections {
|
||||
// ranges.push((name.clone(), range.clone()));
|
||||
// }
|
||||
// }
|
||||
// None => bail!("Failed to locate sections for unit '{unit}'"),
|
||||
// }
|
||||
// ranges.sort_by(|(_, a), (_, b)| a.start.cmp(&b.start));
|
||||
// for (name, range) in ranges {
|
||||
// println!("\t{}: [{:#010x}, {:#010x}]", name, range.start, range.end);
|
||||
// }
|
||||
}
|
||||
|
||||
_ = entries;
|
||||
// TODO
|
||||
|
||||
// let order = resolve_link_order(&entries.unit_order)?;
|
||||
// for unit in order {
|
||||
// let unit_path = if let Some((lib, name)) = unit.split_once(' ') {
|
||||
// format!("{}/{}", lib.strip_suffix(".a").unwrap_or(lib), name)
|
||||
// } else if let Some(strip) = unit.strip_suffix(".o") {
|
||||
// format!("{strip}.c")
|
||||
// } else {
|
||||
// unit.clone()
|
||||
// };
|
||||
// println!("{unit_path}:");
|
||||
// let mut ranges = Vec::<(String, Range<u32>)>::new();
|
||||
// match entries.unit_section_ranges.get(&unit) {
|
||||
// Some(sections) => {
|
||||
// for (name, range) in sections {
|
||||
// ranges.push((name.clone(), range.clone()));
|
||||
// }
|
||||
// }
|
||||
// None => bail!("Failed to locate sections for unit '{unit}'"),
|
||||
// }
|
||||
// ranges.sort_by(|(_, a), (_, b)| a.start.cmp(&b.start));
|
||||
// for (name, range) in ranges {
|
||||
// println!("\t{}: [{:#010x}, {:#010x}]", name, range.start, range.end);
|
||||
// }
|
||||
// }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn symbols(args: SymbolsArgs) -> Result<()> {
|
||||
let map = map_file(&args.map_file)?;
|
||||
let _entries = process_map(map_reader(&map))?;
|
||||
let entries = process_map(map_reader(&map))?;
|
||||
|
||||
_ = entries;
|
||||
// TODO
|
||||
|
||||
// for (address, symbol) in entries.address_to_symbol {
|
||||
// if symbol.name.starts_with('@') {
|
||||
// continue;
|
||||
|
||||
162
src/cmd/rel.rs
162
src/cmd/rel.rs
@@ -12,14 +12,16 @@ use crate::{
|
||||
analysis::{
|
||||
cfa::AnalyzerState,
|
||||
pass::{AnalysisPass, FindSaveRestSleds, FindTRKInterruptVectorTable},
|
||||
signatures::apply_signatures,
|
||||
tracker::Tracker,
|
||||
},
|
||||
cmd::dol::apply_signatures,
|
||||
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolKind},
|
||||
array_ref_mut,
|
||||
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSymbol, ObjSymbolKind},
|
||||
util::{
|
||||
dol::process_dol,
|
||||
elf::write_elf,
|
||||
nested::{NestedMap, NestedVec},
|
||||
file::{map_file, map_reader, FileIterator},
|
||||
nested::NestedMap,
|
||||
rel::process_rel,
|
||||
},
|
||||
};
|
||||
@@ -71,7 +73,8 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
|
||||
fn info(args: InfoArgs) -> Result<()> {
|
||||
let rel = process_rel(&args.rel_file)?;
|
||||
let map = map_file(args.rel_file)?;
|
||||
let rel = process_rel(map_reader(&map))?;
|
||||
println!("Read REL module ID {}", rel.module_id);
|
||||
// println!("REL: {:#?}", rel);
|
||||
Ok(())
|
||||
@@ -81,22 +84,30 @@ fn info(args: InfoArgs) -> Result<()> {
|
||||
const fn align32(x: u32) -> u32 { (x + 31) & !31 }
|
||||
|
||||
fn merge(args: MergeArgs) -> Result<()> {
|
||||
let mut module_map = BTreeMap::<u32, ObjInfo>::new();
|
||||
log::info!("Loading {}", args.dol_file.display());
|
||||
let mut obj = process_dol(&args.dol_file)?;
|
||||
apply_signatures(&mut obj)?;
|
||||
|
||||
for path in &args.rel_files {
|
||||
log::info!("Performing signature analysis");
|
||||
apply_signatures(&mut obj)?;
|
||||
let Some(arena_lo) = obj.arena_lo else { bail!("Failed to locate __ArenaLo in DOL") };
|
||||
|
||||
let mut processed = 0;
|
||||
let mut module_map = BTreeMap::<u32, ObjInfo>::new();
|
||||
for result in FileIterator::new(&args.rel_files)? {
|
||||
let (path, entry) = result?;
|
||||
log::info!("Loading {}", path.display());
|
||||
let obj = process_rel(path)?;
|
||||
let obj = process_rel(entry.as_reader())?;
|
||||
match module_map.entry(obj.module_id) {
|
||||
btree_map::Entry::Vacant(e) => e.insert(obj),
|
||||
btree_map::Entry::Occupied(_) => bail!("Duplicate module ID {}", obj.module_id),
|
||||
};
|
||||
processed += 1;
|
||||
}
|
||||
|
||||
log::info!("Merging {} REL(s)", processed);
|
||||
let mut section_map: BTreeMap<u32, BTreeMap<u32, u32>> = BTreeMap::new();
|
||||
let mut offset = align32(obj.arena_lo.unwrap() + 0x2000);
|
||||
for (_, module) in &module_map {
|
||||
let mut offset = align32(arena_lo + 0x2000);
|
||||
for module in module_map.values() {
|
||||
for mod_section in &module.sections {
|
||||
let section_idx = obj.sections.len();
|
||||
ensure!(mod_section.relocations.is_empty(), "Unsupported relocations during merge");
|
||||
@@ -115,9 +126,8 @@ fn merge(args: MergeArgs) -> Result<()> {
|
||||
section_known: mod_section.section_known,
|
||||
});
|
||||
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 {
|
||||
for (_, mod_symbol) in module.symbols.for_section(mod_section) {
|
||||
obj.symbols.add_direct(ObjSymbol {
|
||||
name: mod_symbol.name.clone(),
|
||||
demangled_name: mod_symbol.demangled_name.clone(),
|
||||
address: mod_symbol.address + offset as u64,
|
||||
@@ -126,44 +136,41 @@ fn merge(args: MergeArgs) -> Result<()> {
|
||||
size_known: mod_symbol.size_known,
|
||||
flags: mod_symbol.flags,
|
||||
kind: mod_symbol.kind,
|
||||
});
|
||||
align: None,
|
||||
data_kind: Default::default(),
|
||||
})?;
|
||||
}
|
||||
offset += align32(mod_section.size as u32);
|
||||
}
|
||||
}
|
||||
|
||||
let mut symbol_maps = Vec::new();
|
||||
for section in &obj.sections {
|
||||
symbol_maps.push(obj.build_symbol_map(section.index)?);
|
||||
}
|
||||
|
||||
// Apply relocations
|
||||
for (_, module) in &module_map {
|
||||
log::info!("Applying REL relocations");
|
||||
for module in module_map.values() {
|
||||
for rel_reloc in &module.unresolved_relocations {
|
||||
let source_addr =
|
||||
section_map[&module.module_id][&(rel_reloc.section as u32)] + rel_reloc.address;
|
||||
let source_addr = (section_map[&module.module_id][&(rel_reloc.section as u32)]
|
||||
+ rel_reloc.address)
|
||||
& !3;
|
||||
let target_addr = if rel_reloc.module_id == 0 {
|
||||
rel_reloc.addend
|
||||
} else {
|
||||
let base = section_map[&rel_reloc.module_id][&(rel_reloc.target_section as u32)];
|
||||
let addend = rel_reloc.addend;
|
||||
base + addend
|
||||
let section_map = §ion_map.get(&rel_reloc.module_id).with_context(|| {
|
||||
format!("Relocation against unknown module ID {}", rel_reloc.module_id)
|
||||
})?;
|
||||
section_map[&(rel_reloc.target_section as u32)] + rel_reloc.addend
|
||||
};
|
||||
let source_section = obj.section_at(source_addr)?;
|
||||
let target_section = obj.section_at(target_addr)?;
|
||||
let target_section_index = target_section.index;
|
||||
let source_section_index = obj.section_at(source_addr)?.index;
|
||||
let target_section_index = obj.section_at(target_addr)?.index;
|
||||
|
||||
// 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_addr).rev() {
|
||||
for (_addr, symbol_idxs) in obj.symbols.indexes_for_range(..=target_addr).rev() {
|
||||
let symbol_idx = if symbol_idxs.len() == 1 {
|
||||
symbol_idxs.first().cloned().unwrap()
|
||||
} else {
|
||||
let mut symbol_idxs = symbol_idxs.clone();
|
||||
let mut symbol_idxs = symbol_idxs.to_vec();
|
||||
symbol_idxs.sort_by_key(|&symbol_idx| {
|
||||
let symbol = &obj.symbols[symbol_idx];
|
||||
let symbol = obj.symbols.at(symbol_idx);
|
||||
let mut rank = match symbol.kind {
|
||||
ObjSymbolKind::Function | ObjSymbolKind::Object => {
|
||||
match rel_reloc.kind {
|
||||
@@ -199,7 +206,7 @@ fn merge(args: MergeArgs) -> Result<()> {
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
let symbol = &obj.symbols[symbol_idx];
|
||||
let symbol = obj.symbols.at(symbol_idx);
|
||||
if symbol.address == target_addr as u64 {
|
||||
result = Some(symbol_idx);
|
||||
break;
|
||||
@@ -214,12 +221,11 @@ fn merge(args: MergeArgs) -> Result<()> {
|
||||
result
|
||||
};
|
||||
let (symbol_idx, addend) = if let Some(symbol_idx) = target_symbol {
|
||||
let symbol = &obj.symbols[symbol_idx];
|
||||
let symbol = obj.symbols.at(symbol_idx);
|
||||
(symbol_idx, target_addr as i64 - symbol.address as i64)
|
||||
} else {
|
||||
// Create a new label
|
||||
let symbol_idx = obj.symbols.len();
|
||||
obj.symbols.push(ObjSymbol {
|
||||
let symbol_idx = obj.symbols.add_direct(ObjSymbol {
|
||||
name: String::new(),
|
||||
demangled_name: None,
|
||||
address: target_addr as u64,
|
||||
@@ -228,11 +234,12 @@ fn merge(args: MergeArgs) -> Result<()> {
|
||||
size_known: false,
|
||||
flags: Default::default(),
|
||||
kind: Default::default(),
|
||||
});
|
||||
sym_map.nested_push(target_addr, symbol_idx);
|
||||
align: None,
|
||||
data_kind: Default::default(),
|
||||
})?;
|
||||
(symbol_idx, 0)
|
||||
};
|
||||
obj.sections[target_section_index].relocations.push(ObjReloc {
|
||||
obj.sections[source_section_index].relocations.push(ObjReloc {
|
||||
kind: rel_reloc.kind,
|
||||
address: source_addr as u64,
|
||||
target_symbol: symbol_idx,
|
||||
@@ -241,29 +248,11 @@ fn merge(args: MergeArgs) -> Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
// Apply known functions from extab
|
||||
let mut state = AnalyzerState::default();
|
||||
for (&addr, &size) in &obj.known_functions {
|
||||
state.function_entries.insert(addr);
|
||||
state.function_bounds.insert(addr, addr + size);
|
||||
}
|
||||
for symbol in &obj.symbols {
|
||||
if symbol.kind != ObjSymbolKind::Function {
|
||||
continue;
|
||||
}
|
||||
state.function_entries.insert(symbol.address as u32);
|
||||
if !symbol.size_known {
|
||||
continue;
|
||||
}
|
||||
state.function_bounds.insert(symbol.address as u32, (symbol.address + symbol.size) as u32);
|
||||
}
|
||||
// Also check the start of each code section
|
||||
for section in &obj.sections {
|
||||
if section.kind == ObjSectionKind::Code {
|
||||
state.function_entries.insert(section.address as u32);
|
||||
}
|
||||
}
|
||||
// Apply relocations to code/data for analyzer
|
||||
link_relocations(&mut obj)?;
|
||||
|
||||
log::info!("Detecting function boundaries");
|
||||
let mut state = AnalyzerState::default();
|
||||
state.detect_functions(&obj)?;
|
||||
log::info!("Discovered {} functions", state.function_slices.len());
|
||||
|
||||
@@ -281,8 +270,57 @@ fn merge(args: MergeArgs) -> Result<()> {
|
||||
// Write ELF
|
||||
let mut file = File::create(&args.out_file)
|
||||
.with_context(|| format!("Failed to create '{}'", args.out_file.display()))?;
|
||||
log::info!("Writing {}", args.out_file.display());
|
||||
let out_object = write_elf(&obj)?;
|
||||
file.write_all(&out_object)?;
|
||||
file.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn link_relocations(obj: &mut ObjInfo) -> Result<()> {
|
||||
for section in &mut obj.sections {
|
||||
for reloc in §ion.relocations {
|
||||
let source_address = reloc.address /*& !3*/;
|
||||
let target_address =
|
||||
(obj.symbols.address_of(reloc.target_symbol) as i64 + reloc.addend) as u32;
|
||||
let ins_ref =
|
||||
array_ref_mut!(section.data, (source_address - section.address) as usize, 4);
|
||||
let mut ins = u32::from_be_bytes(*ins_ref);
|
||||
match reloc.kind {
|
||||
ObjRelocKind::Absolute => {
|
||||
ins = target_address;
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Hi => {
|
||||
ins = (ins & 0xffff0000) | ((target_address >> 16) & 0xffff);
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Ha => {
|
||||
ins = (ins & 0xffff0000) | (((target_address + 0x8000) >> 16) & 0xffff);
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Lo => {
|
||||
ins = (ins & 0xffff0000) | (target_address & 0xffff);
|
||||
}
|
||||
ObjRelocKind::PpcRel24 => {
|
||||
let diff = target_address as i32 - source_address as i32;
|
||||
ensure!(
|
||||
(-0x2000000..0x2000000).contains(&diff),
|
||||
"R_PPC_REL24 relocation out of range"
|
||||
);
|
||||
ins = (ins & !0x3fffffc) | (diff as u32 & 0x3fffffc);
|
||||
}
|
||||
ObjRelocKind::PpcRel14 => {
|
||||
let diff = target_address as i32 - source_address as i32;
|
||||
ensure!(
|
||||
(-0x2000..0x2000).contains(&diff),
|
||||
"R_PPC_REL14 relocation out of range"
|
||||
);
|
||||
ins = (ins & !0xfffc) | (diff as u32 & 0xfffc);
|
||||
}
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
// Unused in RELs
|
||||
}
|
||||
};
|
||||
*ins_ref = ins.to_be_bytes();
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
|
||||
fn info(args: InfoArgs) -> Result<()> {
|
||||
let rso = process_rso(&args.rso_file)?;
|
||||
let rso = process_rso(args.rso_file)?;
|
||||
println!("Read RSO module {}", rso.name);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ use argh::FromArgs;
|
||||
use filetime::{set_file_mtime, FileTime};
|
||||
use sha1::{Digest, Sha1};
|
||||
|
||||
use crate::util::file::process_rsp;
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Print or check SHA1 (160-bit) checksums.
|
||||
#[argh(subcommand, name = "shasum")]
|
||||
@@ -17,8 +19,8 @@ pub struct Args {
|
||||
/// check SHA sums against given list
|
||||
check: bool,
|
||||
#[argh(positional)]
|
||||
/// path to file
|
||||
file: PathBuf,
|
||||
/// path to input file(s)
|
||||
files: Vec<PathBuf>,
|
||||
#[argh(option, short = 'o')]
|
||||
/// touch output file on successful check
|
||||
output: Option<PathBuf>,
|
||||
@@ -27,16 +29,23 @@ pub struct Args {
|
||||
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.display()))?;
|
||||
if args.check {
|
||||
check(args, file)
|
||||
} else {
|
||||
hash(args, file)
|
||||
for path in process_rsp(&args.files)? {
|
||||
let file = File::open(&path)
|
||||
.with_context(|| format!("Failed to open file '{}'", path.display()))?;
|
||||
if args.check {
|
||||
check(file)?
|
||||
} else {
|
||||
hash(file, &path)?
|
||||
}
|
||||
}
|
||||
if let Some(out_path) = args.output {
|
||||
touch(&out_path)
|
||||
.with_context(|| format!("Failed to touch output file '{}'", out_path.display()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn check(args: Args, file: File) -> Result<()> {
|
||||
fn check(file: File) -> Result<()> {
|
||||
let reader = BufReader::new(file);
|
||||
let mut mismatches = 0usize;
|
||||
for line in reader.lines() {
|
||||
@@ -68,19 +77,15 @@ fn check(args: Args, file: File) -> Result<()> {
|
||||
eprintln!("WARNING: {mismatches} computed checksum did NOT match");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if let Some(out_path) = args.output {
|
||||
touch(&out_path)
|
||||
.with_context(|| format!("Failed to touch output file '{}'", out_path.display()))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn hash(args: Args, file: File) -> Result<()> {
|
||||
fn hash(file: File, path: &Path) -> Result<()> {
|
||||
let hash = file_sha1(file)?;
|
||||
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.display());
|
||||
println!("{} {}", hash_str, path.display());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user