From 434df1dc447ecb7a8cfd08af5219ce0697877fd5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radek=20Barto=C5=88?= <radek.barton@microsoft.com>
Date: Wed, 4 Jun 2025 14:55:34 +0200
Subject: [PATCH] Cygwin: signal: make context structures registers handling
 portable
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

This patch extracts macros from winsup/cygwin/exceptions.cc serving for portable
register access to context structures into a separate local header
winsup/cygwin/local_includes/register.h and implements their AArch64
counterparts.

Then, it adds AArch64 declaration of __mcontext structure based on
mingw-w64-headers/include/winnt.h header to
winsup/cygwin/include/cygwin/singal.h header.

Then, it includes the registers.h header and uses the macros where applicable,
namely at:
 - winsup/cygwin/exceptions.cc
 - winsup/cygwin/profil.c
 - winsup/cygwin/tread.cc

The motivation is to make usage of the context structures portable without
unnecessary #if defined(__x86_64__) while implementations of signal handling
code will be developed later, e.g. implementation of makecontext.

Signed-off-by: Radek Bartoň <radek.barton@microsoft.com>
---
 winsup/cygwin/exceptions.cc             | 51 ++++++++--------
 winsup/cygwin/include/cygwin/signal.h   | 77 ++++++++++++++++++++++++-
 winsup/cygwin/local_includes/register.h | 25 ++++++++
 winsup/cygwin/profil.c                  |  7 +--
 winsup/cygwin/thread.cc                 |  3 +-
 winsup/utils/profiler.cc                |  7 +--
 6 files changed, 135 insertions(+), 35 deletions(-)
 create mode 100644 winsup/cygwin/local_includes/register.h

diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index f79978f73..830846431 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -28,25 +28,9 @@ details. */
 #include "ntdll.h"
 #include "exception.h"
 #include "posix_timer.h"
+#include "register.h"
 #include "gcc_seh.h"
 
-/* Define macros for CPU-agnostic register access.  The _CX_foo
-   macros are for access into CONTEXT, the _MC_foo ones for access into
-   mcontext. The idea is to access the registers in terms of their job,
-   not in terms of their name on the given target. */
-#ifdef __x86_64__
-#define _CX_instPtr	Rip
-#define _CX_stackPtr	Rsp
-#define _CX_framePtr	Rbp
-/* For special register access inside mcontext. */
-#define _MC_retReg	rax
-#define _MC_instPtr	rip
-#define _MC_stackPtr	rsp
-#define _MC_uclinkReg	rbx	/* MUST be callee-saved reg */
-#else
-#error unimplemented for this target
-#endif
-
 #define CALL_HANDLER_RETRY_OUTER 10
 #define CALL_HANDLER_RETRY_INNER 10
 #define DUMPSTACK_FRAME_LIMIT    32
@@ -230,7 +214,7 @@ cygwin_exception::dump_exception ()
 	}
     }
 
-#ifdef __x86_64__
+#if defined(__x86_64__)
   if (exception_name)
     small_printf ("Exception: %s at rip=%012X\r\n", exception_name, ctx->Rip);
   else
@@ -250,6 +234,31 @@ cygwin_exception::dump_exception ()
   small_printf ("cs=%04x ds=%04x es=%04x fs=%04x gs=%04x ss=%04x\r\n",
 		ctx->SegCs, ctx->SegDs, ctx->SegEs, ctx->SegFs,
 		ctx->SegGs, ctx->SegSs);
+#elif defined(__aarch64__)
+  if (exception_name)
+    small_printf ("Exception: %s at pc=%012X\r\n", exception_name, ctx->Pc);
+  else
+    small_printf ("Signal %d at pc=%012X\r\n", e->ExceptionCode, ctx->Pc);
+  small_printf ("x0=%016X x1=%016X x2=%016X x3=%016X\r\n",
+		ctx->X0, ctx->X1, ctx->X2, ctx->X3);
+  small_printf ("x4=%016X x5=%016X x6=%016X x7=%016X\r\n",
+		ctx->X4, ctx->X5, ctx->X6, ctx->X7);
+  small_printf ("x8=%016X x9=%016X x10=%016X x11=%016X\r\n",
+		ctx->X8, ctx->X9, ctx->X10, ctx->X11);
+  small_printf ("x12=%016X x13=%016X x14=%016X x15=%016X\r\n",
+		ctx->X12, ctx->X13, ctx->X14, ctx->X15);
+  small_printf ("x16=%016X x17=%016X x18=%016X x19=%016X\r\n",
+		ctx->X16, ctx->X17, ctx->X18, ctx->X19);
+  small_printf ("x20=%016X x21=%016X x22=%016X x23=%016X\r\n",
+		ctx->X20, ctx->X21, ctx->X22, ctx->X23);
+  small_printf ("x24=%016X x25=%016X x26=%016X x27=%016X\r\n",
+		ctx->X24, ctx->X25, ctx->X26, ctx->X27);
+  small_printf ("x28=%016X fp=%016X lr=%016X sp=%016X\r\n",
+		ctx->X28, ctx->Fp, ctx->Lr, ctx->Sp);
+  small_printf ("program=%W, pid %u, thread %s\r\n",
+		myself->progname, myself->pid, mythreadname ());
+  small_printf ("fpcr=%016X fpsr=%016X\r\n",
+		ctx->Fpcr, ctx->Fpsr);
 #else
 #error unimplemented for this target
 #endif
@@ -1781,11 +1790,7 @@ _cygtls::call_signal_handler ()
 	      __unwind_single_frame ((PCONTEXT) &context1.uc_mcontext);
 	      if (stackptr > stack)
 		{
-#ifdef __x86_64__
-		  context1.uc_mcontext.rip = retaddr ();
-#else
-#error unimplemented for this target
-#endif
+		  context1.uc_mcontext._MC_instPtr = retaddr ();
 		}
 	    }
 
