Skip to content

Dying Light 2 (PPSA02262) on Windows: what stops it between conversion and its own startup #1794

Open

Description

@tv7

Summary

Dying Light 2 Stay Human (PPSA02262, Techland C-Engine, content version 01.090.000, full 73 GB dump) on Windows does not start on main (4ddbf04). Below is everything between conversion and the title's own startup code. To find each next point I added local placeholders, which are not proposed here. With all of them, the title runs its own initialisation and stops at its own assertion Net_StartUp failed (CGame.cpp(2458), CGame::Initialize).

Setup: Windows 11, MinGW GCC 15.2.0, RTX 3070. Input layout: eboot.elf as the input, sce_module/libc.prx, and the title's loose root modules (EOSSDK-PS5-Shipping, ffmpeg, libNRD, runtime_prospero, the Wwise Ak* plugins) beside it.

1. Needed module name differs from the shipped file name

The executable's needed entry is EOSSDK-PS5-Shipping.debug_prx, but the dump ships EOSSDK-PS5-Shipping.prx. The relinker matches needed names by exact filename, so it skips the module without an error. The output then fails at start-up:

Failed to load module: ...\libs\EOSSDK-PS5-Shipping.debug_prx
GetLastError: 126 - The specified module could not be found.

Renaming the file to .debug_prx makes the relinker convert it and the load succeeds.

2. libSceNpCppWebApi has no provider (same as #1033)

EOSSDK-PS5-Shipping imports from libSceNpCppWebApi.prx (first: 5f9xDKxYjbY). The dump has no copy under any name, so this is a second title for #1033.

3. Imports no built library exports

NID Name Imported by
30Rh4ixbKy4 sceKernelFdatasync eboot
9WK-vhNXimw sceKernelAioSetParam eboot
Xweb+naPZ8Y sceNpWebApi2GetMemoryPoolStats eboot
-q-vbO859Tw (unknown, libSceVideoOut) eboot
vwtVpHQKelI (unknown, libSceVideoOut) eboot
GTfJ64qFCPw (unknown, libSceAcm) eboot
tuscfitnhEo, zXvd6iNyfgc, jltWpVKtetg, iNjkt9Poblw, HJ1n138CQ2g, E4a-ahM57QQ sceSslCreateConnection, sceSslConnect, sceSslRead, sceSslWrite, sceSslDeleteConnection, sceSslSetVerifyCallback ffmpeg

None of them is on main or in an open PR (checked with the overlap tool). #1119 covers the Http2, Ssl and Net pool statistics but not NpWebApi2's.

4. sceKernelMprotect on a bundled module's image fails on Windows

libNRD (NVIDIA Real-time Denoisers, bundled) calls sceKernelMprotect on its own image with prot=0x12 and then 0x11:

[memory] protect 00007ff832970000+0x11278 prot=0x12
terminate called after throwing an instance of 'std::invalid_argument'
  what():  Memory protection of a foreign image is not supported

DoMprotect (DirectMemory.cpp) accepts only the main executable's image on Windows. Linux registers the image and applies the protection.

5. Where it stops with all of the above worked around

ASRT: [Assert] | Net_StartUp failed
ASRT: [Assert] - Function: virtual int CGame::Initialize(...)

I haven't narrowed down which network call fails yet.

An earlier private fork (not upstream) got this title to rendering. The changes it needed are listed in the follow-up PRs I'll open one at a time.

AI-assisted: yes (Claude Code).

Activity

  1. tv7 commented on Oct 8, 2026

    @tv7
    ContributorAuthor

    Update: items 1 and 5 are the same problem, and item 5 leads to a second one with guest socket descriptors.

    Item 5 traced. With the title's own logging switched on (the flag at ELF 0x83e5698 and its level check, set from a debugger), it reports:

    [Net.Services] > [StartUp] StartUpOptions { .platform = object, .providers = 'psn,eos,pls' }
    [Lobby.Backend.Eos]  > [StartUp] EOS SDK version 1.18.1.2
    [Lobby.Backend.Eos]  > call to sceKernelLoadStartModule /app0//EOSSDK-PS5-Shipping.prx failed: 0x80020002
    [Lobby.Backend.Eos]  > [StartUp] fail
    

    The executable needs EOSSDK-PS5-Shipping.debug_prx (item 1), so I had renamed the file to get it relinked and loaded. At run time the title loads the same module by its shipped name, /app0//EOSSDK-PS5-Shipping.prx. dlopen then looks for app0/EOSSDK-PS5-Shipping.prx.guest.prx, which does not exist under that name, and returns ENOENT. The EOS backend fails, the lobby's start-up poll sees it, and Net::Services::StartUp returns the error that CGame::Initialize turns into Net_StartUp failed.

    When the relinked module is reachable under both names (a hard link, locally), the title logs [Lobby.Backend.Eos] > [StartUp] ok and [Lobby] > [Initialize] all backends are up, and Net_StartUp passes. So the fix for both items is in the relinker: match a needed X.debug_prx to a shipped X.prx and keep the shipped file name for the output. I'll open a PR for that.

    Next stop: socket descriptors and fd_set. Right after that, EOS's RtcNetworkThread (WebRTC) crashes:

    FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread'
      rip EOSSDK-PS5-Shipping...guest.prx+0xed1fd3
      write of 0x0000000024abfa60
      rcx 0000000010000005 ... rsi 0000000000400000 ... r12 0000000010000005
    

    It is FD_SET of guest socket 0x10000005 into a FreeBSD fd_set (1024 bits) on its stack at 0x22abfa60: 0x22abfa60 + 0x10000005 / 8 = 0x24abfa60, 32 MiB past the set. The title's sockets come from socket in libkernel, which numbers them from GuestSockets::FirstDescriptor (0x10000000). Any title that puts a socket into an fd_set (for select) writes outside it like this. On the console, socket descriptors share the process descriptor table, so they are small numbers.

    This is a design question, because #1706, #1727 and others dispatch on d >= FirstDescriptor, so I'm raising it here rather than in a PR. Should guest sockets get small descriptors (for example by reserving a host CRT descriptor per socket, as a private fork of mine did to get this title rendering), or is there a preferred direction?

  2. sktharunbalaji13-cmd commented on Oct 10, 2026

    @sktharunbalaji13-cmd
    Contributor

    Hi @tv7 — following up on the sceKernelFdatasync import (30Rh4ixbKy4, eboot) in issue #1794.

    A reviewer asked whether Dying Light 2 (PPSA02262) actually stops because this import is unresolved. Could you share the exact loader/startup log line showing NID 30Rh4ixbKy4 failing to resolve, and confirm that the title stops there on clean main without local placeholders?

    If the title proceeds past the import instead, that would also be useful to know. We want to establish the actual failure before adding the export.

  3. tv7 commented on Oct 10, 2026

    @tv7
    ContributorAuthor

    @sktharunbalaji13-cmd Yes: on clean main the title stops on this import, before any title code runs.

    Setup: Windows 11 Pro (10.0.26200), RTX 3070, GCC 15.2.0 UCRT (WinLibs), Release. Dying Light 2 (PPSA02262), relinked with the main build, no local placeholders.

    Clean main (b5f5362): the loader stops on the first line of the log:

    FAIL: unresolved ELF import 30Rh4ixbKy4 from libkernel.prx
    Searched libraries:
      ...
    GetLastError: 127 - The specified procedure could not be found.
    

    main + #2129 (c47f8ca, libs target): 30Rh4ixbKy4 resolves, and the loader stops on the next unresolved import:

    FAIL: unresolved ELF import -q-vbO859Tw from libSceVideoOut.prx
    

    So #2129 removes this stop, but the title does not start yet: 12 other imports are still unresolved on main. In the eboot, -q-vbO859Tw, GTfJ64qFCPw, vwtVpHQKelI (no known names), sceKernelAioSetParam and sceNpWebApi2GetMemoryPoolStats. AkConvolutionReverb.prx also needs GTfJ64qFCPw, and the bundled ffmpeg.prx needs six sceSsl* connection functions. Whether the title depends on what sceKernelFdatasync does after start-up I can't say yet. The earlier runs in this issue had a local placeholder for it that returned 0.

    One merge note: #2129 merged onto current main builds, but guest_font_tests fails to compile (GuestFont.cpp:129: redefinition of 'void SetFontDirectory(...)'). That comes from the PR's test(font) commit and main's copy of that test, so it needs a rebase.

  4. sktharunbalaji13-cmd commented on Oct 10, 2026

    @sktharunbalaji13-cmd
    Contributor

    Thanks a lot, @tv7! Really appreciate you testing this against clean main and sharing the exact logs and side-by-side comparison. That evidence makes it much clearer what sceKernelFdatasync fixes and what still blocks Dying Light 2, without claiming the game is playable yet.

    I've also cleaned up PR #2129 to keep it focused on sceKernelFdatasync and its regression test, removing the unrelated font-test change. Thanks again for taking the time to investigate this thoroughly — it's genuinely helpful for contributors working on compatibility!

  5. Rohan03x commented on Oct 10, 2026

    @Rohan03x
    Contributor

    A second title for the socket-descriptor / select item: Palworld (PPSA23126, v01.102.999) hits the same crash in the EOS SDK.

    Setup: Windows 11 22621, RTX 3070, GCC 15.2.0 UCRT Release; main 1f0c9fe plus #1764 and local unimplemented-export declarations the title needs to bind its imports.

    Crash: unhandled exception 0xc0000005 on thread 'RtcNetworkThread', rip eossdk-ps5-shipping.prx.guest.prx+0xdea2be, write of 0x221f9a0. The instruction is or QWORD PTR [rbp+rsi*8-0xb0], rdx with rsi = fd >> 6, rdx = 1 << (fd & 63) and fd = 0x10000005, i.e. FD_SET on a 1024-bit stack fd_set (0x21fa50 + 0x400000*8 - 0xb0 = 0x221f9a0), followed by a call to select. The descriptor comes from libkernel's socket (FirstDescriptor = 0x10000000).

    What the SDK does with descriptors (static reading of its imports and code): WebRTC builds its wake-up pair from socket/bind/listen/connect/accept (no pipe or socketpair import), uses POSIX read/write on it (covered by #1727 since), puts the sockets in select sets, and calls sceNetSetsockopt/sceNetSocketClose on them. Its libwebsockets also appears to pass descriptor 0 to select, because its wake-up pipe is never created.

    Local experiment: with libkernel sockets numbered from the host descriptor table (on Windows a zero-access CRT descriptor on NUL plus a side table, as directory descriptors already do; range tests replaced by table membership) and select polling libkernel sockets and host descriptors, the out-of-bounds write is gone and the title runs start-up for more than 10 s. The next stop is sceHttp2WaitAsync: waiting for an operation that has not completed. ctest: 0 failed of 619 with those changes on main 292ecfb. Windows only; the non-Windows branch is not built.

    Questions before I propose anything:

    1. Would small socket descriptors be acceptable, given that fix(libkernel): read and write guest sockets #1727 and feat(libkernel): implement read and write events for guest sockets #1091 rely on >= FirstDescriptor?
    2. libSceNet ids start at 32 and would overlap them. Should they move to the same allocator, or should select keep rejecting sets that mix the two?

    AI-assisted: yes.

  6. queeeef commented on Oct 11, 2026

    @queeeef
    Contributor

    Palworld (PPSA23126, 01.102.999) still hits the EOS FD_SET overflow on current main a11c8f7f7 (with only #3461's branch added so that every import binds):

    FATAL: unhandled exception 0xc0000005 on thread 37164 'RtcNetworkThread'
      rip 0x00007ffcdb1ea2be eossdk-ps5-shipping.prx.guest.prx+0xdea2be
      write of 0x000000011fa6f640
      rcx 0000000010000005 rsi 0000000000400000 r12 0000000010000005
    

    Same instruction and descriptor as reported above (fd = 0x10000005, rsi = fd >> 6).

    A local experiment that gets past it (8081a8e1, not a proposal):

    • GuestSockets::FirstDescriptor = 0x300, so libkernel sockets fit a 1024-bit fd_set;
    • select passes the libkernel sockets in its sets to libkernel poll with a zero timeout, and caps its wait at 1 ms while any are present (libSceNet sockets keep going through the poller).

    With it the crash is gone, the EOS threads run, and the title reaches character creation (further stops in #2648). It does not answer the two questions above: 0x300 only avoids the CRT descriptors and libSceNet ids in practice, it does not keep them apart.

    Windows 11 Pro 26300, RTX 2080 Ti, MinGW-w64 GCC 15.2.0 (WinLibs r7), RelWithDebInfo. AI-assisted (Claude Code).

  7. xXPBSXx commented on Oct 11, 2026

    @xXPBSXx
    Contributor

    Data for the two open questions above (@Rohan03x, @queeeef), from reading main a11c8f7 and FreeBSD; I have not implemented or run anything for this.

    • Why the descriptor cannot stay at 0x10000000: FreeBSD 11.0.1 defines FD_SETSIZE as 1024 (sys/sys/select.h, line 59), so the guest's fd_set is 128 bytes and FD_SET writes at byte fd / 8. With fd = 0x10000005 that is the 32 MB write past the set that the crash shows. Any descriptor a title can put in a select set has to be below 1024, so this applies to every title that uses select on a socket, not only the EOS SDK.
    • Where the number is assumed: GuestSockets::FirstDescriptor (Socket/include/SocketRuntime.hpp) is compared in 27 places across 7 source files (File/src/Stdio.cpp 18, File/src/Open.cpp 3, Socket/src/Udp.cpp 2, Equeue/Equeue.cpp, File/src/Terminal.cpp, libScePosixForWebKit/Filesystem.cpp, plus the header) and one test. Moving the base alone keeps every one of them working; replacing the range test with a table lookup, as in @Rohan03x's experiment, touches all of them.
    • What the small numbers have to avoid: libSceNet hands out its own ids from g_next_sock = 32 (libSceNet/Export.cpp:293, a separate counter), and on Windows the CRT hands out the lowest free descriptor for files, so a fixed base such as 0x300 only avoids them while neither grows that far. Directory descriptors already avoid the CRT clash by opening NUL (File/src/DirectoryDescriptor.cpp:40) and keeping a side table, which is what the experiment generalizes to sockets; libSceNet would need the same allocator to keep the two families apart, or select has to reject a set that mixes them.

    I do not know which of these the maintainers prefer, and I have not checked what the console numbers its first socket as.

    AI-assisted (Claude Code).

  8. xXPBSXx commented on Oct 11, 2026

    @xXPBSXx
    Contributor

    The Mound (PPSA26223) on main (0a8b1d32b, plain main, no open PRs), 120 s run: the select stop is gone and the title stops at the same place as described here: FATAL: unhandled exception 0xc0000005 on thread 'RtcNetworkThread', rip eossdk-ps5-shipping.prx.guest.prx+0xefc6f4, rcx = 0x10000005 (a socket from GuestSockets::FirstDescriptor), rsi = 0x400000 (= fd >> 6), i.e. FD_SET on the 1024-bit fd_set with a 0x10000000-based descriptor. So the EOS SDK path described above is also what stops this title.

    AI-assisted (Claude Code).

  9. xXPBSXx commented on Oct 12, 2026

    @xXPBSXx
    Contributor

    The descriptor design discussed here is implemented in #4391 (socket numbers reserved from the host descriptor table, so they fit FD_SETSIZE; dispatch by table membership). On Windows, The Mound (PPSA26223) with that PR no longer stops in RtcNetworkThread at FD_SET: 150 s without a FATAL (details and a needed rebase in #4391 (comment)). Palworld still stops earlier on an unrelated shader-recompiler abort, so it is not confirmed there. Observation only; I did not review the PR's semantics beyond building it and running its tests.

    AI-assisted (Claude Code).

  10. oneandonlydean commented on Oct 13, 2026

    @oneandonlydean
    Collaborator

    #3764 (kqueue) and #4391 (socket descriptors below FD_SETSIZE) are on main. What is the first stop of PPSA02262 on plain main now?

  11. pushkarreddyy commented on Oct 13, 2026

    @pushkarreddyy
    Contributor

    Working on a fix for item 4 (sceKernelMprotect on foreign module images on Windows). Extending Windows DoMprotect to handle registered foreign images via VirtualProtect matching Linux behavior, with unit tests. Will link the PR shortly.

  12. pushkarreddyy commented on Oct 13, 2026

    @pushkarreddyy
    Contributor

    Implemented and opened PR for Section 4 (foreign image \sceKernelMprotect\ support on Windows unblocking Dying Light 2: Stay Human \PPSA02262): #5109

  13. MarcZX10 commented on Oct 14, 2026

    @MarcZX10
    Contributor

    A current-main rerun could narrow this blocker now: #2129 (sceKernelFdatasync) and #4902 (the -q-vbO859Tw VideoOut NID) are both merged. The last clean-main loader trace in this thread predates those changes. Could you rerun the Dying Light 2 conversion/startup on current main with no local export placeholders and share the first remaining unresolved import or runtime stop? That will show whether one of the still-unknown VideoOut/ACM NIDs is actually next, and avoid adding exports based only on the import table.

  14. MarcZX10 commented on Oct 15, 2026

    @MarcZX10
    Contributor

    Follow-up to my earlier current-main rerun request: the current tools/nid_names.py registry now recognizes the formerly absent sceKernelFdatasync, sceKernelAioSetParam, sceNpWebApi2GetMemoryPoolStats, both VideoOut NIDs, and the six SSL NIDs; their implementations are present in current main (including #3305 and #4902 for the VideoOut calls). The remaining ACM NID GTfJ64qFCPw now resolves by name to sceAcmBatchJobPriority, but that export still throws and its callable signature/behavior is not established by the issue. A current unmodified run or import-audit result would show whether this is still the next blocker; I don't have the title dump to verify it here.

  15. tv7 commented on Oct 16, 2026

    @tv7
    ContributorAuthor

    Rerun on current main as asked, with no local exports or placeholders.

    Setup

    • Windows 11, RTX 3070 8 GB, MinGW GCC 15.2, Release
    • main 896e1b2b7, relinked with --module-dir . --to-intel (the title's own prx/ modules)
    • The game's own log was turned on by patching two bytes in a copy of game.exe (log flag + the "logging enabled" check); nothing in AnyPS5 changed

    Result

    Loading, conversion and startup are no longer the problem. All imports resolve, the intro movie plays, and the game reaches the start screen at 60 FPS. After the controller is chosen it sits on a black screen with the loading spinner, without making progress (tested 20 minutes, no error in the AnyPS5 log).

    The game's log shows why. It is stuck in the PSN sign-in step:

    [Game]              > Stage = 'InitialScreen_SigningIn' (prev value 'InitialScreen_ChoosenController')
    [Lobby.Users.Cross] > [SignIn] SignInOptions { ... .auth_interaction_type = Silent ... }
    [Lobby.Users.Psn]   > [SignIn] failed => sceNpGetAccountIdA returned an error: 0x80550006
    [Lobby.Users.Cross] > [SignIn] failed to sign in to the native backend: Unexpected
    [Lobby.Users.Cross] > [SignIn] SignInOptions { ... .auth_interaction_type = Interactive ... }
    [Lobby.Users.Psn]   > [SignIn] failed => sceNpGetAccountIdA returned an error: 0x80550006
    ... (the Interactive attempt repeats ~15 times per second, 343 times in the first 20 s)
    

    sceNpGetAccountIdA in libSceNpManager always returns SCE_NP_ERROR_SIGNED_OUT (the "PSN is not emulated" policy). Dying Light 2 does not fall back to offline play when that happens; it keeps retrying the interactive sign-in. On a console that step would show the system sign-in prompt, which never appears here.

    EOS is not the blocker: its HTTP requests to api.epicgames.dev fail (no real network), but Lobby.Backend.Eos reports CheckConnectionStatus ... ok (signed out) and the game keeps going past that.

    Question

    Would you accept reporting the initial user as a PSN user who is signed up but offline, i.e. sceNpGetAccountIdA (and sceUserServiceGetNpAccountId, which returns 0 today) returning a fixed local account id, while reachability and every online request still fail? On a real PS5 a user with a linked account keeps their account id while offline, so this would match the console without emulating PSN. If yes, I can open a small PR with a test. If you would rather keep "signed out" everywhere, the game needs some other way past this step and I'd like to know which one you prefer.

    AI-assisted: yes

  16. pushkarreddyy commented on Oct 16, 2026

    @pushkarreddyy
    Contributor

    This matches console hardware behavior: on a real PS5, a local profile with a linked account persists its 64-bit AccountId on storage. Even when completely offline (e.g. flight mode or disconnected network), sceNpGetAccountIdA(USER_SERVICE_INITIAL_USER_ID, &account_id) succeeds and returns that cached account_id, while sceNpGetNpReachabilityState reports NP_REACHABILITY_STATE_UNAVAILABLE and sceNpGetState reports NP_STATE_SIGNED_OUT.

    Titles like Dying Light 2 use the account ID to initialize local lobby/profile state and only fall back to offline play once a local identity is present. When sceNpGetAccountIdA returns 0x80550006 (SCE_NP_ERROR_SIGNED_OUT), the title assumes no account is present at all and continuously loops retrying the interactive sign-in dialog.

    Reporting a fixed non-zero account ID (e.g. 0x0100000000000001ull) for USER_SERVICE_INITIAL_USER_ID in sceNpGetAccountIdA (and aligning sceUserServiceGetNpAccountId) while keeping reachability unavailable models an offline linked profile accurately without emulating online PSN infrastructure.

    @tv7 Since you have the title dump and repro environment ready to verify it moving past the spinner into gameplay, opening that PR with the unit test would be great!

  17. MarcZX10 commented on Oct 17, 2026

    @MarcZX10
    Contributor

    Opened MarcZX100 PR #6990 for the fixed emulator-local account ID shared by sceNpGetAccountIdA and sceUserServiceGetNpAccountId. Both focused test targets compile locally; CTest aborts before assertions on this host because it lacks the PS5-required CPU extensions. The Dying Light 2 Windows runtime result remains unverified on the PR branch.

  18. MarcZX10 commented on Oct 18, 2026

    @MarcZX10
    Contributor

    The socket-descriptor concern in the earlier follow-up is now addressed by merged PR #4391 (fix(libkernel): give guest sockets ordinary descriptor numbers below FD_SETSIZE). Its guest_socket_descriptors regression test asserts created sockets are below 1024 and exercises socket readiness through select, mixed socket/file sets, descriptor reuse, and duplication. So the old 0x10000000 descriptor issue should no longer be treated as an outstanding current-main blocker. The latest Dying Light 2 run instead reaches PSN sign-in; #6990 covers the offline account-ID behavior, though title runtime on that PR head remains unverified.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions