Repository navigation
feat(libSceAmpr): implement __cxa_call_unexpected - #6024
Merged
adriadam10 merged 1 commit intoOct 17, 2026
Merged
adriadam10 merged 1 commit into
adriadam10 merged 1 commit into
Conversation
__cxa_call_unexpected takes the in-flight exception's unwind header and never returns, as in libcxxabi. No std::set_unexpected is exported and the exception header's unexpected handler is never set, so the handler is always the default one, which calls std::terminate. It therefore forwards to libc's __cxa_call_terminate, which begins the catch and runs the current terminate handler; a null header terminates in libc's __cxa_begin_catch. libcxxabi's std::terminate would instead run the terminate handler saved in the exception header at throw time; the two differ only if std::set_terminate was called in between, which the debt entry records. The test shares the forked-child helper of the std::_Pad test, which now takes the terminate handler to install. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
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.
|
11 tasks done
This was referenced Oct 15, 2026
KernelPanicNT
force-pushed
the
feat/libsceampr-cxa-call-unexpected
branch
from
October 15, 2026 13:17
424999f to
5b7e3ae
Compare
Collaborator
|
Checked build and ctest (37/37 in scope, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Implements
__cxa_call_unexpected(NIDusKbuvy2hQg) in libSceAmpr. Until now it was a throwingint (void)stub, added by @Pouare514 with the Deathloop loader stubs in #407 (merged); the NID first appeared in their missing-imports list #272, which they closed themselves without merging and without a stated reason. This PR replaces that stub.Compilers emit calls to it from the landing pad of a function whose dynamic exception specification (
throw(...)) an exception violates. It takes the in-flight exception's unwind header and never returns.Source of the behaviour. No console measurement is involved. The model is LLVM libcxxabi 18.1.8 (tag
llvmorg-18.1.8, commit3b5b5c1), together with what this emulator's libc already exports:cxa_personality.cppL1171-L1211:[[noreturn]] void __cxa_call_unexpected(void* arg). A null header is passed tocall_terminate(L329-L340), whose__cxa_begin_catch(cxa_exception.cppL445-L448) reads the header'sexception_classwithout a null check, so libcxxabi does not define the null case. Otherwise it calls__cxa_begin_catch, takes the unexpected handler stored in the exception header (orstd::get_unexpected()for a foreign exception), and callsstd::__unexpectedwith it.cxa_exception.cppL209-L214, called by__cxa_throwat L279: the header's handlers arestd::get_unexpected()andstd::get_terminate()at throw time.cxa_handlers.cppL32-L37:std::__unexpectedcalls the handler and aborts if it returns.cxa_default_handlers.cppL88-L99: the default unexpected handler callsstd::terminate(). After__cxa_begin_catchhas pushed the native exception onto the caught stack (cxa_exception.cppL470-L477),std::terminate()runs the terminate handler saved in that exception's header, and only otherwise the current one (cxa_handlers.cppL72-L93).In AnyPS5, nothing can change the unexpected handler:
std::set_unexpectedorstd::get_unexpected.__cxa_throwand__cxa_init_primary_exceptionnever set the header'sunexpectedfield.So the handler is always the default one, and the libcxxabi path reduces to: begin the catch, then run the terminate handler saved when the exception was thrown.
libc's existing
__cxa_call_terminate:__cxa_begin_catch, thenTerminate()with the current global handler, as AnyPS5's ownstd::terminate()does. So the result matches libcxxabi except that the current terminate handler runs instead of the one saved in the header when the exception was thrown (Runtime.cppL283 records it). The two differ only ifstd::set_terminatewas called between the throw and the call to__cxa_call_unexpected. The debt entry records this. For a null header this emulator's libc__cxa_begin_catchcallsTerminate()itself; that is AnyPS5 behaviour, not something libcxxabi specifies.That the console's runtime is libcxxabi-compatible here is assumed, not checked.
Changes:
__cxa_call_unexpected_nid_postfixgets the[[noreturn]] void (void*)signature and forwards to libc's__cxa_call_terminate_nid_postfix. This follows the earlier libSceAmpr exports that call into libc: feat(libSceAmpr): implement Dinkumware thread helpers #3080 (wcstombs_sthroughwcsrtombs_s, threads throughpthread), feat(libSceAmpr): implement the std::_Pad thread launch helpers #3743 (std::_Padthrough_Mtx/_Cnd) and feat(libSceAmpr): implement std::_Throw_Cpp_error #3679 (std::_Throw_Cpp_errorthrough libc's exception runtime).guest_ampr_threads(Linux) gainsTestCallUnexpected. It uses the forked-child pattern of feat(libSceAmpr): implement the std::_Pad thread launch helpers #3743'sGuestSystemErrorCode, which becomesTerminateExitCodeand takes the terminate handler to install so that both tests share it: the child installs a guest terminate handler with the exportedstd::set_terminate, and the new handler exits with a code that encodes__cxa_current_exception_type().__cxa_allocate_exceptionand__cxa_init_primary_exception, is passed by its unwind header. The handler runs with that exception caught (exit 42).Not modelled: a replaced unexpected handler that throws, which in libcxxabi is then checked against the violated exception specification (rethrow, or
std::bad_exception). It cannot be reached while nostd::set_unexpectedexists. The debt entry says so, so that addingset_unexpectedlater revisits this function.Progress (
tools/progress.pyon the branch basee27ff7c): system libraries 3269/3679 → 3270/3679; libSceAmpr 136/144 → 137/144.Tested
Head
5b7e3aea8b38e3f627366f53e0cb92757c18760e, basee27ff7c545cb5ac669612567d853956ffa02ebe6.BUILD_TESTING=ON, AMD EPYC 7763 (GitHub Codespaces), tests run natively):guest_ampr_threads_testspasses in 5 of 5 runs, the test executable being run directly five times.core/libs/prx/libSceAmpr/Export.cppwith the base version:guest_ampr_threads_testsfails withrequirement failed at line 515. Restoring the PR source and rebuilding,guest_ampr_threads_testspasses in 5 of 5 runs again.mainatb08db6a093f440af697d71ea907cec5f675fcdc5, WinLibs MinGW-w64 GCC 15.2.0, Release,BUILD_TESTING=ON: 1278/1279 tests pass, includingguest_ampr_threads. The one failure,prx_thread_local_destructors, reports athread_localdestructor inlibSceAgcDriver(traceDrawKeyLookup), whichmaingained in de2ddda the same day; this PR does not touch that library. The relinker (Linux, Windows, macOS), register-context and write-watch jobs of the same run pass.git diff --checkpass. The Linux job of the Build workflow now targets the project's self-hosted runner, so it was not run on the fork; no title was run.Duplicate and overlap check
__cxa_call_unexpected,cxa_call_unexpectedand the NIDusKbuvy2hQg. The only hits are feat(libs): Deathloop loading support (Ampr, NetCtlApDialog + stubs) #407 (merged; added the throwing stub) and docs(nids): announce deathloop-ppsa01670 missing imports #272 (closed unmerged by its author with no stated reason; a list of missing imports), both by @Pouare514. No open PR implements the function or names it.core/libs/prx/libSceAmpr/Export.cpp,core/libs/tests/GuestAmprThreads.cppordocs/dev/debt/libSceAmpr.md, so there is no textual-conflict risk. The libc exception runtime is only called, not changed.e27ff7c, so it merges without conflicts.Checklist
main; no other open PR implements the same functionsNotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file.mdfiles or images added to the repository (attach them to this PR)🤖 Generated with Claude Code