# How SharpEmu Handles the proc_param Structure for Process Initialization

> Learn how SharpEmu handles the proc_param structure for process initialization. Discover its ELF parsing, kernel registration, and HLE export mechanisms.

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

---

**SharpEmu parses the `proc_param` structure from ELF program headers during image loading, registers it with the kernel compatibility layer, and exposes it to guest code via the `sceKernelGetProcParam` HLE export with a TLS fallback mechanism.**

The `proc_param` structure is essential for PS4/PS5 process initialization, containing command-line arguments, environment variables, and runtime metadata. In the `par274/sharpemu` codebase, handling this structure is split across the loader, runtime, and kernel compatibility layers to ensure accurate emulation of Sony's process parameter block.

## Parsing the proc_param Address from ELF Headers

When SharpEmu loads an executable image, it must first locate the `SCE_PROC_PARAM` program header within the ELF structure to determine where the process parameter block resides in memory.

### SelfLoader Resolution Logic

In [`src/SharpEmu.Core/Loader/SelfLoader.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Loader/SelfLoader.cs), the `ResolveProcParamAddress` method iterates through the program headers to find the entry with type `ProgramHeaderType.SceProcParam`. Upon discovery, it calculates the runtime virtual address by adding the image base to the header's virtual address, storing the result in `SelfImage.ProcParamAddress`.

```csharp
// 1️⃣ Resolve procparam address while loading an ELF image
ulong ResolveProcParamAddress(IReadOnlyList<ProgramHeader> headers, ulong imageBase)
{
    foreach (var header in headers)
    {
        if (header.HeaderType == ProgramHeaderType.SceProcParam)
            return imageBase + header.VirtualAddress;
    }
    return 0; // not present
}

```

This address is persisted in [`src/SharpEmu.Core/Loader/SelfImage.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Loader/SelfImage.cs) within the `ProcParamAddress` property, making it available for runtime registration.

## Registering the Process Parameter Block at Runtime

After the image is loaded, the runtime must communicate the `proc_param` location to the kernel compatibility layer before guest execution begins.

### Runtime Initialization Hook

