Repository navigation
feat(libkernel): run threads for a lifted guest - #15803
oberonlift wants to merge 31 commits into
Conversation
c1e6f6a to
2a44260
Compare
✅ AMD GPU test: passedPR head
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (11, not counted against this PR)Validation IDs already present on main (8 IDs in 809 tests)Skipped (62)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ NVIDIA GPU test: passedPR head
Unstable on main (3, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validation IDs already present on main (13 IDs in 809 tests)Skipped (53)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ NVIDIA GPU test: passedPR head
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validation IDs already present on main (13 IDs in 809 tests)Skipped (53)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
2a44260 to
c0cdf08
Compare
✅ AMD GPU test: passedPR head
Unstable on main (2, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (12, not counted against this PR)Validated but not compared (65, not in the main baseline's validation pass)Validation IDs already present on main (4 IDs in 811 tests)Skipped (62)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
c0cdf08 to
4b21d14
Compare
✅ NVIDIA GPU test: passedPR head
Unstable on main (3, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validation IDs already present on main (9 IDs in 812 tests)Skipped (59)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
✅ AMD GPU test: passedPR head
Unstable on main (4, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (11, not counted against this PR)Validation IDs already present on main (4 IDs in 812 tests)Skipped (70)ReproduceAutomated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
4b21d14 to
48368e2
Compare
❌ NVIDIA GPU test: failedPR head
Failing testsThis PR changes test files, so each failing test was also built and run on main with only the PR's test changes applied. ✅ there means the failure comes from the PR's other changes; ❌ means the test fails on main too, so this run does not show the PR caused it.
agc_driver_submit_progress: Failed (also failed when re-run alone), assertion and diagnosticsUnstable on main (3, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)Validation IDs already present on main (9 IDs in 812 tests)Skipped (61)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
webrtc's CPU feature query runs cpuid and xgetbv through new GuestCpu helpers, which issue the same instructions as <cpuid.h>. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ps5lift's runtime is the library a lifted program links. It maps the guest images, dispatches indirect calls, lays out TLS as x86-64 does, runs x87 code on Berkeley SoftFloat, answers cpuid as the PS5's Zen 2, calls native functions through guest function pointers and back, and provides the GuestCpu functions, the guest FPU state and timestamp counter that AnyPS5's libraries read when they are built for a lifted guest. `tools/ps5lift.py runtime` builds it with SoftFloat, a new submodule, into a shared library for a target, and the program's main into an archive. Its known gaps are a ps5lift section of the technical debt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A workflow builds the runtime with LLVM 18 on GitHub's ubuntu-24.04-arm runner every night, on request, and for pull requests that change ps5lift or the workflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GuestCpuCpuid and GuestCpuXgetbv give a library built for a lifted guest the PS5's answers that lifted code gets from cpuid and xgetbv, so it can pick the code paths the guest CPU supports. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clang rejects a reinterpret_cast from const void* to a function pointer as casting away qualifiers, so only Apple builds cast the vtable entry to void* first, and a Clang build on Linux failed to compile libc. Every build now takes the const_cast form, which GCC compiles the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The libraries' lifted-guest code paths were only reachable by compiling their sources outside this build with the macro defined by hand, which cannot produce the generated and configured headers that libSceVideoOut, libSceSsl, libSceVorbisDec, libSceUsbd and libSceVideodec2 need. The option defines ANYPS5_GUEST_LIFTED for every target under core/libs, so the libs target builds all 368 libraries for a lifted guest on an aarch64 host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The full build always asked ffmpeg-core for the x86-64 package, so in an aarch64 build every library that links FFmpeg (libSceAjm, libSceAudiodec, libSceAvPlayer, libSceVideodec2) failed to link. The architecture now follows CMAKE_SYSTEM_PROCESSOR. A processor with no package, and MinGW on arm64, which has none either, stop the configuration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lifted build's libraries call the GuestCpu functions that ps5lift's runtime provides. On Linux they resolved against the executable at load time; macOS binds every undefined symbol to a named library, so the libraries have to link the runtime. ANYPS5_LIFTED_RUNTIME names it, and libc links it publicly, so every library that links libc gets it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Under ANYPS5_GUEST_LIFTED GuestCpu's helpers call GuestCpu*_nid_no_patch functions, which ps5lift's runtime implements on the guest's emulated CPU state: the fenv functions, the timestamp counter, cpuid and xgetbv then see the guest's FPU and the PS5's CPU. The soft-float builtins compute in the default floating-point environment through <cfenv> and pick NaN results as x86 does, the 16-byte atomics use __atomic_compare_exchange_n, Bessel uses std::sqrt and std::fma, and the x86 call-trace thunks and the setjmp/longjmp assembly are left to the host runtime, which owns the guest registers. Direct builds are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The libraries link ps5lift's runtime, but their RPATH named only their own directory, so a library that a process loads with dlopen, as some tests load libc, could not find the runtime. In a lifted build their RPATH also names the runtime's directory. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The runtime refuses to describe the guest images until the executable hands it its lifted program, and a test program under core/libs is a native program with none, so libraries that look up guest images stopped. In a lifted build the test programs there link an object that hands the runtime an empty program, and link the runtime. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…arm64 The workflow builds the runtime, configures AnyPS5 with ANYPS5_GUEST_LIFTED on GitHub's ubuntu-24.04-arm runner and runs the tests that ps5lift/tests/lifted_tests.txt names on lavapipe: tools/lifted_tests.py builds the files their commands name and runs them with ctest. The 101 tests that pass with this change are the ones whose programs do not link libc, such as the relinker's and the shader recompiler's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ForwardExports.cpp defines each forwarded export as an x86 jmp to the export it aliases, which does not assemble for aarch64. Lifted builds leave the file out: a lifted guest reaches an export only through an import bridge generated from its C declaration, and these aliases have none. The debt file lists the gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a lifted build the DWARF unwinder starts from the guest frame that called the import, finds frames through the unwind tables of the guest images, which dl_iterate_phdr lists through the program's GuestCpuIterateImages, resolves the personality routine from its import name and installs a landing pad by handing its registers back to the guest CPU, through new GuestCpu functions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In an ANYPS5_GUEST_LIFTED build the guest images are not host shared objects, so glibc's dl_iterate_phdr cannot see them: the application heap never found the executable's process parameters. Every caller now includes ElfTypes.hpp instead of <link.h>, which in a lifted build lists the program's guest images through GuestCpuIterateImages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lifted build keeps the host's C++ runtime, but with libstdc++ the libc still defined std::current_exception, std::rethrow_exception and exception_ptr's members over its own runtime. Host code that caught an exception then got a null exception_ptr from the runtime that had not thrown it: the AGC driver reported a failed compute dispatch as "null asynchronous failure" and terminated. Lifted builds now give the guest its own exception pointer type, as libc++ builds do, and leave the host's exception pointers to the host runtime. The guest's _Result_base destructor export releases the guest pointer in place. Lifted builds also stop exporting the host ABI's names: Exports.cpp's __cxa_* and _Unwind_* aliases and Personality.cpp's global __gxx_personality_v0. A lifted executable loads libc before libstdc++ and libgcc_s, so the host's own throws bound to libc's versions and aborted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a lifted build every VaList is a SysV va_list built by the guest or by a bridge, so the libc formats and scans through FormatArguments and its in-process scanner instead of the host's vprintf and vsscanf. The libc's own variadic functions wrap the host va_list in a tagged VaList through the shared APS5_VA_BEGIN macro, which the formatting and scanning tests now use as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A report handler is guest code, which reads the call's arguments through the x86-64 SysV va_list in the report. The dispatcher copied its own host va_list there, which is that layout only on an x86-64 host, so a lifted build for aarch64 did not compile. In lifted builds the report now carries a SysV va_list built around the call's values: the named parameters fill every integer register, so the values are its overflow area, behind an empty register save area. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… builds sceAudioPropagationReportApi hands its report handlers the call's own va_list, which is the x86-64 SysV one the guest handler reads only on an x86-64 host; a lifted build for aarch64 did not compile. In lifted builds a host caller now stops as not implemented. Guest calls already stop at the bridge, which has no free integer register and no va_list sibling to forward them through. The library's test leaves out its report checks, which read the report's va_list as the host's, in lifted builds and checks the rejection instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add GuestLongDouble to load and store the guest's x87 80-bit values, so the libc can compute with a host long double that is not x87 extended, such as aarch64's binary128, when built for a lifted guest. Formatting, scanning, strtold, modfl, _LDtest and the code that takes 80-bit values apart go through it; strtold parses with round-to-odd so that the final rounding is correct, and long double arguments that host callers pass through a va_list are rounded to the guest's precision. On an x87 host it only copies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lifted libc held the guest's x87 long double in the host's long double:
IEEE binary128 on Linux, which holds every 80-bit value but rounds each
step differently, and a plain double on macOS, which holds neither.
Lifted builds now keep it in GuestLongDouble, laid out as the guest stores
it. Its arithmetic, square root, integer rounding and conversions to
double, float and integers run through the ps5lift runtime's x87, so they
round, honour precision control and raise exceptions as the guest's
control and status words say. Comparisons, conversions from integers and
doubles, frexp, scalbn, fmod, remainder, remquo, modf, logb and the
fixed-direction roundings work on the bits. strtold and scanf("%Lf") parse
with exact big-integer arithmetic, correctly rounded in the guest's
rounding mode; the narrow scanner reads the field itself, with the wide
scanner's FloatFit, now shared. printf already formatted the exact digits.
On an x86-64 host with x87 behind the runtime's entry points, the parser
matched glibc's x87 strtold in 58,244 cases across the four rounding
modes, and the bit operations matched x87 libm in 5.6 million.
The transcendental l functions and fmal still compute in the host's
widest type and round the result, which on macOS keeps 53 bits; the debt
entry says so. long double complex results are left out of lifted builds,
where they were never bridged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Call guest thread entries and exception handlers through GuestCpu, and leave out the x86 assembly for sigsetjmp, siglongjmp, getcontext and their kin in lifted builds, where the runtime provides the jumps and context switching reports itself as not implemented. A handler raised on a lifted thread sees the guest registers of the thread's last call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The bridge already turns the guest entry point into a native thunk, so a lifted build calls it directly instead of through a GuestCpu call. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A lifted thread's guest frames lived on ps5lift's fixed 8 MiB stack while scePthreadCreate sized the host stack, which holds the lifted frames, from the guest's request and reported part of it as the thread's stack. The guest stack is now a guarded mapping of the size the guest asked for, which the thread hands to the runtime through GuestCpuUseStack before its entry runs and which scePthreadAttrGet reports, and the host stack is the size GuestCpuHostStackSize gives. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A thread handle is the address of its PthreadPrivate, which came from the host heap. On the console it is the address of the thread's structure in the process's own memory. On an aarch64 host the host heap lies at 0xaaaa... and 0xffff..., outside anything the guest can address, and a library that prints a handle with %p and reads it back with base 0 saw a leading hex letter and threw where the console's handle starts with a digit. In lifted builds PthreadPrivate now comes from the guest heap, so every handle, those of adopted threads included, is a guest address. Direct builds keep the host heap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ps5lift loads from a lifted image's read-only segments without ordering, which holds only while nothing writes them. In lifted builds DoMprotect reports the pages each protection change makes writable to GuestCpuMakingWritable_nid_no_patch, and the runtime stops the program when they overlap such a segment. A MAP_FIXED mapping over such a segment is not reported; the libkernel debt notes record it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2190ab5 to
df80f50
Compare
✅ NVIDIA GPU test: passedPR head
Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)Failing on main too (3, not counted against this PR)Validation IDs already present on main (9 IDs in 825 tests)Skipped (61)ReproduceAutomated build and test on a contributor's NVIDIA machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs. |
Merge after #15802. Until the PRs before it merge, this PR also shows their changes; oberonlift#13 shows only its own.
ps5lift is a static recompiler for PS5 executables: it lifts a game's x86-64 code to LLVM IR and compiles it ahead of time to aarch64, which runs on Linux against AnyPS5's own libraries with no interpreter or JIT. It runs AnyPS5's PS5-style test programs and a homebrew media player to its home screen; commercial games are not playable yet. Branch
ps5lift-previewmerges all of its PRs (how to try it); native Apple Silicon support is coming along fast. It is a separate approach from the ARM64 compiler in #5021; #1835 has an introduction.This is 7 of 8 PRs that let AnyPS5's libraries run a lifted program; the lifter itself follows them.
x86-64: behaviour unchanged. Thread objects stay on the host heap; the stack size arithmetic shared with lifted builds computes the same values.
What
GuestCpu, and a lifted thread entry, which the bridge already turned into a native thunk, is called directly. The x86 assembly forsigsetjmp,siglongjmp,getcontextand their kin is left out of lifted builds, where the runtime provides the jumps and context switching reports itself as not implemented.GuestCpuUseStackand reported byscePthreadAttrGet; the host stack is the sizeGuestCpuHostStackSizegives. The thread takes its guest stack and the console's x87 control word before it unblocks signals, so a signal raised at a new or suspended thread runs its handler on that guest stack.DoMprotectreports the pages each protection change makes writable toGuestCpuMakingWritable_nid_no_patch. ps5lift loads from read-only segments without ordering, and its runtime stops the program when such pages overlap one. AMAP_FIXEDmapping over such a segment is not reported yet.GuestCpuCallInterruptedand sees the guest registers thatGuestCpuInterruptedContextreports; the signal-delivery assembly is left out of lifted builds.sceKernelWaitOnAddressreads a misaligned value with a plain copy followed by an acquire fence instead of x86'smov.EnterGuestMainStackcalls the guest entry on the stack the runtime gave the main thread, instead of switching to a main stack at the console's address.MAP_FIXEDgap, the host stack that the main and adopted threads report, and the zerofpstate,csandssan exception handler reads.#5021 also changes some of the same files:
Thread.cpp,Pthread.hpp,Exception.cpp,SignalSets.cpp,DirectMemory.cpp.Tested
Compiled for x86-64 Linux before and after this PR (GCC 13, same flags): 158 of the 162 objects it reaches are byte-identical;
Thread.cppdiffers inscePthreadCreate's register allocation and a line-number constant, andException.cpp,SyncOnAddress.cppandUserContext.cpponly in line-number constants.On a DGX Spark (aarch64 Linux, NVIDIA GB10, LLVM 18), with all eight PRs and ps5lift on top: every library builds for a lifted program, 2,285 of AnyPS5's own tests pass in that build on lavapipe, and ps5lift's 69 tests pass, among them its libc, exceptions, kernel, fiber and module-loading fixtures and upstream's libc test programs.
The conventions check passes on this branch.
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