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 }