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"
|
||||
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]]
|
||||
name = "argh"
|
||||
version = "0.1.9"
|
||||
|
@ -149,9 +154,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "decomp-toolkit"
|
||||
version = "0.2.0"
|
||||
version = "0.2.2"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ar",
|
||||
"argh",
|
||||
"base16ct",
|
||||
"cwdemangle",
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "decomp-toolkit"
|
|||
description = "GameCube/Wii decompilation project tools."
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
build = "build.rs"
|
||||
|
@ -22,6 +22,7 @@ strip = "debuginfo"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0.64"
|
||||
ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "do_not_remove_cg_clif_ranlib" }
|
||||
argh = "0.1.8"
|
||||
base16ct = "0.1.1"
|
||||
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::File,
|
||||
io::{BufWriter, Write},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use anyhow::{Context, Error, Result};
|
||||
|
@ -10,7 +11,7 @@ use argh::FromArgs;
|
|||
use object::{
|
||||
write::{SectionId, SymbolId},
|
||||
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};
|
||||
|
@ -36,10 +37,10 @@ enum SubCommand {
|
|||
pub struct DisasmArgs {
|
||||
#[argh(positional)]
|
||||
/// input file
|
||||
elf_file: String,
|
||||
elf_file: PathBuf,
|
||||
#[argh(positional)]
|
||||
/// output directory
|
||||
out_dir: String,
|
||||
out_dir: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
|
@ -48,10 +49,10 @@ pub struct DisasmArgs {
|
|||
pub struct FixupArgs {
|
||||
#[argh(positional)]
|
||||
/// input file
|
||||
in_file: String,
|
||||
in_file: PathBuf,
|
||||
#[argh(positional)]
|
||||
/// output file
|
||||
out_file: String,
|
||||
out_file: PathBuf,
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
const ASM_SUFFIX: &[u8] = " (asm)".as_bytes();
|
||||
|
||||
fn fixup(args: FixupArgs) -> Result<()> {
|
||||
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")?;
|
||||
|
@ -90,11 +93,36 @@ fn fixup(args: FixupArgs) -> Result<()> {
|
|||
object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness());
|
||||
|
||||
// Write file symbol(s) first
|
||||
let mut file_symbol_found = false;
|
||||
for symbol in in_file.symbols() {
|
||||
if symbol.kind() != SymbolKind::File {
|
||||
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
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
pub(crate) mod ar;
|
||||
pub(crate) mod demangle;
|
||||
pub(crate) mod elf;
|
||||
pub(crate) mod elf2dol;
|
||||
|
|
|
@ -16,6 +16,7 @@ struct TopLevel {
|
|||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommand {
|
||||
Ar(cmd::ar::Args),
|
||||
Demangle(cmd::demangle::Args),
|
||||
Elf(cmd::elf::Args),
|
||||
Elf2Dol(cmd::elf2dol::Args),
|
||||
|
@ -29,6 +30,7 @@ fn main() {
|
|||
|
||||
let args: TopLevel = argh_version::from_env();
|
||||
let result = match args.command {
|
||||
SubCommand::Ar(c_args) => cmd::ar::run(c_args),
|
||||
SubCommand::Demangle(c_args) => cmd::demangle::run(c_args),
|
||||
SubCommand::Elf(c_args) => cmd::elf::run(c_args),
|
||||
SubCommand::Elf2Dol(c_args) => cmd::elf2dol::run(c_args),
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use std::{
|
||||
cmp::{min, Ordering},
|
||||
collections::{btree_map, hash_map::Entry, BTreeMap, HashMap},
|
||||
fmt::Display,
|
||||
fs,
|
||||
fs::{DirBuilder, File},
|
||||
io::{BufWriter, Write},
|
||||
|
@ -29,7 +28,7 @@ struct SymbolEntry {
|
|||
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 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 cwdemangle::demangle;
|
||||
|
@ -19,11 +19,13 @@ use crate::util::obj::{
|
|||
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
||||
};
|
||||
|
||||
pub fn process_elf<P: AsRef<Path> + Display>(path: P) -> Result<ObjInfo> {
|
||||
let elf_file =
|
||||
File::open(&path).with_context(|| format!("Failed to open ELF file '{path}'"))?;
|
||||
let map = unsafe { MmapOptions::new().map(&elf_file) }
|
||||
.with_context(|| format!("Failed to mmap ELF file: '{path}'"))?;
|
||||
pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
||||
let elf_file = File::open(&path).with_context(|| {
|
||||
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.as_ref().to_string_lossy())
|
||||
})?;
|
||||
let obj_file = object::read::File::parse(&*map)?;
|
||||
let architecture = match obj_file.architecture() {
|
||||
Architecture::PowerPc => ObjArchitecture::PowerPc,
|
||||
|
|
Loading…
Reference in New Issue