mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-13 23:26:15 +00:00
Version 0.2.0
- Add `elf disasm` (disassemble an ELF) - Add `elf fixup` (for GNU assembler) - Add `map order` (link order deduction) - Add `map slices` (ppcdis slices.yml, WIP) - Add `map symbols` (ppcdis symbols.yml, WIP) - Big speed improvement for map processing - Minor `elf2dol` cleanup
This commit is contained in:
@@ -18,7 +18,7 @@ pub fn run(args: Args) -> Result<()> {
|
||||
let options = DemangleOptions { omit_empty_parameters: !args.keep_void };
|
||||
match demangle(args.symbol.as_str(), &options) {
|
||||
Some(symbol) => {
|
||||
println!("{}", symbol);
|
||||
println!("{symbol}");
|
||||
Ok(())
|
||||
}
|
||||
None => Err(Error::msg("Failed to demangle symbol")),
|
||||
|
||||
275
src/cmd/elf.rs
Normal file
275
src/cmd/elf.rs
Normal file
@@ -0,0 +1,275 @@
|
||||
use std::{
|
||||
collections::{btree_map::Entry, BTreeMap},
|
||||
fs,
|
||||
fs::File,
|
||||
io::{BufWriter, Write},
|
||||
};
|
||||
|
||||
use anyhow::{Context, Error, Result};
|
||||
use argh::FromArgs;
|
||||
use object::{
|
||||
write::{SectionId, SymbolId},
|
||||
Object, ObjectSection, ObjectSymbol, RelocationKind, RelocationTarget, SectionFlags,
|
||||
SectionIndex, SectionKind, SymbolFlags, SymbolKind, SymbolSection,
|
||||
};
|
||||
|
||||
use crate::util::{asm::write_asm, elf::process_elf};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Commands for processing ELF files.
|
||||
#[argh(subcommand, name = "elf")]
|
||||
pub struct Args {
|
||||
#[argh(subcommand)]
|
||||
command: SubCommand,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
#[argh(subcommand)]
|
||||
enum SubCommand {
|
||||
Disasm(DisasmArgs),
|
||||
Fixup(FixupArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Disassembles an ELF file.
|
||||
#[argh(subcommand, name = "disasm")]
|
||||
pub struct DisasmArgs {
|
||||
#[argh(positional)]
|
||||
/// input file
|
||||
elf_file: String,
|
||||
#[argh(positional)]
|
||||
/// output directory
|
||||
out_dir: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Fixes issues with GNU assembler built object files.
|
||||
#[argh(subcommand, name = "fixup")]
|
||||
pub struct FixupArgs {
|
||||
#[argh(positional)]
|
||||
/// input file
|
||||
in_file: String,
|
||||
#[argh(positional)]
|
||||
/// output file
|
||||
out_file: String,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Disasm(c_args) => disasm(c_args),
|
||||
SubCommand::Fixup(c_args) => fixup(c_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn disasm(args: DisasmArgs) -> Result<()> {
|
||||
let obj = process_elf(&args.elf_file)?;
|
||||
write_asm(&args.out_dir, &obj)?;
|
||||
for unit in obj.link_order {
|
||||
let name = format!("$(OBJ_DIR)/asm/{}", file_name_from_unit(&unit));
|
||||
println!(" {name: <70}\\");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn file_name_from_unit(str: &str) -> String {
|
||||
let str = str.strip_prefix("C:").unwrap_or(str);
|
||||
let str = str
|
||||
.strip_suffix(".c")
|
||||
.or_else(|| str.strip_suffix(".cp"))
|
||||
.or_else(|| str.strip_suffix(".cpp"))
|
||||
.or_else(|| str.strip_suffix(".s"))
|
||||
.unwrap_or(str);
|
||||
let str = str.replace('\\', "/");
|
||||
format!("{}.o", str.strip_prefix('/').unwrap_or(&str))
|
||||
}
|
||||
|
||||
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")?;
|
||||
let mut out_file =
|
||||
object::write::Object::new(in_file.format(), in_file.architecture(), in_file.endianness());
|
||||
|
||||
// Write file symbol(s) first
|
||||
for symbol in in_file.symbols() {
|
||||
if symbol.kind() != SymbolKind::File {
|
||||
continue;
|
||||
}
|
||||
out_file.add_symbol(to_write_symbol(&symbol, &[])?);
|
||||
}
|
||||
|
||||
// Write section symbols & sections
|
||||
let mut section_ids: Vec<Option<SectionId>> = vec![];
|
||||
for section in in_file.sections() {
|
||||
// Skip empty sections or metadata sections
|
||||
if section.size() == 0 || section.kind() == SectionKind::Metadata {
|
||||
section_ids.push(None);
|
||||
continue;
|
||||
}
|
||||
let section_id =
|
||||
out_file.add_section(vec![], section.name_bytes()?.to_vec(), section.kind());
|
||||
section_ids.push(Some(section_id));
|
||||
let out_section = out_file.section_mut(section_id);
|
||||
if section.kind() == SectionKind::UninitializedData {
|
||||
out_section.append_bss(section.size(), section.align());
|
||||
} else {
|
||||
out_section.set_data(section.uncompressed_data()?.into_owned(), section.align());
|
||||
}
|
||||
if has_section_flags(section.flags(), object::elf::SHF_ALLOC)? {
|
||||
// Generate section symbol
|
||||
out_file.section_symbol(section_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Write symbols
|
||||
let mut symbol_ids: Vec<Option<SymbolId>> = vec![];
|
||||
let mut addr_to_sym: BTreeMap<SectionId, BTreeMap<u32, SymbolId>> = BTreeMap::new();
|
||||
for symbol in in_file.symbols() {
|
||||
// Skip section and file symbols, we wrote them above
|
||||
if matches!(symbol.kind(), SymbolKind::Section | SymbolKind::File | SymbolKind::Null) {
|
||||
symbol_ids.push(None);
|
||||
continue;
|
||||
}
|
||||
let out_symbol = to_write_symbol(&symbol, §ion_ids)?;
|
||||
let section_id = out_symbol.section.id();
|
||||
let symbol_id = out_file.add_symbol(out_symbol);
|
||||
symbol_ids.push(Some(symbol_id));
|
||||
if symbol.size() != 0 {
|
||||
if let Some(section_id) = section_id {
|
||||
let map = match addr_to_sym.entry(section_id) {
|
||||
Entry::Vacant(e) => e.insert(BTreeMap::new()),
|
||||
Entry::Occupied(e) => e.into_mut(),
|
||||
};
|
||||
map.insert(symbol.address() as u32, symbol_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write relocations
|
||||
for section in in_file.sections() {
|
||||
let section_id = match section_ids[section.index().0] {
|
||||
Some(id) => id,
|
||||
None => continue,
|
||||
};
|
||||
for (addr, reloc) in section.relocations() {
|
||||
let mut symbol = match reloc.target() {
|
||||
RelocationTarget::Symbol(idx) => match symbol_ids[idx.0] {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
let in_symbol = in_file.symbol_by_index(idx)?;
|
||||
match in_symbol.kind() {
|
||||
SymbolKind::Section => {
|
||||
let section_idx = match in_symbol.section_index() {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
return Err(Error::msg("Missing section for relocation"))
|
||||
}
|
||||
};
|
||||
let section_id = match section_ids[section_idx.0] {
|
||||
Some(id) => id,
|
||||
None => {
|
||||
return Err(Error::msg("Missing section for relocation"))
|
||||
}
|
||||
};
|
||||
out_file.section_symbol(section_id)
|
||||
}
|
||||
_ => return Err(Error::msg("Missing symbol for relocation")),
|
||||
}
|
||||
}
|
||||
},
|
||||
RelocationTarget::Section(idx) => {
|
||||
let section_id = match section_ids[idx.0] {
|
||||
Some(id) => id,
|
||||
None => return Err(Error::msg("Missing section for relocation")),
|
||||
};
|
||||
out_file.section_symbol(section_id)
|
||||
}
|
||||
RelocationTarget::Absolute => todo!("Absolute relocation target"),
|
||||
_ => return Err(Error::msg("Invalid relocation target")),
|
||||
};
|
||||
let mut addend = reloc.addend();
|
||||
|
||||
// Attempt to replace section symbols with direct symbol references
|
||||
let target_sym = out_file.symbol(symbol);
|
||||
if target_sym.kind == SymbolKind::Section {
|
||||
if let Some(new_symbol_id) = target_sym
|
||||
.section
|
||||
.id()
|
||||
.and_then(|id| addr_to_sym.get(&id))
|
||||
.and_then(|map| map.get(&(addend as u32)))
|
||||
{
|
||||
symbol = *new_symbol_id;
|
||||
addend = 0;
|
||||
}
|
||||
}
|
||||
|
||||
let kind = match reloc.kind() {
|
||||
// This is a hack to avoid replacement with a section symbol
|
||||
// See [`object::write::elf::object::elf_fixup_relocation`]
|
||||
RelocationKind::Absolute => RelocationKind::Elf(object::elf::R_PPC_ADDR32),
|
||||
other => other,
|
||||
};
|
||||
|
||||
out_file.add_relocation(section_id, object::write::Relocation {
|
||||
offset: addr,
|
||||
size: reloc.size(),
|
||||
kind,
|
||||
encoding: reloc.encoding(),
|
||||
symbol,
|
||||
addend,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
let mut out =
|
||||
BufWriter::new(File::create(&args.out_file).context("Failed to create out file")?);
|
||||
out_file.write_stream(&mut out).map_err(|e| Error::msg(format!("{e:?}")))?;
|
||||
out.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn to_write_symbol_section(
|
||||
section: SymbolSection,
|
||||
section_ids: &[Option<SectionId>],
|
||||
) -> Result<object::write::SymbolSection> {
|
||||
Ok(match section {
|
||||
SymbolSection::None => object::write::SymbolSection::None,
|
||||
SymbolSection::Absolute => object::write::SymbolSection::Absolute,
|
||||
SymbolSection::Common => object::write::SymbolSection::Common,
|
||||
SymbolSection::Section(idx) => match section_ids.get(idx.0).and_then(|opt| *opt) {
|
||||
Some(section_id) => object::write::SymbolSection::Section(section_id),
|
||||
None => return Err(Error::msg("Missing symbol section")),
|
||||
},
|
||||
_ => object::write::SymbolSection::Undefined,
|
||||
})
|
||||
}
|
||||
|
||||
fn to_write_symbol_flags(flags: SymbolFlags<SectionIndex>) -> Result<SymbolFlags<SectionId>> {
|
||||
Ok(match flags {
|
||||
SymbolFlags::Elf { st_info, st_other } => SymbolFlags::Elf { st_info, st_other },
|
||||
SymbolFlags::None => SymbolFlags::None,
|
||||
_ => return Err(Error::msg("Unexpected symbol flags")),
|
||||
})
|
||||
}
|
||||
|
||||
fn to_write_symbol(
|
||||
symbol: &object::read::Symbol,
|
||||
section_ids: &[Option<SectionId>],
|
||||
) -> Result<object::write::Symbol> {
|
||||
Ok(object::write::Symbol {
|
||||
name: symbol.name_bytes()?.to_vec(),
|
||||
value: symbol.address(),
|
||||
size: symbol.size(),
|
||||
kind: symbol.kind(),
|
||||
scope: symbol.scope(),
|
||||
weak: symbol.is_weak(),
|
||||
section: to_write_symbol_section(symbol.section(), section_ids)?,
|
||||
flags: to_write_symbol_flags(symbol.flags())?,
|
||||
})
|
||||
}
|
||||
|
||||
fn has_section_flags(flags: SectionFlags, flag: u32) -> Result<bool> {
|
||||
match flags {
|
||||
SectionFlags::Elf { sh_flags } => Ok(sh_flags & flag as u64 == flag as u64),
|
||||
_ => Err(Error::msg("Unexpected section flags")),
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,10 @@ pub struct DolSection {
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct DolHeader {
|
||||
pub text_sections: Vec<DolSection>,
|
||||
pub data_sections: Vec<DolSection>,
|
||||
pub text_section_count: usize,
|
||||
pub data_section_count: usize,
|
||||
pub text_sections: [DolSection; MAX_TEXT_SECTIONS],
|
||||
pub data_sections: [DolSection; MAX_DATA_SECTIONS],
|
||||
pub bss_address: u32,
|
||||
pub bss_size: u32,
|
||||
pub entry_point: u32,
|
||||
@@ -38,32 +40,32 @@ pub struct DolHeader {
|
||||
|
||||
const MAX_TEXT_SECTIONS: usize = 7;
|
||||
const MAX_DATA_SECTIONS: usize = 11;
|
||||
const ZERO_BUF: [u8; 32] = [0u8; 32];
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
let elf_file = File::open(&args.elf_file)
|
||||
.with_context(|| format!("Failed to open ELF file '{}'", args.elf_file))?;
|
||||
let map = unsafe { MmapOptions::new().map(&elf_file) }
|
||||
.with_context(|| format!("Failed to mmap binary: '{}'", args.elf_file))?;
|
||||
.with_context(|| format!("Failed to mmap ELF file: '{}'", args.elf_file))?;
|
||||
let obj_file = object::read::File::parse(&*map)?;
|
||||
match obj_file.architecture() {
|
||||
Architecture::PowerPc => {}
|
||||
arch => return Err(Error::msg(format!("Unexpected architecture: {:?}", arch))),
|
||||
arch => return Err(Error::msg(format!("Unexpected architecture: {arch:?}"))),
|
||||
};
|
||||
if obj_file.is_little_endian() {
|
||||
return Err(Error::msg("Expected big endian"));
|
||||
}
|
||||
match obj_file.kind() {
|
||||
ObjectKind::Executable => {}
|
||||
kind => return Err(Error::msg(format!("Unexpected ELF type: {:?}", kind))),
|
||||
kind => return Err(Error::msg(format!("Unexpected ELF type: {kind:?}"))),
|
||||
}
|
||||
|
||||
let mut header = DolHeader { entry_point: obj_file.entry() as u32, ..Default::default() };
|
||||
let mut offset = 0x100u32;
|
||||
let mut out = BufWriter::new(
|
||||
File::create(&args.dol_file)
|
||||
.with_context(|| format!("Failed to create DOL file '{}'", args.dol_file))?,
|
||||
);
|
||||
let mut header = DolHeader { entry_point: obj_file.entry() as u32, ..Default::default() };
|
||||
let mut offset = 0x100u32;
|
||||
out.seek(SeekFrom::Start(offset as u64))?;
|
||||
|
||||
// Text sections
|
||||
for section in obj_file.sections() {
|
||||
@@ -72,9 +74,14 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
let address = section.address() as u32;
|
||||
let size = align32(section.size() as u32);
|
||||
header.text_sections.push(DolSection { offset, address, size });
|
||||
out.seek(SeekFrom::Start(offset as u64))?;
|
||||
write_aligned(&mut out, section.data()?)?;
|
||||
*header.text_sections.get_mut(header.text_section_count).ok_or_else(|| {
|
||||
Error::msg(format!(
|
||||
"Too many text sections (while processing '{}')",
|
||||
section.name().unwrap_or("[error]")
|
||||
))
|
||||
})? = DolSection { offset, address, size };
|
||||
header.text_section_count += 1;
|
||||
write_aligned(&mut out, section.data()?, size)?;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
@@ -85,9 +92,14 @@ pub fn run(args: Args) -> Result<()> {
|
||||
}
|
||||
let address = section.address() as u32;
|
||||
let size = align32(section.size() as u32);
|
||||
header.data_sections.push(DolSection { offset, address, size });
|
||||
out.seek(SeekFrom::Start(offset as u64))?;
|
||||
write_aligned(&mut out, section.data()?)?;
|
||||
*header.data_sections.get_mut(header.data_section_count).ok_or_else(|| {
|
||||
Error::msg(format!(
|
||||
"Too many data sections (while processing '{}')",
|
||||
section.name().unwrap_or("[error]")
|
||||
))
|
||||
})? = DolSection { offset, address, size };
|
||||
header.data_section_count += 1;
|
||||
write_aligned(&mut out, section.data()?, size)?;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
@@ -104,68 +116,50 @@ pub fn run(args: Args) -> Result<()> {
|
||||
header.bss_size = (address + size) - header.bss_address;
|
||||
}
|
||||
|
||||
if header.text_sections.len() > MAX_TEXT_SECTIONS {
|
||||
return Err(Error::msg(format!(
|
||||
"Too many text sections: {} / {}",
|
||||
header.text_sections.len(),
|
||||
MAX_TEXT_SECTIONS
|
||||
)));
|
||||
}
|
||||
if header.data_sections.len() > MAX_DATA_SECTIONS {
|
||||
return Err(Error::msg(format!(
|
||||
"Too many data sections: {} / {}",
|
||||
header.data_sections.len(),
|
||||
MAX_DATA_SECTIONS
|
||||
)));
|
||||
}
|
||||
|
||||
// Offsets
|
||||
out.rewind()?;
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.offset.to_be_bytes())?;
|
||||
}
|
||||
out.seek(SeekFrom::Start(0x1c))?;
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.offset.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// Addresses
|
||||
out.seek(SeekFrom::Start(0x48))?;
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.address.to_be_bytes())?;
|
||||
}
|
||||
out.seek(SeekFrom::Start(0x64))?;
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.address.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// Sizes
|
||||
out.seek(SeekFrom::Start(0x90))?;
|
||||
for section in &header.text_sections {
|
||||
out.write_all(§ion.size.to_be_bytes())?;
|
||||
}
|
||||
out.seek(SeekFrom::Start(0xac))?;
|
||||
for section in &header.data_sections {
|
||||
out.write_all(§ion.size.to_be_bytes())?;
|
||||
}
|
||||
|
||||
// BSS + entry
|
||||
out.seek(SeekFrom::Start(0xd8))?;
|
||||
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()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn align32(x: u32) -> u32 { (x + 31) & !31 }
|
||||
const fn align32(x: u32) -> u32 { (x + 31) & !31 }
|
||||
|
||||
const ZERO_BUF: [u8; 32] = [0u8; 32];
|
||||
|
||||
#[inline]
|
||||
fn write_aligned<T: Write>(out: &mut T, bytes: &[u8]) -> std::io::Result<()> {
|
||||
let len = bytes.len() as u32;
|
||||
let padding = align32(len) - len;
|
||||
fn write_aligned<T: Write>(out: &mut T, bytes: &[u8], aligned_size: u32) -> std::io::Result<()> {
|
||||
out.write_all(bytes)?;
|
||||
let padding = aligned_size - bytes.len() as u32;
|
||||
if padding > 0 {
|
||||
out.write_all(&ZERO_BUF[0..padding as usize])?;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::{fs::File, io::BufReader};
|
||||
use std::{fs::File, io::BufReader, ops::Range};
|
||||
|
||||
use anyhow::{Context, Error, Result};
|
||||
use argh::FromArgs;
|
||||
|
||||
use crate::util::map::{process_map, SymbolEntry, SymbolRef};
|
||||
use crate::util::map::{process_map, resolve_link_order, SymbolEntry, SymbolRef};
|
||||
|
||||
#[derive(FromArgs, PartialEq, Debug)]
|
||||
/// Commands for processing CodeWarrior maps.
|
||||
@@ -18,6 +18,9 @@ pub struct Args {
|
||||
enum SubCommand {
|
||||
Entries(EntriesArgs),
|
||||
Symbol(SymbolArgs),
|
||||
Order(OrderArgs),
|
||||
Slices(SlicesArgs),
|
||||
Symbols(SymbolsArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
@@ -44,10 +47,40 @@ pub struct SymbolArgs {
|
||||
symbol: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Attempts to resolve global link order.
|
||||
#[argh(subcommand, name = "order")]
|
||||
pub struct OrderArgs {
|
||||
#[argh(positional)]
|
||||
/// path to input map
|
||||
map_file: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Emits a slices.yml for ppcdis. (WIP)
|
||||
#[argh(subcommand, name = "slices")]
|
||||
pub struct SlicesArgs {
|
||||
#[argh(positional)]
|
||||
/// path to input map
|
||||
map_file: String,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Emits a symbols.yml for ppcdis. (WIP)
|
||||
#[argh(subcommand, name = "symbols")]
|
||||
pub struct SymbolsArgs {
|
||||
#[argh(positional)]
|
||||
/// path to input map
|
||||
map_file: String,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Entries(c_args) => entries(c_args),
|
||||
SubCommand::Symbol(c_args) => symbol(c_args),
|
||||
SubCommand::Order(c_args) => order(c_args),
|
||||
SubCommand::Slices(c_args) => slices(c_args),
|
||||
SubCommand::Symbols(c_args) => symbols(c_args),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,3 +171,64 @@ fn symbol(args: SymbolArgs) -> Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn order(args: OrderArgs) -> Result<()> {
|
||||
let reader = BufReader::new(
|
||||
File::open(&args.map_file)
|
||||
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
|
||||
);
|
||||
let entries = process_map(reader)?;
|
||||
let order = resolve_link_order(&entries.unit_order)?;
|
||||
for unit in order {
|
||||
println!("{unit}");
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn slices(args: SlicesArgs) -> Result<()> {
|
||||
let reader = BufReader::new(
|
||||
File::open(&args.map_file)
|
||||
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
|
||||
);
|
||||
let entries = process_map(reader)?;
|
||||
let order = resolve_link_order(&entries.unit_order)?;
|
||||
for unit in order {
|
||||
let unit_path = if let Some((lib, name)) = unit.split_once(' ') {
|
||||
format!("{}/{}", lib.strip_suffix(".a").unwrap_or(lib), name)
|
||||
} else if let Some(strip) = unit.strip_suffix(".o") {
|
||||
format!("{strip}.c")
|
||||
} else {
|
||||
unit.clone()
|
||||
};
|
||||
println!("{unit_path}:");
|
||||
let mut ranges = Vec::<(String, Range<u32>)>::new();
|
||||
match entries.unit_section_ranges.get(&unit) {
|
||||
Some(sections) => {
|
||||
for (name, range) in sections {
|
||||
ranges.push((name.clone(), range.clone()));
|
||||
}
|
||||
}
|
||||
None => return Err(Error::msg(format!("Failed to locate sections for unit '{unit}'"))),
|
||||
}
|
||||
ranges.sort_by(|(_, a), (_, b)| a.start.cmp(&b.start));
|
||||
for (name, range) in ranges {
|
||||
println!("\t{}: [{:#010x}, {:#010x}]", name, range.start, range.end);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn symbols(args: SymbolsArgs) -> Result<()> {
|
||||
let reader = BufReader::new(
|
||||
File::open(&args.map_file)
|
||||
.with_context(|| format!("Failed to open file '{}'", args.map_file))?,
|
||||
);
|
||||
let entries = process_map(reader)?;
|
||||
for (address, symbol) in entries.address_to_symbol {
|
||||
if symbol.name.starts_with('@') {
|
||||
continue;
|
||||
}
|
||||
println!("{:#010x}: {}", address, symbol.name);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -22,10 +22,10 @@ pub fn run(args: Args) -> Result<()> {
|
||||
let build_string = std::fs::read_to_string(&args.build_info)
|
||||
.with_context(|| format!("Failed to read build info string from '{}'", args.build_info))?;
|
||||
let build_string_trim = build_string.trim_end();
|
||||
if build_string_trim.as_bytes().len() > BUILD_STRING_MAX {
|
||||
let build_string_bytes = build_string_trim.as_bytes();
|
||||
if build_string_bytes.len() > BUILD_STRING_MAX {
|
||||
return Err(Error::msg(format!(
|
||||
"Build string '{}' is greater than maximum size of {}",
|
||||
build_string_trim, BUILD_STRING_MAX
|
||||
"Build string '{build_string_trim}' is greater than maximum size of {BUILD_STRING_MAX}"
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ pub fn run(args: Args) -> Result<()> {
|
||||
Some(idx) => idx + BUILD_STRING_TAG.as_bytes().len(),
|
||||
None => return Err(Error::msg("Failed to find build string tag in binary")),
|
||||
};
|
||||
let end = start + build_string_trim.as_bytes().len();
|
||||
map[start..end].copy_from_slice(build_string_trim.as_bytes());
|
||||
let end = start + build_string_bytes.len();
|
||||
map[start..end].copy_from_slice(build_string_bytes);
|
||||
map[end] = 0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
pub(crate) mod demangle;
|
||||
pub(crate) mod elf;
|
||||
pub(crate) mod elf2dol;
|
||||
pub(crate) mod map;
|
||||
pub(crate) mod metroidbuildinfo;
|
||||
|
||||
@@ -42,34 +42,34 @@ fn check(args: Args, file: File) -> Result<()> {
|
||||
for line in reader.lines() {
|
||||
let line = match line {
|
||||
Ok(line) => line,
|
||||
Err(e) => return Err(Error::msg(format!("File read failed: {}", e))),
|
||||
Err(e) => return Err(Error::msg(format!("File read failed: {e}"))),
|
||||
};
|
||||
let (hash, file_name) =
|
||||
line.split_once(' ').ok_or_else(|| Error::msg(format!("Invalid line: {}", line)))?;
|
||||
line.split_once(' ').ok_or_else(|| Error::msg(format!("Invalid line: {line}")))?;
|
||||
let file_name = match file_name.chars().next() {
|
||||
Some(' ') | Some('*') => &file_name[1..],
|
||||
_ => return Err(Error::msg(format!("Invalid line: {}", line))),
|
||||
_ => return Err(Error::msg(format!("Invalid line: {line}"))),
|
||||
};
|
||||
let mut hash_bytes = [0u8; 20];
|
||||
hex::decode_to_slice(hash, &mut hash_bytes)
|
||||
.with_context(|| format!("Invalid line: {}", line))?;
|
||||
.with_context(|| format!("Invalid line: {line}"))?;
|
||||
|
||||
let file = File::open(file_name)
|
||||
.with_context(|| format!("Failed to open file '{}'", file_name))?;
|
||||
let file =
|
||||
File::open(file_name).with_context(|| format!("Failed to open file '{file_name}'"))?;
|
||||
let found_hash = file_sha1(file)?;
|
||||
if hash_bytes == found_hash.as_ref() {
|
||||
println!("{}: OK", file_name);
|
||||
println!("{file_name}: OK");
|
||||
} else {
|
||||
println!("{}: FAILED", file_name);
|
||||
println!("{file_name}: FAILED");
|
||||
mismatches += 1;
|
||||
}
|
||||
}
|
||||
if mismatches != 0 {
|
||||
eprintln!("WARNING: {} computed checksum did NOT match", mismatches);
|
||||
eprintln!("WARNING: {mismatches} computed checksum did NOT match");
|
||||
std::process::exit(1);
|
||||
}
|
||||
if let Some(out_path) = args.output {
|
||||
touch(&out_path).with_context(|| format!("Failed to touch output file '{}'", out_path))?;
|
||||
touch(&out_path).with_context(|| format!("Failed to touch output file '{out_path}'"))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -78,7 +78,7 @@ fn hash(args: Args, file: File) -> Result<()> {
|
||||
let hash = file_sha1(file)?;
|
||||
let mut hash_buf = [0u8; 40];
|
||||
let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf)
|
||||
.map_err(|e| Error::msg(format!("Failed to encode hash: {}", e)))?;
|
||||
.map_err(|e| Error::msg(format!("Failed to encode hash: {e}")))?;
|
||||
println!("{} {}", hash_str, args.file);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user