Add map config for generating symbols/splits

Useful for extracting information from
map files that aren't fully well-formed,
such as ones from existing decompilation
projects.
This commit is contained in:
Luke Street 2024-09-09 20:38:25 -06:00
parent d4f695ffc7
commit cfcd146dfa
5 changed files with 161 additions and 17 deletions

View File

@ -1,12 +1,15 @@
use std::path::PathBuf;
use std::{fs::DirBuilder, path::PathBuf};
use anyhow::{bail, ensure, Result};
use argp::FromArgs;
use cwdemangle::{demangle, DemangleOptions};
use tracing::error;
use crate::util::{
config::{write_splits_file, write_symbols_file},
file::map_file,
map::{process_map, SymbolEntry, SymbolRef},
map::{create_obj, process_map, SymbolEntry, SymbolRef},
split::update_splits,
};
#[derive(FromArgs, PartialEq, Debug)]
@ -22,6 +25,7 @@ pub struct Args {
enum SubCommand {
Entries(EntriesArgs),
Symbol(SymbolArgs),
Config(ConfigArgs),
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
@ -48,10 +52,23 @@ pub struct SymbolArgs {
symbol: String,
}
#[derive(FromArgs, PartialEq, Eq, Debug)]
/// Generates project configuration files from a map. (symbols.txt, splits.txt)
#[argp(subcommand, name = "config")]
pub struct ConfigArgs {
#[argp(positional)]
/// path to input map
map_file: PathBuf,
#[argp(positional)]
/// output directory for symbols.txt and splits.txt
out_dir: PathBuf,
}
pub fn run(args: Args) -> Result<()> {
match args.command {
SubCommand::Entries(c_args) => entries(c_args),
SubCommand::Symbol(c_args) => symbol(c_args),
SubCommand::Config(c_args) => config(c_args),
}
}
@ -160,3 +177,18 @@ fn symbol(args: SymbolArgs) -> Result<()> {
println!("\n");
Ok(())
}
fn config(args: ConfigArgs) -> Result<()> {
let file = map_file(&args.map_file)?;
log::info!("Processing map...");
let entries = process_map(&mut file.as_reader(), None, None)?;
let mut obj = create_obj(&entries)?;
if let Err(e) = update_splits(&mut obj, None, false) {
error!("Failed to update splits: {}", e)
}
DirBuilder::new().recursive(true).create(&args.out_dir)?;
write_symbols_file(args.out_dir.join("symbols.txt"), &obj, None)?;
write_splits_file(args.out_dir.join("splits.txt"), &obj, false, None)?;
log::info!("Done!");
Ok(())
}

View File

@ -13,7 +13,7 @@ use std::{
use anyhow::{anyhow, bail, ensure, Result};
use objdiff_core::obj::split_meta::SplitMeta;
pub use relocations::{ObjReloc, ObjRelocKind, ObjRelocations};
pub use sections::{ObjSection, ObjSectionKind, ObjSections};
pub use sections::{ObjSection, ObjSectionKind, ObjSections, section_kind_for_section};
pub use splits::{ObjSplit, ObjSplits};
pub use symbols::{
best_match_for_reloc, ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,

View File

@ -218,7 +218,7 @@ impl ObjSection {
}
}
fn section_kind_for_section(section_name: &str) -> Result<ObjSectionKind> {
pub fn section_kind_for_section(section_name: &str) -> Result<ObjSectionKind> {
Ok(match section_name {
".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code,
".ctors" | ".dtors" | ".rodata" | ".sdata2" | "extab" | "extabindex" | ".BINARY" => {

View File

@ -18,8 +18,9 @@ use regex::{Captures, Regex};
use crate::{
obj::{
ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
ObjUnit,
section_kind_for_section, ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind,
ObjSections, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
ObjSymbols, ObjUnit,
},
util::{file::map_file, nested::NestedVec},
};
@ -814,6 +815,108 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
Ok(())
}
pub fn create_obj(result: &MapInfo) -> Result<ObjInfo> {
let sections = result
.sections
.iter()
.map(|s| {
let name = s.name.clone();
let address = s.address as u64;
let size = s.size as u64;
let file_offset = s.file_offset as u64;
let kind = section_kind_for_section(&name).unwrap_or(ObjSectionKind::ReadOnlyData);
ObjSection {
name,
kind,
address,
size,
data: vec![],
align: 0,
elf_index: 0,
relocations: Default::default(),
virtual_address: None,
file_offset,
section_known: true,
splits: Default::default(),
}
})
.collect();
let mut obj = ObjInfo {
kind: ObjKind::Executable,
architecture: ObjArchitecture::PowerPc,
name: "".to_string(),
symbols: ObjSymbols::new(ObjKind::Executable, vec![]),
sections: ObjSections::new(ObjKind::Executable, sections),
entry: None, // TODO result.entry_point
mw_comment: None,
split_meta: None,
sda2_base: None,
sda_base: None,
stack_address: None,
stack_end: None,
db_stack_addr: None,
arena_lo: None,
arena_hi: None,
link_order: vec![],
blocked_relocation_sources: Default::default(),
blocked_relocation_targets: Default::default(),
known_functions: Default::default(),
module_id: 0,
unresolved_relocations: vec![],
};
// Add section symbols
for (section_name, symbol_map) in &result.section_symbols {
let (section_index, _) = obj
.sections
.by_name(section_name)?
.ok_or_else(|| anyhow!("Failed to locate section {section_name} from map"))?;
for symbol_entry in symbol_map.values().flatten() {
add_symbol(&mut obj, symbol_entry, Some(section_index))?;
}
}
// Add splits
for (section_name, unit_order) in &result.section_units {
let (_, section) = obj
.sections
.iter_mut()
.find(|(_, s)| s.name == *section_name)
.ok_or_else(|| anyhow!("Failed to locate section '{}'", section_name))?;
let mut iter = unit_order.iter().peekable();
while let Some((addr, unit)) = iter.next() {
let next = iter
.peek()
.map(|(addr, _)| *addr)
.unwrap_or_else(|| (section.address + section.size) as u32);
let common = section_name == ".bss"
&& matches!(result.common_bss_start, Some(start) if *addr >= start);
let unit = unit.replace(' ', "/");
// Disable mw_comment_version for assembly units
if unit.ends_with(".s") && !obj.link_order.iter().any(|u| u.name == unit) {
obj.link_order.push(ObjUnit {
name: unit.clone(),
autogenerated: false,
comment_version: Some(0),
order: None,
});
}
section.splits.push(*addr, ObjSplit {
unit,
end: next,
align: None,
common,
autogenerated: false,
skip: false,
rename: None,
});
}
}
Ok(obj)
}
fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option<usize>) -> Result<()> {
let demangled_name = demangle(&symbol_entry.name, &DemangleOptions::default());
let mut flags: FlagSet<ObjSymbolFlags> = match symbol_entry.visibility {

View File

@ -758,26 +758,32 @@ fn trim_linker_generated_symbols(obj: &mut ObjInfo) -> Result<()> {
pub fn update_splits(obj: &mut ObjInfo, common_start: Option<u32>, fill_gaps: bool) -> Result<()> {
// Create splits for extab and extabindex entries
if let Some((section_index, section)) = obj.sections.by_name("extabindex")? {
let start = SectionAddress::new(section_index, section.address as u32);
split_extabindex(obj, start)?;
if !section.data.is_empty() {
let start = SectionAddress::new(section_index, section.address as u32);
split_extabindex(obj, start)?;
}
}
// Create splits for .ctors entries
if let Some((section_index, section)) = obj.sections.by_name(".ctors")? {
let start = SectionAddress::new(section_index, section.address as u32);
let end = start + (section.size as u32 - 4);
split_ctors_dtors(obj, start, end)?;
if !section.data.is_empty() {
let start = SectionAddress::new(section_index, section.address as u32);
let end = start + (section.size as u32 - 4);
split_ctors_dtors(obj, start, end)?;
}
}
// Create splits for .dtors entries
if let Some((section_index, section)) = obj.sections.by_name(".dtors")? {
let mut start = SectionAddress::new(section_index, section.address as u32);
let end = start + (section.size as u32 - 4);
if obj.kind == ObjKind::Executable {
// Skip __destroy_global_chain_reference
start += 4;
if !section.data.is_empty() {
let mut start = SectionAddress::new(section_index, section.address as u32);
let end = start + (section.size as u32 - 4);
if obj.kind == ObjKind::Executable {
// Skip __destroy_global_chain_reference
start += 4;
}
split_ctors_dtors(obj, start, end)?;
}
split_ctors_dtors(obj, start, end)?;
}
// Remove linker generated symbols from splits
@ -1352,6 +1358,9 @@ pub fn end_for_section(obj: &ObjInfo, section_index: usize) -> Result<SectionAdd
.get(section_index)
.ok_or_else(|| anyhow!("Invalid section index: {}", section_index))?;
let mut section_end = (section.address + section.size) as u32;
if section.data.is_empty() {
return Ok(SectionAddress::new(section_index, section_end));
}
// .ctors and .dtors end with a linker-generated null pointer,
// adjust section size appropriately
if matches!(section.name.as_str(), ".ctors" | ".dtors")