[PATCH] Cygwin: Add AArch64 support for signal handling and ucontext routines

Aswin Kalies Ramkumar Mangayarkarasi <[email protected]> Mon, 1 Jun 2026 11:36:25 +0000
Newsgroups gmane.os.cygwin.patches
Message-ID <PN3P287MB1320988B69BF5F0D12079191F7152@PN3P287MB1320.INDP287.PROD.OUTLOOK.COM>
Hi Corinna,

This patch adds AArch64 support for signal handling and ucontext routines (call_signal_handler, setcontext, __cont_link_context, makecontext) in exceptions.cc, enabling Cygwin on ARM64 Windows.

Kindly review the patch.


Thanks and Regards,
Aswin Kalies

Inline patch

---
 winsup/cygwin/exceptions.cc | 309 +++++++++++++++++++++++++++++++-----
 1 file changed, 267 insertions(+), 42 deletions(-)

diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 1e129b319..a2212cabd 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -1892,7 +1892,7 @@ _cygtls::call_signal_handler ()

     /* In assembler: Save regs on new stack, move to alternate stack,
        call thisfunc, revert stack regs. */
-#ifdef __x86_64__
+#if defined(__x86_64__)
     /* Clobbered regs: rcx, rdx, r8, r9, r10, r11, rbp, rsp */
     __asm__ ("\n\
         movq  %[NEW_SP], %%rax  # Load alt stack into rax  \n\
@@ -1930,6 +1930,56 @@ _cygtls::call_signal_handler ()
             [FUNC]  "o" (thisfunc),
             [WRAPPER] "o" (altstack_wrapper)
         : "memory");
+#elif defined(__aarch64__)
+    __asm__ ("\n\
+        mov   x9, %[NEW_SP]     // Load alt stack into x9  \n\
+        sub   x9, x9, #0x60     // Make room on alt stack  \n\
+                 // for clobbered regs      \n\
+        str   x0, [x9, #0x00]      // Save clobbered regs     \n\
+        str   x1, [x9, #0x08]                  \n\
+        str   x2, [x9, #0x10]                  \n\
+        str   x3, [x9, #0x18]                  \n\
+        str   x4, [x9, #0x20]                  \n\
+        str   x5, [x9, #0x28]                  \n\
+        str   x6, [x9, #0x30]                  \n\
+        str   x7, [x9, #0x38]                  \n\
+        str   fp, [x9, #0x40]                  \n\
+        mov   x10, sp        // Copy sp into x10 for saving   \n\
+        str   x10, [x9, #0x48]              \n\
+        str   x30, [x9, #0x50]  // Save link register      \n\
+        mov   x0, %[SIG]     // thissig to 1st arg reg  \n\
+        mov   x1, %[SI]      // &thissi to 2nd arg reg  \n\
+        mov   x2, %[CTX]     // thiscontext to 3rd arg reg \n\
+        mov   x3, %[FUNC]    // thisfunc to x3    \n\
+        mov   x4, %[WRAPPER]    // wrapper address to x4   \n\
+        mov   sp, x9         // Move alt stack into sp  \n\
+        blr   x4       // Call wrapper         \n\
+        mov   x9, sp         // Restore clobbered regs  \n\
+        ldr   x30, [x9, #0x50]  // Restore link register   \n\
+        ldr   x10, [x9, #0x48]              \n\
+        ldr   fp,  [x9, #0x40]              \n\
+        ldr   x7,  [x9, #0x38]              \n\
+        ldr   x6,  [x9, #0x30]              \n\
+        ldr   x5,  [x9, #0x28]              \n\
+        ldr   x4,  [x9, #0x20]              \n\
+        ldr   x3,  [x9, #0x18]              \n\
+        ldr   x2,  [x9, #0x10]              \n\
+        ldr   x1,  [x9, #0x08]              \n\
+        ldr   x0,  [x9, #0x00]              \n\
+        mov   sp,  x10    // Restore stack pointer   \n"
+        : : [NEW_SP]    "r" (new_sp),
+            [SIG]    "r" (thissig),
+            [SI]  "r" (&thissi),
+            [CTX]    "r" (thiscontext),
+            [FUNC]   "r" (thisfunc),
+            [WRAPPER] "r" (altstack_wrapper)
+        : "memory", "cc",
+          "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+          "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+          "x16", "x17", "x29", "x30",
+          "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+          "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
+          "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31");
 #else
 #error unimplemented for this target
 #endif
@@ -2009,10 +2059,78 @@ setcontext (const ucontext_t *ucp)
 {
   PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext;
   set_signal_mask (_my_tls.sigmask, ucp->uc_sigmask);
+#if defined(__aarch64__)
+  /* On ARM64 Windows, RtlRestoreContext may fail (STATUS_ILLEGAL_INSTRUCTION)
+     when restoring a synthetic CONTEXT that was constructed by makecontext
+     rather than captured by RtlCaptureContext / GetThreadContext.  This is
+     because RtlRestoreContext may perform validation or use internal
+     mechanisms (e.g. stack unwinding hints) that don't work with synthetic
+     contexts pointing to arbitrary PC addresses and non-thread stacks.
+
+     Instead, we manually restore all registers and branch to the saved PC.
+     This mirrors what glibc/musl do in their setcontext implementations
+     for aarch64-linux.  */
+  register PCONTEXT base __asm__ ("x16") = ctx;
+  __asm__ __volatile__ ("\n\
+  add   x17, x16, #272             \n\
+  ldp   q0, q1, [x17, #0]          \n\
+  ldp   q2, q3, [x17, #32]            \n\
+  ldp   q4, q5, [x17, #64]            \n\
+  ldp   q6, q7, [x17, #96]            \n\
+  ldp   q8, q9, [x17, #128]           \n\
+  ldp   q10, q11, [x17, #160]            \n\
+  ldp   q12, q13, [x17, #192]            \n\
+  ldp   q14, q15, [x17, #224]            \n\
+  ldp   q16, q17, [x17, #256]            \n\
+  ldp   q18, q19, [x17, #288]            \n\
+  ldp   q20, q21, [x17, #320]            \n\
+  ldp   q22, q23, [x17, #352]            \n\
+  ldp   q24, q25, [x17, #384]            \n\
+  ldp   q26, q27, [x17, #416]            \n\
+  ldp   q28, q29, [x17, #448]            \n\
+  ldp   q30, q31, [x17, #480]            \n\
+  /* Restore FPCR and FPSR */            \n\
+  ldr   w17, [x16, #784]           \n\
+  msr   fpcr, x17               \n\
+  ldr   w17, [x16, #788]           \n\
+  msr   fpsr, x17               \n\
+  /* Load PC into x17 (branch target, offset 264) */ \n\
+  ldr   x17, [x16, #264]           \n\
+  /* Restore callee-saved GPRs x18..x28, fp, lr */   \n\
+  ldp   x18, x19, [x16, #152]            \n\
+  ldp   x20, x21, [x16, #168]            \n\
+  ldp   x22, x23, [x16, #184]            \n\
+  ldp   x24, x25, [x16, #200]            \n\
+  ldp   x26, x27, [x16, #216]            \n\
+  ldp   x28, x29, [x16, #232]            \n\
+  ldr   x30, [x16, #248]           \n\
+  /* Restore caller-saved GPRs x2..x15 */         \n\
+  ldp   x2, x3, [x16, #24]            \n\
+  ldp   x4, x5, [x16, #40]            \n\
+  ldp   x6, x7, [x16, #56]            \n\
+  ldp   x8, x9, [x16, #72]            \n\
+  ldp   x10, x11, [x16, #88]          \n\
+  ldp   x12, x13, [x16, #104]            \n\
+  ldp   x14, x15, [x16, #120]            \n\
+  /* Restore x0, x1 */             \n\
+  ldp   x0, x1, [x16, #8]          \n\
+  /* Set SP from context (last use of x16 as base) */   \n\
+  ldr   x16, [x16, #256]           \n\
+  mov   sp, x16                 \n\
+  /* Branch to saved PC */            \n\
+  br x17                  \n\
+"
+  : /* no outputs (noreturn) */
+  : "r" (base)
+  : "memory"
+  );
+  __builtin_unreachable ();
+#else
   RtlRestoreContext (ctx, NULL);
   /* If we got here, something was wrong. */
   set_errno (EINVAL);
   return -1;
+#endif
 }

 extern "C" int
@@ -2049,7 +2167,7 @@ swapcontext (ucontext_t *oucp, const ucontext_t *ucp)
 /* Trampoline function to set the context to uc_link.  The pointer to the
    address of uc_link is stored in a callee-saved register, referenced by
    _MC_uclinkReg from the C code.  If uc_link is NULL, call exit. */
-#ifdef __x86_64__
+#if defined(__x86_64__)
 /* _MC_uclinkReg == %rbx */
 __asm__ ("          \n\
   .global  __cont_link_context  \n\
@@ -2070,7 +2188,21 @@ __cont_link_context:        \n\
   nop            \n\
   .seh_endproc         \n\
   ");
-
+#elif defined(__aarch64__)
+__asm__ ("             \n\
+  .global  __cont_link_context     \n\
+  .seh_proc __cont_link_context    \n\
+__cont_link_context:            \n\
+  .seh_endprologue        \n\
+  ldr   x0, [x19]         \n\
+  and   sp, x19, #~0xf       \n\
+  cbnz  x0, 1f            \n\
+  mov   w0, #0xff         \n\
+  b  cygwin_exit       \n\
+1:                  \n\
+  b  setcontext        \n\
+  .seh_endproc            \n"
+  );
 #else
 #error unimplemented for this target
 #endif
@@ -2078,11 +2210,19 @@ __cont_link_context:       \n\
 /* makecontext is modelled after GLibc's makecontext.  The stack from uc_stack
    is prepared so that it starts with a pointer to the linked context uc_link,
    followed by the arguments to func, and finally at the bottom the "return"
-   address set to __cont_link_context.  In the ucp context, rbx/ebx is set to
-   point to the stack address where the pointer to uc_link is stored.  The
-   requirement to make this work is that rbx/ebx are callee-saved registers
-   per the ABI.  If any function is called which doesn't follow the ABI
-   conventions, e.g. assembler code, this method will break.  But that's ok. */
+   address set to __cont_link_context.
+
+   x86_64: In the ucp context, rbx is set to point to the stack address where
+   the pointer to uc_link is stored.  The requirement to make this work is that
+   rbx is a callee-saved register per the ABI.
+
+   ARM64: In the ucp context, x19 is set to point to the stack address where
+   the pointer to uc_link is stored.  The requirement is that x19 is a
+   callee-saved register per the ARM64 ABI.
+
+   If any function is called which doesn't follow the ABI conventions, e.g.
+   assembler code, this method will break.  But that's ok. */
+
 extern "C" void
 makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
 {
@@ -2090,65 +2230,150 @@ makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
   uintptr_t *sp;
   va_list ap;

+#if defined(__x86_64__)
+  /* x86_64: Arguments beyond the first 4 go on the stack.
+     However, we allocate shadow space for all args including register args. */
+  int stack_args = argc;
+
+#elif defined(__aarch64__)
+  /* ARM64: Arguments beyond the first 8 go on the stack.
+     We only allocate stack space for args beyond registers. */
+  int stack_args = (argc > 8) ? (argc - 8) : 0;
+
+#else
+#error unimplemented for this target
+#endif
+
   /* Initialize sp to the top of the stack. */
   sp = (uintptr_t *) ((uintptr_t) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size);
-  /* Subtract slots required for arguments and the pointer to uc_link. */
-  sp -= (argc + 1);
-  /* Align. */
-  sp = (uintptr_t *) ((uintptr_t) sp & ~0xf);
-  /* Subtract one slot for setting the return address. */
+
+#if defined(__x86_64__)
+  /* x86_64: Subtract slots for all arguments + uc_link pointer
+     and return address.  */
+  sp -= (stack_args + 1);  /* argc + 1 for uc_link */
+  /* Align to 16 bytes. */
+  sp = (uintptr_t *) ((uintptr_t) sp & ~0xfUL);
+  /* Subtract one more slot for the return address. */
   --sp;
-  /* Set return address to the trampolin function __cont_link_context. */
+  /* Set return address to the trampoline function __cont_link_context. */
   sp[0] = (uintptr_t) __cont_link_context;
-  /* Fetch arguments and store them on the stack.

-     x86_64:
+#elif defined(__aarch64__)
+  /* ARM64: Subtract slots for stack arguments + uc_link pointer. */
+  sp -= (stack_args + 1);  /* stack_args + 1 for uc_link */
+  /* ARM64 requires 16-byte alignment at public interfaces. */
+  sp = (uintptr_t *) ((uintptr_t) sp & ~0xfUL);

-     - Store first four args in the AMD64 ABI arg registers.
+#endif

+  /* Fetch arguments and store them.
+     x86_64:
+     - Store first four args in the AMD64 ABI arg registers (rcx, rdx, r8, r9).
      - Note that the stack is not short by these four register args.  The
        reason is the shadow space for these regs required by the AMD64 ABI.
-
      - The definition of makecontext only allows for "int" sized arguments to
        func, 32 bit, likely for historical reasons.  However, the argument
        slots on x86_64 are 64 bit anyway, so we can fetch and store the args
        as 64 bit values, and func can request 64 bit args without violating
        the definition.  This potentially allows porting 32 bit applications
-       providing pointer values to func without additional porting effort. */
+       providing pointer values to func without additional porting effort.
+
+     ARM64:
+     - Store first eight args in ARM64 ABI arg registers (x0-x7).
+     - Arguments beyond 8 go on the stack.
+     - Similar to x86_64, we store as uintptr_t for pointer compatibility. */
+
   va_start (ap, argc);
   for (int i = 0; i < argc; ++i)
-#ifdef __x86_64__
-    switch (i)
-      {
-      case 0:
-  ucp->uc_mcontext.rcx = va_arg (ap, uintptr_t);
-  break;
-      case 1:
-  ucp->uc_mcontext.rdx = va_arg (ap, uintptr_t);
-  break;
-      case 2:
-  ucp->uc_mcontext.r8 = va_arg (ap, uintptr_t);
-  break;
-      case 3:
-  ucp->uc_mcontext.r9 = va_arg (ap, uintptr_t);
-  break;
-      default:
-  sp[i + 1] = va_arg (ap, uintptr_t);
-  break;
-      }
-#else
-#error unimplemented for this target
+    {
+#if defined(__x86_64__)
+      switch (i)
+        {
+        case 0:
+          ucp->uc_mcontext.rcx = va_arg (ap, uintptr_t);
+          break;
+        case 1:
+          ucp->uc_mcontext.rdx = va_arg (ap, uintptr_t);
+          break;
+        case 2:
+          ucp->uc_mcontext.r8 = va_arg (ap, uintptr_t);
+          break;
+        case 3:
+          ucp->uc_mcontext.r9 = va_arg (ap, uintptr_t);
+          break;
+        default:
+          /* Stack arguments start at sp[i + 1] because sp[0] is return address */
+          sp[i + 1] = va_arg (ap, uintptr_t);
+          break;
+        }
+
+#elif defined(__aarch64__)
+      switch (i)
+        {
+        case 0:
+          ucp->uc_mcontext.x0 = va_arg (ap, uintptr_t);
+          break;
+        case 1:
+          ucp->uc_mcontext.x1 = va_arg (ap, uintptr_t);
+          break;
+        case 2:
+          ucp->uc_mcontext.x2 = va_arg (ap, uintptr_t);
+          break;
+        case 3:
+          ucp->uc_mcontext.x3 = va_arg (ap, uintptr_t);
+          break;
+        case 4:
+          ucp->uc_mcontext.x4 = va_arg (ap, uintptr_t);
+          break;
+        case 5:
+          ucp->uc_mcontext.x5 = va_arg (ap, uintptr_t);
+          break;
+        case 6:
+          ucp->uc_mcontext.x6 = va_arg (ap, uintptr_t);
+          break;
+        case 7:
+          ucp->uc_mcontext.x7 = va_arg (ap, uintptr_t);
+          break;
+        default:
+          /* Stack arguments beyond the first 8 registers. */
+          sp[i - 8] = va_arg (ap, uintptr_t);
+          break;
+        }
 #endif
+    }
   va_end (ap);
-  /* Store pointer to uc_link at the top of the stack. */
+
+#if defined(__x86_64__)
+  /* Store pointer to uc_link at sp[argc + 1], after return address
+     and args.  */
   sp[argc + 1] = (uintptr_t) ucp->uc_link;
+
+#elif defined(__aarch64__)
+  /* Store pointer to uc_link at the top of our allocated area. */
+  sp[stack_args] = (uintptr_t) ucp->uc_link;
+
+#endif
+
   /* Last but not least set the register in the context at ucp so that a
      subsequent setcontext or swapcontext picks up the right values:
      - Set instruction pointer to the target function.
      - Set stack pointer to the just computed stack pointer value.
      - Set Cygwin-specific uclink register to the address of the pointer
-       to uc_link. */
+       to uc_link.
+
+     x86_64: uclink register is rbx (callee-saved)
+     ARM64:  uclink register is x19 (callee-saved) */
+
   ucp->uc_mcontext._MC_instPtr = (uint64_t) func;
   ucp->uc_mcontext._MC_stackPtr = (uint64_t) sp;
+
+#if defined(__x86_64__)
   ucp->uc_mcontext._MC_uclinkReg = (uint64_t) (sp + argc + 1);
+
+#elif defined(__aarch64__)
+  /* Set LR to __cont_link_context for ARM64 (used as return address). */
+  ucp->uc_mcontext.lr = (uint64_t) __cont_link_context;
+  ucp->uc_mcontext._MC_uclinkReg = (uint64_t) (sp + stack_args);
+
+#endif
 }
