# How SharpEmu Handles SELFs and PRX Modules for Game Loading

> Learn how SharpEmu's SelfLoader parses ELF and fSELF binaries, maps segments, resolves relocations, and creates import stubs for game loading. Understand its core module transformation process.

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

---

**SharpEmu’s `SelfLoader` class in [`src/SharpEmu.Core/Loader/SelfLoader.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Loader/SelfLoader.cs) parses decrypted ELF or fake-signed SELF (fSELF) binaries, maps segments into virtual memory, resolves relocations, and creates import stubs to transform PlayStation binaries into runnable emulator modules.**

SharpEmu, developed by par274, implements a specialized loading pipeline for PlayStation Executable and Linkable Format (SELF) and PRX modules that converts raw binary images into executable guest code. Understanding how SharpEmu handles SELFs and PRX modules for game loading requires examining the `SelfLoader` implementation, which orchestrates binary validation, memory mapping, and dynamic linking. The loader strictly requires pre-decrypted images—either plain ELF files or fake-signed SELFs (fSELFs)—and explicitly aborts if it encounters encrypted retail eboots.

## Detecting and Validating Binary Formats

The loading process begins with `ParseLayout`, which inspects the first four bytes of the input file to determine the container format. If the magic value matches `0x4F153D1D`, the loader identifies a **SELF** container and proceeds to read the SELF header and segment table to calculate the `elfOffset`. If the magic equals `0x7F454C46` (the ASCII sequence `\x7fELF`), the file is treated as a plain **ELF**. Any other magic value triggers an immediate abort, as SharpEmu does not implement runtime decryption for encrypted retail binaries.

Upon successful detection, the loader returns a `LoadContext` structure that flags whether the image originated as a SELF and contains the calculated ELF offset plus the segment list. The loader then reads the `ElfHeader` structure at the determined offset and validates it via `ValidateElfHeader` before proceeding to memory mapping.

## Mapping Segments and Memory Allocation

The `MapLoadSegments` method walks every `PT_LOAD` program header in the ELF, calculating the guest virtual address by adding `header.VirtualAddress` to the `imageBase`. For each segment, the loader maps the file data directly into the emulator’s virtual memory system (conforming to `IVirtualMemory`), or allocates zero-filled memory for BSS sections. The implementation emits alignment warnings if the ELF-specified alignment does not match the file offset, ensuring that memory layout matches the binary’s expectations.

## Handling Thread-Local Storage (TLS)

When the ELF contains a `PT_TLS` segment, SharpEmu registers a **TLS template** for the module via `RegisterModuleTlsTemplate`. The loader assigns a module-local TLS ID using the `_nextTlsModuleId` counter and records the static offset required for TLS-relative relocations. This mechanism ensures that thread-local variables from PlayStation binaries are correctly addressed during emulation.

## Resolving Relocations and Imports

The `ParseDynamicInfo` method locates the `PT_DYNAMIC` segment and loads the string table, symbol table, and relocation tables (`Rela` and `JmpRel`). The loader builds a list of relocation descriptors and processes each entry through `ComputeRelocationValue`:

- **Relative relocations** (`R_X86_64_RELATIVE` / `R_X86_64_RELATIVE64`) are patched with `imageBase + addend`.
- **TLS-module-id relocations** (`R_X86_64_DTPMOD64`) are patched with the module’s assigned TLS ID.
- **Symbol-based relocations** extract the symbol name, translate it to a PlayStation **NID** (Name Identifier) using `ExtractNid`, and determine if an import stub is required.

For each unique imported NID, `CreateImportStubMapping` allocates a small code stub at a predictable address calculated as `ImportStubBaseAddress + stride * index`. The stub initially contains a `0xCC` trap opcode followed by a `0xC3` return opcode; the emulator can later replace the trap with a real function pointer when the import is resolved by the module manager ([`src/SharpEmu.HLE/ModuleManager.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.HLE/ModuleManager.cs)).

The final relocation values are written to guest memory via `TryWriteRelocationValue`, with the loader warning if a relocation would write a suspiciously small absolute address—often indicating an unresolved import. After resolving imports, `RegisterRuntimeSymbolsAndHooks` registers any exported symbols from the loaded module so that other modules can import them later.

## Preparing Entry Points and Initialization

Before returning the loaded image, the loader extracts the module’s initialization entries from `DT_INIT`, `DT_INIT_ARRAY`, and `DT_PREINIT_ARRAY`. It resolves their guest addresses and stores them in the returned `SelfImage` object along with the final entry point (`elfHeader.EntryPoint + imageBase`). The emulator can then invoke these initializer functions before jumping to the main entry point to ensure proper runtime setup.

## Loading a Game: Practical Example

The following examples demonstrate how to load a decrypted ELF or fSELF into SharpEmu and prepare it for execution:

```csharp
// Load a decrypted ELF or fSELF from a file into the emulator
byte[] fileBytes = File.ReadAllBytes("mygame.elf");          // or .self
var vmem = new PhysicalVirtualMemory();                      // VM implementation
var loader = new SelfLoader();

// Simple load – no extra filesystem, no module manager
SelfImage image = loader.Load(fileBytes, vmem);

// Load with a custom module manager so the game can import symbols from other modules
IModuleManager modMgr = new MyModuleManager();
SelfImage image2 = loader.Load(fileBytes, vmem, modMgr);

```

To manually inspect or add import stubs during development:

```csharp
// Example: Adding a runtime import stub manually (rarely needed)
ulong stubAddr = loader.CreateImportStubMapping(vmem, new[] { "sceAudioOutOpen" })[0];
Console.WriteLine($"Import stub for sceAudioOutOpen placed at 0x{stubAddr:X16}");

```

After loading, invoke initializers before starting execution:

```csharp
// After loading, invoke all initializers before the main entry point
foreach (ulong init in image.InitializerFunctions)
    vmem.CallFunction(init);               // pseudo‑call into guest memory

// Finally jump to the main entry point
vmem.CallFunction(image.EntryPoint);

```

## Summary

- SharpEmu requires **decrypted ELF** or **fSELF** images and rejects encrypted retail binaries with a clear error message.
- The `SelfLoader` class validates magic signatures (`0x4F153D1D` for SELF, `0x7F454C46` for ELF), maps `PT_LOAD` segments, and handles TLS templates via `RegisterModuleTlsTemplate`.
- Relocations are resolved through `ComputeRelocationValue`, supporting `R_X86_64_RELATIVE`, `R_X86_64_DTPMOD64`, and symbol-based types.
- Import stubs are generated at `ImportStubBaseAddress` with `0xCC` trap instructions for unresolved NIDs.
- Runtime initializers (`DT_INIT`, `DT_INIT_ARRAY`) are invoked before jumping to the final `EntryPoint` stored in the `SelfImage`.

## Frequently Asked Questions

### What file formats does SharpEmu support for game loading?

SharpEmu supports decrypted ELF files and fake-signed SELF (fSELF) binaries. It explicitly rejects encrypted retail SELF files (eboots) because it does not implement runtime decryption, instructing users to provide decrypted images instead.

### How does SharpEmu resolve imports from PRX modules?

The loader extracts symbol names from relocation entries, converts them to PlayStation NIDs using `ExtractNid`, and creates import stubs via `CreateImportStubMapping`. These stubs are placed at calculated addresses starting at `ImportStubBaseAddress` and initially contain trap instructions (`0xCC`) that can be replaced with actual function pointers when resolved by the module manager.

### What is the purpose of TLS handling in SharpEmu’s loader?

When loading modules containing `PT_TLS` segments, SharpEmu registers a TLS template via `RegisterModuleTlsTemplate` and assigns a module-local TLS ID (`_nextTlsModuleId`). This enables correct computation of TLS-relative relocation offsets for thread-local variables in PlayStation binaries.

### Where does SharpEmu place loaded segments in memory?

The `MapLoadSegments` method calculates guest virtual addresses by adding the program header’s `VirtualAddress` to the `imageBase`, then maps file data or zero-filled memory for BSS sections into the emulator's virtual memory system (`IVirtualMemory`).