Module Initialization Sequence in SharpEmu Runtime: From Eboot Load to Entry Point

SharpEmu loads the main executable, registers modules, loads adjacent PRX files, runs pre-loaded system initializers, then executes image initializers before dispatching to the program entry point.

SharpEmu is a PlayStation 5 emulator that implements a precise module initialization sequence to prepare guest binaries for execution. According to the par274/sharpemu source code, the runtime follows a strict order defined in SharpEmu.Core/Runtime/SharpEmuRuntime.cs to ensure system libraries initialize before the main executable. Understanding this sequence is essential for developers analyzing PS5 eboot behavior or debugging DT_INIT failures.

Overview of the Initialization Pipeline

The module initialization sequence in SharpEmu runtime is orchestrated by the Run method inside SharpEmu.Core/Runtime/SharpEmuRuntime.cs. This method coordinates six distinct phases, from loading the main image to dispatching the final entry point. Each phase ensures that dependencies are resolved before subsequent initializers execute, with error checking at every stage to prevent corrupted state from reaching the program entry point.

Step-by-Step Module Initialization Sequence

Loading the Main Image and Module Registration

The sequence begins when LoadImage reads the eboot binary into memory. Immediately after loading, RegisterLoadedModule adds the main module to the internal registry. This registration step is essential because later phases need to track which modules have active import stubs and runtime symbols available for resolution.

Loading Adjacent System Modules

Before running any guest code, the runtime calls LoadAdjacentSceModules to discover and load nearby PRX files. These adjacent modules typically include system libraries required by the main executable. The runtime maps these into memory alongside the primary image, making their exports available for import resolution before initialization begins.

Running Pre-Loaded Module Initializers

The first execution phase handles modules flagged with StartAtBoot. The RunPreloadedModuleInitializers method iterates over these pre-loaded modules and performs three critical operations:

  • Retrieves the entry point from SelfImage.InitFunctionEntryPoint
  • Invokes CpuDispatcher.DispatchModuleInitializer to execute the DT_INIT function in the guest context
  • Uses KernelModuleRegistry to track start and completion states

This phase ensures that system libraries initialize before the main program. If any initializer returns an error, the runtime aborts immediately and reports the failure.

Executing Image Initializers

After system modules initialize, RunImageInitializers processes the main executable and any loaded modules. This phase runs in two stages:

  1. Pre-initializers (PreInitializerFunctions) – Early setup routines compiled into the binary
  2. Normal initializers (InitializerFunctions) – Standard DT_INIT functions

Both stages are dispatched via RunInitializerList, which again leverages CpuDispatcher.DispatchModuleInitializer to execute in the guest CPU context. While current PS5 dumps sometimes contain empty DT_INIT arrays, the runtime logic remains ready to handle populated initializer lists.

Key Source Files and Implementation Details

The module initialization sequence spans several core components:

Practical Implementation Example

To invoke the full initialization sequence manually:

// Initialize the runtime and run the eboot
var runtime = SharpEmuRuntime.CreateDefault();
var result = runtime.Run("path/to/eboot.bin");
// This triggers LoadImage, RegisterLoadedModule, LoadAdjacentSceModules,
// RunPreloadedModuleInitializers, and RunImageInitializers

Internally, the RunAllInitializers method structures the sequence as follows:

private OrbisGen2Result? RunAllInitializers(
    SelfImage mainImage,
    IReadOnlyList<LoadedModuleImage> loadedModuleImages,
    Generation generation,
    IReadOnlyDictionary<ulong, string> activeImportStubs,
    IReadOnlyDictionary<string, ulong> activeRuntimeSymbols,
    string processImageName)
{
    // Phase 1: Pre-loaded system modules (StartAtBoot)
    var moduleStartResult = RunPreloadedModuleInitializers(
        loadedModuleImages,
        generation,
        activeImportStubs,
        activeRuntimeSymbols);
    if (moduleStartResult is not null) return moduleStartResult;

    // Phase 2: Main image DT_PREINIT and DT_INIT
    // (Current PS5 dumps may have empty DT_INIT, but logic is ready)
    return null;
}

Summary

  • The module initialization sequence in SharpEmu runtime follows a strict six-phase pipeline orchestrated by SharpEmuRuntime.Run.
  • System modules marked StartAtBoot initialize first via RunPreloadedModuleInitializers, followed by the main image through RunImageInitializers.
  • CpuDispatcher.DispatchModuleInitializer executes all DT_INIT functions in the guest context.
  • KernelModuleRegistry tracks initialization state, while ModuleManager maintains the export registry.
  • Initialization aborts immediately if any initializer returns an error, preventing corrupted state from reaching the entry point.

Frequently Asked Questions

What is the difference between pre-loaded module initializers and image initializers?

Pre-loaded module initializers run via RunPreloadedModuleInitializers and handle system libraries flagged with StartAtBoot that must initialize before the main program. Image initializers run via RunImageInitializers and process the main executable's DT_PREINIT and DT_INIT arrays, along with any additional loaded modules.

How does SharpEmu handle errors during module initialization?

If any initializer returns an error, the runtime aborts immediately and reports the failure. This occurs in RunAllInitializers where the return value of RunPreloadedModuleInitializers is checked; if not null, the error propagates upward and prevents DispatchEntry from executing.

What role does CpuDispatcher play in the initialization sequence?

CpuDispatcher provides the DispatchModuleInitializer method that actually executes the DT_INIT functions. It bridges the gap between the runtime's C# orchestration logic and the guest code execution, ensuring initializers run in the proper CPU context.

Where are the export tables generated that ModuleManager consumes?

The SysAbiExportGenerator in SharpEmu.SourceGenerators/SysAbiExportGenerator.cs generates export tables at build time. These tables feed into SharpEmu.HLE/ModuleManager.cs, which registers available exports before the initialization sequence begins.

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 →