M3: vtable scan + MSVC RTTI walk + 3 new tables
Adds detection of statically-allocated MSVC vtables in .rdata/.data: - New `xenia_analysis::vtables` walks read-only sections looking for runs of ≥3 contiguous big-endian u32 values where each value lands on a known function start (from M1's corrected functions table). 2-slot runs are rejected to keep false-positive rate down. - For each candidate the MSVC RTTI walk vtable[-1] → CompleteObjectLocator → TypeDescriptor → mangled name is attempted; on success the demangled class name is recorded along with a best-effort RTTIClassHierarchyDescriptor walk to fill base_classes_json. On failure (RTTI stripped — common for shipped game binaries) the class is named ANON_Class_<fnv1a-hash> keyed by sorted method-PC list, so identical vtables collapse to one entry. - DB: new tables `vtables`, `methods`, `classes` with indices on function_address and rtti_present. `write_analysis_results` takes a `&[Vtable]` slice; `write_disasm` (back-compat) passes empty. - cmd_dis wires the scan after xref analysis using `func_analysis.functions.keys()` as the function-start oracle. Validation on Sylpheed (RTTI stripped, as expected): 722 vtables / 499 unique classes / 5571 methods. Sanity invariant: every methods.function_address joins to functions.address (0 broken refs). Largest vtable: 131 slots. Tests 617→621 (+4 vtable unit tests covering 3-slot detect, 2-slot reject, synth name stability, and synth name divergence). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4051,6 +4051,21 @@ fn cmd_dis(
|
||||
"xref analysis complete"
|
||||
);
|
||||
|
||||
// Vtable + RTTI scan (M3). Uses M1's corrected function-start set as the
|
||||
// pointer-validity oracle; runs over .rdata + .data.
|
||||
let function_starts: std::collections::BTreeSet<u32> =
|
||||
func_analysis.functions.keys().copied().collect();
|
||||
let vtables = xenia_analysis::vtables::analyze(
|
||||
&pe_image, base, §ions, &function_starts,
|
||||
);
|
||||
let rtti_count = vtables.iter().filter(|v| v.rtti_present).count();
|
||||
info!(
|
||||
vtables = vtables.len(),
|
||||
rtti = rtti_count,
|
||||
anon = vtables.len() - rtti_count,
|
||||
"vtable scan complete",
|
||||
);
|
||||
|
||||
// Build DisasmInfo
|
||||
let disasm_info = xenia_analysis::formatter::DisasmInfo {
|
||||
image_base: base,
|
||||
@@ -4074,6 +4089,7 @@ fn cmd_dis(
|
||||
&func_analysis,
|
||||
&xref_result.labels,
|
||||
&xref_result.xrefs,
|
||||
&vtables,
|
||||
)?;
|
||||
if matches!(analyze, AnalyzeMode::Sql | AnalyzeMode::Both) {
|
||||
w.create_sql_views()?;
|
||||
|
||||
Reference in New Issue
Block a user