mirror of
https://github.com/encounter/decomp-toolkit.git
synced 2025-12-15 16:16:20 +00:00
Begin REL analysis & rework lots of code to be section-address aware
This commit is contained in:
682
src/obj/mod.rs
682
src/obj/mod.rs
@@ -1,180 +1,23 @@
|
||||
pub mod signatures;
|
||||
pub mod split;
|
||||
mod sections;
|
||||
mod splits;
|
||||
mod symbols;
|
||||
|
||||
use std::{
|
||||
cmp::{max, min},
|
||||
collections::{btree_map, BTreeMap, BTreeSet, HashMap},
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Range, RangeBounds},
|
||||
collections::{BTreeMap, BTreeSet},
|
||||
hash::Hash,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use flagset::{flags, FlagSet};
|
||||
use itertools::Itertools;
|
||||
pub use sections::{section_kind_for_section, ObjSection, ObjSectionKind, ObjSections};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::{
|
||||
obj::split::is_linker_generated_label,
|
||||
util::{comment::MWComment, nested::NestedVec, rel::RelReloc},
|
||||
pub use splits::{ObjSplit, ObjSplits};
|
||||
pub use symbols::{
|
||||
ObjDataKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope,
|
||||
ObjSymbols, SymbolIndex,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
pub enum ObjSymbolScope {
|
||||
#[default]
|
||||
Unknown,
|
||||
Global,
|
||||
Weak,
|
||||
Local,
|
||||
}
|
||||
|
||||
flags! {
|
||||
#[repr(u8)]
|
||||
#[derive(Deserialize_repr, Serialize_repr)]
|
||||
pub enum ObjSymbolFlags: u8 {
|
||||
Global,
|
||||
Local,
|
||||
Weak,
|
||||
Common,
|
||||
Hidden,
|
||||
ForceActive,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
||||
|
||||
impl ObjSymbolFlagSet {
|
||||
#[inline]
|
||||
pub fn scope(&self) -> ObjSymbolScope {
|
||||
if self.is_local() {
|
||||
ObjSymbolScope::Local
|
||||
} else if self.is_weak() {
|
||||
ObjSymbolScope::Weak
|
||||
} else if self.0.contains(ObjSymbolFlags::Global) {
|
||||
ObjSymbolScope::Global
|
||||
} else {
|
||||
ObjSymbolScope::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_local(&self) -> bool { self.0.contains(ObjSymbolFlags::Local) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_global(&self) -> bool { !self.is_local() }
|
||||
|
||||
#[inline]
|
||||
pub fn is_common(&self) -> bool { self.0.contains(ObjSymbolFlags::Common) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_weak(&self) -> bool { self.0.contains(ObjSymbolFlags::Weak) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_hidden(&self) -> bool { self.0.contains(ObjSymbolFlags::Hidden) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_force_active(&self) -> bool { self.0.contains(ObjSymbolFlags::ForceActive) }
|
||||
|
||||
#[inline]
|
||||
pub fn set_scope(&mut self, scope: ObjSymbolScope) {
|
||||
match scope {
|
||||
ObjSymbolScope::Unknown => {
|
||||
self.0 &= !(ObjSymbolFlags::Local | ObjSymbolFlags::Global | ObjSymbolFlags::Weak)
|
||||
}
|
||||
ObjSymbolScope::Global => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Local | ObjSymbolFlags::Weak))
|
||||
| ObjSymbolFlags::Global
|
||||
}
|
||||
ObjSymbolScope::Weak => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Local | ObjSymbolFlags::Global))
|
||||
| ObjSymbolFlags::Weak
|
||||
}
|
||||
ObjSymbolScope::Local => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Global | ObjSymbolFlags::Weak))
|
||||
| ObjSymbolFlags::Local
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_force_active(&mut self, value: bool) {
|
||||
if value {
|
||||
self.0 |= ObjSymbolFlags::ForceActive;
|
||||
} else {
|
||||
self.0 &= !ObjSymbolFlags::ForceActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
impl Hash for ObjSymbolFlagSet {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.0.bits().hash(state) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ObjSectionKind {
|
||||
Code,
|
||||
Data,
|
||||
ReadOnlyData,
|
||||
Bss,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSection {
|
||||
pub name: String,
|
||||
pub kind: ObjSectionKind,
|
||||
pub address: u64,
|
||||
pub size: u64,
|
||||
pub data: Vec<u8>,
|
||||
pub align: u64,
|
||||
pub index: usize,
|
||||
/// REL files reference the original ELF section indices
|
||||
pub elf_index: usize,
|
||||
pub relocations: Vec<ObjReloc>,
|
||||
pub original_address: u64,
|
||||
pub file_offset: u64,
|
||||
pub section_known: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
|
||||
pub enum ObjSymbolKind {
|
||||
#[default]
|
||||
Unknown,
|
||||
Function,
|
||||
Object,
|
||||
Section,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||
pub enum ObjDataKind {
|
||||
#[default]
|
||||
Unknown,
|
||||
Byte,
|
||||
Byte2,
|
||||
Byte4,
|
||||
Byte8,
|
||||
Float,
|
||||
Double,
|
||||
String,
|
||||
String16,
|
||||
StringTable,
|
||||
String16Table,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub struct ObjSymbol {
|
||||
pub name: String,
|
||||
pub demangled_name: Option<String>,
|
||||
pub address: u64,
|
||||
pub section: Option<usize>,
|
||||
pub size: u64,
|
||||
pub size_known: bool,
|
||||
pub flags: ObjSymbolFlagSet,
|
||||
pub kind: ObjSymbolKind,
|
||||
pub align: Option<u32>,
|
||||
pub data_kind: ObjDataKind,
|
||||
}
|
||||
use crate::util::{comment::MWComment, rel::RelReloc};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ObjKind {
|
||||
@@ -199,34 +42,13 @@ pub struct ObjUnit {
|
||||
pub comment_version: Option<u8>,
|
||||
}
|
||||
|
||||
/// Marks a split point within a section.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ObjSplit {
|
||||
pub unit: String,
|
||||
pub end: u32,
|
||||
pub align: Option<u32>,
|
||||
/// Whether this is a part of common BSS.
|
||||
pub common: bool,
|
||||
/// Generated, replaceable by user.
|
||||
pub autogenerated: bool,
|
||||
}
|
||||
|
||||
pub type SymbolIndex = usize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSymbols {
|
||||
symbols: Vec<ObjSymbol>,
|
||||
symbols_by_address: BTreeMap<u32, Vec<SymbolIndex>>,
|
||||
symbols_by_name: HashMap<String, Vec<SymbolIndex>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjInfo {
|
||||
pub kind: ObjKind,
|
||||
pub architecture: ObjArchitecture,
|
||||
pub name: String,
|
||||
pub symbols: ObjSymbols,
|
||||
pub sections: Vec<ObjSection>,
|
||||
pub sections: ObjSections,
|
||||
pub entry: u64,
|
||||
pub mw_comment: Option<MWComment>,
|
||||
|
||||
@@ -240,7 +62,6 @@ pub struct ObjInfo {
|
||||
pub arena_hi: Option<u32>,
|
||||
|
||||
// Extracted
|
||||
pub splits: BTreeMap<u32, Vec<ObjSplit>>,
|
||||
pub named_sections: BTreeMap<u32, String>,
|
||||
pub link_order: Vec<ObjUnit>,
|
||||
pub blocked_ranges: BTreeMap<u32, u32>, // start -> end
|
||||
@@ -271,312 +92,8 @@ pub struct ObjReloc {
|
||||
pub address: u64,
|
||||
pub target_symbol: SymbolIndex,
|
||||
pub addend: i64,
|
||||
}
|
||||
|
||||
impl ObjSymbols {
|
||||
pub fn new(symbols: Vec<ObjSymbol>) -> Self {
|
||||
let mut symbols_by_address = BTreeMap::<u32, Vec<SymbolIndex>>::new();
|
||||
let mut symbols_by_name = HashMap::<String, Vec<SymbolIndex>>::new();
|
||||
for (idx, symbol) in symbols.iter().enumerate() {
|
||||
symbols_by_address.nested_push(symbol.address as u32, idx);
|
||||
if !symbol.name.is_empty() {
|
||||
symbols_by_name.nested_push(symbol.name.clone(), idx);
|
||||
}
|
||||
}
|
||||
Self { symbols, symbols_by_address, symbols_by_name }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, in_symbol: ObjSymbol, replace: bool) -> Result<SymbolIndex> {
|
||||
let opt = self.at_address(in_symbol.address as u32).find(|(_, symbol)| {
|
||||
(symbol.kind == in_symbol.kind ||
|
||||
// Replace lbl_* with real symbols
|
||||
(symbol.kind == ObjSymbolKind::Unknown && symbol.name.starts_with("lbl_")))
|
||||
// Hack to avoid replacing different ABS symbols
|
||||
&& (symbol.section.is_some() || symbol.name == in_symbol.name)
|
||||
// Avoid replacing symbols with ABS symbols, and vice versa
|
||||
&& (symbol.section == in_symbol.section)
|
||||
});
|
||||
let target_symbol_idx = if let Some((symbol_idx, existing)) = opt {
|
||||
let size =
|
||||
if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size {
|
||||
log::warn!(
|
||||
"Conflicting size for {}: was {:#X}, now {:#X}",
|
||||
existing.name,
|
||||
existing.size,
|
||||
in_symbol.size
|
||||
);
|
||||
if replace {
|
||||
in_symbol.size
|
||||
} else {
|
||||
existing.size
|
||||
}
|
||||
} else if in_symbol.size_known {
|
||||
in_symbol.size
|
||||
} else {
|
||||
existing.size
|
||||
};
|
||||
if !replace {
|
||||
// Not replacing existing symbol, but update size
|
||||
if in_symbol.size_known && !existing.size_known {
|
||||
self.replace(symbol_idx, ObjSymbol {
|
||||
size: in_symbol.size,
|
||||
size_known: true,
|
||||
..existing.clone()
|
||||
})?;
|
||||
}
|
||||
return Ok(symbol_idx);
|
||||
}
|
||||
let new_symbol = ObjSymbol {
|
||||
name: in_symbol.name,
|
||||
demangled_name: in_symbol.demangled_name,
|
||||
address: in_symbol.address,
|
||||
section: in_symbol.section,
|
||||
size,
|
||||
size_known: existing.size_known || in_symbol.size != 0,
|
||||
flags: in_symbol.flags,
|
||||
kind: in_symbol.kind,
|
||||
align: in_symbol.align.or(existing.align),
|
||||
data_kind: match in_symbol.data_kind {
|
||||
ObjDataKind::Unknown => existing.data_kind,
|
||||
kind => kind,
|
||||
},
|
||||
};
|
||||
if existing != &new_symbol {
|
||||
log::debug!("Replacing {:?} with {:?}", existing, new_symbol);
|
||||
self.replace(symbol_idx, new_symbol)?;
|
||||
}
|
||||
symbol_idx
|
||||
} else {
|
||||
let target_symbol_idx = self.symbols.len();
|
||||
self.add_direct(ObjSymbol {
|
||||
name: in_symbol.name,
|
||||
demangled_name: in_symbol.demangled_name,
|
||||
address: in_symbol.address,
|
||||
section: in_symbol.section,
|
||||
size: in_symbol.size,
|
||||
size_known: in_symbol.size != 0,
|
||||
flags: in_symbol.flags,
|
||||
kind: in_symbol.kind,
|
||||
align: in_symbol.align,
|
||||
data_kind: in_symbol.data_kind,
|
||||
})?;
|
||||
target_symbol_idx
|
||||
};
|
||||
Ok(target_symbol_idx)
|
||||
}
|
||||
|
||||
pub fn add_direct(&mut self, in_symbol: ObjSymbol) -> Result<SymbolIndex> {
|
||||
let symbol_idx = self.symbols.len();
|
||||
self.symbols_by_address.nested_push(in_symbol.address as u32, symbol_idx);
|
||||
if !in_symbol.name.is_empty() {
|
||||
self.symbols_by_name.nested_push(in_symbol.name.clone(), symbol_idx);
|
||||
}
|
||||
self.symbols.push(in_symbol);
|
||||
Ok(symbol_idx)
|
||||
}
|
||||
|
||||
pub fn at(&self, symbol_idx: SymbolIndex) -> &ObjSymbol { &self.symbols[symbol_idx] }
|
||||
|
||||
pub fn address_of(&self, symbol_idx: SymbolIndex) -> u64 { self.symbols[symbol_idx].address }
|
||||
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &ObjSymbol> { self.symbols.iter() }
|
||||
|
||||
pub fn count(&self) -> usize { self.symbols.len() }
|
||||
|
||||
pub fn at_address(
|
||||
&self,
|
||||
addr: u32,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols_by_address
|
||||
.get(&addr)
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(move |&idx| (idx, &self.symbols[idx]))
|
||||
}
|
||||
|
||||
pub fn kind_at_address(
|
||||
&self,
|
||||
addr: u32,
|
||||
kind: ObjSymbolKind,
|
||||
) -> Result<Option<(SymbolIndex, &ObjSymbol)>> {
|
||||
let (count, result) = self
|
||||
.at_address(addr)
|
||||
.filter(|(_, sym)| sym.kind == kind)
|
||||
.fold((0, None), |(i, _), v| (i + 1, Some(v)));
|
||||
ensure!(count <= 1, "Multiple symbols of kind {:?} at address {:#010X}", kind, addr);
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// Iterate over all in address ascending order, including ABS symbols
|
||||
pub fn iter_ordered(&self) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols_by_address
|
||||
.iter()
|
||||
.flat_map(move |(_, v)| v.iter().map(move |u| (*u, &self.symbols[*u])))
|
||||
}
|
||||
|
||||
// Iterate over range in address ascending order, excluding ABS symbols
|
||||
pub fn for_range<R>(
|
||||
&self,
|
||||
range: R,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)>
|
||||
where
|
||||
R: RangeBounds<u32>,
|
||||
{
|
||||
self.symbols_by_address
|
||||
.range(range)
|
||||
.flat_map(move |(_, v)| v.iter().map(move |u| (*u, &self.symbols[*u])))
|
||||
// Ignore ABS symbols
|
||||
.filter(move |(_, sym)| sym.section.is_some() || sym.flags.is_common())
|
||||
}
|
||||
|
||||
pub fn indexes_for_range<R>(
|
||||
&self,
|
||||
range: R,
|
||||
) -> impl DoubleEndedIterator<Item = (u32, &[SymbolIndex])>
|
||||
where
|
||||
R: RangeBounds<u32>,
|
||||
{
|
||||
self.symbols_by_address.range(range).map(|(k, v)| (*k, v.as_ref()))
|
||||
}
|
||||
|
||||
pub fn for_section(
|
||||
&self,
|
||||
section: &ObjSection,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
let section_index = section.index;
|
||||
self.for_range(section.address as u32..(section.address + section.size) as u32)
|
||||
.filter(move |(_, symbol)| symbol.section == Some(section_index))
|
||||
}
|
||||
|
||||
pub fn for_name(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols_by_name
|
||||
.get(name)
|
||||
.into_iter()
|
||||
.flat_map(move |v| v.iter().map(move |u| (*u, &self.symbols[*u])))
|
||||
}
|
||||
|
||||
pub fn by_name(&self, name: &str) -> Result<Option<(SymbolIndex, &ObjSymbol)>> {
|
||||
let mut iter = self.for_name(name);
|
||||
let result = iter.next();
|
||||
if let Some((index, symbol)) = result {
|
||||
if let Some((other_index, other_symbol)) = iter.next() {
|
||||
bail!(
|
||||
"Multiple symbols with name {}: {} {:?} {:#010X} and {} {:?} {:#010X}",
|
||||
name,
|
||||
index,
|
||||
symbol.kind,
|
||||
symbol.address,
|
||||
other_index,
|
||||
other_symbol.kind,
|
||||
other_symbol.address
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn by_kind(
|
||||
&self,
|
||||
kind: ObjSymbolKind,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols.iter().enumerate().filter(move |(_, sym)| sym.kind == kind)
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, index: SymbolIndex, symbol: ObjSymbol) -> Result<()> {
|
||||
let symbol_ref = &mut self.symbols[index];
|
||||
ensure!(symbol_ref.address == symbol.address, "Can't modify address with replace_symbol");
|
||||
if symbol_ref.name != symbol.name {
|
||||
if !symbol_ref.name.is_empty() {
|
||||
self.symbols_by_name.nested_remove(&symbol_ref.name, &index);
|
||||
}
|
||||
if !symbol.name.is_empty() {
|
||||
self.symbols_by_name.nested_push(symbol.name.clone(), index);
|
||||
}
|
||||
}
|
||||
*symbol_ref = symbol;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Try to find a previous sized symbol that encompasses the target
|
||||
pub fn for_relocation(
|
||||
&self,
|
||||
target_addr: u32,
|
||||
reloc_kind: ObjRelocKind,
|
||||
) -> Result<Option<(SymbolIndex, &ObjSymbol)>> {
|
||||
let mut result = None;
|
||||
for (_addr, symbol_idxs) in self.indexes_for_range(..=target_addr).rev() {
|
||||
let mut symbols = symbol_idxs
|
||||
.iter()
|
||||
.map(|&idx| (idx, self.at(idx)))
|
||||
.filter(|(_, sym)| {
|
||||
// Linker generated labels can only be used with @ha/@h/@l relocations
|
||||
!is_linker_generated_label(&sym.name)
|
||||
|| (matches!(
|
||||
reloc_kind,
|
||||
ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Lo
|
||||
))
|
||||
})
|
||||
.collect_vec();
|
||||
let (symbol_idx, symbol) = if symbols.len() == 1 {
|
||||
symbols.pop().unwrap()
|
||||
} else {
|
||||
symbols.sort_by_key(|&(_, symbol)| {
|
||||
let mut rank = match symbol.kind {
|
||||
ObjSymbolKind::Function | ObjSymbolKind::Object => match reloc_kind {
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => 1,
|
||||
ObjRelocKind::Absolute
|
||||
| ObjRelocKind::PpcRel24
|
||||
| ObjRelocKind::PpcRel14
|
||||
| ObjRelocKind::PpcEmbSda21 => 2,
|
||||
},
|
||||
// Label
|
||||
ObjSymbolKind::Unknown => match reloc_kind {
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo
|
||||
if !symbol.name.starts_with("..") =>
|
||||
{
|
||||
3
|
||||
}
|
||||
_ => 1,
|
||||
},
|
||||
ObjSymbolKind::Section => -1,
|
||||
};
|
||||
if symbol.size > 0 {
|
||||
rank += 1;
|
||||
}
|
||||
-rank
|
||||
});
|
||||
match symbols.first() {
|
||||
Some(&v) => v,
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
if symbol.address == target_addr as u64 {
|
||||
result = Some((symbol_idx, symbol));
|
||||
break;
|
||||
}
|
||||
if symbol.size > 0 {
|
||||
if symbol.address + symbol.size > target_addr as u64 {
|
||||
result = Some((symbol_idx, symbol));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&mut self, idx: SymbolIndex) -> &mut ObjSymbolFlagSet {
|
||||
&mut self.symbols[idx].flags
|
||||
}
|
||||
/// If present, relocation against external module
|
||||
pub module: Option<u32>,
|
||||
}
|
||||
|
||||
impl ObjInfo {
|
||||
@@ -591,8 +108,8 @@ impl ObjInfo {
|
||||
kind,
|
||||
architecture,
|
||||
name,
|
||||
symbols: ObjSymbols::new(symbols),
|
||||
sections,
|
||||
symbols: ObjSymbols::new(kind, symbols),
|
||||
sections: ObjSections::new(kind, sections),
|
||||
entry: 0,
|
||||
mw_comment: Default::default(),
|
||||
sda2_base: None,
|
||||
@@ -602,7 +119,7 @@ impl ObjInfo {
|
||||
db_stack_addr: None,
|
||||
arena_lo: None,
|
||||
arena_hi: None,
|
||||
splits: Default::default(),
|
||||
// splits: Default::default(),
|
||||
named_sections: Default::default(),
|
||||
link_order: vec![],
|
||||
blocked_ranges: Default::default(),
|
||||
@@ -626,86 +143,11 @@ impl ObjInfo {
|
||||
self.symbols.add(in_symbol, replace)
|
||||
}
|
||||
|
||||
pub fn section_at(&self, addr: u32) -> Result<&ObjSection> {
|
||||
self.sections
|
||||
.iter()
|
||||
.find(|s| s.contains(addr))
|
||||
.ok_or_else(|| anyhow!("Failed to locate section @ {:#010X}", addr))
|
||||
}
|
||||
|
||||
pub fn section_for(&self, range: Range<u32>) -> Result<&ObjSection> {
|
||||
self.sections.iter().find(|s| s.contains_range(range.clone())).ok_or_else(|| {
|
||||
anyhow!("Failed to locate section @ {:#010X}-{:#010X}", range.start, range.end)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn section_data(&self, start: u32, end: u32) -> Result<(&ObjSection, &[u8])> {
|
||||
let section = self.section_at(start)?;
|
||||
ensure!(
|
||||
section.contains_range(start..end),
|
||||
"Range {:#010X}-{:#010X} outside of section {}: {:#010X}-{:#010X}",
|
||||
start,
|
||||
end,
|
||||
section.name,
|
||||
section.address,
|
||||
section.address + section.size
|
||||
);
|
||||
if section.kind == ObjSectionKind::Bss {
|
||||
return Ok((section, &[]));
|
||||
}
|
||||
let data = if end == 0 {
|
||||
§ion.data[(start as u64 - section.address) as usize..]
|
||||
} else {
|
||||
§ion.data[(start as u64 - section.address) as usize
|
||||
..min(section.data.len(), (end as u64 - section.address) as usize)]
|
||||
};
|
||||
Ok((section, data))
|
||||
}
|
||||
|
||||
/// Locate an existing split for the given address.
|
||||
pub fn split_for(&self, address: u32) -> Option<(u32, &ObjSplit)> {
|
||||
match self.splits_for_range(..=address).next_back() {
|
||||
Some((addr, split)) if split.end == 0 || split.end > address => Some((addr, split)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate existing splits within the given address range.
|
||||
pub fn splits_for_range<R>(
|
||||
&self,
|
||||
range: R,
|
||||
) -> impl DoubleEndedIterator<Item = (u32, &ObjSplit)>
|
||||
where
|
||||
R: RangeBounds<u32>,
|
||||
{
|
||||
self.splits.range(range).flat_map(|(addr, v)| v.iter().map(move |u| (*addr, u)))
|
||||
}
|
||||
|
||||
pub fn split_for_unit(
|
||||
&self,
|
||||
unit: &str,
|
||||
section: &ObjSection,
|
||||
) -> Result<Option<(u32, &ObjSplit)>> {
|
||||
let mut result = None::<(u32, &ObjSplit)>;
|
||||
for (addr, split) in self
|
||||
.splits_for_range(section.address as u32..(section.address + section.size) as u32)
|
||||
.filter(|(_, split)| split.unit == unit)
|
||||
{
|
||||
ensure!(
|
||||
result.is_none(),
|
||||
"Multiple splits for unit {} in section {}: {:#010X}, {:#010X}",
|
||||
unit,
|
||||
section.name,
|
||||
result.unwrap().0,
|
||||
addr
|
||||
);
|
||||
result = Some((addr, split));
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn add_split(&mut self, address: u32, split: ObjSplit) -> Result<()> {
|
||||
let section = self.section_at(address)?;
|
||||
pub fn add_split(&mut self, section_index: usize, address: u32, split: ObjSplit) -> Result<()> {
|
||||
let section = self
|
||||
.sections
|
||||
.get_mut(section_index)
|
||||
.ok_or_else(|| anyhow!("Invalid section index {}", section_index))?;
|
||||
let section_start = section.address as u32;
|
||||
let section_end = (section.address + section.size) as u32;
|
||||
ensure!(
|
||||
@@ -719,7 +161,7 @@ impl ObjInfo {
|
||||
section_end
|
||||
);
|
||||
|
||||
if let Some((existing_addr, existing_split)) = self.split_for_unit(&split.unit, section)? {
|
||||
if let Some((existing_addr, existing_split)) = section.splits.for_unit(&split.unit)? {
|
||||
let new_start = min(existing_addr, address);
|
||||
let new_end = max(existing_split.end, split.end);
|
||||
|
||||
@@ -788,7 +230,7 @@ impl ObjInfo {
|
||||
// Check if new split overlaps any existing splits
|
||||
let mut to_remove = BTreeSet::new();
|
||||
let mut to_rename = BTreeSet::new();
|
||||
for (existing_addr, existing_split) in self.splits_for_range(new_start..new_end) {
|
||||
for (existing_addr, existing_split) in section.splits.for_range(new_start..new_end) {
|
||||
// TODO the logic in this method should be reworked, this is a hack
|
||||
if split.autogenerated && !existing_split.autogenerated {
|
||||
log::debug!(
|
||||
@@ -827,15 +269,15 @@ impl ObjInfo {
|
||||
|
||||
// Remove overlapping splits
|
||||
for addr in to_remove {
|
||||
self.splits.remove(&addr);
|
||||
section.splits.remove(addr);
|
||||
}
|
||||
// Rename any units that were overwritten
|
||||
// TODO this should also merge with existing splits
|
||||
for unit in to_rename {
|
||||
for (existing_addr, existing) in self
|
||||
.splits
|
||||
.sections
|
||||
.iter_mut()
|
||||
.flat_map(|(addr, v)| v.iter_mut().map(move |u| (addr, u)))
|
||||
.flat_map(|(_, section)| section.splits.iter_mut())
|
||||
.filter(|(_, split)| split.unit == unit)
|
||||
{
|
||||
log::debug!(
|
||||
@@ -848,7 +290,7 @@ impl ObjInfo {
|
||||
existing.unit = split.unit.clone();
|
||||
}
|
||||
}
|
||||
self.add_split(new_start, ObjSplit {
|
||||
self.add_split(section_index, new_start, ObjSplit {
|
||||
unit: split.unit,
|
||||
end: new_end,
|
||||
align: new_align,
|
||||
@@ -859,72 +301,14 @@ impl ObjInfo {
|
||||
}
|
||||
|
||||
log::debug!("Adding split @ {} {:#010X}: {:?}", section.name, address, split);
|
||||
self.splits.entry(address).or_default().push(split);
|
||||
section.splits.push(address, split);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn is_unit_autogenerated(&self, unit: &str) -> bool {
|
||||
self.splits_for_range(..)
|
||||
.filter(|(_, split)| split.unit == unit)
|
||||
.all(|(_, split)| split.autogenerated)
|
||||
self.sections
|
||||
.all_splits()
|
||||
.filter(|(_, _, _, split)| split.unit == unit)
|
||||
.all(|(_, _, _, split)| split.autogenerated)
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjSection {
|
||||
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))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains_range(&self, range: Range<u32>) -> bool {
|
||||
(range.start as u64) >= self.address && (range.end as u64) <= self.address + self.size
|
||||
}
|
||||
|
||||
pub fn rename(&mut self, name: String) -> Result<()> {
|
||||
self.kind = section_kind_for_section(&name)?;
|
||||
self.name = name;
|
||||
self.section_known = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn section_kind_for_section(section_name: &str) -> Result<ObjSectionKind> {
|
||||
Ok(match section_name {
|
||||
".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code,
|
||||
".ctors" | ".dtors" | ".rodata" | ".sdata2" | "extab" | "extabindex" => {
|
||||
ObjSectionKind::ReadOnlyData
|
||||
}
|
||||
".bss" | ".sbss" | ".sbss2" => ObjSectionKind::Bss,
|
||||
".data" | ".sdata" => ObjSectionKind::Data,
|
||||
name => bail!("Unknown section {name}"),
|
||||
})
|
||||
}
|
||||
|
||||
247
src/obj/sections.rs
Normal file
247
src/obj/sections.rs
Normal file
@@ -0,0 +1,247 @@
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::{btree_map, BTreeMap, Bound},
|
||||
ops::{Index, IndexMut, Range, RangeBounds},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::obj::{ObjKind, ObjReloc, ObjSplit, ObjSplits, ObjSymbol};
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
|
||||
pub enum ObjSectionKind {
|
||||
Code,
|
||||
Data,
|
||||
ReadOnlyData,
|
||||
Bss,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSection {
|
||||
pub name: String,
|
||||
pub kind: ObjSectionKind,
|
||||
pub address: u64,
|
||||
pub size: u64,
|
||||
pub data: Vec<u8>,
|
||||
pub align: u64,
|
||||
/// REL files reference the original ELF section indices
|
||||
pub elf_index: usize,
|
||||
pub relocations: Vec<ObjReloc>,
|
||||
pub original_address: u64,
|
||||
pub file_offset: u64,
|
||||
pub section_known: bool,
|
||||
pub splits: ObjSplits,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSections {
|
||||
obj_kind: ObjKind,
|
||||
sections: Vec<ObjSection>,
|
||||
}
|
||||
|
||||
impl ObjSections {
|
||||
pub fn new(obj_kind: ObjKind, sections: Vec<ObjSection>) -> Self { Self { obj_kind, sections } }
|
||||
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = (usize, &ObjSection)> {
|
||||
self.sections.iter().enumerate()
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = (usize, &mut ObjSection)> {
|
||||
self.sections.iter_mut().enumerate()
|
||||
}
|
||||
|
||||
pub fn count(&self) -> usize { self.sections.len() }
|
||||
|
||||
pub fn next_section_index(&self) -> usize { self.sections.len() }
|
||||
|
||||
pub fn get(&self, index: usize) -> Option<&ObjSection> { self.sections.get(index) }
|
||||
|
||||
pub fn get_mut(&mut self, index: usize) -> Option<&mut ObjSection> {
|
||||
self.sections.get_mut(index)
|
||||
}
|
||||
|
||||
pub fn get_elf_index(&self, elf_index: usize) -> Option<(usize, &ObjSection)> {
|
||||
self.iter().find(|&(_, s)| s.elf_index == elf_index)
|
||||
}
|
||||
|
||||
pub fn get_elf_index_mut(&mut self, elf_index: usize) -> Option<(usize, &mut ObjSection)> {
|
||||
self.iter_mut().find(|(_, s)| s.elf_index == elf_index)
|
||||
}
|
||||
|
||||
pub fn at_address(&self, addr: u32) -> Result<(usize, &ObjSection)> {
|
||||
ensure!(
|
||||
self.obj_kind == ObjKind::Executable,
|
||||
"Use of ObjSections::at_address in relocatable object"
|
||||
);
|
||||
self.iter()
|
||||
.find(|&(_, s)| s.contains(addr))
|
||||
.ok_or_else(|| anyhow!("Failed to locate section @ {:#010X}", addr))
|
||||
}
|
||||
|
||||
pub fn at_address_mut(&mut self, addr: u32) -> Result<(usize, &mut ObjSection)> {
|
||||
ensure!(
|
||||
self.obj_kind == ObjKind::Executable,
|
||||
"Use of ObjSections::at_address_mut in relocatable object"
|
||||
);
|
||||
self.iter_mut()
|
||||
.find(|(_, s)| s.contains(addr))
|
||||
.ok_or_else(|| anyhow!("Failed to locate section @ {:#010X}", addr))
|
||||
}
|
||||
|
||||
pub fn with_range(&self, range: Range<u32>) -> Result<(usize, &ObjSection)> {
|
||||
ensure!(
|
||||
self.obj_kind == ObjKind::Executable,
|
||||
"Use of ObjSections::with_range in relocatable object"
|
||||
);
|
||||
self.iter().find(|&(_, s)| s.contains_range(range.clone())).ok_or_else(|| {
|
||||
anyhow!("Failed to locate section @ {:#010X}-{:#010X}", range.start, range.end)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn by_kind(
|
||||
&self,
|
||||
kind: ObjSectionKind,
|
||||
) -> impl DoubleEndedIterator<Item = (usize, &ObjSection)> {
|
||||
self.iter().filter(move |(_, s)| s.kind == kind)
|
||||
}
|
||||
|
||||
pub fn by_name(&self, name: &str) -> Result<Option<(usize, &ObjSection)>> {
|
||||
self.iter()
|
||||
.filter(move |(_, s)| s.name == name)
|
||||
.at_most_one()
|
||||
.map_err(|_| anyhow!("Multiple sections with name {}", name))
|
||||
}
|
||||
|
||||
pub fn push(&mut self, section: ObjSection) -> usize {
|
||||
let index = self.sections.len();
|
||||
self.sections.push(section);
|
||||
index
|
||||
}
|
||||
|
||||
pub fn all_splits(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (usize, &ObjSection, u32, &ObjSplit)> {
|
||||
self.iter()
|
||||
.flat_map(|(idx, s)| s.splits.iter().map(move |(addr, split)| (idx, s, addr, split)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<usize> for ObjSections {
|
||||
type Output = ObjSection;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output { &self.sections[index] }
|
||||
}
|
||||
|
||||
impl IndexMut<usize> for ObjSections {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.sections[index] }
|
||||
}
|
||||
|
||||
impl ObjSection {
|
||||
pub fn data_range(&self, start: u32, end: u32) -> Result<&[u8]> {
|
||||
if end == 0 {
|
||||
ensure!(
|
||||
self.contains(start),
|
||||
"Address {:#010X} outside of section {}: {:#010X}-{:#010X}",
|
||||
start,
|
||||
self.name,
|
||||
self.address,
|
||||
self.address + self.size
|
||||
);
|
||||
} else {
|
||||
ensure!(
|
||||
self.contains_range(start..end),
|
||||
"Range {:#010X}-{:#010X} outside of section {}: {:#010X}-{:#010X}",
|
||||
start,
|
||||
end,
|
||||
self.name,
|
||||
self.address,
|
||||
self.address + self.size
|
||||
);
|
||||
}
|
||||
if self.kind == ObjSectionKind::Bss {
|
||||
return Ok(&[]);
|
||||
}
|
||||
let start = (start as u64 - self.address) as usize;
|
||||
Ok(if end == 0 {
|
||||
&self.data[start..]
|
||||
} else {
|
||||
&self.data[start..min(self.data.len(), (end as u64 - self.address) as usize)]
|
||||
})
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn symbol_data(&self, symbol: &ObjSymbol) -> Result<&[u8]> {
|
||||
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))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contains_range<R>(&self, range: R) -> bool
|
||||
where R: RangeBounds<u32> {
|
||||
let start = self.address as u32;
|
||||
let end = self.address as u32 + self.size as u32;
|
||||
let start_in_range = match range.start_bound() {
|
||||
Bound::Included(&n) => n >= start && n < end,
|
||||
Bound::Excluded(&n) => n > start && n < end,
|
||||
Bound::Unbounded => true,
|
||||
};
|
||||
let end_in_range = match range.end_bound() {
|
||||
Bound::Included(&n) => n > start && n < end,
|
||||
Bound::Excluded(&n) => n > start && n <= end,
|
||||
Bound::Unbounded => true,
|
||||
};
|
||||
start_in_range && end_in_range
|
||||
}
|
||||
|
||||
pub fn rename(&mut self, name: String) -> Result<()> {
|
||||
self.kind = section_kind_for_section(&name)?;
|
||||
self.name = name;
|
||||
self.section_known = true;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn section_kind_for_section(section_name: &str) -> Result<ObjSectionKind> {
|
||||
Ok(match section_name {
|
||||
".init" | ".text" | ".dbgtext" | ".vmtext" => ObjSectionKind::Code,
|
||||
".ctors" | ".dtors" | ".rodata" | ".sdata2" | "extab" | "extabindex" => {
|
||||
ObjSectionKind::ReadOnlyData
|
||||
}
|
||||
".bss" | ".sbss" | ".sbss2" => ObjSectionKind::Bss,
|
||||
".data" | ".sdata" => ObjSectionKind::Data,
|
||||
name => bail!("Unknown section {name}"),
|
||||
})
|
||||
}
|
||||
@@ -1,372 +0,0 @@
|
||||
use std::{
|
||||
collections::{btree_map, BTreeMap},
|
||||
path::Path,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use base64::{engine::general_purpose::STANDARD, Engine};
|
||||
use cwdemangle::{demangle, DemangleOptions};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha1::{Digest, Sha1};
|
||||
|
||||
use crate::{
|
||||
analysis::tracker::{Relocation, Tracker},
|
||||
array_ref,
|
||||
obj::{ObjInfo, ObjReloc, ObjRelocKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolKind},
|
||||
util::elf::process_elf,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct OutSymbol {
|
||||
pub kind: ObjSymbolKind,
|
||||
pub name: String,
|
||||
pub size: u32,
|
||||
pub flags: ObjSymbolFlagSet,
|
||||
pub section: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct OutReloc {
|
||||
pub offset: u32,
|
||||
pub kind: ObjRelocKind,
|
||||
pub symbol: usize,
|
||||
pub addend: i32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct FunctionSignature {
|
||||
pub symbol: usize,
|
||||
pub hash: String,
|
||||
pub signature: String,
|
||||
pub symbols: Vec<OutSymbol>,
|
||||
pub relocations: Vec<OutReloc>,
|
||||
}
|
||||
|
||||
pub fn check_signature(mut data: &[u8], sig: &FunctionSignature) -> Result<bool> {
|
||||
let sig_data = STANDARD.decode(&sig.signature)?;
|
||||
// println!(
|
||||
// "\nChecking signature {} {} (size {})",
|
||||
// sig.symbols[sig.symbol].name, sig.hash, sig.symbols[sig.symbol].size
|
||||
// );
|
||||
// for chunk in sig_data.chunks_exact(8) {
|
||||
// let ins = u32::from_be_bytes(*array_ref!(chunk, 0, 4));
|
||||
// let i = Ins::new(ins, 0);
|
||||
// println!("=> {}", i.simplified());
|
||||
// }
|
||||
for chunk in sig_data.chunks_exact(8) {
|
||||
let ins = u32::from_be_bytes(*array_ref!(chunk, 0, 4));
|
||||
let pat = u32::from_be_bytes(*array_ref!(chunk, 4, 4));
|
||||
if (u32::from_be_bytes(*array_ref!(data, 0, 4)) & pat) != ins {
|
||||
return Ok(false);
|
||||
}
|
||||
data = &data[4..];
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
pub fn parse_signatures(sig_str: &str) -> Result<Vec<FunctionSignature>> {
|
||||
Ok(serde_yaml::from_str(sig_str)?)
|
||||
}
|
||||
|
||||
pub fn check_signatures_str(
|
||||
obj: &ObjInfo,
|
||||
addr: u32,
|
||||
sig_str: &str,
|
||||
) -> Result<Option<FunctionSignature>> {
|
||||
check_signatures(obj, addr, &parse_signatures(sig_str)?)
|
||||
}
|
||||
|
||||
pub fn check_signatures(
|
||||
obj: &ObjInfo,
|
||||
addr: u32,
|
||||
signatures: &Vec<FunctionSignature>,
|
||||
) -> Result<Option<FunctionSignature>> {
|
||||
let (_, data) = obj.section_data(addr, 0)?;
|
||||
let mut name = None;
|
||||
for signature in signatures {
|
||||
if name.is_none() {
|
||||
name = Some(signature.symbols[signature.symbol].name.clone());
|
||||
}
|
||||
if check_signature(data, signature)? {
|
||||
log::debug!(
|
||||
"Found {} @ {:#010X} (hash {})",
|
||||
signature.symbols[signature.symbol].name,
|
||||
addr,
|
||||
signature.hash
|
||||
);
|
||||
return Ok(Some(signature.clone()));
|
||||
}
|
||||
}
|
||||
// if let Some(name) = name {
|
||||
// log::debug!("Didn't find {} @ {:#010X}", name, addr);
|
||||
// }
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> Result<usize> {
|
||||
let mut target_section_index = obj.section_at(target).ok().map(|section| section.index);
|
||||
if let Some(target_section_index) = target_section_index {
|
||||
let target_section = &mut obj.sections[target_section_index];
|
||||
if !target_section.section_known {
|
||||
if let Some(section_name) = &sig_symbol.section {
|
||||
target_section.rename(section_name.clone())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
if sig_symbol.kind == ObjSymbolKind::Unknown
|
||||
&& (sig_symbol.name.starts_with("_f_") || sig_symbol.name.starts_with("_SDA"))
|
||||
{
|
||||
// Hack to mark linker generated symbols as ABS
|
||||
target_section_index = None;
|
||||
}
|
||||
let demangled_name = demangle(&sig_symbol.name, &DemangleOptions::default());
|
||||
let target_symbol_idx = obj.add_symbol(
|
||||
ObjSymbol {
|
||||
name: sig_symbol.name.clone(),
|
||||
demangled_name,
|
||||
address: target as u64,
|
||||
section: target_section_index,
|
||||
size: sig_symbol.size as u64,
|
||||
size_known: sig_symbol.size > 0 || sig_symbol.kind == ObjSymbolKind::Unknown,
|
||||
flags: sig_symbol.flags,
|
||||
kind: sig_symbol.kind,
|
||||
align: None,
|
||||
data_kind: Default::default(),
|
||||
},
|
||||
true,
|
||||
)?;
|
||||
Ok(target_symbol_idx)
|
||||
}
|
||||
|
||||
pub fn apply_signature(obj: &mut ObjInfo, addr: u32, signature: &FunctionSignature) -> Result<()> {
|
||||
let section_index = obj.section_at(addr)?.index;
|
||||
let in_symbol = &signature.symbols[signature.symbol];
|
||||
let symbol_idx = apply_symbol(obj, addr, in_symbol)?;
|
||||
let mut tracker = Tracker::new(obj);
|
||||
for reloc in &signature.relocations {
|
||||
tracker.known_relocations.insert(addr + reloc.offset);
|
||||
}
|
||||
tracker.process_function(obj, obj.symbols.at(symbol_idx))?;
|
||||
for (&reloc_addr, reloc) in &tracker.relocations {
|
||||
if reloc_addr < addr || reloc_addr >= addr + in_symbol.size {
|
||||
continue;
|
||||
}
|
||||
let offset = reloc_addr - addr;
|
||||
let sig_reloc = match signature.relocations.iter().find(|r| r.offset == offset) {
|
||||
Some(reloc) => reloc,
|
||||
None => continue,
|
||||
};
|
||||
let target = match (reloc, sig_reloc.kind) {
|
||||
(&Relocation::Absolute(addr), ObjRelocKind::Absolute)
|
||||
| (&Relocation::Hi(addr), ObjRelocKind::PpcAddr16Hi)
|
||||
| (&Relocation::Ha(addr), ObjRelocKind::PpcAddr16Ha)
|
||||
| (&Relocation::Lo(addr), ObjRelocKind::PpcAddr16Lo)
|
||||
| (&Relocation::Rel24(addr), ObjRelocKind::PpcRel24)
|
||||
| (&Relocation::Rel14(addr), ObjRelocKind::PpcRel14)
|
||||
| (&Relocation::Sda21(addr), ObjRelocKind::PpcEmbSda21) => {
|
||||
(addr as i64 - sig_reloc.addend as i64) as u32
|
||||
}
|
||||
_ => bail!("Relocation mismatch: {:?} != {:?}", reloc, sig_reloc.kind),
|
||||
};
|
||||
let sig_symbol = &signature.symbols[sig_reloc.symbol];
|
||||
let target_symbol_idx = apply_symbol(obj, target, sig_symbol)?;
|
||||
let obj_reloc = ObjReloc {
|
||||
kind: sig_reloc.kind,
|
||||
address: reloc_addr as u64,
|
||||
target_symbol: target_symbol_idx,
|
||||
addend: sig_reloc.addend as i64,
|
||||
};
|
||||
// log::info!("Applying relocation {:#010X?}", obj_reloc);
|
||||
obj.sections[section_index].relocations.push(obj_reloc);
|
||||
}
|
||||
for reloc in &signature.relocations {
|
||||
let addr = addr + reloc.offset;
|
||||
if !tracker.relocations.contains_key(&addr) {
|
||||
let sig_symbol = &signature.symbols[reloc.symbol];
|
||||
bail!("Missing relocation @ {:#010X}: {:?} -> {:?}", addr, reloc, sig_symbol);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compare_signature(existing: &mut FunctionSignature, new: &FunctionSignature) -> Result<()> {
|
||||
ensure!(
|
||||
existing.symbols.len() == new.symbols.len(),
|
||||
"Mismatched symbol count: {} != {}\n{:?}\n{:?}",
|
||||
new.symbols.len(),
|
||||
existing.symbols.len(),
|
||||
new.symbols,
|
||||
existing.symbols,
|
||||
);
|
||||
ensure!(
|
||||
existing.relocations.len() == new.relocations.len(),
|
||||
"Mismatched relocation count: {} != {}",
|
||||
new.relocations.len(),
|
||||
existing.relocations.len()
|
||||
);
|
||||
for (idx, (a, b)) in existing.symbols.iter_mut().zip(&new.symbols).enumerate() {
|
||||
if a != b {
|
||||
// If mismatched sections, clear
|
||||
if a.name == b.name
|
||||
&& a.size == b.size
|
||||
&& a.flags == b.flags
|
||||
&& a.kind == b.kind
|
||||
&& a.section != b.section
|
||||
{
|
||||
log::warn!("Clearing section for {} ({:?} != {:?})", a.name, a.section, b.section);
|
||||
a.section = None;
|
||||
} else if !a.name.starts_with('@') {
|
||||
log::error!("Symbol {} mismatch: {:?} != {:?}", idx, a, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (a, b) in existing.relocations.iter().zip(&new.relocations) {
|
||||
if a != b {
|
||||
log::error!("Relocation {} mismatch: {:?} != {:?}", a.offset, a, b);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn generate_signature<P: AsRef<Path>>(
|
||||
path: P,
|
||||
symbol_name: &str,
|
||||
) -> Result<Option<FunctionSignature>> {
|
||||
let mut out_symbols: Vec<OutSymbol> = Vec::new();
|
||||
let mut out_relocs: Vec<OutReloc> = Vec::new();
|
||||
let mut symbol_map: BTreeMap<usize, usize> = BTreeMap::new();
|
||||
|
||||
let mut obj = process_elf(path)?;
|
||||
if obj.sda2_base.is_none()
|
||||
|| obj.sda_base.is_none()
|
||||
|| obj.stack_address.is_none()
|
||||
|| obj.stack_end.is_none()
|
||||
|| obj.db_stack_addr.is_none()
|
||||
{
|
||||
log::warn!(
|
||||
"Failed to locate all abs symbols {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?} {:#010X?}",
|
||||
obj.sda2_base,
|
||||
obj.sda_base,
|
||||
obj.stack_address,
|
||||
obj.stack_end,
|
||||
obj.db_stack_addr,
|
||||
obj.arena_hi,
|
||||
obj.arena_lo
|
||||
);
|
||||
return Ok(None);
|
||||
}
|
||||
let mut tracker = Tracker::new(&obj);
|
||||
// tracker.ignore_addresses.insert(0x80004000);
|
||||
for (_, symbol) in obj.symbols.by_kind(ObjSymbolKind::Function) {
|
||||
if symbol.name != symbol_name && symbol.name != symbol_name.replace("TRK", "TRK_") {
|
||||
continue;
|
||||
}
|
||||
tracker.process_function(&obj, symbol)?;
|
||||
}
|
||||
tracker.apply(&mut obj, true)?; // true
|
||||
for (_, symbol) in obj.symbols.by_kind(ObjSymbolKind::Function) {
|
||||
if symbol.name != symbol_name && symbol.name != symbol_name.replace("TRK", "TRK_") {
|
||||
continue;
|
||||
}
|
||||
let section_idx = symbol.section.unwrap();
|
||||
let section = &obj.sections[section_idx];
|
||||
// let out_symbol_idx = out_symbols.len();
|
||||
out_symbols.push(OutSymbol {
|
||||
kind: symbol.kind,
|
||||
name: symbol.name.clone(),
|
||||
size: symbol.size as u32,
|
||||
flags: symbol.flags,
|
||||
section: Some(section.name.clone()),
|
||||
});
|
||||
// println!(
|
||||
// "Building signature for {} ({:#010X}-{:#010X})",
|
||||
// symbol.name,
|
||||
// symbol.address,
|
||||
// symbol.address + symbol.size
|
||||
// );
|
||||
let relocations = section.build_relocation_map()?;
|
||||
let mut instructions = section.data[(symbol.address - section.address) as usize
|
||||
..(symbol.address - section.address + symbol.size) as usize]
|
||||
.chunks_exact(4)
|
||||
.map(|c| (u32::from_be_bytes(c.try_into().unwrap()), !0u32))
|
||||
.collect::<Vec<(u32, u32)>>();
|
||||
for (idx, (ins, pat)) in instructions.iter_mut().enumerate() {
|
||||
let addr = (symbol.address as usize + idx * 4) as u32;
|
||||
if let Some(&reloc_idx) = relocations.get(&addr) {
|
||||
let reloc = §ion.relocations[reloc_idx];
|
||||
let symbol_idx = match symbol_map.entry(reloc.target_symbol) {
|
||||
btree_map::Entry::Vacant(e) => {
|
||||
let target = obj.symbols.at(reloc.target_symbol);
|
||||
let symbol_idx = out_symbols.len();
|
||||
e.insert(symbol_idx);
|
||||
out_symbols.push(OutSymbol {
|
||||
kind: target.kind,
|
||||
name: target.name.clone(),
|
||||
size: if target.kind == ObjSymbolKind::Function {
|
||||
0
|
||||
} else {
|
||||
target.size as u32
|
||||
},
|
||||
flags: target.flags,
|
||||
section: target.section.map(|idx| obj.sections[idx].name.clone()),
|
||||
});
|
||||
symbol_idx
|
||||
}
|
||||
btree_map::Entry::Occupied(e) => *e.get(),
|
||||
};
|
||||
match reloc.kind {
|
||||
ObjRelocKind::Absolute => {
|
||||
*ins = 0;
|
||||
*pat = 0;
|
||||
}
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => {
|
||||
*ins &= !0xFFFF;
|
||||
*pat = !0xFFFF;
|
||||
}
|
||||
ObjRelocKind::PpcRel24 => {
|
||||
*ins &= !0x3FFFFFC;
|
||||
*pat = !0x3FFFFFC;
|
||||
}
|
||||
ObjRelocKind::PpcRel14 => {
|
||||
*ins &= !0xFFFC;
|
||||
*pat = !0xFFFC;
|
||||
}
|
||||
ObjRelocKind::PpcEmbSda21 => {
|
||||
*ins &= !0x1FFFFF;
|
||||
*pat = !0x1FFFFF;
|
||||
}
|
||||
}
|
||||
out_relocs.push(OutReloc {
|
||||
offset: addr - (symbol.address as u32),
|
||||
kind: reloc.kind,
|
||||
symbol: symbol_idx,
|
||||
addend: reloc.addend as i32,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let mut data = vec![0u8; instructions.len() * 8];
|
||||
for (idx, &(ins, pat)) in instructions.iter().enumerate() {
|
||||
data[idx * 8..idx * 8 + 4].copy_from_slice(&ins.to_be_bytes());
|
||||
data[idx * 8 + 4..idx * 8 + 8].copy_from_slice(&pat.to_be_bytes());
|
||||
}
|
||||
|
||||
let encoded = STANDARD.encode(&data);
|
||||
let mut hasher = Sha1::new();
|
||||
hasher.update(&data);
|
||||
let hash = hasher.finalize();
|
||||
let mut hash_buf = [0u8; 40];
|
||||
let hash_str = base16ct::lower::encode_str(&hash, &mut hash_buf)
|
||||
.map_err(|e| anyhow!("Failed to encode hash: {e}"))?;
|
||||
return Ok(Some(FunctionSignature {
|
||||
symbol: 0,
|
||||
hash: hash_str.to_string(),
|
||||
signature: encoded,
|
||||
symbols: out_symbols,
|
||||
relocations: out_relocs,
|
||||
}));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
991
src/obj/split.rs
991
src/obj/split.rs
@@ -1,991 +0,0 @@
|
||||
use std::{
|
||||
cmp::min,
|
||||
collections::{BTreeMap, HashMap, HashSet},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use itertools::Itertools;
|
||||
use petgraph::{graph::NodeIndex, Graph};
|
||||
|
||||
use crate::{
|
||||
obj::{
|
||||
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSplit,
|
||||
ObjSymbol, ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind, ObjSymbolScope, ObjUnit,
|
||||
},
|
||||
util::comment::MWComment,
|
||||
};
|
||||
|
||||
/// Create splits for function pointers in the given section.
|
||||
fn split_ctors_dtors(obj: &mut ObjInfo, section_start: u32, section_end: u32) -> Result<()> {
|
||||
let mut new_splits = BTreeMap::new();
|
||||
let mut current_address = section_start;
|
||||
let mut referenced_symbols = vec![];
|
||||
|
||||
while current_address < section_end {
|
||||
let (section, chunk) = obj.section_data(current_address, current_address + 4)?;
|
||||
let function_addr = u32::from_be_bytes(chunk[0..4].try_into().unwrap());
|
||||
log::debug!("Found {} entry: {:#010X}", section.name, function_addr);
|
||||
|
||||
let Some((function_symbol_idx, function_symbol)) =
|
||||
obj.symbols.kind_at_address(function_addr, ObjSymbolKind::Function)?
|
||||
else {
|
||||
bail!("Failed to find function symbol @ {:#010X}", function_addr);
|
||||
};
|
||||
referenced_symbols.push(function_symbol_idx);
|
||||
|
||||
let ctors_split = obj.split_for(current_address);
|
||||
let function_split = obj.split_for(function_addr);
|
||||
|
||||
let mut expected_unit = None;
|
||||
if let Some((_, ctors_split)) = ctors_split {
|
||||
expected_unit = Some(ctors_split.unit.clone());
|
||||
}
|
||||
if let Some((_, function_split)) = function_split {
|
||||
if let Some(unit) = &expected_unit {
|
||||
ensure!(
|
||||
unit == &function_split.unit,
|
||||
"Mismatched splits for {} {:#010X} ({}) and function {:#010X} ({})",
|
||||
section.name,
|
||||
current_address,
|
||||
unit,
|
||||
function_addr,
|
||||
function_split.unit
|
||||
);
|
||||
} else {
|
||||
expected_unit = Some(function_split.unit.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if ctors_split.is_none() || function_split.is_none() {
|
||||
let unit = expected_unit.unwrap_or_else(|| {
|
||||
let section_name = function_symbol
|
||||
.section
|
||||
.and_then(|idx| obj.sections.get(idx).map(|s| s.name.clone()))
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
format!("{}_{}", function_symbol.name, section_name.trim_start_matches('.'))
|
||||
});
|
||||
log::debug!("Adding splits to unit {}", unit);
|
||||
|
||||
if ctors_split.is_none() {
|
||||
log::debug!("Adding split for {} entry @ {:#010X}", section.name, current_address);
|
||||
new_splits.insert(current_address, ObjSplit {
|
||||
unit: unit.clone(),
|
||||
end: current_address + 4,
|
||||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
});
|
||||
}
|
||||
if function_split.is_none() {
|
||||
log::debug!("Adding split for function @ {:#010X}", function_addr);
|
||||
new_splits.insert(function_addr, ObjSplit {
|
||||
unit,
|
||||
end: function_addr + function_symbol.size as u32,
|
||||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
current_address += 4;
|
||||
}
|
||||
|
||||
for (addr, split) in new_splits {
|
||||
obj.add_split(addr, split)?;
|
||||
}
|
||||
|
||||
// Hack to avoid deadstripping
|
||||
for symbol_idx in referenced_symbols {
|
||||
obj.symbols.flags(symbol_idx).set_force_active(true);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create splits for extabindex + extab entries.
|
||||
fn split_extabindex(obj: &mut ObjInfo, section_index: usize, section_start: u32) -> Result<()> {
|
||||
let mut new_splits = BTreeMap::new();
|
||||
let (_, eti_init_info) = obj
|
||||
.symbols
|
||||
.by_name("_eti_init_info")?
|
||||
.ok_or_else(|| anyhow!("Failed to find _eti_init_info symbol"))?;
|
||||
ensure!(
|
||||
eti_init_info.section == Some(section_index),
|
||||
"_eti_init_info symbol in the wrong section: {:?} != {}",
|
||||
eti_init_info.section,
|
||||
section_index
|
||||
);
|
||||
let mut current_address = section_start;
|
||||
let section_end = eti_init_info.address as u32;
|
||||
while current_address < section_end {
|
||||
let (_eti_section, chunk) = obj.section_data(current_address, current_address + 12)?;
|
||||
let function_addr = u32::from_be_bytes(chunk[0..4].try_into().unwrap());
|
||||
let function_size = u32::from_be_bytes(chunk[4..8].try_into().unwrap());
|
||||
let extab_addr = u32::from_be_bytes(chunk[8..12].try_into().unwrap());
|
||||
log::debug!(
|
||||
"Found extabindex entry: {:#010X} size {:#010X} extab {:#010X}",
|
||||
function_addr,
|
||||
function_size,
|
||||
extab_addr
|
||||
);
|
||||
|
||||
let Some((_, eti_symbol)) =
|
||||
obj.symbols.kind_at_address(current_address, ObjSymbolKind::Object)?
|
||||
else {
|
||||
bail!("Failed to find extabindex symbol @ {:#010X}", current_address);
|
||||
};
|
||||
ensure!(
|
||||
eti_symbol.size_known && eti_symbol.size == 12,
|
||||
"extabindex symbol {} has mismatched size ({:#X}, expected {:#X})",
|
||||
eti_symbol.name,
|
||||
eti_symbol.size,
|
||||
12
|
||||
);
|
||||
|
||||
let Some((_, function_symbol)) =
|
||||
obj.symbols.kind_at_address(function_addr, ObjSymbolKind::Function)?
|
||||
else {
|
||||
bail!("Failed to find function symbol @ {:#010X}", function_addr);
|
||||
};
|
||||
ensure!(
|
||||
function_symbol.size_known && function_symbol.size == function_size as u64,
|
||||
"Function symbol {} has mismatched size ({:#X}, expected {:#X})",
|
||||
function_symbol.name,
|
||||
function_symbol.size,
|
||||
function_size
|
||||
);
|
||||
|
||||
let Some((_, extab_symbol)) =
|
||||
obj.symbols.kind_at_address(extab_addr, ObjSymbolKind::Object)?
|
||||
else {
|
||||
bail!("Failed to find extab symbol @ {:#010X}", extab_addr);
|
||||
};
|
||||
ensure!(
|
||||
extab_symbol.size_known && extab_symbol.size > 0,
|
||||
"extab symbol {} has unknown size",
|
||||
extab_symbol.name
|
||||
);
|
||||
|
||||
let extabindex_split = obj.split_for(current_address);
|
||||
let extab_split = obj.split_for(extab_addr);
|
||||
let function_split = obj.split_for(function_addr);
|
||||
|
||||
let mut expected_unit = None;
|
||||
if let Some((_, extabindex_split)) = extabindex_split {
|
||||
expected_unit = Some(extabindex_split.unit.clone());
|
||||
}
|
||||
if let Some((_, extab_split)) = extab_split {
|
||||
if let Some(unit) = &expected_unit {
|
||||
ensure!(
|
||||
unit == &extab_split.unit,
|
||||
"Mismatched splits for extabindex {:#010X} ({}) and extab {:#010X} ({})",
|
||||
current_address,
|
||||
unit,
|
||||
extab_addr,
|
||||
extab_split.unit
|
||||
);
|
||||
} else {
|
||||
expected_unit = Some(extab_split.unit.clone());
|
||||
}
|
||||
}
|
||||
if let Some((_, function_split)) = function_split {
|
||||
if let Some(unit) = &expected_unit {
|
||||
ensure!(
|
||||
unit == &function_split.unit,
|
||||
"Mismatched splits for extabindex {:#010X} ({}) and function {:#010X} ({})",
|
||||
current_address,
|
||||
unit,
|
||||
function_addr,
|
||||
function_split.unit
|
||||
);
|
||||
} else {
|
||||
expected_unit = Some(function_split.unit.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if extabindex_split.is_none() || extab_split.is_none() || function_split.is_none() {
|
||||
let unit = expected_unit.unwrap_or_else(|| {
|
||||
let section_name = function_symbol
|
||||
.section
|
||||
.and_then(|idx| obj.sections.get(idx).map(|s| s.name.clone()))
|
||||
.unwrap_or_else(|| "unknown".to_string());
|
||||
format!("{}_{}", function_symbol.name, section_name.trim_start_matches('.'))
|
||||
});
|
||||
log::debug!("Adding splits to unit {}", unit);
|
||||
|
||||
if extabindex_split.is_none() {
|
||||
let end = current_address + 12;
|
||||
log::debug!(
|
||||
"Adding split for extabindex entry @ {:#010X}-{:#010X}",
|
||||
current_address,
|
||||
end
|
||||
);
|
||||
new_splits.insert(current_address, ObjSplit {
|
||||
unit: unit.clone(),
|
||||
end,
|
||||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
});
|
||||
}
|
||||
if extab_split.is_none() {
|
||||
let end = extab_addr + extab_symbol.size as u32;
|
||||
log::debug!("Adding split for extab @ {:#010X}-{:#010X}", extab_addr, end);
|
||||
new_splits.insert(extab_addr, ObjSplit {
|
||||
unit: unit.clone(),
|
||||
end,
|
||||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
});
|
||||
}
|
||||
if function_split.is_none() {
|
||||
let end = function_addr + function_symbol.size as u32;
|
||||
log::debug!("Adding split for function @ {:#010X}-{:#010X}", function_addr, end);
|
||||
new_splits.insert(function_addr, ObjSplit {
|
||||
unit,
|
||||
end,
|
||||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
current_address += 12;
|
||||
}
|
||||
|
||||
for (addr, split) in new_splits {
|
||||
obj.add_split(addr, split)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Create splits for gaps between existing splits.
|
||||
fn create_gap_splits(obj: &mut ObjInfo) -> Result<()> {
|
||||
let mut new_splits = BTreeMap::new();
|
||||
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
let mut current_address = section.address as u32;
|
||||
let section_end = end_for_section(obj, section_idx)?;
|
||||
let mut file_iter = obj.splits_for_range(current_address..section_end).peekable();
|
||||
|
||||
log::debug!(
|
||||
"Checking splits for section {} ({:#010X}..{:#010X})",
|
||||
section.name,
|
||||
current_address,
|
||||
section_end
|
||||
);
|
||||
loop {
|
||||
if current_address >= section_end {
|
||||
break;
|
||||
}
|
||||
|
||||
let (split_start, split_end) = match file_iter.peek() {
|
||||
Some(&(addr, split)) => {
|
||||
log::debug!("Found split {} ({:#010X}..{:#010X})", split.unit, addr, split.end);
|
||||
(addr, split.end)
|
||||
}
|
||||
None => (section_end, 0),
|
||||
};
|
||||
ensure!(
|
||||
split_start >= current_address,
|
||||
"Split {:#010X}..{:#010X} overlaps with previous split",
|
||||
split_start,
|
||||
split_end
|
||||
);
|
||||
|
||||
if split_start > current_address {
|
||||
// Find any duplicate symbols in this range
|
||||
let mut new_split_end = split_start;
|
||||
let symbols = obj.symbols.for_range(current_address..split_start).collect_vec();
|
||||
let mut existing_symbols = HashSet::new();
|
||||
for (_, symbol) in symbols {
|
||||
// Sanity check? Maybe not required?
|
||||
ensure!(
|
||||
symbol.section == Some(section_idx),
|
||||
"Expected symbol {} to be in section {}",
|
||||
symbol.name,
|
||||
section_idx
|
||||
);
|
||||
if !existing_symbols.insert(symbol.name.clone()) {
|
||||
log::debug!(
|
||||
"Found duplicate symbol {} at {:#010X}",
|
||||
symbol.name,
|
||||
symbol.address
|
||||
);
|
||||
new_split_end = symbol.address as u32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
log::debug!(
|
||||
"Creating split from {:#010X}..{:#010X}",
|
||||
current_address,
|
||||
new_split_end
|
||||
);
|
||||
let unit =
|
||||
format!("{:08X}_{}", current_address, section.name.trim_start_matches('.'));
|
||||
new_splits.insert(current_address, ObjSplit {
|
||||
unit: unit.clone(),
|
||||
end: new_split_end,
|
||||
align: None,
|
||||
common: false,
|
||||
autogenerated: true,
|
||||
});
|
||||
current_address = new_split_end;
|
||||
continue;
|
||||
}
|
||||
|
||||
file_iter.next();
|
||||
if split_end > 0 {
|
||||
current_address = split_end;
|
||||
} else {
|
||||
let mut file_end = section_end;
|
||||
if let Some(&(next_addr, _next_split)) = file_iter.peek() {
|
||||
file_end = min(next_addr, section_end);
|
||||
}
|
||||
current_address = file_end;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add new splits
|
||||
for (addr, split) in new_splits {
|
||||
obj.add_split(addr, split)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Ensures that all .bss splits following a common split are also marked as common.
|
||||
fn update_common_splits(obj: &mut ObjInfo) -> Result<()> {
|
||||
let Some(bss_section) = obj.sections.iter().find(|s| s.name == ".bss") else {
|
||||
return Ok(());
|
||||
};
|
||||
let bss_section_start = bss_section.address as u32;
|
||||
let bss_section_end = (bss_section.address + bss_section.size) as u32;
|
||||
let Some(common_bss_start) = obj
|
||||
.splits_for_range(bss_section_start..bss_section_end)
|
||||
.find(|(_, split)| split.common)
|
||||
.map(|(addr, _)| addr)
|
||||
else {
|
||||
return Ok(());
|
||||
};
|
||||
log::debug!("Found common BSS start at {:#010X}", common_bss_start);
|
||||
for (addr, vec) in obj.splits.range_mut(common_bss_start..bss_section_end) {
|
||||
for split in vec {
|
||||
if !split.common {
|
||||
split.common = true;
|
||||
log::debug!("Added common flag to split {} at {:#010X}", split.unit, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Final validation of splits.
|
||||
fn validate_splits(obj: &ObjInfo) -> Result<()> {
|
||||
let mut last_split_end = 0;
|
||||
for (addr, split) in obj.splits_for_range(..) {
|
||||
let section = obj.section_at(addr)?;
|
||||
ensure!(
|
||||
addr >= last_split_end,
|
||||
"Split {} {} {:#010X}..{:#010X} overlaps with previous split",
|
||||
split.unit,
|
||||
section.name,
|
||||
addr,
|
||||
split.end
|
||||
);
|
||||
ensure!(
|
||||
split.end > 0 && split.end > addr,
|
||||
"Invalid split end {} {} {:#010X}..{:#010X}",
|
||||
split.unit,
|
||||
section.name,
|
||||
addr,
|
||||
split.end
|
||||
);
|
||||
last_split_end = split.end;
|
||||
|
||||
if let Some((_, symbol)) =
|
||||
obj.symbols.for_range(..addr).filter(|&(_, s)| s.size_known && s.size > 0).next_back()
|
||||
{
|
||||
ensure!(
|
||||
addr >= symbol.address as u32 + symbol.size as u32,
|
||||
"Split {} {} {:#010X}..{:#010X} overlaps symbol '{}' {:#010X}..{:#010X}",
|
||||
split.unit,
|
||||
section.name,
|
||||
addr,
|
||||
split.end,
|
||||
symbol.name,
|
||||
symbol.address,
|
||||
symbol.address + symbol.size
|
||||
);
|
||||
}
|
||||
|
||||
if let Some((_, symbol)) = obj
|
||||
.symbols
|
||||
.for_range(..split.end)
|
||||
.filter(|&(_, s)| s.size_known && s.size > 0)
|
||||
.next_back()
|
||||
{
|
||||
ensure!(
|
||||
split.end >= symbol.address as u32 + symbol.size as u32,
|
||||
"Split {} {} ({:#010X}..{:#010X}) ends within symbol '{}' ({:#010X}..{:#010X})",
|
||||
split.unit,
|
||||
section.name,
|
||||
addr,
|
||||
split.end,
|
||||
symbol.name,
|
||||
symbol.address,
|
||||
symbol.address + symbol.size
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform any necessary adjustments to allow relinking.
|
||||
/// This includes:
|
||||
/// - Ensuring .ctors & .dtors entries are split with their associated function
|
||||
/// - Ensuring extab & extabindex entries are split with their associated function
|
||||
/// - Creating splits for gaps between existing splits
|
||||
/// - Resolving a new object link order
|
||||
pub fn update_splits(obj: &mut ObjInfo) -> Result<()> {
|
||||
// Create splits for extab and extabindex entries
|
||||
if let Some(section) = obj.sections.iter().find(|s| s.name == "extabindex") {
|
||||
split_extabindex(obj, section.index, section.address as u32)?;
|
||||
}
|
||||
|
||||
// Create splits for .ctors entries
|
||||
if let Some(section) = obj.sections.iter().find(|s| s.name == ".ctors") {
|
||||
let section_start = section.address as u32;
|
||||
let section_end = section.address as u32 + section.size as u32 - 4;
|
||||
split_ctors_dtors(obj, section_start, section_end)?;
|
||||
}
|
||||
|
||||
// Create splits for .dtors entries
|
||||
if let Some(section) = obj.sections.iter().find(|s| s.name == ".dtors") {
|
||||
let section_start = section.address as u32 + 4; // skip __destroy_global_chain_reference
|
||||
let section_end = section.address as u32 + section.size as u32 - 4;
|
||||
split_ctors_dtors(obj, section_start, section_end)?;
|
||||
}
|
||||
|
||||
// Create gap splits
|
||||
create_gap_splits(obj)?;
|
||||
|
||||
// Update common BSS splits
|
||||
update_common_splits(obj)?;
|
||||
|
||||
// Ensure splits don't overlap symbols or each other
|
||||
validate_splits(obj)?;
|
||||
|
||||
// Resolve link order
|
||||
obj.link_order = resolve_link_order(obj)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// The ordering of TUs inside of each section represents a directed edge in a DAG.
|
||||
/// We can use a topological sort to determine a valid global TU order.
|
||||
/// There can be ambiguities, but any solution that satisfies the link order
|
||||
/// constraints is considered valid.
|
||||
fn resolve_link_order(obj: &ObjInfo) -> Result<Vec<ObjUnit>> {
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct SplitEdge {
|
||||
from: u32,
|
||||
to: u32,
|
||||
}
|
||||
|
||||
let mut graph = Graph::<String, SplitEdge>::new();
|
||||
let mut unit_to_index_map = BTreeMap::<String, NodeIndex>::new();
|
||||
for (_, split) in obj.splits_for_range(..) {
|
||||
unit_to_index_map.insert(split.unit.clone(), NodeIndex::new(0));
|
||||
}
|
||||
for (unit, index) in unit_to_index_map.iter_mut() {
|
||||
*index = graph.add_node(unit.clone());
|
||||
}
|
||||
|
||||
for section in &obj.sections {
|
||||
let mut iter = obj
|
||||
.splits_for_range(section.address as u32..(section.address + section.size) as u32)
|
||||
.peekable();
|
||||
if section.name == ".ctors" || section.name == ".dtors" {
|
||||
// Skip __init_cpp_exceptions.o
|
||||
let skipped = iter.next();
|
||||
log::debug!("Skipping split {:?} (next: {:?})", skipped, iter.peek());
|
||||
}
|
||||
while let (Some((a_addr, a)), Some(&(b_addr, b))) = (iter.next(), iter.peek()) {
|
||||
if !a.common && b.common {
|
||||
// This marks the beginning of the common BSS section.
|
||||
continue;
|
||||
}
|
||||
|
||||
if a.unit != b.unit {
|
||||
log::debug!(
|
||||
"Adding dependency {} ({:#010X}) -> {} ({:#010X})",
|
||||
a.unit,
|
||||
a_addr,
|
||||
b.unit,
|
||||
b_addr
|
||||
);
|
||||
let a_index = *unit_to_index_map.get(&a.unit).unwrap();
|
||||
let b_index = *unit_to_index_map.get(&b.unit).unwrap();
|
||||
graph.add_edge(a_index, b_index, SplitEdge { from: a_addr, to: b_addr });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// use petgraph::{
|
||||
// dot::{Config, Dot},
|
||||
// graph::EdgeReference,
|
||||
// };
|
||||
// let get_edge_attributes = |_, e: EdgeReference<SplitEdge>| {
|
||||
// let &SplitEdge { from, to } = e.weight();
|
||||
// let section_name = &obj.section_at(from).unwrap().name;
|
||||
// format!("label=\"{} {:#010X} -> {:#010X}\"", section_name, from, to)
|
||||
// };
|
||||
// let dot = Dot::with_attr_getters(
|
||||
// &graph,
|
||||
// &[Config::EdgeNoLabel, Config::NodeNoLabel],
|
||||
// &get_edge_attributes,
|
||||
// &|_, (_, s)| format!("label=\"{}\"", s),
|
||||
// );
|
||||
// println!("{:?}", dot);
|
||||
|
||||
match petgraph::algo::toposort(&graph, None) {
|
||||
Ok(vec) => Ok(vec
|
||||
.iter()
|
||||
.map(|&idx| {
|
||||
let name = &graph[idx];
|
||||
if let Some(existing) = obj.link_order.iter().find(|u| &u.name == name) {
|
||||
existing.clone()
|
||||
} else {
|
||||
ObjUnit {
|
||||
name: name.clone(),
|
||||
autogenerated: obj.is_unit_autogenerated(name),
|
||||
comment_version: None,
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect_vec()),
|
||||
Err(e) => Err(anyhow!(
|
||||
"Cyclic dependency (involving {}) encountered while resolving link order",
|
||||
graph[e.node_id()]
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Split an executable object into relocatable objects.
|
||||
pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
|
||||
ensure!(obj.kind == ObjKind::Executable, "Expected executable object");
|
||||
|
||||
let mut objects: Vec<ObjInfo> = vec![];
|
||||
let mut object_symbols: Vec<Vec<Option<usize>>> = vec![];
|
||||
let mut name_to_obj: HashMap<String, usize> = HashMap::new();
|
||||
for unit in &obj.link_order {
|
||||
name_to_obj.insert(unit.name.clone(), objects.len());
|
||||
object_symbols.push(vec![None; obj.symbols.count()]);
|
||||
let mut split_obj = ObjInfo::new(
|
||||
ObjKind::Relocatable,
|
||||
ObjArchitecture::PowerPc,
|
||||
unit.name.clone(),
|
||||
vec![],
|
||||
vec![],
|
||||
);
|
||||
if let Some(comment_version) = unit.comment_version {
|
||||
split_obj.mw_comment = Some(MWComment::new(comment_version)?);
|
||||
} else {
|
||||
split_obj.mw_comment = obj.mw_comment.clone();
|
||||
}
|
||||
objects.push(split_obj);
|
||||
}
|
||||
|
||||
for (section_idx, section) in obj.sections.iter().enumerate() {
|
||||
let mut current_address = section.address as u32;
|
||||
let section_end = end_for_section(obj, section_idx)?;
|
||||
let mut file_iter = obj.splits_for_range(current_address..section_end).peekable();
|
||||
|
||||
// Build address to relocation / address to symbol maps
|
||||
let relocations = section.build_relocation_map()?;
|
||||
|
||||
loop {
|
||||
if current_address >= section_end {
|
||||
break;
|
||||
}
|
||||
|
||||
let (file_addr, split) = match file_iter.next() {
|
||||
Some((addr, split)) => (addr, split),
|
||||
None => bail!("No file found"),
|
||||
};
|
||||
ensure!(
|
||||
file_addr <= current_address,
|
||||
"Gap in files: {} @ {:#010X}, {} @ {:#010X}",
|
||||
section.name,
|
||||
section.address,
|
||||
split.unit,
|
||||
file_addr
|
||||
);
|
||||
let mut file_end = section_end;
|
||||
if let Some(&(next_addr, _next_split)) = file_iter.peek() {
|
||||
file_end = min(next_addr, section_end);
|
||||
}
|
||||
|
||||
let file = name_to_obj
|
||||
.get(&split.unit)
|
||||
.and_then(|&idx| objects.get_mut(idx))
|
||||
.ok_or_else(|| anyhow!("Unit '{}' not in link order", split.unit))?;
|
||||
let symbol_idxs = name_to_obj
|
||||
.get(&split.unit)
|
||||
.and_then(|&idx| object_symbols.get_mut(idx))
|
||||
.ok_or_else(|| anyhow!("Unit '{}' not in link order", split.unit))?;
|
||||
|
||||
// Calculate & verify section alignment
|
||||
let mut align =
|
||||
split.align.map(u64::from).unwrap_or_else(|| default_section_align(section));
|
||||
if current_address & (align as u32 - 1) != 0 {
|
||||
log::warn!(
|
||||
"Alignment for {} {} expected {}, but starts at {:#010X}",
|
||||
split.unit,
|
||||
section.name,
|
||||
align,
|
||||
current_address
|
||||
);
|
||||
while align > 4 {
|
||||
align /= 2;
|
||||
if current_address & (align as u32 - 1) == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
ensure!(
|
||||
current_address & (align as u32 - 1) == 0,
|
||||
"Invalid alignment for split: {} {} {:#010X}",
|
||||
split.unit,
|
||||
section.name,
|
||||
current_address
|
||||
);
|
||||
|
||||
// Collect relocations; target_symbol will be updated later
|
||||
let out_relocations = relocations
|
||||
.range(current_address..file_end)
|
||||
.map(|(_, &idx)| {
|
||||
let o = §ion.relocations[idx];
|
||||
ObjReloc {
|
||||
kind: o.kind,
|
||||
address: o.address - current_address as u64,
|
||||
target_symbol: o.target_symbol,
|
||||
addend: o.addend,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Add section symbols
|
||||
let out_section_idx = file.sections.len();
|
||||
let mut comm_addr = current_address;
|
||||
for (symbol_idx, symbol) in obj.symbols.for_range(current_address..file_end) {
|
||||
if symbol_idxs[symbol_idx].is_some() {
|
||||
continue; // should never happen?
|
||||
}
|
||||
|
||||
if split.common && symbol.address as u32 > comm_addr {
|
||||
// HACK: Add padding for common bug
|
||||
file.symbols.add_direct(ObjSymbol {
|
||||
name: format!("pad_{:010X}", comm_addr),
|
||||
demangled_name: None,
|
||||
address: 0,
|
||||
section: None,
|
||||
size: symbol.address - comm_addr as u64,
|
||||
size_known: true,
|
||||
flags: ObjSymbolFlagSet(ObjSymbolFlags::Common.into()),
|
||||
kind: ObjSymbolKind::Object,
|
||||
align: Some(4),
|
||||
data_kind: Default::default(),
|
||||
})?;
|
||||
}
|
||||
comm_addr = (symbol.address + symbol.size) as u32;
|
||||
|
||||
symbol_idxs[symbol_idx] = Some(file.symbols.count());
|
||||
file.symbols.add_direct(ObjSymbol {
|
||||
name: symbol.name.clone(),
|
||||
demangled_name: symbol.demangled_name.clone(),
|
||||
address: if split.common { 4 } else { symbol.address - current_address as u64 },
|
||||
section: if split.common { None } else { Some(out_section_idx) },
|
||||
size: symbol.size,
|
||||
size_known: symbol.size_known,
|
||||
flags: if split.common {
|
||||
ObjSymbolFlagSet(ObjSymbolFlags::Common.into())
|
||||
} else {
|
||||
symbol.flags
|
||||
},
|
||||
kind: symbol.kind,
|
||||
align: if split.common { Some(4) } else { symbol.align },
|
||||
data_kind: symbol.data_kind,
|
||||
})?;
|
||||
}
|
||||
|
||||
// For mwldeppc 2.7 and above, a .comment section is required to link without error
|
||||
// when common symbols are present. Automatically add one if needed.
|
||||
if split.common && file.mw_comment.is_none() {
|
||||
file.mw_comment = Some(MWComment::new(8)?);
|
||||
}
|
||||
|
||||
if !split.common {
|
||||
let data = match section.kind {
|
||||
ObjSectionKind::Bss => vec![],
|
||||
_ => section.data[(current_address as u64 - section.address) as usize
|
||||
..(file_end as u64 - section.address) as usize]
|
||||
.to_vec(),
|
||||
};
|
||||
let name = if let Some(name) = obj.named_sections.get(¤t_address) {
|
||||
name.clone()
|
||||
} else {
|
||||
section.name.clone()
|
||||
};
|
||||
file.sections.push(ObjSection {
|
||||
name,
|
||||
kind: section.kind,
|
||||
address: 0,
|
||||
size: file_end as u64 - current_address as u64,
|
||||
data,
|
||||
align,
|
||||
index: out_section_idx,
|
||||
elf_index: out_section_idx + 1,
|
||||
relocations: out_relocations,
|
||||
original_address: current_address as u64,
|
||||
file_offset: section.file_offset + (current_address as u64 - section.address),
|
||||
section_known: true,
|
||||
});
|
||||
}
|
||||
|
||||
current_address = file_end;
|
||||
}
|
||||
}
|
||||
|
||||
// Update relocations
|
||||
let mut globalize_symbols = vec![];
|
||||
for (obj_idx, out_obj) in objects.iter_mut().enumerate() {
|
||||
let symbol_idxs = &mut object_symbols[obj_idx];
|
||||
for section in &mut out_obj.sections {
|
||||
for reloc in &mut section.relocations {
|
||||
match symbol_idxs[reloc.target_symbol] {
|
||||
Some(out_sym_idx) => {
|
||||
reloc.target_symbol = out_sym_idx;
|
||||
}
|
||||
None => {
|
||||
// Extern
|
||||
let out_sym_idx = out_obj.symbols.count();
|
||||
let target_sym = obj.symbols.at(reloc.target_symbol);
|
||||
|
||||
// If the symbol is local, we'll upgrade the scope to global
|
||||
// and rename it to avoid conflicts
|
||||
if target_sym.flags.is_local() {
|
||||
let address_str = format!("{:08X}", target_sym.address);
|
||||
let new_name = if target_sym.name.ends_with(&address_str) {
|
||||
target_sym.name.clone()
|
||||
} else {
|
||||
format!("{}_{}", target_sym.name, address_str)
|
||||
};
|
||||
globalize_symbols.push((reloc.target_symbol, new_name));
|
||||
}
|
||||
|
||||
symbol_idxs[reloc.target_symbol] = Some(out_sym_idx);
|
||||
out_obj.symbols.add_direct(ObjSymbol {
|
||||
name: target_sym.name.clone(),
|
||||
demangled_name: target_sym.demangled_name.clone(),
|
||||
..Default::default()
|
||||
})?;
|
||||
reloc.target_symbol = out_sym_idx;
|
||||
|
||||
if section.name.as_str() == "extabindex" {
|
||||
let Some((target_addr, target_split)) =
|
||||
obj.split_for(target_sym.address as u32)
|
||||
else {
|
||||
bail!(
|
||||
"Bad extabindex relocation @ {:#010X}",
|
||||
reloc.address + section.original_address
|
||||
);
|
||||
};
|
||||
let target_section = &obj.section_at(target_addr)?.name;
|
||||
log::error!(
|
||||
"Bad extabindex relocation @ {:#010X}\n\
|
||||
\tSource object: {}:{:#010X} ({})\n\
|
||||
\tTarget object: {}:{:#010X} ({})\n\
|
||||
\tTarget symbol: {:#010X} ({})\n\
|
||||
This will cause the linker to crash.\n",
|
||||
reloc.address + section.original_address,
|
||||
section.name,
|
||||
section.original_address,
|
||||
out_obj.name,
|
||||
target_section,
|
||||
target_addr,
|
||||
target_split.unit,
|
||||
target_sym.address,
|
||||
target_sym.demangled_name.as_deref().unwrap_or(&target_sym.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Upgrade local symbols to global if necessary
|
||||
for (obj, symbol_map) in objects.iter_mut().zip(&object_symbols) {
|
||||
for (globalize_idx, new_name) in &globalize_symbols {
|
||||
if let Some(symbol_idx) = symbol_map[*globalize_idx] {
|
||||
let mut symbol = obj.symbols.at(symbol_idx).clone();
|
||||
symbol.name = new_name.clone();
|
||||
if symbol.flags.is_local() {
|
||||
log::debug!("Globalizing {} in {}", symbol.name, obj.name);
|
||||
symbol.flags.set_scope(ObjSymbolScope::Global);
|
||||
}
|
||||
obj.symbols.replace(symbol_idx, symbol)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Extern linker generated symbols
|
||||
for obj in &mut objects {
|
||||
let mut replace_symbols = vec![];
|
||||
for (symbol_idx, symbol) in obj.symbols.iter().enumerate() {
|
||||
if is_linker_generated_label(&symbol.name) && symbol.section.is_some() {
|
||||
log::debug!("Externing {:?} in {}", symbol, obj.name);
|
||||
replace_symbols.push((symbol_idx, ObjSymbol {
|
||||
name: symbol.name.clone(),
|
||||
demangled_name: symbol.demangled_name.clone(),
|
||||
..Default::default()
|
||||
}));
|
||||
}
|
||||
}
|
||||
for (symbol_idx, symbol) in replace_symbols {
|
||||
obj.symbols.replace(symbol_idx, symbol)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(objects)
|
||||
}
|
||||
|
||||
/// mwld doesn't preserve the original section alignment values
|
||||
pub fn default_section_align(section: &ObjSection) -> u64 {
|
||||
match section.kind {
|
||||
ObjSectionKind::Code => 4,
|
||||
_ => match section.name.as_str() {
|
||||
".ctors" | ".dtors" | "extab" | "extabindex" => 4,
|
||||
".sbss" => 4, // ?
|
||||
_ => 8,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Linker-generated symbols to extern
|
||||
#[inline]
|
||||
pub fn is_linker_generated_label(name: &str) -> bool {
|
||||
matches!(
|
||||
name,
|
||||
"_ctors"
|
||||
| "_dtors"
|
||||
| "_f_init"
|
||||
| "_f_init_rom"
|
||||
| "_e_init"
|
||||
| "_fextab"
|
||||
| "_fextab_rom"
|
||||
| "_eextab"
|
||||
| "_fextabindex"
|
||||
| "_fextabindex_rom"
|
||||
| "_eextabindex"
|
||||
| "_f_text"
|
||||
| "_f_text_rom"
|
||||
| "_e_text"
|
||||
| "_f_ctors"
|
||||
| "_f_ctors_rom"
|
||||
| "_e_ctors"
|
||||
| "_f_dtors"
|
||||
| "_f_dtors_rom"
|
||||
| "_e_dtors"
|
||||
| "_f_rodata"
|
||||
| "_f_rodata_rom"
|
||||
| "_e_rodata"
|
||||
| "_f_data"
|
||||
| "_f_data_rom"
|
||||
| "_e_data"
|
||||
| "_f_sdata"
|
||||
| "_f_sdata_rom"
|
||||
| "_e_sdata"
|
||||
| "_f_sbss"
|
||||
| "_f_sbss_rom"
|
||||
| "_e_sbss"
|
||||
| "_f_sdata2"
|
||||
| "_f_sdata2_rom"
|
||||
| "_e_sdata2"
|
||||
| "_f_sbss2"
|
||||
| "_f_sbss2_rom"
|
||||
| "_e_sbss2"
|
||||
| "_f_bss"
|
||||
| "_f_bss_rom"
|
||||
| "_e_bss"
|
||||
| "_f_stack"
|
||||
| "_f_stack_rom"
|
||||
| "_e_stack"
|
||||
| "_stack_addr"
|
||||
| "_stack_end"
|
||||
| "_db_stack_addr"
|
||||
| "_db_stack_end"
|
||||
| "_heap_addr"
|
||||
| "_heap_end"
|
||||
| "_nbfunctions"
|
||||
| "SIZEOF_HEADERS"
|
||||
| "_SDA_BASE_"
|
||||
| "_SDA2_BASE_"
|
||||
| "_ABS_SDA_BASE_"
|
||||
| "_ABS_SDA2_BASE_"
|
||||
)
|
||||
}
|
||||
|
||||
/// Linker generated objects to strip entirely
|
||||
#[inline]
|
||||
pub fn is_linker_generated_object(name: &str) -> bool {
|
||||
matches!(
|
||||
name,
|
||||
"_eti_init_info" | "_rom_copy_info" | "_bss_init_info" | "_ctors$99" | "_dtors$99"
|
||||
)
|
||||
}
|
||||
|
||||
/// Locate the end address of a section when excluding linker generated objects
|
||||
pub fn end_for_section(obj: &ObjInfo, section_index: usize) -> Result<u32> {
|
||||
let section = &obj.sections[section_index];
|
||||
let section_start = section.address as u32;
|
||||
let mut section_end = (section.address + section.size) as u32;
|
||||
// .ctors and .dtors end with a linker-generated null pointer,
|
||||
// adjust section size appropriately
|
||||
if matches!(section.name.as_str(), ".ctors" | ".dtors")
|
||||
&& section.data[section.data.len() - 4..] == [0u8; 4]
|
||||
{
|
||||
section_end -= 4;
|
||||
return Ok(section_end);
|
||||
}
|
||||
loop {
|
||||
let last_symbol = obj
|
||||
.symbols
|
||||
.for_range(section_start..section_end)
|
||||
.filter(|(_, s)| s.kind == ObjSymbolKind::Object && s.size_known && s.size > 0)
|
||||
.next_back();
|
||||
match last_symbol {
|
||||
Some((_, symbol)) if is_linker_generated_object(&symbol.name) => {
|
||||
log::debug!(
|
||||
"Found {}, adjusting section {} end {:#010X} -> {:#010X}",
|
||||
section.name,
|
||||
symbol.name,
|
||||
section_end,
|
||||
symbol.address
|
||||
);
|
||||
section_end = symbol.address as u32;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
Ok(section_end)
|
||||
}
|
||||
76
src/obj/splits.rs
Normal file
76
src/obj/splits.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use std::{collections::BTreeMap, ops::RangeBounds};
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::util::nested::NestedVec;
|
||||
|
||||
/// Marks a split point within a section.
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub struct ObjSplit {
|
||||
pub unit: String,
|
||||
pub end: u32,
|
||||
pub align: Option<u32>,
|
||||
/// Whether this is a part of common BSS.
|
||||
pub common: bool,
|
||||
/// Generated, replaceable by user.
|
||||
pub autogenerated: bool,
|
||||
}
|
||||
|
||||
/// Splits within a section.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjSplits {
|
||||
splits: BTreeMap<u32, Vec<ObjSplit>>,
|
||||
}
|
||||
|
||||
impl ObjSplits {
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = (u32, &ObjSplit)> {
|
||||
self.splits.iter().flat_map(|(addr, v)| v.iter().map(move |u| (*addr, u)))
|
||||
}
|
||||
|
||||
pub fn iter_mut(&mut self) -> impl DoubleEndedIterator<Item = (u32, &mut ObjSplit)> {
|
||||
self.splits.iter_mut().flat_map(|(addr, v)| v.iter_mut().map(move |u| (*addr, u)))
|
||||
}
|
||||
|
||||
pub fn has_split_at(&self, address: u32) -> bool { self.splits.contains_key(&address) }
|
||||
|
||||
/// Locate an existing split for the given address.
|
||||
pub fn for_address(&self, address: u32) -> Option<(u32, &ObjSplit)> {
|
||||
match self.for_range(..=address).next_back() {
|
||||
Some((addr, split)) if split.end == 0 || split.end > address => Some((addr, split)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Locate existing splits within the given address range.
|
||||
pub fn for_range<R>(&self, range: R) -> impl DoubleEndedIterator<Item = (u32, &ObjSplit)>
|
||||
where R: RangeBounds<u32> {
|
||||
self.splits.range(range).flat_map(|(addr, v)| v.iter().map(move |u| (*addr, u)))
|
||||
}
|
||||
|
||||
/// Locate existing splits within the given address range.
|
||||
pub fn for_range_mut<R>(
|
||||
&mut self,
|
||||
range: R,
|
||||
) -> impl DoubleEndedIterator<Item = (u32, &mut ObjSplit)>
|
||||
where
|
||||
R: RangeBounds<u32>,
|
||||
{
|
||||
self.splits.range_mut(range).flat_map(|(addr, v)| v.iter_mut().map(move |u| (*addr, u)))
|
||||
}
|
||||
|
||||
pub fn for_unit(&self, unit: &str) -> Result<Option<(u32, &ObjSplit)>> {
|
||||
self.splits
|
||||
.iter()
|
||||
.flat_map(|(addr, v)| v.iter().map(move |u| (*addr, u)))
|
||||
.filter(|&(_, split)| split.unit == unit)
|
||||
.at_most_one()
|
||||
.map_err(|_| anyhow!("Multiple splits for unit {}", unit))
|
||||
}
|
||||
|
||||
pub fn push(&mut self, address: u32, split: ObjSplit) {
|
||||
self.splits.nested_push(address, split);
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, address: u32) -> Option<Vec<ObjSplit>> { self.splits.remove(&address) }
|
||||
}
|
||||
536
src/obj/symbols.rs
Normal file
536
src/obj/symbols.rs
Normal file
@@ -0,0 +1,536 @@
|
||||
use std::{
|
||||
collections::{BTreeMap, HashMap},
|
||||
hash::{Hash, Hasher},
|
||||
ops::{Index, RangeBounds},
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Result};
|
||||
use flagset::{flags, FlagSet};
|
||||
use itertools::Itertools;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
|
||||
use crate::{
|
||||
obj::{ObjKind, ObjRelocKind},
|
||||
util::{config::is_auto_symbol, nested::NestedVec, split::is_linker_generated_label},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default)]
|
||||
pub enum ObjSymbolScope {
|
||||
#[default]
|
||||
Unknown,
|
||||
Global,
|
||||
Weak,
|
||||
Local,
|
||||
}
|
||||
|
||||
flags! {
|
||||
#[repr(u8)]
|
||||
#[derive(Deserialize_repr, Serialize_repr)]
|
||||
pub enum ObjSymbolFlags: u8 {
|
||||
Global,
|
||||
Local,
|
||||
Weak,
|
||||
Common,
|
||||
Hidden,
|
||||
ForceActive,
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
|
||||
|
||||
impl ObjSymbolFlagSet {
|
||||
#[inline]
|
||||
pub fn scope(&self) -> ObjSymbolScope {
|
||||
if self.is_local() {
|
||||
ObjSymbolScope::Local
|
||||
} else if self.is_weak() {
|
||||
ObjSymbolScope::Weak
|
||||
} else if self.0.contains(ObjSymbolFlags::Global) {
|
||||
ObjSymbolScope::Global
|
||||
} else {
|
||||
ObjSymbolScope::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_local(&self) -> bool { self.0.contains(ObjSymbolFlags::Local) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_global(&self) -> bool { !self.is_local() }
|
||||
|
||||
#[inline]
|
||||
pub fn is_common(&self) -> bool { self.0.contains(ObjSymbolFlags::Common) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_weak(&self) -> bool { self.0.contains(ObjSymbolFlags::Weak) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_hidden(&self) -> bool { self.0.contains(ObjSymbolFlags::Hidden) }
|
||||
|
||||
#[inline]
|
||||
pub fn is_force_active(&self) -> bool { self.0.contains(ObjSymbolFlags::ForceActive) }
|
||||
|
||||
#[inline]
|
||||
pub fn set_scope(&mut self, scope: ObjSymbolScope) {
|
||||
match scope {
|
||||
ObjSymbolScope::Unknown => {
|
||||
self.0 &= !(ObjSymbolFlags::Local | ObjSymbolFlags::Global | ObjSymbolFlags::Weak)
|
||||
}
|
||||
ObjSymbolScope::Global => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Local | ObjSymbolFlags::Weak))
|
||||
| ObjSymbolFlags::Global
|
||||
}
|
||||
ObjSymbolScope::Weak => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Local | ObjSymbolFlags::Global))
|
||||
| ObjSymbolFlags::Weak
|
||||
}
|
||||
ObjSymbolScope::Local => {
|
||||
self.0 = (self.0 & !(ObjSymbolFlags::Global | ObjSymbolFlags::Weak))
|
||||
| ObjSymbolFlags::Local
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_force_active(&mut self, value: bool) {
|
||||
if value {
|
||||
self.0 |= ObjSymbolFlags::ForceActive;
|
||||
} else {
|
||||
self.0 &= !ObjSymbolFlags::ForceActive;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::derived_hash_with_manual_eq)]
|
||||
impl Hash for ObjSymbolFlagSet {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) { self.0.bits().hash(state) }
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
|
||||
pub enum ObjSymbolKind {
|
||||
#[default]
|
||||
Unknown,
|
||||
Function,
|
||||
Object,
|
||||
Section,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
|
||||
pub enum ObjDataKind {
|
||||
#[default]
|
||||
Unknown,
|
||||
Byte,
|
||||
Byte2,
|
||||
Byte4,
|
||||
Byte8,
|
||||
Float,
|
||||
Double,
|
||||
String,
|
||||
String16,
|
||||
StringTable,
|
||||
String16Table,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq)]
|
||||
pub struct ObjSymbol {
|
||||
pub name: String,
|
||||
pub demangled_name: Option<String>,
|
||||
pub address: u64,
|
||||
pub section: Option<usize>,
|
||||
pub size: u64,
|
||||
pub size_known: bool,
|
||||
pub flags: ObjSymbolFlagSet,
|
||||
pub kind: ObjSymbolKind,
|
||||
pub align: Option<u32>,
|
||||
pub data_kind: ObjDataKind,
|
||||
}
|
||||
|
||||
pub type SymbolIndex = usize;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ObjSymbols {
|
||||
obj_kind: ObjKind,
|
||||
symbols: Vec<ObjSymbol>,
|
||||
symbols_by_address: BTreeMap<u32, Vec<SymbolIndex>>,
|
||||
symbols_by_name: HashMap<String, Vec<SymbolIndex>>,
|
||||
symbols_by_section: Vec<BTreeMap<u32, Vec<SymbolIndex>>>,
|
||||
}
|
||||
|
||||
impl ObjSymbols {
|
||||
pub fn new(obj_kind: ObjKind, symbols: Vec<ObjSymbol>) -> Self {
|
||||
let mut symbols_by_address = BTreeMap::<u32, Vec<SymbolIndex>>::new();
|
||||
let mut symbols_by_section: Vec<BTreeMap<u32, Vec<SymbolIndex>>> = vec![];
|
||||
let mut symbols_by_name = HashMap::<String, Vec<SymbolIndex>>::new();
|
||||
for (idx, symbol) in symbols.iter().enumerate() {
|
||||
if obj_kind == ObjKind::Executable {
|
||||
symbols_by_address.nested_push(symbol.address as u32, idx);
|
||||
}
|
||||
if let Some(section_idx) = symbol.section {
|
||||
if section_idx >= symbols_by_section.len() {
|
||||
symbols_by_section.resize_with(section_idx + 1, BTreeMap::new);
|
||||
}
|
||||
symbols_by_section[section_idx].nested_push(symbol.address as u32, idx);
|
||||
} else {
|
||||
debug_assert!(
|
||||
symbol.address == 0
|
||||
|| symbol.flags.is_common()
|
||||
|| obj_kind == ObjKind::Executable,
|
||||
"ABS symbol in relocatable object"
|
||||
);
|
||||
}
|
||||
if !symbol.name.is_empty() {
|
||||
symbols_by_name.nested_push(symbol.name.clone(), idx);
|
||||
}
|
||||
}
|
||||
Self { obj_kind, symbols, symbols_by_address, symbols_by_name, symbols_by_section }
|
||||
}
|
||||
|
||||
pub fn add(&mut self, in_symbol: ObjSymbol, replace: bool) -> Result<SymbolIndex> {
|
||||
let opt = if let Some(section_index) = in_symbol.section {
|
||||
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))
|
||||
})
|
||||
} else {
|
||||
// TODO hmmm
|
||||
self.iter_abs().find(|(_, symbol)| symbol.name == in_symbol.name)
|
||||
};
|
||||
let target_symbol_idx = if let Some((symbol_idx, existing)) = opt {
|
||||
let size =
|
||||
if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size {
|
||||
log::warn!(
|
||||
"Conflicting size for {}: was {:#X}, now {:#X}",
|
||||
existing.name,
|
||||
existing.size,
|
||||
in_symbol.size
|
||||
);
|
||||
if replace {
|
||||
in_symbol.size
|
||||
} else {
|
||||
existing.size
|
||||
}
|
||||
} else if in_symbol.size_known {
|
||||
in_symbol.size
|
||||
} else {
|
||||
existing.size
|
||||
};
|
||||
if !replace {
|
||||
// Not replacing existing symbol, but update size
|
||||
if in_symbol.size_known && !existing.size_known {
|
||||
self.replace(symbol_idx, ObjSymbol {
|
||||
size: in_symbol.size,
|
||||
size_known: true,
|
||||
..existing.clone()
|
||||
})?;
|
||||
}
|
||||
return Ok(symbol_idx);
|
||||
}
|
||||
let new_symbol = ObjSymbol {
|
||||
name: in_symbol.name,
|
||||
demangled_name: in_symbol.demangled_name,
|
||||
address: in_symbol.address,
|
||||
section: in_symbol.section,
|
||||
size,
|
||||
size_known: existing.size_known || in_symbol.size != 0,
|
||||
flags: in_symbol.flags,
|
||||
kind: in_symbol.kind,
|
||||
align: in_symbol.align.or(existing.align),
|
||||
data_kind: match in_symbol.data_kind {
|
||||
ObjDataKind::Unknown => existing.data_kind,
|
||||
kind => kind,
|
||||
},
|
||||
};
|
||||
if existing != &new_symbol {
|
||||
log::debug!("Replacing {:?} with {:?}", existing, new_symbol);
|
||||
self.replace(symbol_idx, new_symbol)?;
|
||||
}
|
||||
symbol_idx
|
||||
} else {
|
||||
let target_symbol_idx = self.symbols.len();
|
||||
self.add_direct(ObjSymbol {
|
||||
name: in_symbol.name,
|
||||
demangled_name: in_symbol.demangled_name,
|
||||
address: in_symbol.address,
|
||||
section: in_symbol.section,
|
||||
size: in_symbol.size,
|
||||
size_known: in_symbol.size != 0,
|
||||
flags: in_symbol.flags,
|
||||
kind: in_symbol.kind,
|
||||
align: in_symbol.align,
|
||||
data_kind: in_symbol.data_kind,
|
||||
})?;
|
||||
target_symbol_idx
|
||||
};
|
||||
Ok(target_symbol_idx)
|
||||
}
|
||||
|
||||
pub fn add_direct(&mut self, in_symbol: ObjSymbol) -> Result<SymbolIndex> {
|
||||
let symbol_idx = self.symbols.len();
|
||||
if self.obj_kind == ObjKind::Executable {
|
||||
self.symbols_by_address.nested_push(in_symbol.address as u32, symbol_idx);
|
||||
}
|
||||
if let Some(section_idx) = in_symbol.section {
|
||||
if section_idx >= self.symbols_by_section.len() {
|
||||
self.symbols_by_section.resize_with(section_idx + 1, BTreeMap::new);
|
||||
}
|
||||
self.symbols_by_section[section_idx].nested_push(in_symbol.address as u32, symbol_idx);
|
||||
} else {
|
||||
ensure!(
|
||||
in_symbol.address == 0
|
||||
|| in_symbol.flags.is_common()
|
||||
|| self.obj_kind == ObjKind::Executable,
|
||||
"ABS symbol in relocatable object"
|
||||
);
|
||||
}
|
||||
if !in_symbol.name.is_empty() {
|
||||
self.symbols_by_name.nested_push(in_symbol.name.clone(), symbol_idx);
|
||||
}
|
||||
self.symbols.push(in_symbol);
|
||||
Ok(symbol_idx)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &ObjSymbol> { self.symbols.iter() }
|
||||
|
||||
pub fn count(&self) -> usize { self.symbols.len() }
|
||||
|
||||
pub fn at_section_address(
|
||||
&self,
|
||||
section_idx: usize,
|
||||
addr: u32,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols_by_section
|
||||
.get(section_idx)
|
||||
.and_then(|v| v.get(&addr))
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(move |&idx| (idx, &self.symbols[idx]))
|
||||
}
|
||||
|
||||
pub fn kind_at_section_address(
|
||||
&self,
|
||||
section_idx: usize,
|
||||
addr: u32,
|
||||
kind: ObjSymbolKind,
|
||||
) -> Result<Option<(SymbolIndex, &ObjSymbol)>> {
|
||||
self.at_section_address(section_idx, addr)
|
||||
.filter(|(_, sym)| sym.kind == kind)
|
||||
.at_most_one()
|
||||
.map_err(|_| anyhow!("Multiple symbols of kind {:?} at address {:#010X}", kind, addr))
|
||||
}
|
||||
|
||||
// Iterate over all in address ascending order, excluding ABS symbols
|
||||
pub fn iter_ordered(&self) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols_by_section
|
||||
.iter()
|
||||
.flat_map(|v| v.iter().map(|(_, v)| v))
|
||||
.flat_map(move |v| v.iter().map(move |u| (*u, &self.symbols[*u])))
|
||||
}
|
||||
|
||||
// Iterate over all ABS symbols
|
||||
pub fn iter_abs(&self) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
debug_assert!(self.obj_kind == ObjKind::Executable);
|
||||
self.symbols_by_address
|
||||
.iter()
|
||||
.flat_map(|(_, v)| v.iter().map(|&u| (u, &self.symbols[u])))
|
||||
.filter(|(_, s)| s.section.is_none())
|
||||
}
|
||||
|
||||
// Iterate over range in address ascending order, excluding ABS symbols
|
||||
pub fn for_section_range<R>(
|
||||
&self,
|
||||
section_index: usize,
|
||||
range: R,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)>
|
||||
where
|
||||
R: RangeBounds<u32> + Clone,
|
||||
{
|
||||
self.symbols_by_section
|
||||
.get(section_index)
|
||||
.into_iter()
|
||||
.flat_map(move |v| v.range(range.clone()))
|
||||
.flat_map(move |(_, v)| v.iter().map(move |u| (*u, &self.symbols[*u])))
|
||||
}
|
||||
|
||||
pub fn indexes_for_range<R>(
|
||||
&self,
|
||||
range: R,
|
||||
) -> impl DoubleEndedIterator<Item = (u32, &[SymbolIndex])>
|
||||
where
|
||||
R: RangeBounds<u32>,
|
||||
{
|
||||
debug_assert!(self.obj_kind == ObjKind::Executable);
|
||||
self.symbols_by_address.range(range).map(|(k, v)| (*k, v.as_ref()))
|
||||
}
|
||||
|
||||
pub fn for_section(
|
||||
&self,
|
||||
section_idx: usize,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols_by_section
|
||||
.get(section_idx)
|
||||
.into_iter()
|
||||
.flat_map(|v| v.iter().map(|(_, v)| v))
|
||||
.flat_map(move |v| v.iter().map(move |u| (*u, &self.symbols[*u])))
|
||||
}
|
||||
|
||||
pub fn for_name(
|
||||
&self,
|
||||
name: &str,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols_by_name
|
||||
.get(name)
|
||||
.into_iter()
|
||||
.flat_map(move |v| v.iter().map(move |u| (*u, &self.symbols[*u])))
|
||||
}
|
||||
|
||||
pub fn by_name(&self, name: &str) -> Result<Option<(SymbolIndex, &ObjSymbol)>> {
|
||||
let mut iter = self.for_name(name);
|
||||
let result = iter.next();
|
||||
if let Some((index, symbol)) = result {
|
||||
if let Some((other_index, other_symbol)) = iter.next() {
|
||||
bail!(
|
||||
"Multiple symbols with name {}: {} {:?} {:#010X} and {} {:?} {:#010X}",
|
||||
name,
|
||||
index,
|
||||
symbol.kind,
|
||||
symbol.address,
|
||||
other_index,
|
||||
other_symbol.kind,
|
||||
other_symbol.address
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn by_kind(
|
||||
&self,
|
||||
kind: ObjSymbolKind,
|
||||
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
|
||||
self.symbols.iter().enumerate().filter(move |(_, sym)| sym.kind == kind)
|
||||
}
|
||||
|
||||
pub fn replace(&mut self, index: SymbolIndex, symbol: ObjSymbol) -> Result<()> {
|
||||
let symbol_ref = &mut self.symbols[index];
|
||||
ensure!(symbol_ref.address == symbol.address, "Can't modify address with replace_symbol");
|
||||
ensure!(symbol_ref.section == symbol.section, "Can't modify section with replace_symbol");
|
||||
if symbol_ref.name != symbol.name {
|
||||
if !symbol_ref.name.is_empty() {
|
||||
self.symbols_by_name.nested_remove(&symbol_ref.name, &index);
|
||||
}
|
||||
if !symbol.name.is_empty() {
|
||||
self.symbols_by_name.nested_push(symbol.name.clone(), index);
|
||||
}
|
||||
}
|
||||
*symbol_ref = symbol;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Try to find a previous sized symbol that encompasses the target
|
||||
pub fn for_relocation(
|
||||
&self,
|
||||
target_addr: u32,
|
||||
reloc_kind: ObjRelocKind,
|
||||
) -> Result<Option<(SymbolIndex, &ObjSymbol)>> {
|
||||
ensure!(self.obj_kind == ObjKind::Executable);
|
||||
let mut result = None;
|
||||
for (_addr, symbol_idxs) in self.indexes_for_range(..=target_addr).rev() {
|
||||
let mut symbols = symbol_idxs
|
||||
.iter()
|
||||
.map(|&idx| (idx, &self.symbols[idx]))
|
||||
.filter(|(_, sym)| sym.referenced_by(reloc_kind))
|
||||
.collect_vec();
|
||||
let (symbol_idx, symbol) = if symbols.len() == 1 {
|
||||
symbols.pop().unwrap()
|
||||
} else {
|
||||
symbols.sort_by_key(|&(_, symbol)| {
|
||||
let mut rank = match symbol.kind {
|
||||
ObjSymbolKind::Function | ObjSymbolKind::Object => match reloc_kind {
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo => 1,
|
||||
ObjRelocKind::Absolute
|
||||
| ObjRelocKind::PpcRel24
|
||||
| ObjRelocKind::PpcRel14
|
||||
| ObjRelocKind::PpcEmbSda21 => 2,
|
||||
},
|
||||
// Label
|
||||
ObjSymbolKind::Unknown => match reloc_kind {
|
||||
ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Lo
|
||||
if !symbol.name.starts_with("..") =>
|
||||
{
|
||||
3
|
||||
}
|
||||
_ => 1,
|
||||
},
|
||||
ObjSymbolKind::Section => -1,
|
||||
};
|
||||
if symbol.size > 0 {
|
||||
rank += 1;
|
||||
}
|
||||
-rank
|
||||
});
|
||||
match symbols.first() {
|
||||
Some(&v) => v,
|
||||
None => continue,
|
||||
}
|
||||
};
|
||||
if symbol.address == target_addr as u64 {
|
||||
result = Some((symbol_idx, symbol));
|
||||
break;
|
||||
}
|
||||
if symbol.size > 0 {
|
||||
if symbol.address + symbol.size > target_addr as u64 {
|
||||
result = Some((symbol_idx, symbol));
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn flags(&mut self, idx: SymbolIndex) -> &mut ObjSymbolFlagSet {
|
||||
&mut self.symbols[idx].flags
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<SymbolIndex> for ObjSymbols {
|
||||
type Output = ObjSymbol;
|
||||
|
||||
fn index(&self, index: usize) -> &Self::Output { &self.symbols[index] }
|
||||
}
|
||||
|
||||
impl ObjSymbol {
|
||||
/// Whether this symbol can be referenced by the given relocation kind.
|
||||
pub fn referenced_by(&self, reloc_kind: ObjRelocKind) -> bool {
|
||||
if is_linker_generated_label(&self.name) {
|
||||
// Linker generated labels will only be referenced by @ha/@h/@l relocations
|
||||
return matches!(
|
||||
reloc_kind,
|
||||
ObjRelocKind::PpcAddr16Ha | ObjRelocKind::PpcAddr16Hi | ObjRelocKind::PpcAddr16Lo
|
||||
);
|
||||
}
|
||||
|
||||
match self.kind {
|
||||
ObjSymbolKind::Unknown => true,
|
||||
ObjSymbolKind::Function => !matches!(reloc_kind, ObjRelocKind::PpcEmbSda21),
|
||||
ObjSymbolKind::Object => {
|
||||
!matches!(reloc_kind, ObjRelocKind::PpcRel14 | ObjRelocKind::PpcRel24)
|
||||
}
|
||||
ObjSymbolKind::Section => {
|
||||
matches!(
|
||||
reloc_kind,
|
||||
ObjRelocKind::PpcAddr16Ha
|
||||
| ObjRelocKind::PpcAddr16Hi
|
||||
| ObjRelocKind::PpcAddr16Lo
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user