Skip to content

Commit e5c0c43

Browse files
authored
Improve MWCC class unique ID normalization; normalize on the demangled name (#411)
* Normalize MWCC class unique IDs on the demangled name * Clippy * Fix MSVC test
1 parent 94c7761 commit e5c0c43

1 file changed

Lines changed: 70 additions & 8 deletions

File tree

‎objdiff-core/src/obj/read.rs‎

Lines changed: 70 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,40 @@ fn map_section_kind(section: &object::Section) -> SectionKind {
4040
}
4141
}
4242

43+
/// Normalize Metrowerks class names that are suffixed with a unique ID and the source filename.
44+
/// This is done on the demangled name as the mangled names include the length of the name before
45+
/// the name, which varies depending on the length of the unique ID.
46+
fn normalize_mwcc_unique_id_with_filename(demangled_name: &str) -> Option<String> {
47+
const DUMMY_UNIQUE_ID: &str = "$0000";
48+
let mut parts = Vec::new();
49+
let mut next = demangled_name;
50+
while let Some((prefix, suffix)) = next.split_once("$")
51+
&& let Some(idx) = suffix.chars().position(|c| !c.is_numeric())
52+
&& idx > 0
53+
{
54+
parts.push(prefix);
55+
next = &suffix[idx..];
56+
}
57+
parts.push(next);
58+
if parts.len() >= 2 {
59+
return Some(parts.join(DUMMY_UNIQUE_ID));
60+
}
61+
None
62+
}
63+
4364
/// Check if a symbol's name is partially compiler-generated, and if so normalize it for pairing.
4465
/// e.g. symbol$1234 and symbol$2345 will both be replaced with symbol$0000 internally.
45-
fn get_normalized_symbol_name(name: &str) -> Option<String> {
66+
fn get_normalized_symbol_name(name: &str, demangled_name: &Option<String>) -> Option<String> {
4667
const DUMMY_UNIQUE_ID: &str = "0000";
4768
const DUMMY_UNIQUE_MSVC_ID: &str = "00000000";
48-
if let Some((prefix, suffix)) = name.split_once("@class$")
69+
if let Some(demangled_name) = demangled_name
70+
&& let Some((_, suffix)) = name.split_once("$")
4971
&& let Some(idx) = suffix.chars().position(|c| !c.is_numeric())
5072
&& idx > 0
5173
{
52-
// Match Metrowerks anonymous class symbol names, ignoring the unique ID.
53-
// e.g. __dt__Q29dCamera_c23@class$3665d_camera_cppFv
54-
// and: __dt__Q29dCamera_c23@class$1727d_camera_cppFv
55-
let suffix = &suffix[idx..];
56-
Some(format!("{prefix}@class${DUMMY_UNIQUE_ID}{suffix}"))
74+
// Match Metrowerks symbols that have a class name, a unique ID, and the source filename.
75+
// Unlike the other normalizations, this is done on the demangled name.
76+
normalize_mwcc_unique_id_with_filename(demangled_name)
5777
} else if let Some((prefix, suffix)) = name.split_once('$')
5878
&& suffix.chars().all(char::is_numeric)
5979
{
@@ -174,7 +194,7 @@ fn map_symbol(
174194
.and_then(|m| m.virtual_addresses.as_ref())
175195
.and_then(|v| v.get(symbol.index().0).cloned());
176196
let section = symbol.section_index().and_then(|i| section_indices.get(i.0).copied());
177-
let normalized_name = get_normalized_symbol_name(&name);
197+
let normalized_name = get_normalized_symbol_name(&name, &demangled_name);
178198
if is_symbol_name_compiler_generated(&name) {
179199
flags |= SymbolFlag::CompilerGenerated;
180200
}
@@ -1205,6 +1225,48 @@ fn parse_mw_comment_syms(obj_file: &object::File) -> Result<Option<Vec<CommentSy
12051225
#[cfg(test)]
12061226
mod test {
12071227
use super::*;
1228+
use crate::diff::Demangler;
1229+
1230+
#[test]
1231+
fn test_get_normalized_symbol_name() {
1232+
// Metrowerks in-function static variable.
1233+
let left = "eff_scale$24884";
1234+
let right = "eff_scale$21920";
1235+
assert_eq!(
1236+
get_normalized_symbol_name(left, &Demangler::Codewarrior.demangle(left)),
1237+
get_normalized_symbol_name(right, &Demangler::Codewarrior.demangle(right))
1238+
);
1239+
1240+
// Metrowerks anonymous class. Uses @class prefix, unique ID, then filename suffix.
1241+
let left = "__dt__Q29dCamera_c23@class$3665d_camera_cppFv";
1242+
let right = "__dt__Q29dCamera_c23@class$1727d_camera_cppFv";
1243+
assert_eq!(
1244+
get_normalized_symbol_name(left, &Demangler::Codewarrior.demangle(left)),
1245+
get_normalized_symbol_name(right, &Demangler::Codewarrior.demangle(right))
1246+
);
1247+
// Metrowerks class defined in function body. Uses class name prefix, unique ID, then filename suffix.
1248+
let left = "makeDL__Q219TMBindShadowManager26TSetup1$2172ShadowUtil_cppFv";
1249+
let right = "makeDL__Q219TMBindShadowManager25TSetup1$874ShadowUtil_cppFv";
1250+
assert_eq!(
1251+
get_normalized_symbol_name(left, &Demangler::Codewarrior.demangle(left)),
1252+
get_normalized_symbol_name(right, &Demangler::Codewarrior.demangle(right))
1253+
);
1254+
// Metrowerks class defined in function body, but the unique ID appears again within the function parameters.
1255+
let left = "__as__Q210daB_ZANT_c31dZantSph_c$132966d_a_b_zant_cppFRCQ210daB_ZANT_c31dZantSph_c$132966d_a_b_zant_cpp";
1256+
let right = "__as__Q210daB_ZANT_c30dZantSph_c$55068d_a_b_zant_cppFRCQ210daB_ZANT_c30dZantSph_c$55068d_a_b_zant_cpp";
1257+
assert_eq!(
1258+
get_normalized_symbol_name(left, &Demangler::Codewarrior.demangle(left)),
1259+
get_normalized_symbol_name(right, &Demangler::Codewarrior.demangle(right))
1260+
);
1261+
1262+
// MSVC anonymous class.
1263+
let left = "?CheckContextOr@?A0x24773155@@YA_NPBVDataArray@@@Z";
1264+
let right = "?CheckContextOr@?A0xddf6240c@@YA_NPBVDataArray@@@Z";
1265+
assert_eq!(
1266+
get_normalized_symbol_name(left, &Demangler::Msvc.demangle(left)),
1267+
get_normalized_symbol_name(right, &Demangler::Msvc.demangle(right))
1268+
);
1269+
}
12081270

12091271
#[test]
12101272
fn test_combine_sections() {

0 commit comments

Comments
 (0)