diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs index f226f3b..059ec9d 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Diagnostics.cs @@ -463,6 +463,29 @@ public sealed partial class DirectExecutionBackend return false; } + // The committed readable run of host pages that contains address. + private unsafe static bool TryQueryReadableRange(ulong address, out ulong start, out ulong end) + { + start = 0; + end = 0; + if (address <= 65536 || address >= 140737488355328L || + VirtualQuery((void*)address, out var info, (nuint)sizeof(MEMORY_BASIC_INFORMATION64)) == 0) + { + return false; + } + + var regionEnd = info.BaseAddress + info.RegionSize; + if (regionEnd < info.BaseAddress || address > regionEnd - 8 || + info.State != 4096 || !IsReadableProtection(info.Protect)) + { + return false; + } + + start = info.BaseAddress; + end = regionEnd; + return true; + } + private unsafe static bool TryReadStackU64(ulong address, out ulong value) { value = 0uL; diff --git a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs index 0456569..dc4ef3a 100644 --- a/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs +++ b/src/SharpEmu.Core/Cpu/Native/DirectExecutionBackend.Imports.cs @@ -238,8 +238,8 @@ public sealed partial class DirectExecutionBackend if (probeTarget && Interlocked.Increment(ref _probeImportReturnAddressCount) <= 2048) { - var frameValue = TryReadStackU64(value4, out var savedRbp) ? savedRbp : 0; - var frameReturn = TryReadStackU64(value4 + sizeof(ulong), out var savedReturn) + var frameValue = TryReadImportStackU64(value4, out var savedRbp) ? savedRbp : 0; + var frameReturn = TryReadImportStackU64(value4 + sizeof(ulong), out var savedReturn) ? savedReturn : 0; Console.Error.WriteLine( @@ -1217,10 +1217,45 @@ public sealed partial class DirectExecutionBackend } } + // Every import reads its stack arguments. On POSIX a region query takes the global region lock, + // so keep the last readable range per thread until any mapping changes. The signal handlers + // use TryReadStackU64 instead: they must not touch thread-static storage. + [ThreadStatic] private static ulong _importReadableStart; + [ThreadStatic] private static ulong _importReadableEnd; + [ThreadStatic] private static long _importReadableGeneration; + + private unsafe static bool TryReadImportStackU64(ulong address, out ulong value) + { + if (OperatingSystem.IsWindows()) + { + return TryReadHostQword(address, out value); + } + + var generation = HostMemory.MappingGeneration; + if (generation == _importReadableGeneration && + address >= _importReadableStart && address <= _importReadableEnd - 8) + { + value = *(ulong*)address; + return true; + } + + if (!TryQueryReadableRange(address, out var start, out var end)) + { + value = 0; + return false; + } + + _importReadableStart = start; + _importReadableEnd = end; + _importReadableGeneration = generation; + value = *(ulong*)address; + return true; + } + private static ulong ReadImportStackArgument(nint argPackPtr, int index) { var address = checked((ulong)argPackPtr + 104UL + (ulong)index * sizeof(ulong)); - return TryReadHostQword(address, out var value) ? value : 0; + return TryReadImportStackU64(address, out var value) ? value : 0; } private static GuestCpuContinuation CaptureImportBoundaryContinuation( diff --git a/src/SharpEmu.HLE/Host/Posix/PosixHostMemory.cs b/src/SharpEmu.HLE/Host/Posix/PosixHostMemory.cs index 82504d6..f427e67 100644 --- a/src/SharpEmu.HLE/Host/Posix/PosixHostMemory.cs +++ b/src/SharpEmu.HLE/Host/Posix/PosixHostMemory.cs @@ -328,6 +328,7 @@ internal sealed unsafe class PosixHostMemory : IHostMemory } } + HostMemory.OnMappingChanged(); Regions[(ulong)result] = new Region { Base = (ulong)result, @@ -357,7 +358,8 @@ internal sealed unsafe class PosixHostMemory : IHostMemory : munmap((nint)address, (nuint)region.Size) == 0; if (released) { - Regions.Remove((ulong)address); + HostMemory.OnMappingChanged(); + Regions.Remove((ulong)address); } return released; @@ -492,6 +494,7 @@ internal sealed unsafe class PosixHostMemory : IHostMemory private static void SetProtectRangeLocked(Region region, ulong start, ulong size, uint protect) { + HostMemory.OnMappingChanged(); if (start == region.Base && size >= region.Size) { region.DefaultProtect = protect; diff --git a/src/SharpEmu.HLE/Host/Posix/PosixViewRegions.cs b/src/SharpEmu.HLE/Host/Posix/PosixViewRegions.cs index b238eed..24869ab 100644 --- a/src/SharpEmu.HLE/Host/Posix/PosixViewRegions.cs +++ b/src/SharpEmu.HLE/Host/Posix/PosixViewRegions.cs @@ -13,6 +13,7 @@ internal static class PosixViewRegions internal static void Replace(ulong address, ulong size, uint state, uint protection) { + HostMemory.OnMappingChanged(); var end = RoundPageEnd(address, size); var replacement = new List(Regions.Count + 2); foreach (var region in Regions) @@ -50,6 +51,7 @@ internal static class PosixViewRegions // Mapping creates the storage. Fault-time protection updates must not allocate. internal static void ChangeProtection(ulong address, ulong size, uint protection) { + HostMemory.OnMappingChanged(); var end = RoundPageEnd(address, size); var pageSize = (ulong)Environment.SystemPageSize; foreach (var region in Regions) diff --git a/src/SharpEmu.HLE/HostMemory.cs b/src/SharpEmu.HLE/HostMemory.cs index 4c07442..6586bf5 100644 --- a/src/SharpEmu.HLE/HostMemory.cs +++ b/src/SharpEmu.HLE/HostMemory.cs @@ -16,6 +16,14 @@ namespace SharpEmu.HLE; /// public static unsafe class HostMemory { + private static long _mappingGeneration; + + // Changes whenever a POSIX mapping or its protection changes, so callers can cache a + // query result until the address space changes. + public static long MappingGeneration => Volatile.Read(ref _mappingGeneration); + + internal static void OnMappingChanged() => Interlocked.Increment(ref _mappingGeneration); + public const uint MEM_COMMIT = 0x1000; public const uint MEM_RESERVE = 0x2000; public const uint MEM_RELEASE = 0x8000; @@ -254,6 +262,7 @@ public static unsafe class HostMemory } } + HostMemory.OnMappingChanged(); Regions[(ulong)result] = new Region { Base = (ulong)result, @@ -277,6 +286,7 @@ public static unsafe class HostMemory return false; } + HostMemory.OnMappingChanged(); Regions.Remove((ulong)address); return munmap((nint)address, (nuint)region.Size) == 0; } @@ -439,6 +449,7 @@ public static unsafe class HostMemory private static void SetProtectRangeLocked(Region region, ulong start, ulong size, uint protect) { + HostMemory.OnMappingChanged(); if (start == region.Base && size >= region.Size) { region.DefaultProtect = protect;