Skip to content

fix(libkernel): report guest modules' unwind tables on Linux in sceKernelGetModuleInfoForUnwind - #2401

Closed
hugodearaujo wants to merge 1 commit into
boykopovar:mainfrom
hugodearaujo:fix/linux-module-unwind-info
Closed

hugodearaujo wants to merge 1 commit into
boykopovar:mainfrom
hugodearaujo:fix/linux-module-unwind-info

Conversation

@hugodearaujo

Copy link
Copy Markdown
Contributor

What

On Linux, sceKernelGetModuleInfoForUnwind read /proc/self/maps and always returned eh_frame_hdr_addr, eh_frame_addr and eh_frame_size as 0. A title that ships its own libc.prx unwinds with the console's unwinder, which asks this function for each frame's tables. With no tables it finds no handler, so every C++ exception thrown in guest code terminated the title, even ones the title catches.

  • Linux. The function now finds the module with dl_iterate_phdr, as the macOS branch already does. For a guest module (the main program or a *.guest.prx) with a PT_GNU_EH_FRAME, it reports that header, the eh_frame it points to, and the size of the records. seg0 is the module's first PT_LOAD. Host libraries still get no tables, as on Windows.
  • Both platforms. The eh_frame_hdr parsing and the record walk move out of the Windows-only block into one helper. eh_frame_size now includes the 4-byte zero terminator, as the console reports it (see below). This changes the Windows expectation in GuestUnwindModuleInfo.cpp from 4 + 12 to 4 + 12 + 4.
  • TechnicalDebt. The entry assumed the size excludes the terminator and said the console's value was unknown. It is replaced with what is still different from the console: flags and name.

Measured on a PS5 (firmware 13.60)

A payload calls sceKernelGetModuleInfoForUnwind for addresses in libkernel and libSceLibcInternal and checks each field independently: it decodes the returned header, compares its eh_frame pointer, and walks the records to the terminator.

libkernel libSceLibcInternal
return (flags 1) 0 0
st_size 0x130 0x130
eh_frame_hdr_addr header, version 1, encodings 1b/03/3b same
eh_frame_addr equals the header's decoded eh_frame_ptr same
eh_frame_size 0x1391c = walk 0x13918 + 4 0x2ccc0 = walk 0x2ccbc + 4
seg0 0x800000000 + 0x4c000, contains the address; the header lies outside it 0x8000a8000 + 0x128000, same
name empty empty

Flags 0, 3 and 8 return 0x80020016, and flags 2 returns 0x80020003. An address outside every module (or in the payload, which is not a module) returns 0x80020003. Every failure zeroes the whole structure. The flags and name differences stay in TechnicalDebt; this PR does not change them.

Evidence: Hollow Knight: Silksong (PPSA12544) on Linux

After a new game's opening video, the title stops:

Terminating due to uncaught exception of type Il2CppExceptionWrapper

The exception is System.Reflection.AmbiguousMatchException, thrown on this path:

PlayMakerFSM.Init → Fsm.Init → CallMethodProper.PreCache → Type.GetMethod("CancelAttackNotDownspikeBounce")

I read the class from the exception object, and the name from a breakpoint on RuntimeType.GetMethodImplCommon. HeroController has two overloads with that name, so the exception is expected. The LSDA of CallMethodProper.PreCache in the title's Il2cppUserAssemblies.prx has a catch clause over that call (call site 0x123+0xd, landing pad 0x1e1, action 3), so on the console it is caught. The title's bundled libc.prx imports sceKernelGetModuleInfoForUnwind, and with no tables it never reached that handler.

With this change, the title gets past the opening and into gameplay. Someone played it with a controller for several minutes, including dying and respawning, without an exception reaching terminate. That run used an earlier revision of this commit, which differed only in leaving the terminator out of eh_frame_size.

Base for these runs: main + #1836 + #1431 + #846 + #2323 + #861. They are needed to reach the opening, not for this change.

Tested

  • Linux (Arch, GCC 16.2, Release): ctest 514/514. guest_unwind_module_info now has a Linux version, which compares every field against dl_iterate_phdr and its own header decode and record walk. It covers the main program, a module loaded as *.guest.prx, a host library (no tables, seg0 still set) and an unmapped address (ESRCH). It aborts with main's Module.cpp.
  • Windows (Server 2025 VM, WinLibs GCC 15.2, CI flags, no GPU, so Vulkan tests are skipped): 507/507, including guest_unwind_module_info with the terminator counted.
  • feat(libs): implement font, libc, libkernel and misc prx stubs #2193 also edits Module.cpp (sceKernelInternalMemoryGetModuleSegmentInfo). The two conflict on text near the includes, not in the same function.

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
  • AI-assisted: yes

🤖 Generated with Claude Code

…rnelGetModuleInfoForUnwind

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the intake paused Closed while PR intake is limited to authors with at least 10 PRs merged into main label Oct 9, 2026
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown

Thank you for your interest in the project! We are sorry, but we cannot accept more PRs while we work through the existing backlog. This PR has been automatically closed and labeled "intake paused". This is not a judgment of the quality of your changes.

@hugodearaujo

Copy link
Copy Markdown
Contributor Author

Closed by the intake check. The diagnosis, the PS5 measurements and this commit are in #2419, so whoever can carry the fix can pick it up from there.

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

Labels

intake paused Closed while PR intake is limited to authors with at least 10 PRs merged into main

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant