4.9 KiB
TLS probes — offline build results (2026-10-09)
No console access was used. Both titles were built in the VM with the same SDK, compiler wrappers and
link recipe as the RPCS3 title (tools/launcher/build.sh is the template). They were not deployed.
Build: limactl shell ps5build -- bash tools/tlsprobe/build.sh EF|N (from the Mac combo worktree).
PPSA99391 (EF) — emulated TLS
- Output:
~/work/tlsprobe-EF/app/eboot.bin(556,179 bytes). obj/probe.elf(linker output): no PT_TLS.obj/eboot.elf(afterps5-native-tool link): zero-size TLS header (filesz 0, memsz 0, align 1) — the same shape as the known-good RPCS3 title.- Entry points:
__emutls_get_address(t, from libc.a) andps5_fast_emutls_get_address(T, our F). - The title also emits
__emutls_v.g_a,__emutls_v.g_magic,__emutls_v.g_z,__emutls_v.g_tracked(the E/F controls) plus amovq %fs:0inf_tcb().
PPSA99392 (N) — native TLS
-
Output:
~/work/tlsprobe-N/app/eboot.bin(554,419 bytes). -
obj/probe.elf: PT_TLS vaddr 0x8c718, filesz 0x10, memsz 0x31, align 0x8. -
obj/eboot.elf: PT_TLS filesz 0x10, memsz 0x31, align 0x20 (the converter rewrites align). -
probe.orelocations:R_X86_64_GOTTPOFF(initial-exec), matching the handoff's native-TLS note. -
TLS block layout lld assigned (st_value in
obj/probe.elf):symbol offset size section g_a0x00 4 .tdata g_magic0x08 8 .tdata g_z0x10 8 .tbss g_tracked0x18 24 .tbss __tls_guard0x30 1 .tbss -
The runtime
%fsoffset per variable is what the on-console layout diagnosis inprobe.cppprints (fs0,&magic - fs0, and a 128 KiB scan belowfs0for the magic value). Offline we can only record the TLS-block offsets above; theGOTTPOFFGOT slot is zero in the file and is filled by the loader, so the signed TPOFF is not visible statically.
Test coverage in probe.cpp (runs on the console; not run here)
- initial values of
a=42,magic=0x5EC0DE00A11CE5ED,z=0; each thread's own copy; main thread unchanged; distinct addresses across 16 threads (8std::thread+ 8pthread_create). - 1,000,000 self-increments per thread (isolation).
- 2,000 sequential create/join (fresh initial values each time).
- destructor counts for
std::threadreturn, raw return,pthread_exit,ps5_pthread_exit. SIGUSR1handler read/write of athread_local, on the main and a second thread.- E/F:
__emutls_get_addressandps5_fast_emutls_get_addressmust return the same address for the same__emutls_v.*control; 1e8-access ns/access for plain / E / F. - N:
%fs:0,&magic - fs0, the magic scan, and 1e8-access ns/access for native. - Results are appended to
/app0/tlsprobe-<variant>.txt; the title ends withsceSystemServiceLoadExec("exit").
Revision 2 — review fixes (2026-10-09)
Fixed in fast_emutls.c / probe.cpp / build.sh:
- Cache lifetime / use-after-free: removal now clears the slot value before tombstoning the key, so
a reader that already observed
key == tcbloadsNULL, never a pointer about to be freed. Insertion into an empty/tombstoned slot publishes aNULLvalue before claiming the slot, and an existing entry for the same tcb is updated in place (verified by the live-entry count below). - Allocation failures:
malloc/realloc/pthread_key_create/pthread_setspecificfailures nowabort()instead of dereferencing a NULL array (a failedreallocno longer loses the old array). - Invalid test design removed: E and F no longer share a
__emutls_control; the E/F address-equality assertion is gone. F is tested on controls of its own (g_f_ctl), E ong_e_ctl. - EF2 production-style (
PPSA99393):-DTLS_F_AS_STOCKgives a strong__emutls_get_addressthat forwards to F, so the whole title uses F (as a release would) and the common tests validate it. - Cache-lifetime check:
ps5_fast_emutls_cache_live()counts live entries; after 8 worker threads exit it must be 1 (the main thread's). - Logging: results are written to
/app0/tlsprobe-<variant>.txtand mirrored to the kernel log (sceKernelDebugOutText), so a failed file write still leaves evidence.
Build results (all three built OK, no PT_TLS in probe.elf; only the converter's zero-size TLS header in
eboot.elf):
| title | variant | eboot.bin | __emutls_get_address |
|---|---|---|---|
| PPSA99391 | EF (E stock + F) | 557,507 B | t (libc.a weak) |
| PPSA99393 | EF2 (F as the provider) | 556,291 B | T (our strong F) |
| PPSA99392 | N (native TLS) | 554,419 B | — (PT_TLS 0x10/0x31) |
Still not deployed; the runtime tests need the console.
- Heap growth across the 2,000 threads (
ps5_heap_stats) is declared in the probe but not yet wired into a leak check. scePthreadCreate(the SDK does not expose it) — rawpthread_createonly.- The runtime results themselves (the PS5 is off).