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:
parent
d4f695ffc7
commit
cfcd146dfa
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" => {
|
||||
|
|
107
src/util/map.rs
107
src/util/map.rs
|
@ -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 {
|
||||
|
|
|
@ -758,19 +758,24 @@ 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")? {
|
||||
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")? {
|
||||
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")? {
|
||||
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 {
|
||||
|
@ -779,6 +784,7 @@ pub fn update_splits(obj: &mut ObjInfo, common_start: Option<u32>, fill_gaps: bo
|
|||
}
|
||||
split_ctors_dtors(obj, start, end)?;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove linker generated symbols from splits
|
||||
trim_linker_generated_symbols(obj)?;
|
||||
|
@ -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")
|
||||
|
|
Loading…
Reference in New Issue