Skip to content

loader: support ps5 SELF and validate ELF signatures#157

Merged
par274 merged 1 commit into
sharpemu:mainfrom
anesr5:self-elf-loader
Jul 14, 2026
Merged

loader: support ps5 SELF and validate ELF signatures#157
par274 merged 1 commit into
sharpemu:mainfrom
anesr5:self-elf-loader

Conversation

@anesr5

@anesr5 anesr5 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Add support for loading Prospero/PS5 SELF executables.

Reference

@Chelu97

Chelu97 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Tested this on my Demon's Souls dump (PPSA01341 v01.005) and it checks out.

On current main, loading eboot.bin dies with Input does not contain a valid ELF header because the PS5 magic 54 14 F5 EE isn't recognized. With 0x5414F5EE accepted (I tried an equivalent local patch), the eboot and the sce_module prx files (libc.prx, libSceNpCppWebApi.prx) all load fine: segments map through the SELF segment table, ~173k relocations get processed, 1173 import stubs resolve, and execution reaches libc.prx module init before crashing in the CPU backend — which is expected, that's separate work.

A few things I saw in that dump that back this change:

  • The embedded ELF sits at 0x1A0 = 0x20 + segment_count(12) * 0x20, exactly where ParseLayout computes it.
  • Ident bytes 4..11 (00 01 01 12 01 01 00 00) match the PS4 layout HasKnownLayout already validates — the 4-byte magic really is the only difference.
  • All 12 segments have compressed_size == decompressed_size and no encrypted (& 0x2) or compressed (& 0x8) flags, so the payload is plaintext and nothing here needs decryption.

While checking this I ended up writing a small xunit fixture suite for SelfLoader (synthetic in-memory PS5/PS4/raw-ELF images, encrypted-segment error path, ident validation). Happy to open it as a follow-up PR once this lands so these paths stay covered.

@Kaotic

Kaotic commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Tested this on my Demon's Souls dump (PPSA01341 v01.005) and it checks out.

On current main, loading eboot.bin dies with Input does not contain a valid ELF header because the PS5 magic 54 14 F5 EE isn't recognized. With 0x5414F5EE accepted (I tried an equivalent local patch), the eboot and the sce_module prx files (libc.prx, libSceNpCppWebApi.prx) all load fine: segments map through the SELF segment table, ~173k relocations get processed, 1173 import stubs resolve, and execution reaches libc.prx module init before crashing in the CPU backend — which is expected, that's separate work.

A few things I saw in that dump that back this change:

* The embedded ELF sits at `0x1A0` = `0x20 + segment_count(12) * 0x20`, exactly where `ParseLayout` computes it.

* Ident bytes 4..11 (`00 01 01 12 01 01 00 00`) match the PS4 layout `HasKnownLayout` already validates — the 4-byte magic really is the only difference.

* All 12 segments have `compressed_size == decompressed_size` and no encrypted (`& 0x2`) or compressed (`& 0x8`) flags, so the payload is plaintext and nothing here needs decryption.

While checking this I ended up writing a small xunit fixture suite for SelfLoader (synthetic in-memory PS5/PS4/raw-ELF images, encrypted-segment error path, ident validation). Happy to open it as a follow-up PR once this lands so these paths stay covered.

I have do that in #89 and for loading ELF files :

In SelfLoader -> ParseLayout function

uint magic = BinaryPrimitives.ReadUInt32LittleEndian(imageData);
if (magic == 0x464C457F) // ELF magic
{
    return new LoadContext(
        IsSelf: false,
        ElfOffset: 0,
        SelfFileSize: (ulong)imageData.Length,
        SelfSegments: Array.Empty<SelfSegment>()
    );
}

@anesr5

anesr5 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Tested this on my Demon's Souls dump (PPSA01341 v01.005) and it checks out.

On current main, loading eboot.bin dies with Input does not contain a valid ELF header because the PS5 magic 54 14 F5 EE isn't recognized. With 0x5414F5EE accepted (I tried an equivalent local patch), the eboot and the sce_module prx files (libc.prx, libSceNpCppWebApi.prx) all load fine: segments map through the SELF segment table, ~173k relocations get processed, 1173 import stubs resolve, and execution reaches libc.prx module init before crashing in the CPU backend — which is expected, that's separate work.

A few things I saw in that dump that back this change:

* The embedded ELF sits at `0x1A0` = `0x20 + segment_count(12) * 0x20`, exactly where `ParseLayout` computes it.

* Ident bytes 4..11 (`00 01 01 12 01 01 00 00`) match the PS4 layout `HasKnownLayout` already validates — the 4-byte magic really is the only difference.

* All 12 segments have `compressed_size == decompressed_size` and no encrypted (`& 0x2`) or compressed (`& 0x8`) flags, so the payload is plaintext and nothing here needs decryption.

While checking this I ended up writing a small xunit fixture suite for SelfLoader (synthetic in-memory PS5/PS4/raw-ELF images, encrypted-segment error path, ident validation). Happy to open it as a follow-up PR once this lands so these paths stay covered.

Thanks for testing this and for the details.

I hit the same issue on a Space Adventure Cobra dump (PPSA17337), which is what led me to this fix in the first place. Once the PS5 SELF magic was accepted, it started loading correctly.

@par274 par274 merged commit 2a9a261 into sharpemu:main Jul 14, 2026
4 checks passed
Aspenini added a commit to Aspenini/sharpemu that referenced this pull request Jul 15, 2026
Brings in 22 upstream commits, notably the host platform abstraction for
the execution engine (sharpemu#181), multiple render targets (sharpemu#149), the SELF/ELF
loader hardening (sharpemu#157), the SysAbi export corrections (sharpemu#167, sharpemu#189), and a
new SharpEmu.Libs.Tests project.

Conflicts resolved (upstream design taken as the source of truth):

- DirectExecutionBackend.cs: adopt upstream's IHostPlatform abstraction.
  Dropped the fork's dead CreateExceptionHandlerTrampoline plus its
  _virtualQueryAddress/GetProcAddress(kernel32) resolution; the trampoline
  is superseded by WindowsFaultHandling.CreateHandlerThunk, which is what
  the merged Exceptions.cs now installs. QueryPerformanceCounter now
  resolves through _hostSymbols. Kept both added usings.
  (Note: the fork's MEM_IMAGE native fault pre-classification is not carried
  over; upstream's fault path routes through the managed VectoredHandler.)

- Gen5SpirvTranslator.cs: take upstream's pixel-output export, which
  preserves the existing output value for a masked-off component (read via
  CompositeExtract) instead of writing zero, and uses the per-output
  Kind/Type/Variable model.

- VulkanVideoPresenter.cs: drop the fork's unused GetRenderTargetFormat;
  upstream's TryDecodeRenderTargetFormat/VulkanRenderTargetFormat supersede
  it. The guest-frame present-trace cadence tweak is retained.

- AgcExports.cs (auto-merged duplicate): keep upstream's sceAgcDcbJump
  (clean dcb/target/sizeDwords ABI) and remove the fork's probing variant
  plus its now-dead TryReadDcbJumpArguments/IsReadableDcbSegment helpers.
  The fork-only sceAgcDcbJumpGetSize and the indirect-buffer DCB recursion
  in ParseSubmittedDcb are retained.

Validation: dotnet restore --locked-mode, Release build (0/0), and
dotnet test (18/18) all pass; GTA V (PPSA04264) boots cleanly across
repeated runs with no regression.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants