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:
parent
4d039140f2
commit
c484952912
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
102
src/cmd/rel.rs
102
src/cmd/rel.rs
|
@ -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,12 +197,11 @@ 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
|
} as u8;
|
||||||
} as u8;
|
|
||||||
for (address, reloc) in section.relocations() {
|
for (address, reloc) in section.relocations() {
|
||||||
let reloc_target = match reloc.target() {
|
let reloc_target = match reloc.target() {
|
||||||
RelocationTarget::Symbol(idx) => {
|
RelocationTarget::Symbol(idx) => {
|
||||||
|
@ -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,18 +229,18 @@ 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;
|
||||||
relocations.push(RelReloc {
|
relocations.push(RelReloc {
|
||||||
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()?;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue