mirror of
https://github.com/par274/sharpemu.git
synced 2026-07-23 11:19:01 +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))
|
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}");
|
Console.Error.WriteLine($"[LOADER][INFO] Raw exception handler installed: 0x{_rawExceptionHandler:X16}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -29,12 +34,22 @@ public sealed partial class DirectExecutionBackend
|
|||||||
|
|
||||||
_handlerDelegate = VectoredHandler;
|
_handlerDelegate = VectoredHandler;
|
||||||
_handlerHandle = GCHandle.Alloc(_handlerDelegate);
|
_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}");
|
Console.Error.WriteLine($"[LOADER][INFO] Exception handler installed: 0x{_exceptionHandler:X16}");
|
||||||
|
|
||||||
_unhandledFilterDelegate = UnhandledExceptionFilter;
|
_unhandledFilterDelegate = UnhandledExceptionFilter;
|
||||||
_unhandledFilterHandle = GCHandle.Alloc(_unhandledFilterDelegate);
|
_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)
|
private unsafe int UnhandledExceptionFilter(void* exceptionInfo)
|
||||||
|
|||||||
@@ -143,6 +143,8 @@ public sealed partial class DirectExecutionBackend
|
|||||||
ProbeReturnRip(num7, num);
|
ProbeReturnRip(num7, num);
|
||||||
}
|
}
|
||||||
TrackStrlenPrelude(importStubEntry.Nid, num, num7);
|
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);
|
bool logBootstrap = string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_BOOTSTRAP"), "1", StringComparison.Ordinal);
|
||||||
if (logBootstrap && string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
|
if (logBootstrap && string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
@@ -279,13 +281,15 @@ public sealed partial class DirectExecutionBackend
|
|||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
TryBypassStackChkFailTrap(num, num7);
|
|
||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
OrbisGen2Result orbisGen2Result;
|
OrbisGen2Result orbisGen2Result;
|
||||||
bool dispatchResolved = true;
|
bool dispatchResolved = true;
|
||||||
var previousImportCallFrame = GuestThreadExecution.EnterImportCallFrame(num7, (ulong)argPackPtr + 104uL);
|
var previousImportCallFrame = GuestThreadExecution.EnterImportCallFrame(
|
||||||
|
num7,
|
||||||
|
(ulong)argPackPtr + 104uL,
|
||||||
|
ActiveGuestReturnSlotAddress);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (string.Equals(importStubEntry.Nid, RuntimeStubNids.BootstrapBridge, StringComparison.Ordinal))
|
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)
|
private unsafe bool TryForceGuestExitToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid)
|
||||||
{
|
{
|
||||||
ulong num = ActiveEntryReturnSentinelRip;
|
ulong num = ActiveEntryReturnSentinelRip;
|
||||||
if (num < 65536)
|
if (num < 65536 || !TryPatchActiveGuestReturnSlot(num))
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
private unsafe bool TryCompleteGuestEntryToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid, string reason, int status)
|
||||||
{
|
{
|
||||||
ulong hostExit = ActiveEntryReturnSentinelRip;
|
ulong hostExit = ActiveEntryReturnSentinelRip;
|
||||||
if (hostExit < 65536)
|
if (hostExit < 65536 || !TryPatchActiveGuestReturnSlot(hostExit))
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
private unsafe bool TryYieldGuestThreadToHostStub(nint argPackPtr, long dispatchIndex, ulong returnRip, string nid, string reason)
|
||||||
{
|
{
|
||||||
ulong hostExit = ActiveEntryReturnSentinelRip;
|
ulong hostExit = ActiveEntryReturnSentinelRip;
|
||||||
if (hostExit < 65536)
|
if (hostExit < 65536 || !TryPatchActiveGuestReturnSlot(hostExit))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -470,6 +474,14 @@ public sealed partial class DirectExecutionBackend
|
|||||||
return true;
|
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)
|
private bool ShouldForceGuestExitOnImportLoop(string nid, ulong returnRip, long dispatchIndex, ulong arg0, ulong arg1)
|
||||||
{
|
{
|
||||||
if (dispatchIndex < 1200)
|
if (dispatchIndex < 1200)
|
||||||
@@ -480,6 +492,11 @@ public sealed partial class DirectExecutionBackend
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (IsImportLoopGuardBoundary(nid))
|
||||||
|
{
|
||||||
|
ResetImportLoopPattern();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if (!_importNidHashCache.TryGetValue(nid, out var value))
|
if (!_importNidHashCache.TryGetValue(nid, out var value))
|
||||||
{
|
{
|
||||||
value = StableHash64(nid);
|
value = StableHash64(nid);
|
||||||
@@ -513,6 +530,17 @@ public sealed partial class DirectExecutionBackend
|
|||||||
return elapsedTicks >= (long)(guardSeconds * Stopwatch.Frequency);
|
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()
|
private static int GetImportLoopGuardSeconds()
|
||||||
{
|
{
|
||||||
if (int.TryParse(Environment.GetEnvironmentVariable("SHARPEMU_IMPORT_LOOP_GUARD_SECONDS"), out var seconds))
|
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)
|
private unsafe void TryPatchEa020eLookupCall(long dispatchIndex, ulong returnRip)
|
||||||
{
|
{
|
||||||
if (_patchedEa020eLookupCall || returnRip != 0x0000000800EA01A6uL)
|
if (_patchedEa020eLookupCall || returnRip != 0x0000000800EA01A6uL)
|
||||||
|
|||||||
@@ -136,10 +136,18 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
|
|
||||||
private nint _unresolvedReturnStub;
|
private nint _unresolvedReturnStub;
|
||||||
|
|
||||||
|
private nint _guestReturnStub;
|
||||||
|
|
||||||
private nint _rawExceptionHandler;
|
private nint _rawExceptionHandler;
|
||||||
|
|
||||||
|
private nint _rawExceptionHandlerStub;
|
||||||
|
|
||||||
private nint _exceptionHandler;
|
private nint _exceptionHandler;
|
||||||
|
|
||||||
|
private nint _exceptionHandlerStub;
|
||||||
|
|
||||||
|
private nint _unhandledFilterStub;
|
||||||
|
|
||||||
private nint _lowIndexedTableScratch;
|
private nint _lowIndexedTableScratch;
|
||||||
|
|
||||||
private nint _stackGuardCompareScratch;
|
private nint _stackGuardCompareScratch;
|
||||||
@@ -161,6 +169,9 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static ulong _activeEntryReturnSentinelRip;
|
private static ulong _activeEntryReturnSentinelRip;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ulong _activeGuestReturnSlotAddress;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static bool _activeForcedGuestExit;
|
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> _fallbackTrapStubs = new HashSet<ulong>();
|
||||||
|
|
||||||
private readonly HashSet<ulong> _stackChkBypassSites = new HashSet<ulong>();
|
|
||||||
|
|
||||||
private readonly HashSet<ulong> _patchedResolverReturnSites = new HashSet<ulong>();
|
private readonly HashSet<ulong> _patchedResolverReturnSites = new HashSet<ulong>();
|
||||||
|
|
||||||
private readonly HashSet<ulong> _patchedTlsImmediateThunkTargets = 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
|
private bool ActiveForcedGuestExit
|
||||||
{
|
{
|
||||||
get => HasActiveExecutionThread ? _activeForcedGuestExit : _forcedGuestExit;
|
get => HasActiveExecutionThread ? _activeForcedGuestExit : _forcedGuestExit;
|
||||||
@@ -591,6 +603,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
DirectExecutionBackend? previousBackend,
|
DirectExecutionBackend? previousBackend,
|
||||||
CpuContext? previousContext,
|
CpuContext? previousContext,
|
||||||
ulong previousSentinel,
|
ulong previousSentinel,
|
||||||
|
ulong previousReturnSlotAddress,
|
||||||
bool previousForcedExit,
|
bool previousForcedExit,
|
||||||
bool previousYieldRequested,
|
bool previousYieldRequested,
|
||||||
string? previousYieldReason)
|
string? previousYieldReason)
|
||||||
@@ -598,6 +611,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
_activeExecutionBackend = previousBackend;
|
_activeExecutionBackend = previousBackend;
|
||||||
_activeCpuContext = previousContext;
|
_activeCpuContext = previousContext;
|
||||||
_activeEntryReturnSentinelRip = previousSentinel;
|
_activeEntryReturnSentinelRip = previousSentinel;
|
||||||
|
_activeGuestReturnSlotAddress = previousReturnSlotAddress;
|
||||||
_activeForcedGuestExit = previousForcedExit;
|
_activeForcedGuestExit = previousForcedExit;
|
||||||
_activeGuestThreadYieldRequested = previousYieldRequested;
|
_activeGuestThreadYieldRequested = previousYieldRequested;
|
||||||
_activeGuestThreadYieldReason = previousYieldReason;
|
_activeGuestThreadYieldReason = previousYieldReason;
|
||||||
@@ -634,6 +648,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
throw new OutOfMemoryException("Failed to allocate host stack slot storage");
|
throw new OutOfMemoryException("Failed to allocate host stack slot storage");
|
||||||
}
|
}
|
||||||
_unresolvedReturnStub = CreateUnresolvedReturnStub();
|
_unresolvedReturnStub = CreateUnresolvedReturnStub();
|
||||||
|
_guestReturnStub = CreateGuestReturnStub();
|
||||||
|
if (_guestReturnStub == 0)
|
||||||
|
{
|
||||||
|
throw new OutOfMemoryException("Failed to allocate guest return stub");
|
||||||
|
}
|
||||||
SetupExceptionHandler();
|
SetupExceptionHandler();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -930,9 +949,24 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
yield return "_" + exportName;
|
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)
|
private unsafe void BindTlsBase(CpuContext context)
|
||||||
@@ -1243,6 +1277,145 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
return (nint)ptr;
|
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)
|
private unsafe void* TryAllocateNearEntry(nuint size)
|
||||||
{
|
{
|
||||||
ulong entryPoint = _entryPoint;
|
ulong entryPoint = _entryPoint;
|
||||||
@@ -1980,8 +2153,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
{
|
{
|
||||||
Rip = continuation.Rip,
|
Rip = continuation.Rip,
|
||||||
Rflags = continuation.Rflags == 0 ? 0x202UL : continuation.Rflags,
|
Rflags = continuation.Rflags == 0 ? 0x202UL : continuation.Rflags,
|
||||||
FsBase = continuation.FsBase != 0 ? continuation.FsBase : (callerContext.FsBase != 0 ? callerContext.FsBase : fallbackTlsBase),
|
FsBase = callerContext.FsBase != 0 ? callerContext.FsBase : (continuation.FsBase != 0 ? continuation.FsBase : fallbackTlsBase),
|
||||||
GsBase = continuation.GsBase != 0 ? continuation.GsBase : (callerContext.GsBase != 0 ? callerContext.GsBase : fallbackTlsBase),
|
GsBase = callerContext.GsBase != 0 ? callerContext.GsBase : (continuation.GsBase != 0 ? continuation.GsBase : fallbackTlsBase),
|
||||||
};
|
};
|
||||||
|
|
||||||
context[CpuRegister.Rax] = continuation.Rax;
|
context[CpuRegister.Rax] = continuation.Rax;
|
||||||
@@ -1997,21 +2170,39 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
context[CpuRegister.R13] = continuation.R13;
|
context[CpuRegister.R13] = continuation.R13;
|
||||||
context[CpuRegister.R14] = continuation.R14;
|
context[CpuRegister.R14] = continuation.R14;
|
||||||
context[CpuRegister.R15] = continuation.R15;
|
context[CpuRegister.R15] = continuation.R15;
|
||||||
context[CpuRegister.Rsp] = continuation.Rsp + 16uL;
|
context[CpuRegister.Rsp] = continuation.Rsp;
|
||||||
|
|
||||||
var currentGuestThreadHandle = GuestThreadExecution.CurrentGuestThreadHandle;
|
|
||||||
var exitReason = GuestNativeCallExitReason.Exception;
|
var exitReason = GuestNativeCallExitReason.Exception;
|
||||||
string? callbackReason = null;
|
string? callbackReason = null;
|
||||||
string? callbackLastError = null;
|
string? callbackLastError = null;
|
||||||
Exception? callbackException = null;
|
Exception? callbackException = null;
|
||||||
|
var currentGuestThreadHandle = GuestThreadExecution.CurrentGuestThreadHandle;
|
||||||
|
var currentFiberAddress = GuestThreadExecution.CurrentFiberAddress;
|
||||||
|
|
||||||
void RunContinuation()
|
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;
|
var previousLastError = LastError;
|
||||||
try
|
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;
|
LastError = null;
|
||||||
exitReason = ExecuteGuestContinuationEntry(context, continuation.Rip, reason, out callbackReason);
|
exitReason = ExecuteGuestContinuationEntry(
|
||||||
|
context,
|
||||||
|
continuation.Rip,
|
||||||
|
continuation.ReturnSlotAddress,
|
||||||
|
reason,
|
||||||
|
out callbackReason);
|
||||||
callbackLastError = LastError;
|
callbackLastError = LastError;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -2022,7 +2213,17 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
TraceGuestContext(
|
||||||
|
$"continuation-exit reason={reason} managed={Environment.CurrentManagedThreadId} guest=0x{GuestThreadExecution.CurrentGuestThreadHandle:X16} fiber=0x{GuestThreadExecution.CurrentFiberAddress:X16} exit={exitReason}");
|
||||||
LastError = previousLastError;
|
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);
|
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
|
else
|
||||||
{
|
{
|
||||||
RunContinuationOnTemporaryThread(currentGuestThreadHandle, RunContinuation);
|
RunContinuationOnTemporaryThread(currentGuestThreadHandle, RunContinuation);
|
||||||
@@ -2070,6 +2277,14 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
return true;
|
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)
|
private static void RunContinuationOnTemporaryThread(ulong guestThreadHandle, Action continuation)
|
||||||
{
|
{
|
||||||
var continuationThread = new Thread(() =>
|
var continuationThread = new Thread(() =>
|
||||||
@@ -2351,6 +2566,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
var previousActiveBackend = _activeExecutionBackend;
|
var previousActiveBackend = _activeExecutionBackend;
|
||||||
var previousActiveContext = _activeCpuContext;
|
var previousActiveContext = _activeCpuContext;
|
||||||
var previousSentinel = _activeEntryReturnSentinelRip;
|
var previousSentinel = _activeEntryReturnSentinelRip;
|
||||||
|
var previousReturnSlotAddress = _activeGuestReturnSlotAddress;
|
||||||
var previousForcedExit = _activeForcedGuestExit;
|
var previousForcedExit = _activeForcedGuestExit;
|
||||||
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
||||||
var previousYieldReason = _activeGuestThreadYieldReason;
|
var previousYieldReason = _activeGuestThreadYieldReason;
|
||||||
@@ -2360,6 +2576,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
_activeExecutionBackend = this;
|
_activeExecutionBackend = this;
|
||||||
_activeCpuContext = context;
|
_activeCpuContext = context;
|
||||||
_activeEntryReturnSentinelRip = 0;
|
_activeEntryReturnSentinelRip = 0;
|
||||||
|
_activeGuestReturnSlotAddress = 0;
|
||||||
_activeForcedGuestExit = false;
|
_activeForcedGuestExit = false;
|
||||||
_activeGuestThreadYieldRequested = false;
|
_activeGuestThreadYieldRequested = false;
|
||||||
_activeGuestThreadYieldReason = null;
|
_activeGuestThreadYieldReason = null;
|
||||||
@@ -2463,7 +2680,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
ptr2[offset++] = 91;
|
ptr2[offset++] = 91;
|
||||||
ptr2[offset++] = 195;
|
ptr2[offset++] = 195;
|
||||||
ulong sentinel = (ulong)ptr + (ulong)sentinelOffset;
|
ulong sentinel = (ulong)ptr + (ulong)sentinelOffset;
|
||||||
ActiveEntryReturnSentinelRip = sentinel;
|
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
||||||
|
_activeGuestReturnSlotAddress = context[CpuRegister.Rsp] - 16uL;
|
||||||
if (!context.TryWriteUInt64(context[CpuRegister.Rsp], sentinel))
|
if (!context.TryWriteUInt64(context[CpuRegister.Rsp], sentinel))
|
||||||
{
|
{
|
||||||
reason = $"failed to patch guest thread return sentinel at 0x{context[CpuRegister.Rsp]:X16}";
|
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,
|
previousActiveBackend,
|
||||||
previousActiveContext,
|
previousActiveContext,
|
||||||
previousSentinel,
|
previousSentinel,
|
||||||
|
previousReturnSlotAddress,
|
||||||
previousForcedExit,
|
previousForcedExit,
|
||||||
previousYieldRequested,
|
previousYieldRequested,
|
||||||
previousYieldReason);
|
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;
|
reason = null;
|
||||||
if (context[CpuRegister.Rsp] == 0)
|
if (context[CpuRegister.Rsp] == 0)
|
||||||
@@ -2534,6 +2758,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
var previousActiveBackend = _activeExecutionBackend;
|
var previousActiveBackend = _activeExecutionBackend;
|
||||||
var previousActiveContext = _activeCpuContext;
|
var previousActiveContext = _activeCpuContext;
|
||||||
var previousSentinel = _activeEntryReturnSentinelRip;
|
var previousSentinel = _activeEntryReturnSentinelRip;
|
||||||
|
var previousReturnSlotAddress = _activeGuestReturnSlotAddress;
|
||||||
var previousForcedExit = _activeForcedGuestExit;
|
var previousForcedExit = _activeForcedGuestExit;
|
||||||
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
||||||
var previousYieldReason = _activeGuestThreadYieldReason;
|
var previousYieldReason = _activeGuestThreadYieldReason;
|
||||||
@@ -2543,6 +2768,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
_activeExecutionBackend = this;
|
_activeExecutionBackend = this;
|
||||||
_activeCpuContext = context;
|
_activeCpuContext = context;
|
||||||
_activeEntryReturnSentinelRip = 0;
|
_activeEntryReturnSentinelRip = 0;
|
||||||
|
_activeGuestReturnSlotAddress = returnSlotAddress;
|
||||||
_activeForcedGuestExit = false;
|
_activeForcedGuestExit = false;
|
||||||
_activeGuestThreadYieldRequested = false;
|
_activeGuestThreadYieldRequested = false;
|
||||||
_activeGuestThreadYieldReason = null;
|
_activeGuestThreadYieldReason = null;
|
||||||
@@ -2577,7 +2803,6 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
Emit(0x49); Emit(0x89); Emit(0x22); // mov [r10], rsp
|
Emit(0x49); Emit(0x89); Emit(0x22); // mov [r10], rsp
|
||||||
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rsp]); // mov rax, guest rsp
|
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rsp]); // mov rax, guest rsp
|
||||||
Emit(0x48); Emit(0x89); Emit(0xC4); // mov rsp, rax
|
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, 0xBB, context[CpuRegister.Rbx]); // mov rbx, imm64
|
||||||
EmitMovR64Imm(0x48, 0xBD, context[CpuRegister.Rbp]); // mov rbp, imm64
|
EmitMovR64Imm(0x48, 0xBD, context[CpuRegister.Rbp]); // mov rbp, imm64
|
||||||
EmitMovR64Imm(0x48, 0xBF, context[CpuRegister.Rdi]); // mov rdi, 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(0x49, 0xBF, context[CpuRegister.R15]); // mov r15, imm64
|
||||||
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rax]); // mov rax, imm64
|
EmitMovR64Imm(0x48, 0xB8, context[CpuRegister.Rax]); // mov rax, imm64
|
||||||
EmitMovR64Imm(0x49, 0xBB, entryPoint); // mov r11, entryPoint
|
EmitMovR64Imm(0x49, 0xBB, entryPoint); // mov r11, entryPoint
|
||||||
Emit(0x41); Emit(0xFF); Emit(0xD3); // call r11
|
Emit(0x41); Emit(0xFF); Emit(0xE3); // jmp r11
|
||||||
int sentinelOffset = offset + 4;
|
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
||||||
Emit(0x48); Emit(0x83); Emit(0xC4); Emit(0x08); // add rsp, 8
|
if (returnSlotAddress == 0 || !context.TryWriteUInt64(returnSlotAddress, (ulong)_guestReturnStub))
|
||||||
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}";
|
reason = $"failed to patch guest continuation return slot at 0x{returnSlotAddress:X16}";
|
||||||
return GuestNativeCallExitReason.Exception;
|
return GuestNativeCallExitReason.Exception;
|
||||||
}
|
}
|
||||||
uint oldProtect = default(uint);
|
uint oldProtect = default(uint);
|
||||||
@@ -2657,6 +2864,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
previousActiveBackend,
|
previousActiveBackend,
|
||||||
previousActiveContext,
|
previousActiveContext,
|
||||||
previousSentinel,
|
previousSentinel,
|
||||||
|
previousReturnSlotAddress,
|
||||||
previousForcedExit,
|
previousForcedExit,
|
||||||
previousYieldRequested,
|
previousYieldRequested,
|
||||||
previousYieldReason);
|
previousYieldReason);
|
||||||
@@ -2717,9 +2925,18 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
}
|
}
|
||||||
EmitByte(code, ref offset, 0x0F);
|
EmitByte(code, ref offset, 0x0F);
|
||||||
EmitByte(code, ref offset, store ? (byte)0x7F : (byte)0x6F);
|
EmitByte(code, ref offset, store ? (byte)0x7F : (byte)0x6F);
|
||||||
EmitByte(code, ref offset, (byte)(0x44 | ((xmm & 7) << 3)));
|
if (displacement < 0x80)
|
||||||
EmitByte(code, ref offset, 0x24);
|
{
|
||||||
EmitByte(code, ref offset, displacement);
|
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)
|
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 previousActiveBackend = _activeExecutionBackend;
|
||||||
var previousActiveContext = _activeCpuContext;
|
var previousActiveContext = _activeCpuContext;
|
||||||
var previousSentinel = _activeEntryReturnSentinelRip;
|
var previousSentinel = _activeEntryReturnSentinelRip;
|
||||||
|
var previousReturnSlotAddress = _activeGuestReturnSlotAddress;
|
||||||
var previousForcedExit = _activeForcedGuestExit;
|
var previousForcedExit = _activeForcedGuestExit;
|
||||||
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
var previousYieldRequested = _activeGuestThreadYieldRequested;
|
||||||
var previousYieldReason = _activeGuestThreadYieldReason;
|
var previousYieldReason = _activeGuestThreadYieldReason;
|
||||||
@@ -2754,6 +2972,7 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
_activeExecutionBackend = this;
|
_activeExecutionBackend = this;
|
||||||
_activeCpuContext = context;
|
_activeCpuContext = context;
|
||||||
_activeEntryReturnSentinelRip = 0;
|
_activeEntryReturnSentinelRip = 0;
|
||||||
|
_activeGuestReturnSlotAddress = 0;
|
||||||
_activeForcedGuestExit = false;
|
_activeForcedGuestExit = false;
|
||||||
_activeGuestThreadYieldRequested = false;
|
_activeGuestThreadYieldRequested = false;
|
||||||
_activeGuestThreadYieldReason = null;
|
_activeGuestThreadYieldReason = null;
|
||||||
@@ -2857,7 +3076,8 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
ptr2[num3++] = 91;
|
ptr2[num3++] = 91;
|
||||||
ptr2[num3++] = 195;
|
ptr2[num3++] = 195;
|
||||||
ulong value = (ulong)ptr + (ulong)num4;
|
ulong value = (ulong)ptr + (ulong)num4;
|
||||||
ActiveEntryReturnSentinelRip = value;
|
ActiveEntryReturnSentinelRip = (ulong)_guestReturnStub;
|
||||||
|
_activeGuestReturnSlotAddress = context[CpuRegister.Rsp] - 16uL;
|
||||||
if (!context.TryWriteUInt64(context[CpuRegister.Rsp], value))
|
if (!context.TryWriteUInt64(context[CpuRegister.Rsp], value))
|
||||||
{
|
{
|
||||||
LastError = $"Failed to patch native return sentinel at 0x{context[CpuRegister.Rsp]:X16}";
|
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,
|
previousActiveBackend,
|
||||||
previousActiveContext,
|
previousActiveContext,
|
||||||
previousSentinel,
|
previousSentinel,
|
||||||
|
previousReturnSlotAddress,
|
||||||
previousForcedExit,
|
previousForcedExit,
|
||||||
previousYieldRequested,
|
previousYieldRequested,
|
||||||
previousYieldReason);
|
previousYieldReason);
|
||||||
@@ -3116,6 +3337,22 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
RemoveVectoredExceptionHandler((void*)_rawExceptionHandler);
|
RemoveVectoredExceptionHandler((void*)_rawExceptionHandler);
|
||||||
_rawExceptionHandler = 0;
|
_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)
|
if (_handlerHandle.IsAllocated)
|
||||||
{
|
{
|
||||||
_handlerHandle.Free();
|
_handlerHandle.Free();
|
||||||
@@ -3172,6 +3409,11 @@ public sealed unsafe partial class DirectExecutionBackend : INativeCpuBackend, I
|
|||||||
VirtualFree((void*)_unresolvedReturnStub, 0u, 32768u);
|
VirtualFree((void*)_unresolvedReturnStub, 0u, 32768u);
|
||||||
_unresolvedReturnStub = 0;
|
_unresolvedReturnStub = 0;
|
||||||
}
|
}
|
||||||
|
if (_guestReturnStub != 0)
|
||||||
|
{
|
||||||
|
VirtualFree((void*)_guestReturnStub, 0u, 32768u);
|
||||||
|
_guestReturnStub = 0;
|
||||||
|
}
|
||||||
if (_lowIndexedTableScratch != 0)
|
if (_lowIndexedTableScratch != 0)
|
||||||
{
|
{
|
||||||
VirtualFree((void*)_lowIndexedTableScratch, 0u, 32768u);
|
VirtualFree((void*)_lowIndexedTableScratch, 0u, 32768u);
|
||||||
|
|||||||
@@ -222,6 +222,56 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
return actualAddress;
|
return actualAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool TryAllocateAtOrAbove(
|
||||||
|
ulong desiredAddress,
|
||||||
|
ulong size,
|
||||||
|
bool executable,
|
||||||
|
ulong alignment,
|
||||||
|
out ulong actualAddress)
|
||||||
|
{
|
||||||
|
actualAddress = 0;
|
||||||
|
if (size == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var alignedSize = AlignUp(size, PageSize);
|
||||||
|
var effectiveAlignment = Math.Max(PageSize, alignment == 0 ? PageSize : alignment);
|
||||||
|
var cursor = AlignUp(desiredAddress, effectiveAlignment);
|
||||||
|
|
||||||
|
for (var attempt = 0; attempt < 0x10000; attempt++)
|
||||||
|
{
|
||||||
|
if (cursor == 0 || ulong.MaxValue - cursor < alignedSize)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryGetOverlappingRegionEnd(cursor, alignedSize, out var overlapEnd))
|
||||||
|
{
|
||||||
|
cursor = AlignUp(overlapEnd, effectiveAlignment);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
actualAddress = AllocateAt(cursor, alignedSize, executable, allowAlternative: false);
|
||||||
|
if (actualAddress == cursor)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
actualAddress = 0;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = AlignUp(cursor + effectiveAlignment, effectiveAlignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryAllocateGuestMemory(ulong size, ulong alignment, out ulong address)
|
public bool TryAllocateGuestMemory(ulong size, ulong alignment, out ulong address)
|
||||||
{
|
{
|
||||||
address = 0;
|
address = 0;
|
||||||
@@ -532,6 +582,30 @@ public sealed unsafe class PhysicalVirtualMemory : IVirtualMemory, IGuestMemoryA
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool TryGetOverlappingRegionEnd(ulong address, ulong size, out ulong overlapEnd)
|
||||||
|
{
|
||||||
|
overlapEnd = 0;
|
||||||
|
if (size == 0 || ulong.MaxValue - address < size - 1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var end = address + size;
|
||||||
|
lock (_gate)
|
||||||
|
{
|
||||||
|
foreach (var region in _regions)
|
||||||
|
{
|
||||||
|
var regionEnd = region.VirtualAddress + region.Size;
|
||||||
|
if (address < regionEnd && region.VirtualAddress < end)
|
||||||
|
{
|
||||||
|
overlapEnd = Math.Max(overlapEnd, regionEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return overlapEnd != 0;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryResolveRegionOffset(ulong address, ulong size, MemoryRegion region, out ulong offset)
|
private static bool TryResolveRegionOffset(ulong address, ulong size, MemoryRegion region, out ulong offset)
|
||||||
{
|
{
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|||||||
@@ -36,11 +36,13 @@ public interface IGuestThreadScheduler
|
|||||||
public readonly record struct GuestImportCallFrame(
|
public readonly record struct GuestImportCallFrame(
|
||||||
bool IsValid,
|
bool IsValid,
|
||||||
ulong ReturnRip,
|
ulong ReturnRip,
|
||||||
ulong ResumeRsp);
|
ulong ResumeRsp,
|
||||||
|
ulong ReturnSlotAddress);
|
||||||
|
|
||||||
public readonly record struct GuestCpuContinuation(
|
public readonly record struct GuestCpuContinuation(
|
||||||
ulong Rip,
|
ulong Rip,
|
||||||
ulong Rsp,
|
ulong Rsp,
|
||||||
|
ulong ReturnSlotAddress,
|
||||||
ulong Rflags,
|
ulong Rflags,
|
||||||
ulong FsBase,
|
ulong FsBase,
|
||||||
ulong GsBase,
|
ulong GsBase,
|
||||||
@@ -63,6 +65,9 @@ public static class GuestThreadExecution
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static ulong _currentGuestThreadHandle;
|
private static ulong _currentGuestThreadHandle;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ulong _currentFiberAddress;
|
||||||
|
|
||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static string? _pendingBlockReason;
|
private static string? _pendingBlockReason;
|
||||||
|
|
||||||
@@ -84,12 +89,17 @@ public static class GuestThreadExecution
|
|||||||
[ThreadStatic]
|
[ThreadStatic]
|
||||||
private static ulong _currentImportResumeRsp;
|
private static ulong _currentImportResumeRsp;
|
||||||
|
|
||||||
|
[ThreadStatic]
|
||||||
|
private static ulong _currentImportReturnSlotAddress;
|
||||||
|
|
||||||
public static IGuestThreadScheduler? Scheduler { get; set; }
|
public static IGuestThreadScheduler? Scheduler { get; set; }
|
||||||
|
|
||||||
public static bool IsGuestThread => _currentGuestThreadHandle != 0;
|
public static bool IsGuestThread => _currentGuestThreadHandle != 0;
|
||||||
|
|
||||||
public static ulong CurrentGuestThreadHandle => _currentGuestThreadHandle;
|
public static ulong CurrentGuestThreadHandle => _currentGuestThreadHandle;
|
||||||
|
|
||||||
|
public static ulong CurrentFiberAddress => _currentFiberAddress;
|
||||||
|
|
||||||
public static ulong EnterGuestThread(ulong threadHandle)
|
public static ulong EnterGuestThread(ulong threadHandle)
|
||||||
{
|
{
|
||||||
var previous = _currentGuestThreadHandle;
|
var previous = _currentGuestThreadHandle;
|
||||||
@@ -101,6 +111,7 @@ public static class GuestThreadExecution
|
|||||||
_hasCurrentImportCallFrame = false;
|
_hasCurrentImportCallFrame = false;
|
||||||
_currentImportReturnRip = 0;
|
_currentImportReturnRip = 0;
|
||||||
_currentImportResumeRsp = 0;
|
_currentImportResumeRsp = 0;
|
||||||
|
_currentImportReturnSlotAddress = 0;
|
||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,6 +125,19 @@ public static class GuestThreadExecution
|
|||||||
_hasCurrentImportCallFrame = false;
|
_hasCurrentImportCallFrame = false;
|
||||||
_currentImportReturnRip = 0;
|
_currentImportReturnRip = 0;
|
||||||
_currentImportResumeRsp = 0;
|
_currentImportResumeRsp = 0;
|
||||||
|
_currentImportReturnSlotAddress = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ulong EnterFiber(ulong fiberAddress)
|
||||||
|
{
|
||||||
|
var previous = _currentFiberAddress;
|
||||||
|
_currentFiberAddress = fiberAddress;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RestoreFiber(ulong previousFiberAddress)
|
||||||
|
{
|
||||||
|
_currentFiberAddress = previousFiberAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool RequestCurrentThreadBlock(string reason)
|
public static bool RequestCurrentThreadBlock(string reason)
|
||||||
@@ -161,15 +185,20 @@ public static class GuestThreadExecution
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static GuestImportCallFrame EnterImportCallFrame(ulong returnRip, ulong resumeRsp)
|
public static GuestImportCallFrame EnterImportCallFrame(
|
||||||
|
ulong returnRip,
|
||||||
|
ulong resumeRsp,
|
||||||
|
ulong returnSlotAddress)
|
||||||
{
|
{
|
||||||
var previous = new GuestImportCallFrame(
|
var previous = new GuestImportCallFrame(
|
||||||
_hasCurrentImportCallFrame,
|
_hasCurrentImportCallFrame,
|
||||||
_currentImportReturnRip,
|
_currentImportReturnRip,
|
||||||
_currentImportResumeRsp);
|
_currentImportResumeRsp,
|
||||||
|
_currentImportReturnSlotAddress);
|
||||||
_hasCurrentImportCallFrame = true;
|
_hasCurrentImportCallFrame = true;
|
||||||
_currentImportReturnRip = returnRip;
|
_currentImportReturnRip = returnRip;
|
||||||
_currentImportResumeRsp = resumeRsp;
|
_currentImportResumeRsp = resumeRsp;
|
||||||
|
_currentImportReturnSlotAddress = returnSlotAddress;
|
||||||
return previous;
|
return previous;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,6 +207,7 @@ public static class GuestThreadExecution
|
|||||||
_hasCurrentImportCallFrame = previous.IsValid;
|
_hasCurrentImportCallFrame = previous.IsValid;
|
||||||
_currentImportReturnRip = previous.ReturnRip;
|
_currentImportReturnRip = previous.ReturnRip;
|
||||||
_currentImportResumeRsp = previous.ResumeRsp;
|
_currentImportResumeRsp = previous.ResumeRsp;
|
||||||
|
_currentImportReturnSlotAddress = previous.ReturnSlotAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool TryGetCurrentImportCallFrame(out GuestImportCallFrame frame)
|
public static bool TryGetCurrentImportCallFrame(out GuestImportCallFrame frame)
|
||||||
@@ -188,7 +218,11 @@ public static class GuestThreadExecution
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
frame = new GuestImportCallFrame(true, _currentImportReturnRip, _currentImportResumeRsp);
|
frame = new GuestImportCallFrame(
|
||||||
|
true,
|
||||||
|
_currentImportReturnRip,
|
||||||
|
_currentImportResumeRsp,
|
||||||
|
_currentImportReturnSlotAddress);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
|
using SharpEmu.Libs.Kernel;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
@@ -17,8 +18,10 @@ public static class AmprExports
|
|||||||
private const ulong CommandBufferAux1Offset = 0x20;
|
private const ulong CommandBufferAux1Offset = 0x20;
|
||||||
private const ulong ReadFileRecordSize = 0x30;
|
private const ulong ReadFileRecordSize = 0x30;
|
||||||
private const ulong KernelEventQueueRecordSize = 0x30;
|
private const ulong KernelEventQueueRecordSize = 0x30;
|
||||||
|
private const ulong WriteAddressRecordSize = 0x20;
|
||||||
private const uint ReadFileRecordType = 1;
|
private const uint ReadFileRecordType = 1;
|
||||||
private const uint KernelEventQueueRecordType = 2;
|
private const uint KernelEventQueueRecordType = 2;
|
||||||
|
private const uint WriteAddressRecordType = 3;
|
||||||
private static readonly ConcurrentDictionary<ulong, CommandBufferState> _commandBuffers = new();
|
private static readonly ConcurrentDictionary<ulong, CommandBufferState> _commandBuffers = new();
|
||||||
|
|
||||||
private sealed class CommandBufferState
|
private sealed class CommandBufferState
|
||||||
@@ -284,6 +287,18 @@ public static class AmprExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "C+IEj+BsAFM",
|
||||||
|
ExportName = "sceAmprMeasureCommandSizeWriteAddressOnCompletion",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAmpr")]
|
||||||
|
public static int MeasureCommandSizeWriteAddressOnCompletion(CpuContext ctx)
|
||||||
|
{
|
||||||
|
TraceAmpr(ctx, "measure_write_address_complete", 0, WriteAddressRecordSize, 0);
|
||||||
|
ctx[CpuRegister.Rax] = WriteAddressRecordSize;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
Nid = "tZDDEo2tE5k",
|
Nid = "tZDDEo2tE5k",
|
||||||
ExportName = "sceAmprCommandBufferGetSize",
|
ExportName = "sceAmprCommandBufferGetSize",
|
||||||
@@ -362,6 +377,92 @@ public static class AmprExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[SysAbiExport(
|
||||||
|
Nid = "sJXyWHjP-F8",
|
||||||
|
ExportName = "sceAmprCommandBufferWriteAddressOnCompletion",
|
||||||
|
Target = Generation.Gen5,
|
||||||
|
LibraryName = "libSceAmpr")]
|
||||||
|
public static int CommandBufferWriteAddressOnCompletion(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var commandBuffer = ctx[CpuRegister.Rdi];
|
||||||
|
var address = ctx[CpuRegister.Rsi];
|
||||||
|
var value = ctx[CpuRegister.Rdx];
|
||||||
|
|
||||||
|
if (commandBuffer == 0 || address == 0)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AppendWriteAddressRecord(ctx, commandBuffer, address, value))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAmpr(ctx, "write_address_complete", commandBuffer, address, value);
|
||||||
|
ctx[CpuRegister.Rax] = 0;
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CompleteCommandBuffer(CpuContext ctx, ulong commandBuffer)
|
||||||
|
{
|
||||||
|
if (commandBuffer == 0)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryGetCommandBufferState(ctx, commandBuffer, out var buffer, out _, out var state) || state is null)
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong writeOffset;
|
||||||
|
lock (state)
|
||||||
|
{
|
||||||
|
writeOffset = state.WriteOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = 0UL;
|
||||||
|
while (offset < writeOffset)
|
||||||
|
{
|
||||||
|
if (!TryReadUInt32(ctx, buffer + offset, out var recordType))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (recordType)
|
||||||
|
{
|
||||||
|
case ReadFileRecordType:
|
||||||
|
offset += ReadFileRecordSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case KernelEventQueueRecordType:
|
||||||
|
if (!CompleteKernelEventQueueRecord(ctx, buffer + offset))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += KernelEventQueueRecordSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case WriteAddressRecordType:
|
||||||
|
if (!CompleteWriteAddressRecord(ctx, buffer + offset))
|
||||||
|
{
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += WriteAddressRecordSize;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
TraceAmpr(ctx, "complete_unknown", commandBuffer, recordType, offset);
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_INVALID_ARGUMENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAmpr(ctx, "complete", commandBuffer, buffer, writeOffset);
|
||||||
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool InitializeCommandBuffer(
|
private static bool InitializeCommandBuffer(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
ulong commandBuffer,
|
ulong commandBuffer,
|
||||||
@@ -576,6 +677,17 @@ public static class AmprExports
|
|||||||
return AppendCommandBufferRecord(ctx, commandBuffer, record);
|
return AppendCommandBufferRecord(ctx, commandBuffer, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool AppendWriteAddressRecord(CpuContext ctx, ulong commandBuffer, ulong address, ulong value)
|
||||||
|
{
|
||||||
|
Span<byte> record = stackalloc byte[(int)WriteAddressRecordSize];
|
||||||
|
record.Clear();
|
||||||
|
BinaryPrimitives.WriteUInt32LittleEndian(record[0x00..], WriteAddressRecordType);
|
||||||
|
BinaryPrimitives.WriteUInt64LittleEndian(record[0x08..], address);
|
||||||
|
BinaryPrimitives.WriteUInt64LittleEndian(record[0x10..], value);
|
||||||
|
|
||||||
|
return AppendCommandBufferRecord(ctx, commandBuffer, record);
|
||||||
|
}
|
||||||
|
|
||||||
private static bool AppendCommandBufferRecord(CpuContext ctx, ulong commandBuffer, ReadOnlySpan<byte> record)
|
private static bool AppendCommandBufferRecord(CpuContext ctx, ulong commandBuffer, ReadOnlySpan<byte> record)
|
||||||
{
|
{
|
||||||
if (!TryGetCommandBufferState(ctx, commandBuffer, out _, out _, out var state) || state is null)
|
if (!TryGetCommandBufferState(ctx, commandBuffer, out _, out _, out var state) || state is null)
|
||||||
@@ -604,6 +716,66 @@ public static class AmprExports
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool CompleteKernelEventQueueRecord(CpuContext ctx, ulong recordAddress)
|
||||||
|
{
|
||||||
|
Span<byte> record = stackalloc byte[(int)KernelEventQueueRecordSize];
|
||||||
|
if (!ctx.Memory.TryRead(recordAddress, record))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var filter = unchecked((short)BinaryPrimitives.ReadUInt32LittleEndian(record[0x04..]));
|
||||||
|
var equeue = BinaryPrimitives.ReadUInt64LittleEndian(record[0x08..]);
|
||||||
|
var ident = BinaryPrimitives.ReadUInt64LittleEndian(record[0x10..]);
|
||||||
|
var userData = BinaryPrimitives.ReadUInt64LittleEndian(record[0x18..]);
|
||||||
|
var data = BinaryPrimitives.ReadUInt64LittleEndian(record[0x20..]);
|
||||||
|
var extra = BinaryPrimitives.ReadUInt64LittleEndian(record[0x28..]);
|
||||||
|
|
||||||
|
var queuedEvent = new KernelEventQueueCompatExports.KernelQueuedEvent(
|
||||||
|
ident,
|
||||||
|
filter,
|
||||||
|
0x20,
|
||||||
|
unchecked((uint)extra),
|
||||||
|
data,
|
||||||
|
userData);
|
||||||
|
|
||||||
|
_ = KernelEventQueueCompatExports.EnqueueEvent(equeue, queuedEvent);
|
||||||
|
TraceAmpr(ctx, "complete_equeue", equeue, ident, data);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool CompleteWriteAddressRecord(CpuContext ctx, ulong recordAddress)
|
||||||
|
{
|
||||||
|
Span<byte> record = stackalloc byte[(int)WriteAddressRecordSize];
|
||||||
|
if (!ctx.Memory.TryRead(recordAddress, record))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var address = BinaryPrimitives.ReadUInt64LittleEndian(record[0x08..]);
|
||||||
|
var value = BinaryPrimitives.ReadUInt64LittleEndian(record[0x10..]);
|
||||||
|
if (!ctx.TryWriteUInt64(address, value))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TraceAmpr(ctx, "complete_write_address", address, value, 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryReadUInt32(CpuContext ctx, ulong address, out uint value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||||
|
if (!ctx.Memory.TryRead(address, buffer))
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = BinaryPrimitives.ReadUInt32LittleEndian(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void TraceAmpr(CpuContext ctx, string operation, ulong commandBuffer, ulong arg0, ulong arg1)
|
private static void TraceAmpr(CpuContext ctx, string operation, ulong commandBuffer, ulong arg0, ulong arg1)
|
||||||
{
|
{
|
||||||
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AMPR"), "1", StringComparison.Ordinal))
|
if (!string.Equals(Environment.GetEnvironmentVariable("SHARPEMU_LOG_AMPR"), "1", StringComparison.Ordinal))
|
||||||
|
|||||||
@@ -230,9 +230,9 @@ public static class FiberExports
|
|||||||
LibraryName = "libSceFiber")]
|
LibraryName = "libSceFiber")]
|
||||||
public static int FiberReturnToThread(CpuContext ctx)
|
public static int FiberReturnToThread(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var fiberAddress = _currentFiberAddress;
|
var fiberAddress = ResolveCurrentFiberAddress(ctx);
|
||||||
var inferredFiber = false;
|
var inferredFiber = false;
|
||||||
if (fiberAddress == 0 && TryFindFiberByStack(ctx, out fiberAddress))
|
if (_currentFiberAddress == 0 && fiberAddress != 0)
|
||||||
{
|
{
|
||||||
inferredFiber = true;
|
inferredFiber = true;
|
||||||
}
|
}
|
||||||
@@ -258,9 +258,9 @@ public static class FiberExports
|
|||||||
if (GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
|
if (GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame))
|
||||||
{
|
{
|
||||||
_continuations[fiberAddress] = new FiberContinuation(
|
_continuations[fiberAddress] = new FiberContinuation(
|
||||||
CaptureContinuation(ctx, frame.ReturnRip, frame.ResumeRsp),
|
CaptureContinuation(ctx, frame.ReturnRip, frame.ResumeRsp, frame.ReturnSlotAddress),
|
||||||
argOnRunAddress);
|
argOnRunAddress);
|
||||||
TraceFiber($"yield{(inferredFiber ? "-inferred" : string.Empty)} fiber=0x{fiberAddress:X16} resume=0x{frame.ReturnRip:X16} rsp=0x{frame.ResumeRsp:X16} arg_out=0x{argOnRunAddress:X16}");
|
TraceFiber($"yield{(inferredFiber ? "-inferred" : string.Empty)} fiber=0x{fiberAddress:X16} resume=0x{frame.ReturnRip:X16} rsp=0x{frame.ResumeRsp:X16} return_slot=0x{frame.ReturnSlotAddress:X16} arg_out=0x{argOnRunAddress:X16}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -284,12 +284,13 @@ public static class FiberExports
|
|||||||
return SetReturn(ctx, FiberErrorNull);
|
return SetReturn(ctx, FiberErrorNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentFiberAddress == 0)
|
var fiberAddress = ResolveCurrentFiberAddress(ctx);
|
||||||
|
if (fiberAddress == 0)
|
||||||
{
|
{
|
||||||
return SetReturn(ctx, FiberErrorPermission);
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
return TryWriteUInt64(ctx, outAddress, _currentFiberAddress)
|
return TryWriteUInt64(ctx, outAddress, fiberAddress)
|
||||||
? SetReturn(ctx, 0)
|
? SetReturn(ctx, 0)
|
||||||
: SetReturn(ctx, FiberErrorInvalid);
|
: SetReturn(ctx, FiberErrorInvalid);
|
||||||
}
|
}
|
||||||
@@ -407,7 +408,7 @@ public static class FiberExports
|
|||||||
return SetReturn(ctx, FiberErrorNull);
|
return SetReturn(ctx, FiberErrorNull);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_currentFiberAddress == 0)
|
if (ResolveCurrentFiberAddress(ctx) == 0)
|
||||||
{
|
{
|
||||||
return SetReturn(ctx, FiberErrorPermission);
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
}
|
}
|
||||||
@@ -537,10 +538,11 @@ public static class FiberExports
|
|||||||
|
|
||||||
if (fields.State != FiberStateIdle)
|
if (fields.State != FiberStateIdle)
|
||||||
{
|
{
|
||||||
|
TraceFiber($"run-state-error reason={reason} fiber=0x{fiber:X16} state=0x{fields.State:X8}");
|
||||||
return SetReturn(ctx, FiberErrorState);
|
return SetReturn(ctx, FiberErrorState);
|
||||||
}
|
}
|
||||||
|
|
||||||
var previousFiber = _currentFiberAddress;
|
var previousFiber = ResolveCurrentFiberAddress(ctx);
|
||||||
var switchingFromFiber = isSwitch && previousFiber != 0 && previousFiber != fiber;
|
var switchingFromFiber = isSwitch && previousFiber != 0 && previousFiber != fiber;
|
||||||
if (isSwitch && previousFiber == 0)
|
if (isSwitch && previousFiber == 0)
|
||||||
{
|
{
|
||||||
@@ -553,56 +555,111 @@ public static class FiberExports
|
|||||||
return SetReturn(ctx, FiberErrorPermission);
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var session = new FiberRunSession();
|
||||||
|
var ownsSession = _runSessions.TryAdd(fiber, session);
|
||||||
|
if (!ownsSession && !_runSessions.TryGetValue(fiber, out session))
|
||||||
|
{
|
||||||
|
return SetReturn(ctx, FiberErrorState);
|
||||||
|
}
|
||||||
|
|
||||||
|
FiberContinuation? previousContinuation = null;
|
||||||
|
GuestImportCallFrame switchFrame = default;
|
||||||
|
if (switchingFromFiber)
|
||||||
|
{
|
||||||
|
if (!GuestThreadExecution.TryGetCurrentImportCallFrame(out switchFrame))
|
||||||
|
{
|
||||||
|
if (ownsSession)
|
||||||
|
{
|
||||||
|
_runSessions.TryRemove(fiber, out _);
|
||||||
|
}
|
||||||
|
TraceFiber($"switch-no-frame from=0x{previousFiber:X16} to=0x{fiber:X16} arg_out=0x{outArgumentAddress:X16}");
|
||||||
|
return SetReturn(ctx, FiberErrorPermission);
|
||||||
|
}
|
||||||
|
|
||||||
|
previousContinuation = new FiberContinuation(
|
||||||
|
CaptureContinuation(ctx, switchFrame.ReturnRip, switchFrame.ResumeRsp, switchFrame.ReturnSlotAddress),
|
||||||
|
outArgumentAddress);
|
||||||
|
}
|
||||||
|
|
||||||
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
|
if (!TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateRun))
|
||||||
{
|
{
|
||||||
|
if (ownsSession)
|
||||||
|
{
|
||||||
|
_runSessions.TryRemove(fiber, out _);
|
||||||
|
}
|
||||||
return SetReturn(ctx, FiberErrorInvalid);
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (switchingFromFiber && !TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateIdle))
|
if (switchingFromFiber && !TryWriteUInt32(ctx, previousFiber + FiberStateOffset, FiberStateIdle))
|
||||||
{
|
{
|
||||||
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateIdle);
|
_ = TryWriteUInt32(ctx, fiber + FiberStateOffset, FiberStateIdle);
|
||||||
|
if (ownsSession)
|
||||||
|
{
|
||||||
|
_runSessions.TryRemove(fiber, out _);
|
||||||
|
}
|
||||||
return SetReturn(ctx, FiberErrorInvalid);
|
return SetReturn(ctx, FiberErrorInvalid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previousContinuation.HasValue)
|
||||||
|
{
|
||||||
|
_continuations[previousFiber] = previousContinuation.Value;
|
||||||
|
TraceFiber($"switch-save from=0x{previousFiber:X16} to=0x{fiber:X16} resume=0x{switchFrame.ReturnRip:X16} rsp=0x{switchFrame.ResumeRsp:X16} return_slot=0x{switchFrame.ReturnSlotAddress:X16} arg_out=0x{outArgumentAddress:X16}");
|
||||||
|
}
|
||||||
|
|
||||||
var previousReturnRequested = _fiberReturnRequested;
|
var previousReturnRequested = _fiberReturnRequested;
|
||||||
var previousReturnArgument = _fiberReturnArgument;
|
var previousReturnArgument = _fiberReturnArgument;
|
||||||
var session = new FiberRunSession();
|
|
||||||
_runSessions[fiber] = session;
|
|
||||||
_currentFiberAddress = fiber;
|
_currentFiberAddress = fiber;
|
||||||
_fiberReturnRequested = false;
|
_fiberReturnRequested = false;
|
||||||
_fiberReturnArgument = 0;
|
_fiberReturnArgument = 0;
|
||||||
|
var previousExecutionFiber = GuestThreadExecution.EnterFiber(fiber);
|
||||||
|
TraceFiber($"run-enter reason={reason} fiber=0x{fiber:X16} prev=0x{previousFiber:X16} thread=0x{GuestThreadExecution.CurrentGuestThreadHandle:X16} managed={Environment.CurrentManagedThreadId} stack=0x{fields.ContextAddress:X16}");
|
||||||
|
|
||||||
var hasContinuation = _continuations.TryGetValue(fiber, out var continuation);
|
var hasContinuation = _continuations.TryGetValue(fiber, out var continuation);
|
||||||
bool callbackOk;
|
|
||||||
string? callbackError;
|
|
||||||
if (hasContinuation)
|
if (hasContinuation)
|
||||||
{
|
{
|
||||||
if (continuation.ArgOnRunAddress != 0 &&
|
_continuations.TryRemove(fiber, out _);
|
||||||
!TryWriteUInt64(ctx, continuation.ArgOnRunAddress, argOnRun))
|
}
|
||||||
|
|
||||||
|
TraceFiber(hasContinuation
|
||||||
|
? $"run-dispatch reason={reason} fiber=0x{fiber:X16} resume=1 rip=0x{continuation.Context.Rip:X16} rsp=0x{continuation.Context.Rsp:X16} return_slot=0x{continuation.Context.ReturnSlotAddress:X16} arg_out=0x{continuation.ArgOnRunAddress:X16}"
|
||||||
|
: $"run-dispatch reason={reason} fiber=0x{fiber:X16} resume=0 rip=0x{fields.Entry:X16} rsp=0x{fields.ContextAddress + fields.ContextSize:X16} arg_out=0x{outArgumentAddress:X16}");
|
||||||
|
bool callbackOk;
|
||||||
|
string? callbackError;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (hasContinuation)
|
||||||
{
|
{
|
||||||
callbackOk = false;
|
if (continuation.ArgOnRunAddress != 0 &&
|
||||||
callbackError = $"failed to write resumed argOnRun to 0x{continuation.ArgOnRunAddress:X16}";
|
!TryWriteUInt64(ctx, continuation.ArgOnRunAddress, argOnRun))
|
||||||
|
{
|
||||||
|
callbackOk = false;
|
||||||
|
callbackError = $"failed to write resumed argOnRun to 0x{continuation.ArgOnRunAddress:X16}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
callbackOk = scheduler.TryCallGuestContinuation(
|
||||||
|
ctx,
|
||||||
|
continuation.Context,
|
||||||
|
reason,
|
||||||
|
out callbackError);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
callbackOk = scheduler.TryCallGuestContinuation(
|
callbackOk = scheduler.TryCallGuestFunction(
|
||||||
ctx,
|
ctx,
|
||||||
continuation.Context,
|
fields.Entry,
|
||||||
|
fields.ArgOnInitialize,
|
||||||
|
argOnRun,
|
||||||
|
fields.ContextAddress,
|
||||||
|
fields.ContextSize,
|
||||||
reason,
|
reason,
|
||||||
out callbackError);
|
out callbackError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
callbackOk = scheduler.TryCallGuestFunction(
|
GuestThreadExecution.RestoreFiber(previousExecutionFiber);
|
||||||
ctx,
|
|
||||||
fields.Entry,
|
|
||||||
fields.ArgOnInitialize,
|
|
||||||
argOnRun,
|
|
||||||
fields.ContextAddress,
|
|
||||||
fields.ContextSize,
|
|
||||||
reason,
|
|
||||||
out callbackError);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var returnRequested = _fiberReturnRequested;
|
var returnRequested = _fiberReturnRequested;
|
||||||
@@ -613,11 +670,10 @@ public static class FiberExports
|
|||||||
returnArgument = sessionReturnArgument;
|
returnArgument = sessionReturnArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!returnRequested)
|
if (ownsSession)
|
||||||
{
|
{
|
||||||
_continuations.TryRemove(fiber, out _);
|
_runSessions.TryRemove(fiber, out _);
|
||||||
}
|
}
|
||||||
_runSessions.TryRemove(fiber, out _);
|
|
||||||
|
|
||||||
_currentFiberAddress = previousFiber;
|
_currentFiberAddress = previousFiber;
|
||||||
_fiberReturnRequested = previousReturnRequested;
|
_fiberReturnRequested = previousReturnRequested;
|
||||||
@@ -644,10 +700,15 @@ public static class FiberExports
|
|||||||
return SetReturn(ctx, 0);
|
return SetReturn(ctx, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GuestCpuContinuation CaptureContinuation(CpuContext ctx, ulong resumeRip, ulong resumeRsp) =>
|
private static GuestCpuContinuation CaptureContinuation(
|
||||||
|
CpuContext ctx,
|
||||||
|
ulong resumeRip,
|
||||||
|
ulong resumeRsp,
|
||||||
|
ulong returnSlotAddress) =>
|
||||||
new(
|
new(
|
||||||
resumeRip,
|
resumeRip,
|
||||||
resumeRsp,
|
resumeRsp,
|
||||||
|
returnSlotAddress,
|
||||||
ctx.Rflags == 0 ? 0x202UL : ctx.Rflags,
|
ctx.Rflags == 0 ? 0x202UL : ctx.Rflags,
|
||||||
ctx.FsBase,
|
ctx.FsBase,
|
||||||
ctx.GsBase,
|
ctx.GsBase,
|
||||||
@@ -665,6 +726,24 @@ public static class FiberExports
|
|||||||
ctx[CpuRegister.R14],
|
ctx[CpuRegister.R14],
|
||||||
ctx[CpuRegister.R15]);
|
ctx[CpuRegister.R15]);
|
||||||
|
|
||||||
|
private static ulong ResolveCurrentFiberAddress(CpuContext ctx)
|
||||||
|
{
|
||||||
|
if (_currentFiberAddress != 0)
|
||||||
|
{
|
||||||
|
return _currentFiberAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GuestThreadExecution.CurrentFiberAddress != 0)
|
||||||
|
{
|
||||||
|
return GuestThreadExecution.CurrentFiberAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TryFindFiberByStack(ctx, out var fiberAddress) ? fiberAddress : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static ulong GetCurrentFiberAddressForDiagnostics(CpuContext ctx) =>
|
||||||
|
ResolveCurrentFiberAddress(ctx);
|
||||||
|
|
||||||
private static int AttachContext(
|
private static int AttachContext(
|
||||||
CpuContext ctx,
|
CpuContext ctx,
|
||||||
ulong fiber,
|
ulong fiber,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
|
using SharpEmu.Libs.Ampr;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -38,6 +39,12 @@ public static class KernelAprCompatExports
|
|||||||
|
|
||||||
_submittedCommandBuffers[submissionId] = commandBuffer;
|
_submittedCommandBuffers[submissionId] = commandBuffer;
|
||||||
|
|
||||||
|
var completionResult = AmprExports.CompleteCommandBuffer(ctx, commandBuffer);
|
||||||
|
if (completionResult != (int)OrbisGen2Result.ORBIS_GEN2_OK)
|
||||||
|
{
|
||||||
|
return completionResult;
|
||||||
|
}
|
||||||
|
|
||||||
if (outSubmissionId != 0 && !TryWriteUInt32(ctx, outSubmissionId, submissionId))
|
if (outSubmissionId != 0 && !TryWriteUInt32(ctx, outSubmissionId, submissionId))
|
||||||
{
|
{
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
@@ -93,6 +100,12 @@ public static class KernelAprCompatExports
|
|||||||
var submissionId = unchecked((uint)Interlocked.Increment(ref _nextSubmissionId));
|
var submissionId = unchecked((uint)Interlocked.Increment(ref _nextSubmissionId));
|
||||||
_submittedCommandBuffers[submissionId] = commandBuffer;
|
_submittedCommandBuffers[submissionId] = commandBuffer;
|
||||||
|
|
||||||
|
var completionResult = AmprExports.CompleteCommandBuffer(ctx, commandBuffer);
|
||||||
|
if (completionResult != (int)OrbisGen2Result.ORBIS_GEN2_OK)
|
||||||
|
{
|
||||||
|
return completionResult;
|
||||||
|
}
|
||||||
|
|
||||||
TraceApr(ctx, "submit", submissionId, commandBuffer, ctx[CpuRegister.Rsi], 0);
|
TraceApr(ctx, "submit", submissionId, commandBuffer, ctx[CpuRegister.Rsi], 0);
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
@@ -114,6 +127,12 @@ public static class KernelAprCompatExports
|
|||||||
var submissionId = unchecked((uint)Interlocked.Increment(ref _nextSubmissionId));
|
var submissionId = unchecked((uint)Interlocked.Increment(ref _nextSubmissionId));
|
||||||
_submittedCommandBuffers[submissionId] = commandBuffer;
|
_submittedCommandBuffers[submissionId] = commandBuffer;
|
||||||
|
|
||||||
|
var completionResult = AmprExports.CompleteCommandBuffer(ctx, commandBuffer);
|
||||||
|
if (completionResult != (int)OrbisGen2Result.ORBIS_GEN2_OK)
|
||||||
|
{
|
||||||
|
return completionResult;
|
||||||
|
}
|
||||||
|
|
||||||
if (!TryWriteUInt32(ctx, outSubmissionId, submissionId))
|
if (!TryWriteUInt32(ctx, outSubmissionId, submissionId))
|
||||||
{
|
{
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
@@ -154,5 +173,12 @@ public static class KernelAprCompatExports
|
|||||||
_ = ctx.TryReadUInt64(ctx[CpuRegister.Rsp], out returnRip);
|
_ = ctx.TryReadUInt64(ctx[CpuRegister.Rsp], out returnRip);
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[LOADER][TRACE] apr.{operation}: id=0x{submissionId:X8} cmd=0x{commandBuffer:X16} priority=0x{priority:X16} aux=0x{aux:X16} ret=0x{returnRip:X16}");
|
$"[LOADER][TRACE] apr.{operation}: id=0x{submissionId:X8} cmd=0x{commandBuffer:X16} priority=0x{priority:X16} aux=0x{aux:X16} ret=0x{returnRip:X16}");
|
||||||
|
if (aux != 0 &&
|
||||||
|
ctx.TryReadUInt64(aux, out var result0) &&
|
||||||
|
ctx.TryReadUInt64(aux + sizeof(ulong), out var result1))
|
||||||
|
{
|
||||||
|
Console.Error.WriteLine(
|
||||||
|
$"[LOADER][TRACE] apr.{operation}.result: addr=0x{aux:X16} q0=0x{result0:X16} q1=0x{result1:X16}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using System.Buffers.Binary;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
|
using SharpEmu.Libs.Fiber;
|
||||||
|
|
||||||
namespace SharpEmu.Libs.Kernel;
|
namespace SharpEmu.Libs.Kernel;
|
||||||
|
|
||||||
@@ -113,6 +114,7 @@ public static class KernelEventFlagCompatExports
|
|||||||
{
|
{
|
||||||
var handle = ctx[CpuRegister.Rdi];
|
var handle = ctx[CpuRegister.Rdi];
|
||||||
var pattern = ctx[CpuRegister.Rsi];
|
var pattern = ctx[CpuRegister.Rsi];
|
||||||
|
var returnRip = GetCurrentReturnRip();
|
||||||
if (!_eventFlags.TryGetValue(handle, out var state))
|
if (!_eventFlags.TryGetValue(handle, out var state))
|
||||||
{
|
{
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND);
|
||||||
@@ -122,7 +124,7 @@ public static class KernelEventFlagCompatExports
|
|||||||
{
|
{
|
||||||
state.Bits |= pattern;
|
state.Bits |= pattern;
|
||||||
Monitor.PulseAll(state.Gate);
|
Monitor.PulseAll(state.Gate);
|
||||||
TraceEventFlag($"set handle=0x{handle:X16} pattern=0x{pattern:X16} bits=0x{state.Bits:X16}");
|
TraceEventFlag($"set handle=0x{handle:X16} pattern=0x{pattern:X16} bits=0x{state.Bits:X16} ret=0x{returnRip:X16}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
@@ -203,6 +205,7 @@ public static class KernelEventFlagCompatExports
|
|||||||
var waitMode = unchecked((uint)ctx[CpuRegister.Rdx]);
|
var waitMode = unchecked((uint)ctx[CpuRegister.Rdx]);
|
||||||
var resultAddress = ctx[CpuRegister.Rcx];
|
var resultAddress = ctx[CpuRegister.Rcx];
|
||||||
var timeoutAddress = ctx[CpuRegister.R8];
|
var timeoutAddress = ctx[CpuRegister.R8];
|
||||||
|
var returnRip = GetCurrentReturnRip();
|
||||||
|
|
||||||
if (!_eventFlags.TryGetValue(handle, out var state))
|
if (!_eventFlags.TryGetValue(handle, out var state))
|
||||||
{
|
{
|
||||||
@@ -232,11 +235,17 @@ public static class KernelEventFlagCompatExports
|
|||||||
{
|
{
|
||||||
_ = TryWriteUInt32(ctx, timeoutAddress, 0);
|
_ = TryWriteUInt32(ctx, timeoutAddress, 0);
|
||||||
_ = TryWriteResultPattern(ctx, resultAddress, state.Bits);
|
_ = TryWriteResultPattern(ctx, resultAddress, state.Bits);
|
||||||
TraceEventFlag($"wait-timeout handle=0x{handle:X16} pattern=0x{pattern:X16} timeout={timeoutUsec}");
|
TraceEventFlag($"wait-timeout handle=0x{handle:X16} pattern=0x{pattern:X16} timeout={timeoutUsec} ret=0x{returnRip:X16}");
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT);
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_ERROR_TIMED_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GuestThreadExecution.RequestCurrentThreadBlock("sceKernelWaitEventFlag"))
|
var currentGuestThread = GuestThreadExecution.CurrentGuestThreadHandle;
|
||||||
|
var currentFiber = FiberExports.GetCurrentFiberAddressForDiagnostics(ctx);
|
||||||
|
var managedThread = Environment.CurrentManagedThreadId;
|
||||||
|
var requestedBlock = GuestThreadExecution.RequestCurrentThreadBlock("sceKernelWaitEventFlag");
|
||||||
|
TraceEventFlag($"wait-unsatisfied handle=0x{handle:X16} pattern=0x{pattern:X16} bits=0x{state.Bits:X16} guest_thread=0x{currentGuestThread:X16} fiber=0x{currentFiber:X16} managed={managedThread} block={requestedBlock} ret=0x{returnRip:X16} frames={FormatFrameChain(ctx)}");
|
||||||
|
TraceEventFlag($"wait-object handle=0x{handle:X16} name='{state.Name}' {FormatGuestWaitObject(ctx)}");
|
||||||
|
if (!requestedBlock)
|
||||||
{
|
{
|
||||||
var scheduler = GuestThreadExecution.Scheduler;
|
var scheduler = GuestThreadExecution.Scheduler;
|
||||||
if (scheduler is null)
|
if (scheduler is null)
|
||||||
@@ -245,7 +254,7 @@ public static class KernelEventFlagCompatExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.WaitingThreads++;
|
state.WaitingThreads++;
|
||||||
TraceEventFlag($"wait-pump handle=0x{handle:X16} pattern=0x{pattern:X16} waiters={state.WaitingThreads}");
|
TraceEventFlag($"wait-pump handle=0x{handle:X16} pattern=0x{pattern:X16} waiters={state.WaitingThreads} guest_thread=0x{currentGuestThread:X16} fiber=0x{currentFiber:X16} managed={managedThread} ret=0x{returnRip:X16}");
|
||||||
var releaseWaiter = true;
|
var releaseWaiter = true;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -265,7 +274,7 @@ public static class KernelEventFlagCompatExports
|
|||||||
{
|
{
|
||||||
state.WaitingThreads = Math.Max(0, state.WaitingThreads - 1);
|
state.WaitingThreads = Math.Max(0, state.WaitingThreads - 1);
|
||||||
releaseWaiter = false;
|
releaseWaiter = false;
|
||||||
TraceEventFlag($"wait-wake handle=0x{handle:X16} pattern=0x{pattern:X16} bits=0x{state.Bits:X16} waiters={state.WaitingThreads}");
|
TraceEventFlag($"wait-wake handle=0x{handle:X16} pattern=0x{pattern:X16} bits=0x{state.Bits:X16} waiters={state.WaitingThreads} ret=0x{returnRip:X16}");
|
||||||
return SetReturn(ctx, pumpedWaitResult);
|
return SetReturn(ctx, pumpedWaitResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +291,7 @@ public static class KernelEventFlagCompatExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.WaitingThreads++;
|
state.WaitingThreads++;
|
||||||
TraceEventFlag($"wait-block handle=0x{handle:X16} pattern=0x{pattern:X16} waiters={state.WaitingThreads}");
|
TraceEventFlag($"wait-block handle=0x{handle:X16} pattern=0x{pattern:X16} waiters={state.WaitingThreads} guest_thread=0x{currentGuestThread:X16} fiber=0x{currentFiber:X16} managed={managedThread} ret=0x{returnRip:X16}");
|
||||||
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
return SetReturn(ctx, OrbisGen2Result.ORBIS_GEN2_OK);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
@@ -398,6 +407,32 @@ public static class KernelEventFlagCompatExports
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool TryReadUInt64(CpuContext ctx, ulong address, out ulong value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[sizeof(ulong)];
|
||||||
|
if (!ctx.Memory.TryRead(address, buffer))
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = BinaryPrimitives.ReadUInt64LittleEndian(buffer);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryReadByte(CpuContext ctx, ulong address, out byte value)
|
||||||
|
{
|
||||||
|
Span<byte> buffer = stackalloc byte[1];
|
||||||
|
if (!ctx.Memory.TryRead(address, buffer))
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
value = buffer[0];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
private static bool TryWriteUInt32(CpuContext ctx, ulong address, uint value)
|
||||||
{
|
{
|
||||||
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
Span<byte> buffer = stackalloc byte[sizeof(uint)];
|
||||||
@@ -444,4 +479,101 @@ public static class KernelEventFlagCompatExports
|
|||||||
Console.Error.WriteLine($"[LOADER][TRACE] event_flag.{message}");
|
Console.Error.WriteLine($"[LOADER][TRACE] event_flag.{message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ulong GetCurrentReturnRip() =>
|
||||||
|
GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame)
|
||||||
|
? frame.ReturnRip
|
||||||
|
: 0UL;
|
||||||
|
|
||||||
|
private static string FormatFrameChain(CpuContext ctx)
|
||||||
|
{
|
||||||
|
Span<ulong> returns = stackalloc ulong[4];
|
||||||
|
var count = 0;
|
||||||
|
var frame = ctx[CpuRegister.Rbp];
|
||||||
|
for (var index = 0; index < returns.Length && frame != 0; index++)
|
||||||
|
{
|
||||||
|
if (!ctx.TryReadUInt64(frame, out var nextFrame) ||
|
||||||
|
!ctx.TryReadUInt64(frame + sizeof(ulong), out var returnAddress))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
returns[count++] = returnAddress;
|
||||||
|
if (nextFrame <= frame)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame = nextFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count switch
|
||||||
|
{
|
||||||
|
0 => "none",
|
||||||
|
1 => $"0x{returns[0]:X16}",
|
||||||
|
2 => $"0x{returns[0]:X16},0x{returns[1]:X16}",
|
||||||
|
3 => $"0x{returns[0]:X16},0x{returns[1]:X16},0x{returns[2]:X16}",
|
||||||
|
_ => $"0x{returns[0]:X16},0x{returns[1]:X16},0x{returns[2]:X16},0x{returns[3]:X16}",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatGuestWaitObject(CpuContext ctx)
|
||||||
|
{
|
||||||
|
var r12 = ctx[CpuRegister.R12];
|
||||||
|
var r13 = ctx[CpuRegister.R13];
|
||||||
|
var objectAddress = r12 != 0
|
||||||
|
? r12
|
||||||
|
: r13 >= 0xA8
|
||||||
|
? r13 - 0xA8
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
var builder = new StringBuilder(256);
|
||||||
|
builder.Append($"r12=0x{r12:X16} r13=0x{r13:X16}");
|
||||||
|
if (objectAddress == 0)
|
||||||
|
{
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Append($" obj=0x{objectAddress:X16}");
|
||||||
|
AppendUInt32(builder, ctx, objectAddress + 0x58, "o58");
|
||||||
|
AppendUInt32(builder, ctx, objectAddress + 0x5C, "o5C");
|
||||||
|
AppendUInt64(builder, ctx, objectAddress + 0x60, "o60");
|
||||||
|
AppendByte(builder, ctx, objectAddress + 0x6C, "state6C");
|
||||||
|
AppendByte(builder, ctx, objectAddress + 0x6D, "o6D");
|
||||||
|
AppendByte(builder, ctx, objectAddress + 0xA0, "waitA0");
|
||||||
|
AppendByte(builder, ctx, objectAddress + 0xA1, "stateA1");
|
||||||
|
AppendByte(builder, ctx, objectAddress + 0xA2, "oA2");
|
||||||
|
AppendUInt64(builder, ctx, objectAddress + 0xA8, "eventA8");
|
||||||
|
if (r13 != 0)
|
||||||
|
{
|
||||||
|
AppendUInt64(builder, ctx, r13, "r13_0");
|
||||||
|
AppendUInt64(builder, ctx, r13 + 8, "r13_8");
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AppendByte(StringBuilder builder, CpuContext ctx, ulong address, string name)
|
||||||
|
{
|
||||||
|
if (TryReadByte(ctx, address, out var value))
|
||||||
|
{
|
||||||
|
builder.Append($" {name}=0x{value:X2}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AppendUInt32(StringBuilder builder, CpuContext ctx, ulong address, string name)
|
||||||
|
{
|
||||||
|
if (TryReadUInt32(ctx, address, out var value))
|
||||||
|
{
|
||||||
|
builder.Append($" {name}=0x{value:X8}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AppendUInt64(StringBuilder builder, CpuContext ctx, ulong address, string name)
|
||||||
|
{
|
||||||
|
if (TryReadUInt64(ctx, address, out var value))
|
||||||
|
{
|
||||||
|
builder.Append($" {name}=0x{value:X16}");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
using SharpEmu.Libs.Ampr;
|
using SharpEmu.Libs.Ampr;
|
||||||
|
using System.Buffers;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -164,7 +165,7 @@ public static class KernelMemoryCompatExports
|
|||||||
var desiredAddress = AlignUp(
|
var desiredAddress = AlignUp(
|
||||||
_nextVirtualAddress == 0 ? 0x1_0000_0000UL : _nextVirtualAddress,
|
_nextVirtualAddress == 0 ? 0x1_0000_0000UL : _nextVirtualAddress,
|
||||||
effectiveAlignment);
|
effectiveAlignment);
|
||||||
if (!TryReserveGuestVirtualRange(ctx, desiredAddress, mappedLength, OrbisProtCpuReadWrite, out address) ||
|
if (!TryReserveGuestVirtualRange(ctx, desiredAddress, mappedLength, OrbisProtCpuReadWrite, effectiveAlignment, out address) ||
|
||||||
address == 0)
|
address == 0)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@@ -597,7 +598,6 @@ public static class KernelMemoryCompatExports
|
|||||||
ulong NextGpArg() => vaCursor.NextGpArg();
|
ulong NextGpArg() => vaCursor.NextGpArg();
|
||||||
double NextFloatArg() => vaCursor.NextFloatArg();
|
double NextFloatArg() => vaCursor.NextFloatArg();
|
||||||
rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
|
rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
|
||||||
vaCursor.Commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.Write(rendered);
|
Console.Write(rendered);
|
||||||
@@ -2242,7 +2242,7 @@ public static class KernelMemoryCompatExports
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
reserved = TryReserveGuestVirtualRange(ctx, desiredAddress, length, protection, out mappedAddress);
|
reserved = TryReserveGuestVirtualRange(ctx, desiredAddress, length, protection, effectiveAlignment, out mappedAddress);
|
||||||
}
|
}
|
||||||
if (ShouldTraceDirectMemory())
|
if (ShouldTraceDirectMemory())
|
||||||
{
|
{
|
||||||
@@ -2331,7 +2331,7 @@ public static class KernelMemoryCompatExports
|
|||||||
{
|
{
|
||||||
mappedAddress = requestedAddress;
|
mappedAddress = requestedAddress;
|
||||||
}
|
}
|
||||||
else if (!TryReserveGuestVirtualRange(ctx, desiredAddress, length, protection, out mappedAddress))
|
else if (!TryReserveGuestVirtualRange(ctx, desiredAddress, length, protection, OrbisPageSize, out mappedAddress))
|
||||||
{
|
{
|
||||||
mappedAddress = requestedAddress != 0 && fixedMapping
|
mappedAddress = requestedAddress != 0 && fixedMapping
|
||||||
? requestedAddress
|
? requestedAddress
|
||||||
@@ -2721,7 +2721,6 @@ public static class KernelMemoryCompatExports
|
|||||||
ulong NextGpArg() => vaCursor.NextGpArg();
|
ulong NextGpArg() => vaCursor.NextGpArg();
|
||||||
double NextFloatArg() => vaCursor.NextFloatArg();
|
double NextFloatArg() => vaCursor.NextFloatArg();
|
||||||
var rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
|
var rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
|
||||||
vaCursor.Commit();
|
|
||||||
|
|
||||||
var outputBytes = Encoding.UTF8.GetBytes(rendered);
|
var outputBytes = Encoding.UTF8.GetBytes(rendered);
|
||||||
return WriteSnprintfOutput(ctx, destination, bufferSize, outputBytes);
|
return WriteSnprintfOutput(ctx, destination, bufferSize, outputBytes);
|
||||||
@@ -2768,7 +2767,6 @@ public static class KernelMemoryCompatExports
|
|||||||
ulong NextGpArg() => vaCursor.NextGpArg();
|
ulong NextGpArg() => vaCursor.NextGpArg();
|
||||||
double NextFloatArg() => vaCursor.NextFloatArg();
|
double NextFloatArg() => vaCursor.NextFloatArg();
|
||||||
rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
|
rendered = FormatString(ctx, format, NextGpArg, NextFloatArg);
|
||||||
vaCursor.Commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TraceWidePrintf(ctx, "vswprintf", destination, bufferSize, format, rendered);
|
TraceWidePrintf(ctx, "vswprintf", destination, bufferSize, format, rendered);
|
||||||
@@ -3552,13 +3550,6 @@ public static class KernelMemoryCompatExports
|
|||||||
: 0.0;
|
: 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Commit()
|
|
||||||
{
|
|
||||||
_ = TryWriteUInt32Compat(_ctx, _vaListAddress + 0, _gpOffset);
|
|
||||||
_ = TryWriteUInt32Compat(_ctx, _vaListAddress + 4, _fpOffset);
|
|
||||||
_ = TryWriteUInt64Compat(_ctx, _vaListAddress + 8, _overflowArgArea);
|
|
||||||
}
|
|
||||||
|
|
||||||
public uint GpOffset => _gpOffset;
|
public uint GpOffset => _gpOffset;
|
||||||
|
|
||||||
public uint FpOffset => _fpOffset;
|
public uint FpOffset => _fpOffset;
|
||||||
@@ -3613,6 +3604,7 @@ public static class KernelMemoryCompatExports
|
|||||||
ulong desiredAddress,
|
ulong desiredAddress,
|
||||||
ulong length,
|
ulong length,
|
||||||
int protection,
|
int protection,
|
||||||
|
ulong alignment,
|
||||||
out ulong mappedAddress)
|
out ulong mappedAddress)
|
||||||
{
|
{
|
||||||
mappedAddress = 0;
|
mappedAddress = 0;
|
||||||
@@ -3625,40 +3617,51 @@ public static class KernelMemoryCompatExports
|
|||||||
{
|
{
|
||||||
object memoryObject = ctx.Memory;
|
object memoryObject = ctx.Memory;
|
||||||
MethodInfo? allocateAt = null;
|
MethodInfo? allocateAt = null;
|
||||||
|
MethodInfo? allocateAtOrAbove = null;
|
||||||
var allocateAtHasAllowAlternativeArg = false;
|
var allocateAtHasAllowAlternativeArg = false;
|
||||||
for (var depth = 0; depth < 4; depth++)
|
for (var depth = 0; depth < 4; depth++)
|
||||||
{
|
{
|
||||||
foreach (var candidate in memoryObject.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
foreach (var candidate in memoryObject.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
||||||
{
|
{
|
||||||
if (!string.Equals(candidate.Name, "AllocateAt", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameters = candidate.GetParameters();
|
var parameters = candidate.GetParameters();
|
||||||
if (parameters.Length == 3 &&
|
if (string.Equals(candidate.Name, "TryAllocateAtOrAbove", StringComparison.Ordinal) &&
|
||||||
parameters[0].ParameterType == typeof(ulong) &&
|
parameters.Length == 5 &&
|
||||||
parameters[1].ParameterType == typeof(ulong) &&
|
|
||||||
parameters[2].ParameterType == typeof(bool))
|
|
||||||
{
|
|
||||||
allocateAt = candidate;
|
|
||||||
allocateAtHasAllowAlternativeArg = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameters.Length == 4 &&
|
|
||||||
parameters[0].ParameterType == typeof(ulong) &&
|
parameters[0].ParameterType == typeof(ulong) &&
|
||||||
parameters[1].ParameterType == typeof(ulong) &&
|
parameters[1].ParameterType == typeof(ulong) &&
|
||||||
parameters[2].ParameterType == typeof(bool) &&
|
parameters[2].ParameterType == typeof(bool) &&
|
||||||
parameters[3].ParameterType == typeof(bool))
|
parameters[3].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[4].ParameterType == typeof(ulong).MakeByRefType())
|
||||||
|
{
|
||||||
|
allocateAtOrAbove = candidate;
|
||||||
|
}
|
||||||
|
else if (string.Equals(candidate.Name, "AllocateAt", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
if (parameters.Length == 3 &&
|
||||||
|
parameters[0].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[1].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[2].ParameterType == typeof(bool))
|
||||||
|
{
|
||||||
|
allocateAt = candidate;
|
||||||
|
allocateAtHasAllowAlternativeArg = false;
|
||||||
|
}
|
||||||
|
else if (parameters.Length == 4 &&
|
||||||
|
parameters[0].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[1].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[2].ParameterType == typeof(bool) &&
|
||||||
|
parameters[3].ParameterType == typeof(bool))
|
||||||
|
{
|
||||||
|
allocateAt = candidate;
|
||||||
|
allocateAtHasAllowAlternativeArg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allocateAtOrAbove is not null && allocateAt is not null)
|
||||||
{
|
{
|
||||||
allocateAt = candidate;
|
|
||||||
allocateAtHasAllowAlternativeArg = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allocateAt is not null)
|
if (allocateAtOrAbove is not null || allocateAt is not null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3678,15 +3681,27 @@ public static class KernelMemoryCompatExports
|
|||||||
memoryObject = innerValue;
|
memoryObject = innerValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var executable = (protection & OrbisProtCpuExec) != 0;
|
||||||
|
if (allocateAtOrAbove is not null)
|
||||||
|
{
|
||||||
|
var searchArgs = new object[] { desiredAddress, length, executable, alignment, 0UL };
|
||||||
|
var searchResult = allocateAtOrAbove.Invoke(memoryObject, searchArgs);
|
||||||
|
if (searchResult is bool trueValue && trueValue &&
|
||||||
|
searchArgs[4] is ulong searchedAddress && searchedAddress != 0)
|
||||||
|
{
|
||||||
|
mappedAddress = searchedAddress;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (allocateAt is null)
|
if (allocateAt is null)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine($"[LOADER][TRACE] reserve range: AllocateAt missing on {ctx.Memory.GetType().FullName}");
|
Console.Error.WriteLine($"[LOADER][TRACE] reserve range: AllocateAt missing on {ctx.Memory.GetType().FullName}");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var executable = (protection & OrbisProtCpuExec) != 0;
|
|
||||||
var invokeArgs = allocateAtHasAllowAlternativeArg
|
var invokeArgs = allocateAtHasAllowAlternativeArg
|
||||||
? new object[] { desiredAddress, length, executable, true }
|
? new object[] { desiredAddress, length, executable, false }
|
||||||
: new object[] { desiredAddress, length, executable };
|
: new object[] { desiredAddress, length, executable };
|
||||||
var result = allocateAt.Invoke(memoryObject, invokeArgs);
|
var result = allocateAt.Invoke(memoryObject, invokeArgs);
|
||||||
if (result is not ulong allocated || allocated == 0)
|
if (result is not ulong allocated || allocated == 0)
|
||||||
@@ -3959,6 +3974,16 @@ public static class KernelMemoryCompatExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static bool TryReadCString(CpuContext ctx, ulong address, ulong maxLength, out byte[] bytes)
|
private static bool TryReadCString(CpuContext ctx, ulong address, ulong maxLength, out byte[] bytes)
|
||||||
|
{
|
||||||
|
return TryReadBytesUntilNull(ctx, address, maxLength, 1_048_576, out bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryReadBytesUntilNull(
|
||||||
|
CpuContext ctx,
|
||||||
|
ulong address,
|
||||||
|
ulong maxLength,
|
||||||
|
int hardLimit,
|
||||||
|
out byte[] bytes)
|
||||||
{
|
{
|
||||||
bytes = Array.Empty<byte>();
|
bytes = Array.Empty<byte>();
|
||||||
if (address == 0)
|
if (address == 0)
|
||||||
@@ -3966,26 +3991,65 @@ public static class KernelMemoryCompatExports
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var limit = (int)Math.Min(maxLength, 1_048_576UL);
|
var limit = (int)Math.Min(maxLength, (ulong)Math.Max(0, hardLimit));
|
||||||
var buffer = new List<byte>(Math.Min(limit, 256));
|
if (limit == 0)
|
||||||
Span<byte> one = stackalloc byte[1];
|
|
||||||
for (var i = 0; i < limit; i++)
|
|
||||||
{
|
{
|
||||||
if (!TryReadCompat(ctx, address + (ulong)i, one))
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int maxChunkSize = 4096;
|
||||||
|
var chunk = GC.AllocateUninitializedArray<byte>(Math.Min(maxChunkSize, limit));
|
||||||
|
var writer = new ArrayBufferWriter<byte>(Math.Min(limit, 256));
|
||||||
|
ulong offset = 0;
|
||||||
|
while (offset < (ulong)limit)
|
||||||
|
{
|
||||||
|
var current = address + offset;
|
||||||
|
if (current < address)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pageRemaining = maxChunkSize - (int)(current & (maxChunkSize - 1));
|
||||||
|
var remaining = (int)Math.Min((ulong)limit - offset, (ulong)Math.Min(chunk.Length, pageRemaining));
|
||||||
|
var span = chunk.AsSpan(0, remaining);
|
||||||
|
if (TryReadCompat(ctx, current, span))
|
||||||
|
{
|
||||||
|
var nulIndex = span.IndexOf((byte)0);
|
||||||
|
var copyLength = nulIndex >= 0 ? nulIndex : remaining;
|
||||||
|
if (copyLength > 0)
|
||||||
|
{
|
||||||
|
span[..copyLength].CopyTo(writer.GetSpan(copyLength));
|
||||||
|
writer.Advance(copyLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nulIndex >= 0)
|
||||||
|
{
|
||||||
|
bytes = writer.WrittenSpan.ToArray();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += (ulong)remaining;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Span<byte> one = stackalloc byte[1];
|
||||||
|
if (!TryReadCompat(ctx, current, one))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (one[0] == 0)
|
if (one[0] == 0)
|
||||||
{
|
{
|
||||||
bytes = buffer.ToArray();
|
bytes = writer.WrittenSpan.ToArray();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.Add(one[0]);
|
one.CopyTo(writer.GetSpan(1));
|
||||||
|
writer.Advance(1);
|
||||||
|
offset++;
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes = buffer.ToArray();
|
bytes = writer.WrittenSpan.ToArray();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4165,25 +4229,12 @@ public static class KernelMemoryCompatExports
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var buffer = new List<byte>(Math.Min(maxLength, 256));
|
if (!TryReadBytesUntilNull(ctx, address, (ulong)maxLength, maxLength, out var bytes))
|
||||||
Span<byte> one = stackalloc byte[1];
|
|
||||||
for (var i = 0; i < maxLength; i++)
|
|
||||||
{
|
{
|
||||||
if (!TryReadCompat(ctx, address + (ulong)i, one))
|
return false;
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (one[0] == 0)
|
|
||||||
{
|
|
||||||
value = Encoding.UTF8.GetString(buffer.ToArray());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer.Add(one[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
value = Encoding.UTF8.GetString(buffer.ToArray());
|
value = Encoding.UTF8.GetString(bytes);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
using SharpEmu.HLE;
|
using SharpEmu.HLE;
|
||||||
|
using SharpEmu.Libs.Fiber;
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -32,6 +33,8 @@ public static class KernelRuntimeCompatExports
|
|||||||
private const ulong DefaultKernelTscFrequency = 10_000_000UL;
|
private const ulong DefaultKernelTscFrequency = 10_000_000UL;
|
||||||
private const ulong PrtAreaStartAddress = 0x0000001000000000UL;
|
private const ulong PrtAreaStartAddress = 0x0000001000000000UL;
|
||||||
private const ulong PrtAreaSize = 0x000000EC00000000UL;
|
private const ulong PrtAreaSize = 0x000000EC00000000UL;
|
||||||
|
private const int MapFlagFixed = 0x10;
|
||||||
|
private const ulong DefaultVirtualRangeAlignment = 0x4000UL;
|
||||||
private const int AioInitParamSize = 0x3C;
|
private const int AioInitParamSize = 0x3C;
|
||||||
private const uint MemCommit = 0x1000;
|
private const uint MemCommit = 0x1000;
|
||||||
private const uint MemReserve = 0x2000;
|
private const uint MemReserve = 0x2000;
|
||||||
@@ -73,6 +76,8 @@ public static class KernelRuntimeCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GuestThreadExecution.Scheduler?.Pump(ctx, "sceKernelUsleep");
|
||||||
|
|
||||||
if (micros < 1000)
|
if (micros < 1000)
|
||||||
{
|
{
|
||||||
Thread.Yield();
|
Thread.Yield();
|
||||||
@@ -108,8 +113,14 @@ public static class KernelRuntimeCompatExports
|
|||||||
lockText = $"0x{lockValue:X16}";
|
lockText = $"0x{lockValue:X16}";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var returnRip = GuestThreadExecution.TryGetCurrentImportCallFrame(out var frame)
|
||||||
|
? frame.ReturnRip
|
||||||
|
: 0UL;
|
||||||
|
var thread = GuestThreadExecution.CurrentGuestThreadHandle;
|
||||||
|
var fiber = FiberExports.GetCurrentFiberAddressForDiagnostics(ctx);
|
||||||
|
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[LOADER][TRACE] usleep#{count}: usec={micros} rbx=0x{rbx:X16} lock@+F78=0x{lockAddress:X16}:{lockText} r13=0x{ctx[CpuRegister.R13]:X16} r14=0x{ctx[CpuRegister.R14]:X16} r15=0x{ctx[CpuRegister.R15]:X16}");
|
$"[LOADER][TRACE] usleep#{count}: usec={micros} ret=0x{returnRip:X16} thread=0x{thread:X16} fiber=0x{fiber:X16} rbx=0x{rbx:X16} lock@+F78=0x{lockAddress:X16}:{lockText} r13=0x{ctx[CpuRegister.R13]:X16} r14=0x{ctx[CpuRegister.R14]:X16} r15=0x{ctx[CpuRegister.R15]:X16}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
@@ -653,7 +664,7 @@ public static class KernelRuntimeCompatExports
|
|||||||
{
|
{
|
||||||
var inOutAddressPointer = ctx[CpuRegister.Rdi];
|
var inOutAddressPointer = ctx[CpuRegister.Rdi];
|
||||||
var length = ctx[CpuRegister.Rsi];
|
var length = ctx[CpuRegister.Rsi];
|
||||||
var _flags = ctx[CpuRegister.Rdx];
|
var flags = unchecked((int)ctx[CpuRegister.Rdx]);
|
||||||
var alignment = ctx[CpuRegister.Rcx];
|
var alignment = ctx[CpuRegister.Rcx];
|
||||||
if (inOutAddressPointer == 0 || length == 0)
|
if (inOutAddressPointer == 0 || length == 0)
|
||||||
{
|
{
|
||||||
@@ -665,7 +676,8 @@ public static class KernelRuntimeCompatExports
|
|||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_MEMORY_FAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
var effectiveAlignment = alignment == 0 ? 0x10000UL : alignment;
|
var effectiveAlignment = alignment == 0 ? DefaultVirtualRangeAlignment : alignment;
|
||||||
|
var fixedMapping = (flags & MapFlagFixed) != 0;
|
||||||
ulong desiredAddress;
|
ulong desiredAddress;
|
||||||
lock (_stateGate)
|
lock (_stateGate)
|
||||||
{
|
{
|
||||||
@@ -674,13 +686,13 @@ public static class KernelRuntimeCompatExports
|
|||||||
: AlignUp(_nextReservedVirtualBase, effectiveAlignment);
|
: AlignUp(_nextReservedVirtualBase, effectiveAlignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryReserveVirtualRange(ctx, desiredAddress, length, out var mappedAddress))
|
if (!TryReserveVirtualRange(ctx, desiredAddress, length, effectiveAlignment, allowSearch: !fixedMapping, out var mappedAddress))
|
||||||
{
|
{
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
return (int)OrbisGen2Result.ORBIS_GEN2_ERROR_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.Error.WriteLine(
|
Console.Error.WriteLine(
|
||||||
$"[LOADER][TRACE] reserve_virtual_range: req=0x{requestedAddress:X16} desired=0x{desiredAddress:X16} mapped=0x{mappedAddress:X16} len=0x{length:X16}");
|
$"[LOADER][TRACE] reserve_virtual_range: req=0x{requestedAddress:X16} desired=0x{desiredAddress:X16} mapped=0x{mappedAddress:X16} len=0x{length:X16} flags=0x{flags:X8} align=0x{effectiveAlignment:X16}");
|
||||||
|
|
||||||
if (!ctx.TryWriteUInt64(inOutAddressPointer, mappedAddress))
|
if (!ctx.TryWriteUInt64(inOutAddressPointer, mappedAddress))
|
||||||
{
|
{
|
||||||
@@ -964,14 +976,12 @@ public static class KernelRuntimeCompatExports
|
|||||||
public static int StackCheckFail(CpuContext ctx)
|
public static int StackCheckFail(CpuContext ctx)
|
||||||
{
|
{
|
||||||
var count = Interlocked.Increment(ref _stackChkFailCount);
|
var count = Interlocked.Increment(ref _stackChkFailCount);
|
||||||
if (count <= 8)
|
Console.Error.WriteLine(
|
||||||
{
|
$"[LOADER][ERROR] __stack_chk_fail#{count}: rip=0x{ctx.Rip:X16} rdi=0x{ctx[CpuRegister.Rdi]:X16}");
|
||||||
Console.Error.WriteLine(
|
var result = (int)OrbisGen2Result.ORBIS_GEN2_ERROR_CPU_TRAP;
|
||||||
$"[LOADER][WARNING] __stack_chk_fail recovery#{count}: rip=0x{ctx.Rip:X16} rdi=0x{ctx[CpuRegister.Rdi]:X16}");
|
GuestThreadExecution.RequestCurrentEntryExit("__stack_chk_fail", result);
|
||||||
}
|
ctx[CpuRegister.Rax] = unchecked((ulong)result);
|
||||||
|
return result;
|
||||||
ctx[CpuRegister.Rax] = 0;
|
|
||||||
return (int)OrbisGen2Result.ORBIS_GEN2_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SysAbiExport(
|
[SysAbiExport(
|
||||||
@@ -1656,7 +1666,13 @@ public static class KernelRuntimeCompatExports
|
|||||||
[DllImport("kernel32.dll", SetLastError = true)]
|
[DllImport("kernel32.dll", SetLastError = true)]
|
||||||
private static extern nint VirtualAlloc(nint lpAddress, nuint dwSize, uint flAllocationType, uint flProtect);
|
private static extern nint VirtualAlloc(nint lpAddress, nuint dwSize, uint flAllocationType, uint flProtect);
|
||||||
|
|
||||||
private static bool TryReserveVirtualRange(CpuContext ctx, ulong desiredAddress, ulong length, out ulong mappedAddress)
|
private static bool TryReserveVirtualRange(
|
||||||
|
CpuContext ctx,
|
||||||
|
ulong desiredAddress,
|
||||||
|
ulong length,
|
||||||
|
ulong alignment,
|
||||||
|
bool allowSearch,
|
||||||
|
out ulong mappedAddress)
|
||||||
{
|
{
|
||||||
mappedAddress = 0;
|
mappedAddress = 0;
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
@@ -1668,40 +1684,51 @@ public static class KernelRuntimeCompatExports
|
|||||||
{
|
{
|
||||||
object memoryObject = ctx.Memory;
|
object memoryObject = ctx.Memory;
|
||||||
MethodInfo? allocateAt = null;
|
MethodInfo? allocateAt = null;
|
||||||
|
MethodInfo? allocateAtOrAbove = null;
|
||||||
var allocateAtHasAllowAlternativeArg = false;
|
var allocateAtHasAllowAlternativeArg = false;
|
||||||
for (var depth = 0; depth < 4; depth++)
|
for (var depth = 0; depth < 4; depth++)
|
||||||
{
|
{
|
||||||
foreach (var candidate in memoryObject.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
foreach (var candidate in memoryObject.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance))
|
||||||
{
|
{
|
||||||
if (!string.Equals(candidate.Name, "AllocateAt", StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameters = candidate.GetParameters();
|
var parameters = candidate.GetParameters();
|
||||||
if (parameters.Length == 3 &&
|
if (string.Equals(candidate.Name, "TryAllocateAtOrAbove", StringComparison.Ordinal) &&
|
||||||
parameters[0].ParameterType == typeof(ulong) &&
|
parameters.Length == 5 &&
|
||||||
parameters[1].ParameterType == typeof(ulong) &&
|
|
||||||
parameters[2].ParameterType == typeof(bool))
|
|
||||||
{
|
|
||||||
allocateAt = candidate;
|
|
||||||
allocateAtHasAllowAlternativeArg = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parameters.Length == 4 &&
|
|
||||||
parameters[0].ParameterType == typeof(ulong) &&
|
parameters[0].ParameterType == typeof(ulong) &&
|
||||||
parameters[1].ParameterType == typeof(ulong) &&
|
parameters[1].ParameterType == typeof(ulong) &&
|
||||||
parameters[2].ParameterType == typeof(bool) &&
|
parameters[2].ParameterType == typeof(bool) &&
|
||||||
parameters[3].ParameterType == typeof(bool))
|
parameters[3].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[4].ParameterType == typeof(ulong).MakeByRefType())
|
||||||
|
{
|
||||||
|
allocateAtOrAbove = candidate;
|
||||||
|
}
|
||||||
|
else if (string.Equals(candidate.Name, "AllocateAt", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
if (parameters.Length == 3 &&
|
||||||
|
parameters[0].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[1].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[2].ParameterType == typeof(bool))
|
||||||
|
{
|
||||||
|
allocateAt = candidate;
|
||||||
|
allocateAtHasAllowAlternativeArg = false;
|
||||||
|
}
|
||||||
|
else if (parameters.Length == 4 &&
|
||||||
|
parameters[0].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[1].ParameterType == typeof(ulong) &&
|
||||||
|
parameters[2].ParameterType == typeof(bool) &&
|
||||||
|
parameters[3].ParameterType == typeof(bool))
|
||||||
|
{
|
||||||
|
allocateAt = candidate;
|
||||||
|
allocateAtHasAllowAlternativeArg = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allocateAtOrAbove is not null && allocateAt is not null)
|
||||||
{
|
{
|
||||||
allocateAt = candidate;
|
|
||||||
allocateAtHasAllowAlternativeArg = true;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (allocateAt is not null)
|
if (allocateAtOrAbove is not null || allocateAt is not null)
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -1721,6 +1748,18 @@ public static class KernelRuntimeCompatExports
|
|||||||
memoryObject = innerValue;
|
memoryObject = innerValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (allowSearch && allocateAtOrAbove is not null)
|
||||||
|
{
|
||||||
|
var searchArgs = new object[] { desiredAddress, length, false, alignment, 0UL };
|
||||||
|
var searchResult = allocateAtOrAbove.Invoke(memoryObject, searchArgs);
|
||||||
|
if (searchResult is bool trueValue && trueValue &&
|
||||||
|
searchArgs[4] is ulong searchedAddress && searchedAddress != 0)
|
||||||
|
{
|
||||||
|
mappedAddress = searchedAddress;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (allocateAt is null)
|
if (allocateAt is null)
|
||||||
{
|
{
|
||||||
Console.Error.WriteLine($"[LOADER][TRACE] reserve_virtual_range: AllocateAt missing on {ctx.Memory.GetType().FullName}");
|
Console.Error.WriteLine($"[LOADER][TRACE] reserve_virtual_range: AllocateAt missing on {ctx.Memory.GetType().FullName}");
|
||||||
@@ -1728,7 +1767,7 @@ public static class KernelRuntimeCompatExports
|
|||||||
}
|
}
|
||||||
|
|
||||||
var invokeArgs = allocateAtHasAllowAlternativeArg
|
var invokeArgs = allocateAtHasAllowAlternativeArg
|
||||||
? new object[] { desiredAddress, length, false, true }
|
? new object[] { desiredAddress, length, false, false }
|
||||||
: new object[] { desiredAddress, length, false };
|
: new object[] { desiredAddress, length, false };
|
||||||
var result = allocateAt.Invoke(memoryObject, invokeArgs);
|
var result = allocateAt.Invoke(memoryObject, invokeArgs);
|
||||||
if (result is not ulong allocated || allocated == 0)
|
if (result is not ulong allocated || allocated == 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user