mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-14 07:36:25 +00:00
Support loading Wii Menu (BootStage) DOL files
Also adds support to elf2dol to extract the inner DOL from a BootStage DOL.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::BTreeMap,
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
fmt::{Debug, Display, Formatter, UpperHex},
|
||||
ops::{Add, AddAssign, BitAnd, Sub},
|
||||
};
|
||||
@@ -572,6 +572,26 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
|
||||
Some((sda2_base, sda_base)) => {
|
||||
obj.sda2_base = Some(sda2_base);
|
||||
obj.sda_base = Some(sda_base);
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_SDA2_BASE_".to_string(),
|
||||
address: sda2_base as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: "_SDA_BASE_".to_string(),
|
||||
address: sda_base as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Global.into()),
|
||||
..Default::default()
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
Ok(true)
|
||||
}
|
||||
None => Ok(false),
|
||||
@@ -581,7 +601,7 @@ pub fn locate_sda_bases(obj: &mut ObjInfo) -> Result<bool> {
|
||||
/// ProDG hardcodes .bss and .sbss section initialization in `entry`
|
||||
/// This function locates the memset calls and returns a list of
|
||||
/// (address, size) pairs for the .bss sections.
|
||||
pub fn locate_bss_memsets(obj: &mut ObjInfo) -> Result<Vec<(u32, u32)>> {
|
||||
pub fn locate_bss_memsets(obj: &ObjInfo) -> Result<Vec<(u32, u32)>> {
|
||||
let mut bss_sections: Vec<(u32, u32)> = Vec::new();
|
||||
let Some(entry) = obj.entry else {
|
||||
return Ok(bss_sections);
|
||||
@@ -632,3 +652,50 @@ pub fn locate_bss_memsets(obj: &mut ObjInfo) -> Result<Vec<(u32, u32)>> {
|
||||
)?;
|
||||
Ok(bss_sections)
|
||||
}
|
||||
|
||||
/// Execute VM from specified entry point following inner-section branches and function calls,
|
||||
/// noting all branch targets outside the current section.
|
||||
pub fn locate_cross_section_branch_targets(
|
||||
obj: &ObjInfo,
|
||||
entry: SectionAddress,
|
||||
) -> Result<BTreeSet<SectionAddress>> {
|
||||
let mut branch_targets = BTreeSet::<SectionAddress>::new();
|
||||
let mut executor = Executor::new(obj);
|
||||
executor.push(entry, VM::new(), false);
|
||||
executor.run(
|
||||
obj,
|
||||
|ExecCbData { executor, vm, result, ins_addr, section: _, ins: _, block_start: _ }| {
|
||||
match result {
|
||||
StepResult::Continue | StepResult::LoadStore { .. } => {
|
||||
Ok(ExecCbResult::<()>::Continue)
|
||||
}
|
||||
StepResult::Illegal => bail!("Illegal instruction @ {}", ins_addr),
|
||||
StepResult::Jump(target) => {
|
||||
if let BranchTarget::Address(RelocationTarget::Address(addr)) = target {
|
||||
if addr.section == entry.section {
|
||||
executor.push(addr, vm.clone_all(), true);
|
||||
} else {
|
||||
branch_targets.insert(addr);
|
||||
}
|
||||
}
|
||||
Ok(ExecCbResult::EndBlock)
|
||||
}
|
||||
StepResult::Branch(branches) => {
|
||||
for branch in branches {
|
||||
if let BranchTarget::Address(RelocationTarget::Address(addr)) =
|
||||
branch.target
|
||||
{
|
||||
if addr.section == entry.section {
|
||||
executor.push(addr, branch.vm, true);
|
||||
} else {
|
||||
branch_targets.insert(addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(ExecCbResult::Continue)
|
||||
}
|
||||
}
|
||||
},
|
||||
)?;
|
||||
Ok(branch_targets)
|
||||
}
|
||||
|
||||
@@ -97,8 +97,8 @@ fn check_prologue_sequence(
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_stwu(ins: Ins) -> bool {
|
||||
// stwu r1, d(r1)
|
||||
ins.op == Opcode::Stwu && ins.field_rs() == 1 && ins.field_ra() == 1
|
||||
// stwu[x] r1, d(r1)
|
||||
matches!(ins.op, Opcode::Stwu | Opcode::Stwux) && ins.field_rs() == 1 && ins.field_ra() == 1
|
||||
}
|
||||
#[inline(always)]
|
||||
fn is_stw(ins: Ins) -> bool {
|
||||
@@ -213,7 +213,11 @@ impl FunctionSlices {
|
||||
ins.op == Opcode::Or && ins.field_rd() == 1
|
||||
}
|
||||
|
||||
if check_sequence(section, addr, Some(ins), &[(&is_mtlr, &is_addi), (&is_or, &is_mtlr)])? {
|
||||
if check_sequence(section, addr, Some(ins), &[
|
||||
(&is_mtlr, &is_addi),
|
||||
(&is_mtlr, &is_or),
|
||||
(&is_or, &is_mtlr),
|
||||
])? {
|
||||
if let Some(epilogue) = self.epilogue {
|
||||
if epilogue != addr {
|
||||
bail!("Found duplicate epilogue: {:#010X} and {:#010X}", epilogue, addr)
|
||||
@@ -373,7 +377,14 @@ impl FunctionSlices {
|
||||
function_end.or_else(|| self.end()),
|
||||
)?;
|
||||
log::debug!("-> size {}: {:?}", size, entries);
|
||||
if (entries.contains(&next_address) || self.blocks.contains_key(&next_address))
|
||||
let max_block = self
|
||||
.blocks
|
||||
.keys()
|
||||
.next_back()
|
||||
.copied()
|
||||
.unwrap_or(next_address)
|
||||
.max(next_address);
|
||||
if entries.iter().any(|&addr| addr > function_start && addr <= max_block)
|
||||
&& !entries.iter().any(|&addr| {
|
||||
self.is_known_function(known_functions, addr)
|
||||
.is_some_and(|fn_addr| fn_addr != function_start)
|
||||
@@ -736,7 +747,7 @@ impl FunctionSlices {
|
||||
}
|
||||
}
|
||||
// If we discovered a function prologue, known tail call.
|
||||
if slices.prologue.is_some() {
|
||||
if slices.prologue.is_some() || slices.has_r1_load {
|
||||
log::trace!("Prologue discovered; known tail call: {:#010X}", addr);
|
||||
return TailCallResult::Is;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user