Runtime: async invocation stacks alias thread stacks, so every interrupt handler corrupts the interrupted frame's return address
Los mantenedores suelen responder en 5 días
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 4/5
- Tiempo estimado
- 3-5 días
- Aptitud para principiantes
- 32/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- cpp
Línea de trabajo
Start in ps2xRuntime/include/ps2_runtime.h and ps2xRuntime/src/lib/ps2_runtime.cpp at reserveAsyncCallbackStack() and the two resets of m_asyncCallbackStackTop to PS2_RAM_SIZE. Trace how SetupThread/CreateThread pass stack address and size into EeScheduler and how GuestThread::stack is stored (top vs base). Done when async stacks are derived from declared thread extents, overlap is refused, and stacks entirely below the async floor are ignored so titles can take interrupts without clobbering $ra.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
What happens
PS2Runtime::reserveAsyncCallbackStack() carves stacks for asynchronous invocations (INTC/DMAC handlers, GS callbacks, alarms) downwards from m_asyncCallbackStackTop, initialised to PS2_RAM_SIZE (0x02000000):
ps2xRuntime/include/ps2_runtime.h--uint32_t m_asyncCallbackStackTop = PS2_RAM_SIZE;ps2xRuntime/src/lib/ps2_runtime.cpp:507and:976-- both reset it toPS2_RAM_SIZE
So the first reservation returns 0x01FFFFF0.
That is exactly where the guest's main thread stack lives. Measured at the first schedule in Ridge Racer V: sp=0x1fffff0. The "dedicated" invocation stack therefore aliases the stack of the very thread it interrupts, and each asynchronous handler overwrites the frames of the function it interrupted.
What it costs
Captured with a hardware watchpoint on 0x01FFFFA0:
--- write 1: 0x01FFFFA0 <- 0x0021DF40 ---
#0 rrv_00220470 <- its prologue saves its return address
#1 PS2Runtime::dispatchGuestBranch
#2 rrv_0021DE90 <- the game loop
#3 EeScheduler::run
--- write 2: 0x01FFFFA0 <- 0x00000000 ---
#0 rrv_0021FE88 <- interrupt handler, running as an invocation
#1 EeScheduler::run
FUN_00220470 keeps its return address at 16($sp) with sp = 0x01FFFF90, i.e. 0x01FFFFA0. The handler, started on an invocation stack beginning at 0x01FFFFF0, lays its own frame there and writes its $ra -- zero, as expected for an invocation -- right on top of it.
FUN_00220470 is then resumed at its epilogue, reloads $ra, and finds zero. jr $ra jumps to address 0. The scheduler sees pc == 0 with an empty invocation list and puts the thread to sleep; _start reaches its Exit. The game loop did not complete a single iteration.
Suggested fix
A constant reserve below the top of RAM proves the cause but is not a solution: nothing tells a constant how far down a thread stack reaches, and a game with many threads would exhaust the region.
The kernel already has the information. SetupThread (syscall 0x3C, InitMainThread) and CreateThread both receive a stack address and a stack size and pass them to EeScheduler. Derive the asynchronous region from the actual declared thread stack extents, and refuse the allocation when they would overlap.
Two traps worth knowing if you implement it:
- The meaning of
GuestThread::stackis not uniform -- see the companion issue aboutSetupThreadstoring a top whereCreateThreadstores a base. - A stack sitting entirely below the asynchronous floor constrains nothing. Ridge Racer V's
_spuCBThreadhas its stack at0x01DAD500, inside the game's.bssand below the0x01F00000floor; taking it into account pushed the ceiling under the floor, the allocation failed, and the runtime raised "EE invocation stack space exhausted". Only keep stacks whose top is above the floor.
Result after fixing it locally
| before | after | |
|---|---|---|
| game loop body completions | 0 | 2552 |
| main thread state | Dormant |
Running |
| GS kicks / primitives | 4 / 2 | 24 / 12 |
Any title that takes an interrupt during a nested call loses the interrupted frame's return address the same way.
Found while statically recompiling Ridge Racer V (NTSC-U, SLUS_200.02) with PS2Recomp.
Tested on commit 75d729ce; main was c5a9d02 at the time of writing, one README-only commit ahead.
Host: Windows 11 x64, clang-cl 22.1.3 + Ninja, MSVC toolset 14.51.36231, Windows SDK 10.0.26100.0, CMake 4.2.3.
None of these are specific to this game.
- Lenguaje dominante
- C++
- Estrellas
- 3.3k
- Forks
- 149
- Merge medio
- 5 d 10 h
- PR fusionados (30 d)
- 4
Preparar el entorno
Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de ran-j/PS2Recomp
-
VU0 macro VSQI / VSQD: address and source registers swapped in generated codePosiblemente ocupada @yanm1103 la tomó hace 1 día. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 5 días
-
Runtime: SampleTexture casts unbounded or NaN texture coordinates to int, which is undefined behaviourPosiblemente ocupada @luisbraganca la tomó hace 7 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
ran-j/PS2Recomp#273 · 2 comentarios ·
Los mantenedores suelen responder en 5 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 5 días
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
Los mantenedores suelen responder en 5 días
-
Runtime: writes to D_ENABLEW (0x1000F590) are never visible at D_ENABLER (0x1000F520), hanging any game that suspends the DMACPosiblemente ocupada @luisbraganca la tomó hace 7 días. Abierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
ran-j/PS2Recomp#257 · 1 comentario ·
Los mantenedores suelen responder en 5 días
Todos los issues de ran-j/PS2Recomp
Issues similares
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
Mac mini M6 (16 GB): Q2_0 streaming works - measured 11.7->27.2 tok/s + arena fix for sub-32 GB MacsAbierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
Los mantenedores suelen responder en 1 día
-
champion good first issue remake source/qq
Dificultad 2/5 1-3 horas Aptitud para principiantes 65/100
metasequoiaime/msime-windows#1025 ·
Los mantenedores suelen responder en 1 día
-
backlog bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 75/100
EchoTools/nevr-runtime#653 ·
Los mantenedores suelen responder en 1 día
-
`--fail-require-skip` never fires: run_tests.py searches stdout, but the unittest binary writes the skip summary to stderrPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
Los mantenedores suelen responder en 1 día