[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) <noreply@anthropic.com>
This commit is contained in:
shadowbeat070
2026-07-19 14:01:12 +02:00
parent 3e39fb4c6e
commit 848f035827
2 changed files with 44 additions and 0 deletions
@@ -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",
@@ -578,6 +578,19 @@ public static class KernelPthreadCompatExports
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
}
/// <summary>
/// The POSIX-named alias of <see cref="PthreadOnce"/>. 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.
/// </summary>
[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)