aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
diff options
context:
space:
mode:
authorgdkchan <gab.dark.100@gmail.com>2024-01-18 14:08:40 -0300
committerGitHub <noreply@github.com>2024-01-18 14:08:40 -0300
commit2dbbc9bc05998baa94d7b1953d9e0ffc7f1651f8 (patch)
tree5a3bf00a55f4c1a92b681119aef6dd68315b60e5 /src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
parent72634c80f408fd6cf6602c8c6729643e9ea1ffe3 (diff)
Move most of signal handling to Ryujinx.Cpu project (#6128)1.1.1113
* Move most of signal handling to Ryujinx.Cpu project * Format whitespace * Remove unused member * Format whitespace * This does not need to be public anymore
Diffstat (limited to 'src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs')
-rw-r--r--src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs179
1 files changed, 179 insertions, 0 deletions
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
+ {
+ /// <summary>
+ /// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
+ /// </summary>
+ public int StructAddressOffset;
+
+ /// <summary>
+ /// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
+ /// </summary>
+ public int StructWriteOffset;
+
+ /// <summary>
+ /// The sigaction handler that was registered before this one. (unix only)
+ /// </summary>
+ public nuint UnixOldSigaction;
+
+ /// <summary>
+ /// The type of the previous sigaction. True for the 3 argument variant. (unix only)
+ /// </summary>
+ public int UnixOldSigaction3Arg;
+
+ /// <summary>
+ /// Fixed size array of tracked ranges.
+ /// </summary>
+ 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<SignalHandlerConfig>());
+ ref SignalHandlerConfig config = ref GetConfigRef();
+
+ config = new SignalHandlerConfig();
+ }
+
+ public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
+ {
+ if (_initialized)
+ {
+ return;
+ }
+
+ lock (_lock)
+ {
+ if (_initialized)
+ {
+ return;
+ }
+
+ int rangeStructSize = Unsafe.SizeOf<SignalHandlerRange>();
+
+ 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<byte> 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<SignalHandlerConfig>((void*)_handlerConfig);
+ }
+
+ public static bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
+ {
+ Span<SignalHandlerRange> 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<SignalHandlerRange> 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;
+ }
+ }
+}