Version 0.2.2
- Add `ar create` command for static libraries - Update `elf fixup` to add an "(asm)" suffix to object file symbols, for use with progress tracking.
This commit is contained in:
parent
21c386d1a6
commit
828766b22b
|
@ -28,6 +28,11 @@ version = "1.0.66"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
checksum = "216261ddc8289130e551ddcd5ce8a064710c0d064a4d2895c67151c92b5443f6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ar"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "git+https://github.com/bjorn3/rust-ar.git?branch=do_not_remove_cg_clif_ranlib#de9ab0e56bf3a208381d342aa5b60f9ff2891648"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "argh"
|
name = "argh"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
@ -149,9 +154,10 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "decomp-toolkit"
|
name = "decomp-toolkit"
|
||||||
version = "0.2.0"
|
version = "0.2.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"ar",
|
||||||
"argh",
|
"argh",
|
||||||
"base16ct",
|
"base16ct",
|
||||||
"cwdemangle",
|
"cwdemangle",
|
||||||
|
|
|
@ -3,7 +3,7 @@ name = "decomp-toolkit"
|
||||||
description = "GameCube/Wii decompilation project tools."
|
description = "GameCube/Wii decompilation project tools."
|
||||||
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.2.1"
|
version = "0.2.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
publish = false
|
publish = false
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
@ -22,6 +22,7 @@ strip = "debuginfo"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.64"
|
anyhow = "1.0.64"
|
||||||
|
ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" }
|
||||||
argh = "0.1.8"
|
argh = "0.1.8"
|
||||||
base16ct = "0.1.1"
|
base16ct = "0.1.1"
|
||||||
cwdemangle = "0.1.3"
|
cwdemangle = "0.1.3"
|
||||||
|
|
|
@ -0,0 +1,110 @@
|
||||||
|
use std::{
|
||||||
|
collections::{btree_map::Entry, BTreeMap},
|
||||||
|
fs::File,
|
||||||
|
io::{BufRead, BufReader, BufWriter, Write},
|
||||||
|
path::PathBuf,
|
||||||
|
};
|
||||||
|
|
||||||
|
use anyhow::{Context, Error, Result};
|
||||||
|
use argh::FromArgs;
|
||||||
|
use object::{Object, ObjectSymbol};
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
/// Commands for processing static libraries.
|
||||||
|
#[argh(subcommand, name = "ar")]
|
||||||
|
pub struct Args {
|
||||||
|
#[argh(subcommand)]
|
||||||
|
command: SubCommand,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
#[argh(subcommand)]
|
||||||
|
enum SubCommand {
|
||||||
|
Create(CreateArgs),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
/// Creates a static library.
|
||||||
|
#[argh(subcommand, name = "create")]
|
||||||
|
pub struct CreateArgs {
|
||||||
|
#[argh(positional)]
|
||||||
|
/// output file
|
||||||
|
out: PathBuf,
|
||||||
|
#[argh(positional)]
|
||||||
|
/// input files
|
||||||
|
files: Vec<PathBuf>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(args: Args) -> Result<()> {
|
||||||
|
match args.command {
|
||||||
|
SubCommand::Create(c_args) => create(c_args),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(|| {
|
||||||
|
Error::msg(format!("'{}' is not valid UTF-8", path.to_string_lossy()))
|
||||||
|
})?;
|
||||||
|
match path_str.strip_prefix('@') {
|
||||||
|
Some(rsp_file) => {
|
||||||
|
let reader = BufReader::new(
|
||||||
|
File::open(rsp_file)
|
||||||
|
.with_context(|| format!("Failed to open file '{}'", rsp_file))?,
|
||||||
|
);
|
||||||
|
for result in reader.lines() {
|
||||||
|
let line = result?;
|
||||||
|
if !line.is_empty() {
|
||||||
|
files.push(PathBuf::from(line));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
files.push(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build identifiers & symbol table
|
||||||
|
let mut identifiers = Vec::with_capacity(files.len());
|
||||||
|
let mut symbol_table = BTreeMap::new();
|
||||||
|
for path in &files {
|
||||||
|
let file_name = path.file_name().ok_or_else(|| {
|
||||||
|
Error::msg(format!("'{}' is not a file path", path.to_string_lossy()))
|
||||||
|
})?;
|
||||||
|
let file_name = file_name.to_str().ok_or_else(|| {
|
||||||
|
Error::msg(format!("'{}' is not valid UTF-8", file_name.to_string_lossy()))
|
||||||
|
})?;
|
||||||
|
let identifier = file_name.as_bytes().to_vec();
|
||||||
|
identifiers.push(identifier.clone());
|
||||||
|
|
||||||
|
let entries = match symbol_table.entry(identifier) {
|
||||||
|
Entry::Vacant(e) => e.insert(Vec::new()),
|
||||||
|
Entry::Occupied(_) => {
|
||||||
|
return Err(Error::msg(format!("Duplicate file name '{file_name}'")))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let object_file = File::open(path)
|
||||||
|
.with_context(|| format!("Failed to open object file '{}'", path.to_string_lossy()))?;
|
||||||
|
let map = unsafe { memmap2::MmapOptions::new().map(&object_file) }
|
||||||
|
.with_context(|| format!("Failed to mmap object file: '{}'", path.to_string_lossy()))?;
|
||||||
|
let obj = object::File::parse(map.as_ref())?;
|
||||||
|
for symbol in obj.symbols() {
|
||||||
|
if symbol.is_global() {
|
||||||
|
entries.push(symbol.name_bytes()?.to_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write archive
|
||||||
|
let out = BufWriter::new(File::create(&args.out)?);
|
||||||
|
let mut builder =
|
||||||
|
ar::GnuBuilder::new(out, identifiers, ar::GnuSymbolTableFormat::Size32, symbol_table)?;
|
||||||
|
for path in files {
|
||||||
|
builder.append_path(path)?;
|
||||||
|
}
|
||||||
|
builder.into_inner()?.flush()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ use std::{
|
||||||
fs,
|
fs,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{Context, Error, Result};
|
use anyhow::{Context, Error, Result};
|
||||||
|
@ -10,7 +11,7 @@ use argh::FromArgs;
|
||||||
use object::{
|
use object::{
|
||||||
write::{SectionId, SymbolId},
|
write::{SectionId, SymbolId},
|
||||||
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionFlags,
|
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionFlags,
|
||||||
SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolSection,
|
SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolScope, SymbolSection,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::util::{asm::write_asm, elf::process_elf};
|
use crate::util::{asm::write_asm, elf::process_elf};
|
||||||
|
@ -36,10 +37,10 @@ enum SubCommand {
|
||||||
pub struct DisasmArgs {
|
pub struct DisasmArgs {
|
||||||
#[argh(positional)]
|
#[argh(positional)]
|
||||||
/// input file
|
/// input file
|
||||||
elf_file: String,
|
elf_file: PathBuf,
|
||||||
#[argh(positional)]
|
#[argh(positional)]
|
||||||
/// output directory
|
/// output directory
|
||||||
out_dir: String,
|
out_dir: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
@ -48,10 +49,10 @@ pub struct DisasmArgs {
|
||||||
pub struct FixupArgs {
|
pub struct FixupArgs {
|
||||||
#[argh(positional)]
|
#[argh(positional)]
|
||||||
/// input file
|
/// input file
|
||||||
in_file: String,
|
in_file: PathBuf,
|
||||||
#[argh(positional)]
|
#[argh(positional)]
|
||||||
/// output file
|
/// output file
|
||||||
out_file: String,
|
out_file: PathBuf,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(args: Args) -> Result<()> {
|
pub fn run(args: Args) -> Result<()> {
|
||||||
|
@ -83,6 +84,8 @@ fn file_name_from_unit(str: &str) -> String {
|
||||||
format!("{}.o", str.strip_prefix('/').unwrap_or(&str))
|
format!("{}.o", str.strip_prefix('/').unwrap_or(&str))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ASM_SUFFIX: &[u8] = " (asm)".as_bytes();
|
||||||
|
|
||||||
fn fixup(args: FixupArgs) -> Result<()> {
|
fn fixup(args: FixupArgs) -> Result<()> {
|
||||||
let in_buf = fs::read(&args.in_file).context("Failed to open input file")?;
|
let in_buf = fs::read(&args.in_file).context("Failed to open input file")?;
|
||||||
let in_file = object::read::File::parse(&*in_buf).context("Failed to parse input ELF")?;
|
let in_file = object::read::File::parse(&*in_buf).context("Failed to parse input ELF")?;
|
||||||
|
@ -90,11 +93,36 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
||||||
object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness());
|
object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness());
|
||||||
|
|
||||||
// Write file symbol(s) first
|
// Write file symbol(s) first
|
||||||
|
let mut file_symbol_found = false;
|
||||||
for symbol in in_file.symbols() {
|
for symbol in in_file.symbols() {
|
||||||
if symbol.kind() != SymbolKind::File {
|
if symbol.kind() != SymbolKind::File {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
out_file.add_symbol(to_write_symbol(&symbol, &[])?);
|
let mut out_symbol = to_write_symbol(&symbol, &[])?;
|
||||||
|
out_symbol.name.append(&mut ASM_SUFFIX.to_vec());
|
||||||
|
out_file.add_symbol(out_symbol);
|
||||||
|
file_symbol_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if !file_symbol_found {
|
||||||
|
let file_name = args.in_file.file_name().ok_or_else(|| {
|
||||||
|
Error::msg(format!("'{}' is not a file path", args.in_file.to_string_lossy()))
|
||||||
|
})?;
|
||||||
|
let file_name = file_name.to_str().ok_or_else(|| {
|
||||||
|
Error::msg(format!("'{}' is not valid UTF-8", file_name.to_string_lossy()))
|
||||||
|
})?;
|
||||||
|
let mut name_bytes = file_name.as_bytes().to_vec();
|
||||||
|
name_bytes.append(&mut ASM_SUFFIX.to_vec());
|
||||||
|
out_file.add_symbol(object::write::Symbol {
|
||||||
|
name: name_bytes,
|
||||||
|
value: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: SymbolKind::File,
|
||||||
|
scope: SymbolScope::Compilation,
|
||||||
|
weak: false,
|
||||||
|
section: object::write::SymbolSection::Absolute,
|
||||||
|
flags: SymbolFlags::None,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write section symbols & sections
|
// Write section symbols & sections
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
pub(crate) mod ar;
|
||||||
pub(crate) mod demangle;
|
pub(crate) mod demangle;
|
||||||
pub(crate) mod elf;
|
pub(crate) mod elf;
|
||||||
pub(crate) mod elf2dol;
|
pub(crate) mod elf2dol;
|
||||||
|
|
|
@ -16,6 +16,7 @@ struct TopLevel {
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
#[argh(subcommand)]
|
#[argh(subcommand)]
|
||||||
enum SubCommand {
|
enum SubCommand {
|
||||||
|
Ar(cmd::ar::Args),
|
||||||
Demangle(cmd::demangle::Args),
|
Demangle(cmd::demangle::Args),
|
||||||
Elf(cmd::elf::Args),
|
Elf(cmd::elf::Args),
|
||||||
Elf2Dol(cmd::elf2dol::Args),
|
Elf2Dol(cmd::elf2dol::Args),
|
||||||
|
@ -29,6 +30,7 @@ fn main() {
|
||||||
|
|
||||||
let args: TopLevel = argh_version::from_env();
|
let args: TopLevel = argh_version::from_env();
|
||||||
let result = match args.command {
|
let result = match args.command {
|
||||||
|
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::Elf(c_args) => cmd::elf::run(c_args),
|
SubCommand::Elf(c_args) => cmd::elf::run(c_args),
|
||||||
SubCommand::Elf2Dol(c_args) => cmd::elf2dol::run(c_args),
|
SubCommand::Elf2Dol(c_args) => cmd::elf2dol::run(c_args),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{min, Ordering},
|
cmp::{min, Ordering},
|
||||||
collections::{btree_map, hash_map::Entry, BTreeMap, HashMap},
|
collections::{btree_map, hash_map::Entry, BTreeMap, HashMap},
|
||||||
fmt::Display,
|
|
||||||
fs,
|
fs,
|
||||||
fs::{DirBuilder, File},
|
fs::{DirBuilder, File},
|
||||||
io::{BufWriter, Write},
|
io::{BufWriter, Write},
|
||||||
|
@ -29,7 +28,7 @@ struct SymbolEntry {
|
||||||
kind: SymbolEntryKind,
|
kind: SymbolEntryKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_asm<P: AsRef<Path> + Display>(path: P, obj: &ObjInfo) -> Result<()> {
|
pub fn write_asm<P: AsRef<Path>>(path: P, obj: &ObjInfo) -> Result<()> {
|
||||||
let mut file_map = HashMap::<String, BufWriter<File>>::new();
|
let mut file_map = HashMap::<String, BufWriter<File>>::new();
|
||||||
|
|
||||||
let asm_dir = path.as_ref().join("asm");
|
let asm_dir = path.as_ref().join("asm");
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::BTreeMap, fmt::Display, fs::File, path::Path};
|
use std::{collections::BTreeMap, fs::File, path::Path};
|
||||||
|
|
||||||
use anyhow::{Context, Error, Result};
|
use anyhow::{Context, Error, Result};
|
||||||
use cwdemangle::demangle;
|
use cwdemangle::demangle;
|
||||||
|
@ -19,11 +19,13 @@ use crate::util::obj::{
|
||||||
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn process_elf<P: AsRef<Path> + Display>(path: P) -> Result<ObjInfo> {
|
pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||||
let elf_file =
|
let elf_file = File::open(&path).with_context(|| {
|
||||||
File::open(&path).with_context(|| format!("Failed to open ELF file '{path}'"))?;
|
format!("Failed to open ELF file '{}'", path.as_ref().to_string_lossy())
|
||||||
let map = unsafe { MmapOptions::new().map(&elf_file) }
|
})?;
|
||||||
.with_context(|| format!("Failed to mmap ELF file: '{path}'"))?;
|
let map = unsafe { MmapOptions::new().map(&elf_file) }.with_context(|| {
|
||||||
|
format!("Failed to mmap ELF file: '{}'", path.as_ref().to_string_lossy())
|
||||||
|
})?;
|
||||||
let obj_file = object::read::File::parse(&*map)?;
|
let obj_file = object::read::File::parse(&*map)?;
|
||||||
let architecture = match obj_file.architecture() {
|
let architecture = match obj_file.architecture() {
|
||||||
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
||||||
|
|
Loading…
Reference in New Issue