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 CpuContext parameter containing the emulated CPU state
  • It invokes SetZeroReturn to satisfy the return contract
  • It targets specific generations (Gen4 or Gen5) via the Target property

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 0 prevents the guest process from crashing when calling unimplemented kernel functions
  • Discoverability: New NIDs are logged and tracked in ps5_names.txt until 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 SetZeroReturn method in src/SharpEmu.Libs/Kernel/KernelExports.cs, which writes 0 to the RAX register 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.txt and routed to stub methods that prevent crashes while maintaining ABI compatibility.

  • Functional implementations like sceKernelGetCompiledSdkVersion demonstrate how real kernel logic integrates with the stub architecture, using CpuContext for register access and memory operations.

  • The HLE system registers all exports through ModuleManager.cs and initializes them via SharpEmuRuntime.cs based 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →