PPSA16064 (Minecraft Dungeons II) ships its own libc.prx, which imports
two C++ runtime symbols from libSceLibcInternal that no host library
exported, so the title failed to load:
- std::__sce_v2::_Xbad_function_call() (_ZNSt8__sce_v219_Xbad_function_callEv),
the inline-namespace spelling of the existing std::_Xbad_function_call.
It throws std::bad_function_call the same way.
- operator delete(void*, std::size_t, std::align_val_t)
(_ZdlPvmSt11align_val_t), the sized form of the existing aligned
delete. It frees the pointer the same way and ignores the size.
application_heap_default frees an aligned block through the sized form
and checks that a null pointer frees nothing. exception_runtime checks
that the new thrower throws a standard exception; it is built only on
Linux and was not run here (Windows 11, MinGW GCC 15.2).
The finalizer was registered with std::atexit from the heap handshake, which
runs after libc's own __cxa_atexit runner and the title's exit callbacks
register theirs. std::atexit runs handlers in reverse registration order, so
the heap was torn down first: an exit user that allocated then threw
"application heap: allocator has been finalized" out of a destructor path,
which terminates the process.
Register the finalizer from a namespace-scope object instead, so it is
registered when libc is loaded and therefore runs last, and make finalize()
idempotent rather than throwing on a second pass.
application_heap_exit_order registers a guest exit callback that allocates
through the heap and aborts with "application heap: allocator has been
finalized" on the previous code.
SceLibcMallocReplace lists calloc before realloc (malloc, free, calloc,
realloc, memalign, reallocalign, posix_memalign, ...), as the table in
retail titles shows. realloc therefore called the application's calloc
with the old pointer as the element count, and calloc called realloc.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>