mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-16 08:27:02 +00:00
A lot more section-address-aware refactoring
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
mod relocations;
|
||||
mod sections;
|
||||
mod splits;
|
||||
mod symbols;
|
||||
@@ -9,8 +10,8 @@ use std::{
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
pub use relocations::{ObjReloc, ObjRelocKind, ObjRelocations};
|
||||
pub use sections::{section_kind_for_section, ObjSection, ObjSectionKind, ObjSections};
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use splits::{ObjSplit, ObjSplits};
|
||||
pub use symbols::{
|
||||
ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope,
|
||||
@@ -49,7 +50,7 @@ pub struct ObjInfo {
|
||||
pub name: String,
|
||||
pub symbols: ObjSymbols,
|
||||
pub sections: ObjSections,
|
||||
pub entry: u64,
|
||||
pub entry: Option<u64>,
|
||||
pub mw_comment: Option<MWComment>,
|
||||
|
||||
// Linker generated
|
||||
@@ -75,27 +76,6 @@ pub struct ObjInfo {
|
||||
pub unresolved_relocations: Vec<RelReloc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub enum ObjRelocKind {
|
||||
Absolute,
|
||||
PpcAddr16Hi,
|
||||
PpcAddr16Ha,
|
||||
PpcAddr16Lo,
|
||||
PpcRel24,
|
||||
PpcRel14,
|
||||
PpcEmbSda21,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjReloc {
|
||||
pub kind: ObjRelocKind,
|
||||
pub address: u64,
|
||||
pub target_symbol: SymbolIndex,
|
||||
pub addend: i64,
|
||||
/// If present, relocation against external module
|
||||
pub module: Option<u32>,
|
||||
}
|
||||
|
||||
impl ObjInfo {
|
||||
pub fn new(
|
||||
kind: ObjKind,
|
||||
@@ -110,7 +90,7 @@ impl ObjInfo {
|
||||
name,
|
||||
symbols: ObjSymbols::new(kind, symbols),
|
||||
sections: ObjSections::new(kind, sections),
|
||||
entry: 0,
|
||||
entry: None,
|
||||
mw_comment: Default::default(),
|
||||
sda2_base: None,
|
||||
sda_base: None,
|
||||
|
||||
109
src/obj/relocations.rs
Normal file
109
src/obj/relocations.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use std::{
|
||||
collections::{btree_map, BTreeMap},
|
||||
error::Error,
|
||||
fmt,
|
||||
ops::RangeBounds,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::obj::SymbolIndex;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||
pub enum ObjRelocKind {
|
||||
Absolute,
|
||||
PpcAddr16Hi,
|
||||
PpcAddr16Ha,
|
||||
PpcAddr16Lo,
|
||||
PpcRel24,
|
||||
PpcRel14,
|
||||
PpcEmbSda21,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjReloc {
|
||||
pub kind: ObjRelocKind,
|
||||
// pub address: u64,
|
||||
pub target_symbol: SymbolIndex,
|
||||
pub addend: i64,
|
||||
/// If present, relocation against external module
|
||||
pub module: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjRelocations {
|
||||
relocations: BTreeMap<u32, ObjReloc>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExistingRelocationError {
|
||||
pub address: u32,
|
||||
pub value: ObjReloc,
|
||||
}
|
||||
|
||||
impl fmt::Display for ExistingRelocationError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "relocation already exists at address {:#010X}", self.address)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ExistingRelocationError {}
|
||||
|
||||
impl ObjRelocations {
|
||||
pub fn new(relocations: Vec<(u32, ObjReloc)>) -> Result<Self, ExistingRelocationError> {
|
||||
let mut map = BTreeMap::new();
|
||||
for (address, reloc) in relocations {
|
||||
let address = address & !3;
|
||||
match map.entry(address) {
|
||||
btree_map::Entry::Vacant(e) => e.insert(reloc),
|
||||
btree_map::Entry::Occupied(e) => {
|
||||
return Err(ExistingRelocationError { address, value: e.get().clone() })
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(Self { relocations: map })
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize { self.relocations.len() }
|
||||
|
||||
pub fn insert(&mut self, address: u32, reloc: ObjReloc) -> Result<(), ExistingRelocationError> {
|
||||
let address = address & !3;
|
||||
match self.relocations.entry(address) {
|
||||
btree_map::Entry::Vacant(e) => e.insert(reloc),
|
||||
btree_map::Entry::Occupied(e) => {
|
||||
return Err(ExistingRelocationError { address, value: e.get().clone() })
|
||||
}
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, address: u32, reloc: ObjReloc) {
|
||||
self.relocations.insert(address, reloc);
|
||||
}
|
||||
|
||||
pub fn at(&self, address: u32) -> Option<&ObjReloc> { self.relocations.get(&address) }
|
||||
|
||||
pub fn at_mut(&mut self, address: u32) -> Option<&mut ObjReloc> {
|
||||
self.relocations.get_mut(&address)
|
||||
}
|
||||
|
||||
pub fn clone_map(&self) -> BTreeMap<u32, ObjReloc> { self.relocations.clone() }
|
||||
|
||||
pub fn is_empty(&self) -> bool { self.relocations.is_empty() }
|
||||
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = (u32, &ObjReloc)> {
|
||||
self.relocations.iter().map(|(&addr, reloc)| (addr, reloc))
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = (u32, &mut ObjReloc)> {
|
||||
self.relocations.iter_mut().map(|(&addr, reloc)| (addr, reloc))
|
||||
}
|
||||
|
||||
pub fn range<R>(&self, range: R) -> impl DoubleEndedIterator<Item = (u32, &ObjReloc)>
|
||||
where R: RangeBounds<u32> {
|
||||
self.relocations.range(range).map(|(&addr, reloc)| (addr, reloc))
|
||||
}
|
||||
|
||||
pub fn contains(&self, address: u32) -> bool { self.relocations.contains_key(&address) }
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::{btree_map, BTreeMap, Bound},
|
||||
collections::Bound,
|
||||
ops::{Index, IndexMut, Range, RangeBounds},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::obj::{ObjKind, ObjReloc, ObjSplit, ObjSplits, ObjSymbol};
|
||||
use crate::obj::{ObjKind, ObjRelocations, ObjSplit, ObjSplits, ObjSymbol};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ObjSectionKind {
|
||||
@@ -27,7 +27,7 @@ pub struct ObjSection {
|
||||
pub align: u64,
|
||||
/// REL files reference the original ELF section indices
|
||||
pub elf_index: usize,
|
||||
pub relocations: Vec<ObjReloc>,
|
||||
pub relocations: ObjRelocations,
|
||||
pub original_address: u64,
|
||||
pub file_offset: u64,
|
||||
pub section_known: bool,
|
||||
@@ -175,34 +175,6 @@ impl ObjSection {
|
||||
self.data_range(symbol.address as u32, symbol.address as u32 + symbol.size as u32)
|
||||
}
|
||||
|
||||
pub fn build_relocation_map(&self) -> Result<BTreeMap<u32, usize>> {
|
||||
let mut relocations = BTreeMap::new();
|
||||
for (idx, reloc) in self.relocations.iter().enumerate() {
|
||||
let address = reloc.address as u32;
|
||||
match relocations.entry(address) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
e.insert(idx);
|
||||
}
|
||||
btree_map::Entry::Occupied(_) => bail!("Duplicate relocation @ {address:#010X}"),
|
||||
}
|
||||
}
|
||||
Ok(relocations)
|
||||
}
|
||||
|
||||
pub fn build_relocation_map_cloned(&self) -> Result<BTreeMap<u32, ObjReloc>> {
|
||||
let mut relocations = BTreeMap::new();
|
||||
for reloc in self.relocations.iter().cloned() {
|
||||
let address = reloc.address as u32;
|
||||
match relocations.entry(address) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
e.insert(reloc);
|
||||
}
|
||||
btree_map::Entry::Occupied(_) => bail!("Duplicate relocation @ {address:#010X}"),
|
||||
}
|
||||
}
|
||||
Ok(relocations)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains(&self, addr: u32) -> bool {
|
||||
(self.address..self.address + self.size).contains(&(addr as u64))
|
||||
|
||||
@@ -11,6 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::{
|
||||
analysis::cfa::SectionAddress,
|
||||
obj::{ObjKind, ObjRelocKind},
|
||||
util::{config::is_auto_symbol, nested::NestedVec, split::is_linker_generated_label},
|
||||
};
|
||||
@@ -34,6 +35,8 @@ flags! {
|
||||
Common,
|
||||
Hidden,
|
||||
ForceActive,
|
||||
/// Symbol isn't referenced by any relocations
|
||||
RelocationIgnore,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +75,9 @@ impl ObjSymbolFlagSet {
|
||||
#[inline]
|
||||
pub fn is_force_active(&self) -> bool { self.0.contains(ObjSymbolFlags::ForceActive) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_relocation_ignore(&self) -> bool { self.0.contains(ObjSymbolFlags::RelocationIgnore) }
|
||||
|
||||
#[inline]
|
||||
pub fn set_scope(&mut self, scope: ObjSymbolScope) {
|
||||
match scope {
|
||||
@@ -194,9 +200,11 @@ impl ObjSymbols {
|
||||
// Replace auto symbols with real symbols
|
||||
(symbol.kind == ObjSymbolKind::Unknown && is_auto_symbol(&symbol.name))
|
||||
})
|
||||
} else {
|
||||
} else if self.obj_kind == ObjKind::Executable {
|
||||
// TODO hmmm
|
||||
self.iter_abs().find(|(_, symbol)| symbol.name == in_symbol.name)
|
||||
} else {
|
||||
bail!("ABS symbol in relocatable object: {:?}", in_symbol);
|
||||
};
|
||||
let target_symbol_idx = if let Some((symbol_idx, existing)) = opt {
|
||||
let size =
|
||||
@@ -361,7 +369,7 @@ impl ObjSymbols {
|
||||
where
|
||||
R: RangeBounds<u32>,
|
||||
{
|
||||
debug_assert!(self.obj_kind == ObjKind::Executable);
|
||||
// debug_assert!(self.obj_kind == ObjKind::Executable);
|
||||
self.symbols_by_address.range(range).map(|(k, v)| (*k, v.as_ref()))
|
||||
}
|
||||
|
||||
@@ -432,16 +440,19 @@ impl ObjSymbols {
|
||||
// Try to find a previous sized symbol that encompasses the target
|
||||
pub fn for_relocation(
|
||||
&self,
|
||||
target_addr: u32,
|
||||
target_addr: SectionAddress,
|
||||
reloc_kind: ObjRelocKind,
|
||||
) -> Result<Option<(SymbolIndex, &ObjSymbol)>> {
|
||||
ensure!(self.obj_kind == ObjKind::Executable);
|
||||
// ensure!(self.obj_kind == ObjKind::Executable);
|
||||
let mut result = None;
|
||||
for (_addr, symbol_idxs) in self.indexes_for_range(..=target_addr).rev() {
|
||||
for (_addr, symbol_idxs) in self.indexes_for_range(..=target_addr.address).rev() {
|
||||
let mut symbols = symbol_idxs
|
||||
.iter()
|
||||
.map(|&idx| (idx, &self.symbols[idx]))
|
||||
.filter(|(_, sym)| sym.referenced_by(reloc_kind))
|
||||
.filter(|(_, sym)| {
|
||||
(sym.section.is_none() || sym.section == Some(target_addr.section))
|
||||
&& sym.referenced_by(reloc_kind)
|
||||
})
|
||||
.collect_vec();
|
||||
let (symbol_idx, symbol) = if symbols.len() == 1 {
|
||||
symbols.pop().unwrap()
|
||||
@@ -480,12 +491,12 @@ impl ObjSymbols {
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
if symbol.address == target_addr as u64 {
|
||||
if symbol.address == target_addr.address as u64 {
|
||||
result = Some((symbol_idx, symbol));
|
||||
break;
|
||||
}
|
||||
if symbol.size > 0 {
|
||||
if symbol.address + symbol.size > target_addr as u64 {
|
||||
if symbol.address + symbol.size > target_addr.address as u64 {
|
||||
result = Some((symbol_idx, symbol));
|
||||
}
|
||||
break;
|
||||
@@ -509,6 +520,10 @@ impl Index<SymbolIndex> for ObjSymbols {
|
||||
impl ObjSymbol {
|
||||
/// Whether this symbol can be referenced by the given relocation kind.
|
||||
pub fn referenced_by(&self, reloc_kind: ObjRelocKind) -> bool {
|
||||
if self.flags.is_relocation_ignore() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if is_linker_generated_label(&self.name) {
|
||||
// Linker generated labels will only be referenced by @ha/@h/@l relocations
|
||||
return matches!(
|
||||
|
||||
Reference in New Issue
Block a user