M5.5: this-flow indirect-dispatch resolution via vptr-write inference
Closes the dominant case M5 could not resolve — `lwz vt, off(this);
lwz fn, slot(vt); mtctr; bcctrl` (real C++ dispatch). Implements
class-membership inference using constructor-side vptr writes as an
oracle for which vtables can land at each offset.
## Algorithm
Phase 1 — vptr-write scan: walk every function with the existing
lis+addi register tracker. When `stw rA, off(rB)` writes a known M3
vtable address into off(rB), record `(vtable_addr, vptr_offset,
writer_pc, writer_function)` as a constructor-side vptr write.
Phase 2 — invert by offset: `vtables_by_offset[off] = {V : V written
at off in any ctor}`.
Phase 3 — dispatch detection: from each `bcctrl LK=1`, walk back
≤16 instructions looking for the canonical chain. Bail on register
clobber, branch, or label (basic-block) boundary.
Phase 4 — edge emission: for `(dispatch_pc, vptr_off, slot)`, emit one
`xrefs.kind='ind_call'` row per vtable V where:
- `vtables_by_offset[vptr_off]` contains V, AND
- `V.length > slot` (V actually has a method at that slot)
Multi-candidate sites (the common case at offset 0) are an
over-approximation; downstream queries filter to single-candidate sites
for high confidence:
`WHERE candidate_count=1` in `indirect_dispatch_sites`.
## Schema
NEW TABLES:
- `vptr_writes(writer_pc, vtable_address, vptr_offset, writer_function)`
- `indirect_dispatch_sites(dispatch_pc PK, vptr_offset, slot, candidate_count)`
- `indirect_dispatch_candidates(dispatch_pc, vtable_address, method_address)`
NEW INDICES on vtable_address / vptr_offset / method_address /
(vptr_offset, slot) for fast joins.
## Sylpheed yield
- 567 vptr writes / 214 vtables / 29 offsets (offset 0 = 88%).
- 6,842 dispatch sites resolved: 97 single-candidate (high-confidence) +
6,745 multi-candidate.
- 687,963 ind_call xref rows.
- 2,746 newly-reachable functions via v_indirect_reachability_from_entry
(compared to 0 with M5 alone).
- Audit-009 cluster: functions including 0x823BC9E0, 0x823BC290,
0x823BC5A0, 0x823BB158 newly reachable — actionable for the
renderer-plateau hunt.
Tests 640→649 (+4 ind_dispatch_typed unit tests + 5 from tighter golden
expansion). Schema golden + write_analysis_results signature updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -394,11 +394,71 @@ byte-identical digests (`instructions=2000005`).
|
||||
|
||||
---
|
||||
|
||||
## Layer M5.5 — `this`-flow indirect-dispatch resolution (landed)
|
||||
|
||||
### Schema additions
|
||||
- New table `vptr_writes(writer_pc, vtable_address, vptr_offset, writer_function)` —
|
||||
every detected `stw rVtable, vptr_off(rThis)` site.
|
||||
- New table `indirect_dispatch_sites(dispatch_pc PK, vptr_offset, slot, candidate_count)` —
|
||||
one row per resolved dispatch.
|
||||
- New table `indirect_dispatch_candidates(dispatch_pc, vtable_address, method_address)` —
|
||||
one row per (dispatch × candidate vtable). Joined to existing
|
||||
`xrefs.kind='ind_call'` edges (one ind_call row per candidate).
|
||||
- New indices on `vptr_writes.vtable_address`, `vptr_writes.vptr_offset`,
|
||||
`indirect_dispatch_candidates.method_address`,
|
||||
`indirect_dispatch_candidates.vtable_address`,
|
||||
`indirect_dispatch_sites.(vptr_offset, slot)`.
|
||||
|
||||
### What this layer does (class-membership inference)
|
||||
1. **Phase 1 — vptr-write scan**: walk every function with the lis+addi
|
||||
tracker; whenever `stw rA, off(rB)` writes a known M3 vtable address,
|
||||
record `(vtable_addr, vptr_offset, writer_pc)`.
|
||||
2. **Phase 2 — invert**: build `vtables_by_offset[vptr_off] = {V}` for the
|
||||
set of vtables ever written at that offset.
|
||||
3. **Phase 3 — dispatch detection**: walk back ≤16 instructions from each
|
||||
`bcctrl`/`bctr LK=1`, find the canonical
|
||||
`lwz vt, off(this); lwz fn, slot*4(vt); mtctr fn` chain. Extract
|
||||
`(vptr_off, slot)`. Bail on register clobber, branch, or label
|
||||
boundary.
|
||||
4. **Phase 4 — emit**: for each `(dispatch_pc, vptr_off, slot)`, emit one
|
||||
`xrefs.kind='ind_call'` row per candidate vtable that has a
|
||||
matching slot. Multi-candidate rows are an over-approximation.
|
||||
|
||||
### What this layer does NOT do
|
||||
- No alias resolution at multi-candidate sites — emits one edge per
|
||||
matching vtable. Downstream queries should filter
|
||||
`indirect_dispatch_sites WHERE candidate_count=1` for high-confidence
|
||||
edges.
|
||||
- No flow-sensitive analysis: register state is killed at every label
|
||||
(basic-block boundary) and at `bl`/`bcl` calls (volatile r0..r12 +
|
||||
ctr). We do NOT propagate values across calls in the chain-walker.
|
||||
- No tracking of vptr writes via X-form indexed (`stwx`), VMX, or
|
||||
multiword stores. Only D-form `stw rA, off(rB)`.
|
||||
- Does not synthesise vptr writes for inlined / elided constructors.
|
||||
If a class never has a writer at offset `vptr_off`, dispatches
|
||||
through that offset find no candidates.
|
||||
|
||||
### Sylpheed yield
|
||||
- 567 vptr writes covering 214 distinct vtables (~30% of M3's 722).
|
||||
- 29 distinct vptr offsets used; offset 0 dominates (501/567 = 88%,
|
||||
single-inheritance).
|
||||
- **6,842 dispatch sites resolved**: 97 single-candidate
|
||||
(high-confidence) + 6,745 multi-candidate (over-approximation).
|
||||
- 687,963 `ind_call` xref rows total.
|
||||
- **2,746 newly-reachable functions** via the M5 BFS view
|
||||
(`v_indirect_reachability_from_entry`) compared to call/j/br alone.
|
||||
- Audit-009 cluster (renderer plateau): functions newly visible
|
||||
include `0x823BC9E0`, `0x823BC290`, `0x823BC5A0`, `0x823BB158`,
|
||||
`0x823BB1E0`, `0x823BCAF0`, `0x823BC4C8` — actionable starting
|
||||
points for the cluster's reachability hunt.
|
||||
|
||||
### Reference docs
|
||||
- IBM PowerPC ABI (volatile/non-volatile register partition).
|
||||
- Itanium C++ ABI on vtable layout (offset-from-`this` model adapted
|
||||
by MSVC for Win32 PPC).
|
||||
|
||||
## Forward work (not yet landed)
|
||||
|
||||
- **M5.5** — `this`-flow extension to M5. Resolve vtable dispatches via
|
||||
`lwz vt, off(this)` patterns by tracing constructor-side vptr writes.
|
||||
Highest-value future work for the audit-009 cluster renderer hunt.
|
||||
- **M9.5** — full `__CxxFrameHandler` scope-table parsing (try/catch
|
||||
range names, per-state cleanup actions).
|
||||
- **M11.5** — walk the static-initialiser driver call chain from the
|
||||
|
||||
Reference in New Issue
Block a user