Migrate argh to argp, topological-sort to petgraph

This commit is contained in:
Luke Street 2023-08-03 18:55:57 -04:00
parent 8660984d40
commit bd0422e92a
18 changed files with 366 additions and 260 deletions

77
Cargo.lock generated
View File

@ -79,6 +79,27 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f" checksum = "64cb94155d965e3d37ffbbe7cc5b82c3dd79dd33bd48e536f73d2cfb8d85506f"
[[package]]
name = "argp"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c16c577a1a3b720a90eb2127bd0ae61530a71064d1a6babaaaa87f6174b9f1"
dependencies = [
"argp_derive",
]
[[package]]
name = "argp_derive"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe3763c8b5e0ef2f7d0df26daa671808cc75e2d81547f63ccca96bf045e41799"
dependencies = [
"proc-macro2",
"pulldown-cmark",
"quote",
"syn 1.0.107",
]
[[package]] [[package]]
name = "autocfg" name = "autocfg"
version = "1.1.0" version = "1.1.0"
@ -197,7 +218,7 @@ version = "0.3.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ar", "ar",
"argh", "argp",
"base16ct", "base16ct",
"base64", "base64",
"byteorder", "byteorder",
@ -219,6 +240,7 @@ dependencies = [
"num_enum", "num_enum",
"object 0.31.1", "object 0.31.1",
"once_cell", "once_cell",
"petgraph",
"ppc750cl", "ppc750cl",
"regex", "regex",
"rmp-serde", "rmp-serde",
@ -227,7 +249,6 @@ dependencies = [
"serde_yaml", "serde_yaml",
"sha-1", "sha-1",
"smallvec", "smallvec",
"topological-sort",
] ]
[[package]] [[package]]
@ -343,6 +364,15 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "getopts"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "gimli" name = "gimli"
version = "0.27.0" version = "0.27.0"
@ -577,6 +607,16 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba"
[[package]]
name = "petgraph"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4"
dependencies = [
"fixedbitset",
"indexmap 1.9.2",
]
[[package]] [[package]]
name = "ppc750cl" name = "ppc750cl"
version = "0.2.0" version = "0.2.0"
@ -606,6 +646,18 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "pulldown-cmark"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998"
dependencies = [
"bitflags",
"getopts",
"memchr",
"unicase",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.29" version = "1.0.29"
@ -822,24 +874,33 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "topological-sort"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
[[package]] [[package]]
name = "typenum" name = "typenum"
version = "1.15.0" version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicase"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6"
dependencies = [
"version_check",
]
[[package]] [[package]]
name = "unicode-ident" name = "unicode-ident"
version = "1.0.5" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]] [[package]]
name = "unsafe-libyaml" name = "unsafe-libyaml"
version = "0.2.8" version = "0.2.8"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "decomp-toolkit" name = "decomp-toolkit"
description = "GameCube/Wii decompilation project tools." description = "Yet another GameCube/Wii decompilation toolkit."
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.3.0" version = "0.3.0"
@ -23,7 +23,7 @@ strip = "debuginfo"
[dependencies] [dependencies]
anyhow = { version = "1.0.71", features = ["backtrace"] } anyhow = { version = "1.0.71", features = ["backtrace"] }
ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "write_symbol_table" } ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "write_symbol_table" }
argh = "0.1.10" argp = "0.3.0"
base16ct = "0.2.0" base16ct = "0.2.0"
base64 = "0.21.2" base64 = "0.21.2"
byteorder = "1.4.3" byteorder = "1.4.3"
@ -45,6 +45,7 @@ multimap = "0.9.0"
num_enum = "0.6.1" num_enum = "0.6.1"
object = { version = "0.31.1", features = ["read_core", "std", "elf", "write_std"], default-features = false } object = { version = "0.31.1", features = ["read_core", "std", "elf", "write_std"], default-features = false }
once_cell = "1.18.0" once_cell = "1.18.0"
petgraph = "0.6.3"
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" } ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" }
regex = "1.9.0" regex = "1.9.0"
serde = "1.0.166" serde = "1.0.166"
@ -52,8 +53,6 @@ serde_repr = "0.1.14"
serde_yaml = "0.9.22" serde_yaml = "0.9.22"
sha-1 = "0.10.1" sha-1 = "0.10.1"
smallvec = "1.11.0" smallvec = "1.11.0"
topological-sort = "0.2.2"
[build-dependencies] [build-dependencies]
anyhow = { version = "1.0.71", features = ["backtrace"] } anyhow = { version = "1.0.71", features = ["backtrace"] }

View File

@ -1,64 +0,0 @@
// From https://gist.github.com/suluke/e0c672492126be0a4f3b4f0e1115d77c
//! Extend `argh` to be better integrated with the `cargo` ecosystem
//!
//! For now, this only adds a --version/-V option which causes early-exit.
use argh::{FromArgs, TopLevelCommand};
struct ArgsOrVersion<T: FromArgs>(T);
impl<T> TopLevelCommand for ArgsOrVersion<T> where T: FromArgs {}
impl<T> FromArgs for ArgsOrVersion<T>
where T: FromArgs
{
fn from_args(command_name: &[&str], args: &[&str]) -> Result<Self, argh::EarlyExit> {
/// Also use argh for catching `--version`-only invocations
#[derive(FromArgs)]
struct Version {
/// print version information and exit
#[argh(switch, short = 'V')]
pub version: bool,
}
match Version::from_args(command_name, args) {
Ok(v) => {
if v.version {
Err(argh::EarlyExit {
output: format!(
"{} {} {}",
command_name.first().unwrap_or(&""),
env!("CARGO_PKG_VERSION"),
env!("GIT_COMMIT_SHA"),
),
status: Ok(()),
})
} else {
// seems args are empty
T::from_args(command_name, args).map(Self)
}
}
Err(exit) => match exit.status {
Ok(()) => {
// must have been --help
let help = match T::from_args(command_name, &["--help"]) {
Ok(_) => unreachable!(),
Err(exit) => exit.output,
};
Err(argh::EarlyExit {
output: format!(
"{help} -V, --version print version information and exit"
),
status: Ok(()),
})
}
Err(()) => T::from_args(command_name, args).map(Self),
},
}
}
}
/// Create a `FromArgs` type from the current processs `env::args`.
///
/// This function will exit early from the current process if argument parsing was unsuccessful or if information like `--help` was requested.
/// Error messages will be printed to stderr, and `--help` output to stdout.
pub fn from_env<T>() -> T
where T: TopLevelCommand {
argh::from_env::<ArgsOrVersion<T>>().0
}

61
src/argp_version.rs Normal file
View File

@ -0,0 +1,61 @@
// Originally from https://gist.github.com/suluke/e0c672492126be0a4f3b4f0e1115d77c
//! Extend `argp` to be better integrated with the `cargo` ecosystem
//!
//! For now, this only adds a --version/-V option which causes early-exit.
use std::ffi::OsStr;
use argp::{parser::ParseGlobalOptions, EarlyExit, FromArgs, TopLevelCommand};
struct ArgsOrVersion<T: FromArgs>(T);
impl<T> TopLevelCommand for ArgsOrVersion<T> where T: FromArgs {}
impl<T> FromArgs for ArgsOrVersion<T>
where T: FromArgs
{
fn _from_args(
command_name: &[&str],
args: &[&OsStr],
parent: Option<&mut dyn ParseGlobalOptions>,
) -> Result<Self, EarlyExit> {
/// Also use argp for catching `--version`-only invocations
#[derive(FromArgs)]
struct Version {
/// Print version information and exit.
#[argp(switch, short = 'V')]
pub version: bool,
}
match Version::from_args(command_name, args) {
Ok(v) => {
if v.version {
println!(
"{} {} {}",
command_name.first().unwrap_or(&""),
env!("CARGO_PKG_VERSION"),
env!("GIT_COMMIT_SHA"),
);
std::process::exit(0);
} else {
// Pass through empty arguments
T::_from_args(command_name, args, parent).map(Self)
}
}
Err(exit) => match exit {
EarlyExit::Help(_help) => {
// TODO: Chain help info from Version
// For now, we just put the switch on T as well
T::from_args(command_name, &["--help"]).map(Self)
}
EarlyExit::Err(_) => T::_from_args(command_name, args, parent).map(Self),
},
}
}
}
/// Create a `FromArgs` type from the current processs `env::args`.
///
/// This function will exit early from the current process if argument parsing was unsuccessful or if information like `--help` was requested.
/// Error messages will be printed to stderr, and `--help` output to stdout.
pub fn from_env<T>() -> T
where T: TopLevelCommand {
argp::parse_args_or_exit::<ArgsOrVersion<T>>(argp::DEFAULT).0
}

View File

@ -6,33 +6,33 @@ use std::{
}; };
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use argh::FromArgs; use argp::FromArgs;
use object::{Object, ObjectSymbol, SymbolScope}; use object::{Object, ObjectSymbol, SymbolScope};
use crate::util::file::{map_file, process_rsp}; use crate::util::file::{map_file, process_rsp};
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing static libraries. /// Commands for processing static libraries.
#[argh(subcommand, name = "ar")] #[argp(subcommand, name = "ar")]
pub struct Args { pub struct Args {
#[argh(subcommand)] #[argp(subcommand)]
command: SubCommand, command: SubCommand,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] #[argp(subcommand)]
enum SubCommand { enum SubCommand {
Create(CreateArgs), Create(CreateArgs),
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Creates a static library. /// Creates a static library.
#[argh(subcommand, name = "create")] #[argp(subcommand, name = "create")]
pub struct CreateArgs { pub struct CreateArgs {
#[argh(positional)] #[argp(positional)]
/// output file /// output file
out: PathBuf, out: PathBuf,
#[argh(positional)] #[argp(positional)]
/// input files /// input files
files: Vec<PathBuf>, files: Vec<PathBuf>,
} }

View File

@ -1,15 +1,15 @@
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use argh::FromArgs; use argp::FromArgs;
use cwdemangle::{demangle, DemangleOptions}; use cwdemangle::{demangle, DemangleOptions};
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Demangle a CodeWarrior C++ symbol. /// Demangle a CodeWarrior C++ symbol.
#[argh(subcommand, name = "demangle")] #[argp(subcommand, name = "demangle")]
pub struct Args { pub struct Args {
#[argh(positional)] #[argp(positional)]
/// symbol to demangle /// symbol to demangle
symbol: String, symbol: String,
#[argh(switch)] #[argp(switch)]
/// disable replacing `(void)` with `()` /// disable replacing `(void)` with `()`
keep_void: bool, keep_void: bool,
} }

View File

@ -7,7 +7,7 @@ use std::{
}; };
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use argh::FromArgs; use argp::FromArgs;
use crate::{ use crate::{
analysis::{ analysis::{
@ -33,14 +33,14 @@ use crate::{
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing DOL files. /// Commands for processing DOL files.
#[argh(subcommand, name = "dol")] #[argp(subcommand, name = "dol")]
pub struct Args { pub struct Args {
#[argh(subcommand)] #[argp(subcommand)]
command: SubCommand, command: SubCommand,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] #[argp(subcommand)]
enum SubCommand { enum SubCommand {
Info(InfoArgs), Info(InfoArgs),
Split(SplitArgs), Split(SplitArgs),
@ -48,30 +48,30 @@ enum SubCommand {
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Views DOL file information. /// Views DOL file information.
#[argh(subcommand, name = "info")] #[argp(subcommand, name = "info")]
pub struct InfoArgs { pub struct InfoArgs {
#[argh(positional)] #[argp(positional)]
/// DOL file /// DOL file
dol_file: PathBuf, dol_file: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Splits a DOL into relocatable objects. /// Splits a DOL into relocatable objects.
#[argh(subcommand, name = "split")] #[argp(subcommand, name = "split")]
pub struct SplitArgs { pub struct SplitArgs {
#[argh(positional)] #[argp(positional)]
/// input file /// input file
in_file: PathBuf, in_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// output directory /// output directory
out_dir: PathBuf, out_dir: PathBuf,
#[argh(option, short = 's')] #[argp(option, short = 's')]
/// path to symbols file /// path to symbols file
symbols_file: Option<PathBuf>, symbols_file: Option<PathBuf>,
#[argh(option, short = 'p')] #[argp(option, short = 'p')]
/// path to splits file /// path to splits file
splits_file: Option<PathBuf>, splits_file: Option<PathBuf>,
#[argh(option, short = 'e')] #[argp(option, short = 'e')]
/// ELF file to validate against (debugging only) /// ELF file to validate against (debugging only)
elf_file: Option<PathBuf>, elf_file: Option<PathBuf>,
} }

View File

@ -6,7 +6,7 @@ use std::{
}; };
use anyhow::{anyhow, bail, Result}; use anyhow::{anyhow, bail, Result};
use argh::FromArgs; use argp::FromArgs;
use object::{elf, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, Section}; use object::{elf, Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, Section};
use crate::util::{ use crate::util::{
@ -18,28 +18,28 @@ use crate::util::{
}; };
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// process DWARF 1.1 information /// Commands for processing DWARF 1.1 information.
#[argh(subcommand, name = "dwarf")] #[argp(subcommand, name = "dwarf")]
pub struct Args { pub struct Args {
#[argh(subcommand)] #[argp(subcommand)]
command: SubCommand, command: SubCommand,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] #[argp(subcommand)]
enum SubCommand { enum SubCommand {
Dump(DumpArgs), Dump(DumpArgs),
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// dumps DWARF 1.1 info from an object or archive /// Dumps DWARF 1.1 info from an object or archive.
#[argh(subcommand, name = "dump")] #[argp(subcommand, name = "dump")]
pub struct DumpArgs { pub struct DumpArgs {
#[argh(positional)] #[argp(positional)]
/// input object (ELF or archive) /// Input object. (ELF or archive)
in_file: PathBuf, in_file: PathBuf,
#[argh(option, short = 'o')] #[argp(option, short = 'o')]
/// output file (or directory, for archive) /// Output file. (Or directory, for archive)
out: Option<PathBuf>, out: Option<PathBuf>,
} }

View File

@ -7,7 +7,7 @@ use std::{
}; };
use anyhow::{anyhow, bail, ensure, Context, Result}; use anyhow::{anyhow, bail, ensure, Context, Result};
use argh::FromArgs; use argp::FromArgs;
use object::{ use object::{
elf, elf,
write::{Mangling, SectionId, SymbolId}, write::{Mangling, SectionId, SymbolId},
@ -31,14 +31,14 @@ use crate::{
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing ELF files. /// Commands for processing ELF files.
#[argh(subcommand, name = "elf")] #[argp(subcommand, name = "elf")]
pub struct Args { pub struct Args {
#[argh(subcommand)] #[argp(subcommand)]
command: SubCommand, command: SubCommand,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] #[argp(subcommand)]
enum SubCommand { enum SubCommand {
Config(ConfigArgs), Config(ConfigArgs),
Disasm(DisasmArgs), Disasm(DisasmArgs),
@ -49,63 +49,63 @@ enum SubCommand {
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Disassembles an ELF file. /// Disassembles an ELF file.
#[argh(subcommand, name = "disasm")] #[argp(subcommand, name = "disasm")]
pub struct DisasmArgs { pub struct DisasmArgs {
#[argh(positional)] #[argp(positional)]
/// input file /// input file
elf_file: PathBuf, elf_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// output file (.o) or directory (.elf) /// output file (.o) or directory (.elf)
out: PathBuf, out: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Fixes issues with GNU assembler built object files. /// Fixes issues with GNU assembler built object files.
#[argh(subcommand, name = "fixup")] #[argp(subcommand, name = "fixup")]
pub struct FixupArgs { pub struct FixupArgs {
#[argh(positional)] #[argp(positional)]
/// input file /// input file
in_file: PathBuf, in_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// output file /// output file
out_file: PathBuf, out_file: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Splits an executable ELF into relocatable objects. /// Splits an executable ELF into relocatable objects.
#[argh(subcommand, name = "split")] #[argp(subcommand, name = "split")]
pub struct SplitArgs { pub struct SplitArgs {
#[argh(positional)] #[argp(positional)]
/// input file /// input file
in_file: PathBuf, in_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// output directory /// output directory
out_dir: PathBuf, out_dir: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Generates configuration files from an executable ELF. /// Generates configuration files from an executable ELF.
#[argh(subcommand, name = "config")] #[argp(subcommand, name = "config")]
pub struct ConfigArgs { pub struct ConfigArgs {
#[argh(positional)] #[argp(positional)]
/// input file /// input file
in_file: PathBuf, in_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// output directory /// output directory
out_dir: PathBuf, out_dir: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Builds function signatures from an ELF file. /// Builds function signatures from an ELF file.
#[argh(subcommand, name = "sigs")] #[argp(subcommand, name = "sigs")]
pub struct SignaturesArgs { pub struct SignaturesArgs {
#[argh(positional)] #[argp(positional)]
/// input file(s) /// input file(s)
files: Vec<PathBuf>, files: Vec<PathBuf>,
#[argh(option, short = 's')] #[argp(option, short = 's')]
/// symbol name /// symbol name
symbol: String, symbol: String,
#[argh(option, short = 'o')] #[argp(option, short = 'o')]
/// output yml /// output yml
out_file: PathBuf, out_file: PathBuf,
} }

View File

@ -5,19 +5,19 @@ use std::{
}; };
use anyhow::{anyhow, bail, ensure, Context, Result}; use anyhow::{anyhow, bail, ensure, Context, Result};
use argh::FromArgs; use argp::FromArgs;
use object::{Architecture, Endianness, Object, ObjectKind, ObjectSection, SectionKind}; use object::{Architecture, Endianness, Object, ObjectKind, ObjectSection, SectionKind};
use crate::util::file::map_file; use crate::util::file::map_file;
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Converts an ELF file to a DOL file. /// Converts an ELF file to a DOL file.
#[argh(subcommand, name = "elf2dol")] #[argp(subcommand, name = "elf2dol")]
pub struct Args { pub struct Args {
#[argh(positional)] #[argp(positional)]
/// path to input ELF /// path to input ELF
elf_file: PathBuf, elf_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// path to output DOL /// path to output DOL
dol_file: PathBuf, dol_file: PathBuf,
} }

View File

@ -2,7 +2,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use argh::FromArgs; use argp::FromArgs;
use cwdemangle::{demangle, DemangleOptions}; use cwdemangle::{demangle, DemangleOptions};
use crate::util::{ use crate::util::{
@ -12,14 +12,14 @@ use crate::util::{
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing CodeWarrior maps. /// Commands for processing CodeWarrior maps.
#[argh(subcommand, name = "map")] #[argp(subcommand, name = "map")]
pub struct Args { pub struct Args {
#[argh(subcommand)] #[argp(subcommand)]
command: SubCommand, command: SubCommand,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] #[argp(subcommand)]
enum SubCommand { enum SubCommand {
Entries(EntriesArgs), Entries(EntriesArgs),
Symbol(SymbolArgs), Symbol(SymbolArgs),
@ -30,51 +30,51 @@ enum SubCommand {
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Displays all entries for a particular TU. /// Displays all entries for a particular TU.
#[argh(subcommand, name = "entries")] #[argp(subcommand, name = "entries")]
pub struct EntriesArgs { pub struct EntriesArgs {
#[argh(positional)] #[argp(positional)]
/// path to input map /// path to input map
map_file: PathBuf, map_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// TU to display entries for /// TU to display entries for
unit: String, unit: String,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Displays all references to a symbol. /// Displays all references to a symbol.
#[argh(subcommand, name = "symbol")] #[argp(subcommand, name = "symbol")]
pub struct SymbolArgs { pub struct SymbolArgs {
#[argh(positional)] #[argp(positional)]
/// path to input map /// path to input map
map_file: PathBuf, map_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// symbol to display references for /// symbol to display references for
symbol: String, symbol: String,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Attempts to resolve global link order. /// Attempts to resolve global link order.
#[argh(subcommand, name = "order")] #[argp(subcommand, name = "order")]
pub struct OrderArgs { pub struct OrderArgs {
#[argh(positional)] #[argp(positional)]
/// path to input map /// path to input map
map_file: PathBuf, map_file: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Emits a slices.yml for ppcdis. (WIP) /// Emits a slices.yml for ppcdis. (WIP)
#[argh(subcommand, name = "slices")] #[argp(subcommand, name = "slices")]
pub struct SlicesArgs { pub struct SlicesArgs {
#[argh(positional)] #[argp(positional)]
/// path to input map /// path to input map
map_file: PathBuf, map_file: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Emits a symbols.yml for ppcdis. (WIP) /// Emits a symbols.yml for ppcdis. (WIP)
#[argh(subcommand, name = "symbols")] #[argp(subcommand, name = "symbols")]
pub struct SymbolsArgs { pub struct SymbolsArgs {
#[argh(positional)] #[argp(positional)]
/// path to input map /// path to input map
map_file: PathBuf, map_file: PathBuf,
} }

View File

@ -1,18 +1,18 @@
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::{bail, ensure, Context, Result}; use anyhow::{bail, ensure, Context, Result};
use argh::FromArgs; use argp::FromArgs;
use memchr::memmem; use memchr::memmem;
use memmap2::MmapOptions; use memmap2::MmapOptions;
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Sets the MetroidBuildInfo tag value in a given binary. /// Sets the MetroidBuildInfo tag value in a given binary.
#[argh(subcommand, name = "metroidbuildinfo")] #[argp(subcommand, name = "metroidbuildinfo")]
pub struct Args { pub struct Args {
#[argh(positional)] #[argp(positional)]
/// path to source binary /// path to source binary
binary: PathBuf, binary: PathBuf,
#[argh(positional)] #[argp(positional)]
/// path to build info string /// path to build info string
build_info: PathBuf, build_info: PathBuf,
} }

View File

@ -6,7 +6,7 @@ use std::{
}; };
use anyhow::{bail, ensure, Context, Result}; use anyhow::{bail, ensure, Context, Result};
use argh::FromArgs; use argp::FromArgs;
use crate::{ use crate::{
analysis::{ analysis::{
@ -28,14 +28,14 @@ use crate::{
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing REL files. /// Commands for processing REL files.
#[argh(subcommand, name = "rel")] #[argp(subcommand, name = "rel")]
pub struct Args { pub struct Args {
#[argh(subcommand)] #[argp(subcommand)]
command: SubCommand, command: SubCommand,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] #[argp(subcommand)]
enum SubCommand { enum SubCommand {
Info(InfoArgs), Info(InfoArgs),
Merge(MergeArgs), Merge(MergeArgs),
@ -43,24 +43,24 @@ enum SubCommand {
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Views REL file information. /// Views REL file information.
#[argh(subcommand, name = "info")] #[argp(subcommand, name = "info")]
pub struct InfoArgs { pub struct InfoArgs {
#[argh(positional)] #[argp(positional)]
/// REL file /// REL file
rel_file: PathBuf, rel_file: PathBuf,
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Merges a DOL + REL(s) into an ELF. /// Merges a DOL + REL(s) into an ELF.
#[argh(subcommand, name = "merge")] #[argp(subcommand, name = "merge")]
pub struct MergeArgs { pub struct MergeArgs {
#[argh(positional)] #[argp(positional)]
/// DOL file /// DOL file
dol_file: PathBuf, dol_file: PathBuf,
#[argh(positional)] #[argp(positional)]
/// REL file(s) /// REL file(s)
rel_files: Vec<PathBuf>, rel_files: Vec<PathBuf>,
#[argh(option, short = 'o')] #[argp(option, short = 'o')]
/// output ELF /// output ELF
out_file: PathBuf, out_file: PathBuf,
} }

View File

@ -1,29 +1,29 @@
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Result; use anyhow::Result;
use argh::FromArgs; use argp::FromArgs;
use crate::util::rso::process_rso; use crate::util::rso::process_rso;
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
/// Commands for processing RSO files. /// Commands for processing RSO files.
#[argh(subcommand, name = "rso")] #[argp(subcommand, name = "rso")]
pub struct Args { pub struct Args {
#[argh(subcommand)] #[argp(subcommand)]
command: SubCommand, command: SubCommand,
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] #[argp(subcommand)]
enum SubCommand { enum SubCommand {
Info(InfoArgs), Info(InfoArgs),
} }
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Views RSO file information. /// Views RSO file information.
#[argh(subcommand, name = "info")] #[argp(subcommand, name = "info")]
pub struct InfoArgs { pub struct InfoArgs {
#[argh(positional)] #[argp(positional)]
/// RSO file /// RSO file
rso_file: PathBuf, rso_file: PathBuf,
} }

View File

@ -5,7 +5,7 @@ use std::{
}; };
use anyhow::{anyhow, bail, Context, Result}; use anyhow::{anyhow, bail, Context, Result};
use argh::FromArgs; use argp::FromArgs;
use filetime::{set_file_mtime, FileTime}; use filetime::{set_file_mtime, FileTime};
use sha1::{Digest, Sha1}; use sha1::{Digest, Sha1};
@ -13,15 +13,15 @@ use crate::util::file::process_rsp;
#[derive(FromArgs, PartialEq, Eq, Debug)] #[derive(FromArgs, PartialEq, Eq, Debug)]
/// Print or check SHA1 (160-bit) checksums. /// Print or check SHA1 (160-bit) checksums.
#[argh(subcommand, name = "shasum")] #[argp(subcommand, name = "shasum")]
pub struct Args { pub struct Args {
#[argh(switch, short = 'c')] #[argp(switch, short = 'c')]
/// check SHA sums against given list /// check SHA sums against given list
check: bool, check: bool,
#[argh(positional)] #[argp(positional)]
/// path to input file(s) /// path to input file(s)
files: Vec<PathBuf>, files: Vec<PathBuf>,
#[argh(option, short = 'o')] #[argp(option, short = 'o')]
/// touch output file on successful check /// touch output file on successful check
output: Option<PathBuf>, output: Option<PathBuf>,
} }

View File

@ -1,22 +1,76 @@
use std::io::Write; use std::{ffi::OsStr, io::Write, path::PathBuf, str::FromStr};
use argh::FromArgs; use argp::{FromArgValue, FromArgs};
pub mod analysis; pub mod analysis;
pub mod argh_version; pub mod argp_version;
pub mod cmd; pub mod cmd;
pub mod obj; pub mod obj;
pub mod util; pub mod util;
#[derive(FromArgs, PartialEq, Debug)] #[derive(Debug, Eq, PartialEq, Copy, Clone)]
/// GameCube/Wii decompilation project tools. enum LogLevel {
struct TopLevel { Error,
#[argh(subcommand)] Warn,
command: SubCommand, Info,
Debug,
Trace,
}
impl FromStr for LogLevel {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"error" => Self::Error,
"warn" => Self::Warn,
"info" => Self::Info,
"debug" => Self::Debug,
"trace" => Self::Trace,
_ => return Err(()),
})
}
}
impl ToString for LogLevel {
fn to_string(&self) -> String {
match self {
LogLevel::Error => "error",
LogLevel::Warn => "warn",
LogLevel::Info => "info",
LogLevel::Debug => "debug",
LogLevel::Trace => "trace",
}
.to_string()
}
}
impl FromArgValue for LogLevel {
fn from_arg_value(value: &OsStr) -> Result<Self, String> {
String::from_arg_value(value)
.and_then(|s| Self::from_str(&s).map_err(|_| format!("Invalid log level")))
}
} }
#[derive(FromArgs, PartialEq, Debug)] #[derive(FromArgs, PartialEq, Debug)]
#[argh(subcommand)] /// Yet another GameCube/Wii decompilation toolkit.
struct TopLevel {
#[argp(subcommand)]
command: SubCommand,
#[argp(option, short = 'C')]
/// Change working directory.
chdir: Option<PathBuf>,
#[argp(option, short = 'L', default = "LogLevel::Info")]
/// Minimum logging level. (Default: info)
/// Possible values: error, warn, info, debug, trace
log_level: LogLevel,
/// Print version information and exit.
#[argp(switch, short = 'V')]
version: bool,
}
#[derive(FromArgs, PartialEq, Debug)]
#[argp(subcommand)]
enum SubCommand { enum SubCommand {
Ar(cmd::ar::Args), Ar(cmd::ar::Args),
Demangle(cmd::demangle::Args), Demangle(cmd::demangle::Args),
@ -32,12 +86,21 @@ enum SubCommand {
} }
fn main() { fn main() {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")) let args: TopLevel = argp_version::from_env();
.format(|f, r| writeln!(f, "[{}] {}", r.level(), r.args())) env_logger::Builder::from_env(
.init(); env_logger::Env::default().default_filter_or(args.log_level.to_string()),
)
.format(|f, r| writeln!(f, "[{}] {}", r.level(), r.args()))
.init();
let args: TopLevel = argh_version::from_env(); let mut result = Ok(());
let result = match args.command { if let Some(dir) = &args.chdir {
result = std::env::set_current_dir(dir).map_err(|e| {
anyhow::Error::new(e)
.context(format!("Failed to change working directory to '{}'", dir.display()))
});
}
result = result.and_then(|_| match args.command {
SubCommand::Ar(c_args) => cmd::ar::run(c_args), SubCommand::Ar(c_args) => cmd::ar::run(c_args),
SubCommand::Demangle(c_args) => cmd::demangle::run(c_args), SubCommand::Demangle(c_args) => cmd::demangle::run(c_args),
SubCommand::Dol(c_args) => cmd::dol::run(c_args), SubCommand::Dol(c_args) => cmd::dol::run(c_args),
@ -49,7 +112,7 @@ fn main() {
SubCommand::Rel(c_args) => cmd::rel::run(c_args), SubCommand::Rel(c_args) => cmd::rel::run(c_args),
SubCommand::Rso(c_args) => cmd::rso::run(c_args), SubCommand::Rso(c_args) => cmd::rso::run(c_args),
SubCommand::Shasum(c_args) => cmd::shasum::run(c_args), SubCommand::Shasum(c_args) => cmd::shasum::run(c_args),
}; });
if let Err(e) = result { if let Err(e) = result {
eprintln!("Failed: {e:?}"); eprintln!("Failed: {e:?}");
std::process::exit(1); std::process::exit(1);

View File

@ -5,7 +5,7 @@ use std::{
use anyhow::{anyhow, bail, ensure, Result}; use anyhow::{anyhow, bail, ensure, Result};
use itertools::Itertools; use itertools::Itertools;
use topological_sort::TopologicalSort; use petgraph::{graph::NodeIndex, Graph};
use crate::obj::{ use crate::obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSplit, ObjSymbol, ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSplit, ObjSymbol,
@ -376,8 +376,23 @@ pub fn update_splits(obj: &mut ObjInfo) -> Result<()> {
/// There can be ambiguities, but any solution that satisfies the link order /// There can be ambiguities, but any solution that satisfies the link order
/// constraints is considered valid. /// constraints is considered valid.
fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<String>> { fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<String>> {
let mut global_unit_order = Vec::<String>::new(); #[allow(dead_code)]
let mut t_sort = TopologicalSort::<String>::new(); #[derive(Debug, Copy, Clone)]
struct SplitEdge {
from: u32,
to: u32,
}
let mut graph = Graph::<String, SplitEdge>::new();
let mut unit_to_index_map = BTreeMap::<String, NodeIndex>::new();
for (_, split) in obj.splits_for_range(..) {
unit_to_index_map.insert(split.unit.clone(), NodeIndex::new(0));
}
for (unit, index) in unit_to_index_map.iter_mut() {
let new_index = graph.add_node(unit.clone());
*index = new_index;
}
for section in &obj.sections { for section in &obj.sections {
let mut iter = obj let mut iter = obj
.splits_for_range(section.address as u32..(section.address + section.size) as u32) .splits_for_range(section.address as u32..(section.address + section.size) as u32)
@ -387,38 +402,46 @@ fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<String>> {
let skipped = iter.next(); let skipped = iter.next();
log::debug!("Skipping split {:?} (next: {:?})", skipped, iter.peek()); log::debug!("Skipping split {:?} (next: {:?})", skipped, iter.peek());
} }
loop { while let (Some((a_addr, a)), Some(&(b_addr, b))) = (iter.next(), iter.peek()) {
match (iter.next(), iter.peek()) { if a.unit != b.unit {
(Some((a_addr, a)), Some((b_addr, b))) => { log::debug!(
if a.unit != b.unit { "Adding dependency {} ({:#010X}) -> {} ({:#010X})",
log::debug!( a.unit,
"Adding dependency {} ({:#010X}) -> {} ({:#010X})", a_addr,
a.unit, b.unit,
a_addr, b_addr
b.unit, );
b_addr let a_index = *unit_to_index_map.get(&a.unit).unwrap();
); let b_index = *unit_to_index_map.get(&b.unit).unwrap();
t_sort.add_dependency(a.unit.clone(), b.unit.clone()); graph.add_edge(a_index, b_index, SplitEdge { from: a_addr, to: b_addr });
}
}
(Some((_, a)), None) => {
t_sort.insert(a.unit.clone());
break;
}
_ => break,
} }
} }
} }
for unit in &mut t_sort {
global_unit_order.push(unit); // use petgraph::{
// dot::{Config, Dot},
// graph::EdgeReference,
// };
// let get_edge_attributes = |_, e: EdgeReference<SplitEdge>| {
// let &SplitEdge { from, to } = e.weight();
// let section_name = &obj.section_at(from).unwrap().name;
// format!("label=\"{} {:#010X} -> {:#010X}\"", section_name, from, to)
// };
// let dot = Dot::with_attr_getters(
// &graph,
// &[Config::EdgeNoLabel, Config::NodeNoLabel],
// &get_edge_attributes,
// &|_, (_, s)| format!("label=\"{}\"", s),
// );
// println!("{:?}", dot);
match petgraph::algo::toposort(&graph, None) {
Ok(vec) => Ok(vec.iter().map(|&idx| graph[idx].clone()).collect_vec()),
Err(e) => Err(anyhow!(
"Cyclic dependency (involving {}) encountered while resolving link order",
graph[e.node_id()]
)),
} }
// An incomplete topological sort indicates that a cyclic dependency was encountered.
ensure!(t_sort.is_empty(), "Cyclic dependency encountered while resolving link order");
// Sanity check, did we get all TUs in the final order?
for unit in obj.splits.values().flatten().map(|s| &s.unit) {
ensure!(global_unit_order.contains(unit), "Failed to find an order for {unit}");
}
Ok(global_unit_order)
} }
/// Split an executable object into relocatable objects. /// Split an executable object into relocatable objects.

View File

@ -12,7 +12,6 @@ use cwdemangle::{demangle, DemangleOptions};
use multimap::MultiMap; use multimap::MultiMap;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::{Captures, Regex}; use regex::{Captures, Regex};
use topological_sort::TopologicalSort;
use crate::{ use crate::{
obj::{ obj::{
@ -144,43 +143,6 @@ fn resolve_section_order(
Ok(ordering) Ok(ordering)
} }
/// The ordering of TUs inside of each section represents a directed edge in a DAG.
/// We can use a topological sort to determine a valid global TU order.
/// There can be ambiguities, but any solution that satisfies the link order
/// constraints is considered valid.
// TODO account for library ordering
pub fn resolve_link_order(section_unit_order: &[(String, Vec<String>)]) -> Result<Vec<String>> {
let mut global_unit_order = Vec::<String>::new();
let mut t_sort = TopologicalSort::<String>::new();
for (section, order) in section_unit_order {
let mut order = order.clone();
if matches!(section.as_str(), ".ctors" | ".dtors" | "extab") {
continue;
// if order.len() > 1 {
// // __init_cpp_exceptions.o has symbols that get ordered to the beginning of
// // .ctors and .dtors, so our topological sort would fail if we added them.
// // Always skip the first TU of .ctors and .dtors.
// order = order[1..].to_vec();
// }
}
for iter in order.windows(2) {
t_sort.add_dependency(iter[0].clone(), iter[1].clone());
}
}
for unit in &mut t_sort {
global_unit_order.push(unit);
}
// An incomplete topological sort indicates that a cyclic dependency was encountered.
ensure!(t_sort.is_empty(), "Cyclic dependency encountered while resolving link order");
// Sanity check, did we get all TUs in the final order?
for (_, order) in section_unit_order {
for unit in order {
ensure!(global_unit_order.contains(unit), "Failed to find an order for {unit}");
}
}
Ok(global_unit_order)
}
macro_rules! static_regex { macro_rules! static_regex {
($name:ident, $str:expr) => { ($name:ident, $str:expr) => {
static $name: Lazy<Regex> = Lazy::new(|| Regex::new($str).unwrap()); static $name: Lazy<Regex> = Lazy::new(|| Regex::new($str).unwrap());
@ -752,7 +714,8 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
section_order.push((section.clone(), units)); section_order.push((section.clone(), units));
} }
log::info!("Section order: {:#?}", section_order); log::info!("Section order: {:#?}", section_order);
obj.link_order = resolve_link_order(&section_order)?; // TODO
// obj.link_order = resolve_link_order(&section_order)?;
Ok(()) Ok(())
} }