/ src / Ryujinx.Cpu / Signal / NativeSignalHandler.cs
NativeSignalHandler.cs
  1  using ARMeilleure.Signal;
  2  using Ryujinx.Common;
  3  using Ryujinx.Memory;
  4  using System;
  5  using System.Diagnostics;
  6  using System.Runtime.CompilerServices;
  7  using System.Runtime.InteropServices;
  8  
  9  namespace Ryujinx.Cpu.Signal
 10  {
 11      [StructLayout(LayoutKind.Sequential, Pack = 1)]
 12      struct SignalHandlerRange
 13      {
 14          public int IsActive;
 15          public nuint RangeAddress;
 16          public nuint RangeEndAddress;
 17          public IntPtr ActionPointer;
 18      }
 19  
 20      [InlineArray(NativeSignalHandlerGenerator.MaxTrackedRanges)]
 21      struct SignalHandlerRangeArray
 22      {
 23          public SignalHandlerRange Range0;
 24      }
 25  
 26      [StructLayout(LayoutKind.Sequential, Pack = 1)]
 27      struct SignalHandlerConfig
 28      {
 29          /// <summary>
 30          /// The byte offset of the faulting address in the SigInfo or ExceptionRecord struct.
 31          /// </summary>
 32          public int StructAddressOffset;
 33  
 34          /// <summary>
 35          /// The byte offset of the write flag in the SigInfo or ExceptionRecord struct.
 36          /// </summary>
 37          public int StructWriteOffset;
 38  
 39          /// <summary>
 40          /// The sigaction handler that was registered before this one. (unix only)
 41          /// </summary>
 42          public nuint UnixOldSigaction;
 43  
 44          /// <summary>
 45          /// The type of the previous sigaction. True for the 3 argument variant. (unix only)
 46          /// </summary>
 47          public int UnixOldSigaction3Arg;
 48  
 49          /// <summary>
 50          /// Fixed size array of tracked ranges.
 51          /// </summary>
 52          public SignalHandlerRangeArray Ranges;
 53      }
 54  
 55      static class NativeSignalHandler
 56      {
 57          private static readonly IntPtr _handlerConfig;
 58          private static IntPtr _signalHandlerPtr;
 59  
 60          private static MemoryBlock _codeBlock;
 61  
 62          private static readonly object _lock = new();
 63          private static bool _initialized;
 64  
 65          static NativeSignalHandler()
 66          {
 67              _handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
 68              ref SignalHandlerConfig config = ref GetConfigRef();
 69  
 70              config = new SignalHandlerConfig();
 71          }
 72  
 73          public static void InitializeSignalHandler(Func<IntPtr, IntPtr, IntPtr> customSignalHandlerFactory = null)
 74          {
 75              if (_initialized)
 76              {
 77                  return;
 78              }
 79  
 80              lock (_lock)
 81              {
 82                  if (_initialized)
 83                  {
 84                      return;
 85                  }
 86  
 87                  int rangeStructSize = Unsafe.SizeOf<SignalHandlerRange>();
 88  
 89                  ref SignalHandlerConfig config = ref GetConfigRef();
 90  
 91                  if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
 92                  {
 93                      _signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateUnixSignalHandler(_handlerConfig, rangeStructSize));
 94  
 95                      if (customSignalHandlerFactory != null)
 96                      {
 97                          _signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
 98                      }
 99  
100                      var old = UnixSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
101  
102                      config.UnixOldSigaction = (nuint)(ulong)old.sa_handler;
103                      config.UnixOldSigaction3Arg = old.sa_flags & 4;
104                  }
105                  else
106                  {
107                      config.StructAddressOffset = 40; // ExceptionInformation1
108                      config.StructWriteOffset = 32; // ExceptionInformation0
109  
110                      _signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateWindowsSignalHandler(_handlerConfig, rangeStructSize));
111  
112                      if (customSignalHandlerFactory != null)
113                      {
114                          _signalHandlerPtr = customSignalHandlerFactory(IntPtr.Zero, _signalHandlerPtr);
115                      }
116  
117                      WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
118                  }
119  
120                  _initialized = true;
121              }
122          }
123  
124          private static IntPtr MapCode(ReadOnlySpan<byte> code)
125          {
126              Debug.Assert(_codeBlock == null);
127  
128              ulong codeSizeAligned = BitUtils.AlignUp((ulong)code.Length, MemoryBlock.GetPageSize());
129  
130              _codeBlock = new MemoryBlock(codeSizeAligned);
131              _codeBlock.Write(0, code);
132              _codeBlock.Reprotect(0, codeSizeAligned, MemoryPermission.ReadAndExecute);
133  
134              return _codeBlock.Pointer;
135          }
136  
137          private static unsafe ref SignalHandlerConfig GetConfigRef()
138          {
139              return ref Unsafe.AsRef<SignalHandlerConfig>((void*)_handlerConfig);
140          }
141  
142          public static bool AddTrackedRegion(nuint address, nuint endAddress, IntPtr action)
143          {
144              Span<SignalHandlerRange> ranges = GetConfigRef().Ranges;
145  
146              for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++)
147              {
148                  if (ranges[i].IsActive == 0)
149                  {
150                      ranges[i].RangeAddress = address;
151                      ranges[i].RangeEndAddress = endAddress;
152                      ranges[i].ActionPointer = action;
153                      ranges[i].IsActive = 1;
154  
155                      return true;
156                  }
157              }
158  
159              return false;
160          }
161  
162          public static bool RemoveTrackedRegion(nuint address)
163          {
164              Span<SignalHandlerRange> ranges = GetConfigRef().Ranges;
165  
166              for (int i = 0; i < NativeSignalHandlerGenerator.MaxTrackedRanges; i++)
167              {
168                  if (ranges[i].IsActive == 1 && ranges[i].RangeAddress == address)
169                  {
170                      ranges[i].IsActive = 0;
171  
172                      return true;
173                  }
174              }
175  
176              return false;
177          }
178  
179          public static bool SupportsFaultAddressPatching()
180          {
181              return NativeSignalHandlerGenerator.SupportsFaultAddressPatchingForHost();
182          }
183      }
184  }