diff --git a/winsup/cygwin/include/cygwin/signal.h b/winsup/cygwin/include/cygwin/signal.h
index de728bede..4e9eafba7 100644
--- a/winsup/cygwin/include/cygwin/signal.h
+++ b/winsup/cygwin/include/cygwin/signal.h
@@ -19,7 +19,7 @@ extern "C" {
   Define a struct __mcontext, which should be identical in layout to the Win32
   API type CONTEXT with the addition of oldmask and cr2 fields at the end.
 */
-#ifdef __x86_64__
+#if defined(__x86_64__)
 
 struct _uc_fpxreg {
   __uint16_t significand[4];
@@ -98,6 +98,81 @@ struct __attribute__ ((__aligned__ (16))) __mcontext
   __uint64_t cr2;
 };
 
+#elif defined(__aarch64__)
+
+/* Based on mingw-w64-headers/include/winnt.h. */
+
+#define ARM64_MAX_BREAKPOINTS 8
+#define ARM64_MAX_WATCHPOINTS 2
+
+union _neon128
+{
+  struct
+  {
+    __uint64_t low;
+    __int64_t high;
+  };
+  double d[2];
+  float s[4];
+  __uint16_t h[8];
+  __uint8_t b[16];
+};
+
+struct __attribute__ ((__aligned__ (16))) __mcontext
+{
+  __uint32_t ctxflags;
+  __uint32_t cpsr;
+  union
+  {
+    struct
+    {
+      __uint64_t x0;
+      __uint64_t x1;
+      __uint64_t x2;
+      __uint64_t x3;
+      __uint64_t x4;
+      __uint64_t x5;
+      __uint64_t x6;
+      __uint64_t x7;
+      __uint64_t x8;
+      __uint64_t x9;
+      __uint64_t x10;
+      __uint64_t x11;
+      __uint64_t x12;
+      __uint64_t x13;
+      __uint64_t x14;
+      __uint64_t x15;
+      __uint64_t x16;
+      __uint64_t x17;
+      __uint64_t x18;
+      __uint64_t x19;
+      __uint64_t x20;
+      __uint64_t x21;
+      __uint64_t x22;
+      __uint64_t x23;
+      __uint64_t x24;
+      __uint64_t x25;
+      __uint64_t x26;
+      __uint64_t x27;
+      __uint64_t x28;
+      __uint64_t fp;
+      __uint64_t lr;
+    };
+    __uint64_t x[31];
+  };
+  __uint64_t sp;
+  __uint64_t pc;
+  union _neon128 v[32];
+  __uint32_t fpcr;
+  __uint32_t fpsr;
+  __uint32_t bcr[ARM64_MAX_BREAKPOINTS];
+  __uint64_t bvr[ARM64_MAX_BREAKPOINTS];
+  __uint32_t wcr[ARM64_MAX_WATCHPOINTS];
+  __uint64_t wvr[ARM64_MAX_WATCHPOINTS];
+  __uint64_t oldmask;
+  __uint64_t cr2;
+};
+
 #else
 #error unimplemented for this target
 #endif
diff --git a/winsup/cygwin/local_includes/register.h b/winsup/cygwin/local_includes/register.h
new file mode 100644
index 000000000..1ddfe2ec0
--- /dev/null
+++ b/winsup/cygwin/local_includes/register.h
@@ -0,0 +1,25 @@
+/* Define macros for CPU-agnostic register access.  The _CX_foo
+   macros are for access into CONTEXT, the _MC_foo ones for access into
+   mcontext. The idea is to access the registers in terms of their job,
+   not in terms of their name on the given target. */
+#if defined(__x86_64__)
+#define _CX_instPtr	Rip
+#define _CX_stackPtr	Rsp
+#define _CX_framePtr	Rbp
+/* For special register access inside mcontext. */
+#define _MC_retReg	rax
+#define _MC_instPtr	rip
+#define _MC_stackPtr	rsp
+#define _MC_uclinkReg	rbx	/* MUST be callee-saved reg */
+#elif defined(__aarch64__)
+#define _CX_instPtr	Pc
+#define _CX_stackPtr	Sp
+#define _CX_framePtr	Fp
+/* For special register access inside mcontext. */
+#define _MC_retReg	x0
+#define _MC_instPtr	pc
+#define _MC_stackPtr	sp
+#define _MC_uclinkReg	x19	/* MUST be callee-saved reg */
+#else
+#error unimplemented for this target
+#endif
diff --git a/winsup/cygwin/profil.c b/winsup/cygwin/profil.c
index 30b37244a..9578ab1df 100644
--- a/winsup/cygwin/profil.c
+++ b/winsup/cygwin/profil.c
@@ -21,6 +21,7 @@
 #include <errno.h>
 #include <pthread.h>
 #include "profil.h"
+#include "register.h"
 
 #define SLEEPTIME (1000 / PROF_HZ)
 
@@ -42,11 +43,7 @@ get_thrpc (HANDLE thr)
   ctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
   pc = (size_t) - 1;
   if (GetThreadContext (thr, &ctx)) {
-#ifdef __x86_64__
-    pc = ctx.Rip;
-#else
-#error unimplemented for this target
-#endif
+    pc = ctx._CX_instPtr;
   }
   ResumeThread (thr);
   return pc;
diff --git a/winsup/cygwin/thread.cc b/winsup/cygwin/thread.cc
index 510e2be93..b462e2f9f 100644
--- a/winsup/cygwin/thread.cc
+++ b/winsup/cygwin/thread.cc
@@ -32,6 +32,7 @@ details. */
 #include "ntdll.h"
 #include "cygwait.h"
 #include "exception.h"
+#include "register.h"
 
 /* For Linux compatibility, the length of a thread name is 16 characters. */
 #define THRNAMELEN 16
@@ -629,7 +630,7 @@ pthread::cancel ()
       threadlist_t *tl_entry = cygheap->find_tls (cygtls);
       if (!cygtls->inside_kernel (&context))
 	{
-	  context.Rip = (ULONG_PTR) pthread::static_cancel_self;
+	  context._CX_instPtr = (ULONG_PTR) pthread::static_cancel_self;
 	  SetThreadContext (win32_obj_id, &context);
 	}
       cygheap->unlock_tls (tl_entry);
diff --git a/winsup/utils/profiler.cc b/winsup/utils/profiler.cc
index 4fe900b7f..04c6b3ed3 100644
--- a/winsup/utils/profiler.cc
+++ b/winsup/utils/profiler.cc
@@ -33,6 +33,7 @@ typedef uint16_t u_int16_t; // Non-standard sized type needed by ancient gmon.h
 #define NO_GLOBALS_H
 #include "gmon.h"
 #include "path.h"
+#include "register.h"
 
 /* Undo this #define from winsup.h. */
 #ifdef ExitThread
@@ -193,11 +194,7 @@ sample (CONTEXT *context, HANDLE h)
       return 0ULL;
     }
   else
-#ifdef __x86_64__
-    return context->Rip;
-#else
-#error unimplemented for this target
-#endif
+    return context->_CX_instPtr;
 }
 
 void
-- 
2.50.1.vfs.0.0

