Fix Linux module-bounds detection: extend through Wine's anonymous PE mapping

Running the Linux build against a live game showed it stuck forever on
"scanning signatures": scanMaps only collected mappings whose file matched
eldenring.exe, but this Proton build backs just the 4 KB PE header with the
real path and maps the rest of the module (~94 MB of .text/.rdata/.data,
where every AOB signature lives) as one anonymous mapping with no path.
findModuleBase was handing the scanner the header alone.

moduleSpan (the matching logic, now pulled out of scanMaps as a pure
function for process_linux_test.go) extends the span through contiguous
anonymous mappings that follow the last named one, and stops at the first
mapping with its own path so it can't merge in an unrelated module.
Confirmed against the live process: all three signatures resolve, PlayerIns
confirms, and /deaths reports real numbers end to end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
emmatherockandClaude Sonnet 5 committed 2026-09-18 01:54:52 -03:00
1 parent 66aba2fff2
commit 356df6cbd5
3 files changed
+167 -32

No files matched your search

+13 -4
View File
@@ -89,10 +89,19 @@ Windows, mismas firmas y offsets):
(`findProcessID`/`scanMaps`): Proton levanta varios procesos: se
recorre `/proc/*/maps` y se toma el pid que tenga mapeado un archivo
terminado en `eldenring.exe`. Los mismos mapeos dan la base y el
tamaño del módulo para `findModuleBase`: puede venir partido en varios
tramos (`.text`/`.rdata`/`.data`), así que se toma el span completo
(mínimo inicio, máximo final) — alcanza, porque el escáner ya lee por
chunks y saltea los que no puede leer.
tamaño del módulo para `findModuleBase` (`moduleSpan`, la parte pura
de `scanMaps`, testeada en `process_linux_test.go`) — pero el tamaño
real **no** sale de sumar los tramos con ese nombre. Confirmado
corriendo contra un Proton real: el loader de PE de Wine sólo mapea
con el archivo real la página del header (unos pocos KB); el resto
del módulo — `.text`/`.rdata`/`.data`, donde viven las firmas — es UN
mapeo anónimo enorme (acá, ~94 MB) pegado justo después, sin ruta.
Quedarse con el span de los tramos nombrados solos deja al escáner con
el header y nada más: todas las firmas fallan y `resolvePointers` da
vueltas para siempre. `moduleSpan` extiende el span a través de
mapeos anónimos contiguos que sigan al último tramo nombrado —
contiguos y sin ruta nada más, para no comerse por accidente un
módulo distinto que justo cargue pegado.
- **Leer memoria vía `/proc/<pid>/mem`** (`ReadAt`, sin dependencias
externas), no `process_vm_readv(2)` crudo: mismo resultado, sin tener
que hacer un syscall a mano con structs `iovec` sin `golang.org/x/sys`.