D521 - The callback has no branch that avoids the read, so one of D519's two readings is dead
measured - 2026-09-03 (the guest's own code, and a watchpoint on the slot it is installed in)
D519 left two readings of the wall and refused to choose between them without a measurement. One of them can be settled by reading forty instructions, and it is now settled.
Reading (2): "the eboot code has a branch that on hardware does not reach the read"
It does not. The whole function, entry to fault:
0x13891b0 push rbp; mov rbp,rsp; push r15..rbx; sub rsp,0x98
0x13891c4 mov r13,[0x1992A10] the stack-guard pointer
0x13891cb lea r12,[rbp+0x10] the argument, a character array by value
0x13891de ... zero four locals, set one to 0x48
0x1389205 cmp byte [rbp+0x10],0
0x1389209 je 0x1389246 <- branch one
0x138920e call 0x1658960 strlen
0x1389216 cmp rax,0x10
0x138921a jb 0x1389248 <- branch two
... allocate, copy ...
0x1389246 xor ebx,ebx
0x1389248 mov rdi,r14; mov rsi,r12; mov rdx,rbx
0x1389251 call 0x16588F0 memcpy
0x1389256 mov byte [r14+rbx],0
0x1389262 mov r14,[0x1A30610] <- the singleton
0x1389269 mov rbx,[r14+0xa0] <- FAULT
Both conditional jumps land at or before
0x1389248, which flows unconditionally into the singleton
read. There is no path through this function that does not
dereference it. It builds a std::string from a character
array and then reads the memory manager, every time.
The last calls before the fault - strlen then
memcpy, from eboot addresses - are this function's own
string construction, which is the check that the decode is right rather
than plausible.
How the module has the address at all
The eboot exports exactly one symbol, so the module cannot import this function. It is installed into a table:
0x1389148 lea rcx,[rip+0x61] -> 0x13891B0
0x1389168 mov [rip+0x71c149],rcx -> 0x1AA52B8
And a watchpoint on that slot shows both halves of its life:
ORBISTOUN_WATCHPOINT=0x400001aa52b8+8:rw
touched after the access at image+0x138916f; it now holds 0x4000013891b0 <- installed
touched after the access at image+0x13891b0; it now holds 0x4000013891b0 <- called through
The second is an indirect call: a data breakpoint fires after the access and the instruction pointer is then the target, which is the function itself. The slot is read once in the whole run, and that read is the call that faults.
So the surviving reading is the first one
The chain, every step measured:
eboot installs callback 0x1389130 -> [0x1AA52B8] = 0x13891B0
eboot loads Il2CppUserAssemblies 0xf23a06
eboot calls into the module 0xf23ac3 -> PLT -> module +0x13d5e90
module calls back through the slot -> 0x13891B0
0x13891B0 dereferences the memory manager -> null -> read of 0xa0
...and 0xf269e2 would have created it, after 0xf23970 returns.
The module invokes an eboot callback before the memory manager exists. Since the callback cannot avoid the read, and the singleton has exactly one writer which runs later (D518, D519), the anomaly must be that the callback is invoked at all at this point.
What is still open, stated so it is not read as settled
Why the module invokes it. A plausible shape - it takes a short text label and then does memory accounting, and the fault registers show a four-character argument - is that it is a naming or scope hook that a module reports through. If a module only reports through it on an unusual path, then the real fault is whatever put the module on that path, and orbistoun's 85 unresolved imports in that module are the obvious suspects. That is a hypothesis and it is not tested; naming it here is not adopting it.
What is closed is that no branch inside the callback was ever going to save this, so there is no point looking for one.