How SharpEmu Implements Kernel Function Stubs for PlayStation 5 Emulation
SharpEmu implements kernel function stubs by returning 0 (success) through a centralized SetZeroReturn method in KernelExports.cs, allowing unimplemented PlayStation 5 kernel calls to execute safely without crashing the guest code.
SharpEmu is an open-source PlayStation 5 emulator that uses High-Level Emulation (HLE) to bridge the gap between the guest kernel API and the host system. When encountering unimplemented kernel functions, the emulator relies on kernel function stubs to maintain ABI compatibility while preventing crashes. These stubs are defined in src/SharpEmu.Libs/Kernel/KernelExports.cs and provide a safe fallback for unknown NIDs (Named Identifiers).
The Centralized Stub Mechanism
At the core of SharpEmu’s kernel emulation lies a simple but effective pattern for handling unimplemented functions. Rather than leaving undefined behavior or throwing exceptions, the emulator returns success codes to keep the guest process running.
The SetZeroReturn Method
In src/SharpEmu.Libs/Kernel/KernelExports.cs, the private method SetZeroReturn serves as the universal handler for stubbed kernel functions. This method writes 0 into the RAX register (the x86-64 return value register) and returns 0 as the C-style function result:
private static int SetZeroReturn(CpuContext ctx)
{
ctx[CpuRegister.Rax] = 0; // Return value = success
return 0; // C‑style int return
}
By zeroing out RAX, the stub mimics a successful kernel call according to the PlayStation 5 ABI contract, allowing the guest binary to continue execution without detecting an error condition.
Marking Exports with SysAbiExport
Kernel functions in SharpEmu are exposed to the HLE system through the [SysAbiExport] attribute, defined in src/SharpEmu.HLE/SysAbiExportAttribute.cs. This attribute maps C# methods to specific PlayStation 5 kernel symbols by NID (Named Identifier) and export name.
Each stubbed function follows a consistent pattern:
- It receives a
CpuContextparameter containing the emulated CPU state - It invokes
SetZeroReturnto satisfy the return contract - It targets specific generations (Gen4 or Gen5) via the
Targetproperty
For example, the stub for an unknown kernel symbol appears as a simple one-liner forwarding the context:
[SysAbiExport(
Nid = "9T2pDF2Ryqg",
ExportName = "sceKernelUnknown9T2p",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int KernelUnknown9T2p(CpuContext ctx) => SetZeroReturn(ctx);
Handling Unknown NIDs
When the runtime encounters kernel NIDs that lack full implementations, SharpEmu consults scripts/ps5_names.txt. This file collects observed NIDs from the wild, and any unknown entries are routed to stub methods in KernelExports.cs that call SetZeroReturn.
This approach provides three key benefits:
- Safety: Returning
0prevents the guest process from crashing when calling unimplemented kernel functions - Discoverability: New NIDs are logged and tracked in
ps5_names.txtuntil developers add proper implementations - Performance: Simple stubs avoid the overhead of full kernel emulation while maintaining ABI compliance
Functional Implementations vs. Stubs
While many kernel functions are stubbed with zero returns, some require minimal functional logic. The implementation of sceKernelGetCompiledSdkVersion demonstrates how functional exports coexist with the stub pattern.
Located in the same KernelExports.cs file, this method uses CpuContext helpers to read the version pointer from RDI, validate the address, and write the appropriate SDK version for the target generation:
[SysAbiExport(
Nid = "WB66evu8bsU",
ExportName = "sceKernelGetCompiledSdkVersion",
Target = Generation.Gen4 | Generation.Gen5,
LibraryName = "libKernel")]
public static int KernelGetCompiledSdkVersion(CpuContext ctx)
{
var versionAddress = ctx[CpuRegister.Rdi];
if (versionAddress == 0)
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
var sdkVersion = ctx.TargetGeneration == Generation.Gen5
? Gen5CompiledSdkVersion
: Gen4CompiledSdkVersion;
if (!ctx.TryWriteUInt32(versionAddress, sdkVersion))
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
ctx[CpuRegister.Rax] = 0;
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}
Unlike the simple stubs, this method performs actual work: validating arguments, selecting the correct version based on generation, and handling memory writes. However, it still concludes by setting RAX to 0 to indicate success, maintaining consistency with the kernel ABI.
Runtime Registration and Integration
The kernel exports are registered with the emulator through src/SharpEmu.HLE/ModuleManager.cs, which scans for [SysAbiExport] attributes and builds the mapping between NIDs and C# methods. During initialization, src/SharpEmu.Core/Runtime/SharpEmuRuntime.cs loads the appropriate kernel exports for the target generation (PlayStation 4 or PlayStation 5 compatibility).
This architecture ensures that:
- Fully implemented kernel functions provide realistic behavior
- Unknown or unimplemented calls fall back to safe stubs
- The emulator can incrementally add proper kernel support without breaking existing functionality
Summary
-
Kernel function stubs in SharpEmu are implemented via the
SetZeroReturnmethod insrc/SharpEmu.Libs/Kernel/KernelExports.cs, which writes0to theRAXregister to simulate successful kernel calls. -
The
[SysAbiExport]attribute registers C# methods as PlayStation 5 kernel exports, mapping them to specific NIDs and library names. -
Unknown NIDs are tracked in
scripts/ps5_names.txtand routed to stub methods that prevent crashes while maintaining ABI compatibility. -
Functional implementations like
sceKernelGetCompiledSdkVersiondemonstrate how real kernel logic integrates with the stub architecture, usingCpuContextfor register access and memory operations. -
The HLE system registers all exports through
ModuleManager.csand initializes them viaSharpEmuRuntime.csbased on the target generation.
Frequently Asked Questions
What is the purpose of kernel function stubs in SharpEmu?
Kernel function stubs allow the emulator to handle unimplemented PlayStation 5 kernel calls gracefully. By returning 0 (success) through the SetZeroReturn method, SharpEmu prevents the guest code from crashing while maintaining the expected ABI contract, giving developers time to implement full kernel functionality later.
How does SetZeroReturn maintain ABI compatibility?
The SetZeroReturn method maintains ABI compatibility by writing 0 into the RAX register and returning 0 as the function result. According to the x86-64 System V ABI used by the PlayStation 5, RAX holds the return value of functions, so zeroing it signals success to the caller, satisfying the contract without requiring actual kernel logic.
Where are unknown kernel NIDs documented in SharpEmu?
Unknown kernel NIDs are collected in scripts/ps5_names.txt. When the runtime encounters an NID that lacks a full implementation, it routes the call to a stub method in KernelExports.cs. This file serves as a living document of observed kernel symbols that need proper implementation.
How do functional kernel exports differ from simple stubs?
Functional kernel exports like sceKernelGetCompiledSdkVersion perform actual logic using CpuContext helpers to read registers, validate pointers, and write memory, while simple stubs immediately return 0 through SetZeroReturn. Both types conclude by setting RAX to 0, but functional implementations handle the specific kernel behavior before returning.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →