You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Linux: dlsym(NULL, name) is rejected and DT_NEEDED-loaded libraries are invisible to AnyPS5's dlsym, so titles cannot resolve scriptingGetMem #694
Symptom. The title performs a process-wide lookup for its scripting-heap entry point and gets nil:
[dl] dlsym((nil), scriptingGetMem) -> (nil)
Two independent defects in dlsym_nid_postfix_impl()
(core/libs/prx/libkernel/Module/src/DynamicLoader.cpp) combine:
handle == 0 is not treated as the default/global scope - only -2 is - so the call fails with dlsym: invalid or unsupported module handle.
Even in the global branch, the search only walks the modules map, which contains modules created
through AnyPS5's own dlopen. Everything the dynamic loader pulled in via DT_NEEDED (all of libs/*.prx) is invisible, so a plain-name lookup that ld.so would happily resolve is reported
missing.
Nothing in the tree exports scriptingGetMem / scriptingFreeMem (nor the NID ayuoL6Vjz2k), and no module imports it at link time, so it can only be satisfied as a run-time
export.
Fix we use locally.
handle == nullptr behaves like RTLD_DEFAULT; the global branch additionally falls back to ::dlsym(RTLD_DEFAULT, name) and then ::dlsym(RTLD_DEFAULT, Nid::ComputeNid(name, "")).
Added scriptingGetMem(alignment, bytes) and scriptingFreeMem(pointer) to core/libs/prx/libc/src/ApplicationHeap.cpp, wrapping ApplicationHeapAlign / ApplicationHeapFree.
After that the lookup resolves and the allocator behaves identically to Kyty's - same arguments, same
block size, same stride:
Questions. Is scriptingGetMem/scriptingFreeMem intentionally omitted (i.e. does the engine use
a different scripting heap path on this title)? And would you accept the RTLD_DEFAULT fallback for DT_NEEDED-loaded libraries, or should the loader register every libs/*.prx in the modules map at
start-up the way the Windows path loads them all explicitly?
Both defects were needed to make the lookup fail: handle 0 was rejected outright, and even the global
branch never saw DT_NEEDED-loaded libraries. After the change:
before [dl] dlsym((nil), scriptingGetMem) -> (nil)
after [dl] dlsym((nil), scriptingGetMem) -> 0x7f03d8a71370
The semantics question you will probably want answered: scriptingGetMem(alignment, size) is the right
shape, established from KytyPS5 (KernelApplicationHeapGetMem in src/libs/libKernel.cpp, which floors
alignment at 0x10 and rejects non-power-of-two) rather than SharpEmu's malloc(size) alias, and Raeen
reaches the same conclusion independently from a different IL2CPP title (docs/unity-dlsym-blocker.md).
The block sequence then matches Kyty byte for byte:
We deliberately did not add scriptingRealloc/scriptingCalloc: if the first argument really is an
alignment, guessing the argument order on a resize corrupts the guest heap, and the references that
boot this engine implement only the get/free halves. scriptingFreeMem is unambiguous (one pointer, no
return) so it is included; note the title does not call it at this phase (4 gets, 0 frees).
One more data point for the "should the loader register DT_NEEDED libraries in the module map"
question: we kept the ::dlsym(RTLD_DEFAULT, ...) fallback rather than registering every libs/*.prx,
because preloading them globally would let the bundled guest libc interpose the allocator over the
whole process.
Partly addressed by #864: dlsym with handle 0 or -2 now takes the default scope (global modules, then the process scope filtered with dladdr to converted modules), asserted by guest_dynamic_loader (dlsym(nullptr, "GuestModuleAdd")); still open: scriptingGetMem/scriptingFreeMem are not exported (dropped from the PR on purpose, #780 closed) and no test exercises the process-scope fallback itself.
Useful split, and the guest_dynamic_loader assertion covers the part we needed for the engine's own symbols.
For the record on scriptingGetMem and scriptingFreeMem, in case it matters when #780's decision is
revisited: our title (PPSA28997) is a real-world case of the need. Its managed runtime resolves both by
name through the process scope rather than importing them, and with neither present it ends up with a null
scripting allocator and leaves managed objects unconstructed. We carry the two exports as a title-local patch
in our own fork, so we are not asking for an engine change and this is not a request — just the data point
that at least one shipping title requires them by name.
On the missing test for the process-scope fallback: our title is the case that exercises it, but it is not
something CI here can run. If a synthetic test would be useful, the smallest one I can see is a module that
resolves a symbol which exists only in the process scope and is absent from every registered global module,
then asserts the fallback path returns it. I would rather propose that than guess at your fixture layout, so
say the word and I will write it against current main.
Closing: #864 makes dlsym with handle 0 take the default scope, including the converted modules, which is what this reported. Exporting scriptingGetMem/scriptingFreeMem needs a title that reaches them on main, as its own issue. If the lookup still fails on current main, comment here.
Activity
jeffrey12cali commentedon Oct 5, 2026
Confirmed and drafted in #780 (third commit).
Both defects were needed to make the lookup fail: handle 0 was rejected outright, and even the global
branch never saw
DT_NEEDED-loaded libraries. After the change:The semantics question you will probably want answered:
scriptingGetMem(alignment, size)is the rightshape, established from KytyPS5 (
KernelApplicationHeapGetMeminsrc/libs/libKernel.cpp, which floorsalignment at
0x10and rejects non-power-of-two) rather than SharpEmu'smalloc(size)alias, and Raeenreaches the same conclusion independently from a different IL2CPP title (
docs/unity-dlsym-blocker.md).The block sequence then matches Kyty byte for byte:
We deliberately did not add
scriptingRealloc/scriptingCalloc: if the first argument really is analignment, guessing the argument order on a resize corrupts the guest heap, and the references that
boot this engine implement only the get/free halves.
scriptingFreeMemis unambiguous (one pointer, noreturn) so it is included; note the title does not call it at this phase (4 gets, 0 frees).
One more data point for the "should the loader register
DT_NEEDEDlibraries in the module map"question: we kept the
::dlsym(RTLD_DEFAULT, ...)fallback rather than registering everylibs/*.prx,because preloading them globally would let the bundled guest
libcinterpose the allocator over thewhole process.
adriadam10 commentedon Oct 10, 2026
Partly addressed by #864:
dlsymwith handle 0 or -2 now takes the default scope (global modules, then the process scope filtered withdladdrto converted modules), asserted byguest_dynamic_loader(dlsym(nullptr, "GuestModuleAdd")); still open:scriptingGetMem/scriptingFreeMemare not exported (dropped from the PR on purpose, #780 closed) and no test exercises the process-scope fallback itself.jeffrey12cali commentedon Oct 10, 2026
Useful split, and the
guest_dynamic_loaderassertion covers the part we needed for the engine's own symbols.For the record on
scriptingGetMemandscriptingFreeMem, in case it matters when #780's decision isrevisited: our title (
PPSA28997) is a real-world case of the need. Its managed runtime resolves both byname through the process scope rather than importing them, and with neither present it ends up with a null
scripting allocator and leaves managed objects unconstructed. We carry the two exports as a title-local patch
in our own fork, so we are not asking for an engine change and this is not a request — just the data point
that at least one shipping title requires them by name.
On the missing test for the process-scope fallback: our title is the case that exercises it, but it is not
something CI here can run. If a synthetic test would be useful, the smallest one I can see is a module that
resolves a symbol which exists only in the process scope and is absent from every registered global module,
then asserts the fallback path returns it. I would rather propose that than guess at your fixture layout, so
say the word and I will write it against current
main.oneandonlydean commentedon Oct 14, 2026
Closing: #864 makes
dlsymwith handle 0 take the default scope, including the converted modules, which is what this reported. ExportingscriptingGetMem/scriptingFreeMemneeds a title that reaches them on main, as its own issue. If the lookup still fails on current main, comment here.