From 2dbbc9bc05998baa94d7b1953d9e0ffc7f1651f8 Mon Sep 17 00:00:00 2001 From: gdkchan Date: Thu, 18 Jan 2024 14:08:40 -0300 Subject: 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 +- src/ARMeilleure/Signal/NativeSignalHandler.cs | 427 --------------------- .../Signal/NativeSignalHandlerGenerator.cs | 260 +++++++++++++ .../Signal/UnixSignalHandlerRegistration.cs | 83 ---- .../Signal/WindowsPartialUnmapHandler.cs | 37 +- .../Signal/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 | 83 ++++ .../Signal/WindowsSignalHandlerRegistration.cs | 24 ++ 14 files changed, 579 insertions(+), 579 deletions(-) delete mode 100644 src/ARMeilleure/Signal/NativeSignalHandler.cs create mode 100644 src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs delete mode 100644 src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs delete mode 100644 src/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs create mode 100644 src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs create mode 100644 src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs create mode 100644 src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs (limited to 'src') 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/NativeSignalHandler.cs deleted file mode 100644 index 31ec16cb..00000000 --- a/src/ARMeilleure/Signal/NativeSignalHandler.cs +++ /dev/null @@ -1,427 +0,0 @@ -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 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; - - private const int StructAddressOffset = 0; - private const int StructWriteOffset = 4; - private const int UnixOldSigaction = 8; - private const int UnixOldSigaction3Arg = 16; - private const int RangeOffset = 20; - - private const int EXCEPTION_CONTINUE_SEARCH = 0; - private const int EXCEPTION_CONTINUE_EXECUTION = -1; - - 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() - { - _handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf()); - ref SignalHandlerConfig config = ref GetConfigRef(); - - 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)); - - Operand endLabel = Label(); - - for (int i = 0; i < MaxTrackedRanges; i++) - { - ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf()); - - Operand nextLabel = Label(); - - Operand isActive = context.Load(OperandType.I32, Const((ulong)signalStructPtr + rangeBaseOffset)); - - context.BranchIfFalse(nextLabel, isActive); - - Operand rangeAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 4)); - Operand rangeEndAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 12)); - - // Is the fault address within this tracked region? - Operand inRange = context.BitwiseAnd( - context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI), - 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)); - - // 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)); - - context.Copy(inRegionLocal, Const(0)); - - Operand skipActionLabel = Label(); - - // 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); - context.Copy(inRegionLocal, result); - - context.MarkLabel(skipActionLabel); - - // If the tracking action returns false or does not exist, it might be an invalid access due to a partial overlap on Windows. - if (OperatingSystem.IsWindows()) - { - context.BranchIfTrue(endLabel, inRegionLocal); - - context.Copy(inRegionLocal, WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context)); - } - - context.Branch(endLabel); - - context.MarkLabel(nextLabel); - } - - context.MarkLabel(endLabel); - - return context.Copy(inRegionLocal); - } - - private static Operand GenerateUnixFaultAddress(EmitterContext context, Operand sigInfoPtr) - { - ulong structAddressOffset = OperatingSystem.IsMacOS() ? 24ul : 16ul; // si_addr - return context.Load(OperandType.I64, context.Add(sigInfoPtr, Const(structAddressOffset))); - } - - private static Operand GenerateUnixWriteFlag(EmitterContext context, Operand ucontextPtr) - { - if (OperatingSystem.IsMacOS()) - { - const ulong McontextOffset = 48; // uc_mcontext - Operand ctxPtr = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(McontextOffset))); - - if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) - { - const ulong EsrOffset = 8; // __es.__esr - Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset))); - return context.BitwiseAnd(esr, Const(0x40ul)); - } - - if (RuntimeInformation.ProcessArchitecture == Architecture.X64) - { - const ulong ErrOffset = 4; // __es.__err - Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset))); - return context.BitwiseAnd(err, Const(2ul)); - } - } - else if (OperatingSystem.IsLinux()) - { - if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) - { - Operand auxPtr = context.AllocateLocal(OperandType.I64); - - Operand loopLabel = Label(); - Operand successLabel = Label(); - - const ulong AuxOffset = 464; // uc_mcontext.__reserved - const uint EsrMagic = 0x45535201; - - context.Copy(auxPtr, context.Add(ucontextPtr, Const(AuxOffset))); - - context.MarkLabel(loopLabel); - - // _aarch64_ctx::magic - Operand magic = context.Load(OperandType.I32, auxPtr); - // _aarch64_ctx::size - Operand size = context.Load(OperandType.I32, context.Add(auxPtr, Const(4ul))); - - context.BranchIf(successLabel, magic, Const(EsrMagic), Comparison.Equal); - - context.Copy(auxPtr, context.Add(auxPtr, context.ZeroExtend32(OperandType.I64, size))); - - context.Branch(loopLabel); - - context.MarkLabel(successLabel); - - // esr_context::esr - Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul))); - return context.BitwiseAnd(esr, Const(0x40ul)); - } - - 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))); - return context.BitwiseAnd(err, Const(2ul)); - } - } - - throw new PlatformNotSupportedException(); - } - - private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr) - { - EmitterContext context = new(); - - // (int sig, SigInfo* sigInfo, void* ucontext) - Operand sigInfoPtr = context.LoadArgument(OperandType.I64, 1); - Operand ucontextPtr = context.LoadArgument(OperandType.I64, 2); - - Operand faultAddress = GenerateUnixFaultAddress(context, sigInfoPtr); - Operand writeFlag = GenerateUnixWriteFlag(context, ucontextPtr); - - Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1. - - Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite); - - Operand endLabel = Label(); - - context.BranchIfTrue(endLabel, isInRegion); - - Operand unixOldSigaction = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction)); - Operand unixOldSigaction3Arg = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction3Arg)); - Operand threeArgLabel = Label(); - - context.BranchIfTrue(threeArgLabel, unixOldSigaction3Arg); - - context.Call(unixOldSigaction, OperandType.None, context.LoadArgument(OperandType.I32, 0)); - context.Branch(endLabel); - - context.MarkLabel(threeArgLabel); - - context.Call(unixOldSigaction, - OperandType.None, - context.LoadArgument(OperandType.I32, 0), - sigInfoPtr, - context.LoadArgument(OperandType.I64, 2) - ); - - context.MarkLabel(endLabel); - - context.Return(); - - ControlFlowGraph cfg = context.GetControlFlowGraph(); - - OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 }; - - return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); - } - - private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr) - { - EmitterContext context = new(); - - // (ExceptionPointers* exceptionInfo) - Operand exceptionInfoPtr = context.LoadArgument(OperandType.I64, 0); - Operand exceptionRecordPtr = context.Load(OperandType.I64, exceptionInfoPtr); - - // First thing's first - this catches a number of exceptions, but we only want access violations. - Operand validExceptionLabel = Label(); - - Operand exceptionCode = context.Load(OperandType.I32, exceptionRecordPtr); - - context.BranchIf(validExceptionLabel, exceptionCode, Const(EXCEPTION_ACCESS_VIOLATION), Comparison.Equal); - - context.Return(Const(EXCEPTION_CONTINUE_SEARCH)); // Don't handle this one. - - context.MarkLabel(validExceptionLabel); - - // Next, read the address of the invalid access, and whether it is a write or not. - - Operand structAddressOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructAddressOffset)); - Operand structWriteOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructWriteOffset)); - - Operand faultAddress = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset))); - Operand writeFlag = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structWriteOffset))); - - Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1. - - Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite); - - Operand endLabel = Label(); - - // If the region check result is false, then run the next vectored exception handler. - - context.BranchIfTrue(endLabel, isInRegion); - - context.Return(Const(EXCEPTION_CONTINUE_SEARCH)); - - context.MarkLabel(endLabel); - - // Otherwise, return to execution. - - context.Return(Const(EXCEPTION_CONTINUE_EXECUTION)); - - // Compile and return the function. - - ControlFlowGraph cfg = context.GetControlFlowGraph(); - - OperandType[] argTypes = new OperandType[] { OperandType.I64 }; - - return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map(); - } - } -} diff --git a/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs new file mode 100644 index 00000000..c5e708e1 --- /dev/null +++ b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs @@ -0,0 +1,260 @@ +using ARMeilleure.IntermediateRepresentation; +using ARMeilleure.Translation; +using System; +using System.Runtime.InteropServices; +using static ARMeilleure.IntermediateRepresentation.Operand.Factory; + +namespace ARMeilleure.Signal +{ + public static class NativeSignalHandlerGenerator + { + public const int MaxTrackedRanges = 8; + + private const int StructAddressOffset = 0; + private const int StructWriteOffset = 4; + private const int UnixOldSigaction = 8; + private const int UnixOldSigaction3Arg = 16; + private const int RangeOffset = 20; + + private const int EXCEPTION_CONTINUE_SEARCH = 0; + private const int EXCEPTION_CONTINUE_EXECUTION = -1; + + private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005; + + private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize, ulong pageSize) + { + ulong pageMask = pageSize - 1; + + Operand inRegionLocal = context.AllocateLocal(OperandType.I32); + context.Copy(inRegionLocal, Const(0)); + + Operand endLabel = Label(); + + for (int i = 0; i < MaxTrackedRanges; i++) + { + ulong rangeBaseOffset = (ulong)(RangeOffset + i * rangeStructSize); + + Operand nextLabel = Label(); + + Operand isActive = context.Load(OperandType.I32, Const((ulong)signalStructPtr + rangeBaseOffset)); + + context.BranchIfFalse(nextLabel, isActive); + + Operand rangeAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 4)); + Operand rangeEndAddress = context.Load(OperandType.I64, Const((ulong)signalStructPtr + rangeBaseOffset + 12)); + + // Is the fault address within this tracked region? + Operand inRange = context.BitwiseAnd( + context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI), + 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)); + + // 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)); + + context.Copy(inRegionLocal, Const(0)); + + Operand skipActionLabel = Label(); + + // 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); + context.Copy(inRegionLocal, result); + + context.MarkLabel(skipActionLabel); + + // If the tracking action returns false or does not exist, it might be an invalid access due to a partial overlap on Windows. + if (OperatingSystem.IsWindows()) + { + context.BranchIfTrue(endLabel, inRegionLocal); + + context.Copy(inRegionLocal, WindowsPartialUnmapHandler.EmitRetryFromAccessViolation(context)); + } + + context.Branch(endLabel); + + context.MarkLabel(nextLabel); + } + + context.MarkLabel(endLabel); + + return context.Copy(inRegionLocal); + } + + private static Operand GenerateUnixFaultAddress(EmitterContext context, Operand sigInfoPtr) + { + ulong structAddressOffset = OperatingSystem.IsMacOS() ? 24ul : 16ul; // si_addr + return context.Load(OperandType.I64, context.Add(sigInfoPtr, Const(structAddressOffset))); + } + + private static Operand GenerateUnixWriteFlag(EmitterContext context, Operand ucontextPtr) + { + if (OperatingSystem.IsMacOS()) + { + const ulong McontextOffset = 48; // uc_mcontext + Operand ctxPtr = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(McontextOffset))); + + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + const ulong EsrOffset = 8; // __es.__esr + Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset))); + return context.BitwiseAnd(esr, Const(0x40ul)); + } + else if (RuntimeInformation.ProcessArchitecture == Architecture.X64) + { + const ulong ErrOffset = 4; // __es.__err + Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset))); + return context.BitwiseAnd(err, Const(2ul)); + } + } + else if (OperatingSystem.IsLinux()) + { + if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64) + { + Operand auxPtr = context.AllocateLocal(OperandType.I64); + + Operand loopLabel = Label(); + Operand successLabel = Label(); + + const ulong AuxOffset = 464; // uc_mcontext.__reserved + const uint EsrMagic = 0x45535201; + + context.Copy(auxPtr, context.Add(ucontextPtr, Const(AuxOffset))); + + context.MarkLabel(loopLabel); + + // _aarch64_ctx::magic + Operand magic = context.Load(OperandType.I32, auxPtr); + // _aarch64_ctx::size + Operand size = context.Load(OperandType.I32, context.Add(auxPtr, Const(4ul))); + + context.BranchIf(successLabel, magic, Const(EsrMagic), Comparison.Equal); + + context.Copy(auxPtr, context.Add(auxPtr, context.ZeroExtend32(OperandType.I64, size))); + + context.Branch(loopLabel); + + context.MarkLabel(successLabel); + + // esr_context::esr + Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul))); + return context.BitwiseAnd(esr, Const(0x40ul)); + } + 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))); + return context.BitwiseAnd(err, Const(2ul)); + } + } + + throw new PlatformNotSupportedException(); + } + + public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize) + { + EmitterContext context = new(); + + // (int sig, SigInfo* sigInfo, void* ucontext) + Operand sigInfoPtr = context.LoadArgument(OperandType.I64, 1); + Operand ucontextPtr = context.LoadArgument(OperandType.I64, 2); + + Operand faultAddress = GenerateUnixFaultAddress(context, sigInfoPtr); + Operand writeFlag = GenerateUnixWriteFlag(context, ucontextPtr); + + Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1. + + Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize); + + Operand endLabel = Label(); + + context.BranchIfTrue(endLabel, isInRegion); + + Operand unixOldSigaction = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction)); + Operand unixOldSigaction3Arg = context.Load(OperandType.I64, Const((ulong)signalStructPtr + UnixOldSigaction3Arg)); + Operand threeArgLabel = Label(); + + context.BranchIfTrue(threeArgLabel, unixOldSigaction3Arg); + + context.Call(unixOldSigaction, OperandType.None, context.LoadArgument(OperandType.I32, 0)); + context.Branch(endLabel); + + context.MarkLabel(threeArgLabel); + + context.Call(unixOldSigaction, + OperandType.None, + context.LoadArgument(OperandType.I32, 0), + sigInfoPtr, + context.LoadArgument(OperandType.I64, 2) + ); + + context.MarkLabel(endLabel); + + context.Return(); + + ControlFlowGraph cfg = context.GetControlFlowGraph(); + + OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 }; + + return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code; + } + + public static byte[] GenerateWindowsSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize) + { + EmitterContext context = new(); + + // (ExceptionPointers* exceptionInfo) + Operand exceptionInfoPtr = context.LoadArgument(OperandType.I64, 0); + Operand exceptionRecordPtr = context.Load(OperandType.I64, exceptionInfoPtr); + + // First thing's first - this catches a number of exceptions, but we only want access violations. + Operand validExceptionLabel = Label(); + + Operand exceptionCode = context.Load(OperandType.I32, exceptionRecordPtr); + + context.BranchIf(validExceptionLabel, exceptionCode, Const(EXCEPTION_ACCESS_VIOLATION), Comparison.Equal); + + context.Return(Const(EXCEPTION_CONTINUE_SEARCH)); // Don't handle this one. + + context.MarkLabel(validExceptionLabel); + + // Next, read the address of the invalid access, and whether it is a write or not. + + Operand structAddressOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructAddressOffset)); + Operand structWriteOffset = context.Load(OperandType.I32, Const((ulong)signalStructPtr + StructWriteOffset)); + + Operand faultAddress = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset))); + Operand writeFlag = context.Load(OperandType.I64, context.Add(exceptionRecordPtr, context.ZeroExtend32(OperandType.I64, structWriteOffset))); + + Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1. + + Operand isInRegion = EmitGenericRegionCheck(context, signalStructPtr, faultAddress, isWrite, rangeStructSize, pageSize); + + Operand endLabel = Label(); + + // If the region check result is false, then run the next vectored exception handler. + + context.BranchIfTrue(endLabel, isInRegion); + + context.Return(Const(EXCEPTION_CONTINUE_SEARCH)); + + context.MarkLabel(endLabel); + + // Otherwise, return to execution. + + context.Return(Const(EXCEPTION_CONTINUE_EXECUTION)); + + // Compile and return the function. + + ControlFlowGraph cfg = context.GetControlFlowGraph(); + + OperandType[] argTypes = new OperandType[] { OperandType.I64 }; + + return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code; + } + } +} diff --git a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs b/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs deleted file mode 100644 index 70e9f220..00000000 --- a/src/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Runtime.InteropServices; - -namespace ARMeilleure.Signal -{ - static partial class UnixSignalHandlerRegistration - { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public unsafe struct SigSet - { - fixed long sa_mask[16]; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct SigAction - { - public IntPtr sa_handler; - public SigSet sa_mask; - public int sa_flags; - public IntPtr sa_restorer; - } - - private const int SIGSEGV = 11; - private const int SIGBUS = 10; - private const int SA_SIGINFO = 0x00000004; - - [LibraryImport("libc", SetLastError = true)] - private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction); - - [LibraryImport("libc", SetLastError = true)] - private static partial int sigaction(int signum, IntPtr sigAction, out SigAction oldAction); - - [LibraryImport("libc", SetLastError = true)] - private static partial int sigemptyset(ref SigSet set); - - public static SigAction GetSegfaultExceptionHandler() - { - int result = sigaction(SIGSEGV, IntPtr.Zero, out SigAction old); - - if (result != 0) - { - throw new InvalidOperationException($"Could not get SIGSEGV sigaction. Error: {result}"); - } - - return old; - } - - public static SigAction RegisterExceptionHandler(IntPtr action) - { - SigAction sig = new() - { - sa_handler = action, - sa_flags = SA_SIGINFO, - }; - - sigemptyset(ref sig.sa_mask); - - int result = sigaction(SIGSEGV, ref sig, out SigAction old); - - if (result != 0) - { - throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}"); - } - - if (OperatingSystem.IsMacOS()) - { - result = sigaction(SIGBUS, ref sig, out _); - - if (result != 0) - { - throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}"); - } - } - - return old; - } - - public static bool RestoreExceptionHandler(SigAction oldAction) - { - return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0 && (!OperatingSystem.IsMacOS() || sigaction(SIGBUS, ref oldAction, out SigAction _) == 0); - } - } -} 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/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs new file mode 100644 index 00000000..e88a6c0f --- /dev/null +++ b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs @@ -0,0 +1,83 @@ +using System; +using System.Runtime.InteropServices; + +namespace Ryujinx.Cpu.Signal +{ + static partial class UnixSignalHandlerRegistration + { + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public unsafe struct SigSet + { + fixed long sa_mask[16]; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct SigAction + { + public IntPtr sa_handler; + public SigSet sa_mask; + public int sa_flags; + public IntPtr sa_restorer; + } + + private const int SIGSEGV = 11; + private const int SIGBUS = 10; + private const int SA_SIGINFO = 0x00000004; + + [LibraryImport("libc", SetLastError = true)] + private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction); + + [LibraryImport("libc", SetLastError = true)] + private static partial int sigaction(int signum, IntPtr sigAction, out SigAction oldAction); + + [LibraryImport("libc", SetLastError = true)] + private static partial int sigemptyset(ref SigSet set); + + public static SigAction GetSegfaultExceptionHandler() + { + int result = sigaction(SIGSEGV, IntPtr.Zero, out SigAction old); + + if (result != 0) + { + throw new InvalidOperationException($"Could not get SIGSEGV sigaction. Error: {result}"); + } + + return old; + } + + public static SigAction RegisterExceptionHandler(IntPtr action) + { + SigAction sig = new() + { + sa_handler = action, + sa_flags = SA_SIGINFO, + }; + + sigemptyset(ref sig.sa_mask); + + int result = sigaction(SIGSEGV, ref sig, out SigAction old); + + if (result != 0) + { + throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}"); + } + + if (OperatingSystem.IsMacOS()) + { + result = sigaction(SIGBUS, ref sig, out _); + + if (result != 0) + { + throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}"); + } + } + + return old; + } + + public static bool RestoreExceptionHandler(SigAction oldAction) + { + return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0 && (!OperatingSystem.IsMacOS() || sigaction(SIGBUS, ref oldAction, out SigAction _) == 0); + } + } +} 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; + } + } +} -- cgit v1.2.3