From 848f035827bb2b1217dd0859c5adbf66129ce3a5 Mon Sep 17 00:00:00 2001 From: shadowbeat070 <70843316+shadowbeat070@users.noreply.github.com> Date: Sun, 19 Jul 2026 14:01:12 +0200 Subject: [PATCH] [Kernel] Implement clock_getres and the POSIX pthread_once alias clock_getres (smIj7eqzZE8) was missing entirely. It reports 100ns, which is the resolution clock_gettime here actually delivers via DateTimeOffset.UtcNow, rather than claiming the 1ns a caller might otherwise rely on. A null res pointer is accepted per POSIX. pthread_once (Z4QosVuAsA0) needed no new logic: libKernel exports the same routine under two NIDs and only scePthreadOnce (14bOACANTBo) was registered. Shipped middleware links the plain name. Both are imported by DOOM + DOOM II (PPSA21444): clock_getres blocked party.prx from initialising, and pthread_once is used by libcohtml, libPlayFabMultiplayer, party.prx and the eboot. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../Kernel/KernelMemoryCompatExports.cs | 31 +++++++++++++++++++ .../Kernel/KernelPthreadCompatExports.cs | 13 ++++++++ 2 files changed, 44 insertions(+) diff --git a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs index 2e45594..d82b7ed 100644 --- a/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelMemoryCompatExports.cs @@ -2327,6 +2327,37 @@ public static partial class KernelMemoryCompatExports return (int)OrbisGen2Result.ORBIS_GEN2_OK; } + [SysAbiExport( + Nid = "smIj7eqzZE8", + ExportName = "clock_getres", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libKernel")] + public static int ClockGetres(CpuContext ctx) + { + var timespecAddress = ctx[CpuRegister.Rsi]; + + // POSIX allows a null resolution pointer: the call then only validates + // the clock id, which every id a title passes here does. + if (timespecAddress == 0) + { + ctx[CpuRegister.Rax] = 0; + return (int)OrbisGen2Result.ORBIS_GEN2_OK; + } + + // clock_gettime above is backed by DateTimeOffset.UtcNow, whose tick is + // 100 ns, so that is the honest resolution to report rather than the 1 ns + // a caller might otherwise assume it can rely on. + const ulong ResolutionNanoseconds = 100; + if (!ctx.TryWriteUInt64(timespecAddress, 0) || + !ctx.TryWriteUInt64(timespecAddress + sizeof(long), ResolutionNanoseconds)) + { + return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT; + } + + ctx[CpuRegister.Rax] = 0; + return (int)OrbisGen2Result.ORBIS_GEN2_OK; + } + [SysAbiExport( Nid = "vNe1w4diLCs", ExportName = "__tls_get_addr", diff --git a/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs b/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs index cd2c92c..5b4e43c 100644 --- a/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs +++ b/src/SharpEmu.Libs/Kernel/KernelPthreadCompatExports.cs @@ -578,6 +578,19 @@ public static class KernelPthreadCompatExports return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK); } + /// + /// The POSIX-named alias of . libKernel exports the + /// same routine under two NIDs, and shipped middleware links the plain name: + /// DOOM's libcohtml, PlayFab and party modules all import this one rather + /// than scePthreadOnce. + /// + [SysAbiExport( + Nid = "Z4QosVuAsA0", + ExportName = "pthread_once", + Target = Generation.Gen4 | Generation.Gen5, + LibraryName = "libKernel")] + public static int PthreadOncePOSIX(CpuContext ctx) => PthreadOnce(ctx); + private static int PthreadMutexInitCore(CpuContext ctx, ulong mutexAddress, ulong attrAddress) { if (mutexAddress == 0)