How SharpEmu Handles the proc_param Structure for Process Initialization
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, 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.
// 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 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), the system invokes KernelRuntimeCompatExports.ConfigureProcessProcParamAddress to pass the resolved address from the loaded image.
// 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, 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:
- Configured Address: First checks the address supplied via
ConfigureProcessProcParamAddress. - TLS Scratch Slot: If no address was configured, falls back to a dedicated TLS offset (
TlsProcParamOffset). - 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.
// 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.ResolveProcParamAddressextracts theproc_paramlocation from theSCE_PROC_PARAMprogram header during image loading. - Runtime Registration:
SharpEmuRuntime.Startpasses the address toKernelRuntimeCompatExports.ConfigureProcessProcParamAddressbefore guest execution. - HLE Export:
KernelRuntimeCompatExports.KernelGetProcParamimplements thesceKernelGetProcParamsyscall, returning the address in RAX. - Fallback Safety: If no address is configured, the system uses a zero-filled TLS scratch slot at
TlsProcParamOffsetto ensure valid guest memory. - Diagnostics: Environment variables
SHARPEMU_LOG_PROC_PARAMandSHARPEMU_LOG_PROC_PARAM_PTRSenable 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, 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.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →