aboutsummaryrefslogtreecommitdiff
path: root/ARMeilleure
diff options
context:
space:
mode:
Diffstat (limited to 'ARMeilleure')
-rw-r--r--ARMeilleure/Instructions/InstEmitMemoryHelper.cs70
-rw-r--r--ARMeilleure/Memory/IMemoryManager.cs52
-rw-r--r--ARMeilleure/Memory/MemoryManagerType.cs41
-rw-r--r--ARMeilleure/Signal/NativeSignalHandler.cs327
-rw-r--r--ARMeilleure/Signal/UnixSignalHandlerRegistration.cs57
-rw-r--r--ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs24
-rw-r--r--ARMeilleure/Translation/PTC/Ptc.cs23
-rw-r--r--ARMeilleure/Translation/Translator.cs6
8 files changed, 581 insertions, 19 deletions
diff --git a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
index 2d7b6799..2de12304 100644
--- a/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
+++ b/ARMeilleure/Instructions/InstEmitMemoryHelper.cs
@@ -1,5 +1,6 @@
using ARMeilleure.Decoders;
using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Memory;
using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC;
using System;
@@ -141,13 +142,16 @@ namespace ARMeilleure.Instructions
SetInt(context, rt, value);
- context.Branch(lblEnd);
+ if (!context.Memory.Type.IsHostMapped())
+ {
+ context.Branch(lblEnd);
- context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
+ context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
- EmitReadIntFallback(context, address, rt, size);
+ EmitReadIntFallback(context, address, rt, size);
- context.MarkLabel(lblEnd);
+ context.MarkLabel(lblEnd);
+ }
}
public static Operand EmitReadIntAligned(ArmEmitterContext context, Operand address, int size)
@@ -195,13 +199,16 @@ namespace ARMeilleure.Instructions
context.Copy(GetVec(rt), value);
- context.Branch(lblEnd);
+ if (!context.Memory.Type.IsHostMapped())
+ {
+ context.Branch(lblEnd);
- context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
+ context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
- EmitReadVectorFallback(context, address, vector, rt, elem, size);
+ EmitReadVectorFallback(context, address, vector, rt, elem, size);
- context.MarkLabel(lblEnd);
+ context.MarkLabel(lblEnd);
+ }
}
private static Operand VectorCreate(ArmEmitterContext context, Operand value)
@@ -231,13 +238,16 @@ namespace ARMeilleure.Instructions
case 3: context.Store (physAddr, value); break;
}
- context.Branch(lblEnd);
+ if (!context.Memory.Type.IsHostMapped())
+ {
+ context.Branch(lblEnd);
- context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
+ context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
- EmitWriteIntFallback(context, address, rt, size);
+ EmitWriteIntFallback(context, address, rt, size);
- context.MarkLabel(lblEnd);
+ context.MarkLabel(lblEnd);
+ }
}
public static void EmitWriteIntAligned(ArmEmitterContext context, Operand address, Operand value, int size)
@@ -291,17 +301,25 @@ namespace ARMeilleure.Instructions
case 4: context.Store (physAddr, value); break;
}
- context.Branch(lblEnd);
+ if (!context.Memory.Type.IsHostMapped())
+ {
+ context.Branch(lblEnd);
- context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
+ context.MarkLabel(lblSlowPath, BasicBlockFrequency.Cold);
- EmitWriteVectorFallback(context, address, rt, elem, size);
+ EmitWriteVectorFallback(context, address, rt, elem, size);
- context.MarkLabel(lblEnd);
+ context.MarkLabel(lblEnd);
+ }
}
public static Operand EmitPtPointerLoad(ArmEmitterContext context, Operand address, Operand lblSlowPath, bool write, int size)
{
+ if (context.Memory.Type.IsHostMapped())
+ {
+ return EmitHostMappedPointer(context, address);
+ }
+
int ptLevelBits = context.Memory.AddressSpaceBits - PageBits;
int ptLevelSize = 1 << ptLevelBits;
int ptLevelMask = ptLevelSize - 1;
@@ -380,6 +398,26 @@ namespace ARMeilleure.Instructions
return context.Add(pte, pageOffset);
}
+ public static Operand EmitHostMappedPointer(ArmEmitterContext context, Operand address)
+ {
+ if (address.Type == OperandType.I32)
+ {
+ address = context.ZeroExtend32(OperandType.I64, address);
+ }
+
+ if (context.Memory.Type == MemoryManagerType.HostMapped)
+ {
+ Operand mask = Const(ulong.MaxValue >> (64 - context.Memory.AddressSpaceBits));
+ address = context.BitwiseAnd(address, mask);
+ }
+
+ Operand baseAddr = Ptc.State == PtcState.Disabled
+ ? Const(context.Memory.PageTablePointer.ToInt64())
+ : Const(context.Memory.PageTablePointer.ToInt64(), true, Ptc.PageTablePointerIndex);
+
+ return context.Add(baseAddr, address);
+ }
+
private static void EmitReadIntFallback(ArmEmitterContext context, Operand address, int rt, int size)
{
MethodInfo info = null;
diff --git a/ARMeilleure/Memory/IMemoryManager.cs b/ARMeilleure/Memory/IMemoryManager.cs
index cacfc4ac..0a25eb97 100644
--- a/ARMeilleure/Memory/IMemoryManager.cs
+++ b/ARMeilleure/Memory/IMemoryManager.cs
@@ -8,16 +8,68 @@ namespace ARMeilleure.Memory
IntPtr PageTablePointer { get; }
+ MemoryManagerType Type { get; }
+
+ event Action<ulong, ulong> UnmapEvent;
+
+ /// <summary>
+ /// Reads data from CPU mapped memory.
+ /// </summary>
+ /// <typeparam name="T">Type of the data being read</typeparam>
+ /// <param name="va">Virtual address of the data in memory</param>
+ /// <returns>The data</returns>
T Read<T>(ulong va) where T : unmanaged;
+
+ /// <summary>
+ /// Reads data from CPU mapped memory, with read tracking
+ /// </summary>
+ /// <typeparam name="T">Type of the data being read</typeparam>
+ /// <param name="va">Virtual address of the data in memory</param>
+ /// <returns>The data</returns>
T ReadTracked<T>(ulong va) where T : unmanaged;
+
+ /// <summary>
+ /// Writes data to CPU mapped memory.
+ /// </summary>
+ /// <typeparam name="T">Type of the data being written</typeparam>
+ /// <param name="va">Virtual address to write the data into</param>
+ /// <param name="value">Data to be written</param>
void Write<T>(ulong va, T value) where T : unmanaged;
+ /// <summary>
+ /// Gets a read-only span of data from CPU mapped memory.
+ /// </summary>
+ /// <param name="va">Virtual address of the data</param>
+ /// <param name="size">Size of the data</param>
+ /// <param name="tracked">True if read tracking is triggered on the span</param>
+ /// <returns>A read-only span of the data</returns>
ReadOnlySpan<byte> GetSpan(ulong va, int size, bool tracked = false);
+ /// <summary>
+ /// Gets a reference for the given type at the specified virtual memory address.
+ /// </summary>
+ /// <remarks>
+ /// The data must be located at a contiguous memory region.
+ /// </remarks>
+ /// <typeparam name="T">Type of the data to get the reference</typeparam>
+ /// <param name="va">Virtual address of the data</param>
+ /// <returns>A reference to the data in memory</returns>
ref T GetRef<T>(ulong va) where T : unmanaged;
+ /// <summary>
+ /// Checks if the page at a given CPU virtual address is mapped.
+ /// </summary>
+ /// <param name="va">Virtual address to check</param>
+ /// <returns>True if the address is mapped, false otherwise</returns>
bool IsMapped(ulong va);
+ /// <summary>
+ /// Alerts the memory tracking that a given region has been read from or written to.
+ /// This should be called before read/write is performed.
+ /// </summary>
+ /// <param name="va">Virtual address of the region</param>
+ /// <param name="size">Size of the region</param>
+ /// <param name="write">True if the region was written, false if read</param>
void SignalMemoryTracking(ulong va, ulong size, bool write);
}
} \ No newline at end of file
diff --git a/ARMeilleure/Memory/MemoryManagerType.cs b/ARMeilleure/Memory/MemoryManagerType.cs
new file mode 100644
index 00000000..ce84ccaf
--- /dev/null
+++ b/ARMeilleure/Memory/MemoryManagerType.cs
@@ -0,0 +1,41 @@
+namespace ARMeilleure.Memory
+{
+ /// <summary>
+ /// Indicates the type of a memory manager and the method it uses for memory mapping
+ /// and address translation. This controls the code generated for memory accesses on the JIT.
+ /// </summary>
+ public enum MemoryManagerType
+ {
+ /// <summary>
+ /// Complete software MMU implementation, the read/write methods are always called,
+ /// without any attempt to perform faster memory access.
+ /// </summary>
+ SoftwareMmu,
+
+ /// <summary>
+ /// High level implementation using a software flat page table for address translation,
+ /// used to speed up address translation if possible without calling the read/write methods.
+ /// </summary>
+ SoftwarePageTable,
+
+ /// <summary>
+ /// High level implementation with mappings managed by the host OS, effectively using hardware
+ /// page tables. No address translation is performed in software and the memory is just accessed directly.
+ /// </summary>
+ HostMapped,
+
+ /// <summary>
+ /// Same as the host mapped memory manager type, but without masking the address within the address space.
+ /// Allows invalid access from JIT code to the rest of the program, but is faster.
+ /// </summary>
+ HostMappedUnsafe
+ }
+
+ static class MemoryManagerTypeExtensions
+ {
+ public static bool IsHostMapped(this MemoryManagerType type)
+ {
+ return type == MemoryManagerType.HostMapped || type == MemoryManagerType.HostMappedUnsafe;
+ }
+ }
+}
diff --git a/ARMeilleure/Signal/NativeSignalHandler.cs b/ARMeilleure/Signal/NativeSignalHandler.cs
new file mode 100644
index 00000000..e5387ca6
--- /dev/null
+++ b/ARMeilleure/Signal/NativeSignalHandler.cs
@@ -0,0 +1,327 @@
+using ARMeilleure.IntermediateRepresentation;
+using ARMeilleure.Translation;
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+using static ARMeilleure.IntermediateRepresentation.OperandHelper;
+
+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
+ {
+ /// <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 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 const ulong PageSize = 0x1000;
+ private const ulong PageMask = PageSize - 1;
+
+ private static IntPtr _handlerConfig;
+ private static IntPtr _signalHandlerPtr;
+ private static IntPtr _signalHandlerHandle;
+
+ private static readonly object _lock = new object();
+ private static bool _initialized;
+
+ static NativeSignalHandler()
+ {
+ _handlerConfig = Marshal.AllocHGlobal(Unsafe.SizeOf<SignalHandlerConfig>());
+ ref SignalHandlerConfig config = ref GetConfigRef();
+
+ config = new SignalHandlerConfig();
+ }
+
+ public static void InitializeSignalHandler()
+ {
+ if (_initialized) return;
+
+ lock (_lock)
+ {
+ if (_initialized) return;
+
+ Translator.PreparePool();
+
+ bool unix = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
+ ref SignalHandlerConfig config = ref GetConfigRef();
+
+ if (unix)
+ {
+ // Unix siginfo struct locations.
+ // NOTE: These are incredibly likely to be different between kernel version and architectures.
+
+ config.StructAddressOffset = 16; // si_addr
+ config.StructWriteOffset = 8; // si_code
+
+ _signalHandlerPtr = Marshal.GetFunctionPointerForDelegate(GenerateUnixSignalHandler(_handlerConfig));
+
+ SigAction 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));
+
+ _signalHandlerHandle = WindowsSignalHandlerRegistration.RegisterExceptionHandler(_signalHandlerPtr);
+ }
+
+ Translator.ResetPool();
+
+ Translator.DisposePools();
+
+ _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);
+ context.Copy(inRegionLocal, Const(0));
+
+ Operand endLabel = Label();
+
+ for (int i = 0; i < MaxTrackedRanges; i++)
+ {
+ ulong rangeBaseOffset = (ulong)(RangeOffset + i * Unsafe.SizeOf<SignalHandlerRange>());
+
+ 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.Less)
+ );
+
+ // Only call tracking if in range.
+ context.BranchIfFalse(nextLabel, inRange, BasicBlockFrequency.Cold);
+
+ context.Copy(inRegionLocal, Const(1));
+ 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.Call(trackingActionPtr, OperandType.I32, offset, Const(PageSize), isWrite);
+
+ context.Branch(endLabel);
+
+ context.MarkLabel(nextLabel);
+ }
+
+ context.MarkLabel(endLabel);
+
+ return context.Copy(inRegionLocal);
+ }
+
+ private static UnixExceptionHandler GenerateUnixSignalHandler(IntPtr signalStructPtr)
+ {
+ EmitterContext context = new EmitterContext();
+
+ // (int sig, SigInfo* sigInfo, void* ucontext)
+ Operand sigInfoPtr = context.LoadArgument(OperandType.I64, 1);
+
+ Operand structAddressOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructAddressOffset));
+ Operand structWriteOffset = context.Load(OperandType.I64, Const((ulong)signalStructPtr + StructWriteOffset));
+
+ Operand faultAddress = context.Load(OperandType.I64, context.Add(sigInfoPtr, context.ZeroExtend32(OperandType.I64, structAddressOffset)));
+ Operand writeFlag = context.Load(OperandType.I64, context.Add(sigInfoPtr, 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();
+
+ 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<UnixExceptionHandler>(cfg, argTypes, OperandType.None, CompilerOptions.HighCq);
+ }
+
+ private static VectoredExceptionHandler GenerateWindowsSignalHandler(IntPtr signalStructPtr)
+ {
+ EmitterContext context = new EmitterContext();
+
+ // (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<VectoredExceptionHandler>(cfg, argTypes, OperandType.I32, CompilerOptions.HighCq);
+ }
+ }
+}
diff --git a/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs b/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs
new file mode 100644
index 00000000..9e87749e
--- /dev/null
+++ b/ARMeilleure/Signal/UnixSignalHandlerRegistration.cs
@@ -0,0 +1,57 @@
+using Mono.Unix.Native;
+using System;
+using System.Runtime.InteropServices;
+
+namespace ARMeilleure.Signal
+{
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ unsafe struct SigSet
+ {
+ fixed long sa_mask[16];
+ }
+
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ struct SigAction
+ {
+ public IntPtr sa_handler;
+ public SigSet sa_mask;
+ public int sa_flags;
+ public IntPtr sa_restorer;
+ }
+
+ static class UnixSignalHandlerRegistration
+ {
+ private const int SA_SIGINFO = 0x00000004;
+
+ [DllImport("libc", SetLastError = true)]
+ private static extern int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
+
+ [DllImport("libc", SetLastError = true)]
+ private static extern int sigemptyset(ref SigSet set);
+
+ public static SigAction RegisterExceptionHandler(IntPtr action)
+ {
+ SigAction sig = new SigAction
+ {
+ sa_handler = action,
+ sa_flags = SA_SIGINFO
+ };
+
+ sigemptyset(ref sig.sa_mask);
+
+ int result = sigaction((int)Signum.SIGSEGV, ref sig, out SigAction old);
+
+ if (result != 0)
+ {
+ throw new InvalidOperationException($"Could not register sigaction. Error: {result}");
+ }
+
+ return old;
+ }
+
+ public static bool RestoreExceptionHandler(SigAction oldAction)
+ {
+ return sigaction((int)Signum.SIGSEGV, ref oldAction, out SigAction _) == 0;
+ }
+ }
+}
diff --git a/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs b/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs
new file mode 100644
index 00000000..959d1c47
--- /dev/null
+++ b/ARMeilleure/Signal/WindowsSignalHandlerRegistration.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ARMeilleure.Signal
+{
+ class WindowsSignalHandlerRegistration
+ {
+ [DllImport("kernel32.dll")]
+ private static extern IntPtr AddVectoredExceptionHandler(uint first, IntPtr handler);
+
+ [DllImport("kernel32.dll")]
+ private static extern ulong RemoveVectoredExceptionHandler(IntPtr handle);
+
+ public static IntPtr RegisterExceptionHandler(IntPtr action)
+ {
+ return AddVectoredExceptionHandler(1, action);
+ }
+
+ public static bool RemoveExceptionHandler(IntPtr handle)
+ {
+ return RemoveVectoredExceptionHandler(handle) != 0;
+ }
+ }
+}
diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs
index f6494c23..ed4a003d 100644
--- a/ARMeilleure/Translation/PTC/Ptc.cs
+++ b/ARMeilleure/Translation/PTC/Ptc.cs
@@ -28,7 +28,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0";
- private const uint InternalVersion = 2305; //! To be incremented manually for each change to the ARMeilleure project.
+ private const uint InternalVersion = 2289; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0";
private const string BackupDir = "1";
@@ -64,6 +64,8 @@ namespace ARMeilleure.Translation.PTC
internal static string TitleIdText { get; private set; }
internal static string DisplayVersion { get; private set; }
+ private static MemoryManagerMode _memoryMode;
+
internal static string CachePathActual { get; private set; }
internal static string CachePathBackup { get; private set; }
@@ -98,7 +100,7 @@ namespace ARMeilleure.Translation.PTC
Disable();
}
- public static void Initialize(string titleIdText, string displayVersion, bool enabled)
+ public static void Initialize(string titleIdText, string displayVersion, bool enabled, MemoryManagerMode memoryMode)
{
Wait();
@@ -122,6 +124,7 @@ namespace ARMeilleure.Translation.PTC
TitleIdText = titleIdText;
DisplayVersion = !string.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
+ _memoryMode = memoryMode;
string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
@@ -244,6 +247,13 @@ namespace ARMeilleure.Translation.PTC
return false;
}
+ if (outerHeader.MemoryManagerMode != GetMemoryManagerMode())
+ {
+ InvalidateCompressedStream(compressedStream);
+
+ return false;
+ }
+
if (outerHeader.OSPlatform != GetOSPlatform())
{
InvalidateCompressedStream(compressedStream);
@@ -441,6 +451,7 @@ namespace ARMeilleure.Translation.PTC
outerHeader.CacheFileVersion = InternalVersion;
outerHeader.Endianness = GetEndianness();
outerHeader.FeatureInfo = GetFeatureInfo();
+ outerHeader.MemoryManagerMode = GetMemoryManagerMode();
outerHeader.OSPlatform = GetOSPlatform();
outerHeader.UncompressedStreamSize =
@@ -954,6 +965,11 @@ namespace ARMeilleure.Translation.PTC
return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
}
+ private static byte GetMemoryManagerMode()
+ {
+ return (byte)_memoryMode;
+ }
+
private static uint GetOSPlatform()
{
uint osPlatform = 0u;
@@ -966,7 +982,7 @@ namespace ARMeilleure.Translation.PTC
return osPlatform;
}
- [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 49*/)]
+ [StructLayout(LayoutKind.Sequential, Pack = 1/*, Size = 50*/)]
private struct OuterHeader
{
public ulong Magic;
@@ -975,6 +991,7 @@ namespace ARMeilleure.Translation.PTC
public bool Endianness;
public ulong FeatureInfo;
+ public byte MemoryManagerMode;
public uint OSPlatform;
public long UncompressedStreamSize;
diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs
index f8b074c9..eeeb517f 100644
--- a/ARMeilleure/Translation/Translator.cs
+++ b/ARMeilleure/Translation/Translator.cs
@@ -4,6 +4,7 @@ using ARMeilleure.Diagnostics;
using ARMeilleure.Instructions;
using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.Memory;
+using ARMeilleure.Signal;
using ARMeilleure.State;
using ARMeilleure.Translation.Cache;
using ARMeilleure.Translation.PTC;
@@ -63,6 +64,11 @@ namespace ARMeilleure.Translation
JitCache.Initialize(allocator);
DirectCallStubs.InitializeStubs();
+
+ if (memory.Type.IsHostMapped())
+ {
+ NativeSignalHandler.InitializeSignalHandler();
+ }
}
private void TranslateStackedSubs()