[PATCH] Cygwin: ssp: add AArch64 build stubs

Chandru Kumaresan chandru.kumaresan@multicorewareinc.com
Thu May 21 08:58:42 GMT 2026


Hi Corinna,

This patch adds AArch64 support to SSP, including ARM64 breakpoints, single-step handling, register dumps, and kernel-call tracing support.

Comments and reviews are welcome.
Thanks & regards,
K Chandru

Inline patch

---
 winsup/utils/ssp.c | 152 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 140 insertions(+), 12 deletions(-)

diff --git a/winsup/utils/ssp.c b/winsup/utils/ssp.c
index 96a90a1d9..8dd7ef2ed 100644
--- a/winsup/utils/ssp.c
+++ b/winsup/utils/ssp.c
@@ -48,6 +48,13 @@ static char opts[] = "+cdehlstvV";
 typedef DWORD64 CONTEXT_REG;
 #define CONTEXT_REG_FMT "%016llx"
 #define ADDR_SSCANF_FMT "%lli"
+#elif defined(__aarch64__)
+#define KERNEL_ADDR 0x00007FF000000000
+#define CONTEXT_SP Sp
+#define CONTEXT_IP Pc
+typedef DWORD64 CONTEXT_REG;
+#define CONTEXT_REG_FMT "%016llx"
+#define ADDR_SSCANF_FMT "%lli"
 #else
 #error unimplemented for this target
 #endif
@@ -85,11 +92,18 @@ typedef struct {

 typedef struct {
   CONTEXT_REG address;
+#if defined(__aarch64__)
+  unsigned char real_insn[4]; /* ARM64 instructions are 4 bytes */
+#else
   unsigned char real_byte;
+#endif
 } PendingBreakpoints;

 CONTEXT_REG low_pc, high_pc=0;
 CONTEXT_REG last_pc=0, pc, last_sp=0, sp;
+#if defined(__aarch64__)
+CONTEXT_REG last_lr=0, lr;
+#endif
 int total_cycles, count;
 HANDLE hProcess;
 PROCESS_INFORMATION procinfo;
@@ -129,7 +143,12 @@ add_breakpoint (CONTEXT_REG address)
 {
   int i;
   SIZE_T rv;
-  static char int3[] = { 0xcc };
+#if defined(__aarch64__)
+  static unsigned char brk_insn[] = { 0x00, 0x00, 0x20, 0xd4 }; /* BRK #0, which matches MSVC's
+     __debugbreak recommendation on Windows/ARM64.*/
+#else
+  static unsigned char int3[] = { 0xcc };
+#endif
   for (i=0; i<num_breakpoints; i++)
     {
       if (pending_breakpoints[i].address == address)
@@ -140,14 +159,23 @@ add_breakpoint (CONTEXT_REG address)
   if (i == MAXPENDS)
     return;
   pending_breakpoints[i].address = address;
+#if defined(__aarch64__)
+  ReadProcessMemory (hProcess,
+                (void *)address,
+                pending_breakpoints[i].real_insn,
+                4, &rv);
+  WriteProcessMemory (hProcess,
+                 (void *)address,
+                 (LPVOID)brk_insn, 4, &rv);
+#else
   ReadProcessMemory (hProcess,
                 (void *)address,
                 &(pending_breakpoints[i].real_byte),
                 1, &rv);
-
   WriteProcessMemory (hProcess,
                  (void *)address,
                  (LPVOID)int3, 1, &rv);
+#endif
   if (i >= num_breakpoints)
     num_breakpoints = i+1;
 }
@@ -162,10 +190,17 @@ remove_breakpoint (CONTEXT_REG address)
       if (pending_breakpoints[i].address == address)
      {
        pending_breakpoints[i].address = 0;
+#if defined(__aarch64__)
+       WriteProcessMemory (hProcess,
+                       (void *)address,
+                       pending_breakpoints[i].real_insn,
+                       4, &rv);
+#else
        WriteProcessMemory (hProcess,
                        (void *)address,
                        &(pending_breakpoints[i].real_byte),
                        1, &rv);
+#endif
        return 1;
      }
     }
@@ -200,10 +235,19 @@ set_step_threads (int threadId, int trace)
   if (rv != -1)
     {
       thread_step_flags[tix] = trace;
+#if defined(__i386__) || defined(__x86_64__)
       if (trace)
-     context.EFlags |= 0x100; /* TRAP (single step) flag */
+       context.EFlags |= 0x100; /* TRAP (single step) flag */
       else
-     context.EFlags &= ~0x100; /* TRAP (single step) flag */
+       context.EFlags &= ~0x100; /* TRAP (single step) flag */
+#elif defined(__aarch64__)
+      if (trace)
+       context.Cpsr |= 0x00200000; /* PSTATE.SS (single step) flag */
+      else
+       context.Cpsr &= ~0x00200000; /* PSTATE.SS (single step) flag */
+#else
+#error unimplemented for this target
+#endif
       SetThreadContext (thread, &context);
     }
 }
@@ -215,7 +259,13 @@ set_steps ()
   for (i=0; i<num_active_threads; i++)
     {
       GetThreadContext (active_threads[i], &context);
+#if defined(__i386__) || defined(__x86_64__)
       s = context.EFlags & 0x0100;
+#elif defined(__aarch64__)
+      s = context.Cpsr & 0x00200000; /* PSTATE.SS (single step) flag */
+#else
+#error unimplemented for this target
+#endif
       if (!s && thread_step_flags[i])
      {
        set_step_threads (active_thread_ids[i], 1);
@@ -257,6 +307,25 @@ dump_registers (HANDLE thread)
        context.Rax, context.Rbx, context.Rcx, context.Rdx);
   printf ("esi %016llx edi %016llx ebp %016llx esp %016llx %016llx\n",
        context.Rsi, context.Rdi, context.Rbp, context.Rsp, context.Rip);
+#elif defined(__aarch64__)
+  printf ("x0 %016llx x1 %016llx x2 %016llx x3 %016llx\n",
+       context.X[0], context.X[1], context.X[2], context.X[3]);
+  printf ("x4 %016llx x5 %016llx x6 %016llx x7 %016llx\n",
+       context.X[4], context.X[5], context.X[6], context.X[7]);
+  printf ("x8 %016llx x9 %016llx x10 %016llx x11 %016llx\n",
+       context.X[8], context.X[9], context.X[10], context.X[11]);
+  printf ("x12 %016llx x13 %016llx x14 %016llx x15 %016llx\n",
+       context.X[12], context.X[13], context.X[14], context.X[15]);
+  printf ("x16 %016llx x17 %016llx x18 %016llx x19 %016llx\n",
+       context.X[16], context.X[17], context.X[18], context.X[19]);
+  printf ("x20 %016llx x21 %016llx x22 %016llx x23 %016llx\n",
+       context.X[20], context.X[21], context.X[22], context.X[23]);
+  printf ("x24 %016llx x25 %016llx x26 %016llx x27 %016llx\n",
+       context.X[24], context.X[25], context.X[26], context.X[27]);
+  printf ("x28 %016llx fp %016llx lr %016llx\n",
+       context.X[28], context.Fp, context.Lr);
+  printf ("sp %016llx pc %016llx cpsr %08x\n",
+       context.Sp, context.Pc, context.Cpsr);
 #else
 #error unimplemented for this target
 #endif
@@ -450,11 +519,17 @@ run_program (char *cmdline)
          case STATUS_BREAKPOINT:
            if (remove_breakpoint ((CONTEXT_REG)event.u.Exception.ExceptionRecord.ExceptionAddress))
            {
+#if defined(__aarch64__)
+             if (!rv)
+               SetThreadContext (hThread, &context);
+             thread_return_address[tix] = context.Lr;
+#else
              context.CONTEXT_IP --;
              if (!rv)
                SetThreadContext (hThread, &context);
              if (ReadProcessMemory (hProcess, (void *)context.CONTEXT_SP, &rv, sizeof(rv), &rv))
                  thread_return_address[tix] = rv;
+#endif
            }
            set_step_threads (event.dwThreadId, stepping_enabled);
            /*FALLTHRU*/
@@ -462,6 +537,9 @@ run_program (char *cmdline)
            opcode_count++;
            pc = (CONTEXT_REG)event.u.Exception.ExceptionRecord.ExceptionAddress;
            sp = context.CONTEXT_SP;
+#if defined(__aarch64__)
+           lr = context.Lr;
+#endif
            if (tracing_enabled)
            fprintf (tracefile, CONTEXT_REG_FMT " %08x\n", pc, (int)event.dwThreadId);
            if (trace_console)
@@ -486,20 +564,34 @@ run_program (char *cmdline)
                }
            }

-           if (pc < last_pc || pc > last_pc+10)
+       if (pc < last_pc || pc > last_pc+10)
            {
              static int ncalls=0;
              static int qq=0;
              if (++qq % 100 == 0)
                fprintf (stderr, " " CONTEXT_REG_FMT " %d %d \r",
                      pc, ncalls, opcode_count);
-
+#if defined(__aarch64__)
+             if (lr != last_lr && lr == last_pc + 4)
+#else
              if (sp == last_sp-sizeof(CONTEXT_REG))
+#endif
                {
                  ncalls++;
                  store_call_edge (last_pc, pc);
                  if (last_pc < KERNEL_ADDR && pc > KERNEL_ADDR)
-                 {
+                       {
+#if defined(__aarch64__)
+                   CONTEXT_REG retaddr = lr;
+                   if (verbose)
+                   printf ("skip kernel call: " CONTEXT_REG_FMT " -> " CONTEXT_REG_FMT ", ret = " CONTEXT_REG_FMT "\n",
+                               last_pc, pc, retaddr);
+                   if (retaddr && retaddr < KERNEL_ADDR)
+                     {
+                       add_breakpoint (retaddr);
+                       set_step_threads (event.dwThreadId, 0);
+                     }
+#else
 #if 0
                    CONTEXT_REG retaddr;
                    SIZE_T rv;
@@ -513,13 +605,17 @@ run_program (char *cmdline)
                    add_breakpoint (retaddr);
                    set_step_threads (event.dwThreadId, 0);
 #endif
-                 }
+#endif
+                       }
                }
            }

            total_cycles++;
            last_sp = sp;
            last_pc = pc;
+#if defined(__aarch64__)
+           last_lr = lr;
+#endif
            if (pc >= low_pc && pc < high_pc)
            hits[(pc - low_pc)/2] ++;
            break;
@@ -534,7 +630,12 @@ run_program (char *cmdline)
                dump_registers (hThread);
            }
            contv = DBG_EXCEPTION_NOT_HANDLED;
+#if defined(__aarch64__)
+           if (!event.u.Exception.dwFirstChance)
+           running = 0;
+#else
            running = 0;
+#endif
            break;
          }

@@ -542,19 +643,39 @@ run_program (char *cmdline)
          {
            if (pc == thread_return_address[tix])
            {
+#if defined(__i386__) || defined(__x86_64__)
              if (context.EFlags & 0x100)
                {
                  context.EFlags &= ~0x100; /* TRAP (single step) flag */
                  SetThreadContext (hThread, &context);
                }
+#elif defined(__aarch64__)
+             if (context.Cpsr & 0x00200000)
+               {
+                 context.Cpsr &= ~0x00200000; /* PSTATE.SS (single step) flag */
+                 SetThreadContext (hThread, &context);
+               }
+#else
+#error unimplemented for this target
+#endif
            }
            else if (stepping_enabled)
            {
+#if defined(__i386__) || defined(__x86_64__)
              if (!(context.EFlags & 0x100))
                {
                  context.EFlags |= 0x100; /* TRAP (single step) flag */
                  SetThreadContext (hThread, &context);
                }
+#elif defined(__aarch64__)
+             if (!(context.Cpsr & 0x00200000))
+               {
+                 context.Cpsr |= 0x00200000; /* PSTATE.SS (single step) flag */
+                 SetThreadContext (hThread, &context);
+               }
+#else
+#error unimplemented for this target
+#endif
            }
          }
        break;
@@ -916,10 +1037,17 @@ main (int argc, char **argv)
     }
   memset (hits, 0, range+4);

-  fprintf (stderr, "prun: [" CONTEXT_REG_FMT "," CONTEXT_REG_FMT "] Running '%s'\n",
+    fprintf (stderr, "prun: [" CONTEXT_REG_FMT "," CONTEXT_REG_FMT "] Running '%s'\n",
        low_pc, high_pc, argv[optind]);
-
-  run_program (argv[optind]);
+  {
+    char *cmdline_copy = strdup (argv[optind]);
+    if (!cmdline_copy)
+      {
+     fprintf (stderr, "Out of memory duplicating cmdline\n");
+     exit (1);
+      }
+    run_program (cmdline_copy);
+  }

   hdr.lpc = low_pc;
   hdr.hpc = high_pc;
@@ -935,7 +1063,7 @@ main (int argc, char **argv)

   if (dll_counts)
     {
-#ifdef __x86_64__
+#if defined(__x86_64__) || defined(__aarch64__)
       /*       1234567 123% 1234567 123% 1234567812345678 xxxxxxxxxxx */
       printf (" Main-Thread Other-Thread BaseAddr         DLL Name\n");
 #else
--
2.49.0.windows.1




-------------- next part --------------
A non-text attachment was scrubbed...
Name: Cygwin-ssp-add-AArch64-build-stubs.patch
Type: application/octet-stream
Size: 10940 bytes
Desc: Cygwin-ssp-add-AArch64-build-stubs.patch
URL: <https://cygwin.com/pipermail/cygwin-patches/attachments/20260521/bb46a557/attachment-0001.obj>


More information about the Cygwin-patches mailing list