--
2.50.1.windows.1
Cygwin-Add-AArch64-support-for-signal-handling-and-u.patch (application/octet-stream, 14.9 KB)
From 177874c775d257076ca204bca8ebecbb64d7cfdf Mon Sep 17 00:00:00 2001
From: Aswin Kalies <[email protected]>
Date: Mon, 1 Jun 2026 16:30:36 +0530
Subject: [PATCH] Cygwin: Add AArch64 support for signal handling and ucontext
 routines
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add AArch64 support for signal handling and ucontext routines
(call_signal_handler, setcontext, __cont_link_context, makecontext)
in exceptions.cc, enabling Cygwin on ARM64 Windows. setcontext uses
manual asm instead of RtlRestoreContext, which fails with synthetic
CONTEXT structs from makecontext. #ifdef guards are also updated to
 #if defined() for consistency.

Signed-off-by: Radek BartoĊˆ <[email protected]>
Signed-off-by: Thirumalai Nagalingam <[email protected]>
Signed-off-by: Aswin Kalies <[email protected]>
---
 winsup/cygwin/exceptions.cc | 309 +++++++++++++++++++++++++++++++-----
 1 file changed, 267 insertions(+), 42 deletions(-)

diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc
index 1e129b319..a2212cabd 100644
--- a/winsup/cygwin/exceptions.cc
+++ b/winsup/cygwin/exceptions.cc
@@ -1892,7 +1892,7 @@ _cygtls::call_signal_handler ()
 
 	  /* In assembler: Save regs on new stack, move to alternate stack,
 	     call thisfunc, revert stack regs. */
-#ifdef __x86_64__
+#if defined(__x86_64__)
 	  /* Clobbered regs: rcx, rdx, r8, r9, r10, r11, rbp, rsp */
 	  __asm__ ("\n\
 		   movq  %[NEW_SP], %%rax  # Load alt stack into rax	\n\
@@ -1930,6 +1930,56 @@ _cygtls::call_signal_handler ()
 		       [FUNC]	"o" (thisfunc),
 		       [WRAPPER] "o" (altstack_wrapper)
 		   : "memory");
+#elif defined(__aarch64__)
+	  __asm__ ("\n\
+		   mov	x9, %[NEW_SP]		// Load alt stack into x9	\n\
+		   sub	x9, x9, #0x60		// Make room on alt stack	\n\
+						// for clobbered regs		\n\
+		   str	x0, [x9, #0x00]		// Save clobbered regs		\n\
+		   str	x1, [x9, #0x08]						\n\
+		   str	x2, [x9, #0x10]						\n\
+		   str	x3, [x9, #0x18]						\n\
+		   str	x4, [x9, #0x20]						\n\
+		   str	x5, [x9, #0x28]						\n\
+		   str	x6, [x9, #0x30]						\n\
+		   str	x7, [x9, #0x38]						\n\
+		   str	fp, [x9, #0x40]						\n\
+		   mov	x10, sp			// Copy sp into x10 for saving	\n\
+		   str	x10, [x9, #0x48]					\n\
+		   str	x30, [x9, #0x50]	// Save link register		\n\
+		   mov	x0, %[SIG]		// thissig to 1st arg reg	\n\
+		   mov	x1, %[SI]		// &thissi to 2nd arg reg	\n\
+		   mov	x2, %[CTX]		// thiscontext to 3rd arg reg	\n\
+		   mov	x3, %[FUNC]		// thisfunc to x3		\n\
+		   mov	x4, %[WRAPPER]		// wrapper address to x4	\n\
+		   mov	sp, x9			// Move alt stack into sp	\n\
+		   blr	x4			// Call wrapper			\n\
+		   mov	x9, sp			// Restore clobbered regs	\n\
+		   ldr	x30, [x9, #0x50]	// Restore link register	\n\
+		   ldr	x10, [x9, #0x48]					\n\
+		   ldr	fp,  [x9, #0x40]					\n\
+		   ldr	x7,  [x9, #0x38]					\n\
+		   ldr	x6,  [x9, #0x30]					\n\
+		   ldr	x5,  [x9, #0x28]					\n\
+		   ldr	x4,  [x9, #0x20]					\n\
+		   ldr	x3,  [x9, #0x18]					\n\
+		   ldr	x2,  [x9, #0x10]					\n\
+		   ldr	x1,  [x9, #0x08]					\n\
+		   ldr	x0,  [x9, #0x00]					\n\
+		   mov	sp,  x10		// Restore stack pointer	\n"
+		   : : [NEW_SP]	 "r" (new_sp),
+		       [SIG]	 "r" (thissig),
+		       [SI]	 "r" (&thissi),
+		       [CTX]	 "r" (thiscontext),
+		       [FUNC]	 "r" (thisfunc),
+		       [WRAPPER] "r" (altstack_wrapper)
+		   : "memory", "cc",
+		     "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7",
+		     "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15",
+		     "x16", "x17", "x29", "x30",
+		     "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7",
+		     "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23",
+		     "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31");
 #else
 #error unimplemented for this target
 #endif
@@ -2009,10 +2059,78 @@ setcontext (const ucontext_t *ucp)
 {
   PCONTEXT ctx = (PCONTEXT) &ucp->uc_mcontext;
   set_signal_mask (_my_tls.sigmask, ucp->uc_sigmask);
+#if defined(__aarch64__)
+  /* On ARM64 Windows, RtlRestoreContext may fail (STATUS_ILLEGAL_INSTRUCTION)
+     when restoring a synthetic CONTEXT that was constructed by makecontext
+     rather than captured by RtlCaptureContext / GetThreadContext.  This is
+     because RtlRestoreContext may perform validation or use internal
+     mechanisms (e.g. stack unwinding hints) that don't work with synthetic
+     contexts pointing to arbitrary PC addresses and non-thread stacks.
+
+     Instead, we manually restore all registers and branch to the saved PC.
+     This mirrors what glibc/musl do in their setcontext implementations
+     for aarch64-linux.  */
+  register PCONTEXT base __asm__ ("x16") = ctx;
+  __asm__ __volatile__ ("\n\
+	add	x17, x16, #272					\n\
+	ldp	q0, q1, [x17, #0]				\n\
+	ldp	q2, q3, [x17, #32]				\n\
+	ldp	q4, q5, [x17, #64]				\n\
+	ldp	q6, q7, [x17, #96]				\n\
+	ldp	q8, q9, [x17, #128]				\n\
+	ldp	q10, q11, [x17, #160]				\n\
+	ldp	q12, q13, [x17, #192]				\n\
+	ldp	q14, q15, [x17, #224]				\n\
+	ldp	q16, q17, [x17, #256]				\n\
+	ldp	q18, q19, [x17, #288]				\n\
+	ldp	q20, q21, [x17, #320]				\n\
+	ldp	q22, q23, [x17, #352]				\n\
+	ldp	q24, q25, [x17, #384]				\n\
+	ldp	q26, q27, [x17, #416]				\n\
+	ldp	q28, q29, [x17, #448]				\n\
+	ldp	q30, q31, [x17, #480]				\n\
+	/* Restore FPCR and FPSR */				\n\
+	ldr	w17, [x16, #784]				\n\
+	msr	fpcr, x17					\n\
+	ldr	w17, [x16, #788]				\n\
+	msr	fpsr, x17					\n\
+	/* Load PC into x17 (branch target, offset 264) */	\n\
+	ldr	x17, [x16, #264]				\n\
+	/* Restore callee-saved GPRs x18..x28, fp, lr */	\n\
+	ldp	x18, x19, [x16, #152]				\n\
+	ldp	x20, x21, [x16, #168]				\n\
+	ldp	x22, x23, [x16, #184]				\n\
+	ldp	x24, x25, [x16, #200]				\n\
+	ldp	x26, x27, [x16, #216]				\n\
+	ldp	x28, x29, [x16, #232]				\n\
+	ldr	x30, [x16, #248]				\n\
+	/* Restore caller-saved GPRs x2..x15 */			\n\
+	ldp	x2, x3, [x16, #24]				\n\
+	ldp	x4, x5, [x16, #40]				\n\
+	ldp	x6, x7, [x16, #56]				\n\
+	ldp	x8, x9, [x16, #72]				\n\
+	ldp	x10, x11, [x16, #88]				\n\
+	ldp	x12, x13, [x16, #104]				\n\
+	ldp	x14, x15, [x16, #120]				\n\
+	/* Restore x0, x1 */					\n\
+	ldp	x0, x1, [x16, #8]				\n\
+	/* Set SP from context (last use of x16 as base) */	\n\
+	ldr	x16, [x16, #256]				\n\
+	mov	sp, x16						\n\
+	/* Branch to saved PC */				\n\
+	br	x17						\n\
+"
+	: /* no outputs (noreturn) */
+	: "r" (base)
+	: "memory"
+  );
+  __builtin_unreachable ();
+#else
   RtlRestoreContext (ctx, NULL);
   /* If we got here, something was wrong. */
   set_errno (EINVAL);
   return -1;
+#endif
 }
 
 extern "C" int
@@ -2049,7 +2167,7 @@ swapcontext (ucontext_t *oucp, const ucontext_t *ucp)
 /* Trampoline function to set the context to uc_link.  The pointer to the
    address of uc_link is stored in a callee-saved register, referenced by
    _MC_uclinkReg from the C code.  If uc_link is NULL, call exit. */
-#ifdef __x86_64__
+#if defined(__x86_64__)
 /* _MC_uclinkReg == %rbx */
 __asm__ ("				\n\
 	.global	__cont_link_context	\n\
@@ -2070,7 +2188,21 @@ __cont_link_context:			\n\
 	nop				\n\
 	.seh_endproc			\n\
 	");
-
+#elif defined(__aarch64__)
+__asm__ ("					\n\
+	.global	__cont_link_context		\n\
+	.seh_proc __cont_link_context		\n\
+__cont_link_context:				\n\
+	.seh_endprologue			\n\
+	ldr	x0, [x19]			\n\
+	and	sp, x19, #~0xf			\n\
+	cbnz	x0, 1f				\n\
+	mov	w0, #0xff			\n\
+	b	cygwin_exit			\n\
+1:						\n\
+	b	setcontext			\n\
+	.seh_endproc				\n"
+	);
 #else
 #error unimplemented for this target
 #endif
@@ -2078,11 +2210,19 @@ __cont_link_context:			\n\
 /* makecontext is modelled after GLibc's makecontext.  The stack from uc_stack
    is prepared so that it starts with a pointer to the linked context uc_link,
    followed by the arguments to func, and finally at the bottom the "return"
-   address set to __cont_link_context.  In the ucp context, rbx/ebx is set to
-   point to the stack address where the pointer to uc_link is stored.  The
-   requirement to make this work is that rbx/ebx are callee-saved registers
-   per the ABI.  If any function is called which doesn't follow the ABI
-   conventions, e.g. assembler code, this method will break.  But that's ok. */
+   address set to __cont_link_context.
+
+   x86_64: In the ucp context, rbx is set to point to the stack address where
+   the pointer to uc_link is stored.  The requirement to make this work is that
+   rbx is a callee-saved register per the ABI.
+
+   ARM64: In the ucp context, x19 is set to point to the stack address where
+   the pointer to uc_link is stored.  The requirement is that x19 is a
+   callee-saved register per the ARM64 ABI.
+
+   If any function is called which doesn't follow the ABI conventions, e.g.
+   assembler code, this method will break.  But that's ok. */
+
 extern "C" void
 makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
 {
@@ -2090,65 +2230,150 @@ makecontext (ucontext_t *ucp, void (*func) (void), int argc, ...)
   uintptr_t *sp;
   va_list ap;
 
+#if defined(__x86_64__)
+  /* x86_64: Arguments beyond the first 4 go on the stack.
+     However, we allocate shadow space for all args including register args. */
+  int stack_args = argc;
+
+#elif defined(__aarch64__)
+  /* ARM64: Arguments beyond the first 8 go on the stack.
+     We only allocate stack space for args beyond registers. */
+  int stack_args = (argc > 8) ? (argc - 8) : 0;
+
+#else
+#error unimplemented for this target
+#endif
+
   /* Initialize sp to the top of the stack. */
   sp = (uintptr_t *) ((uintptr_t) ucp->uc_stack.ss_sp + ucp->uc_stack.ss_size);
-  /* Subtract slots required for arguments and the pointer to uc_link. */
-  sp -= (argc + 1);
-  /* Align. */
-  sp = (uintptr_t *) ((uintptr_t) sp & ~0xf);
-  /* Subtract one slot for setting the return address. */
+
+#if defined(__x86_64__)
+  /* x86_64: Subtract slots for all arguments + uc_link pointer
+     and return address.  */
+  sp -= (stack_args + 1);  /* argc + 1 for uc_link */
+  /* Align to 16 bytes. */
+  sp = (uintptr_t *) ((uintptr_t) sp & ~0xfUL);
+  /* Subtract one more slot for the return address. */
   --sp;
-  /* Set return address to the trampolin function __cont_link_context. */
+  /* Set return address to the trampoline function __cont_link_context. */
   sp[0] = (uintptr_t) __cont_link_context;
-  /* Fetch arguments and store them on the stack.
 
-     x86_64:
+#elif defined(__aarch64__)
+  /* ARM64: Subtract slots for stack arguments + uc_link pointer. */
+  sp -= (stack_args + 1);  /* stack_args + 1 for uc_link */
+  /* ARM64 requires 16-byte alignment at public interfaces. */
+  sp = (uintptr_t *) ((uintptr_t) sp & ~0xfUL);
 
-     - Store first four args in the AMD64 ABI arg registers.
+#endif
 
+  /* Fetch arguments and store them.
+     x86_64:
+     - Store first four args in the AMD64 ABI arg registers (rcx, rdx, r8, r9).
      - Note that the stack is not short by these four register args.  The
        reason is the shadow space for these regs required by the AMD64 ABI.
-
      - The definition of makecontext only allows for "int" sized arguments to
        func, 32 bit, likely for historical reasons.  However, the argument
        slots on x86_64 are 64 bit anyway, so we can fetch and store the args
        as 64 bit values, and func can request 64 bit args without violating
        the definition.  This potentially allows porting 32 bit applications
-       providing pointer values to func without additional porting effort. */
+       providing pointer values to func without additional porting effort.
+
+     ARM64:
+     - Store first eight args in ARM64 ABI arg registers (x0-x7).
+     - Arguments beyond 8 go on the stack.
+     - Similar to x86_64, we store as uintptr_t for pointer compatibility. */
+
   va_start (ap, argc);
   for (int i = 0; i < argc; ++i)
-#ifdef __x86_64__
-    switch (i)
-      {
-      case 0:
-	ucp->uc_mcontext.rcx = va_arg (ap, uintptr_t);
-	break;
-      case 1:
-	ucp->uc_mcontext.rdx = va_arg (ap, uintptr_t);
-	break;
-      case 2:
-	ucp->uc_mcontext.r8 = va_arg (ap, uintptr_t);
-	break;
-      case 3:
-	ucp->uc_mcontext.r9 = va_arg (ap, uintptr_t);
-	break;
-      default:
-	sp[i + 1] = va_arg (ap, uintptr_t);
-	break;
-      }
-#else
-#error unimplemented for this target
+    {
+#if defined(__x86_64__)
+      switch (i)
+        {
+        case 0:
+          ucp->uc_mcontext.rcx = va_arg (ap, uintptr_t);
+          break;
+        case 1:
+          ucp->uc_mcontext.rdx = va_arg (ap, uintptr_t);
+          break;
+        case 2:
+          ucp->uc_mcontext.r8 = va_arg (ap, uintptr_t);
+          break;
+        case 3:
+          ucp->uc_mcontext.r9 = va_arg (ap, uintptr_t);
+          break;
+        default:
+          /* Stack arguments start at sp[i + 1] because sp[0] is return address */
+          sp[i + 1] = va_arg (ap, uintptr_t);
+          break;
+        }
+
+#elif defined(__aarch64__)
+      switch (i)
+        {
+        case 0:
+          ucp->uc_mcontext.x0 = va_arg (ap, uintptr_t);
+          break;
+        case 1:
+          ucp->uc_mcontext.x1 = va_arg (ap, uintptr_t);
+          break;
+        case 2:
+          ucp->uc_mcontext.x2 = va_arg (ap, uintptr_t);
+          break;
+        case 3:
+          ucp->uc_mcontext.x3 = va_arg (ap, uintptr_t);
+          break;
+        case 4:
+          ucp->uc_mcontext.x4 = va_arg (ap, uintptr_t);
+          break;
+        case 5:
+          ucp->uc_mcontext.x5 = va_arg (ap, uintptr_t);
+          break;
+        case 6:
+          ucp->uc_mcontext.x6 = va_arg (ap, uintptr_t);
+          break;
+        case 7:
+          ucp->uc_mcontext.x7 = va_arg (ap, uintptr_t);
+          break;
+        default:
+          /* Stack arguments beyond the first 8 registers. */
+          sp[i - 8] = va_arg (ap, uintptr_t);
+          break;
+        }
 #endif
+    }
   va_end (ap);
-  /* Store pointer to uc_link at the top of the stack. */
+
+#if defined(__x86_64__)
+  /* Store pointer to uc_link at sp[argc + 1], after return address
+     and args.  */
   sp[argc + 1] = (uintptr_t) ucp->uc_link;
+
+#elif defined(__aarch64__)
+  /* Store pointer to uc_link at the top of our allocated area. */
+  sp[stack_args] = (uintptr_t) ucp->uc_link;
+
+#endif
+
   /* Last but not least set the register in the context at ucp so that a
      subsequent setcontext or swapcontext picks up the right values:
      - Set instruction pointer to the target function.
      - Set stack pointer to the just computed stack pointer value.
      - Set Cygwin-specific uclink register to the address of the pointer
-       to uc_link. */
+       to uc_link.
+
+     x86_64: uclink register is rbx (callee-saved)
+     ARM64:  uclink register is x19 (callee-saved) */
+
   ucp->uc_mcontext._MC_instPtr = (uint64_t) func;
   ucp->uc_mcontext._MC_stackPtr = (uint64_t) sp;
+
+#if defined(__x86_64__)
   ucp->uc_mcontext._MC_uclinkReg = (uint64_t) (sp + argc + 1);
+
+#elif defined(__aarch64__)
+  /* Set LR to __cont_link_context for ARM64 (used as return address). */
+  ucp->uc_mcontext.lr = (uint64_t) __cont_link_context;
+  ucp->uc_mcontext._MC_uclinkReg = (uint64_t) (sp + stack_args);
+
+#endif
 }
-- 
2.50.1.windows.1