From 2a52ccfe6a004e36c01e182a9ffae3e2eba57adb Mon Sep 17 00:00:00 2001 From: ParantezTech Date: Sun, 21 Jun 2026 23:18:29 +0300 Subject: [PATCH] [cpu] Add guest thread continuation --- src/SharpEmu.Core/Cpu/CpuDispatcher.cs | 19 +- .../DirectExecutionBackend.Diagnostics.cs | 81 +- .../DirectExecutionBackend.Exceptions.cs | 52 +- .../Native/DirectExecutionBackend.Imports.cs | 300 +++-- .../Cpu/Native/DirectExecutionBackend.cs | 1106 +++++++++++++++-- src/SharpEmu.HLE/GuestThreadExecution.cs | 87 ++ 6 files changed, 1458 insertions(+), 187 deletions(-) diff --git a/src/SharpEmu.Core/Cpu/CpuDispatcher.cs b/src/SharpEmu.Core/Cpu/CpuDispatcher.cs index 2a39fab..11d85cf 100644 --- a/src/SharpEmu.Core/Cpu/CpuDispatcher.cs +++ b/src/SharpEmu.Core/Cpu/CpuDispatcher.cs @@ -22,6 +22,7 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable private const ulong StackSize = 0x0020_0000UL; private const ulong TlsBaseAddress = 0x7FFE_0000_0000UL; private const ulong TlsSize = 0x0001_0000UL; + private const ulong TlsPrefixSize = 0x0000_1000UL; private const ulong BootstrapStubBaseAddress = 0x7FFD_F000_0000UL; private const ulong BootstrapPayloadBaseAddress = 0x7FFD_E000_0000UL; private const ulong DynlibFallbackStubBaseAddress = 0x7FFD_D000_0000UL; @@ -342,11 +343,12 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable for (var i = 0; i < 32; i++) { var candidateBase = TlsBaseAddress - ((ulong)i * tlsStride); + var mappedBase = candidateBase - TlsPrefixSize; try { _virtualMemory.Map( - candidateBase, - TlsSize, + mappedBase, + TlsSize + TlsPrefixSize, fileOffset: 0, fileData: ReadOnlySpan.Empty, ProgramHeaderFlags.Read | ProgramHeaderFlags.Write); @@ -363,9 +365,11 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable private static bool InitializeTls(CpuContext context, ulong tlsBase) { - return context.TryWriteUInt64(tlsBase + 0x00, tlsBase) && + return context.TryWriteUInt64(tlsBase - 0xF0, 0) && + context.TryWriteUInt64(tlsBase + 0x00, tlsBase) && context.TryWriteUInt64(tlsBase + 0x10, tlsBase) && - context.TryWriteUInt64(tlsBase + 0x28, 0xC0DEC0DECAFEBABEUL); + context.TryWriteUInt64(tlsBase + 0x28, 0xC0DEC0DECAFEBABEUL) && + context.TryWriteUInt64(tlsBase + 0x60, tlsBase); } private static bool InitializeGuestFrameChainSentinel(CpuContext context) @@ -420,6 +424,13 @@ public sealed class CpuDispatcher : ICpuDispatcher, IDisposable return false; } + var entryStackPointer = entryParamsAddress - sizeof(ulong); + if (!context.TryWriteUInt64(entryStackPointer, 0)) + { + return false; + } + + context[CpuRegister.Rsp] = entryStackPointer; context[CpuRegister.Rdi] = entryParamsAddress; context[CpuRegister.Rsi] = programExitHandlerAddress; context[CpuRegister.Rdx] = 0; diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs index 1b6777d..b9034dd 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs @@ -97,12 +97,13 @@ public sealed partial class DirectExecutionBackend private void ProbeReturnRip(ulong returnRip, long dispatchIndex) { - if (_cpuContext == null || returnRip == 0) + var cpuContext = ActiveCpuContext; + if (cpuContext == null || returnRip == 0) { return; } Span destination = stackalloc byte[128]; - if (!_cpuContext.Memory.TryRead(returnRip, destination)) + if (!cpuContext.Memory.TryRead(returnRip, destination)) { Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} return-rip probe: unreadable @0x{returnRip:X16}"); return; @@ -113,7 +114,7 @@ public sealed partial class DirectExecutionBackend { int num = BitConverter.ToInt32(destination.Slice(2, 4)); ulong num2 = returnRip + 6 + (ulong)num; - if (_cpuContext.TryReadUInt64(num2, out var value2)) + if (cpuContext.TryReadUInt64(num2, out var value2)) { Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} return-rip slot: [0x{num2:X16}] = 0x{value2:X16}"); } @@ -122,7 +123,7 @@ public sealed partial class DirectExecutionBackend { int num3 = BitConverter.ToInt32(destination.Slice(3, 4)); ulong num4 = returnRip + 7 + (ulong)num3; - if (_cpuContext.TryReadUInt64(num4, out var value3)) + if (cpuContext.TryReadUInt64(num4, out var value3)) { Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} return-rip mov-slot: [0x{num4:X16}] = 0x{value3:X16}"); } @@ -134,12 +135,82 @@ public sealed partial class DirectExecutionBackend int num5 = BitConverter.ToInt32(destination.Slice(i + 2, 4)); ulong num6 = returnRip + (ulong)i; ulong num7 = num6 + 6 + (ulong)num5; - if (_cpuContext.TryReadUInt64(num7, out var value4)) + if (cpuContext.TryReadUInt64(num7, out var value4)) { Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} near-indirect @{num6:X16}: slot=0x{num7:X16} val=0x{value4:X16}"); } } } + Span targetBytes = stackalloc byte[32]; + for (int i = 0; i + 5 <= destination.Length; i++) + { + if (destination[i] != 0xE8) + { + continue; + } + + int rel32 = BitConverter.ToInt32(destination.Slice(i + 1, 4)); + ulong callRip = returnRip + (ulong)i; + ulong target = unchecked((ulong)((long)(callRip + 5) + rel32)); + Console.Error.WriteLine($"[LOADER][TRACE] Import#{dispatchIndex} near-call @{callRip:X16}: target=0x{target:X16}"); + for (int importIndex = 0; importIndex < _importEntries.Length; importIndex++) + { + if (_importEntries[importIndex].Address != target) + { + continue; + } + + string nid = _importEntries[importIndex].Nid; + if (_moduleManager.TryGetExport(nid, out var export)) + { + Console.Error.WriteLine( + $"[LOADER][TRACE] Import#{dispatchIndex} near-call import: index={importIndex} {export.LibraryName}:{export.Name} ({nid})"); + } + else + { + Console.Error.WriteLine( + $"[LOADER][TRACE] Import#{dispatchIndex} near-call import: index={importIndex} nid={nid}"); + } + break; + } + + if (cpuContext.Memory.TryRead(target, targetBytes)) + { + Console.Error.WriteLine( + $"[LOADER][TRACE] Import#{dispatchIndex} near-call target bytes @0x{target:X16}: " + + BitConverter.ToString(targetBytes.ToArray()).Replace("-", " ")); + if (targetBytes[0] == 0xFF && targetBytes[1] == 0x25) + { + int slotRel32 = BitConverter.ToInt32(targetBytes.Slice(2, 4)); + ulong slot = unchecked((ulong)((long)(target + 6) + slotRel32)); + if (cpuContext.TryReadUInt64(slot, out var slotTarget)) + { + Console.Error.WriteLine( + $"[LOADER][TRACE] Import#{dispatchIndex} near-call PLT slot: [0x{slot:X16}] = 0x{slotTarget:X16}"); + for (int importIndex = 0; importIndex < _importEntries.Length; importIndex++) + { + if (_importEntries[importIndex].Address != slotTarget) + { + continue; + } + + string nid = _importEntries[importIndex].Nid; + if (_moduleManager.TryGetExport(nid, out var export)) + { + Console.Error.WriteLine( + $"[LOADER][TRACE] Import#{dispatchIndex} near-call PLT import: index={importIndex} {export.LibraryName}:{export.Name} ({nid})"); + } + else + { + Console.Error.WriteLine( + $"[LOADER][TRACE] Import#{dispatchIndex} near-call PLT import: index={importIndex} nid={nid}"); + } + break; + } + } + } + } + } } private static bool IsUnresolvedSentinel(ulong value) diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Exceptions.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Exceptions.cs index c7619ab..acbe268 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Exceptions.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Exceptions.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.Reflection; using System.Runtime.InteropServices; +using System.Threading; using SharpEmu.Core.Cpu.Disasm; using SharpEmu.HLE; @@ -61,7 +62,7 @@ public sealed partial class DirectExecutionBackend { if (_vectoredHandlerDepth > 0) { - Console.Error.WriteLine("[LOADER][TRACE] Nested VEH exception; passing through."); + LogNestedVectoredException(exceptionInfo); Console.Error.Flush(); return 0; } @@ -84,10 +85,18 @@ public sealed partial class DirectExecutionBackend ulong rip = ReadCtxU64(contextRecord, 248); ulong rsp = ReadCtxU64(contextRecord, 152); - if (exceptionCode == 3221225477u && TryHandleLazyCommittedPage(exceptionRecord)) + if (exceptionCode == 3221225477u && TryHandleLazyCommittedPage(exceptionRecord, rip, rsp)) { return -1; } + if (IsBenignHostDebugException(exceptionCode)) + { + return -1; + } + if (exceptionCode == MSVC_CPP_EXCEPTION) + { + return 0; + } switch (exceptionCode) { @@ -278,6 +287,37 @@ public sealed partial class DirectExecutionBackend } } + private static bool IsBenignHostDebugException(uint exceptionCode) + { + return exceptionCode is DBG_PRINTEXCEPTION_C or DBG_PRINTEXCEPTION_WIDE_C or MS_VC_THREADNAME_EXCEPTION; + } + + private unsafe static void LogNestedVectoredException(void* exceptionInfo) + { + int count = Interlocked.Increment(ref _nestedVehTraceCount); + if (count > 16 && count % 128 != 0) + { + return; + } + + try + { + EXCEPTION_POINTERS* pointers = (EXCEPTION_POINTERS*)exceptionInfo; + EXCEPTION_RECORD* record = pointers->ExceptionRecord; + void* contextRecord = pointers->ContextRecord; + ulong rip = contextRecord != null ? ReadCtxU64(contextRecord, 248) : 0; + ulong rsp = contextRecord != null ? ReadCtxU64(contextRecord, 152) : 0; + ulong accessType = record->NumberParameters >= 1 ? *record->ExceptionInformation : 0; + ulong target = record->NumberParameters >= 2 ? record->ExceptionInformation[1] : 0; + Console.Error.WriteLine( + $"[LOADER][TRACE] Nested VEH exception#{count}: code=0x{record->ExceptionCode:X8} ex=0x{(ulong)record->ExceptionAddress:X16} rip=0x{rip:X16} rsp=0x{rsp:X16} type={accessType} target=0x{target:X16}; passing through."); + } + catch + { + Console.Error.WriteLine($"[LOADER][TRACE] Nested VEH exception#{count}; passing through."); + } + } + private unsafe void LogAccessViolationTrace(ulong exceptionAddress, EXCEPTION_RECORD* exceptionRecord) { ulong accessType = exceptionRecord->NumberParameters >= 1 ? (*exceptionRecord->ExceptionInformation) : 0; @@ -828,7 +868,7 @@ public sealed partial class DirectExecutionBackend return true; } - private unsafe static bool TryHandleLazyCommittedPage(EXCEPTION_RECORD* exceptionRecord) + private unsafe bool TryHandleLazyCommittedPage(EXCEPTION_RECORD* exceptionRecord, ulong rip, ulong rsp) { if (exceptionRecord->NumberParameters < 2) { @@ -845,6 +885,10 @@ public sealed partial class DirectExecutionBackend { return false; } + if (!IsGuestOwnedLazyCommitAddress(faultAddress, out var owner)) + { + return false; + } if (VirtualQuery((void*)faultAddress, out var mbi, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0) { return false; @@ -852,7 +896,7 @@ public sealed partial class DirectExecutionBackend ulong pageBase = faultAddress & 0xFFFFFFFFFFFFF000uL; uint commitProtect = ResolveLazyCommitProtection(accessType, mbi.AllocationProtect); - Console.Error.WriteLine($"[LOADER][TRACE] lazy-query: fault=0x{faultAddress:X16} state=0x{mbi.State:X08} base=0x{mbi.BaseAddress:X16} size=0x{mbi.RegionSize:X16} alloc=0x{mbi.AllocationProtect:X08} prot=0x{mbi.Protect:X08}"); + Console.Error.WriteLine($"[LOADER][TRACE] lazy-query: fault=0x{faultAddress:X16} owner={owner} rip=0x{rip:X16} rsp=0x{rsp:X16} state=0x{mbi.State:X08} base=0x{mbi.BaseAddress:X16} size=0x{mbi.RegionSize:X16} alloc=0x{mbi.AllocationProtect:X08} prot=0x{mbi.Protect:X08}"); bool committed = false; ulong committedBase = 0; diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs index b088537..0063a42 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading; @@ -74,7 +75,8 @@ public sealed partial class DirectExecutionBackend { long num = Interlocked.Increment(ref _importDispatchCount); MarkExecutionProgress(); - if (_cpuContext == null) + var cpuContext = ActiveCpuContext; + if (cpuContext == null) { LastError = "Import dispatch called without active CPU context"; return 18446744071562199298uL; @@ -91,32 +93,32 @@ public sealed partial class DirectExecutionBackend Console.Error.WriteLine($"[LOADER][TRACE] Raw sentinel recoveries: {num2} (last import index={importIndex})"); _lastReportedRawSentinelRecoveries = num2; } - _cpuContext.Rip = importStubEntry.Address; - _cpuContext[CpuRegister.Rdi] = *(ulong*)argPackPtr; - _cpuContext[CpuRegister.Rsi] = *(ulong*)(argPackPtr + 8); - _cpuContext[CpuRegister.Rdx] = *(ulong*)(argPackPtr + 16); - _cpuContext[CpuRegister.Rcx] = *(ulong*)(argPackPtr + 24); - _cpuContext[CpuRegister.R8] = *(ulong*)(argPackPtr + 32); - _cpuContext[CpuRegister.R9] = *(ulong*)(argPackPtr + 40); - _cpuContext[CpuRegister.Rbx] = *(ulong*)(argPackPtr + 48); - _cpuContext[CpuRegister.Rbp] = *(ulong*)(argPackPtr + 56); - _cpuContext[CpuRegister.R12] = *(ulong*)(argPackPtr + 64); - _cpuContext[CpuRegister.R13] = *(ulong*)(argPackPtr + 72); - _cpuContext[CpuRegister.R14] = *(ulong*)(argPackPtr + 80); - _cpuContext[CpuRegister.R15] = *(ulong*)(argPackPtr + 88); - _cpuContext[CpuRegister.Rsp] = (ulong)argPackPtr + 96uL; - ulong value = _cpuContext[CpuRegister.Rdi]; - ulong value2 = _cpuContext[CpuRegister.Rsi]; - ulong num3 = _cpuContext[CpuRegister.Rdx]; - ulong num4 = _cpuContext[CpuRegister.Rcx]; - ulong num5 = _cpuContext[CpuRegister.R8]; - ulong num6 = _cpuContext[CpuRegister.R9]; - ulong value3 = _cpuContext[CpuRegister.Rbx]; - ulong value4 = _cpuContext[CpuRegister.Rbp]; - ulong value5 = _cpuContext[CpuRegister.R12]; - ulong value6 = _cpuContext[CpuRegister.R13]; - ulong value7 = _cpuContext[CpuRegister.R14]; - ulong value8 = _cpuContext[CpuRegister.R15]; + cpuContext.Rip = importStubEntry.Address; + cpuContext[CpuRegister.Rdi] = *(ulong*)argPackPtr; + cpuContext[CpuRegister.Rsi] = *(ulong*)(argPackPtr + 8); + cpuContext[CpuRegister.Rdx] = *(ulong*)(argPackPtr + 16); + cpuContext[CpuRegister.Rcx] = *(ulong*)(argPackPtr + 24); + cpuContext[CpuRegister.R8] = *(ulong*)(argPackPtr + 32); + cpuContext[CpuRegister.R9] = *(ulong*)(argPackPtr + 40); + cpuContext[CpuRegister.Rbx] = *(ulong*)(argPackPtr + 48); + cpuContext[CpuRegister.Rbp] = *(ulong*)(argPackPtr + 56); + cpuContext[CpuRegister.R12] = *(ulong*)(argPackPtr + 64); + cpuContext[CpuRegister.R13] = *(ulong*)(argPackPtr + 72); + cpuContext[CpuRegister.R14] = *(ulong*)(argPackPtr + 80); + cpuContext[CpuRegister.R15] = *(ulong*)(argPackPtr + 88); + cpuContext[CpuRegister.Rsp] = (ulong)argPackPtr + 96uL; + ulong value = cpuContext[CpuRegister.Rdi]; + ulong value2 = cpuContext[CpuRegister.Rsi]; + ulong num3 = cpuContext[CpuRegister.Rdx]; + ulong num4 = cpuContext[CpuRegister.Rcx]; + ulong num5 = cpuContext[CpuRegister.R8]; + ulong num6 = cpuContext[CpuRegister.R9]; + ulong value3 = cpuContext[CpuRegister.Rbx]; + ulong value4 = cpuContext[CpuRegister.Rbp]; + ulong value5 = cpuContext[CpuRegister.R12]; + ulong value6 = cpuContext[CpuRegister.R13]; + ulong value7 = cpuContext[CpuRegister.R14]; + ulong value8 = cpuContext[CpuRegister.R15]; ulong num7 = *(ulong*)(argPackPtr + 96); if (!IsLikelyReturnAddress(num7)) { @@ -133,6 +135,13 @@ public sealed partial class DirectExecutionBackend } } TrackDistinctImportNid(importStubEntry.Nid); + var probeImportReturn = Environment.GetEnvironmentVariable("SHARPEMU_PROBE_IMPORT_RET"); + if (!string.IsNullOrWhiteSpace(probeImportReturn) && + (string.Equals(probeImportReturn, "*", StringComparison.Ordinal) || + string.Equals(probeImportReturn, importStubEntry.Nid, StringComparison.Ordinal))) + { + ProbeReturnRip(num7, num); + } TrackStrlenPrelude(importStubEntry.Nid, num, num7); bool logBootstrap = string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_BOOTSTRAP"), "1", StringComparison.Ordinal); if (logBootstrap && string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal)) @@ -145,9 +154,9 @@ public sealed partial class DirectExecutionBackend Console.Error.WriteLine( $"[LOADER][TRACE] bootstrap_call#{num}: op=0x{value:X16} sym_ptr=0x{value2:X16} sym='{symbolText}' out_ptr=0x{num3:X16} ret=0x{num7:X16}"); } - if (!_forcedGuestExit && ShouldForceGuestExitOnImportLoop(importStubEntry.Nid, num7, num, value, value2) && TryForceGuestExitToHostStub(argPackPtr, num, num7, importStubEntry.Nid)) + if (!ActiveForcedGuestExit && ShouldForceGuestExitOnImportLoop(importStubEntry.Nid, num7, num, value, value2) && TryForceGuestExitToHostStub(argPackPtr, num, num7, importStubEntry.Nid)) { - _cpuContext[CpuRegister.Rax] = 1uL; + cpuContext[CpuRegister.Rax] = 1uL; return 1uL; } bool flag0 = ShouldSuppressStrlenTrace(importStubEntry.Nid); @@ -209,33 +218,41 @@ public sealed partial class DirectExecutionBackend } if (!flag0) { - RecordRecentImportTrace($"#{num} nid={importStubEntry.Nid} ret=0x{num7:X16} rdi=0x{_cpuContext[CpuRegister.Rdi]:X16} rsi=0x{_cpuContext[CpuRegister.Rsi]:X16} rdx=0x{_cpuContext[CpuRegister.Rdx]:X16}"); + RecordRecentImportTrace($"#{num} nid={importStubEntry.Nid} ret=0x{num7:X16} rdi=0x{cpuContext[CpuRegister.Rdi]:X16} rsi=0x{cpuContext[CpuRegister.Rsi]:X16} rdx=0x{cpuContext[CpuRegister.Rdx]:X16}"); } if (importStubEntry.Nid == "8zTFvBIAIN8" && num <= 256) { - Console.Error.WriteLine($"[LOADER][TRACE] memset#{num}: dst=0x{_cpuContext[CpuRegister.Rdi]:X16} val=0x{_cpuContext[CpuRegister.Rsi] & 0xFF:X2} len=0x{_cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}"); + Console.Error.WriteLine($"[LOADER][TRACE] memset#{num}: dst=0x{cpuContext[CpuRegister.Rdi]:X16} val=0x{cpuContext[CpuRegister.Rsi] & 0xFF:X2} len=0x{cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}"); } if (importStubEntry.Nid == "tsvEmnenz48" && num <= 64) { - Console.Error.WriteLine($"[LOADER][TRACE] __cxa_atexit#{num}: func=0x{_cpuContext[CpuRegister.Rdi]:X16} arg=0x{_cpuContext[CpuRegister.Rsi]:X16} dso=0x{_cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}"); + Console.Error.WriteLine($"[LOADER][TRACE] __cxa_atexit#{num}: func=0x{cpuContext[CpuRegister.Rdi]:X16} arg=0x{cpuContext[CpuRegister.Rsi]:X16} dso=0x{cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}"); } if (importStubEntry.Nid == "bzQExy189ZI" || importStubEntry.Nid == "8G2LB+A3rzg") { - Console.Error.WriteLine($"[LOADER][TRACE] {importStubEntry.Nid}#{num}: rdi=0x{_cpuContext[CpuRegister.Rdi]:X16} rsi=0x{_cpuContext[CpuRegister.Rsi]:X16} rdx=0x{_cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}"); + Console.Error.WriteLine($"[LOADER][TRACE] {importStubEntry.Nid}#{num}: rdi=0x{cpuContext[CpuRegister.Rdi]:X16} rsi=0x{cpuContext[CpuRegister.Rsi]:X16} rdx=0x{cpuContext[CpuRegister.Rdx]:X16} ret=0x{num7:X16}"); } - if (flag || flag2 || flag3) + if (flag6 || flag || flag2 || flag3) { - Console.Error.WriteLine($"[LOADER][TRACE] ImportCtx#{num}: nid={importStubEntry.Nid} ret=0x{num7:X16} rdi=0x{_cpuContext[CpuRegister.Rdi]:X16} rsi=0x{_cpuContext[CpuRegister.Rsi]:X16} rdx=0x{_cpuContext[CpuRegister.Rdx]:X16} rcx=0x{_cpuContext[CpuRegister.Rcx]:X16}"); + Console.Error.WriteLine($"[LOADER][TRACE] ImportCtx#{num}: nid={importStubEntry.Nid} ret=0x{num7:X16} rdi=0x{cpuContext[CpuRegister.Rdi]:X16} rsi=0x{cpuContext[CpuRegister.Rsi]:X16} rdx=0x{cpuContext[CpuRegister.Rdx]:X16} rcx=0x{cpuContext[CpuRegister.Rcx]:X16}"); Console.Error.WriteLine($"[LOADER][TRACE] ImportNV#{num}: rbx=0x{value3:X16} rbp=0x{value4:X16} r12=0x{value5:X16} r13=0x{value6:X16} r14=0x{value7:X16} r15=0x{value8:X16}"); if (flag3) { - ulong num9 = _cpuContext[CpuRegister.Rsp]; - if (_cpuContext.TryReadUInt64(num9, out var value9) && _cpuContext.TryReadUInt64(num9 + 8, out var value10) && _cpuContext.TryReadUInt64(num9 + 16, out var value11) && _cpuContext.TryReadUInt64(num9 + 24, out var value12) && _cpuContext.TryReadUInt64(num9 + 32, out var value13) && _cpuContext.TryReadUInt64(num9 + 40, out var value14) && _cpuContext.TryReadUInt64(num9 + 48, out var value15) && _cpuContext.TryReadUInt64(num9 + 56, out var value16) && _cpuContext.TryReadUInt64(num9 + 64, out var value17)) + ulong num9 = cpuContext[CpuRegister.Rsp]; + if (cpuContext.TryReadUInt64(num9, out var value9) && cpuContext.TryReadUInt64(num9 + 8, out var value10) && cpuContext.TryReadUInt64(num9 + 16, out var value11) && cpuContext.TryReadUInt64(num9 + 24, out var value12) && cpuContext.TryReadUInt64(num9 + 32, out var value13) && cpuContext.TryReadUInt64(num9 + 40, out var value14) && cpuContext.TryReadUInt64(num9 + 48, out var value15) && cpuContext.TryReadUInt64(num9 + 56, out var value16) && cpuContext.TryReadUInt64(num9 + 64, out var value17)) { Console.Error.WriteLine($"[LOADER][TRACE] ImportStackHead#{num}: rsp=0x{num9:X16} [0]=0x{value9:X16} [20]=0x{value13:X16} [40]=0x{value17:X16}"); Console.Error.WriteLine($"[LOADER][TRACE] ImportStack#{num}: rsp=0x{num9:X16} [0]=0x{value9:X16} [8]=0x{value10:X16} [10]=0x{value11:X16} [18]=0x{value12:X16} [20]=0x{value13:X16} [28]=0x{value14:X16} [30]=0x{value15:X16} [38]=0x{value16:X16} [40]=0x{value17:X16}"); } } + if (flag6 && string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_IMPORT_FRAMES"), "1", StringComparison.Ordinal)) + { + TraceImportFrameChain(cpuContext, num); + } + if (flag6 && string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_IMPORT_RECENT"), "1", StringComparison.Ordinal)) + { + DumpRecentImportTrace(); + } if (flag3) { Console.Error.Flush(); @@ -243,6 +260,16 @@ public sealed partial class DirectExecutionBackend } if (importStubEntry.Nid == "Ou3iL1abvng") { + if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_STACK_CHK"), "1", StringComparison.Ordinal)) + { + var savedGuardAddress = value4 >= 0x10 ? value4 - 0x10 : 0; + var guardKnown = TryReadUInt64Compat(value3, out var guardValue); + var savedKnown = TryReadUInt64Compat(savedGuardAddress, out var savedGuardValue); + Console.Error.WriteLine( + $"[LOADER][TRACE] stack_chk_diag#{num}: ret=0x{num7:X16} guard_ptr=0x{value3:X16} " + + $"guard={(guardKnown ? $"0x{guardValue:X16}" : "?")} saved@0x{savedGuardAddress:X16}={(savedKnown ? $"0x{savedGuardValue:X16}" : "?")} " + + $"rbp=0x{value4:X16} rsp=0x{((ulong)argPackPtr + 96uL):X16}"); + } try { byte[] array = new byte[64]; @@ -258,17 +285,31 @@ public sealed partial class DirectExecutionBackend { OrbisGen2Result orbisGen2Result; bool dispatchResolved = true; - if (string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal)) + var previousImportCallFrame = GuestThreadExecution.EnterImportCallFrame(num7, (ulong)argPackPtr + 104uL); + try { - orbisGen2Result = DispatchBootstrapBridge(); + if (string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal)) + { + orbisGen2Result = DispatchBootstrapBridge(); + } + else if (string.Equals(importStubEntry.Nid, RuntimeStubNids.KernelDynlibDlsym, StringComparison.Ordinal)) + { + orbisGen2Result = DispatchKernelDynlibDlsym(); + } + else + { + dispatchResolved = _moduleManager.TryDispatch(importStubEntry.Nid, cpuContext, out orbisGen2Result); + } } - else if (string.Equals(importStubEntry.Nid, RuntimeStubNids.KernelDynlibDlsym, StringComparison.Ordinal)) + finally { - orbisGen2Result = DispatchKernelDynlibDlsym(); + GuestThreadExecution.RestoreImportCallFrame(previousImportCallFrame); } - else + if (dispatchResolved && + orbisGen2Result == OrbisGen2Result.ORBIS_GEN2_OK && + string.Equals(importStubEntry.Nid, "BohYr-F7-is", StringComparison.Ordinal)) { - dispatchResolved = _moduleManager.TryDispatch(importStubEntry.Nid, _cpuContext, out orbisGen2Result); + RegisterPrtLazyCommitRange(value2, num3); } if (!dispatchResolved) { @@ -287,59 +328,86 @@ public sealed partial class DirectExecutionBackend ExportedFunction export2; bool value20 = _moduleManager.TryGetExport(importStubEntry.Nid, out export2); Console.Error.WriteLine($"[LOADER][WARN] map_direct lookup with import nid: function={value19}, export={value20}"); - Console.Error.WriteLine(_moduleManager.TryGetExport("L-Q3LEjIbgA", out ExportedFunction export3) ? $"[LOADER][WARN] Canonical map_direct exists as {export3.LibraryName}:{export3.Name}, target={export3.Target}, ctx_target={_cpuContext.TargetGeneration}" : "[LOADER][WARN] Canonical map_direct export lookup also missing"); + Console.Error.WriteLine(_moduleManager.TryGetExport("L-Q3LEjIbgA", out ExportedFunction export3) ? $"[LOADER][WARN] Canonical map_direct exists as {export3.LibraryName}:{export3.Name}, target={export3.Target}, ctx_target={cpuContext.TargetGeneration}" : "[LOADER][WARN] Canonical map_direct export lookup also missing"); } } else if (orbisGen2Result != OrbisGen2Result.ORBIS_GEN2_OK) { - Console.Error.WriteLine($"[LOADER][WARN] Import#{num} result: {orbisGen2Result} ({importStubEntry.Nid})"); + Console.Error.WriteLine( + $"[LOADER][WARN] Import#{num} result: {orbisGen2Result} ({importStubEntry.Nid}) " + + $"rdi=0x{value:X16} rsi=0x{value2:X16} rdx=0x{num3:X16} rcx=0x{num4:X16} ret=0x{num7:X16}"); } - _cpuContext[CpuRegister.Rbx] = value3; - _cpuContext[CpuRegister.Rbp] = value4; - _cpuContext[CpuRegister.R12] = value5; - _cpuContext[CpuRegister.R13] = value6; - _cpuContext[CpuRegister.R14] = value7; - _cpuContext[CpuRegister.R15] = value8; - _cpuContext[CpuRegister.Rdi] = value; - _cpuContext[CpuRegister.Rsi] = value2; + cpuContext[CpuRegister.Rbx] = value3; + cpuContext[CpuRegister.Rbp] = value4; + cpuContext[CpuRegister.R12] = value5; + cpuContext[CpuRegister.R13] = value6; + cpuContext[CpuRegister.R14] = value7; + cpuContext[CpuRegister.R15] = value8; + cpuContext[CpuRegister.Rdi] = value; + cpuContext[CpuRegister.Rsi] = value2; if (GuestThreadExecution.TryConsumeCurrentEntryExit(out var exitStatus, out var exitReason)) { if (TryCompleteGuestEntryToHostStub(argPackPtr, num, num7, importStubEntry.Nid, exitReason, exitStatus)) { - _cpuContext[CpuRegister.Rax] = unchecked((ulong)exitStatus); + cpuContext[CpuRegister.Rax] = unchecked((ulong)exitStatus); } else { LastError = $"Failed to complete guest entry after {importStubEntry.Nid}: missing host return sentinel"; - _cpuContext[CpuRegister.Rax] = 18446744071562199298uL; + cpuContext[CpuRegister.Rax] = 18446744071562199298uL; } } if (GuestThreadExecution.TryConsumeCurrentThreadBlock(out var blockReason) && TryYieldGuestThreadToHostStub(argPackPtr, num, num7, importStubEntry.Nid, blockReason)) { - _cpuContext[CpuRegister.Rax] = 0uL; + cpuContext[CpuRegister.Rax] = 0uL; } if (flag || flag2 || flag3) { - Console.Error.WriteLine($"[LOADER][TRACE] ImportRet#{num}: nid={importStubEntry.Nid} result={orbisGen2Result} rax=0x{_cpuContext[CpuRegister.Rax]:X16}"); + Console.Error.WriteLine($"[LOADER][TRACE] ImportRet#{num}: nid={importStubEntry.Nid} result={orbisGen2Result} rax=0x{cpuContext[CpuRegister.Rax]:X16}"); if (flag3) { Console.Error.Flush(); } } - return _cpuContext[CpuRegister.Rax]; + return cpuContext[CpuRegister.Rax]; } catch (Exception ex) { LastError = $"HLE dispatch error for {importStubEntry.Nid}: {ex.GetType().Name}: {ex.Message}"; - _cpuContext[CpuRegister.Rax] = 18446744071562199298uL; + cpuContext[CpuRegister.Rax] = 18446744071562199298uL; return 18446744071562199298uL; } } + private void TraceImportFrameChain(CpuContext context, long dispatchIndex) + { + var frame = context[CpuRegister.Rbp]; + for (int i = 0; i < 16; i++) + { + if (!context.TryReadUInt64(frame, out var next) || + !context.TryReadUInt64(frame + sizeof(ulong), out var returnRip)) + { + break; + } + + var symbol = TryFormatNearestRuntimeSymbol(returnRip, out var formatted) + ? $" [{formatted}]" + : string.Empty; + Console.Error.WriteLine( + $"[LOADER][TRACE] ImportFrame#{dispatchIndex}.{i}: rbp=0x{frame:X16} ret=0x{returnRip:X16}{symbol} next=0x{next:X16}"); + if (next <= frame || next - frame > 0x100000) + { + break; + } + + frame = next; + } + } + private unsafe bool TryForceGuestExitToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid) { - ulong num = _entryReturnSentinelRip; + ulong num = ActiveEntryReturnSentinelRip; if (num < 65536) { return false; @@ -352,7 +420,7 @@ public sealed partial class DirectExecutionBackend { return false; } - _forcedGuestExit = true; + ActiveForcedGuestExit = true; LastError = $"Detected repeating import loop at import#{dispatchIndex} ({nid}) and forced guest exit."; Console.Error.WriteLine($"[LOADER][ERROR] Import-loop guard fired at import#{dispatchIndex}: nid={nid} ret=0x{returnRip:X16} -> host_exit=0x{num:X16}"); DumpRecentImportTrace(); @@ -361,7 +429,7 @@ public sealed partial class DirectExecutionBackend private unsafe bool TryCompleteGuestEntryToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid, string reason, int status) { - ulong hostExit = _entryReturnSentinelRip; + ulong hostExit = ActiveEntryReturnSentinelRip; if (hostExit < 65536) { return false; @@ -381,7 +449,7 @@ public sealed partial class DirectExecutionBackend private unsafe bool TryYieldGuestThreadToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid, string reason) { - ulong hostExit = _entryReturnSentinelRip; + ulong hostExit = ActiveEntryReturnSentinelRip; if (hostExit < 65536) { return false; @@ -395,10 +463,10 @@ public sealed partial class DirectExecutionBackend return false; } - _guestThreadYieldRequested = true; - _guestThreadYieldReason = string.IsNullOrWhiteSpace(reason) ? nid : reason; + ActiveGuestThreadYieldRequested = true; + ActiveGuestThreadYieldReason = string.IsNullOrWhiteSpace(reason) ? nid : reason; Console.Error.WriteLine( - $"[LOADER][INFO] Guest thread yield at import#{dispatchIndex}: nid={nid} ret=0x{returnRip:X16} reason={_guestThreadYieldReason}"); + $"[LOADER][INFO] Guest thread yield at import#{dispatchIndex}: nid={nid} ret=0x{returnRip:X16} reason={ActiveGuestThreadYieldReason}"); return true; } @@ -424,10 +492,35 @@ public sealed partial class DirectExecutionBackend { _importLoopPatternHits--; } + if (_importLoopPatternHits == 0) + { + _importLoopPatternStartTimestamp = 0; + } return false; } + if (_importLoopPatternStartTimestamp == 0) + { + _importLoopPatternStartTimestamp = Stopwatch.GetTimestamp(); + } _importLoopPatternHits++; - return _importLoopPatternHits >= 6; + var guardSeconds = GetImportLoopGuardSeconds(); + if (guardSeconds <= 0 || _importLoopPatternHits < 6) + { + return false; + } + + var elapsedTicks = Stopwatch.GetTimestamp() - _importLoopPatternStartTimestamp; + return elapsedTicks >= (long)(guardSeconds * Stopwatch.Frequency); + } + + private static int GetImportLoopGuardSeconds() + { + if (int.TryParse(Environment.GetEnvironmentVariable("SHARPEMU_IMPORT_LOOP_GUARD_SECONDS"), out var seconds)) + { + return Math.Max(0, seconds); + } + + return DefaultImportLoopGuardSeconds; } private ulong BuildImportLoopSignature(ulong nidHash, ulong returnRip, ulong arg0, ulong arg1) @@ -645,41 +738,43 @@ public sealed partial class DirectExecutionBackend private OrbisGen2Result DispatchKernelDynlibDlsym() { - if (_cpuContext == null) + var cpuContext = ActiveCpuContext; + if (cpuContext == null) { return OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT; } - ulong symbolNameAddress = _cpuContext[CpuRegister.Rsi]; - ulong outputAddress = _cpuContext[CpuRegister.Rdx]; + ulong symbolNameAddress = cpuContext[CpuRegister.Rsi]; + ulong outputAddress = cpuContext[CpuRegister.Rdx]; if (!TryReadAsciiZ(symbolNameAddress, 512, out var symbolName)) { - _cpuContext[CpuRegister.Rax] = 18446744073709551615uL; + cpuContext[CpuRegister.Rax] = 18446744073709551615uL; return OrbisGen2Result.ORBIS_GEN2_OK; } if (!TryResolveRuntimeSymbolAddress(symbolName, out var resolvedAddress)) { - _cpuContext[CpuRegister.Rax] = 18446744073709551615uL; + cpuContext[CpuRegister.Rax] = 18446744073709551615uL; return OrbisGen2Result.ORBIS_GEN2_OK; } if (outputAddress == 0L || !TryWriteUInt64Compat(outputAddress, resolvedAddress)) { - _cpuContext[CpuRegister.Rax] = 18446744073709551615uL; + cpuContext[CpuRegister.Rax] = 18446744073709551615uL; return OrbisGen2Result.ORBIS_GEN2_OK; } - _cpuContext[CpuRegister.Rax] = 0uL; + cpuContext[CpuRegister.Rax] = 0uL; return OrbisGen2Result.ORBIS_GEN2_OK; } private OrbisGen2Result DispatchBootstrapBridge() { - if (_cpuContext == null) + var cpuContext = ActiveCpuContext; + if (cpuContext == null) { return OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT; } - ulong bridgeHandle = _cpuContext[CpuRegister.Rdi]; - ulong symbolNameAddress = _cpuContext[CpuRegister.Rsi]; - ulong outputAddress = _cpuContext[CpuRegister.Rdx]; + ulong bridgeHandle = cpuContext[CpuRegister.Rdi]; + ulong symbolNameAddress = cpuContext[CpuRegister.Rsi]; + ulong outputAddress = cpuContext[CpuRegister.Rdx]; _ = TryReadAsciiZ(symbolNameAddress, 512, out var symbolName); OrbisGen2Result result = DispatchKernelDynlibDlsym(); @@ -691,10 +786,10 @@ public sealed partial class DirectExecutionBackend if (logBootstrap) { Console.Error.WriteLine( - $"[LOADER][TRACE] bootstrap_dispatch: handle=0x{bridgeHandle:X16} symbol='{symbolName}' out=0x{outputAddress:X16} rax=0x{_cpuContext[CpuRegister.Rax]:X16}"); + $"[LOADER][TRACE] bootstrap_dispatch: handle=0x{bridgeHandle:X16} symbol='{symbolName}' out=0x{outputAddress:X16} rax=0x{cpuContext[CpuRegister.Rax]:X16}"); } - if (_cpuContext[CpuRegister.Rax] == 0uL) + if (cpuContext[CpuRegister.Rax] == 0uL) { return OrbisGen2Result.ORBIS_GEN2_OK; } @@ -737,7 +832,7 @@ public sealed partial class DirectExecutionBackend private bool TryReadAsciiZ(ulong address, int maxLength, out string value) { value = string.Empty; - if (_cpuContext == null || address == 0L || maxLength <= 0) + if (ActiveCpuContext == null || address == 0L || maxLength <= 0) { return false; } @@ -762,11 +857,12 @@ public sealed partial class DirectExecutionBackend private bool TryReadByteCompat(ulong address, Span destination) { - if (_cpuContext == null || destination.Length == 0) + var cpuContext = ActiveCpuContext; + if (cpuContext == null || destination.Length == 0) { return false; } - if (_cpuContext.Memory.TryRead(address, destination)) + if (cpuContext.Memory.TryRead(address, destination)) { return true; } @@ -781,13 +877,38 @@ public sealed partial class DirectExecutionBackend } } - private bool TryWriteUInt64Compat(ulong address, ulong value) + private bool TryReadUInt64Compat(ulong address, out ulong value) { - if (_cpuContext == null || address == 0L) + value = 0; + var cpuContext = ActiveCpuContext; + if (cpuContext == null || address == 0L) { return false; } - if (_cpuContext.TryWriteUInt64(address, value)) + if (cpuContext.TryReadUInt64(address, out value)) + { + return true; + } + try + { + value = unchecked((ulong)Marshal.ReadInt64((nint)address)); + return true; + } + catch + { + value = 0; + return false; + } + } + + private bool TryWriteUInt64Compat(ulong address, ulong value) + { + var cpuContext = ActiveCpuContext; + if (cpuContext == null || address == 0L) + { + return false; + } + if (cpuContext.TryWriteUInt64(address, value)) { return true; } @@ -804,7 +925,8 @@ public sealed partial class DirectExecutionBackend private void TryBypassStackChkFailTrap(long dispatchIndex, ulong returnRip) { - if (_cpuContext == null || returnRip < 32) + var cpuContext = ActiveCpuContext; + if (cpuContext == null || returnRip < 32) { return; } @@ -818,8 +940,8 @@ public sealed partial class DirectExecutionBackend return; } ulong value = returnRip - 21; - ulong address = _cpuContext[CpuRegister.Rsp]; - if (_cpuContext.TryWriteUInt64(address, value)) + ulong address = cpuContext[CpuRegister.Rsp]; + if (cpuContext.TryWriteUInt64(address, value)) { if (_stackChkBypassSites.Add(num) && TryPatchStackChkFailBranch(num)) { diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs index afa736a..9cfff64 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.cs @@ -20,6 +20,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private const int ImportLoopWideDiversityWindow = 768; + private const int DefaultImportLoopGuardSeconds = 5; + private readonly struct ImportStubEntry { public ulong Address { get; } @@ -98,6 +100,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private const ulong GuestThreadTlsSize = 0x0001_0000UL; + private const ulong GuestThreadTlsPrefixSize = 0x0000_1000UL; + private const ulong GuestThreadRegionStride = 0x0100_0000UL; private const uint PAGE_EXECUTE_READWRITE = 64u; @@ -122,6 +126,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private bool _ownsTlsBaseAddress; + private uint _guestTlsBaseTlsIndex = uint.MaxValue; + + private uint _hostRspSlotTlsIndex = uint.MaxValue; + + private nint _tlsGetValueAddress; + private int _tlsPatchStubOffset; private nint _unresolvedReturnStub; @@ -142,6 +152,24 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private CpuContext? _cpuContext; + [ThreadStatic] + private static DirectExecutionBackend? _activeExecutionBackend; + + [ThreadStatic] + private static CpuContext? _activeCpuContext; + + [ThreadStatic] + private static ulong _activeEntryReturnSentinelRip; + + [ThreadStatic] + private static bool _activeForcedGuestExit; + + [ThreadStatic] + private static bool _activeGuestThreadYieldRequested; + + [ThreadStatic] + private static string? _activeGuestThreadYieldReason; + private ImportStubEntry[] _importEntries = Array.Empty(); private readonly List _importHandlerTrampolines = new List(); @@ -182,6 +210,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private readonly HashSet _contextualUnresolvedReturnSites = new HashSet(); + private readonly object _lazyCommitRangeGate = new object(); + + private readonly List _prtLazyCommitRanges = new List(); + private ulong _returnFallbackTarget; private static int _rawSentinelRecoveries; @@ -210,6 +242,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private int _importLoopPatternHits; + private long _importLoopPatternStartTimestamp; + private readonly Dictionary _importNidHashCache = new Dictionary(StringComparer.Ordinal); private enum GuestThreadRunState @@ -244,8 +278,91 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I public GuestThreadRunState State { get; set; } public string? BlockReason { get; set; } + + public Thread? HostThread { get; set; } + + public GuestContinuationRunner? ContinuationRunner { get; set; } } + private sealed class GuestContinuationRunner : IDisposable + { + private readonly ulong _guestThreadHandle; + private readonly object _runGate = new(); + private readonly AutoResetEvent _workAvailable = new(false); + private readonly AutoResetEvent _workCompleted = new(false); + private readonly Thread _thread; + private Action? _work; + private volatile bool _stopping; + + public GuestContinuationRunner(ulong guestThreadHandle) + { + _guestThreadHandle = guestThreadHandle; + _thread = new Thread(ThreadMain) + { + IsBackground = true, + Name = $"GuestContinuation-{guestThreadHandle:X}", + Priority = ThreadPriority.BelowNormal, + }; + _thread.Start(); + } + + public bool IsCurrentThread => ReferenceEquals(Thread.CurrentThread, _thread); + + public void Run(Action work) + { + lock (_runGate) + { + _work = work; + _workAvailable.Set(); + _workCompleted.WaitOne(); + _work = null; + } + } + + private void ThreadMain() + { + var previousGuestThreadHandle = GuestThreadExecution.EnterGuestThread(_guestThreadHandle); + try + { + while (true) + { + _workAvailable.WaitOne(); + if (_stopping) + { + return; + } + + try + { + _work?.Invoke(); + } + finally + { + _workCompleted.Set(); + } + } + } + finally + { + GuestThreadExecution.RestoreGuestThread(previousGuestThreadHandle); + } + } + + public void Dispose() + { + _stopping = true; + _workAvailable.Set(); + if (!IsCurrentThread) + { + _thread.Join(500); + } + _workAvailable.Dispose(); + _workCompleted.Dispose(); + } + } + + private readonly record struct LazyCommitRange(ulong BaseAddress, ulong Size); + private readonly object _guestThreadGate = new object(); private readonly Queue _readyGuestThreads = new Queue(); @@ -284,9 +401,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private delegate ulong ImportGatewayDelegate(nint backendHandle, int importIndex, nint argPackPtr); private delegate int RawExceptionHandlerDelegate(void* exceptionInfo); - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate int NativeEntryDelegate(); - private static readonly ImportGatewayDelegate ImportGatewayDelegateInstance = ImportDispatchGatewayManaged; private static readonly RawExceptionHandlerDelegate RawVectoredHandlerDelegateInstance = RawVectoredHandlerManaged; private static readonly RawExceptionHandlerDelegate RawUnhandledFilterDelegateInstance = RawUnhandledFilterManaged; @@ -347,6 +461,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I [ThreadStatic] private static int _vectoredHandlerDepth; + private static int _nestedVehTraceCount; + private const uint MEM_COMMIT = 4096u; private const uint MEM_RESERVE = 8192u; @@ -363,6 +479,16 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I private const uint PAGE_NOACCESS = 1u; + private const uint DBG_PRINTEXCEPTION_C = 0x40010006u; + + private const uint DBG_PRINTEXCEPTION_WIDE_C = 0x4001000Au; + + private const uint MS_VC_THREADNAME_EXCEPTION = 0x406D1388u; + + private const uint MSVC_CPP_EXCEPTION = 0xE06D7363u; + + private const uint HostXmmSaveAreaSize = 0xA0u; + public string BackendName => "native-backend"; public string? LastError { get; private set; } @@ -372,6 +498,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return *(ulong*)((byte*)contextRecord + offset); } + private unsafe static int CallNativeEntry(void* entry) + { + var nativeEntry = (delegate* unmanaged[Cdecl])entry; + return nativeEntry(); + } + private unsafe static void WriteCtxU64(void* contextRecord, int offset, ulong value) { *(ulong*)((byte*)contextRecord + offset) = value; @@ -387,11 +519,107 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I *(uint*)((byte*)contextRecord + offset) = value; } + private bool HasActiveExecutionThread => ReferenceEquals(_activeExecutionBackend, this); + + private CpuContext? ActiveCpuContext => HasActiveExecutionThread ? _activeCpuContext : _cpuContext; + + private ulong ActiveEntryReturnSentinelRip + { + get => HasActiveExecutionThread ? _activeEntryReturnSentinelRip : _entryReturnSentinelRip; + set + { + if (HasActiveExecutionThread) + { + _activeEntryReturnSentinelRip = value; + } + else + { + _entryReturnSentinelRip = value; + } + } + } + + private bool ActiveForcedGuestExit + { + get => HasActiveExecutionThread ? _activeForcedGuestExit : _forcedGuestExit; + set + { + if (HasActiveExecutionThread) + { + _activeForcedGuestExit = value; + } + else + { + _forcedGuestExit = value; + } + } + } + + private bool ActiveGuestThreadYieldRequested + { + get => HasActiveExecutionThread ? _activeGuestThreadYieldRequested : _guestThreadYieldRequested; + set + { + if (HasActiveExecutionThread) + { + _activeGuestThreadYieldRequested = value; + } + else + { + _guestThreadYieldRequested = value; + } + } + } + + private string? ActiveGuestThreadYieldReason + { + get => HasActiveExecutionThread ? _activeGuestThreadYieldReason : _guestThreadYieldReason; + set + { + if (HasActiveExecutionThread) + { + _activeGuestThreadYieldReason = value; + } + else + { + _guestThreadYieldReason = value; + } + } + } + + private static void RestoreActiveExecutionThread( + DirectExecutionBackend? previousBackend, + CpuContext? previousContext, + ulong previousSentinel, + bool previousForcedExit, + bool previousYieldRequested, + string? previousYieldReason) + { + _activeExecutionBackend = previousBackend; + _activeCpuContext = previousContext; + _activeEntryReturnSentinelRip = previousSentinel; + _activeForcedGuestExit = previousForcedExit; + _activeGuestThreadYieldRequested = previousYieldRequested; + _activeGuestThreadYieldReason = previousYieldReason; + } + public unsafe DirectExecutionBackend(IModuleManager moduleManager) { _moduleManager = moduleManager ?? throw new ArgumentNullException("moduleManager"); _selfHandle = GCHandle.Alloc(this); _selfHandlePtr = GCHandle.ToIntPtr(_selfHandle); + _guestTlsBaseTlsIndex = TlsAlloc(); + _hostRspSlotTlsIndex = TlsAlloc(); + if (_guestTlsBaseTlsIndex == uint.MaxValue || _hostRspSlotTlsIndex == uint.MaxValue) + { + throw new OutOfMemoryException("Failed to allocate native TLS slots"); + } + nint kernel32 = GetModuleHandle("kernel32.dll"); + _tlsGetValueAddress = kernel32 != 0 ? GetProcAddress(kernel32, "TlsGetValue") : 0; + if (_tlsGetValueAddress == 0) + { + throw new InvalidOperationException("Failed to resolve kernel32!TlsGetValue"); + } _tlsBaseAddress = (nint)VirtualAlloc(null, 4096u, 12288u, 4u); if (_tlsBaseAddress == 0) { @@ -437,7 +665,12 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I _importLoopSignatureCount = 0; _importLoopSignatureWriteIndex = 0; _importLoopPatternHits = 0; + _importLoopPatternStartTimestamp = 0; _importNidHashCache.Clear(); + lock (_lazyCommitRangeGate) + { + _prtLazyCommitRanges.Clear(); + } ClearGuestThreads(); _contextualUnresolvedReturnSites.Clear(); _stallWatchdogTriggered = 0; @@ -709,17 +942,17 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I { num = _tlsBaseAddress; } - if (num != _tlsBaseAddress) + if (!HasActiveExecutionThread && num != _tlsBaseAddress) { _tlsBaseAddress = num; _ownsTlsBaseAddress = _tlsBaseAddress == _ownedTlsBaseAddress; } - if (_tlsBaseAddress != 0) + if (num != 0) { - context.FsBase = (ulong)_tlsBaseAddress; - context.GsBase = (ulong)_tlsBaseAddress; - SeedTlsLayout(_tlsBaseAddress); - UpdateTlsHandlerBase(_tlsBaseAddress); + context.FsBase = (ulong)num; + context.GsBase = (ulong)num; + SeedTlsLayout(num); + TlsSetValue(_guestTlsBaseTlsIndex, num); } } @@ -727,7 +960,10 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I { ulong num = (ulong)tlsBase; *(ulong*)tlsBase = num; - *(ulong*)(tlsBase + 16) = num; + if (*(ulong*)(tlsBase + 16) == 0) + { + *(ulong*)(tlsBase + 16) = num; + } *(long*)(tlsBase + 40) = -4548986510476657986L; *(ulong*)(tlsBase + 96) = num; } @@ -786,26 +1022,29 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I ptr2[num++] = 82; ptr2[num++] = 86; ptr2[num++] = 87; - ptr2[num++] = 72; - ptr2[num++] = 185; - *(long*)(ptr2 + num) = _selfHandlePtr; - num += 8; - ptr2[num++] = 186; - *(int*)(ptr2 + num) = importIndex; - num += 4; - ptr2[num++] = 73; - ptr2[num++] = 137; - ptr2[num++] = 224; ptr2[num++] = 73; ptr2[num++] = 137; ptr2[num++] = 228; - ptr2[num++] = 73; - ptr2[num++] = 187; - *(long*)(ptr2 + num) = _hostRspSlotStorage; + ptr2[num++] = 72; + ptr2[num++] = 131; + ptr2[num++] = 236; + ptr2[num++] = 40; + ptr2[num++] = 185; + *(uint*)(ptr2 + num) = _hostRspSlotTlsIndex; + num += 4; + ptr2[num++] = 72; + ptr2[num++] = 184; + *(long*)(ptr2 + num) = _tlsGetValueAddress; num += 8; - ptr2[num++] = 77; - ptr2[num++] = 139; - ptr2[num++] = 27; + ptr2[num++] = byte.MaxValue; + ptr2[num++] = 208; + ptr2[num++] = 72; + ptr2[num++] = 131; + ptr2[num++] = 196; + ptr2[num++] = 40; + ptr2[num++] = 73; + ptr2[num++] = 137; + ptr2[num++] = 195; ptr2[num++] = 73; ptr2[num++] = 139; ptr2[num++] = 35; @@ -814,6 +1053,16 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I ptr2[num++] = 236; ptr2[num++] = 40; ptr2[num++] = 72; + ptr2[num++] = 185; + *(long*)(ptr2 + num) = _selfHandlePtr; + num += 8; + ptr2[num++] = 186; + *(int*)(ptr2 + num) = importIndex; + num += 4; + ptr2[num++] = 77; + ptr2[num++] = 137; + ptr2[num++] = 224; + ptr2[num++] = 72; ptr2[num++] = 184; *(long*)(ptr2 + num) = ImportGatewayPtr; num += 8; @@ -949,9 +1198,22 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I byte* tlsHandlerAddress = (byte*)_tlsHandlerAddress; int num = 0; tlsHandlerAddress[num++] = 72; + tlsHandlerAddress[num++] = 131; + tlsHandlerAddress[num++] = 236; + tlsHandlerAddress[num++] = 40; + tlsHandlerAddress[num++] = 185; + *(uint*)(tlsHandlerAddress + num) = _guestTlsBaseTlsIndex; + num += 4; + tlsHandlerAddress[num++] = 72; tlsHandlerAddress[num++] = 184; - *(long*)(tlsHandlerAddress + num) = _tlsBaseAddress; + *(long*)(tlsHandlerAddress + num) = _tlsGetValueAddress; num += 8; + tlsHandlerAddress[num++] = byte.MaxValue; + tlsHandlerAddress[num++] = 208; + tlsHandlerAddress[num++] = 72; + tlsHandlerAddress[num++] = 131; + tlsHandlerAddress[num++] = 196; + tlsHandlerAddress[num++] = 40; tlsHandlerAddress[num++] = 195; _tlsPatchStubOffset = (num + 15) & ~15; uint num2 = default(uint); @@ -1423,6 +1685,65 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I } } + private void RegisterPrtLazyCommitRange(ulong baseAddress, ulong size) + { + if (size == 0) + { + return; + } + + bool added = false; + lock (_lazyCommitRangeGate) + { + if (!_prtLazyCommitRanges.Any(range => range.BaseAddress == baseAddress && range.Size == size)) + { + _prtLazyCommitRanges.Add(new LazyCommitRange(baseAddress, size)); + added = true; + } + } + + if (added) + { + Console.Error.WriteLine($"[LOADER][TRACE] registered PRT lazy range: base=0x{baseAddress:X16} size=0x{size:X16}"); + } + } + + private bool IsGuestOwnedLazyCommitAddress(ulong address, out string owner) + { + var cpuContext = ActiveCpuContext; + if (cpuContext != null && TryGetVirtualMemory(cpuContext, out var virtualMemory)) + { + foreach (var region in virtualMemory.SnapshotRegions()) + { + if (ContainsAddress(region.VirtualAddress, region.MemorySize, address)) + { + owner = $"vmem:0x{region.VirtualAddress:X16}+0x{region.MemorySize:X}"; + return true; + } + } + } + + lock (_lazyCommitRangeGate) + { + foreach (var range in _prtLazyCommitRanges) + { + if (ContainsAddress(range.BaseAddress, range.Size, address)) + { + owner = $"prt:0x{range.BaseAddress:X16}+0x{range.Size:X}"; + return true; + } + } + } + + owner = string.Empty; + return false; + } + + private static bool ContainsAddress(ulong baseAddress, ulong size, ulong address) + { + return size != 0 && address >= baseAddress && address - baseAddress < size; + } + public bool TryStartThread(CpuContext creatorContext, GuestThreadStartRequest request, out string? error) { error = null; @@ -1442,12 +1763,14 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I } Console.Error.WriteLine( $"[LOADER][INFO] Scheduled guest thread '{thread.Name}' handle=0x{thread.ThreadHandle:X16} entry=0x{thread.EntryPoint:X16} arg=0x{thread.Argument:X16}"); + Pump(creatorContext, "pthread_create"); return true; } public void Pump(CpuContext callerContext, string reason) { _ = callerContext; + var runSynchronously = string.Equals(reason, "entry_return", StringComparison.Ordinal); if (_guestThreadPumpDepth != 0) { return; @@ -1475,7 +1798,25 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I { return; } - RunGuestThread(thread, reason); + + if (runSynchronously) + { + RunGuestThread(thread, reason); + continue; + } + + var hostThread = new Thread(() => RunGuestThread(thread, reason)) + { + IsBackground = true, + Name = $"SharpEmu-{thread.Name}", + Priority = ThreadPriority.BelowNormal, + }; + lock (_guestThreadGate) + { + thread.HostThread = hostThread; + } + hostThread.Start(); + return; } } finally @@ -1484,13 +1825,292 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I } } - private void ClearGuestThreads() + private void PumpUntilGuestThreadsIdle(CpuContext callerContext, string reason) + { + while (!ActiveForcedGuestExit) + { + Pump(callerContext, reason); + + var threads = SnapshotGuestThreads(); + if (threads.Length == 0) + { + return; + } + + var hasReadyThread = false; + var hasBlockedThread = false; + foreach (var thread in threads) + { + switch (thread.State) + { + case GuestThreadRunState.Ready: + hasReadyThread = true; + break; + case GuestThreadRunState.Running: + hasReadyThread = true; + break; + case GuestThreadRunState.Blocked: + hasBlockedThread = true; + break; + } + } + + if (hasReadyThread) + { + continue; + } + + if (!hasBlockedThread) + { + return; + } + + Thread.Sleep(1); + } + } + + private GuestThreadState[] SnapshotGuestThreads() { lock (_guestThreadGate) { + return _guestThreads.Values.ToArray(); + } + } + + public bool TryCallGuestFunction( + CpuContext callerContext, + ulong entryPoint, + ulong arg0, + ulong arg1, + ulong stackAddress, + ulong stackSize, + string reason, + out string? error) + { + error = null; + if (entryPoint < 65536) + { + error = $"invalid guest callback entry=0x{entryPoint:X16}"; + return false; + } + if (!TryGetVirtualMemory(callerContext, out var virtualMemory)) + { + error = "caller context memory is not backed by IVirtualMemory"; + return false; + } + + ulong callbackStackBase; + ulong callbackStackSize; + if (stackAddress != 0 && stackSize >= 0x100) + { + callbackStackBase = stackAddress; + callbackStackSize = stackSize; + } + else + { + if (!TryMapGuestThreadRegion(virtualMemory, GuestThreadStackBaseAddress, GuestThreadStackSize, ProgramHeaderFlags.Read | ProgramHeaderFlags.Write, out callbackStackBase, out error)) + { + return false; + } + callbackStackSize = GuestThreadStackSize; + } + + var trackedMemory = new TrackedCpuMemory(virtualMemory); + var fallbackTlsBase = unchecked((ulong)_tlsBaseAddress); + var context = new CpuContext(trackedMemory, callerContext.TargetGeneration) + { + Rip = entryPoint, + Rflags = 0x202, + FsBase = callerContext.FsBase != 0 ? callerContext.FsBase : fallbackTlsBase, + GsBase = callerContext.GsBase != 0 ? callerContext.GsBase : fallbackTlsBase, + }; + context[CpuRegister.Rsp] = AlignDown(callbackStackBase + callbackStackSize, 16) - sizeof(ulong); + context[CpuRegister.Rdi] = arg0; + context[CpuRegister.Rsi] = arg1; + context[CpuRegister.Rdx] = 0; + context[CpuRegister.Rcx] = 0; + context[CpuRegister.R8] = 0; + context[CpuRegister.R9] = 0; + if (!InitializeGuestThreadFrame(context)) + { + error = "failed to initialize guest callback stack"; + return false; + } + + var previousLastError = LastError; + try + { + LastError = null; + var exitReason = ExecuteGuestThreadEntry(context, entryPoint, reason, out var callbackReason); + if (exitReason is GuestNativeCallExitReason.Exception or GuestNativeCallExitReason.ForcedExit) + { + error = callbackReason ?? LastError ?? "guest callback failed"; + return false; + } + + return true; + } + finally + { + LastError = previousLastError; + } + } + + public bool TryCallGuestContinuation( + CpuContext callerContext, + GuestCpuContinuation continuation, + string reason, + out string? error) + { + error = null; + if (continuation.Rip < 65536 || continuation.Rsp == 0) + { + error = $"invalid guest continuation rip=0x{continuation.Rip:X16} rsp=0x{continuation.Rsp:X16}"; + return false; + } + if (!TryGetVirtualMemory(callerContext, out var virtualMemory)) + { + error = "caller context memory is not backed by IVirtualMemory"; + return false; + } + + var trackedMemory = new TrackedCpuMemory(virtualMemory); + var fallbackTlsBase = unchecked((ulong)_tlsBaseAddress); + var context = new CpuContext(trackedMemory, callerContext.TargetGeneration) + { + 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), + }; + + context[CpuRegister.Rax] = continuation.Rax; + context[CpuRegister.Rcx] = continuation.Rcx; + context[CpuRegister.Rdx] = continuation.Rdx; + context[CpuRegister.Rbx] = continuation.Rbx; + context[CpuRegister.Rbp] = continuation.Rbp; + context[CpuRegister.Rsi] = continuation.Rsi; + context[CpuRegister.Rdi] = continuation.Rdi; + context[CpuRegister.R8] = continuation.R8; + context[CpuRegister.R9] = continuation.R9; + context[CpuRegister.R12] = continuation.R12; + context[CpuRegister.R13] = continuation.R13; + context[CpuRegister.R14] = continuation.R14; + context[CpuRegister.R15] = continuation.R15; + context[CpuRegister.Rsp] = continuation.Rsp + 16uL; + + var currentGuestThreadHandle = GuestThreadExecution.CurrentGuestThreadHandle; + var exitReason = GuestNativeCallExitReason.Exception; + string? callbackReason = null; + string? callbackLastError = null; + Exception? callbackException = null; + + void RunContinuation() + { + var previousLastError = LastError; + try + { + LastError = null; + exitReason = ExecuteGuestContinuationEntry(context, continuation.Rip, reason, out callbackReason); + callbackLastError = LastError; + } + catch (Exception ex) + { + callbackException = ex; + callbackReason = ex.GetType().Name + ": " + ex.Message; + exitReason = GuestNativeCallExitReason.Exception; + } + finally + { + LastError = previousLastError; + } + } + + if (currentGuestThreadHandle != 0) + { + GuestContinuationRunner? runner; + lock (_guestThreadGate) + { + if (_guestThreads.TryGetValue(currentGuestThreadHandle, out var guestThread)) + { + runner = guestThread.ContinuationRunner ??= new GuestContinuationRunner(currentGuestThreadHandle); + } + else + { + runner = null; + } + } + + if (runner is not null && !runner.IsCurrentThread) + { + runner.Run(RunContinuation); + } + else + { + RunContinuationOnTemporaryThread(currentGuestThreadHandle, RunContinuation); + } + } + else + { + RunContinuation(); + } + + if (callbackException is not null) + { + error = callbackReason ?? callbackException.Message; + return false; + } + + if (exitReason is GuestNativeCallExitReason.Exception or GuestNativeCallExitReason.ForcedExit) + { + error = callbackReason ?? callbackLastError ?? "guest continuation failed"; + return false; + } + + return true; + } + + private static void RunContinuationOnTemporaryThread(ulong guestThreadHandle, Action continuation) + { + var continuationThread = new Thread(() => + { + var previousGuestThreadHandle = GuestThreadExecution.EnterGuestThread(guestThreadHandle); + try + { + continuation(); + } + finally + { + GuestThreadExecution.RestoreGuestThread(previousGuestThreadHandle); + } + }) + { + IsBackground = true, + Name = $"GuestContinuationNested-{guestThreadHandle:X}", + Priority = ThreadPriority.BelowNormal, + }; + continuationThread.Start(); + continuationThread.Join(); + } + + private void ClearGuestThreads() + { + GuestContinuationRunner[] runners; + lock (_guestThreadGate) + { + runners = _guestThreads.Values + .Select(static thread => thread.ContinuationRunner) + .Where(static runner => runner is not null) + .Cast() + .ToArray(); _readyGuestThreads.Clear(); _guestThreads.Clear(); } + + foreach (var runner in runners) + { + runner.Dispose(); + } } private bool TryCreateGuestThreadState(CpuContext creatorContext, GuestThreadStartRequest request, out GuestThreadState thread, out string? error) @@ -1505,7 +2125,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I { return false; } - if (!TryMapGuestThreadRegion(virtualMemory, GuestThreadTlsBaseAddress, GuestThreadTlsSize, ProgramHeaderFlags.Read | ProgramHeaderFlags.Write, out var tlsBase, out error)) + if (!TryMapGuestThreadTlsRegion(virtualMemory, out var tlsBase, out error)) { return false; } @@ -1525,7 +2145,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I context[CpuRegister.Rcx] = 0; context[CpuRegister.R8] = 0; context[CpuRegister.R9] = 0; - if (!InitializeGuestThreadFrame(context) || !InitializeGuestThreadTls(context, tlsBase)) + if (!InitializeGuestThreadFrame(context) || !InitializeGuestThreadTls(context, tlsBase, request.ThreadHandle)) { error = "failed to initialize guest thread stack/TLS"; return false; @@ -1598,6 +2218,42 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return false; } + private static bool TryMapGuestThreadTlsRegion( + IVirtualMemory virtualMemory, + out ulong tlsBase, + out string? error) + { + for (int i = 0; i < 64; i++) + { + var candidateBase = GuestThreadTlsBaseAddress - ((ulong)i * GuestThreadRegionStride); + var mappedBase = candidateBase - GuestThreadTlsPrefixSize; + var mappedSize = GuestThreadTlsSize + GuestThreadTlsPrefixSize; + if (!IsGuestThreadRegionFree(virtualMemory, mappedBase, mappedSize)) + { + continue; + } + try + { + virtualMemory.Map( + mappedBase, + mappedSize, + fileOffset: 0, + fileData: ReadOnlySpan.Empty, + protection: ProgramHeaderFlags.Read | ProgramHeaderFlags.Write); + tlsBase = candidateBase; + error = null; + return true; + } + catch (InvalidOperationException) + { + } + } + + tlsBase = 0; + error = $"failed to map guest TLS region near 0x{GuestThreadTlsBaseAddress:X16}"; + return false; + } + private static bool IsGuestThreadRegionFree(IVirtualMemory virtualMemory, ulong candidateBase, ulong size) { var candidateEnd = candidateBase + size; @@ -1631,29 +2287,24 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return true; } - private static bool InitializeGuestThreadTls(CpuContext context, ulong tlsBase) + private static bool InitializeGuestThreadTls(CpuContext context, ulong tlsBase, ulong threadHandle) { - return context.TryWriteUInt64(tlsBase + 0x00, tlsBase) && - context.TryWriteUInt64(tlsBase + 0x10, tlsBase) && - context.TryWriteUInt64(tlsBase + 0x28, 0xC0DEC0DECAFEBABEUL); + return context.TryWriteUInt64(tlsBase - 0xF0, 0) && + context.TryWriteUInt64(tlsBase + 0x00, tlsBase) && + context.TryWriteUInt64(tlsBase + 0x10, threadHandle) && + context.TryWriteUInt64(tlsBase + 0x28, 0xC0DEC0DECAFEBABEUL) && + context.TryWriteUInt64(tlsBase + 0x60, tlsBase); } private void RunGuestThread(GuestThreadState thread, string reason) { Console.Error.WriteLine( $"[LOADER][INFO] Pumping guest thread '{thread.Name}' reason={reason} entry=0x{thread.EntryPoint:X16}"); - var previousContext = _cpuContext; - var previousTlsBase = _tlsBaseAddress; - var previousOwnsTlsBase = _ownsTlsBaseAddress; - var previousForcedGuestExit = _forcedGuestExit; var previousLastError = LastError; var previousGuestThreadHandle = GuestThreadExecution.EnterGuestThread(thread.ThreadHandle); try { - _cpuContext = thread.Context; - _forcedGuestExit = false; LastError = null; - BindTlsBase(thread.Context); var exitReason = ExecuteGuestThreadEntry(thread.Context, thread.EntryPoint, thread.Name, out var blockReason); lock (_guestThreadGate) { @@ -1678,15 +2329,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I finally { GuestThreadExecution.RestoreGuestThread(previousGuestThreadHandle); - _cpuContext = previousContext; - _tlsBaseAddress = previousTlsBase; - _ownsTlsBaseAddress = previousOwnsTlsBase; - if (_tlsBaseAddress != 0) - { - SeedTlsLayout(_tlsBaseAddress); - UpdateTlsHandlerBase(_tlsBaseAddress); - } - _forcedGuestExit = previousForcedGuestExit; LastError = previousLastError; } } @@ -1699,25 +2341,45 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I reason = "guest thread stack pointer is zero"; return GuestNativeCallExitReason.Exception; } - void* ptr = VirtualAlloc(null, 256u, 12288u, 64u); + const uint stubSize = 512u; + void* ptr = VirtualAlloc(null, stubSize, 12288u, 64u); if (ptr == null) { reason = "failed to allocate executable memory for guest thread stub"; return GuestNativeCallExitReason.Exception; } - var previousSentinel = _entryReturnSentinelRip; - var previousHostRspSlotValue = _hostRspSlotStorage != 0 ? *(ulong*)_hostRspSlotStorage : 0; - var previousYieldRequested = _guestThreadYieldRequested; - var previousYieldReason = _guestThreadYieldReason; + var previousActiveBackend = _activeExecutionBackend; + var previousActiveContext = _activeCpuContext; + var previousSentinel = _activeEntryReturnSentinelRip; + var previousForcedExit = _activeForcedGuestExit; + var previousYieldRequested = _activeGuestThreadYieldRequested; + var previousYieldReason = _activeGuestThreadYieldReason; + nint previousHostRspSlotValue = TlsGetValue(_hostRspSlotTlsIndex); try { + _activeExecutionBackend = this; + _activeCpuContext = context; + _activeEntryReturnSentinelRip = 0; + _activeForcedGuestExit = false; + _activeGuestThreadYieldRequested = false; + _activeGuestThreadYieldReason = null; + BindTlsBase(context); byte* ptr2 = (byte*)ptr; - ulong hostRspSlot = (ulong)ptr + 224uL; + ulong hostRspSlot = (ulong)ptr + stubSize - 16uL; int offset = 0; ptr2[offset++] = 83; ptr2[offset++] = 85; ptr2[offset++] = 87; ptr2[offset++] = 86; + ptr2[offset++] = 65; + ptr2[offset++] = 84; + ptr2[offset++] = 65; + ptr2[offset++] = 85; + ptr2[offset++] = 65; + ptr2[offset++] = 86; + ptr2[offset++] = 65; + ptr2[offset++] = 87; + EmitHostNonvolatileXmmSave(ptr2, ref offset); ptr2[offset++] = 73; ptr2[offset++] = 186; *(ulong*)(ptr2 + offset) = hostRspSlot; @@ -1786,36 +2448,42 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I ptr2[offset++] = 73; ptr2[offset++] = 139; ptr2[offset++] = 34; + EmitHostNonvolatileXmmRestore(ptr2, ref offset); + ptr2[offset++] = 65; + ptr2[offset++] = 95; + ptr2[offset++] = 65; + ptr2[offset++] = 94; + ptr2[offset++] = 65; + ptr2[offset++] = 93; + ptr2[offset++] = 65; + ptr2[offset++] = 92; ptr2[offset++] = 94; ptr2[offset++] = 95; ptr2[offset++] = 93; ptr2[offset++] = 91; ptr2[offset++] = 195; ulong sentinel = (ulong)ptr + (ulong)sentinelOffset; - _entryReturnSentinelRip = sentinel; + ActiveEntryReturnSentinelRip = sentinel; if (!context.TryWriteUInt64(context[CpuRegister.Rsp], sentinel)) { reason = $"failed to patch guest thread return sentinel at 0x{context[CpuRegister.Rsp]:X16}"; return GuestNativeCallExitReason.Exception; } uint oldProtect = default(uint); - VirtualProtect(ptr, 256u, 64u, &oldProtect); - FlushInstructionCache(GetCurrentProcess(), ptr, 256u); - if (_hostRspSlotStorage != 0) - { - *(ulong*)_hostRspSlotStorage = hostRspSlot; - } - _guestThreadYieldRequested = false; - _guestThreadYieldReason = null; + VirtualProtect(ptr, stubSize, 64u, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), ptr, stubSize); + TlsSetValue(_hostRspSlotTlsIndex, (nint)hostRspSlot); + ActiveGuestThreadYieldRequested = false; + ActiveGuestThreadYieldReason = null; try { - var nativeReturn = Marshal.GetDelegateForFunctionPointer((nint)ptr)(); - if (_guestThreadYieldRequested) + var nativeReturn = CallNativeEntry(ptr); + if (ActiveGuestThreadYieldRequested) { - reason = _guestThreadYieldReason ?? "guest thread blocked"; + reason = ActiveGuestThreadYieldReason ?? "guest thread blocked"; return GuestNativeCallExitReason.Blocked; } - if (_forcedGuestExit) + if (ActiveForcedGuestExit) { reason = LastError ?? "guest thread forced exit"; return GuestNativeCallExitReason.ForcedExit; @@ -1836,13 +2504,162 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I } finally { - _entryReturnSentinelRip = previousSentinel; - if (_hostRspSlotStorage != 0) + TlsSetValue(_hostRspSlotTlsIndex, previousHostRspSlotValue); + RestoreActiveExecutionThread( + previousActiveBackend, + previousActiveContext, + previousSentinel, + previousForcedExit, + previousYieldRequested, + previousYieldReason); + VirtualFree(ptr, 0u, 32768u); + } + } + + private unsafe GuestNativeCallExitReason ExecuteGuestContinuationEntry(CpuContext context, ulong entryPoint, string name, out string? reason) + { + reason = null; + if (context[CpuRegister.Rsp] == 0) + { + reason = "guest thread stack pointer is zero"; + return GuestNativeCallExitReason.Exception; + } + const uint stubSize = 512u; + void* ptr = VirtualAlloc(null, stubSize, 12288u, 64u); + if (ptr == null) + { + reason = "failed to allocate executable memory for guest thread stub"; + return GuestNativeCallExitReason.Exception; + } + var previousActiveBackend = _activeExecutionBackend; + var previousActiveContext = _activeCpuContext; + var previousSentinel = _activeEntryReturnSentinelRip; + var previousForcedExit = _activeForcedGuestExit; + var previousYieldRequested = _activeGuestThreadYieldRequested; + var previousYieldReason = _activeGuestThreadYieldReason; + nint previousHostRspSlotValue = TlsGetValue(_hostRspSlotTlsIndex); + try + { + _activeExecutionBackend = this; + _activeCpuContext = context; + _activeEntryReturnSentinelRip = 0; + _activeForcedGuestExit = false; + _activeGuestThreadYieldRequested = false; + _activeGuestThreadYieldReason = null; + BindTlsBase(context); + byte* ptr2 = (byte*)ptr; + ulong hostRspSlot = (ulong)ptr + stubSize - 16uL; + int offset = 0; + + void Emit(byte value) => ptr2[offset++] = value; + void EmitU64(ulong value) { - *(ulong*)_hostRspSlotStorage = previousHostRspSlotValue; + *(ulong*)(ptr2 + offset) = value; + offset += sizeof(ulong); } - _guestThreadYieldRequested = previousYieldRequested; - _guestThreadYieldReason = previousYieldReason; + void EmitMovR64Imm(byte rex, byte opcode, ulong value) + { + Emit(rex); + Emit(opcode); + EmitU64(value); + } + + Emit(0x53); // push rbx + Emit(0x55); // push rbp + Emit(0x57); // push rdi + Emit(0x56); // push rsi + Emit(0x41); Emit(0x54); // push r12 + Emit(0x41); Emit(0x55); // push r13 + Emit(0x41); Emit(0x56); // push r14 + Emit(0x41); Emit(0x57); // push r15 + EmitHostNonvolatileXmmSave(ptr2, ref offset); + EmitMovR64Imm(0x49, 0xBA, hostRspSlot); // mov r10, hostRspSlot + 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 + EmitMovR64Imm(0x48, 0xBE, context[CpuRegister.Rsi]); // mov rsi, imm64 + EmitMovR64Imm(0x48, 0xBA, context[CpuRegister.Rdx]); // mov rdx, imm64 + EmitMovR64Imm(0x48, 0xB9, context[CpuRegister.Rcx]); // mov rcx, imm64 + EmitMovR64Imm(0x49, 0xB8, context[CpuRegister.R8]); // mov r8, imm64 + EmitMovR64Imm(0x49, 0xB9, context[CpuRegister.R9]); // mov r9, imm64 + EmitMovR64Imm(0x49, 0xBC, context[CpuRegister.R12]); // mov r12, imm64 + EmitMovR64Imm(0x49, 0xBD, context[CpuRegister.R13]); // mov r13, imm64 + EmitMovR64Imm(0x49, 0xBE, context[CpuRegister.R14]); // mov r14, imm64 + 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)) + { + reason = $"failed to patch guest continuation return sentinel at 0x{sentinelStackAddress:X16}"; + return GuestNativeCallExitReason.Exception; + } + uint oldProtect = default(uint); + VirtualProtect(ptr, stubSize, 64u, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), ptr, stubSize); + TlsSetValue(_hostRspSlotTlsIndex, (nint)hostRspSlot); + ActiveGuestThreadYieldRequested = false; + ActiveGuestThreadYieldReason = null; + try + { + var nativeReturn = CallNativeEntry(ptr); + if (ActiveGuestThreadYieldRequested) + { + reason = ActiveGuestThreadYieldReason ?? "guest thread blocked"; + return GuestNativeCallExitReason.Blocked; + } + if (ActiveForcedGuestExit) + { + reason = LastError ?? "guest thread forced exit"; + return GuestNativeCallExitReason.ForcedExit; + } + reason = $"returned 0x{nativeReturn:X8}"; + return GuestNativeCallExitReason.Returned; + } + catch (AccessViolationException ex) + { + reason = "access violation: " + ex.Message; + return GuestNativeCallExitReason.Exception; + } + catch (Exception ex) + { + reason = ex.GetType().Name + ": " + ex.Message; + return GuestNativeCallExitReason.Exception; + } + } + finally + { + TlsSetValue(_hostRspSlotTlsIndex, previousHostRspSlotValue); + RestoreActiveExecutionThread( + previousActiveBackend, + previousActiveContext, + previousSentinel, + previousForcedExit, + previousYieldRequested, + previousYieldReason); VirtualFree(ptr, 0u, 32768u); } } @@ -1856,6 +2673,55 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return value & ~(alignment - 1); } + private static unsafe void EmitByte(byte* code, ref int offset, byte value) + { + code[offset++] = value; + } + + private static unsafe void EmitUInt32(byte* code, ref int offset, uint value) + { + *(uint*)(code + offset) = value; + offset += sizeof(uint); + } + + private static unsafe void EmitHostNonvolatileXmmSave(byte* code, ref int offset) + { + EmitByte(code, ref offset, 0x48); + EmitByte(code, ref offset, 0x81); + EmitByte(code, ref offset, 0xEC); + EmitUInt32(code, ref offset, HostXmmSaveAreaSize); + for (int xmm = 6; xmm <= 15; xmm++) + { + EmitMovdquRspXmm(code, ref offset, store: true, xmm, (byte)((xmm - 6) * 16)); + } + } + + private static unsafe void EmitHostNonvolatileXmmRestore(byte* code, ref int offset) + { + for (int xmm = 6; xmm <= 15; xmm++) + { + EmitMovdquRspXmm(code, ref offset, store: false, xmm, (byte)((xmm - 6) * 16)); + } + EmitByte(code, ref offset, 0x48); + EmitByte(code, ref offset, 0x81); + EmitByte(code, ref offset, 0xC4); + EmitUInt32(code, ref offset, HostXmmSaveAreaSize); + } + + private static unsafe void EmitMovdquRspXmm(byte* code, ref int offset, bool store, int xmm, byte displacement) + { + EmitByte(code, ref offset, 0xF3); + if (xmm >= 8) + { + EmitByte(code, ref offset, 0x44); + } + 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); + } + private unsafe bool ExecuteEntry(CpuContext context, ulong entryPoint, out OrbisGen2Result result) { Console.Error.WriteLine($"[LOADER][INFO] ExecuteEntry starting at 0x{entryPoint:X16}"); @@ -1868,22 +2734,46 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return false; } Console.Error.WriteLine($"[LOADER][INFO] StackTop: 0x{num:X16}"); - void* ptr = VirtualAlloc(null, 256u, 12288u, 64u); + const uint stubSize = 512u; + void* ptr = VirtualAlloc(null, stubSize, 12288u, 64u); if (ptr == null) { LastError = "Failed to allocate executable memory for stub"; result = OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT; return false; } + var previousActiveBackend = _activeExecutionBackend; + var previousActiveContext = _activeCpuContext; + var previousSentinel = _activeEntryReturnSentinelRip; + var previousForcedExit = _activeForcedGuestExit; + var previousYieldRequested = _activeGuestThreadYieldRequested; + var previousYieldReason = _activeGuestThreadYieldReason; + nint previousHostRspSlotValue = TlsGetValue(_hostRspSlotTlsIndex); try { + _activeExecutionBackend = this; + _activeCpuContext = context; + _activeEntryReturnSentinelRip = 0; + _activeForcedGuestExit = false; + _activeGuestThreadYieldRequested = false; + _activeGuestThreadYieldReason = null; + BindTlsBase(context); byte* ptr2 = (byte*)ptr; - ulong num2 = (ulong)ptr + 224uL; + ulong num2 = (ulong)ptr + stubSize - 16uL; int num3 = 0; ptr2[num3++] = 83; ptr2[num3++] = 85; ptr2[num3++] = 87; ptr2[num3++] = 86; + ptr2[num3++] = 65; + ptr2[num3++] = 84; + ptr2[num3++] = 65; + ptr2[num3++] = 85; + ptr2[num3++] = 65; + ptr2[num3++] = 86; + ptr2[num3++] = 65; + ptr2[num3++] = 87; + EmitHostNonvolatileXmmSave(ptr2, ref num3); ptr2[num3++] = 73; ptr2[num3++] = 186; *(ulong*)(ptr2 + num3) = num2; @@ -1952,13 +2842,22 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I ptr2[num3++] = 73; ptr2[num3++] = 139; ptr2[num3++] = 34; + EmitHostNonvolatileXmmRestore(ptr2, ref num3); + ptr2[num3++] = 65; + ptr2[num3++] = 95; + ptr2[num3++] = 65; + ptr2[num3++] = 94; + ptr2[num3++] = 65; + ptr2[num3++] = 93; + ptr2[num3++] = 65; + ptr2[num3++] = 92; ptr2[num3++] = 94; ptr2[num3++] = 95; ptr2[num3++] = 93; ptr2[num3++] = 91; ptr2[num3++] = 195; ulong value = (ulong)ptr + (ulong)num4; - _entryReturnSentinelRip = value; + ActiveEntryReturnSentinelRip = value; if (!context.TryWriteUInt64(context[CpuRegister.Rsp], value)) { LastError = $"Failed to patch native return sentinel at 0x{context[CpuRegister.Rsp]:X16}"; @@ -1966,16 +2865,17 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I return false; } uint num5 = default(uint); - VirtualProtect(ptr, 256u, 64u, &num5); - FlushInstructionCache(GetCurrentProcess(), ptr, 256u); + VirtualProtect(ptr, stubSize, 64u, &num5); + FlushInstructionCache(GetCurrentProcess(), ptr, stubSize); if (_hostRspSlotStorage != 0) { *(ulong*)_hostRspSlotStorage = num2; } + TlsSetValue(_hostRspSlotTlsIndex, (nint)num2); if (string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_SENTINEL_PROBE"), "1", StringComparison.Ordinal)) { Console.Error.WriteLine("[LOADER][INFO] Running unresolved sentinel probe..."); - Marshal.GetDelegateForFunctionPointer((nint)65534)(); + CallNativeEntry((void*)65534); Console.Error.WriteLine("[LOADER][INFO] Sentinel probe returned."); } Console.Error.WriteLine("[LOADER][INFO] Calling guest entry..."); @@ -1983,9 +2883,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I int num6 = -1; try { - num6 = Marshal.GetDelegateForFunctionPointer((nint)ptr)(); + num6 = CallNativeEntry(ptr); Console.Error.WriteLine($"[LOADER][INFO] Guest returned: {num6}"); - Pump(context, "entry_return"); + PumpUntilGuestThreadsIdle(context, "entry_return"); } catch (AccessViolationException ex) { @@ -2002,7 +2902,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I LastError = "Exception during execution: " + ex2.GetType().Name + ": " + ex2.Message; num6 = -1; } - if (_forcedGuestExit) + if (ActiveForcedGuestExit) { result = OrbisGen2Result.ORBIS_GEN2_ERROR_CPU_TRAP; if (string.IsNullOrEmpty(LastError)) @@ -2029,11 +2929,19 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I finally { StopStallWatchdog(); - _entryReturnSentinelRip = 0uL; + ActiveEntryReturnSentinelRip = 0uL; + TlsSetValue(_hostRspSlotTlsIndex, previousHostRspSlotValue); if (_hostRspSlotStorage != 0) { *(long*)_hostRspSlotStorage = 0L; } + RestoreActiveExecutionThread( + previousActiveBackend, + previousActiveContext, + previousSentinel, + previousForcedExit, + previousYieldRequested, + previousYieldReason); VirtualFree(ptr, 0u, 32768u); } } @@ -2164,6 +3072,24 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I } + [DllImport("kernel32.dll")] + private static extern uint TlsAlloc(); + + [DllImport("kernel32.dll")] + private static extern bool TlsFree(uint dwTlsIndex); + + [DllImport("kernel32.dll")] + private static extern bool TlsSetValue(uint dwTlsIndex, nint lpTlsValue); + + [DllImport("kernel32.dll")] + private static extern nint TlsGetValue(uint dwTlsIndex); + + [DllImport("kernel32.dll", CharSet = CharSet.Unicode)] + private static extern nint GetModuleHandle(string lpModuleName); + + [DllImport("kernel32.dll", CharSet = CharSet.Ansi)] + private static extern nint GetProcAddress(nint hModule, string procName); + [DllImport("kernel32.dll")] private unsafe static extern void* AddVectoredExceptionHandler(uint first, IntPtr handler); @@ -2231,6 +3157,16 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I VirtualFree((void*)_hostRspSlotStorage, 0u, 32768u); _hostRspSlotStorage = 0; } + if (_guestTlsBaseTlsIndex != uint.MaxValue) + { + TlsFree(_guestTlsBaseTlsIndex); + _guestTlsBaseTlsIndex = uint.MaxValue; + } + if (_hostRspSlotTlsIndex != uint.MaxValue) + { + TlsFree(_hostRspSlotTlsIndex); + _hostRspSlotTlsIndex = uint.MaxValue; + } if (_unresolvedReturnStub != 0) { VirtualFree((void*)_unresolvedReturnStub, 0u, 32768u); diff --git a/src/SharpEmu.HLE/GuestThreadExecution.cs b/src/SharpEmu.HLE/GuestThreadExecution.cs index f42de73..e48ef2c 100644 --- a/src/SharpEmu.HLE/GuestThreadExecution.cs +++ b/src/SharpEmu.HLE/GuestThreadExecution.cs @@ -15,8 +15,49 @@ public interface IGuestThreadScheduler bool TryStartThread(CpuContext creatorContext, GuestThreadStartRequest request, out string? error); void Pump(CpuContext callerContext, string reason); + + bool TryCallGuestFunction( + CpuContext callerContext, + ulong entryPoint, + ulong arg0, + ulong arg1, + ulong stackAddress, + ulong stackSize, + string reason, + out string? error); + + bool TryCallGuestContinuation( + CpuContext callerContext, + GuestCpuContinuation continuation, + string reason, + out string? error); } +public readonly record struct GuestImportCallFrame( + bool IsValid, + ulong ReturnRip, + ulong ResumeRsp); + +public readonly record struct GuestCpuContinuation( + ulong Rip, + ulong Rsp, + ulong Rflags, + ulong FsBase, + ulong GsBase, + ulong Rax, + ulong Rcx, + ulong Rdx, + ulong Rbx, + ulong Rbp, + ulong Rsi, + ulong Rdi, + ulong R8, + ulong R9, + ulong R12, + ulong R13, + ulong R14, + ulong R15); + public static class GuestThreadExecution { [ThreadStatic] @@ -34,6 +75,15 @@ public static class GuestThreadExecution [ThreadStatic] private static string? _pendingEntryExitReason; + [ThreadStatic] + private static bool _hasCurrentImportCallFrame; + + [ThreadStatic] + private static ulong _currentImportReturnRip; + + [ThreadStatic] + private static ulong _currentImportResumeRsp; + public static IGuestThreadScheduler? Scheduler { get; set; } public static bool IsGuestThread => _currentGuestThreadHandle != 0; @@ -48,6 +98,9 @@ public static class GuestThreadExecution _pendingEntryExit = false; _pendingEntryExitStatus = 0; _pendingEntryExitReason = null; + _hasCurrentImportCallFrame = false; + _currentImportReturnRip = 0; + _currentImportResumeRsp = 0; return previous; } @@ -58,6 +111,9 @@ public static class GuestThreadExecution _pendingEntryExit = false; _pendingEntryExitStatus = 0; _pendingEntryExitReason = null; + _hasCurrentImportCallFrame = false; + _currentImportReturnRip = 0; + _currentImportResumeRsp = 0; } public static bool RequestCurrentThreadBlock(string reason) @@ -104,4 +160,35 @@ public static class GuestThreadExecution _pendingEntryExitReason = null; return true; } + + public static GuestImportCallFrame EnterImportCallFrame(ulong returnRip, ulong resumeRsp) + { + var previous = new GuestImportCallFrame( + _hasCurrentImportCallFrame, + _currentImportReturnRip, + _currentImportResumeRsp); + _hasCurrentImportCallFrame = true; + _currentImportReturnRip = returnRip; + _currentImportResumeRsp = resumeRsp; + return previous; + } + + public static void RestoreImportCallFrame(GuestImportCallFrame previous) + { + _hasCurrentImportCallFrame = previous.IsValid; + _currentImportReturnRip = previous.ReturnRip; + _currentImportResumeRsp = previous.ResumeRsp; + } + + public static bool TryGetCurrentImportCallFrame(out GuestImportCallFrame frame) + { + if (!_hasCurrentImportCallFrame) + { + frame = default; + return false; + } + + frame = new GuestImportCallFrame(true, _currentImportReturnRip, _currentImportResumeRsp); + return true; + } }