Every call-graph result in this repo was computed over direct (E8) edges.
5,045 of the 5,207 functions named by a vftable slot have zero direct call
sites, so all of those results were lower bounds. Lane Z's dominant RNG
consumer hung off exactly such an edge.
tools/vtable_map.py builds, from the RTTI walk plus a full sweep to the next
function start (rule 17):
* vftable -> class -> sub-object offset -> slot -> target, and its inverse
* the class hierarchy from the RTTI base lists, so an abstract interface
with one concrete override resolves uniquely
* constructor-derived member typing (ctor result -> [this+d])
* the slot index at every indirect call site, with a backward register
resolver that refuses to cross a branch target rather than guess
* `this`-carrier spans and this/member call-graph propagation of class
Validation (12/12): rediscovers ServerTradeManagerImpl slot 10 ->
GenerateTradeRaidEncounters from the dispatch at 0x007d8469 with nothing
hand-fed, and re-derives the *Impl rule for both managers. Receiver-class
pinning reaches only 2.5% of the 6,398 virtual sites, at 0.6% out-of-range
against a 70% chance baseline; the displacement-only route measured worse
than random (81% vs 58%) and is rejected outright.
Closes lane K's tier-4 blind spot: all nine phase-23 calls and both phase-33
calls named. Four of the eleven reach a draw on the strategic generator
(StrategyServer+0x16c) at eight instruction-verified sites, none ever
observed firing — so "the tail draws nothing" is a property of eight turns,
not of the code. Also resolves the nine parked inlined-draw functions to
their vtable roots (correcting how that was recorded: none is itself in a
vftable; their topmost ancestors are), and finds 14,958 inter-function tail
jump edges without which three of them look like dead code.
38 lines
1.6 KiB
JSON
38 lines
1.6 KiB
JSON
{
|
|
"_readme": [
|
|
"Hand-asserted receiver typings for functions that appear in NO vftable and",
|
|
"are not constructors, so tools/vtable_map.py's automatic seeds cannot reach",
|
|
"them. Every entry must carry evidence a reader can re-derive. These are",
|
|
"seeds for the `this`-propagation fixpoint, so one entry types a whole",
|
|
"subtree -- which is also why a wrong entry is expensive. Keep it short."
|
|
],
|
|
"owners": [
|
|
{
|
|
"func": "0x007dc6c0",
|
|
"name": "StrategyServer::ProcessTurn",
|
|
"class": "Game::StrategyServer",
|
|
"offset": 0,
|
|
"evidence": [
|
|
"first member op is `inc [esi+0x8]` = ModCount; lane Z read S+0x8 as",
|
|
"ModCount from StrategyServer::Write's wire tag at 0x0079fb2f",
|
|
"hands its own `this` to 0x007d7f70, which dispatches on [this+0x158]",
|
|
"and [this+0x15c]; the StrategyServer ctor 0x007d78d0 constructs a",
|
|
"ServerTradeManagerImpl into +0x158 and a ServerSpyManager into +0x15c",
|
|
"(vtable_map field 0x158 / field 0x15c)"
|
|
]
|
|
},
|
|
{
|
|
"func": "0x007d92a0",
|
|
"name": "StrategyServer::OnAllCombatDone_Tail",
|
|
"class": "Game::StrategyServer",
|
|
"offset": 0,
|
|
"evidence": [
|
|
"lane K read [ebx+0x54]/[ebx+0x58] as vector<ServerPlayer*> Players",
|
|
"dispatches on [ebx+0x158] and [ebx+0x15c] in one straight-line block,",
|
|
"matching the two managers the StrategyServer ctor 0x007d78d0 builds",
|
|
"there; slot 15 needs >=16 slots and slot 13 needs >=14, which the two",
|
|
"concrete vtables 0x00a31b74 (22) and 0x00a3073c (18) supply"
|
|
]
|
|
}
|
|
]
|
|
}
|