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 anyhow::{bail, ensure, Result};
|
||||||
use argp::FromArgs;
|
use argp::FromArgs;
|
||||||
use cwdemangle::{demangle, DemangleOptions};
|
use cwdemangle::{demangle, DemangleOptions};
|
||||||
|
use tracing::error;
|
||||||
|
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
|
config::{write_splits_file, write_symbols_file},
|
||||||
file::map_file,
|
file::map_file,
|
||||||
map::{process_map, SymbolEntry, SymbolRef},
|
map::{create_obj, process_map, SymbolEntry, SymbolRef},
|
||||||
|
split::update_splits,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Debug)]
|
#[derive(FromArgs, PartialEq, Debug)]
|
||||||
|
@ -22,6 +25,7 @@ pub struct Args {
|
||||||
enum SubCommand {
|
enum SubCommand {
|
||||||
Entries(EntriesArgs),
|
Entries(EntriesArgs),
|
||||||
Symbol(SymbolArgs),
|
Symbol(SymbolArgs),
|
||||||
|
Config(ConfigArgs),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||||
|
@ -48,10 +52,23 @@ pub struct SymbolArgs {
|
||||||
symbol: String,
|
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<()> {
|
pub fn run(args: Args) -> Result<()> {
|
||||||
match args.command {
|
match args.command {
|
||||||
SubCommand::Entries(c_args) => entries(c_args),
|
SubCommand::Entries(c_args) => entries(c_args),
|
||||||
SubCommand::Symbol(c_args) => symbol(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");
|
println!("\n");
|
||||||
Ok(())
|
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 anyhow::{anyhow, bail, ensure, Result};
|
||||||
use objdiff_core::obj::split_meta::SplitMeta;
|
use objdiff_core::obj::split_meta::SplitMeta;
|
||||||
pub use relocations::{ObjReloc, ObjRelocKind, ObjRelocations};
|
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 splits::{ObjSplit, ObjSplits};
|
||||||
pub use symbols::{
|
pub use symbols::{
|
||||||
best_match_for_reloc, ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
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 {
|
Ok(match section_name {
|
||||||
".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code,
|
".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code,
|
||||||
".ctors" | ".dtors" | ".rodata" | ".sdata2" | "extab" | "extabindex" | ".BINARY" => {
|
".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::{
|
use crate::{
|
||||||
obj::{
|
obj::{
|
||||||
ObjInfo, ObjKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
section_kind_for_section, ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind,
|
||||||
ObjUnit,
|
ObjSections, ObjSplit, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
||||||
|
ObjSymbols, ObjUnit,
|
||||||
},
|
},
|
||||||
util::{file::map_file, nested::NestedVec},
|
util::{file::map_file, nested::NestedVec},
|
||||||
};
|
};
|
||||||
|
@ -814,6 +815,108 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
|
||||||
Ok(())
|
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<()> {
|
fn add_symbol(obj: &mut ObjInfo, symbol_entry: &SymbolEntry, section: Option<usize>) -> Result<()> {
|
||||||
let demangled_name = demangle(&symbol_entry.name, &DemangleOptions::default());
|
let demangled_name = demangle(&symbol_entry.name, &DemangleOptions::default());
|
||||||
let mut flags: FlagSet<ObjSymbolFlags> = match symbol_entry.visibility {
|
let mut flags: FlagSet<ObjSymbolFlags> = match symbol_entry.visibility {
|
||||||
|
|
|
@ -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<()> {
|
pub fn update_splits(obj: &mut ObjInfo, common_start: Option<u32>, fill_gaps: bool) -> Result<()> {
|
||||||
// Create splits for extab and extabindex entries
|
// Create splits for extab and extabindex entries
|
||||||
if let Some((section_index, section)) = obj.sections.by_name("extabindex")? {
|
if let Some((section_index, section)) = obj.sections.by_name("extabindex")? {
|
||||||
let start = SectionAddress::new(section_index, section.address as u32);
|
if !section.data.is_empty() {
|
||||||
split_extabindex(obj, start)?;
|
let start = SectionAddress::new(section_index, section.address as u32);
|
||||||
|
split_extabindex(obj, start)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create splits for .ctors entries
|
// Create splits for .ctors entries
|
||||||
if let Some((section_index, section)) = obj.sections.by_name(".ctors")? {
|
if let Some((section_index, section)) = obj.sections.by_name(".ctors")? {
|
||||||
let start = SectionAddress::new(section_index, section.address as u32);
|
if !section.data.is_empty() {
|
||||||
let end = start + (section.size as u32 - 4);
|
let start = SectionAddress::new(section_index, section.address as u32);
|
||||||
split_ctors_dtors(obj, start, end)?;
|
let end = start + (section.size as u32 - 4);
|
||||||
|
split_ctors_dtors(obj, start, end)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create splits for .dtors entries
|
// Create splits for .dtors entries
|
||||||
if let Some((section_index, section)) = obj.sections.by_name(".dtors")? {
|
if let Some((section_index, section)) = obj.sections.by_name(".dtors")? {
|
||||||
let mut start = SectionAddress::new(section_index, section.address as u32);
|
if !section.data.is_empty() {
|
||||||
let end = start + (section.size as u32 - 4);
|
let mut start = SectionAddress::new(section_index, section.address as u32);
|
||||||
if obj.kind == ObjKind::Executable {
|
let end = start + (section.size as u32 - 4);
|
||||||
// Skip __destroy_global_chain_reference
|
if obj.kind == ObjKind::Executable {
|
||||||
start += 4;
|
// 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
|
// 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)
|
.get(section_index)
|
||||||
.ok_or_else(|| anyhow!("Invalid section index: {}", section_index))?;
|
.ok_or_else(|| anyhow!("Invalid section index: {}", section_index))?;
|
||||||
let mut section_end = (section.address + section.size) as u32;
|
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,
|
// .ctors and .dtors end with a linker-generated null pointer,
|
||||||
// adjust section size appropriately
|
// adjust section size appropriately
|
||||||
if matches!(section.name.as_str(), ".ctors" | ".dtors")
|
if matches!(section.name.as_str(), ".ctors" | ".dtors")
|
||||||
|
|
Loading…
Reference in New Issue