From 278683f1033deeb963bbc36af8e634dabdc8d81a Mon Sep 17 00:00:00 2001 From: LagoLunatic Date: Sun, 21 Dec 2025 11:57:17 -0500 Subject: [PATCH] Improve automatic symbol pairing for functions with compiler-generated names (#303) * Fix `__arraydtor$1234`s sometimes being swapped The list of symbols is not necessarily sorted by address, so if there are multiple `__arraydtor$1234` functions it's possible that they're in reverse order on the right, which would cause them to be paired up incorrectly. To fix this we sort by section index and symbol address after filtering down the list of symbols to ones that have matching names. * Support pairing up symbols for anonymous classes * Disallow exact-name pairups for compiler-generated symbols --- objdiff-core/src/diff/data.rs | 23 ++++++++++++++++++----- objdiff-core/src/diff/mod.rs | 23 +++++++++++------------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/objdiff-core/src/diff/data.rs b/objdiff-core/src/diff/data.rs index 73b9175..5eeed55 100644 --- a/objdiff-core/src/diff/data.rs +++ b/objdiff-core/src/diff/data.rs @@ -36,12 +36,25 @@ pub fn diff_bss_symbol( } pub fn symbol_name_matches(left_name: &str, right_name: &str) -> bool { - // Match Metrowerks symbol$1234 against symbol$2345 - // and GCC symbol.1234 against symbol.2345 - if let Some((prefix, suffix)) = left_name.split_once(['$', '.']) { - if !suffix.chars().all(char::is_numeric) { - return false; + if let Some((left_prefix, left_suffix)) = left_name.split_once("@class$") + && let Some((right_prefix, right_suffix)) = right_name.split_once("@class$") + { + // Match Metrowerks anonymous class symbol names, ignoring the unique ID. + // e.g. __dt__Q29dCamera_c23@class$3665d_camera_cppFv + if left_prefix == right_prefix + && let Some(left_idx) = left_suffix.chars().position(|c| !c.is_numeric()) + && let Some(right_idx) = right_suffix.chars().position(|c| !c.is_numeric()) + { + // e.g. d_camera_cppFv (after the unique ID) + left_suffix[left_idx..] == right_suffix[right_idx..] + } else { + false } + } else if let Some((prefix, suffix)) = left_name.split_once(['$', '.']) + && suffix.chars().all(char::is_numeric) + { + // Match Metrowerks symbol$1234 against symbol$2345 + // and GCC symbol.1234 against symbol.2345 right_name .split_once(['$', '.']) .is_some_and(|(p, s)| p == prefix && s.chars().all(char::is_numeric)) diff --git a/objdiff-core/src/diff/mod.rs b/objdiff-core/src/diff/mod.rs index efbe2c2..c8c9c31 100644 --- a/objdiff-core/src/diff/mod.rs +++ b/objdiff-core/src/diff/mod.rs @@ -7,6 +7,7 @@ use alloc::{ use core::{num::NonZeroU32, ops::Range}; use anyhow::Result; +use itertools::Itertools; use crate::{ diff::{ @@ -759,18 +760,16 @@ fn find_symbol( return closest_match_symbol_idx; } - // Try to find an exact name match - if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|(_, symbol)| { - symbol.name == in_symbol.name && symbol_section_kind(obj, symbol) == section_kind - }) { - return Some(symbol_idx); - } - - if let Some((symbol_idx, _)) = unmatched_symbols(obj, used).find(|&(_, symbol)| { - symbol_name_matches(&in_symbol.name, &symbol.name) - && symbol_section_kind(obj, symbol) == section_kind - && symbol_section(obj, symbol).is_some_and(|(name, _)| name == section_name) - }) { + // Try to find a symbol with a matching name + if let Some((symbol_idx, _)) = unmatched_symbols(obj, used) + .filter(|&(_, symbol)| { + symbol_name_matches(&in_symbol.name, &symbol.name) + && symbol_section_kind(obj, symbol) == section_kind + && symbol_section(obj, symbol).is_some_and(|(name, _)| name == section_name) + }) + .sorted_unstable_by_key(|&(_, symbol)| (symbol.section, symbol.address)) + .next() + { return Some(symbol_idx); }