From b7d08e5050da31304541c2d5bf6158fbcfe87c6d Mon Sep 17 00:00:00 2001
From: Mary <mary@mary.zone>
Date: Mon, 8 May 2023 15:52:14 +0200
Subject: [PATCH] armeilleure: Add Android signal handler

---
 .../Signal/NativeSignalHandlerGenerator.cs    |   2 +-
 src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs |   8 +-
 .../Signal/UnixSignalHandlerRegistration.cs   | 130 ++++++++++++++++--
 3 files changed, 123 insertions(+), 17 deletions(-)

diff --git a/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs
index c5e708e16..838448bd9 100644
--- a/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs
+++ b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs
@@ -111,7 +111,7 @@ namespace ARMeilleure.Signal
                     return context.BitwiseAnd(err, Const(2ul));
                 }
             }
-            else if (OperatingSystem.IsLinux())
+            else if (OperatingSystem.IsLinux() || OperatingSystem.IsAndroid())
             {
                 if (RuntimeInformation.ProcessArchitecture == Architecture.Arm64)
                 {
diff --git a/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
index 5a9d92cc4..15d404554 100644
--- a/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
+++ b/src/Ryujinx.Cpu/Signal/NativeSignalHandler.cs
@@ -88,10 +88,16 @@ namespace Ryujinx.Cpu.Signal
 
                 ref SignalHandlerConfig config = ref GetConfigRef();
 
-                if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS())
+                if (OperatingSystem.IsLinux() || OperatingSystem.IsMacOS() || OperatingSystem.IsAndroid())
                 {
                     _signalHandlerPtr = MapCode(NativeSignalHandlerGenerator.GenerateUnixSignalHandler(_handlerConfig, rangeStructSize, pageSize));
 
+                    if (OperatingSystem.IsAndroid())
+                    {
+                        config.StructAddressOffset = 16; // si_addr
+                        config.StructWriteOffset = 8; // si_code
+                    }
+
                     if (customSignalHandlerFactory != null)
                     {
                         _signalHandlerPtr = customSignalHandlerFactory(UnixSignalHandlerRegistration.GetSegfaultExceptionHandler().sa_handler, _signalHandlerPtr);
diff --git a/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs
index e88a6c0f6..065558d27 100644
--- a/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs
+++ b/src/Ryujinx.Cpu/Signal/UnixSignalHandlerRegistration.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Runtime.InteropServices;
+using System.Runtime.Versioning;
 
 namespace Ryujinx.Cpu.Signal
 {
@@ -20,6 +21,16 @@ namespace Ryujinx.Cpu.Signal
             public IntPtr sa_restorer;
         }
 
+        [SupportedOSPlatform("android")]
+        [StructLayout(LayoutKind.Sequential, Pack = 8)]
+        public struct SigActionBionic
+        {
+            public int sa_flags;
+            public IntPtr sa_handler;
+            public SigSet sa_mask;
+            public IntPtr sa_restorer;
+        }
+
         private const int SIGSEGV = 11;
         private const int SIGBUS = 10;
         private const int SA_SIGINFO = 0x00000004;
@@ -27,15 +38,41 @@ namespace Ryujinx.Cpu.Signal
         [LibraryImport("libc", SetLastError = true)]
         private static partial int sigaction(int signum, ref SigAction sigAction, out SigAction oldAction);
 
+        [SupportedOSPlatform("android")]
+        [LibraryImport("libc", SetLastError = true)]
+        private static partial int sigaction(int signum, ref SigActionBionic sigAction, out SigActionBionic oldAction);
+
         [LibraryImport("libc", SetLastError = true)]
         private static partial int sigaction(int signum, IntPtr sigAction, out SigAction oldAction);
 
+        [SupportedOSPlatform("android")]
+        [LibraryImport("libc", SetLastError = true)]
+        private static partial int sigaction(int signum, IntPtr sigAction, out SigActionBionic oldAction);
+
         [LibraryImport("libc", SetLastError = true)]
         private static partial int sigemptyset(ref SigSet set);
 
         public static SigAction GetSegfaultExceptionHandler()
         {
-            int result = sigaction(SIGSEGV, IntPtr.Zero, out SigAction old);
+            int result;
+            SigAction old;
+
+            if (OperatingSystem.IsAndroid())
+            {
+                result = sigaction(SIGSEGV, IntPtr.Zero, out SigActionBionic tmp);
+
+                old = new SigAction
+                {
+                    sa_handler = tmp.sa_handler,
+                    sa_mask = tmp.sa_mask,
+                    sa_flags = tmp.sa_flags,
+                    sa_restorer = tmp.sa_restorer
+                };
+            }
+            else
+            {
+                result = sigaction(SIGSEGV, IntPtr.Zero, out old);
+            }
 
             if (result != 0)
             {
@@ -47,28 +84,69 @@ namespace Ryujinx.Cpu.Signal
 
         public static SigAction RegisterExceptionHandler(IntPtr action)
         {
-            SigAction sig = new()
+            int result;
+            SigAction old;
+
+            if (Ryujinx.Common.SystemInfo.SystemInfo.IsAndroid())
             {
-                sa_handler = action,
-                sa_flags = SA_SIGINFO,
-            };
+                SigActionBionic sig = new()
+                {
+                    sa_handler = action,
+                    sa_flags = SA_SIGINFO
+                };
 
-            sigemptyset(ref sig.sa_mask);
+                sigemptyset(ref sig.sa_mask);
 
-            int result = sigaction(SIGSEGV, ref sig, out SigAction old);
+                result = sigaction(SIGSEGV, ref sig, out SigActionBionic tmp);
 
-            if (result != 0)
-            {
-                throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}");
+                old = new SigAction
+                {
+                    sa_handler = tmp.sa_handler,
+                    sa_mask = tmp.sa_mask,
+                    sa_flags = tmp.sa_flags,
+                    sa_restorer = tmp.sa_restorer
+                };
+
+                if (userSignal != -1)
+                {
+                    result = sigaction(userSignal, ref sig, out SigActionBionic oldu);
+
+                    if (oldu.sa_handler != IntPtr.Zero)
+                    {
+                        throw new InvalidOperationException($"SIG{userSignal} is already in use.");
+                    }
+
+                    if (result != 0)
+                    {
+                        throw new InvalidOperationException($"Could not register SIG{userSignal} sigaction. Error: {result}");
+                    }
+                }
             }
-
-            if (OperatingSystem.IsMacOS())
+            else
             {
-                result = sigaction(SIGBUS, ref sig, out _);
+                SigAction sig = new SigAction
+                {
+                    sa_handler = action,
+                    sa_flags = SA_SIGINFO
+                };
+
+                sigemptyset(ref sig.sa_mask);
+
+                result = sigaction(SIGSEGV, ref sig, out old);
 
                 if (result != 0)
                 {
-                    throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
+                    throw new InvalidOperationException($"Could not register SIGSEGV sigaction. Error: {result}");
+                }
+
+                if (OperatingSystem.IsMacOS())
+                {
+                    result = sigaction(SIGBUS, ref sig, out _);
+
+                    if (result != 0)
+                    {
+                        throw new InvalidOperationException($"Could not register SIGBUS sigaction. Error: {result}");
+                    }
                 }
             }
 
@@ -77,7 +155,29 @@ namespace Ryujinx.Cpu.Signal
 
         public static bool RestoreExceptionHandler(SigAction oldAction)
         {
-            return sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0 && (!OperatingSystem.IsMacOS() || sigaction(SIGBUS, ref oldAction, out SigAction _) == 0);
+            if (OperatingSystem.IsAndroid())
+            {
+                SigActionBionic tmp = new SigActionBionic
+                {
+                    sa_handler = oldAction.sa_handler,
+                    sa_mask = oldAction.sa_mask,
+                    sa_flags = oldAction.sa_flags,
+                    sa_restorer = oldAction.sa_restorer
+                };
+
+                return sigaction(SIGSEGV, ref tmp, out SigActionBionic _) == 0;
+            }
+            else
+            {
+                bool success = sigaction(SIGSEGV, ref oldAction, out SigAction _) == 0;
+
+                if (success && OperatingSystem.IsMacOS())
+                {
+                    success = sigaction(SIGBUS, ref oldAction, out SigAction _) == 0;
+                }
+
+                return success;
+            }
         }
     }
 }