Repository navigation
feat(libc): answer every libunwind cursor slot of an unwind context - #6819
GitamarT045T3R wants to merge 6 commits into
Conversation
Progress report (d6eafa5 → 3551ba1)📈 System libraries: 43.3% (+0.07%, +11 implemented, -1 removed functions) ✅ 11 implemented
⚪ 1 behaviourally empty (constant return or throw)
🗑️ 1 removed
|
e9fd274 to
3bf85ad
Compare
|
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.
|
jotitadev
left a comment
There was a problem hiding this comment.
Native Linux run on exact head 3bf85ad5c71a2feb54df83cbd5eb5673ed539bff, merged on main 82ddae75b.
Machine: Intel i7-8700 (6C/12T), Ubuntu 26.04, GCC 15.2, CMake 4.2.3, Ninja, Release, BUILD_TESTING=ON, full build.
The four abort tests fail on Linux. unwind_cursor_get_register, _set_register, _get_float_register and _set_float_register print the expected line (libunwind: getRegister - unsupported x86_64 register, ...), but std::abort() ends the process with SIGABRT here, and CTest reports a signal as Subprocess aborted***Exception and fails the test even though PASS_REGULAR_EXPRESSION matches. On Windows _set_abort_behavior turns abort() into exit code 3, which is why they pass there. The other abort-plus-regex tests in the tree (guest_call_trace_abort, windows_backtrace_*) are registered only under if(WIN32); these four are registered on every platform.
With a SIGABRT handler that exits normally on non-Windows, all four pass. I tested only this change:
#ifdef _WIN32
_set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
#else
std::signal(SIGABRT, [](int) { std::_Exit(3); });
#endif(plus #include <csignal>). Registering the four tests under if(WIN32) would also work, but then the abort paths would go untested on Linux.
Other results:
unwind_cursorpasses, 25 of 25 repeated runs. This covers the Linux paths you could not run:getFunctionNamethroughdladdr,extrafrom thePT_LOADsegment,stepto the end of the stack, andjumpto.- The same
unwind_cursor_testsbinary run againstmain'slibc.prxaborts withlibunwind cursor: operation other than register access or isSignalFrame not implemented, so the test does cover the change. - Non-GPU suite (
ctest -E "^agc|gpu|vulkan|video_out"): 649/655. The six failures are the four above plusfile_writer_binary/file_writer_text, which also fail onmainon this machine (648/650). All the other exception, unwind and backtrace tests pass.
Not checked: macOS.
Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.
|
@jotitadev thank you for the Linux run, and for pinning down why the four abort tests failed there. 5514a85 adds the SIGABRT handler you tested ( |
jotitadev
left a comment
There was a problem hiding this comment.
Re-ran on exact head 5514a8579ed9a27b2d903d9e9c4d34f5d6548b3a, merged on main 82ddae75b. Same machine as before: Intel i7-8700, Ubuntu 26.04, GCC 15.2, CMake 4.2.3, Ninja, Release, BUILD_TESTING=ON, full build.
unwind_cursorand the four abort tests (unwind_cursor_get_register,_set_register,_get_float_register,_set_float_register) now pass on Linux, 25 of 25 repeated runs each (ctest -R unwind_cursor --repeat until-fail:25). The abort tests print the expectedlibunwind: ... unsupported x86_64 registerline and exit normally through the new handler.- Non-GPU suite (
ctest -E "^agc|gpu|vulkan|video_out" -j6): 653/655 with this PR;main647/650 on the same machine. The two failures arefile_writer_binary/file_writer_text, which also fail onmainhere. The thirdmainfailure was aguest_pthread_starttimeout under load; it passes 5 of 5 when run alone.
Not checked: macOS, Windows.
Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.
5514a85 to
b178ec5
Compare
jotitadev
left a comment
There was a problem hiding this comment.
Re-ran on the new head, since b178ec5 changes the Linux-only path and the PR says it wasn't run on Linux.
Exact head b178ec5dedd774153631e0504684b8e1e1a58505, merged on main 674c25388. Linux x86_64, i7-8700, Ubuntu 26.04, GCC 15.2, CMake 4.2.3, Ninja, Release, BUILD_TESTING=ON, full build.
unwind_cursorand the four abort tests pass, 10 of 10 repeated runs. The 15unwind|exception|backtracetests pass.- Full non-GPU suite: 781/781.
- With b178ec5 reverted on the same merge,
unwind_cursorfails withunwind cursor check failed: getInfo gives the start of the segment holding the function. So the commit is needed on glibc now thatmaintakes the_dl_find_objectpath (2a12dbd, which landed after my run on 5514a85), and the test covers it.
Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.
|
The branch needs a rebase for the conflict in this file anyway (main added the Cutting the current text down to its open parts gives 875 characters, and the check passes with it: - The [slots](../../../core/libs/prx/libc/src/exception/Unwind.cpp) of the libunwind `AbstractUnwindCursor` table at the start of the [`_Unwind_Context`](../../../core/libs/prx/libc/include/exceptions/Unwind.hpp) that `_Unwind_Backtrace` passes to its callback follow `UnwindCursor<LocalAddressSpace, Registers_x86_64>` of [LLVM 18.1.8](https://github.com/llvm/llvm-project/blob/llvmorg-18.1.8/libunwind/src/UnwindCursor.hpp); the console's own `libSceLibcInternal` is not checked. Differences from LLVM 18.1.8: `step` walks Windows host frames without an FDE with `RtlVirtualUnwind` and `getInfo` reports them with no range; `step` looks a signal frame up at its IP instead of the IP minus one and ends at a frame whose rules leave `rsp` and `rip` unchanged; `getFunctionName` on Windows takes the nearest export at or below the IP; `jumpto` does not restore `r10` and `r11`Why the table exists is already recorded in db3ec02 and in the PR description, so the entry does not need to repeat it. |
b178ec5 to
0aab507
Compare
|
Rebased onto
On Windows |
Homebrew built with the ps5-payload-dev SDK links LLVM libunwind statically and calls through the AbstractUnwindCursor table at the start of the _Unwind_Context that _Unwind_Backtrace passes to its callback. Only the register slots and isSignalFrame were answered; getInfo, setInfoBasedOnIPRegister, step, jumpto, getFunctionName, getRegisterName, the float registers and the destructors threw, so a title that asked its libunwind for a frame's procedure info or name, or stepped or resumed that context, stopped there. The slots now answer as UnwindCursor<LocalAddressSpace, Registers_x86_64> of LLVM 18.1.8 does with DWARF unwind information, including its aborts for unknown and float registers. GetRules records the FDE, the end of the function, DW_CFA_GNU_args_size and the segment holding the IP, and Step is split so that the cursor steps with the rules _Unwind_Backtrace uses.
CTest fails a test that ends with a signal even when PASS_REGULAR_EXPRESSION matches, so the four abort tests failed on Linux, where abort() raises SIGABRT; on Windows _set_abort_behavior already turns it into an exit code. A SIGABRT handler now exits with status 3. The test registration moves before guest_string_tests, away from where another pull request adds its test.
Since main's VirtualUnwind records the function start, the language data and the personality of a host frame, getInfo reports those and only the end address stays zero.
0aab507 to
3551ba1
Compare
|
Rebased onto On Windows, the 16 |
MarcZX10
left a comment
There was a problem hiding this comment.
Reviewed current head 3551ba1 against LLVM libunwind 18.1.8 and the changed call paths. The 15-entry cursor slot order and x86_64 register mapping align with the reference; carries the FDE range, LSDA/personality, DWARF format, FDE pointer/size, segment base and parsed ; , end-of-stack handling, IP re-lookup, and the Windows host-frame fallback are consistent with the documented scope. The added cursor test exercises slot behavior, frame traversal, info/name queries, , and abort cases. Current hosted checks pass for conventions, eligibility, labels, macOS and Linux register-context/relinker matrices, ASan, and write-watch. The repository's full Linux and Windows product jobs are skipped by policy. The PR also documents remaining Windows/IP/jumpto gaps and lack of console-side verification.
|
Clarification to my approval summary: the intended references are |
jotitadev
left a comment
There was a problem hiding this comment.
Merge-skew check: head 3551ba114 merged onto current main abed03546 (clean merge, git merge --no-commit). This head's CI ran on the October 22 base, before #10706 landed. Linux x86_64, i7-8700, Ubuntu 26.04, GCC 15.2, Release, BUILD_TESTING=ON, full build, then the full non-GPU suite (ctest -E "^agc|gpu|vulkan|video_out" -j6).
mainalone: 1363/1363 pass.- With this PR:
unwind_cursorand the fourunwind_cursor_*tests pass, butpublic_headersfails 3/3, and its 64public_headers_compile_*dependents don't run:_Unwind_Backtrace_nid_postfix: expected exactly one header declaration; found 2: core/libs/prx/libc/include/Api.hpp:1326, core/libs/tests/UnwindCursor.cpp:17 _Unwind_GetLanguageSpecificData_nid_postfix: expected exactly one header declaration; found 2: core/libs/prx/libc/include/Api.hpp:1321, core/libs/tests/UnwindCursor.cpp:19 _Unwind_GetRegionStart_nid_postfix: expected exactly one header declaration; found 2: core/libs/prx/libc/include/Api.hpp:1322, core/libs/tests/UnwindCursor.cpp:18 - Cause: #10706 (35cf8d5 / f38d29f, merged 2026-10-24) added the
public_headersaudit and canonical declarations of these three exports inlibc/include/Api.hpp. The newUnwindCursor.cppdeclares them a second time.
Minimal fix I checked: include the canonical header and drop the three local declarations:
--- a/core/libs/tests/UnwindCursor.cpp
+++ b/core/libs/tests/UnwindCursor.cpp
@@ -1,3 +1,4 @@
+#include "prx/libc/include/Api.hpp"
#include "prx/libc/include/exceptions/Unwind.hpp"
@@
-extern "C" _Unwind_Reason_Code APS5_VABI _Unwind_Backtrace_nid_postfix(_Unwind_Trace_Fn trace, void* argument);
-extern "C" std::uintptr_t APS5_VABI _Unwind_GetRegionStart_nid_postfix(_Unwind_Context* context);
-extern "C" std::uintptr_t APS5_VABI _Unwind_GetLanguageSpecificData_nid_postfix(_Unwind_Context* context);The rest of the test compiles unchanged. With it, the full non-GPU suite passes 1368/1368, including public_headers, all public_headers_compile_* and the five unwind_cursor* tests.
The head still merges cleanly onto the newer main 92bfca720. Since abed03546, nothing there has touched the audit script, libc/include/Api.hpp or the tab bit.
Written with AI assistance (Claude Code). I ran the builds and tests above on my machine.
|
Static review plus an independent Linux run (CachyOS, kernel 7.2.10, GCC 16.2.1, Ninja 1.13.2, Release, What I checked (static)
Test runs (PR head
Baseline comparison ( Not covered (closing next)
|
Verdict: should-fix
Freshness Envelope
Cross-PR Ledger
|
|
@GitamarT045T3R are you still working on this? MarcZX10 approved the head on 24 October. Two items are left: If you are not, I would like to carry it as a separate PR with your six commits (and adriadam10's) kept: rebase onto main, include the public header and drop the local declarations in the test, resolve the debt conflict, add the missing |
|
@GitamarT045T3R Main moved and this PR conflicts in |
|
@GitamarT045T3R, @B-Howell merged current main into this PR as 0726762. On top of it there is one commit, d83b892, on https://github.com/sherySJ/AnyPS5/tree/agent/anyps5-carry-6819 that applies jotitadev's To take it: Tested on Linux only (Windows not tested): With both pieces the two open items are resolved, so we will not open a separate carry PR. AI-assisted: yes. |
What
The
_Unwind_Contextthat_Unwind_Backtracepasses to its callback starts with a table laid out like LLVM libunwind'sAbstractUnwindCursorvirtual table (db3ec02), because homebrew built with the ps5-payload-dev SDK links libunwind statically and calls through that table from its own_Unwind_GetIP. Only the register slots andisSignalFramewere answered; the other nine slots threw. A title that asks its own libunwind for a frame's procedure info or name (_Unwind_GetRegionStart,_Unwind_GetLanguageSpecificDataandunw_get_proc_infocallgetInfo;_Unwind_SetIPcallsgetInfoandsetInfoBasedOnIPRegister;unw_get_proc_namecallsgetFunctionName), steps a frame (unw_step) or resumes one (unw_resume) stopped there.Every slot now answers as
UnwindCursor<LocalAddressSpace, Registers_x86_64>of LLVM 18.1.8 does with DWARF unwind information:~AbstractUnwindCursor(complete and deleting)operator deleteis emptygetReg,setRegUNW_REG_SP) to 16 (rip); any other number printslibunwind: getRegister - unsupported x86_64 register(orsetRegister) and aborts, asRegisters_x86_64does through_LIBUNWIND_ABORTgetFloatReg,setFloatReglibunwind: getFloatRegister - no x86_64 float registers(orsetFloatRegister) and abortstepUNW_STEP_ENDwithout unwind info,UNW_EBADFRAMEwhen the rules cannot be applied, otherwise moves to the caller with the same rules as_Unwind_Backtraceand looks the new IP up, returningUNW_STEP_ENDwhen nothing covers it (IP 0 included)getInfoDW_CFA_GNU_args_sizeasgp,UNWIND_X86_64_MODE_DWARFasformat, the FDE and its size asunwind_info, the start of the ELF segment or PE section holding the IP asextra; all zero without unwind infojumpto_Unwind_RaiseExceptioninstalls a landing padgetFunctionNamedladdrof the IP (Linux, macOS); on Windows the nearest export at or below the IP in the image holding itsetInfoBasedOnIPRegistergetRegisterNameRegisters_x86_64::getRegisterName's names,unknown registerotherwiseTo share the walk with
_Unwind_Backtrace,Stepis split intoCaller(the registers of the caller) and the end-of-stack checks, andGetRulesrecords the FDE, the function's end, the arguments size and the segment in the context, and clears them when no FDE covers the IP.DW_CFA_GNU_args_sizewas parsed and dropped before.Differences from libunwind are listed in the libc technical debt entry: Windows host frames without an FDE are stepped with
RtlVirtualUnwindand report no range; a frame whose rules leaverspandripunchanged ends the walk instead of being returned again; a signal frame is looked up at its IP;jumptodoes not restorer10andr11.Tested
Windows 10 Pro 22H2 (19045), MinGW-w64 GCC 15.2.0 (WinLibs r7), Ninja, Release,
BUILD_TESTING=ON, onmaineb901bcf4:unwind_cursor(registered on every platform): captures a context in an exported probe and calls every slot through the table:setInfoBasedOnIPRegisterandgetInfoagainst the probe's own address, its FDE and_Unwind_GetRegionStart/_Unwind_GetLanguageSpecificData,getFunctionName(full and truncated), IP 0,stepto the exported caller and on to the end of the stack, register names, the destructors,jumptoback to a captured point, and the context_Unwind_Backtracepasses to its callback. Passes, 25 of 25 repeated runs.unwind_cursor_get_register,_set_register,_get_float_registerand_set_float_registermatch libunwind'slibunwind: getRegister - unsupported x86_64 registerandlibunwind: getFloatRegister - no x86_64 float registerslines: pass.libc.prxofmainstops at the firstsetInfoBasedOnIPRegisterwithlibunwind cursor: operation other than register access or isSignalFrame not implemented.windows_exceptions,windows_backtrace_*,guest_exception_*,guest_raise_exception,guest_unwind_module_info,agc_stack_walk,linux_eh_frame_header, ...) pass.ctest -j 12: 1324 of 1325 pass.agc_shader_disk_cachefails withAccess is deniedas it does onmainon Windows 10 (test(agc): delete mapped shader cache entries with POSIX semantics on Windows #6415).agc_driver_gpu_indirect_arguments_misaligned_writer_ahead_testsdoes not build with MinGW onmain(alignment 65536 above the object file maximum of 8192, test(agc): align the indirect arguments block at run time #6452), so it was excluded.python tools/check_conventions.py --base origin/mainpasses.getFunctionNameusesdladdrandextrathePT_LOADsegment fromdl_iterate_phdr; the test exports its probe withENABLE_EXPORTS.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)