Files
sharpemu/src/SharpEmu.Core/Cpu/Native/StubManager.cs
T
Miguel Cruz 864cbb0fa0 [AGC/Vulkan] Extend PS5 runtime and rendering compatibility (#216)
* [Core] Add POSIX native execution and PS5 SELF support

Extend the native backend, guest TLS, fixed-address memory, and loader paths needed by PS5 titles on Windows, Linux, and macOS. Keep workstation GC so high-core-count hosts do not reserve over fixed guest image bases.

* [HLE] Expand PS5 service and media compatibility

Add the kernel, threading, save-data, networking, audio, video-codec, font, dialog, and service exports required by newer PS5 software. Preserve every SysAbi NID currently registered by main while adding the compatibility surface used by ASTRO BOT.

* [AGC/Vulkan] Extend Gen5 shader and presentation support

Expand PM4 handling, Gen5 shader translation, MRT and packed export support, guest image tracking, depth initialization, texture aliasing, and Vulkan presentation. Add the performance overlay and address-filtered diagnostics used to validate ASTRO BOT with original shaders.

* [Core] Align static TLS reservation across hosts

* [Pad] Align primary user ID with UserService

* [Gpu] Preserve runtime scalar buffers across renderer seam

* [AGC] Restore omitted command helper exports

* [Vulkan] Reuse primary views for promoted MRT targets

* [Vulkan] Preserve scratch storage bindings in compute dispatches
2026-07-16 02:02:34 +03:00

223 lines
6.4 KiB
C#

// Copyright (C) 2026 SharpEmu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
using System.Runtime.InteropServices;
using SharpEmu.HLE;
namespace SharpEmu.Core.Cpu.Native;
public sealed unsafe class StubManager : IDisposable
{
private readonly List<nint> _allocatedStubs = new();
private readonly Dictionary<string, nint> _importHandlers = new();
private readonly Dictionary<ulong, nint> _stubAddresses = new();
private byte* _pltMemory;
private int _pltOffset;
private const int PltMemorySize = 1024 * 1024; // 1MB for stubs
public StubManager()
{
_pltMemory = (byte*)VirtualAlloc(
null,
(nuint)PltMemorySize,
AllocationType.Reserve | AllocationType.Commit,
MemoryProtection.ExecuteReadWrite);
if (_pltMemory == null)
{
throw new OutOfMemoryException("Failed to allocate executable memory for stubs");
}
_pltOffset = 0;
}
public nint CreateImportStub(ulong guestAddress, string nid, ImportHandler handler)
{
if (!_importHandlers.TryGetValue(nid, out var handlerPtr))
{
handlerPtr = CreateHandlerTrampoline(nid, handler);
_importHandlers[nid] = handlerPtr;
}
var stubPtr = _pltMemory + _pltOffset;
var stubIndex = (uint)(_stubAddresses.Count);
JitStubs.CreateJmpWithIndex(stubPtr, stubIndex, (void*)handlerPtr);
_pltOffset += JitStubs.JmpWithIndex.Size;
_stubAddresses[guestAddress] = (nint)stubPtr;
return (nint)stubPtr;
}
public nint CreatePltEntry(uint index, nint gotAddress)
{
var pltPtr = _pltMemory + _pltOffset;
var pltCode = new byte[]
{
0x49, 0xBB, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs r11,<addr>
0x41, 0xFF, 0x73, 0x08, // push QWORD PTR [r11+8]
0x41, 0xFF, 0x63, 0x10, // jmp QWORD PTR [r11+16]
0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 // nop padding
};
*(ulong*)(pltCode.AsSpan().Slice(2).GetPinnableReference()) = (ulong)gotAddress;
fixed (byte* src = pltCode)
{
Buffer.MemoryCopy(src, pltPtr, pltCode.Length, pltCode.Length);
}
_pltOffset += pltCode.Length;
return (nint)pltPtr;
}
public nint CreateTlsHandler(delegate*<void> tlsGetAddrFunc)
{
var handlerPtr = _pltMemory + _pltOffset;
JitStubs.CreateCall9(handlerPtr, (void*)tlsGetAddrFunc);
_pltOffset += JitStubs.Call9.Size;
return (nint)handlerPtr;
}
public nint CreateSafeCallTrampoline(delegate*<void> func, byte* regSaveArea, byte* lockVar)
{
var trampolinePtr = _pltMemory + _pltOffset;
var template = JitStubs.SafeCall.Template;
fixed (byte* src = template)
{
Buffer.MemoryCopy(src, trampolinePtr, template.Length, template.Length);
}
*(ulong*)(trampolinePtr + 0x0c + 2) = (ulong)lockVar;
*(ulong*)(trampolinePtr + 0x16 + 2) = (ulong)regSaveArea;
*(ulong*)(trampolinePtr + 0x20 + 2) = (ulong)func;
_pltOffset += template.Length;
return (nint)trampolinePtr;
}
public bool TryGetStubAddress(ulong guestAddress, out nint hostAddress)
{
return _stubAddresses.TryGetValue(guestAddress, out hostAddress);
}
public bool PatchTlsAccess(byte* guestCode, int codeSize, nint tlsHandler)
{
var locations = JitStubs.FindTlsAccessPatterns(guestCode, codeSize);
foreach (var location in locations)
{
JitStubs.CreateCall9((byte*)location, (void*)tlsHandler);
}
return locations.Count > 0;
}
private nint CreateHandlerTrampoline(string nid, ImportHandler handler)
{
var trampolinePtr = _pltMemory + _pltOffset;
var code = new List<byte>();
code.Add(0x57);
code.Add(0x56);
code.Add(0x55);
code.Add(0x53);
code.AddRange(new byte[] { 0x41, 0x54 });
code.AddRange(new byte[] { 0x41, 0x55 });
code.AddRange(new byte[] { 0x41, 0x56 });
code.AddRange(new byte[] { 0x41, 0x57 });
code.AddRange(new byte[] { 0x48, 0x83, 0xEC, 0x28 });
int contextOffset = code.Count;
code.AddRange(new byte[] { 0x48, 0xB9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
int handlerOffset = code.Count;
code.AddRange(new byte[] { 0x48, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 });
code.AddRange(new byte[] { 0xFF, 0xD0 });
code.AddRange(new byte[] { 0x48, 0x83, 0xC4, 0x28 });
code.AddRange(new byte[] { 0x41, 0x5F });
code.AddRange(new byte[] { 0x41, 0x5E });
code.AddRange(new byte[] { 0x41, 0x5D });
code.AddRange(new byte[] { 0x41, 0x5C });
code.Add(0x5B);
code.Add(0x5D);
code.Add(0x5E);
code.Add(0x5F);
code.Add(0xC3);
var codeArray = code.ToArray();
fixed (byte* src = codeArray)
{
Buffer.MemoryCopy(src, trampolinePtr, codeArray.Length, codeArray.Length);
}
*(ulong*)(trampolinePtr + contextOffset + 2) = 0;
var handlerPtr = Marshal.GetFunctionPointerForDelegate(handler);
*(ulong*)(trampolinePtr + handlerOffset + 2) = (ulong)handlerPtr;
_pltOffset += codeArray.Length;
_allocatedStubs.Add((nint)trampolinePtr);
return (nint)trampolinePtr;
}
public void Dispose()
{
if (_pltMemory != null)
{
VirtualFree(_pltMemory, 0, FreeType.Release);
_pltMemory = null;
}
_allocatedStubs.Clear();
_importHandlers.Clear();
_stubAddresses.Clear();
}
private static void* VirtualAlloc(void* lpAddress, nuint dwSize, AllocationType flAllocationType, MemoryProtection flProtect) =>
HostMemory.Alloc(lpAddress, dwSize, (uint)flAllocationType, (uint)flProtect);
private static bool VirtualFree(void* lpAddress, nuint dwSize, FreeType dwFreeType) =>
HostMemory.Free(lpAddress, dwSize, (uint)dwFreeType);
[Flags]
private enum AllocationType : uint
{
Commit = 0x1000,
Reserve = 0x2000,
}
[Flags]
private enum MemoryProtection : uint
{
ExecuteReadWrite = 0x40,
}
private enum FreeType : uint
{
Release = 0x8000,
}
public delegate void ImportHandler(CpuContext context);
}