Skip to content

fix(libkernel): report unwind information for guest and host addresses on macOS - #9263

Merged
oneandonlydean merged 2 commits into
boykopovar:mainfrom
imellag:fix/libkernel-macos-guest-unwind-info
Oct 27, 2026
Merged

oneandonlydean merged 2 commits into
boykopovar:mainfrom
imellag:fix/libkernel-macos-guest-unwind-info

Conversation

@imellag

@imellag imellag commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

What

On macOS a C++ exception that a title throws and catches itself can end the process: two unwind queries the title's own unwinder makes give it nothing to work with.

  • sceKernelGetModuleInfoForUnwind found the relinked image and its PT_GNU_EH_FRAME header, but left eh_frame_addr and eh_frame_size at zero, so the title's unwinder found no FDE for its own frames. The tables are now read with EhFrame::ReadTables over the image's load segments, as ModuleInfo does on Linux. eh_frame_size includes the zero terminator, as on Linux and as the existing debt entry records for firmware 13.60.
  • sceSysmoduleGetModuleInfoForUnwind read /proc/self/maps everywhere except Windows, so on macOS every call threw (failed to open /proc/self/maps). It now answers from mach_vm_region, with the image path from dladdr, as the Linux branch answers from the mapping.

Every library change is behind __APPLE__. The only line that changes on Linux and Windows is in GuestSysmodule.cpp: CheckUnwindInfo() is now called on every host, because macOS gets its own variant.

Carried over from #7332/#8481, which the open-PR limit closed.

Overlap: #7201 contains the same eh_frame read inside its macOS port; its review asks for that PR to be split into topics, and this is the unwind piece, plus the sysmodule fix #7201 does not have.

Tested

Current main (2026-10-26): I looked for the smallest set of open PRs that boots titles on macOS from main 035ac96edd: #10366, #10552, #7631, #7804, #11249, #10669, #10540, #11447, #11047, #11048, #10876 and this PR, plus a local VulkanDevice.cpp patch that no open PR covers yet. With it, Planet of Lana and God of War Sons of Sparta, relinked with that tree, start from New Game and run without errors (Lana in gameplay for 120 s, GoW to the Cyclops fight in 145 s). Without this PR, Lana still runs, but GoW stops with Terminating due to uncaught exception of type Il2CppExceptionWrapper.

Machine: Apple M4 Pro (24 GB), macOS 27.0.1, x86-64 under Rosetta 2, Apple clang 21.0.0. main does not build the libraries on macOS by itself, so everything ran on top of #8658 (0a6c0fd) with #9193 and the macOS RTLD_GLOBAL loader change from #7332 (2d500a0), which FMOD titles need to load at all.

  • guest_unwind_module_info (new on macOS): the test executable carries the __APS5DATA,__meta program headers that the macOS dl_iterate_phdr reads from a relinked image. It checks the header and frame addresses, a one-record and a two-record table (sizes 20 and 36, terminator included), an image without PT_GNU_EH_FRAME (all zero), and a table whose terminator lies outside the load segment (throws). Passes; with Module.cpp from main it fails ("wrong EH frame address").
  • guest_sysmodule (macOS variant of CheckUnwindInfo): a function in the test executable, one in the library, an anonymous page (empty name) and an unmapped address (throws), checked against mach_vm_region and dladdr. Passes; with Export.cpp from main it aborts.
  • Also pass: guest_dynamic_loader, guest_dynamic_loader_variant_names, unwind_record_bounds, unwind_callback_abi_*.
  • God of War Sons of Sparta (PPSA28997): without this change it stops about 4 s after start with Terminating due to uncaught exception of type Il2CppExceptionWrapper; the managed exception, read from sceKernelDebugWriteCppExceptionInfo with a local log line, is FMODUnity.BusNotFoundException, which the title normally catches. Without only the sysmodule part it stops with libc++abi: terminating due to uncaught exception of type std::runtime_error: sceSysmoduleGetModuleInfoForUnwind: failed to open /proc/self/maps. With both it opens its 38 FMOD banks and plays through to the Cyclops fight (27.0 fps in the fight, frame dumps checked).
  • python3 tools/check_conventions.py --base origin/main: all checks pass.

Not tested: Linux and Windows builds (no library change there), the full CTest suite.

Checklist

…s on macOS

Two unwind queries a title makes went wrong on macOS, so a C++ exception the title would catch ended the process instead: God of War Sons of Sparta (PPSA28997) stops on an IL2CPP exception it normally handles.

- sceKernelGetModuleInfoForUnwind found a relinked image and its PT_GNU_EH_FRAME header but left eh_frame_addr and eh_frame_size at zero, so the title's unwinder found no FDE for its own frame. The tables are now read with EhFrame::ReadTables over the image's load segments, as ModuleInfo does on Linux, and eh_frame_size includes the zero terminator, as on Linux.
- sceSysmoduleGetModuleInfoForUnwind read /proc/self/maps outside Windows and threw on every call on macOS. It now answers from mach_vm_region, with the image path from dladdr, as the Linux branch answers from the mapping.

guest_unwind_module_info now runs on macOS: the test executable carries the __APS5DATA,__meta program headers that the macOS dl_iterate_phdr reads from a relinked image. guest_sysmodule checks the macOS answers against mach_vm_region.

Co-authored-by: mugurc <mceker@gmail.com>
@github-actions github-actions Bot added the unblocks title Names a title ID on the Unblocks line label Oct 21, 2026
@github-actions

github-actions Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 4563 core/libs/prx/libkernel/CMakeLists.txt
PR 4630 docs/dev/debt/libkernel.md
PR 5021 core/libs/prx/libSceSysmodule/Export.cpp
core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/Module/src/Module.cpp
docs/dev/debt/libkernel.md
PR 6149 core/libs/prx/libkernel/Module/src/Module.cpp
docs/dev/debt/libkernel.md
PR 6936 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 7088 docs/dev/debt/libkernel.md
PR 7105 docs/dev/debt/libkernel.md
PR 7121 docs/dev/debt/libkernel.md
PR 7149 docs/dev/debt/libkernel.md
PR 7192 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 7201 core/libs/prx/libkernel/CMakeLists.txt
core/libs/prx/libkernel/Module/src/Module.cpp
PR 7447 docs/dev/debt/libkernel.md
PR 7509 docs/dev/debt/libkernel.md
PR 7804 docs/dev/debt/libkernel.md
PR 8605 docs/dev/debt/libkernel.md
PR 9156 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9199 docs/dev/debt/libkernel.md
PR 9242 docs/dev/debt/libkernel.md
PR 9284 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9293 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9297 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9302 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9383 core/libs/prx/libkernel/CMakeLists.txt
PR 9389 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9491 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9557 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9566 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9575 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 9947 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 10192 docs/dev/debt/libkernel.md
PR 10319 docs/dev/debt/libkernel.md
PR 10442 docs/dev/debt/libkernel.md
PR 10552 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 10558 docs/dev/debt/libkernel.md
PR 10725 docs/dev/debt/libkernel.md
PR 10749 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 11002 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libkernel.md
PR 11042 docs/dev/debt/libkernel.md
PR 11085 docs/dev/debt/libkernel.md
PR 11215 docs/dev/debt/libkernel.md
PR 11223 docs/dev/debt/libkernel.md
PR 11264 docs/dev/debt/libkernel.md
PR 11267 docs/dev/debt/libkernel.md
PR 11341 core/libs/prx/libSceSysmodule/Export.cpp
core/libs/prx/libkernel/Module/src/Module.cpp
docs/dev/debt/libkernel.md

@krishalivaibhav

Copy link
Copy Markdown
Contributor

Summary

Vampire Survivors (PPSA21703, 01.016.107) does not get past startup on macOS with main + #8658 alone: a managed exception that the game catches on the console escapes and the process terminates. With #9263 merged on top, the same converted game boots, initializes PSN, mounts its save data and presents frames for 60 s (and for the 10 minute plays I did), and exits cleanly.

