# How SharpEmu Import Stub Resolution Works: PlayStation NID Handling Explained

> Learn how SharpEmu resolves imported PlayStation NIDs using trap-based import stubs and ELF relocation patching. Explore the inner workings of this essential emulation technique.

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

---

**SharpEmu resolves imported PlayStation NIDs by creating trap-based import stubs in a dedicated virtual address range, then patching ELF relocations to reference these stubs until the host implements the actual function.**

The `par274/sharpemu` project implements a sophisticated import stub resolution system that bridges the gap between encrypted PlayStation binaries and host execution. When loading a decrypted SELF/ELF image, SharpEmu parses the dynamic section to build relocation descriptors, then generates safe import stubs for unresolved symbols. This mechanism isolates external dependencies while allowing the host to deterministically trap and later resolve native PlayStation function calls.

## The Import Stub Resolution Workflow

SharpEmu's resolution process follows a strict eight-step pipeline that transforms raw ELF relocations into executable import stubs.

### Locating the Dynamic Segment

The loader begins by identifying the `ProgramHeaderType.Dynamic` segment in the loaded image. In [`SelfLoader.cs`](https://github.com/par274/sharpemu/blob/main/SelfLoader.cs), the parser reads the dynamic tables—including the string table, symbol table, and RELA/JMPREL tables—to collect all relocations that require external symbol resolution.

### Extracting PlayStation NIDs

For each relocation entry that references an imported symbol, SharpEmu extracts the **NID** (Name Identifier) from the symbol name using the `ExtractNid` method. The system builds an ordered list of unique NIDs (`orderedImportNids`) to ensure each imported function receives exactly one stub allocation.

### Determining Stub Requirements

Not every NID automatically receives a stub. The `ShouldCreateImportStub` method evaluates each symbol against two criteria:

- **Non-weak relocations**: Always require a stub.
- **Weak relocations**: Only receive a stub if `IModuleManager.TryGetExport` can resolve the symbol to a host implementation.

This selective approach prevents unnecessary memory allocation for symbols that may never be invoked.

### Allocating Stub Memory

Approved stubs are allocated in a dedicated virtual address range starting at `ImportStubBaseAddress` (`0x0000_7000_0000_0000`). The memory layout uses a fixed stride of `ImportStubAddressStride` (`0x1000_00`) with each stub occupying `ImportStubSlotSize` (`0x10`) bytes. This sparse allocation scheme prevents accidental execution flow between stubs while maintaining predictable addresses.

### Writing Import Stub Bytes

Each stub consists of a minimal trap sequence: a single `int 3` instruction (`0xCC`) followed by a `ret` (`0xC3`). This `0xCC … 0xC3` pattern ensures that if guest code jumps to the stub before the host provides a real implementation, the emulator traps safely rather than executing garbage instructions. The stub addresses are stored in the `stubsByAddress` dictionary for fast lookup.

### Patching Relocations

The final phase patches the original ELF relocations to point at the newly created stubs. In `ComputeRelocationValue`, when processing a relocation that references an imported NID, the loader substitutes the stub address as the symbol value. The result is written back into guest memory via `TryWriteRelocationValue`. Special handling exists for `R_X86_64_TLS_MODID` relocations, which patch the TLS module identifier directly, while unsupported types like `R_X86_64_COPY` trigger exceptions indicating they require full runtime linker support.

## Key Source Files and Implementation Details

The import stub system spans several core files in the repository:

- **[`SelfLoader.cs`](https://github.com/par274/sharpemu/blob/main/SelfLoader.cs)** ([`src/SharpEmu.Core/Loader/SelfLoader.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Loader/SelfLoader.cs)): Contains the core loading logic, including dynamic section parsing, stub creation, and relocation patching.
- **[`ImportedSymbolRelocation.cs`](https://github.com/par274/sharpemu/blob/main/ImportedSymbolRelocation.cs)** ([`src/SharpEmu.Core/Loader/ImportedSymbolRelocation.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Loader/ImportedSymbolRelocation.cs)): Defines the data structure representing relocations that reference import stubs, tracking the relationship between guest addresses and NIDs.
- **[`WindowsHostSymbolResolver.cs`](https://github.com/par274/sharpemu/blob/main/WindowsHostSymbolResolver.cs)** ([`src/SharpEmu.HLE/Host/Windows/WindowsHostSymbolResolver.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.HLE/Host/Windows/WindowsHostSymbolResolver.cs)): Implements `IHostSymbolResolver` to map resolved NIDs to actual host function implementations via `IModuleManager`.
- **[`Aerolib.cs`](https://github.com/par274/sharpemu/blob/main/Aerolib.cs)** ([`src/SharpEmu.HLE/Aerolib.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.HLE/Aerolib.cs)): Provides the NID database (`Aerolib.Instance.GetAllNidNames()`) that translates raw numeric NIDs into human-readable function names during the stub creation process.

## Practical Example: Hooking NIDs at Runtime

The following example demonstrates loading a PlayStation binary and interacting with the import stub system:

```csharp
// Load an ELF image (simplified)
var image = File.ReadAllBytes("eboot.bin");
var vm    = new PhysicalVirtualMemory();
var loader = new SelfLoader();
SelfImage loaded = loader.Load(image, vm, moduleManager);

// Access the import stub mapping after loading
IReadOnlyDictionary<ulong, string> importStubs = loaded.ImportStubs;

// Find the stub address for a specific NID
ulong stubAddr = importStubs.First(kv => kv.Value == "sceKernelOpen").Key;
Console.WriteLine($"Stub for sceKernelOpen → 0x{stubAddr:X}");

// Register a host implementation to replace the stub
moduleManager.RegisterExport("sceKernelOpen", addr =>
{
    // Called when guest execution reaches the stub
    Console.WriteLine($"Guest called sceKernelOpen @ 0x{addr:X}");
    return 0; // Return success to guest
});

```

This pattern allows developers to implement PlayStation APIs incrementally, with unimplemented functions safely trapping at their stub addresses until host support is added.

## Summary

- SharpEmu creates **import stubs** in the virtual address range `0x0000_7000_0000_0000` to handle unresolved PlayStation NIDs.
- Each stub contains a **trap sequence** (`0xCC 0xC3`) that safely halts execution if called before the host provides an implementation.
- The `ShouldCreateImportStub` logic allocates stubs only for **non-weak relocations** or **resolvable weak symbols**.
- **Relocation patching** substitutes stub addresses for symbol values via `ComputeRelocationValue` and `TryWriteRelocationValue`.
- The system supports **TLS-specific relocations** (`R_X86_64_TLS_MODID`) while rejecting unsupported types like `R_X86_64_COPY`.

## Frequently Asked Questions

### What is a PlayStation NID?

A **NID** (Name Identifier) is a 64-bit hash used by PlayStation binaries to reference imported system functions. SharpEmu extracts these NIDs from ELF symbol names during the loading process, then uses them to look up human-readable names via the `Aerolib` database and to create corresponding import stubs.

### Why does SharpEmu use int 3 (0xCC) for import stubs?

The `int 3` instruction triggers a **deterministic trap** that the emulator can catch immediately. If guest code calls an unimplemented import, execution hits the `0xCC` byte first, allowing the host to intercept the call, log the missing implementation, or redirect to a debugger. The following `0xC3` (ret) provides a safe fallback boundary.

### How does the host resolve a stub to a real implementation?

The host implements `IHostSymbolResolver` and registers concrete functions through `IModuleManager.RegisterExport`. When the guest reaches a stub, the emulator checks `IModuleManager.TryGetExport` using the NID as a key. If a host implementation exists, the emulator redirects execution to the host function; otherwise, the trap remains active.

### What happens when a relocation type is unsupported?

Unsupported relocation types trigger **warnings** during the loading phase. For types that require complex runtime linker support—such as `R_X86_64_COPY`, which copies data between segments—the loader throws an exception indicating that the relocation cannot be resolved statically. This prevents silent memory corruption and alerts developers to implement the specific relocation handling in [`SelfLoader.cs`](https://github.com/par274/sharpemu/blob/main/SelfLoader.cs).