mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-08-19 21:52:22 +00:00
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
This commit is contained in:
parent
72634c80f4
commit
2dbbc9bc05
|
@ -4,7 +4,5 @@ namespace ARMeilleure.Memory
|
||||||
{
|
{
|
||||||
IJitMemoryBlock Allocate(ulong size);
|
IJitMemoryBlock Allocate(ulong size);
|
||||||
IJitMemoryBlock Reserve(ulong size);
|
IJitMemoryBlock Reserve(ulong size);
|
||||||
|
|
||||||
ulong GetPageSize();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ namespace ARMeilleure.Memory
|
||||||
HostMappedUnsafe,
|
HostMappedUnsafe,
|
||||||
}
|
}
|
||||||
|
|
||||||
static class MemoryManagerTypeExtensions
|
public static class MemoryManagerTypeExtensions
|
||||||
{
|
{
|
||||||
public static bool IsHostMapped(this MemoryManagerType type)
|
public static bool IsHostMapped(this MemoryManagerType type)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,63 +1,14 @@
|
||||||
using ARMeilleure.IntermediateRepresentation;
|
using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Memory;
|
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using ARMeilleure.Translation.Cache;
|
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
namespace ARMeilleure.Signal
|
||||||
{
|
{
|
||||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
public static class NativeSignalHandlerGenerator
|
||||||
struct SignalHandlerRange
|
|
||||||
{
|
{
|
||||||
public int IsActive;
|
public const int MaxTrackedRanges = 8;
|
||||||
public nuint RangeAddress;
|
|
||||||
public nuint RangeEndAddress;
|
|
||||||
public IntPtr ActionPointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
[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;
|
|
||||||
|
|
||||||
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 StructAddressOffset = 0;
|
||||||
private const int StructWriteOffset = 4;
|
private const int StructWriteOffset = 4;
|
||||||
|
@ -70,125 +21,10 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
private const uint EXCEPTION_ACCESS_VIOLATION = 0xc0000005;
|
||||||
|
|
||||||
private static ulong _pageSize;
|
private static Operand EmitGenericRegionCheck(EmitterContext context, IntPtr signalStructPtr, Operand faultAddress, Operand isWrite, int rangeStructSize, 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<SignalHandlerConfig>());
|
ulong pageMask = pageSize - 1;
|
||||||
ref SignalHandlerConfig config = ref GetConfigRef();
|
|
||||||
|
|
||||||
config = new SignalHandlerConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Initialize(IJitMemoryAllocator allocator)
|
|
||||||
{
|
|
||||||
JitCache.Initialize(allocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void InitializeSignalHandler(ulong pageSize, Func<IntPtr, IntPtr, IntPtr> 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<SignalHandlerConfig>((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);
|
Operand inRegionLocal = context.AllocateLocal(OperandType.I32);
|
||||||
context.Copy(inRegionLocal, Const(0));
|
context.Copy(inRegionLocal, Const(0));
|
||||||
|
|
||||||
|
@ -196,7 +32,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
for (int i = 0; i < MaxTrackedRanges; i++)
|
for (int i = 0; i < MaxTrackedRanges; i++)
|
||||||
{
|
{
|
||||||
ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf<SignalHandlerRange>());
|
ulong rangeBaseOffset = (ulong)(RangeOffset + i * rangeStructSize);
|
||||||
|
|
||||||
Operand nextLabel = Label();
|
Operand nextLabel = Label();
|
||||||
|
|
||||||
|
@ -210,13 +46,12 @@ namespace ARMeilleure.Signal
|
||||||
// Is the fault address within this tracked region?
|
// Is the fault address within this tracked region?
|
||||||
Operand inRange = context.BitwiseAnd(
|
Operand inRange = context.BitwiseAnd(
|
||||||
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
|
context.ICompare(faultAddress, rangeAddress, Comparison.GreaterOrEqualUI),
|
||||||
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI)
|
context.ICompare(faultAddress, rangeEndAddress, Comparison.LessUI));
|
||||||
);
|
|
||||||
|
|
||||||
// Only call tracking if in range.
|
// Only call tracking if in range.
|
||||||
context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
|
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.
|
// 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));
|
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.
|
// Tracking action should be non-null to call it, otherwise assume false return.
|
||||||
context.BranchIfFalse(skipActionLabel, trackingActionPtr);
|
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.Copy(inRegionLocal, result);
|
||||||
|
|
||||||
context.MarkLabel(skipActionLabel);
|
context.MarkLabel(skipActionLabel);
|
||||||
|
@ -269,8 +104,7 @@ namespace ARMeilleure.Signal
|
||||||
Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset)));
|
Operand esr = context.Load(OperandType.I64, context.Add(ctxPtr, Const(EsrOffset)));
|
||||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||||
}
|
}
|
||||||
|
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
|
||||||
{
|
{
|
||||||
const ulong ErrOffset = 4; // __es.__err
|
const ulong ErrOffset = 4; // __es.__err
|
||||||
Operand err = context.Load(OperandType.I64, context.Add(ctxPtr, Const(ErrOffset)));
|
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)));
|
Operand esr = context.Load(OperandType.I64, context.Add(auxPtr, Const(8ul)));
|
||||||
return context.BitwiseAnd(esr, Const(0x40ul));
|
return context.BitwiseAnd(esr, Const(0x40ul));
|
||||||
}
|
}
|
||||||
|
else if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
||||||
if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
|
|
||||||
{
|
{
|
||||||
const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR]
|
const int ErrOffset = 192; // uc_mcontext.gregs[REG_ERR]
|
||||||
Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset)));
|
Operand err = context.Load(OperandType.I64, context.Add(ucontextPtr, Const(ErrOffset)));
|
||||||
|
@ -322,7 +155,7 @@ namespace ARMeilleure.Signal
|
||||||
throw new PlatformNotSupportedException();
|
throw new PlatformNotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr)
|
public static byte[] GenerateUnixSignalHandler(IntPtr signalStructPtr, int rangeStructSize, ulong pageSize)
|
||||||
{
|
{
|
||||||
EmitterContext context = new();
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
@ -335,7 +168,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
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();
|
Operand endLabel = Label();
|
||||||
|
|
||||||
|
@ -367,10 +200,10 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
|
OperandType[] argTypes = new OperandType[] { OperandType.I32, OperandType.I64, OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.None, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<UnixExceptionHandler>();
|
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();
|
EmitterContext context = new();
|
||||||
|
|
||||||
|
@ -399,7 +232,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
Operand isWrite = context.ICompareNotEqual(writeFlag, Const(0L)); // Normalize to 0/1.
|
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();
|
Operand endLabel = Label();
|
||||||
|
|
||||||
|
@ -421,7 +254,7 @@ namespace ARMeilleure.Signal
|
||||||
|
|
||||||
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
OperandType[] argTypes = new OperandType[] { OperandType.I64 };
|
||||||
|
|
||||||
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Map<VectoredExceptionHandler>();
|
return Compiler.Compile(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq, RuntimeInformation.ProcessArchitecture).Code;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@ using ARMeilleure.IntermediateRepresentation;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Common.Memory.PartialUnmaps;
|
using Ryujinx.Common.Memory.PartialUnmaps;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
namespace ARMeilleure.Signal
|
||||||
|
@ -10,8 +10,28 @@ namespace ARMeilleure.Signal
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Methods to handle signals caused by partial unmaps. See the structs for C# implementations of the methods.
|
/// Methods to handle signals caused by partial unmaps. See the structs for C# implementations of the methods.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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)
|
public static Operand EmitRetryFromAccessViolation(EmitterContext context)
|
||||||
{
|
{
|
||||||
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
IntPtr partialRemapStatePtr = PartialUnmapState.GlobalState;
|
||||||
|
@ -20,7 +40,7 @@ namespace ARMeilleure.Signal
|
||||||
// Get the lock first.
|
// Get the lock first.
|
||||||
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
EmitNativeReaderLockAcquire(context, IntPtr.Add(partialRemapStatePtr, PartialUnmapState.PartialUnmapLockOffset));
|
||||||
|
|
||||||
IntPtr getCurrentThreadId = WindowsSignalHandlerRegistration.GetCurrentThreadIdFunc();
|
IntPtr getCurrentThreadId = GetCurrentThreadIdFunc();
|
||||||
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
Operand threadId = context.Call(Const((ulong)getCurrentThreadId), OperandType.I32);
|
||||||
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
Operand threadIndex = EmitThreadLocalMapIntGetOrReserve(context, localCountsPtr, threadId, Const(0));
|
||||||
|
|
||||||
|
@ -137,17 +157,6 @@ namespace ARMeilleure.Signal
|
||||||
return context.Add(structsPtr, context.SignExtend32(OperandType.I64, offset));
|
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<int>.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)
|
private static void EmitAtomicAddI32(EmitterContext context, Operand ptr, Operand additive)
|
||||||
{
|
{
|
||||||
Operand loop = Label();
|
Operand loop = Label();
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -79,11 +79,6 @@ namespace ARMeilleure.Translation
|
||||||
Stubs = new TranslatorStubs(this);
|
Stubs = new TranslatorStubs(this);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
|
|
||||||
if (memory.Type.IsHostMapped())
|
|
||||||
{
|
|
||||||
NativeSignalHandler.InitializeSignalHandler(allocator.GetPageSize());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
public IPtcLoadState LoadDiskCache(string titleIdText, string displayVersion, bool enabled)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
|
using Ryujinx.Cpu.Signal;
|
||||||
|
using Ryujinx.Memory;
|
||||||
|
|
||||||
namespace Ryujinx.Cpu.Jit
|
namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
|
@ -12,6 +14,12 @@ namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
_tickSource = tickSource;
|
_tickSource = tickSource;
|
||||||
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
|
_translator = new Translator(new JitMemoryAllocator(), memory, for64Bit);
|
||||||
|
|
||||||
|
if (memory.Type.IsHostMapped())
|
||||||
|
{
|
||||||
|
NativeSignalHandler.InitializeSignalHandler(MemoryBlock.GetPageSize());
|
||||||
|
}
|
||||||
|
|
||||||
memory.UnmapEvent += UnmapHandler;
|
memory.UnmapEvent += UnmapHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,5 @@ namespace Ryujinx.Cpu.Jit
|
||||||
{
|
{
|
||||||
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
|
public IJitMemoryBlock Allocate(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.None);
|
||||||
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Jit);
|
public IJitMemoryBlock Reserve(ulong size) => new JitMemoryBlock(size, MemoryAllocationFlags.Reserve | MemoryAllocationFlags.Jit);
|
||||||
|
|
||||||
public ulong GetPageSize() => MemoryBlock.GetPageSize();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
using ARMeilleure.Signal;
|
using Ryujinx.Cpu.Signal;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using Ryujinx.Memory.Tracking;
|
using Ryujinx.Memory.Tracking;
|
||||||
using System;
|
using System;
|
||||||
|
|
179
src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
Normal file
179
src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace ARMeilleure.Signal
|
namespace Ryujinx.Cpu.Signal
|
||||||
{
|
{
|
||||||
static partial class UnixSignalHandlerRegistration
|
static partial class UnixSignalHandlerRegistration
|
||||||
{
|
{
|
24
src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs
Normal file
24
src/Ryujinx.Cpu/Signal/WindowsSignalHandlerRegistration.cs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue