Validate splits don't overlap symbols (or each other)

Use DoubleEndedIterator::next_back instead of Iterator::last to avoid unnecessary iteration.
This commit is contained in:
Luke Street 2023-08-15 09:40:32 -04:00
parent d272b5dce8
commit 2c2ee8bf08
7 changed files with 82 additions and 10 deletions

2
Cargo.lock generated
View File

@ -214,7 +214,7 @@ dependencies = [
[[package]] [[package]]
name = "decomp-toolkit" name = "decomp-toolkit"
version = "0.3.5" version = "0.3.6"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"ar", "ar",

View File

@ -3,7 +3,7 @@ name = "decomp-toolkit"
description = "Yet another GameCube/Wii decompilation toolkit." description = "Yet another GameCube/Wii decompilation toolkit."
authors = ["Luke Street <luke@street.dev>"] authors = ["Luke Street <luke@street.dev>"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.3.5" version = "0.3.6"
edition = "2021" edition = "2021"
publish = false publish = false
build = "build.rs" build = "build.rs"

View File

@ -80,7 +80,7 @@ impl FunctionSlices {
pub fn add_block_start(&mut self, addr: u32) -> bool { pub fn add_block_start(&mut self, addr: u32) -> bool {
// Slice previous block. // Slice previous block.
if let Some((_, end)) = self.blocks.range_mut(..addr).last() { if let Some((_, end)) = self.blocks.range_mut(..addr).next_back() {
let last_end = *end; let last_end = *end;
if last_end > addr { if last_end > addr {
*end = addr; *end = addr;

View File

@ -376,7 +376,7 @@ impl Tracker {
if self.ignore_addresses.contains(&addr) { if self.ignore_addresses.contains(&addr) {
return false; return false;
} }
if let Some((&start, &end)) = obj.blocked_ranges.range(..=from).last() { if let Some((&start, &end)) = obj.blocked_ranges.range(..=from).next_back() {
if from >= start && from < end { if from >= start && from < end {
return false; return false;
} }

View File

@ -509,7 +509,7 @@ fn validate<P: AsRef<Path>>(obj: &ObjInfo, elf_file: P, state: &AnalyzerState) -
continue; continue;
} }
if !real_functions.contains_key(&start) { if !real_functions.contains_key(&start) {
let (real_addr, real_name) = real_functions.range(..start).last().unwrap(); let (real_addr, real_name) = real_functions.range(..start).next_back().unwrap();
log::warn!( log::warn!(
"Function {:#010X} not real (actually a part of {} @ {:#010X})", "Function {:#010X} not real (actually a part of {} @ {:#010X})",
start, start,

View File

@ -478,7 +478,10 @@ impl ObjSymbols {
Ok(result) Ok(result)
} }
pub fn by_kind(&self, kind: ObjSymbolKind) -> impl Iterator<Item = (SymbolIndex, &ObjSymbol)> { pub fn by_kind(
&self,
kind: ObjSymbolKind,
) -> impl DoubleEndedIterator<Item = (SymbolIndex, &ObjSymbol)> {
self.symbols.iter().enumerate().filter(move |(_, sym)| sym.kind == kind) self.symbols.iter().enumerate().filter(move |(_, sym)| sym.kind == kind)
} }
@ -661,15 +664,20 @@ impl ObjInfo {
/// Locate an existing split for the given address. /// Locate an existing split for the given address.
pub fn split_for(&self, address: u32) -> Option<(u32, &ObjSplit)> { pub fn split_for(&self, address: u32) -> Option<(u32, &ObjSplit)> {
match self.splits_for_range(..=address).last() { match self.splits_for_range(..=address).next_back() {
Some((addr, split)) if split.end == 0 || split.end > address => Some((addr, split)), Some((addr, split)) if split.end == 0 || split.end > address => Some((addr, split)),
_ => None, _ => None,
} }
} }
/// Locate existing splits within the given address range. /// Locate existing splits within the given address range.
pub fn splits_for_range<R>(&self, range: R) -> impl Iterator<Item = (u32, &ObjSplit)> pub fn splits_for_range<R>(
where R: RangeBounds<u32> { &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))) self.splits.range(range).flat_map(|(addr, v)| v.iter().map(move |u| (*addr, u)))
} }

View File

@ -386,6 +386,67 @@ fn update_common_splits(obj: &mut ObjInfo) -> Result<()> {
Ok(()) 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. /// Perform any necessary adjustments to allow relinking.
/// This includes: /// This includes:
/// - Ensuring .ctors & .dtors entries are split with their associated function /// - Ensuring .ctors & .dtors entries are split with their associated function
@ -418,6 +479,9 @@ pub fn update_splits(obj: &mut ObjInfo) -> Result<()> {
// Update common BSS splits // Update common BSS splits
update_common_splits(obj)?; update_common_splits(obj)?;
// Ensure splits don't overlap symbols or each other
validate_splits(obj)?;
// Resolve link order // Resolve link order
obj.link_order = resolve_link_order(obj)?; obj.link_order = resolve_link_order(obj)?;
@ -908,7 +972,7 @@ pub fn end_for_section(obj: &ObjInfo, section_index: usize) -> Result<u32> {
.symbols .symbols
.for_range(section_start..section_end) .for_range(section_start..section_end)
.filter(|(_, s)| s.kind == ObjSymbolKind::Object && s.size_known && s.size > 0) .filter(|(_, s)| s.kind == ObjSymbolKind::Object && s.size_known && s.size > 0)
.last(); .next_back();
match last_symbol { match last_symbol {
Some((_, symbol)) if is_linker_generated_object(&symbol.name) => { Some((_, symbol)) if is_linker_generated_object(&symbol.name) => {
log::debug!( log::debug!(