mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-15 08:06:14 +00:00
Add dtk extab clean & config.yml clean_extab
It was discovered that certain extab actions contain uninitalized data from the compiler. This provides a way to zero out uninitialized data in DOL or object files. Usage: `dtk extab clean input.dol output.dol` A `clean_extab` setting was added to config.yml, so projects can link the cleaned objects and target the cleaned DOL hash.
This commit is contained in:
@@ -47,6 +47,7 @@ use crate::{
|
||||
diff::{calc_diff_ranges, print_diff, process_code},
|
||||
dol::process_dol,
|
||||
elf::{process_elf, write_elf},
|
||||
extab::clean_extab,
|
||||
file::{
|
||||
buf_copy_with_hash, buf_writer, check_hash_str, touch, verify_hash, FileIterator,
|
||||
FileReadInfo,
|
||||
@@ -293,6 +294,9 @@ pub struct ModuleConfig {
|
||||
pub block_relocations: Vec<BlockRelocationConfig>,
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub add_relocations: Vec<AddRelocationConfig>,
|
||||
/// Process exception tables and zero out uninitialized data.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub clean_extab: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
|
||||
@@ -818,17 +822,29 @@ struct AnalyzeResult {
|
||||
splits_cache: Option<FileReadInfo>,
|
||||
}
|
||||
|
||||
fn load_analyze_dol(config: &ProjectConfig, object_base: &ObjectBase) -> Result<AnalyzeResult> {
|
||||
let object_path = object_base.join(&config.base.object);
|
||||
fn load_dol_module(
|
||||
config: &ModuleConfig,
|
||||
object_base: &ObjectBase,
|
||||
) -> Result<(ObjInfo, Utf8NativePathBuf)> {
|
||||
let object_path = object_base.join(&config.object);
|
||||
log::debug!("Loading {}", object_path);
|
||||
let mut obj = {
|
||||
let mut file = object_base.open(&config.base.object)?;
|
||||
let mut file = object_base.open(&config.object)?;
|
||||
let data = file.map()?;
|
||||
if let Some(hash_str) = &config.base.hash {
|
||||
if let Some(hash_str) = &config.hash {
|
||||
verify_hash(data, hash_str)?;
|
||||
}
|
||||
process_dol(data, config.base.name())?
|
||||
process_dol(data, config.name())?
|
||||
};
|
||||
if config.clean_extab.unwrap_or(false) {
|
||||
log::debug!("Cleaning extab for {}", config.name());
|
||||
clean_extab(&mut obj)?;
|
||||
}
|
||||
Ok((obj, object_path))
|
||||
}
|
||||
|
||||
fn load_analyze_dol(config: &ProjectConfig, object_base: &ObjectBase) -> Result<AnalyzeResult> {
|
||||
let (mut obj, object_path) = load_dol_module(&config.base, object_base)?;
|
||||
let mut dep = vec![object_path];
|
||||
|
||||
if let Some(comment_version) = config.mw_comment_version {
|
||||
@@ -1658,15 +1674,7 @@ fn diff(args: DiffArgs) -> Result<()> {
|
||||
let config: ProjectConfig = serde_yaml::from_reader(config_file.as_mut())?;
|
||||
let object_base = find_object_base(&config)?;
|
||||
|
||||
log::info!("Loading {}", object_base.join(&config.base.object));
|
||||
let mut obj = {
|
||||
let mut file = object_base.open(&config.base.object)?;
|
||||
let data = file.map()?;
|
||||
if let Some(hash_str) = &config.base.hash {
|
||||
verify_hash(data, hash_str)?;
|
||||
}
|
||||
process_dol(data, config.base.name())?
|
||||
};
|
||||
let (mut obj, _object_path) = load_dol_module(&config.base, &object_base)?;
|
||||
|
||||
if let Some(symbols_path) = &config.base.symbols {
|
||||
apply_symbols_file(&symbols_path.with_encoding(), &mut obj)?;
|
||||
@@ -1882,15 +1890,7 @@ fn apply(args: ApplyArgs) -> Result<()> {
|
||||
let config: ProjectConfig = serde_yaml::from_reader(config_file.as_mut())?;
|
||||
let object_base = find_object_base(&config)?;
|
||||
|
||||
log::info!("Loading {}", object_base.join(&config.base.object));
|
||||
let mut obj = {
|
||||
let mut file = object_base.open(&config.base.object)?;
|
||||
let data = file.map()?;
|
||||
if let Some(hash_str) = &config.base.hash {
|
||||
verify_hash(data, hash_str)?;
|
||||
}
|
||||
process_dol(data, config.base.name())?
|
||||
};
|
||||
let (mut obj, _object_path) = load_dol_module(&config.base, &object_base)?;
|
||||
|
||||
let Some(symbols_path) = &config.base.symbols else {
|
||||
bail!("No symbols file specified in config");
|
||||
|
||||
@@ -6,8 +6,12 @@ use object::{Architecture, Endianness, Object, ObjectKind, ObjectSection, Sectio
|
||||
use typed_path::Utf8NativePathBuf;
|
||||
|
||||
use crate::{
|
||||
obj::ObjSectionKind,
|
||||
util::{alf::ALF_MAGIC, dol::process_dol, file::buf_writer, path::native_path},
|
||||
util::{
|
||||
alf::ALF_MAGIC,
|
||||
dol::{process_dol, write_dol},
|
||||
file::buf_writer,
|
||||
path::native_path,
|
||||
},
|
||||
vfs::open_file,
|
||||
};
|
||||
|
||||
@@ -161,84 +165,8 @@ pub fn run(args: Args) -> Result<()> {
|
||||
|
||||
fn convert_alf(args: Args, data: &[u8]) -> Result<()> {
|
||||
let obj = process_dol(data, "")?;
|
||||
|
||||
let mut header = DolHeader { entry_point: obj.entry.unwrap() as u32, ..Default::default() };
|
||||
let mut offset = 0x100u32;
|
||||
let mut out = buf_writer(&args.dol_file)?;
|
||||
out.seek(SeekFrom::Start(offset as u64))?;
|
||||
|
||||
// Text sections
|
||||
for (_, section) in obj.sections.iter().filter(|(_, s)| s.kind == ObjSectionKind::Code) {
|
||||
log::debug!("Processing text section '{}'", section.name);
|
||||
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(|| {
|
||||
anyhow!("Too many text sections (while processing '{}')", section.name)
|
||||
})? = DolSection { offset, address, size };
|
||||
header.text_section_count += 1;
|
||||
write_aligned(&mut out, §ion.data, size)?;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
// Data sections
|
||||
for (_, section) in obj
|
||||
.sections
|
||||
.iter()
|
||||
.filter(|(_, s)| matches!(s.kind, ObjSectionKind::Data | ObjSectionKind::ReadOnlyData))
|
||||
{
|
||||
log::debug!("Processing data section '{}'", section.name);
|
||||
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(|| {
|
||||
anyhow!("Too many data sections (while processing '{}')", section.name)
|
||||
})? = DolSection { offset, address, size };
|
||||
header.data_section_count += 1;
|
||||
write_aligned(&mut out, §ion.data, size)?;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
// BSS sections
|
||||
for (_, section) in obj.sections.iter().filter(|(_, s)| s.kind == ObjSectionKind::Bss) {
|
||||
let address = section.address as u32;
|
||||
let size = section.size as u32;
|
||||
if header.bss_address == 0 {
|
||||
header.bss_address = address;
|
||||
}
|
||||
header.bss_size = (address + size) - header.bss_address;
|
||||
}
|
||||
|
||||
// Offsets
|
||||
out.rewind()?;
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.offset.to_be_bytes())?;
|
||||
}
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.offset.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// Addresses
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.address.to_be_bytes())?;
|
||||
}
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.address.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// Sizes
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.size.to_be_bytes())?;
|
||||
}
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.size.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// BSS + entry
|
||||
out.write_all(&header.bss_address.to_be_bytes())?;
|
||||
out.write_all(&header.bss_size.to_be_bytes())?;
|
||||
out.write_all(&header.entry_point.to_be_bytes())?;
|
||||
|
||||
// Done!
|
||||
out.flush()?;
|
||||
write_dol(&obj, &mut out)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
69
src/cmd/extab.rs
Normal file
69
src/cmd/extab.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
use std::io::Write;
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use argp::FromArgs;
|
||||
use typed_path::Utf8NativePathBuf;
|
||||
|
||||
use crate::{
|
||||
util,
|
||||
util::{
|
||||
dol::{process_dol, write_dol},
|
||||
elf::{is_elf_file, process_elf, write_elf},
|
||||
file::buf_writer,
|
||||
path::native_path,
|
||||
},
|
||||
vfs::open_file,
|
||||
};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Commands for processing extab (exception table) data.
|
||||
#[argp(subcommand, name = "extab")]
|
||||
pub struct Args {
|
||||
#[argp(subcommand)]
|
||||
command: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argp(subcommand)]
|
||||
enum SubCommand {
|
||||
Clean(CleanArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Rewrites extab data in a DOL or ELF file, zeroing out any uninitialized padding bytes.
|
||||
#[argp(subcommand, name = "clean")]
|
||||
pub struct CleanArgs {
|
||||
#[argp(positional, from_str_fn(native_path))]
|
||||
/// path to input file
|
||||
input: Utf8NativePathBuf,
|
||||
#[argp(positional, from_str_fn(native_path))]
|
||||
/// path to output file
|
||||
output: Utf8NativePathBuf,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Clean(clean_args) => clean_extab(clean_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn clean_extab(args: CleanArgs) -> Result<()> {
|
||||
let is_elf = is_elf_file(&args.input)?;
|
||||
let mut obj = if is_elf {
|
||||
process_elf(&args.input)?
|
||||
} else {
|
||||
let mut file = open_file(&args.input, true)?;
|
||||
let name = args.input.file_stem().unwrap_or_default();
|
||||
process_dol(file.map()?, name)?
|
||||
};
|
||||
let num_cleaned = util::extab::clean_extab(&mut obj)?;
|
||||
tracing::debug!("Cleaned {num_cleaned} extab symbols");
|
||||
let mut out = buf_writer(&args.output)?;
|
||||
if is_elf {
|
||||
let data = write_elf(&obj, false)?;
|
||||
out.write_all(&data).context("Failed to write ELF")?;
|
||||
} else {
|
||||
write_dol(&obj, &mut out).context("Failed to write DOL")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -6,6 +6,7 @@ pub mod dol;
|
||||
pub mod dwarf;
|
||||
pub mod elf;
|
||||
pub mod elf2dol;
|
||||
pub mod extab;
|
||||
pub mod map;
|
||||
pub mod nlzss;
|
||||
pub mod rarc;
|
||||
|
||||
Reference in New Issue
Block a user