From 2dbbc9bc05998baa94d7b1953d9e0ffc7f1651f8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 18 Jan 2024 14:08:40 -0300 Subject: [PATCH] Move most of signal handling to Ryujinx.Cpu project (#6128) * Move most of signal handling to Ryujinx.Cpu project * Format whitespace * Remove unused member * Format whitespace * This does not need to be public anymore --- src/ARMeilleure/Memory/IJitMemoryAllocator.cs | 2 - src/ARMeilleure/Memory/MemoryManagerType.cs | 2 +- ...ler.cs => NativeSignalHandlerGenerator.cs} | 199 ++---------------- .../Signal/WindowsPartialUnmapHandler.cs | 37 ++-- .../WindowsSignalHandlerRegistration.cs | 44 ---- src/ARMeilleure/Translation/Translator.cs | 5 - src/Ryujinx.Cpu/Jit/JitCpuContext.cs | 8 + src/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs | 2 - src/Ryujinx.Cpu/MemoryEhMeilleure.cs | 2 +- src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs | 179 ++++++++++++++++ .../Signal/UnixSignalHandlerRegistration.cs | 2 +- .../WindowsSignalHandlerRegistration.cs | 24 +++ 12 files changed, 253 insertions(+), 253 deletions(-) rename src/ARMeilleure/Signal/{NativeSignalHandler.cs => NativeSignalHandlerGenerator.cs} (62%) delete mode 100644 src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs create mode 100644 src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs rename src/{ARMeilleure => Ryujinx.Cpu}/Signal/UnixSignalHandlerRegistration.cs (98%) create mode 100644 src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs diff --git a/src/ARMeilleure/Memory/IJitMemoryAllocator.cs b/src/ARMeilleure/Memory/IJitMemoryAllocator.cs index 171bfd2f..ff64bf13 100644 --- a/src/ARMeilleure/Memory/IJitMemoryAllocator.cs +++ b/src/ARMeilleure/Memory/IJitMemoryAllocator.cs @@ -4,7 +4,5 @@ namespace ARMeilleure.Memory { IJitMemoryBlock Allocate(ulong size); IJitMemoryBlock Reserve(ulong size); - - ulong GetPageSize(); } } diff --git a/src/ARMeilleure/Memory/MemoryManagerType.cs b/src/ARMeilleure/Memory/MemoryManagerType.cs index 1e656ba2..b1cdbb06 100644 --- a/src/ARMeilleure/Memory/MemoryManagerType.cs +++ b/src/ARMeilleure/Memory/MemoryManagerType.cs @@ -31,7 +31,7 @@ namespace ARMeilleure.Memory HostMappedUnsafe, } - static class MemoryManagerTypeExtensions + public static class MemoryManagerTypeExtensions { public static bool IsHostMapped(this MemoryManagerType type) { diff --git a/src/ARMeilleure/Signal/NativeSignalHandler.cs b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs similarity index 62% rename from src/ARMeilleure/Signal/NativeSignalHandler.cs rename to src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs index 31ec16cb..c5e708e1 100644 --- a/src/ARMeilleure/Signal/NativeSignalHandler.cs +++ b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs @@ -1,63 +1,14 @@ using ARMeilleure.IntermediateRepresentation; -using ARMeilleure.Memory; using ARMeilleure.Translation; -using ARMeilleure.Translation.Cache; using System; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Signal { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct SignalHandlerRange + public static class NativeSignalHandlerGenerator { - public int IsActive; - public nuint RangeAddress; - public nuint RangeEndAddress; - public IntPtr ActionPointer; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct SignalHandlerConfig - { - /// - /// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct. - /// - public int StructAddressOffset; - - /// - /// The byte offset of the write flag in the SigInfo or ExceptionRecord struct. - /// - public int StructWriteOffset; - - /// - /// The sigaction handler that was registered before this one. (unix only) - /// - public nuint UnixOldSigaction; - - /// - /// The type of the previous sigaction. True for the 3 argument variant. (unix only) - /// - public int UnixOldSigaction3Arg; - - public SignalHandlerRange Range0; - public SignalHandlerRange Range1; - public SignalHandlerRange Range2; - public SignalHandlerRange Range3; - public SignalHandlerRange Range4; - public SignalHandlerRange Range5; - public SignalHandlerRange Range6; - public SignalHandlerRange Range7; - } - - public static class NativeSignalHandler - { - private delegate void UnixExceptionHandler(int sig, IntPtr info, IntPtr ucontext); - [UnmanagedFunctionPointer(CallingConvention.Winapi)] - private delegate int VectoredExceptionHandler(IntPtr exceptionInfo); - - private const int MaxTrackedRanges = 8; + public const int MaxTrackedRanges = 8; private const int StructAddressOffset = 0; private const int StructWriteOffset = 4; @@ -70,125 +21,10 @@ namespace ARMeilleure.Signal private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005; - private static ulong _pageSize; - private static ulong _pageMask; - - private static readonly IntPtr _handlerConfig; - private static IntPtr _signalHandlerPtr; - private static IntPtr _signalHandlerHandle; - - private static readonly object _lock = new(); - private static bool _initialized; - - static NativeSignalHandler() + private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize, ulong pageSize) { - _handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf()); - ref SignalHandlerConfig config = ref GetConfigRef(); + ulong pageMask = pageSize - 1; - config = new SignalHandlerConfig(); - } - - public static void Initialize(IJitMemoryAllocator allocator) - { - JitCache.Initialize(allocator); - } - - public static void InitializeSignalHandler(ulong pageSize, Func customSignalHandlerFactory = null) - { - if (_initialized) - { - return; - } - - lock (_lock) - { - if (_initialized) - { - return; - } - - _pageSize = pageSize; - _pageMask = pageSize - 1; - - ref SignalHandlerConfig config = ref GetConfigRef(); - - if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) - { - _signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig)); - - if (customSignalHandlerFactory != null) - { - _signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr); - } - - var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); - - config.UnixOldSigaction = (nuint)(ulong)old.sa_handler; - config.UnixOldSigaction3Arg = old.sa_flags & 4; - } - else - { - config.StructAddressOffset = 40; // ExceptionInformation1 - config.StructWriteOffset = 32; // ExceptionInformation0 - - _signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateWindowsSignalHandler(_handlerConfig)); - - if (customSignalHandlerFactory != null) - { - _signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr); - } - - _signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); - } - - _initialized = true; - } - } - - private static unsafe ref SignalHandlerConfig GetConfigRef() - { - return ref Unsafe.AsRef((void*)_handlerConfig); - } - - public static unsafe bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action) - { - var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0; - - for (int i = 0; i < MaxTrackedRanges; i++) - { - if (ranges[i].IsActive == 0) - { - ranges[i].RangeAddress = address; - ranges[i].RangeEndAddress = endAddress; - ranges[i].ActionPointer = action; - ranges[i].IsActive = 1; - - return true; - } - } - - return false; - } - - public static unsafe bool RemoveTrackedRegion(nuint address) - { - var ranges = &((SignalHandlerConfig*)_handlerConfig)->Range0; - - for (int i = 0; i < MaxTrackedRanges; i++) - { - if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address) - { - ranges[i].IsActive = 0; - - return true; - } - } - - return false; - } - - private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite) - { Operand inRegionLocal = context.AllocateLocal(OperandType.I32); context.Copy(inRegionLocal, Const(0)); @@ -196,7 +32,7 @@ namespace ARMeilleure.Signal for (int i = 0; i < MaxTrackedRanges; i++) { - ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf()); + ulong rangeBaseOffset = (ulong)(RangeOffset + i * rangeStructSize); Operand nextLabel = Label(); @@ -210,13 +46,12 @@ namespace ARMeilleure.Signal // Is the fault address within this tracked region? Operand inRange = context.BitwiseAnd( context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI), - context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI) - ); + context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI)); // Only call tracking if in range. context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold); - Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~_pageMask)); + Operand offset = context.BitwiseAnd(context.Subtract(faultAddress, rangeAddress), Const(~pageMask)); // Call the tracking action, with the pointer's relative offset to the base address. Operand trackingActionPtr = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 20)); @@ -227,7 +62,7 @@ namespace ARMeilleure.Signal // Tracking action should be non-null to call it, otherwise assume false return. context.BranchIfFalse(skipActionLabel, trackingActionPtr); - Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(_pageSize), isWrite); + Operand result = context.Call(trackingActionPtr, OperandType.I32, offset, Const(pageSize), isWrite); context.Copy(inRegionLocal, result); context.MarkLabel(skipActionLabel); @@ -269,8 +104,7 @@ namespace ARMeilleure.Signal Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset))); return context.BitwiseAnd(esr, Const(0x40ul)); } - - if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) { const ulong ErrOffset = 4; // __es.__err Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset))); @@ -310,8 +144,7 @@ namespace ARMeilleure.Signal Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul))); return context.BitwiseAnd(esr, Const(0x40ul)); } - - if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) { const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR] Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset))); @@ -322,7 +155,7 @@ namespace ARMeilleure.Signal throw new PlatformNotSupportedException(); } - private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr) + public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize) { EmitterContext context = new(); @@ -335,7 +168,7 @@ namespace ARMeilleure.Signal Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1. - Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite); + Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize); Operand endLabel = Label(); @@ -367,10 +200,10 @@ namespace ARMeilleure.Signal OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); + return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code; } - private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr) + public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize) { EmitterContext context = new(); @@ -399,7 +232,7 @@ namespace ARMeilleure.Signal Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1. - Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite); + Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize); Operand endLabel = Label(); @@ -421,7 +254,7 @@ namespace ARMeilleure.Signal OperandType[] argTypes = new OperandType[] { OperandType.I64 }; - return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); + return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code; } } } diff --git a/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs b/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs index 27a9ea83..3bf6a449 100644 --- a/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs +++ b/src/ARMeilleure/Signal/WindowsPartialUnmapHandler.cs @@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation; using ARMeilleure.Translation; using Ryujinx.Common.Memory.PartialUnmaps; using System; - +using System.Runtime.InteropServices; using static ARMeilleure.IntermediateRepresentation.Operand.Factory; namespace ARMeilleure.Signal @@ -10,8 +10,28 @@ namespace ARMeilleure.Signal /// /// Methods to handle signals caused by partial unmaps. See the structs for C# implementations of the methods. /// - internal static class WindowsPartialUnmapHandler + internal static partial class WindowsPartialUnmapHandler { + [LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")] + private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); + + [LibraryImport("kernel32.dll", SetLastError = true)] + private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName); + + private static IntPtr _getCurrentThreadIdPtr; + + public static IntPtr GetCurrentThreadIdFunc() + { + if (_getCurrentThreadIdPtr == IntPtr.Zero) + { + IntPtr handle = LoadLibrary("kernel32.dll"); + + _getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId"); + } + + return _getCurrentThreadIdPtr; + } + public static Operand EmitRetryFromAccessViolation(EmitterContext context) { IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState; @@ -20,7 +40,7 @@ namespace ARMeilleure.Signal // Get the lock first. EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset)); - IntPtr getCurrentThreadId = WindowsSignalHandlerRegistration.GetCurrentThreadIdFunc(); + IntPtr getCurrentThreadId = GetCurrentThreadIdFunc(); Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32); Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0)); @@ -137,17 +157,6 @@ namespace ARMeilleure.Signal return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset)); } -#pragma warning disable IDE0051 // Remove unused private member - private static void EmitThreadLocalMapIntRelease(EmitterContext context, IntPtr threadLocalMapPtr, Operand threadId, Operand index) - { - Operand offset = context.Multiply(index, Const(sizeof(int))); - Operand idsPtr = Const((ulong)IntPtr.Add(threadLocalMapPtr, ThreadLocalMap.ThreadIdsOffset)); - Operand idPtr = context.Add(idsPtr, context.SignExtend32(OperandType.I64, offset)); - - context.CompareAndSwap(idPtr, threadId, Const(0)); - } -#pragma warning restore IDE0051 - private static void EmitAtomicAddI32(EmitterContext context, Operand ptr, Operand additive) { Operand loop = Label(); diff --git a/src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs b/src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs deleted file mode 100644 index 5444da0c..00000000 --- a/src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace ARMeilleure.Signal -{ - unsafe partial class WindowsSignalHandlerRegistration - { - [LibraryImport("kernel32.dll")] - private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler); - - [LibraryImport("kernel32.dll")] - private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle); - - [LibraryImport("kernel32.dll", SetLastError = true, EntryPoint = "LoadLibraryA")] - private static partial IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string lpFileName); - - [LibraryImport("kernel32.dll", SetLastError = true)] - private static partial IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procName); - - private static IntPtr _getCurrentThreadIdPtr; - - public static IntPtr RegisterExceptionHandler(IntPtr action) - { - return AddVectoredExceptionHandler(1, action); - } - - public static bool RemoveExceptionHandler(IntPtr handle) - { - return RemoveVectoredExceptionHandler(handle) != 0; - } - - public static IntPtr GetCurrentThreadIdFunc() - { - if (_getCurrentThreadIdPtr == IntPtr.Zero) - { - IntPtr handle = LoadLibrary("kernel32.dll"); - - _getCurrentThreadIdPtr = GetProcAddress(handle, "GetCurrentThreadId"); - } - - return _getCurrentThreadIdPtr; - } - } -} diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index dc18038b..7f6a25b0 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -79,11 +79,6 @@ namespace ARMeilleure.Translation Stubs = new TranslatorStubs(this); FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; - - if (memory.Type.IsHostMapped()) - { - NativeSignalHandler.InitializeSignalHandler(allocator.GetPageSize()); - } } public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled) diff --git a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs index 24bc1e59..5876346a 100644 --- a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs +++ b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs @@ -1,5 +1,7 @@ using ARMeilleure.Memory; using ARMeilleure.Translation; +using Ryujinx.Cpu.Signal; +using Ryujinx.Memory; namespace Ryujinx.Cpu.Jit { @@ -12,6 +14,12 @@ namespace Ryujinx.Cpu.Jit { _tickSource = tickSource; _translator = new Translator(new JitMemoryAllocator(), memory, for64Bit); + + if (memory.Type.IsHostMapped()) + { + NativeSignalHandler.InitializeSignalHandler(MemoryBlock.GetPageSize()); + } + memory.UnmapEvent += UnmapHandler; } diff --git a/src/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs b/src/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs index 529a1a80..eb665c2d 100644 --- a/src/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs +++ b/src/Ryujinx.Cpu/Jit/JitMemoryAllocator.cs @@ -7,7 +7,5 @@ namespace Ryujinx.Cpu.Jit { public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None); public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Jit); - - public ulong GetPageSize() => MemoryBlock.GetPageSize(); } } diff --git a/src/Ryujinx.Cpu/MemoryEhMeilleure.cs b/src/Ryujinx.Cpu/MemoryEhMeilleure.cs index 54e232d9..f3a5b056 100644 --- a/src/Ryujinx.Cpu/MemoryEhMeilleure.cs +++ b/src/Ryujinx.Cpu/MemoryEhMeilleure.cs @@ -1,4 +1,4 @@ -using ARMeilleure.Signal; +using Ryujinx.Cpu.Signal; using Ryujinx.Memory; using Ryujinx.Memory.Tracking; using System; diff --git a/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs new file mode 100644 index 00000000..5a9d92cc --- /dev/null +++ b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs @@ -0,0 +1,179 @@ +using ARMeilleure.Signal; +using Ryujinx.Common; +using Ryujinx.Memory; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Ryujinx.Cpu.Signal +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SignalHandlerRange + { + public int IsActive; + public nuint RangeAddress; + public nuint RangeEndAddress; + public IntPtr ActionPointer; + } + + [InlineArray(NativeSignalHandlerGenerator.MaxTrackedRanges)] + struct SignalHandlerRangeArray + { + public SignalHandlerRange Range0; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SignalHandlerConfig + { + /// + /// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct. + /// + public int StructAddressOffset; + + /// + /// The byte offset of the write flag in the SigInfo or ExceptionRecord struct. + /// + public int StructWriteOffset; + + /// + /// The sigaction handler that was registered before this one. (unix only) + /// + public nuint UnixOldSigaction; + + /// + /// The type of the previous sigaction. True for the 3 argument variant. (unix only) + /// + public int UnixOldSigaction3Arg; + + /// + /// Fixed size array of tracked ranges. + /// + public SignalHandlerRangeArray Ranges; + } + + static class NativeSignalHandler + { + private static readonly IntPtr _handlerConfig; + private static IntPtr _signalHandlerPtr; + + private static MemoryBlock _codeBlock; + + private static readonly object _lock = new(); + private static bool _initialized; + + static NativeSignalHandler() + { + _handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf()); + ref SignalHandlerConfig config = ref GetConfigRef(); + + config = new SignalHandlerConfig(); + } + + public static void InitializeSignalHandler(ulong pageSize, Func customSignalHandlerFactory = null) + { + if (_initialized) + { + return; + } + + lock (_lock) + { + if (_initialized) + { + return; + } + + int rangeStructSize = Unsafe.SizeOf(); + + ref SignalHandlerConfig config = ref GetConfigRef(); + + if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) + { + _signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateUnixSignalHandler(_handlerConfig, rangeStructSize, pageSize)); + + if (customSignalHandlerFactory != null) + { + _signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr); + } + + var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); + + config.UnixOldSigaction = (nuint)(ulong)old.sa_handler; + config.UnixOldSigaction3Arg = old.sa_flags & 4; + } + else + { + config.StructAddressOffset = 40; // ExceptionInformation1 + config.StructWriteOffset = 32; // ExceptionInformation0 + + _signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateWindowsSignalHandler(_handlerConfig, rangeStructSize, pageSize)); + + if (customSignalHandlerFactory != null) + { + _signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr); + } + + WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr); + } + + _initialized = true; + } + } + + private static IntPtr MapCode(ReadOnlySpan code) + { + Debug.Assert(_codeBlock == null); + + ulong codeSizeAligned = BitUtils.AlignUp((ulong)code.Length, MemoryBlock.GetPageSize()); + + _codeBlock = new MemoryBlock(codeSizeAligned); + _codeBlock.Write(0, code); + _codeBlock.Reprotect(0, codeSizeAligned, MemoryPermission.ReadAndExecute); + + return _codeBlock.Pointer; + } + + private static unsafe ref SignalHandlerConfig GetConfigRef() + { + return ref Unsafe.AsRef((void*)_handlerConfig); + } + + public static bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action) + { + Span ranges = GetConfigRef().Ranges; + + for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++) + { + if (ranges[i].IsActive == 0) + { + ranges[i].RangeAddress = address; + ranges[i].RangeEndAddress = endAddress; + ranges[i].ActionPointer = action; + ranges[i].IsActive = 1; + + return true; + } + } + + return false; + } + + public static bool RemoveTrackedRegion(nuint address) + { + Span ranges = GetConfigRef().Ranges; + + for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++) + { + if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address) + { + ranges[i].IsActive = 0; + + return true; + } + } + + return false; + } + } +} diff --git a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs similarity index 98% rename from src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs rename to src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs index 70e9f220..e88a6c0f 100644 --- a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs +++ b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs @@ -1,7 +1,7 @@ using System; using System.Runtime.InteropServices; -namespace ARMeilleure.Signal +namespace Ryujinx.Cpu.Signal { static partial class UnixSignalHandlerRegistration { diff --git a/src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs b/src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs new file mode 100644 index 00000000..1fbce0f7 --- /dev/null +++ b/src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs @@ -0,0 +1,24 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Cpu.Signal +{ + static partial class WindowsSignalHandlerRegistration + { + [LibraryImport("kernel32.dll")] + private static partial IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler); + + [LibraryImport("kernel32.dll")] + private static partial ulong RemoveVectoredExceptionHandler(IntPtr handle); + + public static IntPtr RegisterExceptionHandler(IntPtr action) + { + return AddVectoredExceptionHandler(1, action); + } + + public static bool RemoveExceptionHandler(IntPtr handle) + { + return RemoveVectoredExceptionHandler(handle) != 0; + } + } +}