Working `rel make` & more
- Added `elf info` - Improved `rel info` - Colored output for `shasum` - Fix section `rename` in RELs - Added padding symbols to avoid linker issues - Automatically set symbols to "active" in .comment output
This commit is contained in:
parent
a2374e4fa0
commit
f9f7fb2e1e
|
@ -100,6 +100,23 @@ dependencies = [
|
|||
"syn 1.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "array-init"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d62b7694a562cdf5a74227903507c56ab2cc8bdd1f781ed5cb4cf9c9f810bfc"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
||||
dependencies = [
|
||||
"hermit-abi 0.1.19",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -142,6 +159,30 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binrw"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab81d22cbd2d745852348b2138f3db2103afa8ce043117a374581926a523e267"
|
||||
dependencies = [
|
||||
"array-init",
|
||||
"binrw_derive",
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "binrw_derive"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6b019a3efebe7f453612083202887b6f1ace59e20d010672e336eea4ed5be97"
|
||||
dependencies = [
|
||||
"either",
|
||||
"owo-colors",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 1.0.107",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
|
@ -157,6 +198,12 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -260,13 +307,14 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "decomp-toolkit"
|
||||
version = "0.3.7"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"ar",
|
||||
"argp",
|
||||
"base16ct",
|
||||
"base64",
|
||||
"binrw",
|
||||
"byteorder",
|
||||
"cwdemangle",
|
||||
"dol",
|
||||
|
@ -285,12 +333,14 @@ dependencies = [
|
|||
"num_enum",
|
||||
"object 0.31.1",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"path-slash",
|
||||
"petgraph",
|
||||
"ppc750cl",
|
||||
"rayon",
|
||||
"regex",
|
||||
"rmp-serde",
|
||||
"rustc-hash",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
|
@ -423,6 +473,15 @@ version = "0.14.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.2"
|
||||
|
@ -455,6 +514,12 @@ dependencies = [
|
|||
"hashbrown 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.11.0"
|
||||
|
@ -488,6 +553,15 @@ version = "0.4.19"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4"
|
||||
|
||||
[[package]]
|
||||
name = "matchers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558"
|
||||
dependencies = [
|
||||
"regex-automata 0.1.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
|
@ -564,7 +638,7 @@ version = "1.16.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"hermit-abi 0.3.2",
|
||||
"libc",
|
||||
]
|
||||
|
||||
|
@ -622,6 +696,15 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
dependencies = [
|
||||
"supports-color",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.11"
|
||||
|
@ -739,8 +822,17 @@ checksum = "89089e897c013b3deb627116ae56a6955a72b8bed395c9526af31c9fe528b484"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
"regex-automata 0.3.0",
|
||||
"regex-syntax 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
|
||||
dependencies = [
|
||||
"regex-syntax 0.6.29",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -751,9 +843,15 @@ checksum = "fa250384981ea14565685dea16a9ccc4d1c541a13f82b9c168572264d1df8c56"
|
|||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
"regex-syntax 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.3"
|
||||
|
@ -788,6 +886,12 @@ version = "0.1.23"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.12"
|
||||
|
@ -881,6 +985,16 @@ version = "1.11.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ba6faf2ca7ee42fdd458f4347ae0a9bd6bcc445ad7cb57ad82b383f18870d6f"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.107"
|
||||
|
@ -992,10 +1106,14 @@ version = "0.3.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77"
|
||||
dependencies = [
|
||||
"matchers",
|
||||
"nu-ansi-term",
|
||||
"once_cell",
|
||||
"regex",
|
||||
"sharded-slab",
|
||||
"smallvec",
|
||||
"thread_local",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
"tracing-log",
|
||||
]
|
||||
|
|
|
@ -3,7 +3,7 @@ name = "decomp-toolkit"
|
|||
description = "Yet another GameCube/Wii decompilation toolkit."
|
||||
authors = ["Luke Street <luke@street.dev>"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.3.7"
|
||||
version = "0.4.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
build = "build.rs"
|
||||
|
@ -26,6 +26,7 @@ ar = { git = "https://github.com/bjorn3/rust-ar.git", branch = "write_symbol_tab
|
|||
argp = "0.3.0"
|
||||
base16ct = "0.2.0"
|
||||
base64 = "0.21.2"
|
||||
binrw = "0.11.2"
|
||||
byteorder = "1.4.3"
|
||||
cwdemangle = "0.1.5"
|
||||
dol = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" }
|
||||
|
@ -44,11 +45,13 @@ multimap = "0.9.0"
|
|||
num_enum = "0.6.1"
|
||||
object = { version = "0.31.1", features = ["read_core", "std", "elf", "write_std"], default-features = false }
|
||||
once_cell = "1.18.0"
|
||||
owo-colors = { version = "3.5.0", features = ["supports-colors"] }
|
||||
path-slash = "0.2.1"
|
||||
petgraph = "0.6.3"
|
||||
ppc750cl = { git = "https://github.com/encounter/ppc750cl", rev = "5f6e991bf495388c4104f188d2e90c79da9f78de" }
|
||||
rayon = "1.7.0"
|
||||
regex = "1.9.0"
|
||||
rustc-hash = "1.1.0"
|
||||
serde = "1.0.166"
|
||||
serde_json = "1.0.104"
|
||||
serde_repr = "0.1.14"
|
||||
|
@ -57,7 +60,7 @@ sha-1 = "0.10.1"
|
|||
smallvec = "1.11.0"
|
||||
tracing = "0.1.37"
|
||||
tracing-attributes = "0.1.26"
|
||||
tracing-subscriber = "0.3.17"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
|
||||
[build-dependencies]
|
||||
anyhow = { version = "1.0.71", features = ["backtrace"] }
|
||||
|
|
10
README.md
10
README.md
|
@ -105,8 +105,8 @@ it operates as a one-shot assembly generator, it still suffers from many of the
|
|||
### ppcdis
|
||||
|
||||
[ppcdis](https://github.com/SeekyCt/ppcdis) is one of the tools that inspired decomp-toolkit. It has more accurate
|
||||
analysis than doldisasm.py, and has similar goals to decomp-toolkit. It also has some features that decomp-toolkit does
|
||||
not yet, like support for REL files.
|
||||
analysis than doldisasm.py, and has similar goals to decomp-toolkit. It's been used successfully in several
|
||||
decompilation projects.
|
||||
|
||||
However, decomp-toolkit has a few advantages:
|
||||
|
||||
|
@ -226,7 +226,7 @@ Generates `ldscript.lcf` for `mwldeppc.exe`.
|
|||
|
||||
**Future work**
|
||||
|
||||
- Support REL and RSO files
|
||||
- Support RSO files
|
||||
- Add more signatures
|
||||
- Rework CodeWarrior map parsing
|
||||
|
||||
|
@ -335,7 +335,7 @@ $ dtk map symbol Game.MAP 'Function__5ClassFv'
|
|||
|
||||
### rel info
|
||||
|
||||
Prints basic information about a REL file.
|
||||
Prints information about a REL file.
|
||||
|
||||
```shell
|
||||
$ dtk rel info input.rel
|
||||
|
@ -355,7 +355,7 @@ $ dtk rel info main.dol rels/*.rel -o merged.elf
|
|||
> [!WARNING]
|
||||
> This command is not yet functional.
|
||||
|
||||
Prints basic information about an RSO file.
|
||||
Prints information about an RSO file.
|
||||
|
||||
```shell
|
||||
$ dtk rso info input.rso
|
||||
|
|
|
@ -166,13 +166,8 @@ impl AnalyzerState {
|
|||
pub fn detect_functions(&mut self, obj: &ObjInfo) -> Result<()> {
|
||||
// Apply known functions from extab
|
||||
for (&addr, &size) in &obj.known_functions {
|
||||
let (section_index, _) = obj
|
||||
.sections
|
||||
.at_address(addr)
|
||||
.context(format!("Function {:#010X} outside of any section", addr))?;
|
||||
let addr_ref = SectionAddress::new(section_index, addr);
|
||||
self.function_entries.insert(addr_ref);
|
||||
self.function_bounds.insert(addr_ref, Some(addr_ref + size));
|
||||
self.function_entries.insert(addr);
|
||||
self.function_bounds.insert(addr, Some(addr + size));
|
||||
}
|
||||
// Apply known functions from symbols
|
||||
for (_, symbol) in obj.symbols.by_kind(ObjSymbolKind::Function) {
|
||||
|
|
|
@ -201,7 +201,11 @@ impl AnalysisPass for FindRelCtorsDtors {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
log::debug!("Found .ctors and .dtors: {:?}", possible_sections);
|
||||
log::debug!(
|
||||
"Found .ctors and .dtors: {}, {}",
|
||||
possible_sections[0].0,
|
||||
possible_sections[1].0
|
||||
);
|
||||
let ctors_section_index = possible_sections[0].0;
|
||||
state.known_sections.insert(ctors_section_index, ".ctors".to_string());
|
||||
state.known_symbols.insert(SectionAddress::new(ctors_section_index, 0), ObjSymbol {
|
||||
|
@ -311,7 +315,11 @@ impl AnalysisPass for FindRelRodataData {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
log::debug!("Found .rodata and .data: {:?}", possible_sections);
|
||||
log::debug!(
|
||||
"Found .rodata and .data: {}, {}",
|
||||
possible_sections[0].0,
|
||||
possible_sections[1].0
|
||||
);
|
||||
let rodata_section_index = possible_sections[0].0;
|
||||
state.known_sections.insert(rodata_section_index, ".rodata".to_string());
|
||||
|
||||
|
|
|
@ -264,6 +264,8 @@ fn apply_ctors_signatures(obj: &mut ObjInfo) -> Result<()> {
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
})?;
|
||||
}
|
||||
Ok(())
|
||||
|
@ -362,6 +364,8 @@ fn apply_dtors_signatures(obj: &mut ObjInfo) -> Result<()> {
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
@ -439,6 +443,5 @@ pub fn apply_signatures_post(obj: &mut ObjInfo) -> Result<()> {
|
|||
apply_signature(obj, symbol_addr, &signature)?;
|
||||
}
|
||||
}
|
||||
log::debug!("Done!");
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ fn check_sequence(
|
|||
|
||||
impl FunctionSlices {
|
||||
pub fn end(&self) -> Option<SectionAddress> {
|
||||
self.blocks.last_key_value().map(|(_, &end)| end).flatten()
|
||||
self.blocks.last_key_value().and_then(|(_, &end)| end)
|
||||
}
|
||||
|
||||
pub fn start(&self) -> Option<SectionAddress> {
|
||||
|
@ -404,7 +404,7 @@ impl FunctionSlices {
|
|||
// Likely __noreturn
|
||||
}
|
||||
(None, Some(e)) => {
|
||||
log::info!("{:#010X?}", self);
|
||||
log::warn!("{:#010X?}", self);
|
||||
bail!("Unpaired epilogue {:#010X}", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -403,8 +403,9 @@ impl Tracker {
|
|||
from: SectionAddress,
|
||||
addr: u32,
|
||||
) -> Option<SectionAddress> {
|
||||
if let Some((&start, &end)) = obj.blocked_ranges.range(..=from.address).next_back() {
|
||||
if from.address >= start && from.address < end {
|
||||
if let Some((&start, &end)) = obj.blocked_ranges.range(..=from).next_back() {
|
||||
if from.section == start.section && from.address >= start.address && from.address < end
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ use std::{
|
|||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use argp::FromArgs;
|
||||
use itertools::Itertools;
|
||||
use memmap2::Mmap;
|
||||
use rayon::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, info, info_span};
|
||||
|
@ -28,7 +27,6 @@ use crate::{
|
|||
signatures::{apply_signatures, apply_signatures_post},
|
||||
tracker::Tracker,
|
||||
},
|
||||
cmd::shasum::file_sha1,
|
||||
obj::{
|
||||
best_match_for_reloc, ObjDataKind, ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind,
|
||||
ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, SymbolIndex,
|
||||
|
@ -43,13 +41,13 @@ use crate::{
|
|||
dep::DepFile,
|
||||
dol::process_dol,
|
||||
elf::{process_elf, write_elf},
|
||||
file::{buf_writer, map_file, map_reader, touch, Reader},
|
||||
file::{buf_writer, decompress_if_needed, map_file, touch, verify_hash, Reader},
|
||||
lcf::{asm_path_for_unit, generate_ldscript, obj_path_for_unit},
|
||||
map::apply_map_file,
|
||||
rel::process_rel,
|
||||
rso::{process_rso, DOL_SECTION_ABS, DOL_SECTION_NAMES},
|
||||
split::{is_linker_generated_object, split_obj, update_splits},
|
||||
yaz0,
|
||||
IntoCow, ToCow,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -225,7 +223,7 @@ impl ModuleConfig {
|
|||
pub fn file_prefix(&self) -> Cow<'_, str> {
|
||||
match self.file_name() {
|
||||
Cow::Borrowed(s) => {
|
||||
Cow::Borrowed(s.split_once('.').map(|(prefix, _)| prefix).unwrap_or(&s))
|
||||
Cow::Borrowed(s.split_once('.').map(|(prefix, _)| prefix).unwrap_or(s))
|
||||
}
|
||||
Cow::Owned(s) => {
|
||||
Cow::Owned(s.split_once('.').map(|(prefix, _)| prefix).unwrap_or(&s).to_string())
|
||||
|
@ -379,40 +377,32 @@ fn info(args: InfoArgs) -> Result<()> {
|
|||
);
|
||||
}
|
||||
println!("\nDiscovered symbols:");
|
||||
println!("\t{: >23} | {: <10} | {: <10}", "Name", "Address", "Size");
|
||||
println!("\t{: >10} | {: <10} | {: <10} | {: <10}", "Section", "Address", "Size", "Name");
|
||||
for (_, symbol) in obj.symbols.iter_ordered().chain(obj.symbols.iter_abs()) {
|
||||
if symbol.name.starts_with('@') || is_auto_symbol(&symbol.name) {
|
||||
if symbol.name.starts_with('@') || is_auto_symbol(symbol) {
|
||||
continue;
|
||||
}
|
||||
if symbol.size_known {
|
||||
println!("\t{: >23} | {:#010X} | {: <#10X}", symbol.name, symbol.address, symbol.size);
|
||||
let section_str = if let Some(section) = symbol.section {
|
||||
obj.sections[section].name.as_str()
|
||||
} else {
|
||||
let size_str = if symbol.section.is_none() { "ABS" } else { "?" };
|
||||
println!("\t{: >23} | {:#010X} | {: <10}", symbol.name, symbol.address, size_str);
|
||||
}
|
||||
"ABS"
|
||||
};
|
||||
let size_str = if symbol.size_known {
|
||||
format!("{:#X}", symbol.size).into_cow()
|
||||
} else if symbol.section.is_none() {
|
||||
"ABS".to_cow()
|
||||
} else {
|
||||
"?".to_cow()
|
||||
};
|
||||
println!(
|
||||
"\t{: >10} | {: <#10X} | {: <10} | {: <10}",
|
||||
section_str, symbol.address, size_str, symbol.name
|
||||
);
|
||||
}
|
||||
println!("\n{} discovered functions from exception table", obj.known_functions.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_hash<P: AsRef<Path>>(path: P, hash_str: &str) -> Result<()> {
|
||||
let mut hash_bytes = [0u8; 20];
|
||||
hex::decode_to_slice(hash_str, &mut hash_bytes)
|
||||
.with_context(|| format!("Invalid SHA-1 '{hash_str}'"))?;
|
||||
let file = File::open(path.as_ref())
|
||||
.with_context(|| format!("Failed to open file '{}'", path.as_ref().display()))?;
|
||||
let found_hash = file_sha1(file)?;
|
||||
if found_hash.as_ref() == hash_bytes {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"Hash mismatch: expected {}, but was {}",
|
||||
hex::encode(hash_bytes),
|
||||
hex::encode(found_hash)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
type ModuleMap<'a> = BTreeMap<u32, (&'a ModuleConfig, ObjInfo)>;
|
||||
|
||||
fn update_symbols(obj: &mut ObjInfo, modules: &ModuleMap<'_>) -> Result<()> {
|
||||
|
@ -632,15 +622,9 @@ fn resolve_external_relocations(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn decompress_if_needed(map: &Mmap) -> Result<Cow<[u8]>> {
|
||||
Ok(if map.len() > 4 && map[0..4] == *b"Yaz0" {
|
||||
Cow::Owned(yaz0::decompress_file(&mut map_reader(map))?)
|
||||
} else {
|
||||
Cow::Borrowed(map)
|
||||
})
|
||||
}
|
||||
type AnalyzeResult = (ObjInfo, Vec<PathBuf>);
|
||||
|
||||
fn load_analyze_dol(config: &ProjectConfig) -> Result<(ObjInfo, Vec<PathBuf>)> {
|
||||
fn load_analyze_dol(config: &ProjectConfig) -> Result<AnalyzeResult> {
|
||||
// log::info!("Loading {}", config.object.display());
|
||||
if let Some(hash_str) = &config.base.hash {
|
||||
verify_hash(&config.base.object, hash_str)?;
|
||||
|
@ -697,7 +681,7 @@ fn split_write_obj(
|
|||
obj: &mut ObjInfo,
|
||||
config: &ProjectConfig,
|
||||
module_config: &ModuleConfig,
|
||||
out_dir: &PathBuf,
|
||||
out_dir: &Path,
|
||||
no_update: bool,
|
||||
) -> Result<OutputModule> {
|
||||
debug!("Performing relocation analysis");
|
||||
|
@ -723,15 +707,15 @@ fn split_write_obj(
|
|||
if !no_update {
|
||||
debug!("Writing configuration");
|
||||
if let Some(symbols_path) = &module_config.symbols {
|
||||
write_symbols_file(symbols_path, &obj)?;
|
||||
write_symbols_file(symbols_path, obj)?;
|
||||
}
|
||||
if let Some(splits_path) = &module_config.splits {
|
||||
write_splits_file(splits_path, &obj, false)?;
|
||||
write_splits_file(splits_path, obj, false)?;
|
||||
}
|
||||
}
|
||||
|
||||
debug!("Splitting {} objects", obj.link_order.len());
|
||||
let split_objs = split_obj(&obj)?;
|
||||
let split_objs = split_obj(obj)?;
|
||||
|
||||
debug!("Writing object files");
|
||||
let obj_dir = out_dir.join("obj");
|
||||
|
@ -757,7 +741,7 @@ fn split_write_obj(
|
|||
}
|
||||
|
||||
// Generate ldscript.lcf
|
||||
fs::write(&out_config.ldscript, generate_ldscript(&obj, config.auto_force_files)?)?;
|
||||
fs::write(&out_config.ldscript, generate_ldscript(obj, config.auto_force_files)?)?;
|
||||
|
||||
debug!("Writing disassembly");
|
||||
let asm_dir = out_dir.join("asm");
|
||||
|
@ -772,17 +756,18 @@ fn split_write_obj(
|
|||
Ok(out_config)
|
||||
}
|
||||
|
||||
fn load_analyze_rel(
|
||||
config: &ProjectConfig,
|
||||
module_config: &ModuleConfig,
|
||||
) -> Result<(ObjInfo, Vec<PathBuf>)> {
|
||||
fn load_analyze_rel(config: &ProjectConfig, module_config: &ModuleConfig) -> Result<AnalyzeResult> {
|
||||
debug!("Loading {}", module_config.object.display());
|
||||
if let Some(hash_str) = &module_config.hash {
|
||||
verify_hash(&module_config.object, hash_str)?;
|
||||
}
|
||||
let map = map_file(&module_config.object)?;
|
||||
let buf = decompress_if_needed(&map)?;
|
||||
let mut module_obj = process_rel(Reader::new(&buf))?;
|
||||
let (_, mut module_obj) = process_rel(&mut Reader::new(buf.as_ref()))?;
|
||||
|
||||
if let Some(comment_version) = config.mw_comment_version {
|
||||
module_obj.mw_comment = Some(MWComment::new(comment_version)?);
|
||||
}
|
||||
|
||||
let mut dep = vec![module_config.object.clone()];
|
||||
if let Some(map_path) = &module_config.map {
|
||||
|
@ -833,8 +818,8 @@ fn split(args: SplitArgs) -> Result<()> {
|
|||
module_count,
|
||||
rayon::current_num_threads()
|
||||
);
|
||||
let mut dol_result: Option<Result<(ObjInfo, Vec<PathBuf>)>> = None;
|
||||
let mut modules_result: Option<Result<Vec<(ObjInfo, Vec<PathBuf>)>>> = None;
|
||||
let mut dol_result: Option<Result<AnalyzeResult>> = None;
|
||||
let mut modules_result: Option<Result<Vec<AnalyzeResult>>> = None;
|
||||
let start = Instant::now();
|
||||
rayon::scope(|s| {
|
||||
// DOL
|
||||
|
@ -999,7 +984,7 @@ fn split(args: SplitArgs) -> Result<()> {
|
|||
// }
|
||||
|
||||
let duration = command_start.elapsed();
|
||||
info!("Total duration: {}.{:03}s", duration.as_secs(), duration.subsec_millis());
|
||||
info!("Total time: {}.{:03}s", duration.as_secs(), duration.subsec_millis());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1167,7 +1152,11 @@ fn diff(args: DiffArgs) -> Result<()> {
|
|||
log::info!("Loading {}", args.map_file.display());
|
||||
apply_map_file(&args.map_file, &mut linked_obj)?;
|
||||
|
||||
for orig_sym in obj.symbols.iter().filter(|s| s.kind != ObjSymbolKind::Section) {
|
||||
for orig_sym in obj
|
||||
.symbols
|
||||
.iter()
|
||||
.filter(|s| !matches!(s.kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section))
|
||||
{
|
||||
let Some(orig_section_index) = orig_sym.section else { continue };
|
||||
let orig_section = &obj.sections[orig_section_index];
|
||||
let (linked_section_index, linked_section) =
|
||||
|
@ -1244,7 +1233,9 @@ fn diff(args: DiffArgs) -> Result<()> {
|
|||
}
|
||||
|
||||
// Data diff
|
||||
for orig_sym in obj.symbols.iter().filter(|s| s.kind != ObjSymbolKind::Section) {
|
||||
for orig_sym in obj.symbols.iter().filter(|s| {
|
||||
s.size > 0 && !matches!(s.kind, ObjSymbolKind::Unknown | ObjSymbolKind::Section)
|
||||
}) {
|
||||
let Some(orig_section_index) = orig_sym.section else { continue };
|
||||
let orig_section = &obj.sections[orig_section_index];
|
||||
let (linked_section_index, linked_section) =
|
||||
|
|
132
src/cmd/elf.rs
132
src/cmd/elf.rs
|
@ -19,11 +19,13 @@ use crate::{
|
|||
obj::ObjKind,
|
||||
util::{
|
||||
asm::write_asm,
|
||||
comment::{read_comment_sym, MWComment},
|
||||
config::{write_splits_file, write_symbols_file},
|
||||
elf::{process_elf, write_elf},
|
||||
file::{buf_writer, process_rsp},
|
||||
file::{buf_writer, process_rsp, Reader},
|
||||
signatures::{compare_signature, generate_signature, FunctionSignature},
|
||||
split::split_obj,
|
||||
IntoCow, ToCow,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -43,6 +45,7 @@ enum SubCommand {
|
|||
Fixup(FixupArgs),
|
||||
Signatures(SignaturesArgs),
|
||||
Split(SplitArgs),
|
||||
Info(InfoArgs),
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
|
@ -108,6 +111,15 @@ pub struct SignaturesArgs {
|
|||
out_file: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Prints information about an ELF file.
|
||||
#[argp(subcommand, name = "info")]
|
||||
pub struct InfoArgs {
|
||||
#[argp(positional)]
|
||||
/// input file
|
||||
input: PathBuf,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Config(c_args) => config(c_args),
|
||||
|
@ -115,6 +127,7 @@ pub fn run(args: Args) -> Result<()> {
|
|||
SubCommand::Fixup(c_args) => fixup(c_args),
|
||||
SubCommand::Split(c_args) => split(c_args),
|
||||
SubCommand::Signatures(c_args) => signatures(c_args),
|
||||
SubCommand::Info(c_args) => info(c_args),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -467,3 +480,120 @@ fn signatures(args: SignaturesArgs) -> Result<()> {
|
|||
out.flush()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn info(args: InfoArgs) -> Result<()> {
|
||||
let in_buf = fs::read(&args.input)
|
||||
.with_context(|| format!("Failed to open input file: '{}'", args.input.display()))?;
|
||||
let in_file = object::read::File::parse(&*in_buf).context("Failed to parse input ELF")?;
|
||||
|
||||
println!("ELF type: {:?}", in_file.kind());
|
||||
println!("Section count: {}", in_file.sections().count());
|
||||
println!("Symbol count: {}", in_file.symbols().count());
|
||||
println!(
|
||||
"Relocation count: {}",
|
||||
in_file.sections().map(|s| s.relocations().count()).sum::<usize>()
|
||||
);
|
||||
|
||||
println!("\nSections:");
|
||||
println!(
|
||||
"{: >15} | {: <10} | {: <10} | {: <10} | {: <10}",
|
||||
"Name", "Type", "Size", "File Off", "Index"
|
||||
);
|
||||
for section in in_file.sections().skip(1) {
|
||||
let kind_str = match section.kind() {
|
||||
SectionKind::Text => "code".to_cow(),
|
||||
SectionKind::Data => "data".to_cow(),
|
||||
SectionKind::ReadOnlyData => "rodata".to_cow(),
|
||||
SectionKind::UninitializedData => "bss".to_cow(),
|
||||
SectionKind::Metadata => continue, // "metadata".to_cow()
|
||||
SectionKind::Other => "other".to_cow(),
|
||||
_ => format!("unknown: {:?}", section.kind()).into_cow(),
|
||||
};
|
||||
println!(
|
||||
"{: >15} | {: <10} | {: <#10X} | {: <#10X} | {: <10}",
|
||||
section.name()?,
|
||||
kind_str,
|
||||
section.size(),
|
||||
section.file_range().unwrap_or_default().0,
|
||||
section.index().0
|
||||
);
|
||||
}
|
||||
|
||||
println!("\nSymbols:");
|
||||
println!("{: >15} | {: <10} | {: <10} | {: <10}", "Section", "Address", "Size", "Name");
|
||||
for symbol in in_file.symbols().filter(|s| s.is_definition()) {
|
||||
let section_str = if let Some(section) = symbol.section_index() {
|
||||
in_file.section_by_index(section)?.name()?.to_string().into_cow()
|
||||
} else {
|
||||
"ABS".to_cow()
|
||||
};
|
||||
let size_str = if symbol.section_index().is_none() {
|
||||
"ABS".to_cow()
|
||||
} else {
|
||||
format!("{:#X}", symbol.size()).into_cow()
|
||||
};
|
||||
println!(
|
||||
"{: >15} | {: <#10X} | {: <10} | {: <10}",
|
||||
section_str,
|
||||
symbol.address(),
|
||||
size_str,
|
||||
symbol.name()?
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(comment_section) = in_file.section_by_name(".comment") {
|
||||
let data = comment_section.uncompressed_data()?;
|
||||
if !data.is_empty() {
|
||||
let mut reader = Reader::new(&*data);
|
||||
let header =
|
||||
MWComment::parse_header(&mut reader).context("While reading .comment section")?;
|
||||
println!("\nMetrowerks metadata (.comment):");
|
||||
println!("\tVersion: {}", header.version);
|
||||
println!(
|
||||
"\tCompiler version: {}.{}.{}.{}",
|
||||
header.compiler_version[0],
|
||||
header.compiler_version[1],
|
||||
header.compiler_version[2],
|
||||
header.compiler_version[3]
|
||||
);
|
||||
println!("\tPool data: {}", header.pool_data);
|
||||
println!("\tFloat: {:?}", header.float);
|
||||
println!(
|
||||
"\tProcessor: {}",
|
||||
if header.processor == 0x16 {
|
||||
"Gekko".to_cow()
|
||||
} else {
|
||||
format!("{:#X}", header.processor).into_cow()
|
||||
}
|
||||
);
|
||||
println!(
|
||||
"\tIncompatible return small structs: {}",
|
||||
header.incompatible_return_small_structs
|
||||
);
|
||||
println!(
|
||||
"\tIncompatible sfpe double params: {}",
|
||||
header.incompatible_sfpe_double_params
|
||||
);
|
||||
println!("\tUnsafe global reg vars: {}", header.unsafe_global_reg_vars);
|
||||
println!("\n{: >10} | {: <6} | {: <6} | {: <10}", "Align", "Vis", "Active", "Symbol");
|
||||
for symbol in in_file.symbols() {
|
||||
let comment_sym = read_comment_sym(&mut reader)?;
|
||||
if symbol.is_definition() {
|
||||
println!(
|
||||
"{: >10} | {: <#6X} | {: <#6X} | {: <10}",
|
||||
comment_sym.align,
|
||||
comment_sym.vis_flags,
|
||||
comment_sym.active_flags,
|
||||
symbol.name()?
|
||||
);
|
||||
}
|
||||
}
|
||||
ensure!(
|
||||
data.len() - reader.position() as usize == 0,
|
||||
".comment section data not fully read"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
247
src/cmd/rel.rs
247
src/cmd/rel.rs
|
@ -1,27 +1,45 @@
|
|||
use std::{
|
||||
collections::{btree_map, BTreeMap},
|
||||
ffi::OsStr,
|
||||
fs,
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
time::Instant,
|
||||
};
|
||||
|
||||
use anyhow::{bail, ensure, Context, Result};
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use argp::FromArgs;
|
||||
use object::{
|
||||
Architecture, Endianness, Object, ObjectSection, ObjectSymbol, RelocationTarget, SymbolIndex,
|
||||
};
|
||||
use rayon::prelude::*;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{info, info_span};
|
||||
|
||||
use crate::{
|
||||
analysis::{
|
||||
cfa::{AnalyzerState, SectionAddress},
|
||||
pass::{AnalysisPass, FindSaveRestSleds, FindTRKInterruptVectorTable},
|
||||
pass::{
|
||||
AnalysisPass, FindRelCtorsDtors, FindRelRodataData, FindSaveRestSleds,
|
||||
FindTRKInterruptVectorTable,
|
||||
},
|
||||
signatures::{apply_signatures, apply_signatures_post},
|
||||
tracker::Tracker,
|
||||
},
|
||||
array_ref_mut,
|
||||
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSymbol},
|
||||
cmd::dol::ProjectConfig,
|
||||
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol},
|
||||
util::{
|
||||
config::is_auto_symbol,
|
||||
dol::process_dol,
|
||||
elf::write_elf,
|
||||
file::{map_file, map_reader, FileIterator},
|
||||
elf::{to_obj_reloc_kind, write_elf},
|
||||
file::{
|
||||
buf_reader, buf_writer, decompress_if_needed, map_file, process_rsp, verify_hash,
|
||||
FileIterator, Reader,
|
||||
},
|
||||
nested::NestedMap,
|
||||
rel::process_rel,
|
||||
rel::{process_rel, process_rel_header, write_rel, RelHeader, RelReloc, RelWriteInfo},
|
||||
IntoCow, ToCow,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -37,6 +55,7 @@ pub struct Args {
|
|||
#[argp(subcommand)]
|
||||
enum SubCommand {
|
||||
Info(InfoArgs),
|
||||
Make(MakeArgs),
|
||||
Merge(MergeArgs),
|
||||
}
|
||||
|
||||
|
@ -64,17 +83,227 @@ pub struct MergeArgs {
|
|||
out_file: PathBuf,
|
||||
}
|
||||
|
||||
#[derive(FromArgs, PartialEq, Eq, Debug)]
|
||||
/// Creates RELs from an ELF + PLF(s).
|
||||
#[argp(subcommand, name = "make")]
|
||||
pub struct MakeArgs {
|
||||
#[argp(positional)]
|
||||
/// input file(s)
|
||||
files: Vec<PathBuf>,
|
||||
#[argp(option, short = 'c')]
|
||||
/// (optional) project configuration file
|
||||
config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
pub fn run(args: Args) -> Result<()> {
|
||||
match args.command {
|
||||
SubCommand::Info(c_args) => info(c_args),
|
||||
SubCommand::Merge(c_args) => merge(c_args),
|
||||
SubCommand::Make(c_args) => make(c_args),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_obj(buf: &[u8]) -> Result<object::File> {
|
||||
let obj = object::read::File::parse(buf)?;
|
||||
match obj.architecture() {
|
||||
Architecture::PowerPc => {}
|
||||
arch => bail!("Unexpected architecture: {arch:?}"),
|
||||
};
|
||||
ensure!(obj.endianness() == Endianness::Big, "Expected big endian");
|
||||
Ok(obj)
|
||||
}
|
||||
|
||||
fn make(args: MakeArgs) -> Result<()> {
|
||||
let total = Instant::now();
|
||||
|
||||
// Load existing REL headers (if specified)
|
||||
let mut existing_headers = BTreeMap::<u32, RelHeader>::new();
|
||||
if let Some(config_path) = &args.config {
|
||||
let config: ProjectConfig = serde_yaml::from_reader(&mut buf_reader(config_path)?)?;
|
||||
for module_config in &config.modules {
|
||||
if let Some(hash_str) = &module_config.hash {
|
||||
verify_hash(&module_config.object, hash_str)?;
|
||||
}
|
||||
let map = map_file(&module_config.object)?;
|
||||
let buf = decompress_if_needed(&map)?;
|
||||
let header = process_rel_header(&mut Reader::new(buf.as_ref()))?;
|
||||
existing_headers.insert(header.module_id, header);
|
||||
}
|
||||
}
|
||||
|
||||
let files = process_rsp(&args.files)?;
|
||||
info!("Loading {} modules", files.len());
|
||||
|
||||
// Load all modules
|
||||
let handles = files.iter().map(map_file).collect::<Result<Vec<_>>>()?;
|
||||
let modules = handles
|
||||
.par_iter()
|
||||
.zip(&files)
|
||||
.map(|(map, path)| {
|
||||
load_obj(map).with_context(|| format!("Failed to load '{}'", path.display()))
|
||||
})
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
// Create symbol map
|
||||
let start = Instant::now();
|
||||
let mut symbol_map = FxHashMap::<&[u8], (usize, SymbolIndex)>::default();
|
||||
for (module_id, module) in modules.iter().enumerate() {
|
||||
for symbol in module.symbols() {
|
||||
if symbol.is_definition() && symbol.scope() == object::SymbolScope::Dynamic {
|
||||
symbol_map.entry(symbol.name_bytes()?).or_insert((module_id, symbol.index()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve relocations
|
||||
let mut resolved = 0usize;
|
||||
let mut relocations = Vec::<Vec<RelReloc>>::with_capacity(modules.len() - 1);
|
||||
relocations.resize_with(modules.len() - 1, Vec::new);
|
||||
for ((module_id, module), relocations) in
|
||||
modules.iter().enumerate().skip(1).zip(&mut relocations)
|
||||
{
|
||||
for section in module.sections() {
|
||||
for (address, reloc) in section.relocations() {
|
||||
let reloc_target = match reloc.target() {
|
||||
RelocationTarget::Symbol(idx) => {
|
||||
module.symbol_by_index(idx).with_context(|| {
|
||||
format!("Relocation against invalid symbol index {}", idx.0)
|
||||
})?
|
||||
}
|
||||
reloc_target => bail!("Unsupported relocation target: {reloc_target:?}"),
|
||||
};
|
||||
let (target_module_id, target_symbol) = if reloc_target.is_undefined() {
|
||||
resolved += 1;
|
||||
symbol_map
|
||||
.get(reloc_target.name_bytes()?)
|
||||
.map(|&(module_id, symbol_idx)| {
|
||||
(module_id, modules[module_id].symbol_by_index(symbol_idx).unwrap())
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
anyhow!(
|
||||
"Failed to find symbol {} in any module",
|
||||
reloc_target.name().unwrap_or("[invalid]")
|
||||
)
|
||||
})?
|
||||
} else {
|
||||
(module_id, reloc_target)
|
||||
};
|
||||
relocations.push(RelReloc {
|
||||
kind: to_obj_reloc_kind(reloc.kind())?,
|
||||
section: section.index().0 as u8,
|
||||
address: address as u32,
|
||||
module_id: target_module_id as u32,
|
||||
target_section: target_symbol.section_index().unwrap().0 as u8,
|
||||
addend: target_symbol.address() as u32,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let duration = start.elapsed();
|
||||
info!(
|
||||
"Symbol resolution completed in {}.{:03}s (resolved {} symbols)",
|
||||
duration.as_secs(),
|
||||
duration.subsec_millis(),
|
||||
resolved
|
||||
);
|
||||
|
||||
// Write RELs
|
||||
let start = Instant::now();
|
||||
for (((module_id, module), path), relocations) in
|
||||
modules.iter().enumerate().zip(&files).skip(1).zip(relocations)
|
||||
{
|
||||
let name =
|
||||
path.file_stem().unwrap_or(OsStr::new("[unknown]")).to_str().unwrap_or("[invalid]");
|
||||
let _span = info_span!("module", name = %name).entered();
|
||||
let mut info = RelWriteInfo {
|
||||
module_id: module_id as u32,
|
||||
version: 3,
|
||||
name_offset: None,
|
||||
name_size: None,
|
||||
align: None,
|
||||
bss_align: None,
|
||||
section_count: None,
|
||||
};
|
||||
if let Some(existing_module) = existing_headers.get(&(module_id as u32)) {
|
||||
info.version = existing_module.version;
|
||||
info.name_offset = Some(existing_module.name_offset);
|
||||
info.name_size = Some(existing_module.name_size);
|
||||
info.align = existing_module.align;
|
||||
info.bss_align = existing_module.bss_align;
|
||||
info.section_count = Some(existing_module.num_sections as usize);
|
||||
}
|
||||
let rel_path = path.with_extension("rel");
|
||||
let mut w = buf_writer(&rel_path)?;
|
||||
write_rel(&mut w, &info, module, relocations)
|
||||
.with_context(|| format!("Failed to write '{}'", rel_path.display()))?;
|
||||
w.flush()?;
|
||||
}
|
||||
let duration = start.elapsed();
|
||||
info!("RELs written in {}.{:03}s", duration.as_secs(), duration.subsec_millis());
|
||||
|
||||
let duration = total.elapsed();
|
||||
info!("Total time: {}.{:03}s", duration.as_secs(), duration.subsec_millis());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn info(args: InfoArgs) -> Result<()> {
|
||||
let map = map_file(args.rel_file)?;
|
||||
let rel = process_rel(map_reader(&map))?;
|
||||
println!("Read REL module ID {}", rel.module_id);
|
||||
let buf = decompress_if_needed(&map)?;
|
||||
let (header, mut module_obj) = process_rel(&mut Reader::new(buf.as_ref()))?;
|
||||
|
||||
let mut state = AnalyzerState::default();
|
||||
state.detect_functions(&module_obj)?;
|
||||
FindRelCtorsDtors::execute(&mut state, &module_obj)?;
|
||||
FindRelRodataData::execute(&mut state, &module_obj)?;
|
||||
state.apply(&mut module_obj)?;
|
||||
|
||||
apply_signatures(&mut module_obj)?;
|
||||
apply_signatures_post(&mut module_obj)?;
|
||||
|
||||
println!("REL module ID: {}", header.module_id);
|
||||
println!("REL version: {}", header.version);
|
||||
println!("Original section count: {}", header.num_sections);
|
||||
println!("\nSections:");
|
||||
println!(
|
||||
"{: >10} | {: <10} | {: <10} | {: <10} | {: <10}",
|
||||
"Name", "Type", "Size", "File Off", "Index"
|
||||
);
|
||||
for (_, section) in module_obj.sections.iter() {
|
||||
let kind_str = match section.kind {
|
||||
ObjSectionKind::Code => "code",
|
||||
ObjSectionKind::Data => "data",
|
||||
ObjSectionKind::ReadOnlyData => "rodata",
|
||||
ObjSectionKind::Bss => "bss",
|
||||
};
|
||||
println!(
|
||||
"{: >10} | {: <10} | {: <#10X} | {: <#10X} | {: <10}",
|
||||
section.name, kind_str, section.size, section.file_offset, section.elf_index
|
||||
);
|
||||
}
|
||||
println!("\nDiscovered symbols:");
|
||||
println!("{: >10} | {: <10} | {: <10} | {: <10}", "Section", "Address", "Size", "Name");
|
||||
for (_, symbol) in module_obj.symbols.iter_ordered() {
|
||||
if symbol.name.starts_with('@') || is_auto_symbol(symbol) {
|
||||
continue;
|
||||
}
|
||||
let section_str = if let Some(section) = symbol.section {
|
||||
module_obj.sections[section].name.as_str()
|
||||
} else {
|
||||
"ABS"
|
||||
};
|
||||
let size_str = if symbol.size_known {
|
||||
format!("{:#X}", symbol.size).into_cow()
|
||||
} else if symbol.section.is_none() {
|
||||
"ABS".to_cow()
|
||||
} else {
|
||||
"?".to_cow()
|
||||
};
|
||||
println!(
|
||||
"{: >10} | {: <#10X} | {: <10} | {: <10}",
|
||||
section_str, symbol.address, size_str, symbol.name
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -94,7 +323,7 @@ fn merge(args: MergeArgs) -> Result<()> {
|
|||
for result in FileIterator::new(&args.rel_files)? {
|
||||
let (path, entry) = result?;
|
||||
log::info!("Loading {}", path.display());
|
||||
let obj = process_rel(entry.as_reader())?;
|
||||
let (_, obj) = process_rel(&mut entry.as_reader())?;
|
||||
match module_map.entry(obj.module_id) {
|
||||
btree_map::Entry::Vacant(e) => e.insert(obj),
|
||||
btree_map::Entry::Occupied(_) => bail!("Duplicate module ID {}", obj.module_id),
|
||||
|
|
|
@ -6,6 +6,7 @@ use std::{
|
|||
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use argp::FromArgs;
|
||||
use owo_colors::OwoColorize;
|
||||
use sha1::{Digest, Sha1};
|
||||
|
||||
use crate::util::file::{process_rsp, touch};
|
||||
|
@ -66,14 +67,17 @@ fn check(file: File) -> Result<()> {
|
|||
File::open(file_name).with_context(|| format!("Failed to open file '{file_name}'"))?;
|
||||
let found_hash = file_sha1(file)?;
|
||||
if hash_bytes == found_hash.as_ref() {
|
||||
println!("{file_name}: OK");
|
||||
println!("{}: {}", file_name, "OK".green());
|
||||
} else {
|
||||
println!("{file_name}: FAILED");
|
||||
println!("{}: {}", file_name, "FAILED".red());
|
||||
mismatches += 1;
|
||||
}
|
||||
}
|
||||
if mismatches != 0 {
|
||||
eprintln!("WARNING: {mismatches} computed checksum did NOT match");
|
||||
eprintln!(
|
||||
"{}",
|
||||
format!("WARNING: {mismatches} computed checksum(s) did NOT match").yellow()
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
Ok(())
|
||||
|
|
32
src/main.rs
32
src/main.rs
|
@ -1,6 +1,8 @@
|
|||
use std::{ffi::OsStr, path::PathBuf, str::FromStr};
|
||||
|
||||
use argp::{FromArgValue, FromArgs};
|
||||
use tracing::level_filters::LevelFilter;
|
||||
use tracing_subscriber::EnvFilter;
|
||||
|
||||
pub mod analysis;
|
||||
pub mod argp_version;
|
||||
|
@ -60,10 +62,10 @@ struct TopLevel {
|
|||
#[argp(option, short = 'C')]
|
||||
/// Change working directory.
|
||||
chdir: Option<PathBuf>,
|
||||
#[argp(option, short = 'L', default = "LogLevel::Info")]
|
||||
#[argp(option, short = 'L')]
|
||||
/// Minimum logging level. (Default: info)
|
||||
/// Possible values: error, warn, info, debug, trace
|
||||
log_level: LogLevel,
|
||||
log_level: Option<LogLevel>,
|
||||
/// Print version information and exit.
|
||||
#[argp(switch, short = 'V')]
|
||||
version: bool,
|
||||
|
@ -86,11 +88,29 @@ enum SubCommand {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
let format = tracing_subscriber::fmt::format().with_target(false).without_time();
|
||||
tracing_subscriber::fmt().event_format(format).init();
|
||||
// TODO reimplement log level selection
|
||||
|
||||
let args: TopLevel = argp_version::from_env();
|
||||
let format = tracing_subscriber::fmt::format().with_target(false).without_time();
|
||||
let builder = tracing_subscriber::fmt().event_format(format);
|
||||
if let Some(level) = args.log_level {
|
||||
builder
|
||||
.with_max_level(match level {
|
||||
LogLevel::Error => LevelFilter::ERROR,
|
||||
LogLevel::Warn => LevelFilter::WARN,
|
||||
LogLevel::Info => LevelFilter::INFO,
|
||||
LogLevel::Debug => LevelFilter::DEBUG,
|
||||
LogLevel::Trace => LevelFilter::TRACE,
|
||||
})
|
||||
.init();
|
||||
} else {
|
||||
builder
|
||||
.with_env_filter(
|
||||
EnvFilter::builder()
|
||||
.with_default_directive(LevelFilter::INFO.into())
|
||||
.from_env_lossy(),
|
||||
)
|
||||
.init();
|
||||
}
|
||||
|
||||
let mut result = Ok(());
|
||||
if let Some(dir) = &args.chdir {
|
||||
result = std::env::set_current_dir(dir).map_err(|e| {
|
||||
|
|
|
@ -18,7 +18,10 @@ pub use symbols::{
|
|||
ObjSymbolScope, ObjSymbols, SymbolIndex,
|
||||
};
|
||||
|
||||
use crate::util::{comment::MWComment, rel::RelReloc};
|
||||
use crate::{
|
||||
analysis::cfa::SectionAddress,
|
||||
util::{comment::MWComment, rel::RelReloc},
|
||||
};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ObjKind {
|
||||
|
@ -63,12 +66,11 @@ pub struct ObjInfo {
|
|||
pub arena_hi: Option<u32>,
|
||||
|
||||
// Extracted
|
||||
pub named_sections: BTreeMap<u32, String>,
|
||||
pub link_order: Vec<ObjUnit>,
|
||||
pub blocked_ranges: BTreeMap<u32, u32>, // start -> end
|
||||
pub blocked_ranges: BTreeMap<SectionAddress, u32>, // start -> end
|
||||
|
||||
// From extab
|
||||
pub known_functions: BTreeMap<u32, u32>,
|
||||
pub known_functions: BTreeMap<SectionAddress, u32>,
|
||||
|
||||
// REL
|
||||
/// Module ID (0 for main)
|
||||
|
@ -99,8 +101,6 @@ impl ObjInfo {
|
|||
db_stack_addr: None,
|
||||
arena_lo: None,
|
||||
arena_hi: None,
|
||||
// splits: Default::default(),
|
||||
named_sections: Default::default(),
|
||||
link_order: vec![],
|
||||
blocked_ranges: Default::default(),
|
||||
known_functions: Default::default(),
|
||||
|
@ -276,6 +276,8 @@ impl ObjInfo {
|
|||
align: new_align,
|
||||
common: split.common,
|
||||
autogenerated: new_autogenerated,
|
||||
skip: false, // ?
|
||||
rename: None, // ?
|
||||
})?;
|
||||
return Ok(());
|
||||
}
|
||||
|
|
|
@ -15,6 +15,10 @@ pub struct ObjSplit {
|
|||
pub common: bool,
|
||||
/// Generated, replaceable by user.
|
||||
pub autogenerated: bool,
|
||||
/// Skip when emitting the split object.
|
||||
pub skip: bool,
|
||||
/// Override the section name in the split object. (e.g. `.ctors$10`)
|
||||
pub rename: Option<String>,
|
||||
}
|
||||
|
||||
/// Splits within a section.
|
||||
|
|
|
@ -37,6 +37,8 @@ flags! {
|
|||
ForceActive,
|
||||
/// Symbol isn't referenced by any relocations
|
||||
RelocationIgnore,
|
||||
/// Symbol won't be written to symbols file
|
||||
NoWrite,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,6 +80,9 @@ impl ObjSymbolFlagSet {
|
|||
#[inline]
|
||||
pub fn is_relocation_ignore(&self) -> bool { self.0.contains(ObjSymbolFlags::RelocationIgnore) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_no_write(&self) -> bool { self.0.contains(ObjSymbolFlags::NoWrite) }
|
||||
|
||||
#[inline]
|
||||
pub fn set_scope(&mut self, scope: ObjSymbolScope) {
|
||||
match scope {
|
||||
|
@ -196,7 +201,7 @@ impl ObjSymbols {
|
|||
self.at_section_address(section_index, in_symbol.address as u32).find(|(_, symbol)| {
|
||||
symbol.kind == in_symbol.kind ||
|
||||
// Replace auto symbols with real symbols
|
||||
(symbol.kind == ObjSymbolKind::Unknown && is_auto_symbol(&symbol.name))
|
||||
(symbol.kind == ObjSymbolKind::Unknown && is_auto_symbol(symbol))
|
||||
})
|
||||
} else if self.obj_kind == ObjKind::Executable {
|
||||
// TODO hmmm
|
||||
|
@ -205,6 +210,7 @@ impl ObjSymbols {
|
|||
bail!("ABS symbol in relocatable object: {:?}", in_symbol);
|
||||
};
|
||||
let target_symbol_idx = if let Some((symbol_idx, existing)) = opt {
|
||||
let replace = replace || (is_auto_symbol(existing) && !is_auto_symbol(&in_symbol));
|
||||
let size =
|
||||
if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size {
|
||||
// TODO fix and promote back to warning
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::{
|
|||
io::Write,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use itertools::Itertools;
|
||||
use ppc750cl::{disasm_iter, Argument, Ins, Opcode};
|
||||
|
||||
|
@ -438,7 +438,8 @@ fn write_data<W: Write>(
|
|||
write_symbol_entry(w, symbols, entry)?;
|
||||
}
|
||||
current_symbol_kind = find_symbol_kind(current_symbol_kind, symbols, vec)?;
|
||||
current_data_kind = find_data_kind(current_data_kind, symbols, vec)?;
|
||||
current_data_kind = find_data_kind(current_data_kind, symbols, vec)
|
||||
.with_context(|| format!("At address {:#010X}", sym_addr))?;
|
||||
entry = entry_iter.next();
|
||||
} else if current_address > sym_addr {
|
||||
let dbg_symbols = vec.iter().map(|e| &symbols[e.index]).collect_vec();
|
||||
|
@ -550,10 +551,16 @@ fn find_data_kind(
|
|||
SymbolEntryKind::Start => {
|
||||
let new_kind = symbols[entry.index].data_kind;
|
||||
if !matches!(new_kind, ObjDataKind::Unknown) {
|
||||
ensure!(
|
||||
!found || new_kind == kind,
|
||||
"Conflicting data kinds found: {kind:?} and {new_kind:?}"
|
||||
if found && new_kind != kind {
|
||||
for entry in entries {
|
||||
log::error!("Symbol {:?}", symbols[entry.index]);
|
||||
}
|
||||
bail!(
|
||||
"Conflicting data kinds found: {kind:?} and {new_kind:?}",
|
||||
kind = kind,
|
||||
new_kind = new_kind
|
||||
);
|
||||
}
|
||||
found = true;
|
||||
kind = new_kind;
|
||||
}
|
||||
|
|
|
@ -169,7 +169,7 @@ pub struct CommentSym {
|
|||
}
|
||||
|
||||
impl CommentSym {
|
||||
pub fn from(symbol: &ObjSymbol) -> Self {
|
||||
pub fn from(symbol: &ObjSymbol, force_active: bool) -> Self {
|
||||
let align = match symbol.align {
|
||||
Some(align) => align,
|
||||
None => {
|
||||
|
@ -196,8 +196,12 @@ impl CommentSym {
|
|||
vis_flags |= 0xD;
|
||||
}
|
||||
let mut active_flags = 0;
|
||||
if symbol.flags.is_force_active() {
|
||||
active_flags |= 0x8; // TODO what is 0x10?
|
||||
if symbol.flags.is_force_active()
|
||||
|| (force_active
|
||||
&& matches!(symbol.kind, ObjSymbolKind::Function | ObjSymbolKind::Object)
|
||||
&& symbol.flags.is_global())
|
||||
{
|
||||
active_flags |= 0x8;
|
||||
}
|
||||
Self { align, vis_flags, active_flags }
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use once_cell::sync::Lazy;
|
|||
use regex::{Captures, Regex};
|
||||
|
||||
use crate::{
|
||||
analysis::cfa::SectionAddress,
|
||||
obj::{
|
||||
ObjDataKind, ObjInfo, ObjKind, ObjSectionKind, ObjSplit, ObjSymbol, ObjSymbolFlagSet,
|
||||
ObjSymbolFlags, ObjSymbolKind, ObjUnit,
|
||||
|
@ -118,7 +119,13 @@ pub fn parse_symbol_line(line: &str, obj: &mut ObjInfo) -> Result<Option<ObjSymb
|
|||
"Symbol {} requires size != 0 with noreloc",
|
||||
symbol.name
|
||||
);
|
||||
obj.blocked_ranges.insert(addr, addr + symbol.size as u32);
|
||||
ensure!(
|
||||
section.is_some(),
|
||||
"Symbol {} requires section with noreloc",
|
||||
symbol.name
|
||||
);
|
||||
let addr = SectionAddress::new(section.unwrap(), symbol.address as u32);
|
||||
obj.blocked_ranges.insert(addr, addr.address + symbol.size as u32);
|
||||
}
|
||||
_ => bail!("Unknown symbol attribute '{attr}'"),
|
||||
}
|
||||
|
@ -133,7 +140,9 @@ pub fn parse_symbol_line(line: &str, obj: &mut ObjInfo) -> Result<Option<ObjSymb
|
|||
}
|
||||
|
||||
pub fn is_skip_symbol(symbol: &ObjSymbol) -> bool {
|
||||
let _ = symbol;
|
||||
if symbol.flags.is_no_write() {
|
||||
return true;
|
||||
}
|
||||
// symbol.name.starts_with("lbl_")
|
||||
// || symbol.name.starts_with("func_")
|
||||
// || symbol.name.starts_with("switch_")
|
||||
|
@ -142,7 +151,9 @@ pub fn is_skip_symbol(symbol: &ObjSymbol) -> bool {
|
|||
false
|
||||
}
|
||||
|
||||
pub fn is_auto_symbol(name: &str) -> bool { name.starts_with("lbl_") || name.starts_with("fn_") }
|
||||
pub fn is_auto_symbol(symbol: &ObjSymbol) -> bool {
|
||||
symbol.name.starts_with("lbl_") || symbol.name.starts_with("fn_")
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn write_symbols_file<P: AsRef<Path>>(path: P, obj: &ObjInfo) -> Result<()> {
|
||||
|
@ -188,9 +199,11 @@ fn write_symbol<W: Write>(w: &mut W, obj: &ObjInfo, symbol: &ObjSymbol) -> Resul
|
|||
// if symbol.flags.is_force_active() {
|
||||
// write!(w, " force_active")?;
|
||||
// }
|
||||
if obj.blocked_ranges.contains_key(&(symbol.address as u32)) {
|
||||
if let Some(section) = symbol.section {
|
||||
if obj.blocked_ranges.contains_key(&SectionAddress::new(section, symbol.address as u32)) {
|
||||
write!(w, " noreloc")?;
|
||||
}
|
||||
}
|
||||
writeln!(w)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -335,10 +348,11 @@ pub fn write_splits<W: Write>(w: &mut W, obj: &ObjInfo, all: bool) -> Result<()>
|
|||
if split.common {
|
||||
write!(w, " common")?;
|
||||
}
|
||||
if let Some(name) = obj.named_sections.get(&addr) {
|
||||
if name != §ion.name {
|
||||
if let Some(name) = &split.rename {
|
||||
write!(w, " rename:{}", name)?;
|
||||
}
|
||||
if split.skip {
|
||||
write!(w, " skip")?;
|
||||
}
|
||||
writeln!(w)?;
|
||||
}
|
||||
|
@ -354,6 +368,7 @@ struct SplitSection {
|
|||
/// Whether this is a part of common BSS.
|
||||
common: bool,
|
||||
rename: Option<String>,
|
||||
skip: bool,
|
||||
}
|
||||
|
||||
struct SplitUnit {
|
||||
|
@ -443,6 +458,8 @@ fn parse_section_line(captures: Captures, state: &SplitState) -> Result<SplitLin
|
|||
return Ok(SplitLine::Section(section));
|
||||
}
|
||||
|
||||
let mut start = None;
|
||||
let mut end = None;
|
||||
let mut section = SplitSection {
|
||||
name: captures["name"].to_string(),
|
||||
start: 0,
|
||||
|
@ -450,13 +467,14 @@ fn parse_section_line(captures: Captures, state: &SplitState) -> Result<SplitLin
|
|||
align: None,
|
||||
common: false,
|
||||
rename: None,
|
||||
skip: false,
|
||||
};
|
||||
|
||||
for attr in captures["attrs"].split(' ').filter(|&s| !s.is_empty()) {
|
||||
if let Some((attr, value)) = attr.split_once(':') {
|
||||
match attr {
|
||||
"start" => section.start = parse_hex(value)?,
|
||||
"end" => section.end = parse_hex(value)?,
|
||||
"start" => start = Some(parse_hex(value)?),
|
||||
"end" => end = Some(parse_hex(value)?),
|
||||
"align" => section.align = Some(u32::from_str(value)?),
|
||||
"rename" => section.rename = Some(value.to_string()),
|
||||
_ => bail!("Unknown split attribute '{attr}'"),
|
||||
|
@ -469,11 +487,14 @@ fn parse_section_line(captures: Captures, state: &SplitState) -> Result<SplitLin
|
|||
section.align = Some(4);
|
||||
}
|
||||
}
|
||||
"skip" => section.skip = true,
|
||||
_ => bail!("Unknown split attribute '{attr}'"),
|
||||
}
|
||||
}
|
||||
}
|
||||
if section.start > 0 && section.end > 0 {
|
||||
if let (Some(start), Some(end)) = (start, end) {
|
||||
section.start = start;
|
||||
section.end = end;
|
||||
Ok(SplitLine::UnitSection(section))
|
||||
} else {
|
||||
Err(anyhow!("Section '{}' missing start or end address", section.name))
|
||||
|
@ -531,7 +552,7 @@ pub fn apply_splits<R: BufRead>(r: R, obj: &mut ObjInfo) -> Result<()> {
|
|||
obj.sections.count()
|
||||
);
|
||||
};
|
||||
if let Err(_) = obj_section.rename(name.clone()) {
|
||||
if obj_section.rename(name.clone()).is_err() {
|
||||
// Manual section
|
||||
obj_section.kind =
|
||||
kind.ok_or_else(|| anyhow!("Section '{}' missing type", name))?;
|
||||
|
@ -545,7 +566,15 @@ pub fn apply_splits<R: BufRead>(r: R, obj: &mut ObjInfo) -> Result<()> {
|
|||
}
|
||||
(
|
||||
SplitState::Unit(unit),
|
||||
SplitLine::UnitSection(SplitSection { name, start, end, align, common, rename }),
|
||||
SplitLine::UnitSection(SplitSection {
|
||||
name,
|
||||
start,
|
||||
end,
|
||||
align,
|
||||
common,
|
||||
rename,
|
||||
skip,
|
||||
}),
|
||||
) => {
|
||||
let (section_index, _) = match obj.sections.by_name(&name)? {
|
||||
Some(v) => Ok(v),
|
||||
|
@ -573,10 +602,9 @@ pub fn apply_splits<R: BufRead>(r: R, obj: &mut ObjInfo) -> Result<()> {
|
|||
align,
|
||||
common,
|
||||
autogenerated: false,
|
||||
skip,
|
||||
rename,
|
||||
});
|
||||
if let Some(name) = rename {
|
||||
obj.named_sections.insert(start, name);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use anyhow::{anyhow, bail, ensure, Result};
|
|||
use dol::{Dol, DolSection, DolSectionType};
|
||||
|
||||
use crate::{
|
||||
analysis::cfa::locate_sda_bases,
|
||||
analysis::cfa::{locate_sda_bases, SectionAddress},
|
||||
obj::{
|
||||
ObjArchitecture, ObjInfo, ObjKind, ObjSection, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet,
|
||||
ObjSymbolFlags, ObjSymbolKind,
|
||||
|
@ -406,8 +406,15 @@ pub fn process_dol<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
|||
|
||||
for entry in &eti_entries {
|
||||
// Add functions from extabindex entries as known function bounds
|
||||
if let Some(old_value) = obj.known_functions.insert(entry.function, entry.function_size)
|
||||
{
|
||||
let (section_index, _) = obj.sections.at_address(entry.function).map_err(|_| {
|
||||
anyhow!(
|
||||
"Failed to locate section for function {:#010X} (referenced from extabindex entry {:#010X})",
|
||||
entry.function,
|
||||
entry.address,
|
||||
)
|
||||
})?;
|
||||
let addr = SectionAddress::new(section_index, entry.function);
|
||||
if let Some(old_value) = obj.known_functions.insert(addr, entry.function_size) {
|
||||
if old_value != entry.function_size {
|
||||
log::warn!(
|
||||
"Conflicting sizes for {:#010X}: {:#X} != {:#X}",
|
||||
|
|
|
@ -304,6 +304,8 @@ pub fn process_elf<P: AsRef<Path>>(path: P) -> Result<ObjInfo> {
|
|||
align: None,
|
||||
common: false, // TODO
|
||||
autogenerated: false,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -483,13 +485,19 @@ pub fn write_elf(obj: &ObjInfo) -> Result<Vec<u8>> {
|
|||
}
|
||||
}
|
||||
|
||||
// Add symbols
|
||||
for (symbol, symbol_map) in obj.symbols.iter().zip(&mut symbol_map) {
|
||||
// Add symbols, starting with local symbols
|
||||
for (symbol_index, symbol) in obj
|
||||
.symbols
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|&(_, s)| s.flags.is_local())
|
||||
.chain(obj.symbols.iter().enumerate().filter(|&(_, s)| !s.flags.is_local()))
|
||||
{
|
||||
if obj.kind == ObjKind::Relocatable && symbol.kind == ObjSymbolKind::Section {
|
||||
// We wrote section symbols above, so skip them here
|
||||
let section_index =
|
||||
symbol.section.ok_or_else(|| anyhow!("section symbol without section index"))?;
|
||||
*symbol_map = Some(section_symbol_offset + section_index as u32);
|
||||
symbol_map[symbol_index] = Some(section_symbol_offset + section_index as u32);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -536,9 +544,9 @@ pub fn write_elf(obj: &ObjInfo) -> Result<Vec<u8>> {
|
|||
num_local = writer.symbol_count();
|
||||
}
|
||||
out_symbols.push(OutSymbol { index, sym });
|
||||
*symbol_map = Some(index.0);
|
||||
symbol_map[symbol_index] = Some(index.0);
|
||||
if let Some(comment_data) = &mut comment_data {
|
||||
write_comment_sym(comment_data, CommentSym::from(symbol))?;
|
||||
write_comment_sym(comment_data, CommentSym::from(symbol, true))?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -807,14 +815,8 @@ fn to_obj_symbol(
|
|||
})
|
||||
}
|
||||
|
||||
fn to_obj_reloc(
|
||||
obj_file: &object::File<'_>,
|
||||
symbol_indexes: &[Option<usize>],
|
||||
section_data: &[u8],
|
||||
address: u64,
|
||||
reloc: Relocation,
|
||||
) -> Result<Option<ObjReloc>> {
|
||||
let reloc_kind = match reloc.kind() {
|
||||
pub fn to_obj_reloc_kind(kind: RelocationKind) -> Result<ObjRelocKind> {
|
||||
Ok(match kind {
|
||||
RelocationKind::Absolute => ObjRelocKind::Absolute,
|
||||
RelocationKind::Elf(kind) => match kind {
|
||||
elf::R_PPC_ADDR16_LO => ObjRelocKind::PpcAddr16Lo,
|
||||
|
@ -823,10 +825,20 @@ fn to_obj_reloc(
|
|||
elf::R_PPC_REL24 => ObjRelocKind::PpcRel24,
|
||||
elf::R_PPC_REL14 => ObjRelocKind::PpcRel14,
|
||||
elf::R_PPC_EMB_SDA21 => ObjRelocKind::PpcEmbSda21,
|
||||
_ => bail!("Unhandled PPC relocation type: {kind}"),
|
||||
_ => bail!("Unhandled ELF relocation type: {kind}"),
|
||||
},
|
||||
_ => bail!("Unhandled relocation type: {:?}", reloc.kind()),
|
||||
};
|
||||
_ => bail!("Unhandled relocation type: {:?}", kind),
|
||||
})
|
||||
}
|
||||
|
||||
fn to_obj_reloc(
|
||||
obj_file: &object::File<'_>,
|
||||
symbol_indexes: &[Option<usize>],
|
||||
section_data: &[u8],
|
||||
address: u64,
|
||||
reloc: Relocation,
|
||||
) -> Result<Option<ObjReloc>> {
|
||||
let reloc_kind = to_obj_reloc_kind(reloc.kind())?;
|
||||
let symbol = match reloc.target() {
|
||||
RelocationTarget::Symbol(idx) => {
|
||||
obj_file.symbol_by_index(idx).context("Failed to locate relocation target symbol")?
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
fs::{DirBuilder, File, OpenOptions},
|
||||
io::{BufRead, BufReader, BufWriter, Cursor, Read},
|
||||
path::{Path, PathBuf},
|
||||
|
@ -10,7 +11,10 @@ use filetime::{set_file_mtime, FileTime};
|
|||
use memmap2::{Mmap, MmapOptions};
|
||||
use path_slash::PathBufExt;
|
||||
|
||||
use crate::util::{rarc, rarc::Node, yaz0};
|
||||
use crate::{
|
||||
cmd::shasum::file_sha1,
|
||||
util::{rarc, rarc::Node, yaz0, IntoCow, ToCow},
|
||||
};
|
||||
|
||||
/// Opens a memory mapped file.
|
||||
pub fn map_file<P: AsRef<Path>>(path: P) -> Result<Mmap> {
|
||||
|
@ -25,7 +29,7 @@ pub type Reader<'a> = Cursor<&'a [u8]>;
|
|||
|
||||
/// Creates a reader for the memory mapped file.
|
||||
#[inline]
|
||||
pub fn map_reader(mmap: &Mmap) -> Reader { Cursor::new(&**mmap) }
|
||||
pub fn map_reader(mmap: &Mmap) -> Reader { Reader::new(&**mmap) }
|
||||
|
||||
/// Creates a buffered reader around a file (not memory mapped).
|
||||
pub fn buf_reader<P: AsRef<Path>>(path: P) -> Result<BufReader<File>> {
|
||||
|
@ -130,14 +134,6 @@ impl RarcIterator {
|
|||
}
|
||||
paths
|
||||
}
|
||||
|
||||
fn decompress_if_needed(buf: &[u8]) -> Result<Vec<u8>> {
|
||||
if buf.len() > 4 && buf[0..4] == *b"Yaz0" {
|
||||
yaz0::decompress_file(&mut Cursor::new(buf))
|
||||
} else {
|
||||
Ok(buf.to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for RarcIterator {
|
||||
|
@ -152,8 +148,8 @@ impl Iterator for RarcIterator {
|
|||
self.index += 1;
|
||||
|
||||
let slice = &self.file[off as usize..off as usize + size as usize];
|
||||
match Self::decompress_if_needed(slice) {
|
||||
Ok(buf) => Some(Ok((path, buf))),
|
||||
match decompress_if_needed(slice) {
|
||||
Ok(buf) => Some(Ok((path, buf.into_owned()))),
|
||||
Err(e) => Some(Err(e)),
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +166,7 @@ impl FileEntry {
|
|||
pub fn as_reader(&self) -> Reader {
|
||||
match self {
|
||||
Self::Map(map) => map_reader(map),
|
||||
Self::Buffer(slice) => Cursor::new(slice),
|
||||
Self::Buffer(slice) => Reader::new(slice),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,3 +253,29 @@ pub fn touch<P: AsRef<Path>>(path: P) -> std::io::Result<()> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn decompress_if_needed(buf: &[u8]) -> Result<Cow<[u8]>> {
|
||||
Ok(if buf.len() > 4 && buf[0..4] == *b"Yaz0" {
|
||||
yaz0::decompress_file(&mut Reader::new(buf))?.into_cow()
|
||||
} else {
|
||||
buf.to_cow()
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify_hash<P: AsRef<Path>>(path: P, hash_str: &str) -> Result<()> {
|
||||
let mut hash_bytes = [0u8; 20];
|
||||
hex::decode_to_slice(hash_str, &mut hash_bytes)
|
||||
.with_context(|| format!("Invalid SHA-1 '{hash_str}'"))?;
|
||||
let file = File::open(path.as_ref())
|
||||
.with_context(|| format!("Failed to open file '{}'", path.as_ref().display()))?;
|
||||
let found_hash = file_sha1(file)?;
|
||||
if found_hash.as_ref() == hash_bytes {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(anyhow!(
|
||||
"Hash mismatch: expected {}, but was {}",
|
||||
hex::encode(hash_bytes),
|
||||
hex::encode(found_hash)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,8 @@ pub fn generate_ldscript(obj: &ObjInfo, auto_force_files: bool) -> Result<String
|
|||
|
||||
let mut force_active = vec![];
|
||||
for symbol in obj.symbols.iter() {
|
||||
if symbol.flags.is_force_active() && symbol.flags.is_global() {
|
||||
if symbol.flags.is_force_active() && symbol.flags.is_global() && !symbol.flags.is_no_write()
|
||||
{
|
||||
force_active.push(symbol.name.clone());
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +94,8 @@ pub fn generate_ldscript_partial(obj: &ObjInfo, auto_force_files: bool) -> Resul
|
|||
|
||||
let mut force_active = vec![];
|
||||
for symbol in obj.symbols.iter() {
|
||||
if symbol.flags.is_force_active() && symbol.flags.is_global() {
|
||||
if symbol.flags.is_force_active() && symbol.flags.is_global() && !symbol.flags.is_no_write()
|
||||
{
|
||||
force_active.push(symbol.name.clone());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -565,7 +565,6 @@ pub fn apply_map_file<P: AsRef<Path>>(path: P, obj: &mut ObjInfo) -> Result<()>
|
|||
|
||||
pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
|
||||
for (section_index, section) in obj.sections.iter_mut() {
|
||||
log::info!("Section {}: {} ({:?})", section_index, section.name, result.sections);
|
||||
let opt = if obj.kind == ObjKind::Executable {
|
||||
result.sections.iter().find(|s| s.address == section.address as u32)
|
||||
} else {
|
||||
|
@ -628,6 +627,8 @@ pub fn apply_map(result: &MapInfo, obj: &mut ObjInfo) -> Result<()> {
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: false,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::{borrow::Cow, ops::Deref};
|
||||
|
||||
pub mod asm;
|
||||
pub mod comment;
|
||||
pub mod config;
|
||||
|
@ -39,3 +41,29 @@ macro_rules! array_ref_mut {
|
|||
to_array_mut(&mut $slice[$offset..$offset + $size])
|
||||
}};
|
||||
}
|
||||
|
||||
pub trait IntoCow<'a, B>
|
||||
where B: ToOwned + ?Sized
|
||||
{
|
||||
fn into_cow(self) -> Cow<'a, B>;
|
||||
}
|
||||
|
||||
pub trait ToCow<'a, B>
|
||||
where B: ToOwned + ?Sized
|
||||
{
|
||||
fn to_cow(&'a self) -> Cow<'a, B>;
|
||||
}
|
||||
|
||||
impl<'a, O> IntoCow<'a, <O as Deref>::Target> for O
|
||||
where
|
||||
O: Deref + Clone + 'a,
|
||||
<O as Deref>::Target: ToOwned<Owned = O>,
|
||||
{
|
||||
fn into_cow(self) -> Cow<'a, <O as Deref>::Target> { Cow::Owned(self) }
|
||||
}
|
||||
|
||||
impl<'a, B> ToCow<'a, B> for B
|
||||
where B: ToOwned + ?Sized
|
||||
{
|
||||
fn to_cow(&'a self) -> Cow<'a, B> { Cow::Borrowed(self) }
|
||||
}
|
||||
|
|
588
src/util/rel.rs
588
src/util/rel.rs
|
@ -1,15 +1,21 @@
|
|||
use std::io::Read;
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
io::{Read, Seek, Write},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use byteorder::{BigEndian, ReadBytesExt};
|
||||
use object::elf;
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use binrw::{binrw, io::NoSeek, BinRead, BinWrite};
|
||||
use itertools::Itertools;
|
||||
use object::{elf, Object, ObjectSection, ObjectSymbol};
|
||||
use tracing::warn;
|
||||
|
||||
use crate::{
|
||||
array_ref_mut,
|
||||
obj::{
|
||||
ObjArchitecture, ObjInfo, ObjKind, ObjRelocKind, ObjSection, ObjSectionKind, ObjSymbol,
|
||||
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
|
||||
},
|
||||
util::file::Reader,
|
||||
util::{file::Reader, IntoCow},
|
||||
};
|
||||
|
||||
/// Do not relocate anything, but accumulate the offset field for the next relocation offset calculation.
|
||||
|
@ -24,48 +30,130 @@ pub const R_DOLPHIN_END: u32 = 203;
|
|||
#[allow(unused)]
|
||||
pub const R_DOLPHIN_MRKREF: u32 = 204;
|
||||
|
||||
pub fn process_rel(mut reader: Reader) -> Result<ObjInfo> {
|
||||
let module_id = reader.read_u32::<BigEndian>()?;
|
||||
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'next' to be 0");
|
||||
ensure!(reader.read_u32::<BigEndian>()? == 0, "Expected 'prev' to be 0");
|
||||
let num_sections = reader.read_u32::<BigEndian>()?;
|
||||
let section_info_offset = reader.read_u32::<BigEndian>()?;
|
||||
let _name_offset = reader.read_u32::<BigEndian>()?;
|
||||
let _name_size = reader.read_u32::<BigEndian>()?;
|
||||
let version = reader.read_u32::<BigEndian>()?;
|
||||
ensure!(matches!(version, 1..=3), "Unsupported REL version {}", version);
|
||||
let bss_size = reader.read_u32::<BigEndian>()?;
|
||||
let rel_offset = reader.read_u32::<BigEndian>()?;
|
||||
let imp_offset = reader.read_u32::<BigEndian>()?;
|
||||
let imp_size = reader.read_u32::<BigEndian>()?;
|
||||
let prolog_section = reader.read_u8()?;
|
||||
let epilog_section = reader.read_u8()?;
|
||||
let unresolved_section = reader.read_u8()?;
|
||||
ensure!(reader.read_u8()? == 0, "Expected 'bssSection' to be 0");
|
||||
let prolog_offset = reader.read_u32::<BigEndian>()?;
|
||||
let epilog_offset = reader.read_u32::<BigEndian>()?;
|
||||
let unresolved_offset = reader.read_u32::<BigEndian>()?;
|
||||
let (align, bss_align) = if version >= 2 {
|
||||
let align = reader.read_u32::<BigEndian>()?;
|
||||
let bss_align = reader.read_u32::<BigEndian>()?;
|
||||
(Some(align), Some(bss_align))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
let fix_size = if version >= 3 { Some(reader.read_u32::<BigEndian>()?) } else { None };
|
||||
#[binrw]
|
||||
#[derive(Clone, Debug)]
|
||||
#[br(assert(next == 0))]
|
||||
#[br(assert(prev == 0))]
|
||||
#[br(assert(bss_section == 0))]
|
||||
#[brw(assert(matches!(version, 1..=3), "Unsupported REL version {version}"))]
|
||||
pub struct RelHeader {
|
||||
/// Arbitrary identification number.
|
||||
/// Must be unique amongst all RELs used by a game.
|
||||
/// 0 is reserved for the DOL.
|
||||
pub module_id: u32,
|
||||
/// Pointer to next module.
|
||||
/// Filled at runtime.
|
||||
#[bw(calc = 0)]
|
||||
pub next: u32,
|
||||
/// Pointer to previous module.
|
||||
/// Filled at runtime.
|
||||
#[bw(calc = 0)]
|
||||
pub prev: u32,
|
||||
/// Number of sections in the file.
|
||||
pub num_sections: u32,
|
||||
/// Offset to the start of the section table.
|
||||
pub section_info_offset: u32,
|
||||
/// Offset in the external module name string table file.
|
||||
pub name_offset: u32,
|
||||
/// Size of the module name in bytes.
|
||||
pub name_size: u32,
|
||||
/// Version number of the REL file format.
|
||||
pub version: u32,
|
||||
/// Size of the `.bss` section.
|
||||
pub bss_size: u32,
|
||||
/// Offset to the start of the relocation table.
|
||||
pub rel_offset: u32,
|
||||
/// Offset to the start of the import table.
|
||||
pub imp_offset: u32,
|
||||
/// Size of the import table.
|
||||
pub imp_size: u32,
|
||||
/// Section containing the `_prolog` function.
|
||||
pub prolog_section: u8,
|
||||
/// Section containing the `_epilog` function.
|
||||
pub epilog_section: u8,
|
||||
/// Section containing the `_unresolved` function.
|
||||
pub unresolved_section: u8,
|
||||
/// Index into section table which bss is relative to.
|
||||
/// Filled at runtime.
|
||||
#[bw(calc = 0)]
|
||||
pub bss_section: u8,
|
||||
/// Offset into the section containing `_prolog`.
|
||||
pub prolog_offset: u32,
|
||||
/// Offset into the section containing `_epilog`.
|
||||
pub epilog_offset: u32,
|
||||
/// Offset into the section containing `_unresolved`.
|
||||
pub unresolved_offset: u32,
|
||||
/// (Version >= 2 only)
|
||||
/// Alignment constraint on all sections.
|
||||
#[br(if(version >= 2))]
|
||||
#[bw(if(*version >= 2))]
|
||||
pub align: Option<u32>,
|
||||
/// (Version >= 2 only)
|
||||
/// Alignment constraint on the `.bss` section.
|
||||
#[br(if(version >= 2))]
|
||||
#[bw(if(*version >= 2))]
|
||||
pub bss_align: Option<u32>,
|
||||
/// (Version >= 3 only)
|
||||
/// If REL is linked with `OSLinkFixed` (instead of `OSLink`), the
|
||||
/// space after this offset can be used for other purposes, like BSS.
|
||||
#[br(if(version >= 3))]
|
||||
#[bw(if(*version >= 3))]
|
||||
pub fix_size: Option<u32>,
|
||||
}
|
||||
|
||||
let mut sections = Vec::with_capacity(num_sections as usize);
|
||||
reader.set_position(section_info_offset as u64);
|
||||
#[binrw]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct RelImport {
|
||||
module_id: u32,
|
||||
offset: u32,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct RelSectionHeader {
|
||||
offset_and_flags: u32,
|
||||
size: u32,
|
||||
}
|
||||
|
||||
impl RelSectionHeader {
|
||||
fn new(offset: u32, size: u32, exec: bool) -> Self {
|
||||
Self { offset_and_flags: offset | (exec as u32), size }
|
||||
}
|
||||
|
||||
fn offset(&self) -> u32 { self.offset_and_flags & !1 }
|
||||
|
||||
fn size(&self) -> u32 { self.size }
|
||||
|
||||
fn exec(&self) -> bool { self.offset_and_flags & 1 != 0 }
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct RelRelocRaw {
|
||||
offset: u16,
|
||||
kind: u8,
|
||||
section: u8,
|
||||
addend: u32,
|
||||
}
|
||||
|
||||
pub fn process_rel_header(reader: &mut Reader) -> Result<RelHeader> {
|
||||
RelHeader::read_be(reader).context("Failed to read REL header")
|
||||
}
|
||||
|
||||
pub fn process_rel(reader: &mut Reader) -> Result<(RelHeader, ObjInfo)> {
|
||||
let header = process_rel_header(reader)?;
|
||||
let mut sections = Vec::with_capacity(header.num_sections as usize);
|
||||
reader.set_position(header.section_info_offset as u64);
|
||||
let mut found_text = false;
|
||||
let mut total_bss_size = 0;
|
||||
for idx in 0..num_sections {
|
||||
let offset = reader.read_u32::<BigEndian>()?;
|
||||
let size = reader.read_u32::<BigEndian>()?;
|
||||
for idx in 0..header.num_sections {
|
||||
let section = RelSectionHeader::read_be(reader)
|
||||
.with_context(|| format!("Failed to read REL section header {}", idx))?;
|
||||
let offset = section.offset();
|
||||
let size = section.size();
|
||||
if size == 0 {
|
||||
continue;
|
||||
}
|
||||
let exec = (offset & 1) == 1;
|
||||
let offset = offset & !3;
|
||||
|
||||
let data = if offset == 0 {
|
||||
vec![]
|
||||
|
@ -73,18 +161,18 @@ pub fn process_rel(mut reader: Reader) -> Result<ObjInfo> {
|
|||
let position = reader.position();
|
||||
reader.set_position(offset as u64);
|
||||
let mut data = vec![0u8; size as usize];
|
||||
reader.read_exact(&mut data)?;
|
||||
reader.read_exact(&mut data).with_context(|| {
|
||||
format!("Failed to read REL section {} data with size {:#X}", idx, size)
|
||||
})?;
|
||||
reader.set_position(position);
|
||||
data
|
||||
};
|
||||
|
||||
// println!("Section {} offset {:#X} size {:#X}", idx, offset, size);
|
||||
|
||||
let (name, kind, section_known) = if offset == 0 {
|
||||
ensure!(total_bss_size == 0, "Multiple BSS sections in REL");
|
||||
total_bss_size = size;
|
||||
(".bss".to_string(), ObjSectionKind::Bss, true)
|
||||
} else if exec {
|
||||
} else if section.exec() {
|
||||
ensure!(!found_text, "Multiple text sections in REL");
|
||||
found_text = true;
|
||||
(".text".to_string(), ObjSectionKind::Code, true)
|
||||
|
@ -98,8 +186,8 @@ pub fn process_rel(mut reader: Reader) -> Result<ObjInfo> {
|
|||
size: size as u64,
|
||||
data,
|
||||
align: match offset {
|
||||
0 => bss_align,
|
||||
_ => align,
|
||||
0 => header.bss_align,
|
||||
_ => header.align,
|
||||
}
|
||||
.unwrap_or_default() as u64,
|
||||
elf_index: idx as usize,
|
||||
|
@ -111,10 +199,10 @@ pub fn process_rel(mut reader: Reader) -> Result<ObjInfo> {
|
|||
});
|
||||
}
|
||||
ensure!(
|
||||
total_bss_size == bss_size,
|
||||
total_bss_size == header.bss_size,
|
||||
"Mismatched BSS size: {:#X} != {:#X}",
|
||||
total_bss_size,
|
||||
bss_size
|
||||
header.bss_size
|
||||
);
|
||||
|
||||
let mut symbols = Vec::new();
|
||||
|
@ -141,49 +229,45 @@ pub fn process_rel(mut reader: Reader) -> Result<ObjInfo> {
|
|||
}
|
||||
Ok(())
|
||||
};
|
||||
add_symbol(prolog_section, prolog_offset, "_prolog")?;
|
||||
add_symbol(epilog_section, epilog_offset, "_epilog")?;
|
||||
add_symbol(unresolved_section, unresolved_offset, "_unresolved")?;
|
||||
add_symbol(header.prolog_section, header.prolog_offset, "_prolog")?;
|
||||
add_symbol(header.epilog_section, header.epilog_offset, "_epilog")?;
|
||||
add_symbol(header.unresolved_section, header.unresolved_offset, "_unresolved")?;
|
||||
|
||||
let mut unresolved_relocations = Vec::new();
|
||||
let mut imp_idx = 0;
|
||||
let imp_end = (imp_offset + imp_size) as u64;
|
||||
reader.set_position(imp_offset as u64);
|
||||
let imp_end = (header.imp_offset + header.imp_size) as u64;
|
||||
reader.set_position(header.imp_offset as u64);
|
||||
while reader.position() < imp_end {
|
||||
let reloc_module_id = reader.read_u32::<BigEndian>()?;
|
||||
let reloc_offset = reader.read_u32::<BigEndian>()?;
|
||||
let import = RelImport::read_be(reader)?;
|
||||
|
||||
if imp_idx == 0 {
|
||||
ensure!(
|
||||
reloc_offset == rel_offset,
|
||||
import.offset == header.rel_offset,
|
||||
"imp index 0 offset mismatch: {:#X} != {:#X}",
|
||||
reloc_offset,
|
||||
rel_offset
|
||||
import.offset,
|
||||
header.rel_offset
|
||||
);
|
||||
}
|
||||
imp_idx += 1;
|
||||
|
||||
if reloc_module_id == module_id {
|
||||
if let Some(fix_size) = fix_size {
|
||||
if import.module_id == header.module_id {
|
||||
if let Some(fix_size) = header.fix_size {
|
||||
ensure!(
|
||||
fix_size == reloc_offset,
|
||||
fix_size == import.offset,
|
||||
"fix_size mismatch: {:#X} != {:#X}",
|
||||
fix_size,
|
||||
reloc_offset
|
||||
import.offset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let position = reader.position();
|
||||
reader.set_position(reloc_offset as u64);
|
||||
reader.set_position(import.offset as u64);
|
||||
let mut address = 0u32;
|
||||
let mut section = u8::MAX;
|
||||
loop {
|
||||
let offset = reader.read_u16::<BigEndian>()?;
|
||||
let type_id = reader.read_u8()? as u32;
|
||||
let target_section = reader.read_u8()?;
|
||||
let addend = reader.read_u32::<BigEndian>()?;
|
||||
let kind = match type_id {
|
||||
let reloc = RelRelocRaw::read_be(reader)?;
|
||||
let kind = match reloc.kind as u32 {
|
||||
elf::R_PPC_NONE => continue,
|
||||
elf::R_PPC_ADDR32 | elf::R_PPC_UADDR32 => ObjRelocKind::Absolute,
|
||||
// elf::R_PPC_ADDR24 => ObjRelocKind::PpcAddr24,
|
||||
|
@ -199,36 +283,33 @@ pub fn process_rel(mut reader: Reader) -> Result<ObjInfo> {
|
|||
// elf::R_PPC_REL14_BRTAKEN => ObjRelocKind::PpcRel14BrTaken,
|
||||
// elf::R_PPC_REL14_BRNTAKEN => ObjRelocKind::PpcRel14BrnTaken,
|
||||
R_DOLPHIN_NOP => {
|
||||
address += offset as u32;
|
||||
address += reloc.offset as u32;
|
||||
continue;
|
||||
}
|
||||
R_DOLPHIN_SECTION => {
|
||||
address = 0;
|
||||
section = target_section;
|
||||
section = reloc.section;
|
||||
continue;
|
||||
}
|
||||
R_DOLPHIN_END => break,
|
||||
// R_DOLPHIN_MRKREF => ?
|
||||
reloc_type => bail!("Unhandled REL relocation type {reloc_type}"),
|
||||
};
|
||||
address += offset as u32;
|
||||
unresolved_relocations.push(RelReloc {
|
||||
address += reloc.offset as u32;
|
||||
let reloc = RelReloc {
|
||||
kind,
|
||||
section,
|
||||
address: address & !3,
|
||||
module_id: reloc_module_id,
|
||||
target_section,
|
||||
addend,
|
||||
});
|
||||
module_id: import.module_id,
|
||||
target_section: reloc.section,
|
||||
addend: reloc.addend,
|
||||
};
|
||||
unresolved_relocations.push(reloc);
|
||||
}
|
||||
reader.set_position(position);
|
||||
}
|
||||
|
||||
// let name = match name_offset {
|
||||
// 0 => String::new(),
|
||||
// _ => read_string(&mut reader, name_offset as u64, name_size as usize).unwrap_or_default(),
|
||||
// };
|
||||
log::debug!("Read REL ID {module_id}");
|
||||
log::debug!("Read REL ID {}", header.module_id);
|
||||
let mut obj = ObjInfo::new(
|
||||
ObjKind::Relocatable,
|
||||
ObjArchitecture::PowerPc,
|
||||
|
@ -236,9 +317,9 @@ pub fn process_rel(mut reader: Reader) -> Result<ObjInfo> {
|
|||
symbols,
|
||||
sections,
|
||||
);
|
||||
obj.module_id = module_id;
|
||||
obj.module_id = header.module_id;
|
||||
obj.unresolved_relocations = unresolved_relocations;
|
||||
Ok(obj)
|
||||
Ok((header, obj))
|
||||
}
|
||||
|
||||
/// REL relocation.
|
||||
|
@ -258,3 +339,342 @@ pub struct RelReloc {
|
|||
/// If target module ID is 0 (DOL), this is an absolute address.
|
||||
pub addend: u32,
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn reloc_can_be_applied(_module_id: u32, rel_reloc: &RelReloc) -> bool {
|
||||
matches!(rel_reloc.kind, ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn skip_reloc(module_id: u32, rel_reloc: &RelReloc) -> bool {
|
||||
rel_reloc.module_id == module_id
|
||||
&& rel_reloc.section == rel_reloc.target_section
|
||||
&& matches!(rel_reloc.kind, ObjRelocKind::PpcRel24 | ObjRelocKind::PpcRel14)
|
||||
}
|
||||
|
||||
fn apply_relocation(
|
||||
data: &mut [u8],
|
||||
module_id: u32,
|
||||
rel_reloc: &RelReloc,
|
||||
unresolved: u32,
|
||||
) -> Result<()> {
|
||||
let diff = if rel_reloc.module_id == module_id && rel_reloc.section == rel_reloc.target_section
|
||||
{
|
||||
rel_reloc.addend as i32 - rel_reloc.address as i32
|
||||
} else {
|
||||
unresolved as i32 - rel_reloc.address as i32
|
||||
};
|
||||
let ins_ref = array_ref_mut!(data, rel_reloc.address as usize, 4);
|
||||
let mut ins = u32::from_be_bytes(*ins_ref);
|
||||
match rel_reloc.kind {
|
||||
ObjRelocKind::PpcRel24 => {
|
||||
ensure!((-0x2000000..0x2000000).contains(&diff), "R_PPC_REL24 relocation out of range");
|
||||
ins = (ins & !0x3fffffc) | (diff as u32 & 0x3fffffc);
|
||||
}
|
||||
ObjRelocKind::PpcRel14 => {
|
||||
ensure!((-0x2000..0x2000).contains(&diff), "R_PPC_REL14 relocation out of range");
|
||||
ins = (ins & !0xfffc) | (diff as u32 & 0xfffc);
|
||||
}
|
||||
kind => bail!("Unsupported relocation kind {:?}", kind),
|
||||
}
|
||||
*ins_ref = ins.to_be_bytes();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct RelWriteInfo {
|
||||
/// REL module ID.
|
||||
pub module_id: u32,
|
||||
/// REL version.
|
||||
pub version: u32,
|
||||
/// Override `name_offset` in the REL header.
|
||||
/// Useful for matching RELs without the original string table.
|
||||
pub name_offset: Option<u32>,
|
||||
/// Override `name_size` in the REL header.
|
||||
/// Useful for matching RELs without the original string table.
|
||||
pub name_size: Option<u32>,
|
||||
/// Override `align` in the REL header.
|
||||
pub align: Option<u32>,
|
||||
/// Override `bss_align` in the REL header.
|
||||
pub bss_align: Option<u32>,
|
||||
/// Override the number of sections in the file.
|
||||
/// Useful for matching RELs that included debug sections.
|
||||
pub section_count: Option<usize>,
|
||||
}
|
||||
|
||||
const PERMITTED_SECTIONS: [&str; 7] =
|
||||
[".init", ".text", ".ctors", ".dtors", ".rodata", ".data", ".bss"];
|
||||
|
||||
pub fn should_write_section(section: &object::Section) -> bool {
|
||||
matches!(section.name(), Ok(name) if PERMITTED_SECTIONS.contains(&name))
|
||||
&& section.kind() != object::SectionKind::UninitializedData
|
||||
}
|
||||
|
||||
pub fn write_rel<W: Write>(
|
||||
w: &mut W,
|
||||
info: &RelWriteInfo,
|
||||
file: &object::File,
|
||||
mut relocations: Vec<RelReloc>,
|
||||
) -> Result<()> {
|
||||
relocations.sort_by(|a, b| {
|
||||
if a.module_id == 0 {
|
||||
if b.module_id == 0 {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
} else if a.module_id == info.module_id {
|
||||
if b.module_id == 0 {
|
||||
Ordering::Less
|
||||
} else if b.module_id == info.module_id {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
Ordering::Greater
|
||||
}
|
||||
} else if b.module_id == 0 || b.module_id == info.module_id {
|
||||
Ordering::Less
|
||||
} else {
|
||||
a.module_id.cmp(&b.module_id)
|
||||
}
|
||||
.then(a.section.cmp(&b.section))
|
||||
.then(a.address.cmp(&b.address))
|
||||
});
|
||||
|
||||
let mut apply_relocations = vec![];
|
||||
relocations.retain(|r| {
|
||||
if !should_write_section(
|
||||
&file.section_by_index(object::SectionIndex(r.section as usize)).unwrap(),
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if reloc_can_be_applied(info.module_id, r) {
|
||||
apply_relocations.push(r.clone());
|
||||
!skip_reloc(info.module_id, r)
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
|
||||
let mut align =
|
||||
file.sections().filter(should_write_section).map(|s| s.align() as u32).max().unwrap_or(0);
|
||||
let bss = file.sections().find(|s| s.name() == Ok(".bss"));
|
||||
let mut bss_align = bss.as_ref().map(|s| s.align() as u32).unwrap_or(1);
|
||||
let mut num_sections = file.sections().count() as u32;
|
||||
|
||||
// Apply overrides
|
||||
if let Some(section_count) = info.section_count {
|
||||
if section_count != num_sections as usize {
|
||||
warn!(from = num_sections, to = section_count, "Overriding section count");
|
||||
}
|
||||
num_sections = section_count as u32;
|
||||
}
|
||||
if info.version >= 2 {
|
||||
if let Some(align_override) = info.align {
|
||||
if align_override != align {
|
||||
warn!(from = align, to = align_override, "Overriding alignment");
|
||||
}
|
||||
align = align_override;
|
||||
}
|
||||
if let Some(bss_align_override) = info.bss_align {
|
||||
if bss_align_override != bss_align {
|
||||
warn!(from = bss_align, to = bss_align_override, "Overriding BSS alignment");
|
||||
}
|
||||
bss_align = bss_align_override;
|
||||
}
|
||||
}
|
||||
|
||||
let mut header = RelHeader {
|
||||
module_id: info.module_id,
|
||||
num_sections,
|
||||
section_info_offset: match info.version {
|
||||
1 => 0x40,
|
||||
2 => 0x48,
|
||||
3 => 0x4C,
|
||||
_ => bail!("Unsupported REL version {}", info.version),
|
||||
},
|
||||
name_offset: info.name_offset.unwrap_or(0),
|
||||
name_size: info.name_size.unwrap_or(0),
|
||||
version: info.version,
|
||||
bss_size: bss.as_ref().map(|s| s.size() as u32).unwrap_or(0),
|
||||
rel_offset: 0,
|
||||
imp_offset: 0,
|
||||
imp_size: 0,
|
||||
prolog_section: 0,
|
||||
epilog_section: 0,
|
||||
unresolved_section: 0,
|
||||
prolog_offset: 0,
|
||||
epilog_offset: 0,
|
||||
unresolved_offset: 0,
|
||||
align: if info.version >= 2 { Some(align) } else { None },
|
||||
bss_align: if info.version >= 2 { Some(bss_align) } else { None },
|
||||
fix_size: None,
|
||||
};
|
||||
let mut offset = header.section_info_offset;
|
||||
offset += num_sections * 8;
|
||||
let section_data_offset = offset;
|
||||
for section in file.sections().filter(should_write_section) {
|
||||
let align = section.align() as u32 - 1;
|
||||
offset = (offset + align) & !align;
|
||||
offset += section.size() as u32;
|
||||
}
|
||||
header.imp_offset = offset;
|
||||
let imp_count = relocations.iter().map(|r| r.module_id).dedup().count();
|
||||
header.imp_size = imp_count as u32 * 8;
|
||||
offset += header.imp_size;
|
||||
header.rel_offset = offset;
|
||||
|
||||
let mut imp_entries = Vec::<RelImport>::with_capacity(imp_count);
|
||||
let mut raw_relocations = vec![];
|
||||
{
|
||||
let mut address = 0u32;
|
||||
let mut section = u8::MAX;
|
||||
let mut last_module_id = u32::MAX;
|
||||
for reloc in &relocations {
|
||||
if reloc.module_id != last_module_id {
|
||||
if last_module_id != u32::MAX {
|
||||
raw_relocations.push(RelRelocRaw {
|
||||
offset: 0,
|
||||
kind: R_DOLPHIN_END as u8,
|
||||
section: 0,
|
||||
addend: 0,
|
||||
});
|
||||
offset += 8;
|
||||
}
|
||||
imp_entries.push(RelImport { module_id: reloc.module_id, offset });
|
||||
section = u8::MAX;
|
||||
last_module_id = reloc.module_id;
|
||||
}
|
||||
if info.version >= 3
|
||||
&& header.fix_size.is_none()
|
||||
&& (reloc.module_id == 0 || reloc.module_id == info.module_id)
|
||||
{
|
||||
header.fix_size = Some(offset);
|
||||
}
|
||||
if reloc.section != section {
|
||||
raw_relocations.push(RelRelocRaw {
|
||||
offset: 0,
|
||||
kind: R_DOLPHIN_SECTION as u8,
|
||||
section: reloc.section,
|
||||
addend: 0,
|
||||
});
|
||||
offset += 8;
|
||||
address = 0;
|
||||
section = reloc.section;
|
||||
}
|
||||
let mut reloc_offset = reloc.address - address;
|
||||
while reloc_offset > 0xffff {
|
||||
raw_relocations.push(RelRelocRaw {
|
||||
offset: 0xffff,
|
||||
kind: R_DOLPHIN_NOP as u8,
|
||||
section: 0,
|
||||
addend: 0,
|
||||
});
|
||||
offset += 8;
|
||||
reloc_offset -= 0xffff;
|
||||
}
|
||||
raw_relocations.push(RelRelocRaw {
|
||||
offset: reloc_offset as u16,
|
||||
kind: match reloc.kind {
|
||||
ObjRelocKind::Absolute => elf::R_PPC_ADDR32,
|
||||
ObjRelocKind::PpcAddr16Lo => elf::R_PPC_ADDR16_LO,
|
||||
ObjRelocKind::PpcAddr16Hi => elf::R_PPC_ADDR16_HI,
|
||||
ObjRelocKind::PpcAddr16Ha => elf::R_PPC_ADDR16_HA,
|
||||
ObjRelocKind::PpcRel24 => elf::R_PPC_REL24,
|
||||
ObjRelocKind::PpcRel14 => elf::R_PPC_REL14,
|
||||
_ => bail!("Unsupported relocation kind {:?}", reloc.kind),
|
||||
} as u8,
|
||||
section: reloc.target_section,
|
||||
addend: reloc.addend,
|
||||
});
|
||||
address = reloc.address;
|
||||
offset += 8;
|
||||
}
|
||||
}
|
||||
raw_relocations.push(RelRelocRaw {
|
||||
offset: 0,
|
||||
kind: R_DOLPHIN_END as u8,
|
||||
section: 0,
|
||||
addend: 0,
|
||||
});
|
||||
offset += 8;
|
||||
|
||||
for symbol in file.symbols().filter(|s| s.is_definition()) {
|
||||
let Some(symbol_section) = symbol.section_index() else {
|
||||
continue;
|
||||
};
|
||||
match symbol.name() {
|
||||
Ok("_prolog") => {
|
||||
header.prolog_section = symbol_section.0 as u8;
|
||||
header.prolog_offset = symbol.address() as u32;
|
||||
}
|
||||
Ok("_epilog") => {
|
||||
header.epilog_section = symbol_section.0 as u8;
|
||||
header.epilog_offset = symbol.address() as u32;
|
||||
}
|
||||
Ok("_unresolved") => {
|
||||
header.unresolved_section = symbol_section.0 as u8;
|
||||
header.unresolved_offset = symbol.address() as u32;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let mut w = NoSeek::new(w);
|
||||
header.write_be(&mut w)?;
|
||||
ensure!(w.stream_position()? as u32 == header.section_info_offset);
|
||||
let mut current_data_offset = section_data_offset;
|
||||
for section_index in 0..num_sections {
|
||||
let Ok(section) = file.section_by_index(object::SectionIndex(section_index as usize))
|
||||
else {
|
||||
RelSectionHeader::new(0, 0, false).write_be(&mut w)?;
|
||||
continue;
|
||||
};
|
||||
if matches!(section.name(), Ok(name) if PERMITTED_SECTIONS.contains(&name)) {
|
||||
let mut offset = 0;
|
||||
if section.kind() != object::SectionKind::UninitializedData {
|
||||
let align = section.align() as u32 - 1;
|
||||
current_data_offset = (current_data_offset + align) & !align;
|
||||
offset = current_data_offset;
|
||||
current_data_offset += section.size() as u32;
|
||||
}
|
||||
RelSectionHeader::new(
|
||||
offset,
|
||||
section.size() as u32,
|
||||
section.kind() == object::SectionKind::Text,
|
||||
)
|
||||
.write_be(&mut w)?;
|
||||
} else {
|
||||
RelSectionHeader::new(0, 0, false).write_be(&mut w)?;
|
||||
}
|
||||
}
|
||||
ensure!(w.stream_position()? as u32 == section_data_offset);
|
||||
for section in file.sections().filter(should_write_section) {
|
||||
fn calculate_padding(position: u64, align: u64) -> u64 {
|
||||
let align = align - 1;
|
||||
((position + align) & !align) - position
|
||||
}
|
||||
let position = w.stream_position()?;
|
||||
w.write_all(&vec![0; calculate_padding(position, section.align()) as usize])?;
|
||||
|
||||
let section_index = section.index().0 as u8;
|
||||
let mut section_data = section.uncompressed_data()?;
|
||||
if apply_relocations.iter().any(|r| r.section == section_index) {
|
||||
let mut data = section_data.into_owned();
|
||||
for reloc in apply_relocations.iter().filter(|r| r.section == section_index) {
|
||||
apply_relocation(&mut data, info.module_id, reloc, header.unresolved_offset)?;
|
||||
}
|
||||
section_data = data.into_cow();
|
||||
}
|
||||
w.write_all(§ion_data)?;
|
||||
}
|
||||
ensure!(w.stream_position()? as u32 == header.imp_offset);
|
||||
for entry in imp_entries {
|
||||
entry.write_be(&mut w)?;
|
||||
}
|
||||
ensure!(w.stream_position()? as u32 == header.rel_offset);
|
||||
for reloc in raw_relocations {
|
||||
reloc.write_be(&mut w)?;
|
||||
}
|
||||
ensure!(w.stream_position()? as u32 == offset);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::{
|
||||
cmp::min,
|
||||
cmp::{min, Ordering},
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
};
|
||||
|
||||
|
@ -85,6 +85,8 @@ fn split_ctors_dtors(obj: &mut ObjInfo, start: SectionAddress, end: SectionAddre
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
}
|
||||
if function_split.is_none() {
|
||||
|
@ -95,6 +97,8 @@ fn split_ctors_dtors(obj: &mut ObjInfo, start: SectionAddress, end: SectionAddre
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +267,8 @@ fn split_extabindex(obj: &mut ObjInfo, start: SectionAddress) -> Result<()> {
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
}
|
||||
if extab_split.is_none() {
|
||||
|
@ -274,6 +280,8 @@ fn split_extabindex(obj: &mut ObjInfo, start: SectionAddress) -> Result<()> {
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
}
|
||||
if function_split.is_none() {
|
||||
|
@ -285,6 +293,8 @@ fn split_extabindex(obj: &mut ObjInfo, start: SectionAddress) -> Result<()> {
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -374,6 +384,8 @@ fn create_gap_splits(obj: &mut ObjInfo) -> Result<()> {
|
|||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
skip: false,
|
||||
rename: None,
|
||||
});
|
||||
current_address = new_split_end;
|
||||
continue;
|
||||
|
@ -485,6 +497,131 @@ fn validate_splits(obj: &ObjInfo) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Add padding symbols to fill in gaps between splits and symbols.
|
||||
fn add_padding_symbols(obj: &mut ObjInfo) -> Result<()> {
|
||||
for (section_index, section, addr, _split) in obj.sections.all_splits() {
|
||||
if section.name == ".ctors" || section.name == ".dtors" {
|
||||
continue;
|
||||
}
|
||||
|
||||
if obj
|
||||
.symbols
|
||||
.kind_at_section_address(section_index, addr, match section.kind {
|
||||
ObjSectionKind::Code => ObjSymbolKind::Function,
|
||||
ObjSectionKind::Data => ObjSymbolKind::Object,
|
||||
ObjSectionKind::ReadOnlyData => ObjSymbolKind::Object,
|
||||
ObjSectionKind::Bss => ObjSymbolKind::Object,
|
||||
})?
|
||||
.is_none()
|
||||
{
|
||||
let next_symbol_address = obj
|
||||
.symbols
|
||||
.for_section_range(section_index, addr + 1..)
|
||||
.find(|&(_, s)| s.size_known && s.size > 0)
|
||||
.map(|(_, s)| s.address)
|
||||
.unwrap_or(section.address + section.size);
|
||||
let symbol_name = format!(
|
||||
"pad_{:02}_{:08X}_{}",
|
||||
section_index,
|
||||
addr,
|
||||
section.name.trim_start_matches('.')
|
||||
);
|
||||
log::debug!("Adding padding symbol {} at {:#010X}", symbol_name, addr);
|
||||
obj.symbols.add_direct(ObjSymbol {
|
||||
name: symbol_name,
|
||||
demangled_name: None,
|
||||
address: addr as u64,
|
||||
section: Some(section_index),
|
||||
size: next_symbol_address - addr as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(
|
||||
ObjSymbolFlags::Local | ObjSymbolFlags::ForceActive | ObjSymbolFlags::NoWrite,
|
||||
),
|
||||
kind: match section.kind {
|
||||
ObjSectionKind::Code => ObjSymbolKind::Function,
|
||||
ObjSectionKind::Data | ObjSectionKind::ReadOnlyData | ObjSectionKind::Bss => {
|
||||
ObjSymbolKind::Object
|
||||
}
|
||||
},
|
||||
align: None,
|
||||
data_kind: Default::default(),
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
// Add padding symbols for gaps between symbols
|
||||
for (section_index, section) in obj.sections.iter() {
|
||||
if section.name == ".ctors" || section.name == ".dtors" {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut to_add = vec![];
|
||||
let mut iter = obj
|
||||
.symbols
|
||||
.for_section(section_index)
|
||||
.filter(|(_, s)| s.size_known && s.size > 0)
|
||||
.peekable();
|
||||
while let (Some((_, symbol)), Some(&(_, next_symbol))) = (iter.next(), iter.peek()) {
|
||||
let aligned_end =
|
||||
align_up((symbol.address + symbol.size) as u32, next_symbol.align.unwrap_or(1));
|
||||
match aligned_end.cmp(&(next_symbol.address as u32)) {
|
||||
Ordering::Less => {
|
||||
let symbol_name = format!(
|
||||
"gap_{:02}_{:08X}_{}",
|
||||
section_index,
|
||||
aligned_end,
|
||||
section.name.trim_start_matches('.')
|
||||
);
|
||||
log::debug!("Adding gap symbol {} at {:#010X}", symbol_name, aligned_end);
|
||||
to_add.push(ObjSymbol {
|
||||
name: symbol_name,
|
||||
demangled_name: None,
|
||||
address: aligned_end as u64,
|
||||
section: Some(section_index),
|
||||
size: next_symbol.address - aligned_end as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(
|
||||
ObjSymbolFlags::Global
|
||||
| ObjSymbolFlags::ForceActive
|
||||
| ObjSymbolFlags::NoWrite,
|
||||
),
|
||||
kind: match section.kind {
|
||||
ObjSectionKind::Code => ObjSymbolKind::Function,
|
||||
ObjSectionKind::Data
|
||||
| ObjSectionKind::ReadOnlyData
|
||||
| ObjSectionKind::Bss => ObjSymbolKind::Object,
|
||||
},
|
||||
align: None,
|
||||
data_kind: Default::default(),
|
||||
});
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
Ordering::Greater => {
|
||||
bail!(
|
||||
"Symbol {} ({:#010X}..{:#010X}) overlaps with symbol {} ({:#010X}..{:#010X}, align {})",
|
||||
symbol.name,
|
||||
symbol.address,
|
||||
symbol.address + symbol.size,
|
||||
next_symbol.name,
|
||||
next_symbol.address,
|
||||
next_symbol.address + next_symbol.size,
|
||||
next_symbol.align.unwrap_or(1)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
drop(iter);
|
||||
|
||||
for symbol in to_add {
|
||||
obj.symbols.add_direct(symbol)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
const fn align_up(value: u32, align: u32) -> u32 { (value + (align - 1)) & !(align - 1) }
|
||||
|
||||
/// Perform any necessary adjustments to allow relinking.
|
||||
/// This includes:
|
||||
/// - Ensuring .ctors & .dtors entries are split with their associated function
|
||||
|
@ -526,6 +663,9 @@ pub fn update_splits(obj: &mut ObjInfo, common_start: Option<u32>) -> Result<()>
|
|||
// Ensure splits don't overlap symbols or each other
|
||||
validate_splits(obj)?;
|
||||
|
||||
// Add symbols to beginning of any split that doesn't start with a symbol
|
||||
add_padding_symbols(obj)?;
|
||||
|
||||
// Resolve link order
|
||||
obj.link_order = resolve_link_order(obj)?;
|
||||
|
||||
|
@ -677,6 +817,12 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
|
|||
file_end = min(next_addr, section_end);
|
||||
}
|
||||
|
||||
// Skip over this data
|
||||
if split.skip {
|
||||
current_address = file_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
let file = name_to_obj
|
||||
.get(&split.unit)
|
||||
.and_then(|&idx| objects.get_mut(idx))
|
||||
|
@ -792,13 +938,8 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
|
|||
..(file_end.address as u64 - section.address) as usize]
|
||||
.to_vec(),
|
||||
};
|
||||
let name = if let Some(name) = obj.named_sections.get(¤t_address.address) {
|
||||
name.clone()
|
||||
} else {
|
||||
section.name.clone()
|
||||
};
|
||||
file.sections.push(ObjSection {
|
||||
name,
|
||||
name: split.rename.as_ref().unwrap_or(§ion.name).clone(),
|
||||
kind: section.kind,
|
||||
address: 0,
|
||||
size: file_end.address as u64 - current_address.address as u64,
|
||||
|
@ -1032,7 +1173,6 @@ pub fn end_for_section(obj: &ObjInfo, section_index: usize) -> Result<SectionAdd
|
|||
&& section.data[section.data.len() - 4..] == [0u8; 4]
|
||||
{
|
||||
section_end -= 4;
|
||||
return Ok(SectionAddress::new(section_index, section_end));
|
||||
}
|
||||
loop {
|
||||
let last_symbol = obj
|
||||
|
|
Loading…
Reference in New Issue