Skip to content

feat(libkernel): implement sceKernelGetModuleList and sceKernelGetModuleInfo - #2028

Merged
adriadam10 merged 2 commits into
boykopovar:mainfrom
overlordxrz-source:feat/libkernel-get-module-list
Oct 10, 2026
Merged

adriadam10 merged 2 commits into
boykopovar:mainfrom
overlordxrz-source:feat/libkernel-get-module-list

Conversation

@overlordxrz-source

@overlordxrz-source overlordxrz-source commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

What

sceKernelGetModuleList (IuxnUuXk6Bg, libkernel) is missing on main. Ruffle Flash (PPSA68091) stops at it once its lstat stop (#1624) is fixed.

It now returns the handles of the loaded guest modules: the main program and every loaded .prx image, in load order. Host runtime libraries are left out. Each handle is the id sceKernelGetModuleInfoFromAddr already reports for that image, so the two agree, and it is the sceKernelLoadStartModule handle when the image was opened through the loader. On Linux the images come from dl_iterate_phdr, on Windows from EnumProcessModules.

The (int32_t* handles, size_t count, size_t* actual) signature and the errors follow shadPS4 (process.cpp):

  • a null handles or actual returns EFAULT;
  • when more images are loaded than count, the first count handles are written and it returns ENOMEM without writing actual.

The ENOMEM case, the filtering, and handles that are AnyPS5 ids rather than the console's are listed in TechnicalDebt.

Log

@babyhuey's runs of the homebrew catalog release with #1624 merged (thanks):

Both of those builds also had babyhuey's other open PRs merged. Only #1624 exports lstat.

The call site is in Ruffle's own eboot.bin (release 1.2.0, PPSA68091.zip sha256 b34b40b69da30dafce4ea67ee312a55b6bd9b07b59849741253b2c46147076be). I unwrapped the SELF and disassembled it:

  • At 0xa54 it calls sceKernelGetModuleList(handles, 0x200, &count) with a 4-byte handle array.
  • A nonzero result logs Ruffle PS5: module list -> %#x, and the title then loads /system/common/lib/%s.sprx itself.
  • On success it calls sceKernelGetModuleInfo on each handle. It tries st_size 0x160, 0x1a8, 0x1b0, 0x158 and 0x200, and matches names with strstr. sceKernelGetModuleInfo is also absent on main. A rig run has since shown it is the next stop, and the second commit adds it (see below).

Tested

aarch64 Debian 13 host. The full project can't be built here because it needs x86-64.

  • Extended core/libs/tests/GuestModuleInfo.cpp (guest_module_info, Linux):
    • the list starts with the main program's id;
    • it holds libkernel.prx exactly once, leaves out the dlopened non-.prx fixture, and has no duplicate ids;
    • with one slot too few it returns ENOMEM, fills the slots it has and leaves actual alone;
    • null pointers return EFAULT.
  • I ran the test natively against ModuleInfo.cpp and DynamicLoader.cpp built as a shared libkernel.prx, with Zig 0.17 clang, also under UBSan, and it passes. Two mutants each fail at the expected check: one without the .prx filter, and one that writes actual on ENOMEM. In that harness the existing invalid_argument checks need catch (...), because libc++ is linked statically into both binaries. That applies to main's test as well.
  • ModuleInfo.cpp and the test compile for x86_64-linux-gnu and x86_64-windows-gnu with -Wall -Wextra (Zig 0.17 clang). The only warning is the existing -Wreturn-type-c-linkage from General.hpp. On Windows, ModuleInfo.cpp also links into a DLL and the test into an executable against it, but neither was run here. guest_module_info is registered on Windows on main, so the Windows CI job runs it.
  • python3 tools/check_conventions.py --base origin/main passes.
  • Not run with the title.

Follow-up: sceKernelGetModuleInfo

On my x86-64 Linux box (Ubuntu 24.04, lavapipe), main f145139 with #1624 and this PR merged (ref d973cd6f), Ruffle Flash PPSA68091 1.2.0 gets past lstat and sceKernelGetModuleList and stops at:

./app.elf: symbol lookup error: ./app.elf: undefined symbol: kUpgrXIrz7Q

That NID is sceKernelGetModuleInfo; the second commit adds it. With both commits it moves on to sigemptyset (claimed by #1641), and the guest_module_info test passes on x86-64 (ctest, 1/1):

./app.elf: symbol lookup error: ./app.elf: undefined symbol: +F7C-hdk7+E

The call site is the loop from the description, in Ruffle's unwrapped eboot.bin (capstone):

0x0a54  call [sceKernelGetModuleList]          ; rdi = int32 handles[0x200], esi = 0x200, rdx = &count
0x0ae0  memset(info + 8, 0, 0x500)             ; info at rbp-0x660
0x0af6  mov edi, [handles + i*4]
0x0b07  mov qword [info], 0x160
0x0b12  call [sceKernelGetModuleInfo]          ; rax == 0 -> 0xbd8 (use the name)
0x0b3c  mov qword [info], 0x1a8 ; call ; je    ; then 0x1b0, 0x158, 0x200
0x0bd4  jne 0xc52                              ; every size failed: next handle, first rc kept for the log
0x0bdc  strlen(info + 8) ; strcat into a summary of the first 12 names
0x0c40  strstr(info + 8, needle) ; jne 0xe08   ; match: sceKernelDlsym(handle, ...) for Init/Open/Read/Close
  • It reads only name, at offset 8. Nothing reads the segments, the count or the fingerprint.
  • size 0x160 is tried first. A nonzero return is not fatal: the title tries the next size, then the next handle. When no name matches, it logs Ruffle PS5: %s not in %zu modules (%d named, info rc %#x): %s and loads /system/common/lib/%s.sprx itself.
  • The loop only runs when the GOT entries for the mouse (or keyboard) Init/Open/Read functions are null (0x0a1a). The import is what stops the load. The loop itself is a fallback path.

sceKernelGetModuleInfo(int32_t handle, ModuleInfo* info) fills the 0x160-byte SceKernelModuleInfo: st_size (u64) at 0, name[256] at 8, four {u64 address, u32 size, s32 prot} segments at 0x108, segment_count at 0x148 and fingerprint[20] at 0x14c. The layout is the same in fpPS4 (packed record, size:QWORD … fingerprint:array[0..19] of Byte) and shadPS4 (OrbisKernelModuleInfo). The OpenOrbis docs give the same layout. prosper (643ad45e) does not implement this export. static_asserts in SceTypes.hpp pin the offsets.

  • The handle has to be one that sceKernelGetModuleList returns, so the two agree. Name and segments come from the same code sceKernelGetModuleInfoFromAddr uses. On both Linux and Windows, Fill is a template over the info struct; the ModuleInfoEx-only fields are behind if constexpr. The Windows path reuses the PE Fill from main, so a relinked image reports its .elf sections and any other image its sections with memory access.

  • The errors follow shadPS4 and the other functions in this file:

    • a null info returns EFAULT;
    • any st_size other than 0x160 returns EINVAL and leaves the buffer alone;
    • an unknown handle returns ESRCH, as sceKernelDlsym does here.

    fpPS4 also returns EFAULT and ESRCH but does not check the size.

  • The fingerprint is left zero. Segment prot uses the 1/2/4 bits that sceKernelGetModuleInfoFromAddr already reports, while fpPS4 and shadPS4 copy the ELF p_flags. All of this is listed in TechnicalDebt.

Tested (aarch64 Debian 13, Zig 0.17 clang), extending GuestModuleInfo.cpp:

  • libkernel.prx's info matches sceKernelGetModuleInfoFromAddr (name, segments) and has a zero fingerprint.
  • Every listed handle resolves, and the main program's name matches.
  • Sizes 0x158, 0x1a8, 0x1b0, 0x200 and 0 return EINVAL and leave the buffer alone.
  • Null returns EFAULT. The dlopened non-.prx fixture's handle and -1 return ESRCH.
  • It passes natively, also under UBSan. Four mutants each fail at the expected check: any size ≥ 0x158 accepted, the handle ignored, no segments for ModuleInfo, and EINVAL for null.
  • ModuleInfo.cpp, Module.cpp and the test compile for x86_64-linux-gnu and x86_64-windows-gnu with -Wall -Wextra. The only warning is the existing one from General.hpp.
  • After rebasing onto main c9d4261 the same test, including main's sceKernelGetModuleInfoFromAddr checks, passes natively (3 runs, also with UBSan and ASan). main's unchanged test passes against the rebased library too. The sceKernelGetModuleInfo checks are the same on Windows; they were not run there.
  • check_conventions.py passes.
  • Not run with the title yet.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed in TechnicalDebt
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none (the title reaches it after feat(libkernel): implement lstat #1624)
  • AI-assisted: yes

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Progress report (a291ae0 → 8a41d28)

📈 System libraries: 85.7% (+0.01%, +2 implemented functions)

✅ 2 implemented
Library Name
libkernel sceKernelGetModuleInfo
libkernel sceKernelGetModuleList

@MarcZX10

MarcZX10 commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

The overlap with #2001 is substantive, not just shared context: its current diff deletes ModuleInfo.cpp and GuestModuleInfo.cpp and removes their CMake test registration, while this PR adds the export and regression in those files. If #2001 merges first, this implementation will need to be reapplied; if this PR merges first, #2001 deletes it. Please coordinate with the #2001 author/maintainer on intended scope and merge order before approval. I have not approved while this conflict is unresolved.

@overlordxrz-source

Copy link
Copy Markdown
Contributor Author

Thanks for flagging it. I looked at #2001 before deciding how to handle it: it's a 967-file change (+11,386/−57,803) that, by its own description, deletes ModuleInfo.cpp along with many implemented handlers, the libjpeg-turbo integration, and several CI and conventions gates. Any open PR that touches libkernel, libc, or AGC conflicts with it, so the overlap doesn't come from anything specific to this PR's scope.

I'd rather not make #2028 depend on it. If #2001 is accepted in its current form, I'll reapply the export and the regression onto whatever replaces ModuleInfo.cpp. Until then, #2028 stays scoped to the single export, with the Ruffle PPSA68091 stop line as evidence. @Praetorian0420, if you intend to keep the module-info code, let me know and I'll rebase onto your layout.

@overlordxrz-source overlordxrz-source changed the title feat(libkernel): implement sceKernelGetModuleList feat(libkernel): implement sceKernelGetModuleList and sceKernelGetModuleInfo Oct 9, 2026
@overlordxrz-source

Copy link
Copy Markdown
Contributor Author

Thanks for flagging it. I looked at #2001 before deciding how to handle it: it's a 967-file change (+11,386/−57,803) that, by its own description, deletes ModuleInfo.cpp along with many implemented handlers, the libjpeg-turbo integration, and several CI and conventions gates. Any open PR that touches libkernel, libc, or AGC conflicts with it, so the overlap doesn't come from anything specific to this PR's scope.

I'd rather not make #2028 depend on it. If #2001 is accepted in its current form, I'll reapply the export and the regression onto whatever replaces ModuleInfo.cpp. Until then, #2028 stays scoped to the single export, with the Ruffle PPSA68091 stop line as evidence. @Praetorian0420, if you intend to keep the module-info code, let me know and I'll rebase onto your layout.

@popcoder565

Copy link
Copy Markdown
Contributor

Native Windows run, since no Windows results were posted. Setup: Windows 10 (19045), MinGW-w64 GCC 15.2.0 (WinLibs r7), Release. Head ce9018d1, merged onto main d70b899 in one build with 38 other PRs that had no Windows run.

No title was run.

🤖 Addressed by Claude Code

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR. It now conflicts with main, and it isn't a mechanical conflict:

  • core/libs/prx/libkernel/Module/src/ModuleInfo.cpp: main restructured the file (shared EhFrame.hpp, ToU32/SetName moved out of the #ifndef _WIN32 block, and a Windows Fill for sceKernelGetModuleInfoFromAddr). FillNameAndSegments, ImageSearch<TInfo> and FindImage<TInfo> need to be rebuilt on that layout. Please decide whether sceKernelGetModuleInfo can reuse the new Windows Fill instead of calling NotImplemented_nid_no_patch, or keep it Linux-only and say so in TechnicalDebt.
  • docs/dev/TechnicalDebt.md: conflict in "Unknown function info" next to the sceKernelGetModuleInfoFromAddr line, which changed on main. Update your lines if the Windows behaviour changes, and keep them away from the end of the section.

The overlap with #2001 that was raised earlier is gone, since it was closed.

After rebasing, please rerun guest_module_info and confirm the sceKernelGetModuleInfoFromAddr checks still pass.

Fill takes either info struct, so sceKernelGetModuleInfo reports the
name and segments sceKernelGetModuleInfoFromAddr reports for the same
image, on Linux and Windows.
@overlordxrz-source
overlordxrz-source force-pushed the feat/libkernel-get-module-list branch from ce9018d to 8a41d28 Compare October 9, 2026 23:39
@overlordxrz-source

Copy link
Copy Markdown
Contributor Author

Rebased onto main c9d4261 and rebuilt on the new layout. It's still the same two commits.

  • ModuleInfo.cpp: on both platforms Fill is now a template over the info struct. The ModuleInfoEx-only fields (TLS, eh_frame, init/fini, id, ref_count) are behind if constexpr. ImageSearch<TInfo>/FindImage<TInfo> sit on top of it, and FillNameAndSegments is gone. ToU32, SetName and EhFrame.hpp are shared and not duplicated. Because ToU32 and SetName now serve both calls, their messages say module info: instead of sceKernelGetModuleInfoFromAddr:.
  • Windows: sceKernelGetModuleInfo now reuses your Windows Fill instead of calling NotImplemented_nid_no_patch. It passes the module base from EnumProcessModules, so the name and segments are the ones sceKernelGetModuleInfoFromAddr reports for that image. sceKernelGetModuleList and ImageName share one GetModuleFileNameW loop (ModulePath).
  • TechnicalDebt: I kept main's sceKernelGetModuleInfoFromAddr line as it is. The two new lines follow it, well before the end of the section. I dropped "Not implemented on Windows" from the sceKernelGetModuleInfo line.

Tested:

  • aarch64 Linux, natively: guest_module_info passes, including all of main's sceKernelGetModuleInfoFromAddr checks (3 runs, also with UBSan and ASan). main's unchanged test also passes against the rebased library.
  • On the same binary, the executable's sceKernelGetModuleInfo and sceKernelGetModuleInfoFromAddr results are byte-identical before and after the rebase.
  • Seven mutants (size check, handle match, segments, null order, .prx filter, ENOMEM writing actual, name) are all caught.
  • x86_64 Linux and Windows (Zig 0.17 clang, -Wall -Wextra): both compile. On Windows the DLL and the test executable also link. I couldn't run the Windows test here; guest_module_info is registered on Windows on main, so the Windows CI job will run it.
  • check_conventions.py passes.

AI-assisted: yes

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 10, 2026

@adriadam10 adriadam10 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checked on Linux (merged onto current main): build and guest_module_info pass, and the test no longer builds without the change. sceKernelGetModuleList / sceKernelGetModuleInfo signatures and errors (EFAULT, ENOMEM without writing actual, EINVAL on st_size, ESRCH) match the cited shadPS4 code; the TechnicalDebt lines record the deviations (ids, prot bits, zero fingerprint). #2001 is closed, so the overlap is gone. Windows is code review only (compiles, per the thread). Merging.

@adriadam10
adriadam10 merged commit 426fe62 into boykopovar:main Oct 10, 2026
6 of 7 checks passed
SleglJan pushed a commit to SleglJan/AnyPS5 that referenced this pull request Oct 10, 2026
…rnel-get-module-list

feat(libkernel): implement sceKernelGetModuleList and sceKernelGetModuleInfo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants