mirror of
https://github.com/par274/sharpemu.git
synced 2026-08-01 23:49:44 +08:00
initial commit
This commit is contained in:
@@ -0,0 +1,222 @@
|
||||
// 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();
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern void* VirtualAlloc(void* lpAddress, nuint dwSize, AllocationType flAllocationType, MemoryProtection flProtect);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
private static extern bool VirtualFree(void* lpAddress, nuint dwSize, FreeType 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);
|
||||
}
|
||||
Reference in New Issue
Block a user