Support generating RELs with non-sequential module IDs

Uses the module ID from the configuration, if present,
falling back to the standard behavior of sequential
module IDs.
This commit is contained in:
Luke Street 2024-06-24 17:43:33 -06:00
parent 4d039140f2
commit c484952912
3 changed files with 62 additions and 44 deletions

2
Cargo.lock generated
View File

@ -367,7 +367,7 @@ checksum = "c2e06f9bce634a3c898eb1e5cb949ff63133cbb218af93cc9b38b31d6f3ea285"
[[package]] [[package]]
name = "decomp-toolkit" name = "decomp-toolkit"
version = "0.9.2" version = "0.9.3"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ar", "ar",

View File

@ -3,7 +3,7 @@ name = "decomp-toolkit"
description = "Yet another GameCube/Wii decompilation toolkit." description = "Yet another GameCube/Wii decompilation toolkit."
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.9.2" version = "0.9.3"
edition = "2021" edition = "2021"
publish = false publish = false
repository = "https://github.com/encounter/decomp-toolkit" repository = "https://github.com/encounter/decomp-toolkit"

View File

@ -178,12 +178,18 @@ fn load_rel(module_config: &ModuleConfig) -> Result<RelInfo> {
Ok((header, sections, section_defs)) Ok((header, sections, section_defs))
} }
struct LoadedModule<'a> {
module_id: u32,
file: File<'a>,
path: PathBuf,
}
fn resolve_relocations( fn resolve_relocations(
module: &File, module: &File,
existing_headers: &BTreeMap<u32, RelInfo>, existing_headers: &BTreeMap<u32, RelInfo>,
module_id: usize, module_id: u32,
symbol_map: &FxHashMap<&[u8], (usize, SymbolIndex)>, symbol_map: &FxHashMap<&[u8], (u32, SymbolIndex)>,
modules: &[(File, PathBuf)], modules: &[LoadedModule],
relocations: &mut Vec<RelReloc>, relocations: &mut Vec<RelReloc>,
) -> Result<usize> { ) -> Result<usize> {
let mut resolved = 0usize; let mut resolved = 0usize;
@ -191,8 +197,7 @@ fn resolve_relocations(
if !matches!(section.name(), Ok(name) if PERMITTED_SECTIONS.contains(&name)) { if !matches!(section.name(), Ok(name) if PERMITTED_SECTIONS.contains(&name)) {
continue; continue;
} }
let section_index = let section_index = if let Some((_, sections, _)) = existing_headers.get(&module_id) {
if let Some((_, sections, _)) = existing_headers.get(&(module_id as u32)) {
match_section_index(module, section.index(), sections)? match_section_index(module, section.index(), sections)?
} else { } else {
section.index().0 section.index().0
@ -211,7 +216,8 @@ fn resolve_relocations(
symbol_map symbol_map
.get(reloc_target.name_bytes()?) .get(reloc_target.name_bytes()?)
.map(|&(module_id, symbol_idx)| { .map(|&(module_id, symbol_idx)| {
(module_id, modules[module_id].0.symbol_by_index(symbol_idx).unwrap()) let module = modules.iter().find(|m| m.module_id == module_id).unwrap();
(module_id, module.file.symbol_by_index(symbol_idx).unwrap())
}) })
.ok_or_else(|| { .ok_or_else(|| {
anyhow!( anyhow!(
@ -223,10 +229,10 @@ fn resolve_relocations(
(module_id, reloc_target) (module_id, reloc_target)
}; };
let target_section_index = target_symbol.section_index().unwrap(); let target_section_index = target_symbol.section_index().unwrap();
let target_section = if let Some((_, sections, _)) = let target_section =
existing_headers.get(&(target_module_id as u32)) if let Some((_, sections, _)) = existing_headers.get(&target_module_id) {
{ let module = modules.iter().find(|m| m.module_id == module_id).unwrap();
match_section_index(&modules[target_module_id].0, target_section_index, sections)? match_section_index(&module.file, target_section_index, sections)?
} else { } else {
target_section_index.0 target_section_index.0
} as u8; } as u8;
@ -234,7 +240,7 @@ fn resolve_relocations(
kind: to_obj_reloc_kind(reloc.flags())?, kind: to_obj_reloc_kind(reloc.flags())?,
section: section_index, section: section_index,
address: address as u32, address: address as u32,
module_id: target_module_id as u32, module_id: target_module_id,
target_section, target_section,
addend: (target_symbol.address() as i64 + reloc.addend()) as u32, addend: (target_symbol.address() as i64 + reloc.addend()) as u32,
// Extra // Extra
@ -253,16 +259,19 @@ fn make(args: MakeArgs) -> Result<()> {
// Load existing REL headers (if specified) // Load existing REL headers (if specified)
let mut existing_headers = BTreeMap::<u32, RelInfo>::new(); let mut existing_headers = BTreeMap::<u32, RelInfo>::new();
let mut name_to_module_id = FxHashMap::<String, u32>::default();
if let Some(config_path) = &args.config { if let Some(config_path) = &args.config {
let config: ProjectConfig = serde_yaml::from_reader(&mut buf_reader(config_path)?)?; let config: ProjectConfig = serde_yaml::from_reader(&mut buf_reader(config_path)?)?;
for module_config in &config.modules { for module_config in &config.modules {
if !args.names.is_empty() && !args.names.iter().any(|n| n == &module_config.name()) { let module_name = module_config.name();
if !args.names.is_empty() && !args.names.iter().any(|n| n == &module_name) {
continue; continue;
} }
let _span = info_span!("module", name = %module_config.name()).entered(); let _span = info_span!("module", name = %module_name).entered();
let info = load_rel(module_config).with_context(|| { let info = load_rel(module_config).with_context(|| {
format!("While loading REL '{}'", module_config.object.display()) format!("While loading REL '{}'", module_config.object.display())
})?; })?;
name_to_module_id.insert(module_name.to_string(), info.0.module_id);
match existing_headers.entry(info.0.module_id) { match existing_headers.entry(info.0.module_id) {
btree_map::Entry::Vacant(e) => e.insert(info), btree_map::Entry::Vacant(e) => e.insert(info),
btree_map::Entry::Occupied(_) => { btree_map::Entry::Occupied(_) => {
@ -281,22 +290,33 @@ fn make(args: MakeArgs) -> Result<()> {
let files = paths.iter().map(map_file).collect::<Result<Vec<_>>>()?; let files = paths.iter().map(map_file).collect::<Result<Vec<_>>>()?;
let modules = files let modules = files
.par_iter() .par_iter()
.enumerate()
.zip(&paths) .zip(&paths)
.map(|(file, path)| { .map(|((idx, file), path)| {
// Fetch module ID by module name, if specified, to support non-sequential module IDs
// Otherwise, use sequential module IDs starting with the DOL as 0 (default behavior)
let module_id = args
.names
.get(idx)
.and_then(|n| name_to_module_id.get(n))
.copied()
.unwrap_or(idx as u32);
load_obj(file.as_slice()) load_obj(file.as_slice())
.map(|o| (o, path.clone())) .map(|o| LoadedModule { module_id, file: o, path: path.clone() })
.with_context(|| format!("Failed to load '{}'", path.display())) .with_context(|| format!("Failed to load '{}'", path.display()))
}) })
.collect::<Result<Vec<_>>>()?; .collect::<Result<Vec<_>>>()?;
// Create symbol map // Create symbol map
let start = Instant::now(); let start = Instant::now();
let mut symbol_map = FxHashMap::<&[u8], (usize, SymbolIndex)>::default(); let mut symbol_map = FxHashMap::<&[u8], (u32, SymbolIndex)>::default();
for (module_id, (module, path)) in modules.iter().enumerate() { for module_info in modules.iter() {
let _span = info_span!("file", path = %path.display()).entered(); let _span = info_span!("file", path = %module_info.path.display()).entered();
for symbol in module.symbols() { for symbol in module_info.file.symbols() {
if symbol.scope() == object::SymbolScope::Dynamic { if symbol.scope() == object::SymbolScope::Dynamic {
symbol_map.entry(symbol.name_bytes()?).or_insert((module_id, symbol.index())); symbol_map
.entry(symbol.name_bytes()?)
.or_insert((module_info.module_id, symbol.index()));
} }
} }
} }
@ -305,19 +325,19 @@ fn make(args: MakeArgs) -> Result<()> {
let mut resolved = 0usize; let mut resolved = 0usize;
let mut relocations = Vec::<Vec<RelReloc>>::with_capacity(modules.len() - 1); let mut relocations = Vec::<Vec<RelReloc>>::with_capacity(modules.len() - 1);
relocations.resize_with(modules.len() - 1, Vec::new); relocations.resize_with(modules.len() - 1, Vec::new);
for ((module_id, (module, path)), relocations) in for (module_info, relocations) in modules.iter().skip(1).zip(&mut relocations) {
modules.iter().enumerate().skip(1).zip(&mut relocations) let _span = info_span!("file", path = %module_info.path.display()).entered();
{
let _span = info_span!("file", path = %path.display()).entered();
resolved += resolve_relocations( resolved += resolve_relocations(
module, &module_info.file,
&existing_headers, &existing_headers,
module_id, module_info.module_id,
&symbol_map, &symbol_map,
&modules, &modules,
relocations, relocations,
) )
.with_context(|| format!("While resolving relocations in '{}'", path.display()))?; .with_context(|| {
format!("While resolving relocations in '{}'", module_info.path.display())
})?;
} }
if !args.quiet { if !args.quiet {
@ -332,12 +352,10 @@ fn make(args: MakeArgs) -> Result<()> {
// Write RELs // Write RELs
let start = Instant::now(); let start = Instant::now();
for ((module_id, (module, path)), relocations) in for (module_info, relocations) in modules.iter().skip(1).zip(relocations) {
modules.iter().enumerate().skip(1).zip(relocations) let _span = info_span!("file", path = %module_info.path.display()).entered();
{
let _span = info_span!("file", path = %path.display()).entered();
let mut info = RelWriteInfo { let mut info = RelWriteInfo {
module_id: module_id as u32, module_id: module_info.module_id,
version: 3, version: 3,
name_offset: None, name_offset: None,
name_size: None, name_size: None,
@ -349,7 +367,7 @@ fn make(args: MakeArgs) -> Result<()> {
section_exec: None, section_exec: None,
}; };
if let Some((header, section_headers, section_defs)) = if let Some((header, section_headers, section_defs)) =
existing_headers.get(&(module_id as u32)) existing_headers.get(&module_info.module_id)
{ {
info.version = header.version; info.version = header.version;
info.name_offset = Some(header.name_offset); info.name_offset = Some(header.name_offset);
@ -363,9 +381,9 @@ fn make(args: MakeArgs) -> Result<()> {
.unwrap_or_default(); .unwrap_or_default();
info.section_exec = Some(section_headers.iter().map(|s| s.exec()).collect()); info.section_exec = Some(section_headers.iter().map(|s| s.exec()).collect());
} }
let rel_path = path.with_extension("rel"); let rel_path = module_info.path.with_extension("rel");
let mut w = buf_writer(&rel_path)?; let mut w = buf_writer(&rel_path)?;
write_rel(&mut w, &info, module, relocations) write_rel(&mut w, &info, &module_info.file, relocations)
.with_context(|| format!("Failed to write '{}'", rel_path.display()))?; .with_context(|| format!("Failed to write '{}'", rel_path.display()))?;
w.flush()?; w.flush()?;
} }