mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-24 11:48:39 +08:00
[fiber] synchronization problems have been fixed for such a titles: Demon's Souls
[ampr] new exports [memory] trampoline fixes
This commit is contained in:
@@ -19,7 +19,12 @@ public sealed partial class DirectExecutionBackend
|
||||
{
|
||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_DISABLE_RAW_HANDLER"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
_rawExceptionHandler = (nint)AddVectoredExceptionHandler(1u, RawVectoredHandlerPtrManaged);
|
||||
_rawExceptionHandlerStub = CreateExceptionHandlerTrampoline(RawVectoredHandlerPtrManaged);
|
||||
if (_rawExceptionHandlerStub == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create raw exception handler trampoline");
|
||||
}
|
||||
_rawExceptionHandler = (nint)AddVectoredExceptionHandler(1u, _rawExceptionHandlerStub);
|
||||
Console.Error.WriteLine($"[LOADER][INFO] Raw exception handler installed: 0x{_rawExceptionHandler:X16}");
|
||||
}
|
||||
else
|
||||
@@ -29,12 +34,22 @@ public sealed partial class DirectExecutionBackend
|
||||
|
||||
_handlerDelegate = VectoredHandler;
|
||||
_handlerHandle = GCHandle.Alloc(_handlerDelegate);
|
||||
_exceptionHandler = (nint)AddVectoredExceptionHandler(1u, Marshal.GetFunctionPointerForDelegate(_handlerDelegate));
|
||||
_exceptionHandlerStub = CreateExceptionHandlerTrampoline(Marshal.GetFunctionPointerForDelegate(_handlerDelegate));
|
||||
if (_exceptionHandlerStub == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create exception handler trampoline");
|
||||
}
|
||||
_exceptionHandler = (nint)AddVectoredExceptionHandler(1u, _exceptionHandlerStub);
|
||||
Console.Error.WriteLine($"[LOADER][INFO] Exception handler installed: 0x{_exceptionHandler:X16}");
|
||||
|
||||
_unhandledFilterDelegate = UnhandledExceptionFilter;
|
||||
_unhandledFilterHandle = GCHandle.Alloc(_unhandledFilterDelegate);
|
||||
SetUnhandledExceptionFilter(Marshal.GetFunctionPointerForDelegate(_unhandledFilterDelegate));
|
||||
_unhandledFilterStub = CreateExceptionHandlerTrampoline(Marshal.GetFunctionPointerForDelegate(_unhandledFilterDelegate));
|
||||
if (_unhandledFilterStub == 0)
|
||||
{
|
||||
throw new InvalidOperationException("Failed to create unhandled exception filter trampoline");
|
||||
}
|
||||
SetUnhandledExceptionFilter(_unhandledFilterStub);
|
||||
}
|
||||
|
||||
private unsafe int UnhandledExceptionFilter(void* exceptionInfo)
|
||||
|
||||
@@ -143,6 +143,8 @@ public sealed partial class DirectExecutionBackend
|
||||
ProbeReturnRip(num7, num);
|
||||
}
|
||||
TrackStrlenPrelude(importStubEntry.Nid, num, num7);
|
||||
TraceGuestContext(
|
||||
$"import dispatch={num} nid={importStubEntry.Nid} ret=0x{num7:X16} managed={Environment.CurrentManagedThreadId} guest=0x{GuestThreadExecution.CurrentGuestThreadHandle:X16} fiber=0x{GuestThreadExecution.CurrentFiberAddress:X16} active={HasActiveExecutionThread}");
|
||||
bool logBootstrap = string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_BOOTSTRAP"), "1", StringComparison.Ordinal);
|
||||
if (logBootstrap && string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
|
||||
{
|
||||
@@ -279,13 +281,15 @@ public sealed partial class DirectExecutionBackend
|
||||
catch
|
||||
{
|
||||
}
|
||||
TryBypassStackChkFailTrap(num, num7);
|
||||
}
|
||||
try
|
||||
{
|
||||
OrbisGen2Result orbisGen2Result;
|
||||
bool dispatchResolved = true;
|
||||
var previousImportCallFrame = GuestThreadExecution.EnterImportCallFrame(num7, (ulong)argPackPtr + 104uL);
|
||||
var previousImportCallFrame = GuestThreadExecution.EnterImportCallFrame(
|
||||
num7,
|
||||
(ulong)argPackPtr + 104uL,
|
||||
ActiveGuestReturnSlotAddress);
|
||||
try
|
||||
{
|
||||
if (string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
|
||||
@@ -408,7 +412,7 @@ public sealed partial class DirectExecutionBackend
|
||||
private unsafe bool TryForceGuestExitToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid)
|
||||
{
|
||||
ulong num = ActiveEntryReturnSentinelRip;
|
||||
if (num < 65536)
|
||||
if (num < 65536 || !TryPatchActiveGuestReturnSlot(num))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -430,7 +434,7 @@ public sealed partial class DirectExecutionBackend
|
||||
private unsafe bool TryCompleteGuestEntryToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid, string reason, int status)
|
||||
{
|
||||
ulong hostExit = ActiveEntryReturnSentinelRip;
|
||||
if (hostExit < 65536)
|
||||
if (hostExit < 65536 || !TryPatchActiveGuestReturnSlot(hostExit))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -450,7 +454,7 @@ public sealed partial class DirectExecutionBackend
|
||||
private unsafe bool TryYieldGuestThreadToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid, string reason)
|
||||
{
|
||||
ulong hostExit = ActiveEntryReturnSentinelRip;
|
||||
if (hostExit < 65536)
|
||||
if (hostExit < 65536 || !TryPatchActiveGuestReturnSlot(hostExit))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -470,6 +474,14 @@ public sealed partial class DirectExecutionBackend
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryPatchActiveGuestReturnSlot(ulong hostExit)
|
||||
{
|
||||
ulong returnSlotAddress = ActiveGuestReturnSlotAddress;
|
||||
return returnSlotAddress != 0 &&
|
||||
ActiveCpuContext is not null &&
|
||||
ActiveCpuContext.TryWriteUInt64(returnSlotAddress, hostExit);
|
||||
}
|
||||
|
||||
private bool ShouldForceGuestExitOnImportLoop(string nid, ulong returnRip, long dispatchIndex, ulong arg0, ulong arg1)
|
||||
{
|
||||
if (dispatchIndex < 1200)
|
||||
@@ -480,6 +492,11 @@ public sealed partial class DirectExecutionBackend
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (IsImportLoopGuardBoundary(nid))
|
||||
{
|
||||
ResetImportLoopPattern();
|
||||
return false;
|
||||
}
|
||||
if (!_importNidHashCache.TryGetValue(nid, out var value))
|
||||
{
|
||||
value = StableHash64(nid);
|
||||
@@ -513,6 +530,17 @@ public sealed partial class DirectExecutionBackend
|
||||
return elapsedTicks >= (long)(guardSeconds * Stopwatch.Frequency);
|
||||
}
|
||||
|
||||
private static bool IsImportLoopGuardBoundary(string nid) =>
|
||||
string.Equals(nid, "1jfXLRVzisc", StringComparison.Ordinal);
|
||||
|
||||
private void ResetImportLoopPattern()
|
||||
{
|
||||
_importLoopPatternHits = 0;
|
||||
_importLoopPatternStartTimestamp = 0;
|
||||
_importLoopSignatureCount = 0;
|
||||
_importLoopSignatureWriteIndex = 0;
|
||||
}
|
||||
|
||||
private static int GetImportLoopGuardSeconds()
|
||||
{
|
||||
if (int.TryParse(Environment.GetEnvironmentVariable("SHARPEMU_IMPORT_LOOP_GUARD_SECONDS"), out var seconds))
|
||||
@@ -923,66 +951,6 @@ public sealed partial class DirectExecutionBackend
|
||||
}
|
||||
}
|
||||
|
||||
private void TryBypassStackChkFailTrap(long dispatchIndex, ulong returnRip)
|
||||
{
|
||||
var cpuContext = ActiveCpuContext;
|
||||
if (cpuContext == null || returnRip < 32)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
byte[] array = new byte[19];
|
||||
ulong num = returnRip - 23;
|
||||
Marshal.Copy((nint)num, array, 0, array.Length);
|
||||
if (array[0] != 117 || array[1] != 16 || array[2] != 72 || array[3] != 137 || array[4] != 216 || array[5] != 72 || array[6] != 131 || array[7] != 196 || array[9] != 91 || array[10] != 65 || array[11] != 92 || array[12] != 65 || array[13] != 94 || array[14] != 65 || array[15] != 95 || array[16] != 93 || array[17] != 195 || array[18] != 232)
|
||||
{
|
||||
return;
|
||||
}
|
||||
ulong value = returnRip - 21;
|
||||
ulong address = cpuContext[CpuRegister.Rsp];
|
||||
if (cpuContext.TryWriteUInt64(address, value))
|
||||
{
|
||||
if (_stackChkBypassSites.Add(num) && TryPatchStackChkFailBranch(num))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][WARNING] Import#{dispatchIndex}: patched stack_chk_fail tail branch at 0x{num:X16} -> NOP NOP");
|
||||
}
|
||||
Console.Error.WriteLine($"[LOADER][WARNING] Import#{dispatchIndex}: redirected __stack_chk_fail return to epilogue 0x{value:X16}");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe static bool TryPatchStackChkFailBranch(ulong branchAddress)
|
||||
{
|
||||
uint flNewProtect = default(uint);
|
||||
if (!VirtualProtect((void*)branchAddress, 2u, 64u, &flNewProtect))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
if (Marshal.ReadByte((nint)branchAddress) != 117)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Marshal.WriteByte((nint)branchAddress, 144);
|
||||
Marshal.WriteByte((nint)(branchAddress + 1), 144);
|
||||
FlushInstructionCache(GetCurrentProcess(), (void*)branchAddress, 2u);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
VirtualProtect((void*)branchAddress, 2u, flNewProtect, &flNewProtect);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe void TryPatchEa020eLookupCall(long dispatchIndex, ulong returnRip)
|
||||
{
|
||||
if (_patchedEa020eLookupCall || returnRip != 0x0000000800EA01A6uL)
|
||||
|
||||
@@ -136,10 +136,18 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
private nint _unresolvedReturnStub;
|
||||
|
||||
private nint _guestReturnStub;
|
||||
|
||||
private nint _rawExceptionHandler;
|
||||
|
||||
private nint _rawExceptionHandlerStub;
|
||||
|
||||
private nint _exceptionHandler;
|
||||
|
||||
private nint _exceptionHandlerStub;
|
||||
|
||||
private nint _unhandledFilterStub;
|
||||
|
||||
private nint _lowIndexedTableScratch;
|
||||
|
||||
private nint _stackGuardCompareScratch;
|
||||
@@ -161,6 +169,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
[ThreadStatic]
|
||||
private static ulong _activeEntryReturnSentinelRip;
|
||||
|
||||
[ThreadStatic]
|
||||
private static ulong _activeGuestReturnSlotAddress;
|
||||
|
||||
[ThreadStatic]
|
||||
private static bool _activeForcedGuestExit;
|
||||
|
||||
@@ -202,8 +213,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
|
||||
private readonly HashSet<ulong> _fallbackTrapStubs = new HashSet<ulong>();
|
||||
|
||||
private readonly HashSet<ulong> _stackChkBypassSites = new HashSet<ulong>();
|
||||
|
||||
private readonly HashSet<ulong> _patchedResolverReturnSites = new HashSet<ulong>();
|
||||
|
||||
private readonly HashSet<ulong> _patchedTlsImmediateThunkTargets = new HashSet<ulong>();
|
||||
@@ -539,6 +548,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
}
|
||||
}
|
||||
|
||||
private ulong ActiveGuestReturnSlotAddress =>
|
||||
HasActiveExecutionThread ? _activeGuestReturnSlotAddress : 0;
|
||||
|
||||
private bool ActiveForcedGuestExit
|
||||
{
|
||||
get => HasActiveExecutionThread ? _activeForcedGuestExit : _forcedGuestExit;
|
||||
@@ -591,6 +603,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
DirectExecutionBackend? previousBackend,
|
||||
CpuContext? previousContext,
|
||||
ulong previousSentinel,
|
||||
ulong previousReturnSlotAddress,
|
||||
bool previousForcedExit,
|
||||
bool previousYieldRequested,
|
||||
string? previousYieldReason)
|
||||
@@ -598,6 +611,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
_activeExecutionBackend = previousBackend;
|
||||
_activeCpuContext = previousContext;
|
||||
_activeEntryReturnSentinelRip = previousSentinel;
|
||||
_activeGuestReturnSlotAddress = previousReturnSlotAddress;
|
||||
_activeForcedGuestExit = previousForcedExit;
|
||||
_activeGuestThreadYieldRequested = previousYieldRequested;
|
||||
_activeGuestThreadYieldReason = previousYieldReason;
|
||||
@@ -634,6 +648,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
throw new OutOfMemoryException("Failed to allocate host stack slot storage");
|
||||
}
|
||||
_unresolvedReturnStub = CreateUnresolvedReturnStub();
|
||||
_guestReturnStub = CreateGuestReturnStub();
|
||||
if (_guestReturnStub == 0)
|
||||
{
|
||||
throw new OutOfMemoryException("Failed to allocate guest return stub");
|
||||
}
|
||||
SetupExceptionHandler();
|
||||
}
|
||||
|
||||
@@ -930,9 +949,24 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
yield return "_" + exportName;
|
||||
}
|
||||
|
||||
private static bool IsDirectImportTargetUsable(ulong address)
|
||||
private bool IsDirectImportTargetUsable(ulong address)
|
||||
{
|
||||
return address >= 65536 && !IsUnresolvedSentinel(address);
|
||||
if (address < 65536 || IsUnresolvedSentinel(address) ||
|
||||
_cpuContext is null || !TryGetVirtualMemory(_cpuContext, out var virtualMemory))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var region in virtualMemory.SnapshotRegions())
|
||||
{
|
||||
if ((region.Protection & ProgramHeaderFlags.Execute) != 0 &&
|
||||
ContainsAddress(region.VirtualAddress, region.MemorySize, address))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private unsafe void BindTlsBase(CpuContext context)
|
||||
@@ -1243,6 +1277,145 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
return (nint)ptr;
|
||||
}
|
||||
|
||||
private unsafe nint CreateGuestReturnStub()
|
||||
{
|
||||
const uint stubSize = 256u;
|
||||
void* ptr = VirtualAlloc(null, stubSize, 12288u, 64u);
|
||||
if (ptr == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte* code = (byte*)ptr;
|
||||
int offset = 0;
|
||||
EmitByte(code, ref offset, 0x48); // sub rsp, 0x20
|
||||
EmitByte(code, ref offset, 0x83);
|
||||
EmitByte(code, ref offset, 0xEC);
|
||||
EmitByte(code, ref offset, 0x20);
|
||||
EmitByte(code, ref offset, 0xB9); // mov ecx, tlsIndex
|
||||
EmitUInt32(code, ref offset, _hostRspSlotTlsIndex);
|
||||
EmitByte(code, ref offset, 0x48); // mov rax, TlsGetValue
|
||||
EmitByte(code, ref offset, 0xB8);
|
||||
*(long*)(code + offset) = _tlsGetValueAddress;
|
||||
offset += sizeof(ulong);
|
||||
EmitByte(code, ref offset, 0xFF); // call rax
|
||||
EmitByte(code, ref offset, 0xD0);
|
||||
EmitByte(code, ref offset, 0x48); // add rsp, 0x20
|
||||
EmitByte(code, ref offset, 0x83);
|
||||
EmitByte(code, ref offset, 0xC4);
|
||||
EmitByte(code, ref offset, 0x20);
|
||||
EmitByte(code, ref offset, 0x48); // mov rsp, [rax]
|
||||
EmitByte(code, ref offset, 0x8B);
|
||||
EmitByte(code, ref offset, 0x20);
|
||||
EmitHostNonvolatileXmmRestore(code, ref offset);
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x5F);
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x5E);
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x5D);
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x5C);
|
||||
EmitByte(code, ref offset, 0x5E);
|
||||
EmitByte(code, ref offset, 0x5F);
|
||||
EmitByte(code, ref offset, 0x5D);
|
||||
EmitByte(code, ref offset, 0x5B);
|
||||
EmitByte(code, ref offset, 0xC3);
|
||||
|
||||
uint oldProtect = default;
|
||||
VirtualProtect(ptr, stubSize, 32u, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, (nuint)offset);
|
||||
return (nint)ptr;
|
||||
}
|
||||
|
||||
private unsafe nint CreateExceptionHandlerTrampoline(nint managedHandler)
|
||||
{
|
||||
const uint stubSize = 256u;
|
||||
void* ptr = VirtualAlloc(null, stubSize, 12288u, 64u);
|
||||
if (ptr == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
byte* code = (byte*)ptr;
|
||||
int offset = 0;
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x54); // push r12
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x55); // push r13
|
||||
EmitByte(code, ref offset, 0x49); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xE4); // mov r12, rsp
|
||||
EmitByte(code, ref offset, 0x49); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xCD); // mov r13, rcx
|
||||
EmitByte(code, ref offset, 0x65); EmitByte(code, ref offset, 0x48); // mov rax, gs:[8]
|
||||
EmitByte(code, ref offset, 0x8B); EmitByte(code, ref offset, 0x04); EmitByte(code, ref offset, 0x25);
|
||||
EmitUInt32(code, ref offset, 8u);
|
||||
EmitByte(code, ref offset, 0x49); EmitByte(code, ref offset, 0x39); EmitByte(code, ref offset, 0xC4); // cmp r12, rax
|
||||
EmitByte(code, ref offset, 0x0F); EmitByte(code, ref offset, 0x83); // jae guestStack
|
||||
int aboveStackJump = offset;
|
||||
EmitUInt32(code, ref offset, 0u);
|
||||
EmitByte(code, ref offset, 0x65); EmitByte(code, ref offset, 0x48); // mov rax, gs:[0x10]
|
||||
EmitByte(code, ref offset, 0x8B); EmitByte(code, ref offset, 0x04); EmitByte(code, ref offset, 0x25);
|
||||
EmitUInt32(code, ref offset, 0x10u);
|
||||
EmitByte(code, ref offset, 0x49); EmitByte(code, ref offset, 0x39); EmitByte(code, ref offset, 0xC4); // cmp r12, rax
|
||||
EmitByte(code, ref offset, 0x0F); EmitByte(code, ref offset, 0x82); // jb guestStack
|
||||
int belowStackJump = offset;
|
||||
EmitUInt32(code, ref offset, 0u);
|
||||
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x83); EmitByte(code, ref offset, 0xEC); EmitByte(code, ref offset, 0x28);
|
||||
EmitByte(code, ref offset, 0x4C); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xE9); // mov rcx, r13
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0xB8);
|
||||
*(nint*)(code + offset) = managedHandler;
|
||||
offset += sizeof(nint);
|
||||
EmitByte(code, ref offset, 0xFF); EmitByte(code, ref offset, 0xD0);
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x83); EmitByte(code, ref offset, 0xC4); EmitByte(code, ref offset, 0x28);
|
||||
EmitByte(code, ref offset, 0xE9);
|
||||
int hostRestoreJump = offset;
|
||||
EmitUInt32(code, ref offset, 0u);
|
||||
|
||||
int guestStackOffset = offset;
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x83); EmitByte(code, ref offset, 0xEC); EmitByte(code, ref offset, 0x28);
|
||||
EmitByte(code, ref offset, 0xB9);
|
||||
EmitUInt32(code, ref offset, _hostRspSlotTlsIndex);
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0xB8);
|
||||
*(nint*)(code + offset) = _tlsGetValueAddress;
|
||||
offset += sizeof(nint);
|
||||
EmitByte(code, ref offset, 0xFF); EmitByte(code, ref offset, 0xD0);
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x83); EmitByte(code, ref offset, 0xC4); EmitByte(code, ref offset, 0x28);
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x85); EmitByte(code, ref offset, 0xC0); // test rax, rax
|
||||
EmitByte(code, ref offset, 0x0F); EmitByte(code, ref offset, 0x84);
|
||||
int missingTlsJump = offset;
|
||||
EmitUInt32(code, ref offset, 0u);
|
||||
EmitByte(code, ref offset, 0x4C); EmitByte(code, ref offset, 0x8B); EmitByte(code, ref offset, 0x18); // mov r11, [rax]
|
||||
EmitByte(code, ref offset, 0x4D); EmitByte(code, ref offset, 0x85); EmitByte(code, ref offset, 0xDB); // test r11, r11
|
||||
EmitByte(code, ref offset, 0x0F); EmitByte(code, ref offset, 0x84);
|
||||
int missingHostStackJump = offset;
|
||||
EmitUInt32(code, ref offset, 0u);
|
||||
EmitByte(code, ref offset, 0x4C); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xDC); // mov rsp, r11
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x83); EmitByte(code, ref offset, 0xEC); EmitByte(code, ref offset, 0x28);
|
||||
EmitByte(code, ref offset, 0x4C); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xE9); // mov rcx, r13
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0xB8);
|
||||
*(nint*)(code + offset) = managedHandler;
|
||||
offset += sizeof(nint);
|
||||
EmitByte(code, ref offset, 0xFF); EmitByte(code, ref offset, 0xD0);
|
||||
EmitByte(code, ref offset, 0x48); EmitByte(code, ref offset, 0x83); EmitByte(code, ref offset, 0xC4); EmitByte(code, ref offset, 0x28);
|
||||
EmitByte(code, ref offset, 0xE9);
|
||||
int guestRestoreJump = offset;
|
||||
EmitUInt32(code, ref offset, 0u);
|
||||
|
||||
int passThroughOffset = offset;
|
||||
EmitByte(code, ref offset, 0x31); EmitByte(code, ref offset, 0xC0); // xor eax, eax
|
||||
int restoreOffset = offset;
|
||||
EmitByte(code, ref offset, 0x4C); EmitByte(code, ref offset, 0x89); EmitByte(code, ref offset, 0xE4); // mov rsp, r12
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x5D);
|
||||
EmitByte(code, ref offset, 0x41); EmitByte(code, ref offset, 0x5C);
|
||||
EmitByte(code, ref offset, 0xC3);
|
||||
|
||||
*(int*)(code + aboveStackJump) = guestStackOffset - (aboveStackJump + sizeof(int));
|
||||
*(int*)(code + belowStackJump) = guestStackOffset - (belowStackJump + sizeof(int));
|
||||
*(int*)(code + hostRestoreJump) = restoreOffset - (hostRestoreJump + sizeof(int));
|
||||
*(int*)(code + missingTlsJump) = passThroughOffset - (missingTlsJump + sizeof(int));
|
||||
*(int*)(code + missingHostStackJump) = passThroughOffset - (missingHostStackJump + sizeof(int));
|
||||
*(int*)(code + guestRestoreJump) = restoreOffset - (guestRestoreJump + sizeof(int));
|
||||
|
||||
uint oldProtect = default;
|
||||
VirtualProtect(ptr, stubSize, 32u, &oldProtect);
|
||||
FlushInstructionCache(GetCurrentProcess(), ptr, (nuint)offset);
|
||||
return (nint)ptr;
|
||||
}
|
||||
|
||||
private unsafe void* TryAllocateNearEntry(nuint size)
|
||||
{
|
||||
ulong entryPoint = _entryPoint;
|
||||
@@ -1980,8 +2153,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
{
|
||||
Rip = continuation.Rip,
|
||||
Rflags = continuation.Rflags == 0 ? 0x202UL : continuation.Rflags,
|
||||
FsBase = continuation.FsBase != 0 ? continuation.FsBase : (callerContext.FsBase != 0 ? callerContext.FsBase : fallbackTlsBase),
|
||||
GsBase = continuation.GsBase != 0 ? continuation.GsBase : (callerContext.GsBase != 0 ? callerContext.GsBase : fallbackTlsBase),
|
||||
FsBase = callerContext.FsBase != 0 ? callerContext.FsBase : (continuation.FsBase != 0 ? continuation.FsBase : fallbackTlsBase),
|
||||
GsBase = callerContext.GsBase != 0 ? callerContext.GsBase : (continuation.GsBase != 0 ? continuation.GsBase : fallbackTlsBase),
|
||||
};
|
||||
|
||||
context[CpuRegister.Rax] = continuation.Rax;
|
||||
@@ -1997,21 +2170,39 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
context[CpuRegister.R13] = continuation.R13;
|
||||
context[CpuRegister.R14] = continuation.R14;
|
||||
context[CpuRegister.R15] = continuation.R15;
|
||||
context[CpuRegister.Rsp] = continuation.Rsp + 16uL;
|
||||
context[CpuRegister.Rsp] = continuation.Rsp;
|
||||
|
||||
var currentGuestThreadHandle = GuestThreadExecution.CurrentGuestThreadHandle;
|
||||
var exitReason = GuestNativeCallExitReason.Exception;
|
||||
string? callbackReason = null;
|
||||
string? callbackLastError = null;
|
||||
Exception? callbackException = null;
|
||||
var currentGuestThreadHandle = GuestThreadExecution.CurrentGuestThreadHandle;
|
||||
var currentFiberAddress = GuestThreadExecution.CurrentFiberAddress;
|
||||
|
||||
void RunContinuation()
|
||||
{
|
||||
var restoreGuestThread = currentGuestThreadHandle != 0 &&
|
||||
GuestThreadExecution.CurrentGuestThreadHandle != currentGuestThreadHandle;
|
||||
var previousGuestThreadHandle = restoreGuestThread
|
||||
? GuestThreadExecution.EnterGuestThread(currentGuestThreadHandle)
|
||||
: 0UL;
|
||||
var restoreFiber = currentFiberAddress != 0 &&
|
||||
GuestThreadExecution.CurrentFiberAddress != currentFiberAddress;
|
||||
var previousFiberAddress = restoreFiber
|
||||
? GuestThreadExecution.EnterFiber(currentFiberAddress)
|
||||
: 0UL;
|
||||
var previousLastError = LastError;
|
||||
try
|
||||
{
|
||||
TraceGuestContext(
|
||||
$"continuation-enter reason={reason} managed={Environment.CurrentManagedThreadId} guest=0x{GuestThreadExecution.CurrentGuestThreadHandle:X16} fiber=0x{GuestThreadExecution.CurrentFiberAddress:X16} captured_guest=0x{currentGuestThreadHandle:X16} captured_fiber=0x{currentFiberAddress:X16} restore_guest={restoreGuestThread} restore_fiber={restoreFiber}");
|
||||
LastError = null;
|
||||
exitReason = ExecuteGuestContinuationEntry(context, continuation.Rip, reason, out callbackReason);
|
||||
exitReason = ExecuteGuestContinuationEntry(
|
||||
context,
|
||||
continuation.Rip,
|
||||
continuation.ReturnSlotAddress,
|
||||
reason,
|
||||
out callbackReason);
|
||||
callbackLastError = LastError;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -2022,7 +2213,17 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
}
|
||||
finally
|
||||
{
|
||||
TraceGuestContext(
|
||||
$"continuation-exit reason={reason} managed={Environment.CurrentManagedThreadId} guest=0x{GuestThreadExecution.CurrentGuestThreadHandle:X16} fiber=0x{GuestThreadExecution.CurrentFiberAddress:X16} exit={exitReason}");
|
||||
LastError = previousLastError;
|
||||
if (restoreFiber)
|
||||
{
|
||||
GuestThreadExecution.RestoreFiber(previousFiberAddress);
|
||||
}
|
||||
if (restoreGuestThread)
|
||||
{
|
||||
GuestThreadExecution.RestoreGuestThread(previousGuestThreadHandle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2045,6 +2246,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
{
|
||||
runner.Run(RunContinuation);
|
||||
}
|
||||
else if (runner is not null)
|
||||
{
|
||||
TraceGuestContext(
|
||||
$"continuation-inline reason={reason} managed={Environment.CurrentManagedThreadId} guest=0x{currentGuestThreadHandle:X16} fiber=0x{currentFiberAddress:X16}");
|
||||
RunContinuation();
|
||||
}
|
||||
else
|
||||
{
|
||||
RunContinuationOnTemporaryThread(currentGuestThreadHandle, RunContinuation);
|
||||
@@ -2070,6 +2277,14 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void TraceGuestContext(string message)
|
||||
{
|
||||
if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_CONTEXT"), "1", StringComparison.Ordinal))
|
||||
{
|
||||
Console.Error.WriteLine($"[LOADER][TRACE] guest_context.{message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static void RunContinuationOnTemporaryThread(ulong guestThreadHandle, Action continuation)
|
||||
{
|
||||
var continuationThread = new Thread(() =>
|
||||
@@ -2351,6 +2566,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
var previousActiveBackend = _activeExecutionBackend;
|
||||
var previousActiveContext = _activeCpuContext;
|
||||
var previousSentinel = _activeEntryReturnSentinelRip;
|
||||
var previousReturnSlotAddress = _activeGuestReturnSlotAddress;
|
||||
var previousForcedExit = _activeForcedGuestExit;
|
||||
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
||||
var previousYieldReason = _activeGuestThreadYieldReason;
|
||||
@@ -2360,6 +2576,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
_activeExecutionBackend = this;
|
||||
_activeCpuContext = context;
|
||||
_activeEntryReturnSentinelRip = 0;
|
||||
_activeGuestReturnSlotAddress = 0;
|
||||
_activeForcedGuestExit = false;
|
||||
_activeGuestThreadYieldRequested = false;
|
||||
_activeGuestThreadYieldReason = null;
|
||||
@@ -2463,7 +2680,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
ptr2[offset++] = 91;
|
||||
ptr2[offset++] = 195;
|
||||
ulong sentinel = (ulong)ptr + (ulong)sentinelOffset;
|
||||
ActiveEntryReturnSentinelRip = sentinel;
|
||||
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
||||
_activeGuestReturnSlotAddress = context[CpuRegister.Rsp] - 16uL;
|
||||
if (!context.TryWriteUInt64(context[CpuRegister.Rsp], sentinel))
|
||||
{
|
||||
reason = $"failed to patch guest thread return sentinel at 0x{context[CpuRegister.Rsp]:X16}";
|
||||
@@ -2509,6 +2727,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
previousActiveBackend,
|
||||
previousActiveContext,
|
||||
previousSentinel,
|
||||
previousReturnSlotAddress,
|
||||
previousForcedExit,
|
||||
previousYieldRequested,
|
||||
previousYieldReason);
|
||||
@@ -2516,7 +2735,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe GuestNativeCallExitReason ExecuteGuestContinuationEntry(CpuContext context, ulong entryPoint, string name, out string? reason)
|
||||
private unsafe GuestNativeCallExitReason ExecuteGuestContinuationEntry(
|
||||
CpuContext context,
|
||||
ulong entryPoint,
|
||||
ulong returnSlotAddress,
|
||||
string name,
|
||||
out string? reason)
|
||||
{
|
||||
reason = null;
|
||||
if (context[CpuRegister.Rsp] == 0)
|
||||
@@ -2534,6 +2758,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
var previousActiveBackend = _activeExecutionBackend;
|
||||
var previousActiveContext = _activeCpuContext;
|
||||
var previousSentinel = _activeEntryReturnSentinelRip;
|
||||
var previousReturnSlotAddress = _activeGuestReturnSlotAddress;
|
||||
var previousForcedExit = _activeForcedGuestExit;
|
||||
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
||||
var previousYieldReason = _activeGuestThreadYieldReason;
|
||||
@@ -2543,6 +2768,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
_activeExecutionBackend = this;
|
||||
_activeCpuContext = context;
|
||||
_activeEntryReturnSentinelRip = 0;
|
||||
_activeGuestReturnSlotAddress = returnSlotAddress;
|
||||
_activeForcedGuestExit = false;
|
||||
_activeGuestThreadYieldRequested = false;
|
||||
_activeGuestThreadYieldReason = null;
|
||||
@@ -2577,7 +2803,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
Emit(0x49); Emit(0x89); Emit(0x22); // mov [r10], rsp
|
||||
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rsp]); // mov rax, guest rsp
|
||||
Emit(0x48); Emit(0x89); Emit(0xC4); // mov rsp, rax
|
||||
Emit(0x48); Emit(0x83); Emit(0xEC); Emit(0x08); // sub rsp, 8
|
||||
EmitMovR64Imm(0x48, 0xBB, context[CpuRegister.Rbx]); // mov rbx, imm64
|
||||
EmitMovR64Imm(0x48, 0xBD, context[CpuRegister.Rbp]); // mov rbp, imm64
|
||||
EmitMovR64Imm(0x48, 0xBF, context[CpuRegister.Rdi]); // mov rdi, imm64
|
||||
@@ -2592,29 +2817,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
EmitMovR64Imm(0x49, 0xBF, context[CpuRegister.R15]); // mov r15, imm64
|
||||
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rax]); // mov rax, imm64
|
||||
EmitMovR64Imm(0x49, 0xBB, entryPoint); // mov r11, entryPoint
|
||||
Emit(0x41); Emit(0xFF); Emit(0xD3); // call r11
|
||||
int sentinelOffset = offset + 4;
|
||||
Emit(0x48); Emit(0x83); Emit(0xC4); Emit(0x08); // add rsp, 8
|
||||
EmitMovR64Imm(0x49, 0xBA, hostRspSlot); // mov r10, hostRspSlot
|
||||
Emit(0x49); Emit(0x8B); Emit(0x22); // mov rsp, [r10]
|
||||
EmitHostNonvolatileXmmRestore(ptr2, ref offset);
|
||||
Emit(0x41); Emit(0x5F); // pop r15
|
||||
Emit(0x41); Emit(0x5E); // pop r14
|
||||
Emit(0x41); Emit(0x5D); // pop r13
|
||||
Emit(0x41); Emit(0x5C); // pop r12
|
||||
Emit(0x5E); // pop rsi
|
||||
Emit(0x5F); // pop rdi
|
||||
Emit(0x5D); // pop rbp
|
||||
Emit(0x5B); // pop rbx
|
||||
Emit(0xC3); // ret
|
||||
ulong sentinel = (ulong)ptr + (ulong)sentinelOffset;
|
||||
ActiveEntryReturnSentinelRip = sentinel;
|
||||
var sentinelStackAddress = context[CpuRegister.Rsp] >= 16uL
|
||||
? context[CpuRegister.Rsp] - 16uL
|
||||
: 0;
|
||||
if (sentinelStackAddress == 0 || !context.TryWriteUInt64(sentinelStackAddress, sentinel))
|
||||
Emit(0x41); Emit(0xFF); Emit(0xE3); // jmp r11
|
||||
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
||||
if (returnSlotAddress == 0 || !context.TryWriteUInt64(returnSlotAddress, (ulong)_guestReturnStub))
|
||||
{
|
||||
reason = $"failed to patch guest continuation return sentinel at 0x{sentinelStackAddress:X16}";
|
||||
reason = $"failed to patch guest continuation return slot at 0x{returnSlotAddress:X16}";
|
||||
return GuestNativeCallExitReason.Exception;
|
||||
}
|
||||
uint oldProtect = default(uint);
|
||||
@@ -2657,6 +2864,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
previousActiveBackend,
|
||||
previousActiveContext,
|
||||
previousSentinel,
|
||||
previousReturnSlotAddress,
|
||||
previousForcedExit,
|
||||
previousYieldRequested,
|
||||
previousYieldReason);
|
||||
@@ -2717,9 +2925,18 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
}
|
||||
EmitByte(code, ref offset, 0x0F);
|
||||
EmitByte(code, ref offset, store ? (byte)0x7F : (byte)0x6F);
|
||||
EmitByte(code, ref offset, (byte)(0x44 | ((xmm & 7) << 3)));
|
||||
EmitByte(code, ref offset, 0x24);
|
||||
EmitByte(code, ref offset, displacement);
|
||||
if (displacement < 0x80)
|
||||
{
|
||||
EmitByte(code, ref offset, (byte)(0x44 | ((xmm & 7) << 3)));
|
||||
EmitByte(code, ref offset, 0x24);
|
||||
EmitByte(code, ref offset, displacement);
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitByte(code, ref offset, (byte)(0x84 | ((xmm & 7) << 3)));
|
||||
EmitByte(code, ref offset, 0x24);
|
||||
EmitUInt32(code, ref offset, displacement);
|
||||
}
|
||||
}
|
||||
|
||||
private unsafe bool ExecuteEntry(CpuContext context, ulong entryPoint, out OrbisGen2Result result)
|
||||
@@ -2745,6 +2962,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
var previousActiveBackend = _activeExecutionBackend;
|
||||
var previousActiveContext = _activeCpuContext;
|
||||
var previousSentinel = _activeEntryReturnSentinelRip;
|
||||
var previousReturnSlotAddress = _activeGuestReturnSlotAddress;
|
||||
var previousForcedExit = _activeForcedGuestExit;
|
||||
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
||||
var previousYieldReason = _activeGuestThreadYieldReason;
|
||||
@@ -2754,6 +2972,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
_activeExecutionBackend = this;
|
||||
_activeCpuContext = context;
|
||||
_activeEntryReturnSentinelRip = 0;
|
||||
_activeGuestReturnSlotAddress = 0;
|
||||
_activeForcedGuestExit = false;
|
||||
_activeGuestThreadYieldRequested = false;
|
||||
_activeGuestThreadYieldReason = null;
|
||||
@@ -2857,7 +3076,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
ptr2[num3++] = 91;
|
||||
ptr2[num3++] = 195;
|
||||
ulong value = (ulong)ptr + (ulong)num4;
|
||||
ActiveEntryReturnSentinelRip = value;
|
||||
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
||||
_activeGuestReturnSlotAddress = context[CpuRegister.Rsp] - 16uL;
|
||||
if (!context.TryWriteUInt64(context[CpuRegister.Rsp], value))
|
||||
{
|
||||
LastError = $"Failed to patch native return sentinel at 0x{context[CpuRegister.Rsp]:X16}";
|
||||
@@ -2939,6 +3159,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
previousActiveBackend,
|
||||
previousActiveContext,
|
||||
previousSentinel,
|
||||
previousReturnSlotAddress,
|
||||
previousForcedExit,
|
||||
previousYieldRequested,
|
||||
previousYieldReason);
|
||||
@@ -3116,6 +3337,22 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
RemoveVectoredExceptionHandler((void*)_rawExceptionHandler);
|
||||
_rawExceptionHandler = 0;
|
||||
}
|
||||
if (_rawExceptionHandlerStub != 0)
|
||||
{
|
||||
VirtualFree((void*)_rawExceptionHandlerStub, 0u, 32768u);
|
||||
_rawExceptionHandlerStub = 0;
|
||||
}
|
||||
if (_exceptionHandlerStub != 0)
|
||||
{
|
||||
VirtualFree((void*)_exceptionHandlerStub, 0u, 32768u);
|
||||
_exceptionHandlerStub = 0;
|
||||
}
|
||||
if (_unhandledFilterStub != 0)
|
||||
{
|
||||
SetUnhandledExceptionFilter(0);
|
||||
VirtualFree((void*)_unhandledFilterStub, 0u, 32768u);
|
||||
_unhandledFilterStub = 0;
|
||||
}
|
||||
if (_handlerHandle.IsAllocated)
|
||||
{
|
||||
_handlerHandle.Free();
|
||||
@@ -3172,6 +3409,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
||||
VirtualFree((void*)_unresolvedReturnStub, 0u, 32768u);
|
||||
_unresolvedReturnStub = 0;
|
||||
}
|
||||
if (_guestReturnStub != 0)
|
||||
{
|
||||
VirtualFree((void*)_guestReturnStub, 0u, 32768u);
|
||||
_guestReturnStub = 0;
|
||||
}
|
||||
if (_lowIndexedTableScratch != 0)
|
||||
{
|
||||
VirtualFree((void*)_lowIndexedTableScratch, 0u, 32768u);
|
||||
|
||||
Reference in New Issue
Block a user