6 months of occasional work I guess

This commit is contained in:
2023-07-21 17:59:07 -04:00
parent f1b4afa885
commit 0fa0aafaea
44 changed files with 4688 additions and 1817 deletions

View File

@@ -3,16 +3,17 @@ pub mod split;
use std::{
cmp::min,
collections::{btree_map, BTreeMap},
collections::{btree_map, BTreeMap, HashMap},
hash::{Hash, Hasher},
ops::{Range, RangeBounds},
};
use anyhow::{anyhow, bail, Result};
use anyhow::{anyhow, bail, ensure, Result};
use flagset::{flags, FlagSet};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use crate::util::{nested::NestedVec, rel::RelReloc};
use crate::util::{comment::MWComment, nested::NestedVec, rel::RelReloc};
flags! {
#[repr(u8)]
@@ -23,14 +24,18 @@ flags! {
Weak,
Common,
Hidden,
ForceActive,
}
}
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Serialize, Deserialize)]
pub struct ObjSymbolFlagSet(pub FlagSet<ObjSymbolFlags>);
#[allow(clippy::derive_hash_xor_eq)]
#[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,
@@ -38,6 +43,7 @@ pub enum ObjSectionKind {
ReadOnlyData,
Bss,
}
#[derive(Debug, Clone)]
pub struct ObjSection {
pub name: String,
@@ -54,6 +60,7 @@ pub struct ObjSection {
pub file_offset: u64,
pub section_known: bool,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
pub enum ObjSymbolKind {
#[default]
@@ -62,7 +69,24 @@ pub enum ObjSymbolKind {
Object,
Section,
}
#[derive(Debug, Clone, Default)]
#[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>,
@@ -72,7 +96,10 @@ pub struct ObjSymbol {
pub size_known: bool,
pub flags: ObjSymbolFlagSet,
pub kind: ObjSymbolKind,
pub align: Option<u32>,
pub data_kind: ObjDataKind,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ObjKind {
/// Fully linked object
@@ -80,18 +107,38 @@ pub enum ObjKind {
/// Relocatable object
Relocatable,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum ObjArchitecture {
PowerPc,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ObjSplit {
pub unit: String,
pub end: u32,
pub align: Option<u32>,
pub common: bool,
}
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: Vec<ObjSymbol>,
pub symbols: ObjSymbols,
pub sections: Vec<ObjSection>,
pub entry: u64,
pub mw_comment: MWComment,
// Linker generated
pub sda2_base: Option<u32>,
@@ -103,9 +150,10 @@ pub struct ObjInfo {
pub arena_hi: Option<u32>,
// Extracted
pub splits: BTreeMap<u32, Vec<String>>,
pub splits: BTreeMap<u32, Vec<ObjSplit>>,
pub named_sections: BTreeMap<u32, String>,
pub link_order: Vec<String>,
pub blocked_ranges: BTreeMap<u32, u32>, // start -> end
// From extab
pub known_functions: BTreeMap<u32, u32>,
@@ -115,6 +163,7 @@ pub struct ObjInfo {
pub module_id: u32,
pub unresolved_relocations: Vec<RelReloc>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum ObjRelocKind {
Absolute,
@@ -125,42 +174,289 @@ pub enum ObjRelocKind {
PpcRel14,
PpcEmbSda21,
}
#[derive(Debug, Clone)]
pub struct ObjReloc {
pub kind: ObjRelocKind,
pub address: u64,
pub target_symbol: usize,
pub target_symbol: SymbolIndex,
pub addend: i64,
}
impl ObjInfo {
pub fn symbols_for_section(
&self,
section_idx: usize,
) -> impl Iterator<Item = (usize, &ObjSymbol)> {
self.symbols
.iter()
.enumerate()
.filter(move |&(_, symbol)| symbol.section == Some(section_idx))
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 build_symbol_map(&self, section_idx: usize) -> Result<BTreeMap<u32, Vec<usize>>> {
let mut symbols = BTreeMap::<u32, Vec<usize>>::new();
for (symbol_idx, symbol) in self.symbols_for_section(section_idx) {
symbols.nested_push(symbol.address as u32, symbol_idx);
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)
});
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);
}
Ok(symbols)
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)
}
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])))
}
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)
// TODO required?
.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 Iterator<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(())
}
}
impl ObjInfo {
pub fn new(
kind: ObjKind,
architecture: ObjArchitecture,
name: String,
symbols: Vec<ObjSymbol>,
sections: Vec<ObjSection>,
) -> Self {
Self {
kind,
architecture,
name,
symbols: ObjSymbols::new(symbols),
sections,
entry: 0,
mw_comment: Default::default(),
sda2_base: None,
sda_base: None,
stack_address: None,
stack_end: None,
db_stack_addr: None,
arena_lo: None,
arena_hi: None,
splits: Default::default(),
named_sections: Default::default(),
link_order: vec![],
blocked_ranges: Default::default(),
known_functions: Default::default(),
module_id: 0,
unresolved_relocations: vec![],
}
}
pub fn add_symbol(&mut self, in_symbol: ObjSymbol, replace: bool) -> Result<SymbolIndex> {
match in_symbol.name.as_str() {
"_SDA_BASE_" => self.sda_base = Some(in_symbol.address as u32),
"_SDA2_BASE_" => self.sda2_base = Some(in_symbol.address as u32),
"_stack_addr" => self.stack_address = Some(in_symbol.address as u32),
"_stack_end" => self.stack_end = Some(in_symbol.address as u32),
"_db_stack_addr" => self.db_stack_addr = Some(in_symbol.address as u32),
"__ArenaLo" => self.arena_lo = Some(in_symbol.address as u32),
"__ArenaHi" => self.arena_hi = Some(in_symbol.address as u32),
_ => {}
}
self.symbols.add(in_symbol, replace)
}
pub fn section_at(&self, addr: u32) -> Result<&ObjSection> {
self.sections
.iter()
.find(|&section| {
(addr as u64) >= section.address && (addr as u64) < section.address + section.size
})
.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)?;
let data = if end == 0 {
@@ -171,20 +467,76 @@ impl ObjInfo {
};
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).last() {
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 Iterator<Item = (u32, &ObjSplit)>
where R: RangeBounds<u32> {
self.splits.range(range).flat_map(|(addr, v)| v.iter().map(move |u| (*addr, u)))
}
pub fn add_split(&mut self, address: u32, split: ObjSplit) {
log::debug!("Adding split @ {:#010X}: {:?}", address, split);
// TODO merge with preceding split if possible
self.splits.entry(address).or_default().push(split);
}
}
impl ObjSection {
pub fn build_relocation_map(&self) -> Result<BTreeMap<u32, ObjReloc>> {
let mut relocations = BTreeMap::<u32, ObjReloc>::new();
for reloc in &self.relocations {
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(reloc.clone());
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 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}"),
})
}

View File

@@ -13,7 +13,8 @@ use crate::{
analysis::tracker::{Relocation, Tracker},
array_ref,
obj::{
ObjInfo, ObjReloc, ObjRelocKind, ObjSectionKind, ObjSymbol, ObjSymbolFlagSet, ObjSymbolKind,
section_kind_for_section, ObjInfo, ObjReloc, ObjRelocKind, ObjSymbol, ObjSymbolFlagSet,
ObjSymbolKind,
},
util::elf::process_elf,
};
@@ -112,15 +113,7 @@ pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> R
if !target_section.section_known {
if let Some(section_name) = &sig_symbol.section {
target_section.name = section_name.clone();
target_section.kind = match section_name.as_str() {
".init" | ".text" | ".dbgtext" => ObjSectionKind::Code,
".ctors" | ".dtors" | ".rodata" | ".sdata2" | "extab" | "extabindex" => {
ObjSectionKind::ReadOnlyData
}
".bss" | ".sbss" | ".sbss2" => ObjSectionKind::Bss,
".data" | ".sdata" => ObjSectionKind::Data,
name => bail!("Unknown section {name}"),
};
target_section.kind = section_kind_for_section(section_name)?;
target_section.section_known = true;
}
}
@@ -131,49 +124,22 @@ pub fn apply_symbol(obj: &mut ObjInfo, target: u32, sig_symbol: &OutSymbol) -> R
// Hack to mark linker generated symbols as ABS
target_section_index = None;
}
let target_symbol_idx = if let Some((symbol_idx, existing)) =
obj.symbols.iter_mut().enumerate().find(|(_, symbol)| {
symbol.address == target as u64
&& symbol.kind == sig_symbol.kind
// Hack to avoid replacing different ABS symbols
&& (symbol.section.is_some() || symbol.name == sig_symbol.name)
}) {
log::debug!("Replacing {:?} with {:?}", existing, sig_symbol);
*existing = ObjSymbol {
let demangled_name = demangle(&sig_symbol.name, &DemangleOptions::default());
let target_symbol_idx = obj.add_symbol(
ObjSymbol {
name: sig_symbol.name.clone(),
demangled_name: demangle(&sig_symbol.name, &DemangleOptions::default()),
address: target as u64,
section: target_section_index,
size: if sig_symbol.size == 0 { existing.size } else { sig_symbol.size as u64 },
size_known: existing.size_known || sig_symbol.size != 0,
flags: sig_symbol.flags,
kind: sig_symbol.kind,
};
symbol_idx
} else {
let target_symbol_idx = obj.symbols.len();
obj.symbols.push(ObjSymbol {
name: sig_symbol.name.clone(),
demangled_name: demangle(&sig_symbol.name, &DemangleOptions::default()),
demangled_name,
address: target as u64,
section: target_section_index,
size: sig_symbol.size as u64,
size_known: sig_symbol.size != 0,
size_known: sig_symbol.size > 0 || sig_symbol.kind == ObjSymbolKind::Unknown,
flags: sig_symbol.flags,
kind: sig_symbol.kind,
});
target_symbol_idx
};
match sig_symbol.name.as_str() {
"_SDA_BASE_" => obj.sda_base = Some(target),
"_SDA2_BASE_" => obj.sda2_base = Some(target),
"_stack_addr" => obj.stack_address = Some(target),
"_stack_end" => obj.stack_end = Some(target),
"_db_stack_addr" => obj.db_stack_addr = Some(target),
"__ArenaLo" => obj.arena_lo = Some(target),
"__ArenaHi" => obj.arena_hi = Some(target),
_ => {}
}
align: None,
data_kind: Default::default(),
},
true,
)?;
Ok(target_symbol_idx)
}
@@ -185,7 +151,7 @@ pub fn apply_signature(obj: &mut ObjInfo, addr: u32, signature: &FunctionSignatu
for reloc in &signature.relocations {
tracker.known_relocations.insert(addr + reloc.offset);
}
tracker.process_function(obj, &obj.symbols[symbol_idx])?;
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;
@@ -293,26 +259,20 @@ pub fn generate_signature(path: &Path, symbol_name: &str) -> Result<Option<Funct
}
let mut tracker = Tracker::new(&obj);
// tracker.ignore_addresses.insert(0x80004000);
for symbol in &obj.symbols {
if symbol.kind != ObjSymbolKind::Function {
continue;
}
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 {
if symbol.kind != ObjSymbolKind::Function {
continue;
}
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();
// let out_symbol_idx = out_symbols.len();
out_symbols.push(OutSymbol {
kind: symbol.kind,
name: symbol.name.clone(),
@@ -334,10 +294,11 @@ pub fn generate_signature(path: &Path, symbol_name: &str) -> Result<Option<Funct
.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) = relocations.get(&addr) {
if let Some(&reloc_idx) = relocations.get(&addr) {
let reloc = &section.relocations[reloc_idx];
let symbol_idx = match symbol_map.entry(reloc.target_symbol) {
btree_map::Entry::Vacant(e) => {
let target = &obj.symbols[reloc.target_symbol];
let target = obj.symbols.at(reloc.target_symbol);
let symbol_idx = out_symbols.len();
e.insert(symbol_idx);
out_symbols.push(OutSymbol {
@@ -363,19 +324,19 @@ pub fn generate_signature(path: &Path, symbol_name: &str) -> Result<Option<Funct
ObjRelocKind::PpcAddr16Hi
| ObjRelocKind::PpcAddr16Ha
| ObjRelocKind::PpcAddr16Lo => {
*ins = *ins & !0xFFFF;
*ins &= !0xFFFF;
*pat = !0xFFFF;
}
ObjRelocKind::PpcRel24 => {
*ins = *ins & !0x3FFFFFC;
*ins &= !0x3FFFFFC;
*pat = !0x3FFFFFC;
}
ObjRelocKind::PpcRel14 => {
*ins = *ins & !0xFFFC;
*ins &= !0xFFFC;
*pat = !0xFFFC;
}
ObjRelocKind::PpcEmbSda21 => {
*ins = *ins & !0x1FFFFF;
*ins &= !0x1FFFFF;
*pat = !0x1FFFFF;
}
}

View File

@@ -1,11 +1,426 @@
use std::{cmp::min, collections::HashMap};
use std::{
cmp::min,
collections::{BTreeMap, HashMap, HashSet},
};
use anyhow::{anyhow, bail, ensure, Result};
use itertools::Itertools;
use topological_sort::TopologicalSort;
use crate::obj::{
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSymbol,
ObjArchitecture, ObjInfo, ObjKind, ObjReloc, ObjSection, ObjSectionKind, ObjSplit, ObjSymbol,
ObjSymbolFlagSet, ObjSymbolFlags, ObjSymbolKind,
};
/// 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;
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)) =
obj.symbols.kind_at_address(function_addr, ObjSymbolKind::Function)?
else {
bail!("Failed to find function symbol @ {:#010X}", function_addr);
};
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)
});
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,
});
}
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,
});
}
}
current_address += 4;
}
for (addr, split) in new_splits {
obj.add_split(addr, split);
}
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)
});
log::debug!("Adding splits to unit {}", unit);
if extabindex_split.is_none() {
log::debug!("Adding split for extabindex entry @ {:#010X}", current_address);
new_splits.insert(current_address, ObjSplit {
unit: unit.clone(),
end: current_address + 12,
align: None,
common: false,
});
}
if extab_split.is_none() {
log::debug!("Adding split for extab @ {:#010X}", extab_addr);
new_splits.insert(extab_addr, ObjSplit {
unit: unit.clone(),
end: extab_addr + extab_symbol.size as u32,
align: None,
common: false,
});
}
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,
});
}
}
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);
new_splits.insert(current_address, ObjSplit {
unit: unit.clone(),
end: new_split_end,
align: None,
common: false,
});
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(())
}
/// 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)?;
// 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<String>> {
let mut global_unit_order = Vec::<String>::new();
let mut t_sort = TopologicalSort::<String>::new();
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());
}
loop {
match (iter.next(), iter.peek()) {
(Some((a_addr, a)), Some((b_addr, b))) => {
if a.unit != b.unit {
log::debug!(
"Adding dependency {} ({:#010X}) -> {} ({:#010X})",
a.unit,
a_addr,
b.unit,
b_addr
);
t_sort.add_dependency(a.unit.clone(), b.unit.clone());
}
}
(Some((_, a)), None) => {
t_sort.insert(a.unit.clone());
break;
}
_ => break,
}
}
}
for unit in &mut t_sort {
global_unit_order.push(unit);
}
// An incomplete topological sort indicates that a cyclic dependency was encountered.
ensure!(t_sort.is_empty(), "Cyclic dependency encountered while resolving link order");
// Sanity check, did we get all TUs in the final order?
for unit in obj.splits.values().flatten().map(|s| &s.unit) {
ensure!(global_unit_order.contains(unit), "Failed to find an order for {unit}");
}
Ok(global_unit_order)
}
/// Split an executable object into relocatable objects.
pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
ensure!(obj.kind == ObjKind::Executable, "Expected executable object");
@@ -15,60 +430,33 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
let mut name_to_obj: HashMap<String, usize> = HashMap::new();
for unit in &obj.link_order {
name_to_obj.insert(unit.clone(), objects.len());
object_symbols.push(vec![None; obj.symbols.len()]);
objects.push(ObjInfo {
module_id: 0,
kind: ObjKind::Relocatable,
architecture: ObjArchitecture::PowerPc,
name: unit.clone(),
symbols: vec![],
sections: vec![],
entry: 0,
sda2_base: None,
sda_base: None,
stack_address: None,
stack_end: None,
db_stack_addr: None,
arena_lo: None,
arena_hi: None,
splits: Default::default(),
named_sections: Default::default(),
link_order: vec![],
known_functions: Default::default(),
unresolved_relocations: vec![],
});
object_symbols.push(vec![None; obj.symbols.count()]);
let mut obj = ObjInfo::new(
ObjKind::Relocatable,
ObjArchitecture::PowerPc,
unit.clone(),
vec![],
vec![],
);
obj.mw_comment = obj.mw_comment.clone();
objects.push(obj);
}
for (section_idx, section) in obj.sections.iter().enumerate() {
let mut current_address = section.address as u32;
let mut section_end = (section.address + section.size) as u32;
// if matches!(section.name.as_str(), "extab" | "extabindex") {
// continue;
// }
// .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;
}
let mut file_iter = obj
.splits
.range(current_address..)
.flat_map(|(addr, v)| v.iter().map(move |u| (addr, u)))
.peekable();
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()?;
let symbols = obj.build_symbol_map(section_idx)?;
loop {
if current_address >= section_end {
break;
}
let (file_addr, unit) = match file_iter.next() {
Some((&addr, unit)) => (addr, unit),
let (file_addr, split) = match file_iter.next() {
Some((addr, split)) => (addr, split),
None => bail!("No file found"),
};
ensure!(
@@ -76,41 +464,30 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
"Gap in files: {} @ {:#010X}, {} @ {:#010X}",
section.name,
section.address,
unit,
split.unit,
file_addr
);
let mut file_end = section_end;
let mut dont_go_forward = false;
if let Some(&(&next_addr, next_unit)) = file_iter.peek() {
if file_addr == next_addr {
log::warn!("Duplicating {} in {unit} and {next_unit}", section.name);
dont_go_forward = true;
file_end = obj
.splits
.range(current_address + 1..)
.next()
.map(|(&addr, _)| addr)
.unwrap_or(section_end);
} else {
file_end = min(next_addr, 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(unit)
.get(&split.unit)
.and_then(|&idx| objects.get_mut(idx))
.ok_or_else(|| anyhow!("Unit '{unit}' not in link order"))?;
.ok_or_else(|| anyhow!("Unit '{}' not in link order", split.unit))?;
let symbol_idxs = name_to_obj
.get(unit)
.get(&split.unit)
.and_then(|&idx| object_symbols.get_mut(idx))
.ok_or_else(|| anyhow!("Unit '{unit}' not in link order"))?;
.ok_or_else(|| anyhow!("Unit '{}' not in link order", split.unit))?;
// Calculate & verify section alignment
let mut align = default_section_align(section);
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}",
unit,
split.unit,
section.name,
align,
current_address
@@ -125,7 +502,7 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
ensure!(
current_address & (align as u32 - 1) == 0,
"Invalid alignment for split: {} {} {:#010X}",
unit,
split.unit,
section.name,
current_address
);
@@ -133,67 +510,95 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
// Collect relocations; target_symbol will be updated later
let out_relocations = relocations
.range(current_address..file_end)
.map(|(_, o)| ObjReloc {
kind: o.kind,
address: o.address - current_address as u64,
target_symbol: o.target_symbol,
addend: o.addend,
.map(|(_, &idx)| {
let o = &section.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();
for &symbol_idx in symbols.range(current_address..file_end).flat_map(|(_, vec)| vec) {
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?
}
let symbol = &obj.symbols[symbol_idx];
symbol_idxs[symbol_idx] = Some(file.symbols.len());
file.symbols.push(ObjSymbol {
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: symbol.address - current_address as u64,
section: Some(out_section_idx),
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: symbol.flags,
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,
})?;
}
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(&current_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,
});
}
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(&current_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,
});
if !dont_go_forward {
current_address = file_end;
}
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 {
@@ -204,32 +609,52 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
}
None => {
// Extern
let out_sym_idx = out_obj.symbols.len();
let target_sym = &obj.symbols[reloc.target_symbol];
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.0.contains(ObjSymbolFlags::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.push(ObjSymbol {
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 (target_addr, target_unit) = obj
.splits
.range(..=target_sym.address as u32)
.map(|(addr, v)| (*addr, v.last().unwrap()))
.last()
.unwrap();
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::warn!(
"Extern relocation @ {:#010X}\n\tSource object: {}:{:#010X} {}\n\tTarget object: {}:{:#010X} {}\n\tTarget symbol: {:#010X} {}\n",
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_unit,
target_split.unit,
target_sym.address,
target_sym.demangled_name.as_deref().unwrap_or(&target_sym.name),
);
@@ -240,44 +665,50 @@ pub fn split_obj(obj: &ObjInfo) -> Result<Vec<ObjInfo>> {
}
}
// Strip linker generated symbols
for obj in &mut objects {
for symbol in &mut obj.symbols {
if is_skip_symbol(&symbol.name) {
if symbol.section.is_some() {
log::debug!("Externing {:?} in {}", symbol, obj.name);
*symbol = ObjSymbol {
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
..Default::default()
};
}
} else if is_linker_symbol(&symbol.name) {
if let Some(section_idx) = symbol.section {
log::debug!("Skipping {:?} in {}", symbol, obj.name);
let section = &mut obj.sections[section_idx];
// TODO assuming end of file
section.size -= symbol.size;
section.data.truncate(section.data.len() - symbol.size as usize);
*symbol = ObjSymbol {
name: symbol.name.clone(),
demangled_name: symbol.demangled_name.clone(),
..Default::default()
};
// 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.0.contains(ObjSymbolFlags::Local) {
log::debug!("Globalizing {} in {}", symbol.name, obj.name);
symbol.flags.0 &= !ObjSymbolFlags::Local;
symbol.flags.0 |= ObjSymbolFlags::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
fn default_section_align(section: &ObjSection) -> u64 {
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" => 1, // ?
_ => 8,
},
}
@@ -285,7 +716,7 @@ fn default_section_align(section: &ObjSection) -> u64 {
/// Linker-generated symbols to extern
#[inline]
fn is_skip_symbol(name: &str) -> bool {
pub fn is_linker_generated_label(name: &str) -> bool {
matches!(
name,
"_ctors"
@@ -347,11 +778,47 @@ fn is_skip_symbol(name: &str) -> bool {
)
}
/// Linker generated symbols to strip entirely
/// Linker generated objects to strip entirely
#[inline]
fn is_linker_symbol(name: &str) -> bool {
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)
.last();
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)
}