During `SharpEmuRuntime.Start` (located in [`src/SharpEmu.Core/Runtime/SharpEmuRuntime.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Core/Runtime/SharpEmuRuntime.cs)), the system invokes `KernelRuntimeCompatExports.ConfigureProcessProcParamAddress` to pass the resolved address from the loaded image.

```csharp
// 2️⃣ Register the address with the kernel compatibility layer
public void Start()
{
    var image = LoadSelfImage(...);
    KernelRuntimeCompatExports.ConfigureProcessProcParamAddress(image.ProcParamAddress);
    // … other startup work …
}

```

This registration step ensures that the kernel compatibility layer knows where to find the process parameters when the guest later calls `sceKernelGetProcParam`.

## Kernel-Level Export and Fallback Mechanism

The kernel compatibility layer implements the high-level emulation (HLE) export that guest code uses to query the `proc_param` address at runtime.

### The sceKernelGetProcParam Export

In [`src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs), the `KernelGetProcParam` method is decorated with the `SysAbiExport` attribute targeting the `sceKernelGetProcParam` symbol (NID: `959qrazPIrg`). This export supports both PlayStation 4 (`Generation.Gen4`) and PlayStation 5 (`Generation.Gen5`) compatibility modes.

The implementation follows a strict priority order:
1. **Configured Address**: First checks the address supplied via `ConfigureProcessProcParamAddress`.
2. **TLS Scratch Slot**: If no address was configured, falls back to a dedicated TLS offset (`TlsProcParamOffset`).
3. **Zero-Fill**: When using the TLS slot, the method ensures a valid layout by zero-filling the block with `ctx.Memory.TryWrite(address, new byte[0x80])`.

### TLS Scratch Slot Fallback

The fallback mechanism guarantees that even executables without explicit `SCE_PROC_PARAM` headers receive a valid, zeroed parameter block. This prevents null pointer dereferences in guest code that expects the structure to exist.

```csharp
// 3️⃣ Guest call to retrieve the procparam address
[SysAbiExport(Nid = "959qrazPIrg", ExportName = "sceKernelGetProcParam",
    Target = Generation.Gen4 | Generation.Gen5, LibraryName = "libKernel")]
public static int KernelGetProcParam(CpuContext ctx)
{
    ulong address;
    lock (_stateGate) address = _processProcParamAddress;   // supplied by step 2
    if (address == 0) address = GetTlsScratchAddress(ctx, TlsProcParamOffset);
    if (address != 0 && address == GetTlsScratchAddress(ctx, TlsProcParamOffset))
        _ = ctx.Memory.TryWrite(address, new byte[0x80]);   // zero‑fill TLS slot

    TraceProcParam(ctx, address);   // optional debug dump
    ctx[CpuRegister.Rax] = address; // return to guest
    return (int)OrbisGen2Result.ORBIS_GEN2_OK;
}

```

The resolved address is returned to the guest in the **RAX** register (`ctx[CpuRegister.Rax] = address`), following the AMD64/System V ABI calling convention used by the PS4/PS5.

## Debugging and Diagnostic Output

SharpEmu provides extensive diagnostics for troubleshooting `proc_param` issues. When the environment variable `SHARPEMU_LOG_PROC_PARAM` is set, the emulator dumps up to 0x200 bytes of the parameter block in hexadecimal format. Additionally, setting `SHARPEMU_LOG_PROC_PARAM_PTRS` enables pointer chasing, where the tracer follows embedded pointers to display ASCII, UTF-16, or raw hex representations of referenced strings.

This debug output is handled by the `TraceProcParam` method within `KernelRuntimeCompatExports`, allowing developers to verify that command-line arguments and environment variables are correctly positioned within the emulated address space.

## Summary

- **ELF Parsing**: `SelfLoader.ResolveProcParamAddress` extracts the `proc_param` location from the `SCE_PROC_PARAM` program header during image loading.
- **Runtime Registration**: `SharpEmuRuntime.Start` passes the address to `KernelRuntimeCompatExports.ConfigureProcessProcParamAddress` before guest execution.
- **HLE Export**: `KernelRuntimeCompatExports.KernelGetProcParam` implements the `sceKernelGetProcParam` syscall, returning the address in RAX.
- **Fallback Safety**: If no address is configured, the system uses a zero-filled TLS scratch slot at `TlsProcParamOffset` to ensure valid guest memory.
- **Diagnostics**: Environment variables `SHARPEMU_LOG_PROC_PARAM` and `SHARPEMU_LOG_PROC_PARAM_PTRS` enable hex dumps and pointer tracing for debugging.

## Frequently Asked Questions

### What is the proc_param structure in SharpEmu?

The `proc_param` structure is a Sony-defined data block that contains process-specific information such as command-line arguments (`argc`/`argv`), environment variables, and application metadata. SharpEmu exposes this structure to emulated PS4/PS5 guests to ensure compatibility with binaries that expect standard PlayStation process initialization behavior.

### How does SharpEmu handle binaries without a SCE_PROC_PARAM header?

If the loaded ELF lacks a `SCE_PROC_PARAM` program header, the `ResolveProcParamAddress` method returns 0. When the guest calls `sceKernelGetProcParam`, the kernel compatibility layer detects the missing address and falls back to a TLS scratch slot, zero-filling 0x80 bytes to provide a safe, empty parameter block rather than returning a null pointer.

### Where is the proc_param address returned to the guest code?

According to the `KernelRuntimeCompatExports` implementation in [`src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs`](https://github.com/par274/sharpemu/blob/main/src/SharpEmu.Libs/Kernel/KernelRuntimeCompatExports.cs), the `proc_param` address is placed in the **RAX** register (`ctx[CpuRegister.Rax] = address`) before returning from the `sceKernelGetProcParam` export. This follows the standard AMD64 calling convention where integer return values are passed in RAX.

### Can I inspect the proc_param contents during emulation?

Yes. SharpEmu supports runtime inspection of the `proc_param` block through environment variables. Setting `SHARPEMU_LOG_PROC_PARAM` enables a hex dump of the structure, while `SHARPEMU_LOG_PROC_PARAM_PTRS` follows internal pointers to decode strings. These diagnostics are handled by the `TraceProcParam` method and print to the emulator's log output.