Setup

Apple M2 16 GB, macOS 27.2, Rosetta 2, MoltenVK 1.4.2 (Vulkan SDK 1.4.363). Tree: main at d6e7ab4 + #8658 (merge) = integ/macos-8658; second tree = that + #9263 head (bfa5368) merged. Both built with ninja libs, Release, x86_64. The game was converted with the relinker of that same tree: relinker --macos --to-rosetta --module-dir Media/Modules --module-dir Media/Plugins <game>/eboot.bin eboot.bin. Plain flags (no APS5_FAST_*), no input, 60 s bounded.

Result

| tree | exit | "[PS5Account] PSN Initialized" | SaveData mount | frames |
| main + #8658 | -4 (SIGILL) after "Terminating due to uncaught exception of type Il2CppExceptionWrapper" | no | no | none |
| main + #8658 + #9263 | 0 | yes | yes (mount3 on /savedata0) | yes, about 57 FPS in the menu |

How I located it

  1. A temporary diagnostic in sceKernelDebugWriteCppExceptionInfo (read the Il2Cpp exception object, local only, not in the PR) recovered the managed message: Unrecognized Guid format. (a Rewired ControllerMap_Editor with an empty GUID string). On a build with working unwind info the guest's own exception landing pad catches it.
  2. In the failing tree the guest unwinder calls sceKernelGetModuleInfoForUnwind (macOS branch returns the eh_frame_hdr of a relinked image but eh_frame_addr/eh_frame_size 0) and sceSysmoduleGetModuleInfoForUnwind (reads /proc/self/maps); in an intermediate build that fixed only the first, the process aborted with libc++abi: terminating due to uncaught exception of type std::runtime_error: sceSysmoduleGetModuleInfoForUnwind: failed to open /proc/self/maps.
  3. A local patch of those two functions (about 37 lines, same two files fix(libkernel): report unwind information for guest and host addresses on macOS #9263 changes) was enough; then I replaced it with the unmodified fix(libkernel): report unwind information for guest and host addresses on macOS #9263 head and got the same result (row 2 above).
  4. Isolation: the same converted game runs with an older macOS integration build's libraries (which have the unwind code); a library-by-library overlay could not isolate it because the libs are ABI coupled; 19859a2ca (host fault signals) alone did not change the failure.

Notes

  • Tested locally on macOS only; not on Linux/Windows, and I did not run the PR's own new tests (GuestUnwindModuleInfoMacos, GuestSysmodule) in this pass.
  • Other Unity/IL2CPP titles on macOS probably hit the same path (not tested).
  • AI-assisted.

@imellag

imellag commented Oct 23, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for testing it on another title and machine, and for the detailed write-up. It matches what the PR fixes: without unwind information for relinked images, the guest unwinder cannot find the landing pad and the exception that the game catches on the console escapes. I have added PPSA21703 to the Unblocks line.

@iamthehimansh

Copy link
Copy Markdown
Contributor

Tested on an Apple M2 Pro, macOS, x86_64 Release under Rosetta 2, MoltenVK 1.4.2, applied on top of #8658's feat/macos-port (27abf7e4b) together with #8477, #9263, #9746 and #9406. I built the whole tree and ran the full suite (1,962 tests), then ran any new failure 3x on its own with each PR alone.

  • guest_unwind_module_info (added here): passes.
  • With this PR alone, no test that passes without it fails, including macos_executable_exports (3/3). It failed once in the full parallel run but passes alone with every one of the five PRs, so I count it as load, not this PR.

AI-assisted: yes (Cana)

@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Oct 24, 2026
@orezraey

Copy link
Copy Markdown
Contributor

@imellag #11253 edits a debt entry next to one this PR changes (PS5 measurements of module and process queries), so git may report a conflict there. Whichever merges second keeps both; I'll rebase #11253 if this one lands first.

@oneandonlydean
oneandonlydean merged commit 5023cd5 into boykopovar:main Oct 27, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build docs libkernel prx unblocks title Names a title ID on the Unblocks line

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants