# How SharpEmu Handles Native CPU Exceptions: Inside the Vectored Exception Handler System

> Discover how SharpEmu manages native CPU exceptions using SEH integration for PlayStation 5 emulation. Learn about vectored exception handlers and guest-visible signals.

- Repository: [Berk/sharpemu](https://github.com/par274/sharpemu)
- Tags: internals
- Published: 2026-07-16

---

**SharpEmu catches hardware-level CPU faults during native PlayStation 5 code execution using a dual-layer Windows SEH integration that translates host exceptions into guest-visible signals through vectored exception handlers and managed callback delegates.**

The `par274/sharpemu` repository implements a high-performance native execution backend that runs PS5 code directly on the host CPU. When guest instructions trigger hardware exceptions—such as page faults, divide-by-zero errors, or invalid instructions—the emulator must intercept these host-level faults, preserve the emulated CPU state, and translate them into exceptions the guest operating system understands. This article examines the exception handling system implemented in [`src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs), which bridges Windows Structured Exception Handling (SEH) with the emulator's High-Level Emulation (HLE) layer.

## The Architecture of Native Exception Handling in SharpEmu

SharpEmu's native CPU backend executes guest code directly on the host processor, making it vulnerable to the same memory access violations and arithmetic exceptions as native applications. Unlike interpreted emulation, which traps faults in software, this system requires registering hardware-level exception handlers with the Windows kernel.

### Windows SEH Integration and Vectored Handlers

The backend installs two complementary handlers during initialization in the `SetupExceptionHandler()` method (called from the constructor at line 1037). First, a **Vectored Exception Handler (VEH)** receives first-chance notification of all exceptions via `AddVectoredExceptionHandler`. Second, an **Unhandled Exception Filter** serves as a final safety net through `SetUnhandledExceptionFilter`.

These handlers are not installed directly as managed methods. Instead, the backend allocates executable memory using `VirtualAlloc` with `PAGE_EXECUTE_READWRITE` permissions and generates small native stubs that marshal the `EXCEPTION_POINTERS` structure from the Windows kernel to the managed runtime.

### EXCEPTION_POINTERS and Context Record Structures

To interpret hardware faults, SharpEmu defines blittable structures matching the Windows native SEH layout at lines 86-108 of [`DirectExecutionBackend.cs`](https://github.com/par274/sharpemu/blob/main/DirectExecutionBackend.cs):

```csharp
#pragma warning disable CS0649
private struct EXCEPTION_POINTERS
{
    public unsafe EXCEPTION_RECORD* ExceptionRecord;
    public unsafe void* ContextRecord;
}

private struct EXCEPTION_RECORD
{
    public uint ExceptionCode;
    public uint ExceptionFlags;
    public unsafe EXCEPTION_RECORD* ExceptionRecord;
    public unsafe void* ExceptionAddress;
    public uint NumberParameters;
    public unsafe fixed ulong ExceptionInformation[15];
}
#pragma warning restore CS0649

```

These structures allow the managed handler to dereference the exception code and CPU context registers without marshaling overhead.

## Implementation in DirectExecutionBackend.cs

The exception handling infrastructure spans several thousand lines in the native backend implementation, organized into delegate definitions, stub generation, and registration logic.

### Managed Delegate Callbacks

SharpEmu uses unmanaged function pointers to bridge native and managed code. Two critical delegates are defined:

```csharp
private delegate int ExceptionHandlerDelegate(void* exceptionInfo);
private static readonly ExceptionHandlerDelegate RawVectoredHandlerDelegateInstance = RawVectoredHandlerManaged;
private static readonly nint RawVectoredHandlerPtrManaged =
    Marshal.GetFunctionPointerForDelegate(RawVectoredHandlerDelegateInstance);

```

The `RawVectoredHandlerManaged` method (around line 444) serves as the primary entry point for all hardware exceptions, while `RawUnhandledFilterManaged` handles fatal errors that slip past the vectored handler.

### Stub Generation and OS Registration

The `SetupExceptionHandler()` method creates executable stubs that forward the exception pointer to the managed delegates:

```csharp
private void SetupExceptionHandler()
{
    // Allocate native stubs around lines 4800-5300
    _rawExceptionHandlerStub = CreateExceptionHandlerStub(RawVectoredHandlerPtrManaged);
    _unhandledFilterStub = CreateExceptionHandlerStub(RawUnhandledFilterPtrManaged);
    
    // Register with Windows APIs
    AddVectoredExceptionHandler(1, _rawExceptionHandlerStub);
    SetUnhandledExceptionFilter(_unhandledFilterStub);
}

```

After writing the stub machine code, the backend calls `FlushInstructionCache` to ensure CPU coherence, then registers the handlers with the OS.

## The Exception Handling Flow

When guest code triggers a hardware fault, the system processes the exception through a precise six-stage pipeline:

1. **Hardware Trap**: The CPU executes JIT-compiled guest code and raises a fault (e.g., `STATUS_ACCESS_VIOLATION` 0xC0000005), causing Windows to create an `EXCEPTION_POINTERS` structure on the stack.

2. **Vectored Handler Invocation**: The registered VEH stub (`_rawExceptionHandlerStub`) receives control immediately. This assembly stub extracts the `EXCEPTION_POINTERS` pointer and jumps to `RawVectoredHandlerManaged`.

3. **Exception Classification**: Inside the managed handler, the code reads `exceptionInfo->ExceptionRecord->ExceptionCode` and maps Windows codes to internal `GuestNativeCallExitReason` values:
   - `0xC0000005` (Access Violation) → `Exception`
   - `0xC0000094` (Integer Divide by Zero) → `Exception`
   - `DBG_PRINTEXCEPTION_C` → Ignored/debug output

4. **Context Preservation**: For recoverable faults, the handler copies the saved CPU registers from `exceptionInfo->ContextRecord` into the thread-static `ActiveCpuContext`. It populates a `PendingGuestException` record with the fault address, exception type, and guest stack pointer.

5. **HLE Delivery**: When the guest thread resumes, the backend checks `_pendingGuestExceptions` and invokes the appropriate HLE fault handler (e.g., `sceKernelRaiseException`) so the emulated PS5 code observes the expected POSIX signal behavior.

6. **Continuation**: The handler returns `EXCEPTION_CONTINUE_EXECUTION` to resume with the patched context, or `EXCEPTION_CONTINUE_SEARCH` if the fault originated outside the emulation boundary.

## Safety Mechanisms and Thread State Management

SharpEmu implements several guards to prevent handler corruption and re-entrancy deadlocks.

### Thread-Static State Isolation

The backend maintains per-thread execution state using thread-static fields that the vectored handler accesses to determine which guest context to patch:

```csharp
[ThreadStatic]
private static DirectExecutionBackend? _activeExecutionBackend;

[ThreadStatic]
private static CpuContext? _activeCpuContext;

```

These fields ensure that when multiple PS5 threads run concurrently, exception handlers route faults to the correct virtual CPU instance.

### Re-entrancy Guards and Memory Protection

The handler tracks recursion depth using `_vectoredHandlerDepth` and `_nestedVehTraceCount` to prevent infinite loops if the exception handler itself triggers a fault. Before executing generated stubs, the backend verifies memory protection using `VirtualProtect` and ensures allocated stub pages remain `PAGE_EXECUTE_READ` after initialization to prevent code injection.

## Summary

- **SharpEmu registers vectored exception handlers** via `AddVectoredExceptionHandler` in [`DirectExecutionBackend.cs`](https://github.com/par274/sharpemu/blob/main/DirectExecutionBackend.cs) to catch hardware faults during native PS5 code execution.
- **Managed callbacks bridge native SEH** through executable stubs that marshal `EXCEPTION_POINTERS` to `RawVectoredHandlerManaged` and `RawUnhandledFilterManaged`.
- **Exception translation** maps Windows error codes (0xC0000005, 0xC0000094) to guest-visible `GuestNativeCallExitReason` values, preserving the emulated CPU state in `ActiveCpuContext`.
- **Thread-static isolation** ensures multi-threaded safety, while re-entrancy counters prevent handler deadlock.
- **Fault delivery** ultimately routes through the HLE layer to present standard POSIX signals to the guest operating system.

## Frequently Asked Questions

### How does SharpEmu prevent host crashes when guest code causes a page fault?

SharpEmu registers a first-chance vectored exception handler that intercepts `STATUS_ACCESS_VIOLATION` (0xC0000005) before the Windows kernel terminates the process. The handler in `RawVectoredHandlerManaged` checks thread-static state to determine if the fault occurred within emulated memory, translates the violation into a guest exception record, and returns `EXCEPTION_CONTINUE_EXECUTION` to resume the thread safely.

### What is the difference between the vectored handler and the unhandled exception filter in SharpEmu?

The **vectored exception handler** receives all exceptions first via `AddVectoredExceptionHandler`, allowing SharpEmu to handle guest faults transparently. The **unhandled exception filter** serves as a final safety net via `SetUnhandledExceptionFilter`, catching any faults that bypass the VEH (such as corruption or bugs in the handler itself) and logging the crash before terminating the emulator gracefully.

### Why does SharpEmu use executable memory stubs instead of direct managed callbacks?

Windows requires exception handlers to be native code addresses, not managed function pointers. SharpEmu allocates executable memory using `VirtualAlloc`, writes small assembly stubs that conform to the `EXCEPTION_POINTERS` calling convention, and marshals the pointer to the managed delegate. This indirection satisfies the OS API requirements while allowing the handler logic to remain in type-safe C# code.

### How does the exception handler distinguish between emulator bugs and legitimate guest exceptions?

The handler checks the `ExceptionCode` field against known hardware fault signatures and validates the fault address against the current guest memory map. If the exception originates from emulator code (outside the JIT-compiled guest regions) or indicates an unrecoverable state (like stack overflow), it routes to `RawUnhandledFilterManaged` for fatal error handling. Otherwise, it treats the fault as a legitimate guest signal requiring HLE delivery.