Analyzer fixes galore

- Transparent NLZSS decompression (add `:nlzss` to path)
- Overhaul portions of the analyzer to support more games
- Reject some invalid data relocations automatically
- Jump table analysis fixes
This commit is contained in:
2023-09-13 02:08:51 -04:00
parent 50f913c4eb
commit d4ef1ce16a
29 changed files with 1533 additions and 669 deletions

View File

@@ -69,8 +69,8 @@ pub struct ObjInfo {
pub link_order: Vec<ObjUnit>,
pub blocked_ranges: BTreeMap<SectionAddress, u32>, // start -> end
// From extab
pub known_functions: BTreeMap<SectionAddress, u32>,
// From .ctors, .dtors and extab
pub known_functions: BTreeMap<SectionAddress, Option<u32>>,
// REL
/// Module ID (0 for main)

View File

@@ -1,9 +1,12 @@
use std::{collections::BTreeMap, ops::RangeBounds};
use std::{cmp::max, collections::BTreeMap, ops::RangeBounds};
use anyhow::{anyhow, Result};
use itertools::Itertools;
use crate::util::nested::NestedVec;
use crate::{
obj::{ObjInfo, ObjSection},
util::{nested::NestedVec, split::default_section_align},
};
/// Marks a split point within a section.
#[derive(Debug, Clone, Eq, PartialEq)]
@@ -21,6 +24,30 @@ pub struct ObjSplit {
pub rename: Option<String>,
}
impl ObjSplit {
pub fn alignment(
&self,
obj: &ObjInfo,
section_index: usize,
section: &ObjSection,
split_addr: u32,
) -> u32 {
self.align.unwrap_or_else(|| {
let default_align = default_section_align(section) as u32;
max(
// Maximum alignment of any symbol in this split
obj.symbols
.for_section_range(section_index, split_addr..self.end)
.filter(|&(_, s)| s.size_known && s.size > 0)
.filter_map(|(_, s)| s.align)
.max()
.unwrap_or(default_align),
default_align,
)
})
}
}
/// Splits within a section.
#[derive(Debug, Clone, Default)]
pub struct ObjSplits {
@@ -46,6 +73,13 @@ impl ObjSplits {
}
}
pub fn at_mut(&mut self, address: u32) -> Option<&mut ObjSplit> {
match self.for_range_mut(..=address).next_back() {
Some((_, split)) if split.end == 0 || split.end > address => Some(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> {

View File

@@ -112,6 +112,15 @@ impl ObjSymbolFlagSet {
self.0 &= !ObjSymbolFlags::ForceActive;
}
}
/// Special flags to keep when merging symbols.
#[inline]
pub fn keep_flags(&self) -> FlagSet<ObjSymbolFlags> {
self.0
& (ObjSymbolFlags::ForceActive
| ObjSymbolFlags::NoWrite
| ObjSymbolFlags::RelocationIgnore)
}
}
#[allow(clippy::derived_hash_with_manual_eq)]
@@ -213,8 +222,7 @@ impl ObjSymbols {
let replace = replace || (is_auto_symbol(existing) && !is_auto_symbol(&in_symbol));
let size =
if existing.size_known && in_symbol.size_known && existing.size != in_symbol.size {
// TODO fix and promote back to warning
log::debug!(
log::warn!(
"Conflicting size for {}: was {:#X}, now {:#X}",
existing.name,
existing.size,
@@ -248,7 +256,7 @@ impl ObjSymbols {
section: in_symbol.section,
size,
size_known: existing.size_known || in_symbol.size != 0,
flags: in_symbol.flags,
flags: ObjSymbolFlagSet(in_symbol.flags.0 | existing.flags.keep_flags()),
kind: in_symbol.kind,
align: in_symbol.align.or(existing.align),
data_kind: match in_symbol.data_kind {
@@ -511,7 +519,8 @@ impl ObjSymbol {
ObjSymbolKind::Unknown => true,
ObjSymbolKind::Function => !matches!(reloc_kind, ObjRelocKind::PpcEmbSda21),
ObjSymbolKind::Object => {
!matches!(reloc_kind, ObjRelocKind::PpcRel14 | ObjRelocKind::PpcRel24)
// !matches!(reloc_kind, ObjRelocKind::PpcRel14 | ObjRelocKind::PpcRel24)
true // SADX has bugged relocations that jump from .text to .bss, how awful
}
ObjSymbolKind::Section => {
matches!(