A lot more section-address-aware refactoring

This commit is contained in:
2023-08-23 23:13:12 -04:00
parent 5843ee021e
commit 3f63f1ef47
29 changed files with 10110 additions and 1206 deletions

View File

@@ -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
View 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) }
}

View File

@@ -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))

View File

@@ -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!(