From e55dec2bd7fb60c4b552a26896db4e9e8a598fad Mon Sep 17 00:00:00 2001 From: chandru-mcw Date: Thu, 21 May 2026 13:07:09 +0530 Subject: [PATCH v2] Cygwin: ssp: Add AArch64 implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add AArch64 support to ssp by implementing ARM64 breakpoints, single-step handling, register dumps, and kernel-call tracing. Use BRK instructions for breakpoints, handle PSTATE.SS for single-stepping, and add ARM64-specific context/register support. Also fix command-line handling by duplicating the input command string before passing it to run_program(). Signed-off-by: Radek BartoĊˆ Signed-off-by: Thirumalai Nagalingam Signed-off-by: Chandru Kumaresan --- winsup/utils/ssp.c | 154 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 140 insertions(+), 14 deletions(-) diff --git a/winsup/utils/ssp.c b/winsup/utils/ssp.c index 96a90a1d9..6d4736c78 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 @@ -83,13 +90,26 @@ typedef struct { char *name; } DllInfo; +/* Size in bytes of the software breakpoint instruction (INT3 on x86, + BRK on AArch64). */ +#if defined(__i386__) || defined(__x86_64__) +#define SW_BREAKPOINT_SIZE 1 +#elif defined(__aarch64__) +#define SW_BREAKPOINT_SIZE 4 +#else +#error unimplemented for this target +#endif + typedef struct { CONTEXT_REG address; - unsigned char real_byte; + unsigned char real_insn[SW_BREAKPOINT_SIZE]; } 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 +149,13 @@ add_breakpoint (CONTEXT_REG address) { int i; SIZE_T rv; - static char int3[] = { 0xcc }; +#if defined(__i386__) || defined(__x86_64__) + static unsigned char brk_insn[] = { 0xcc }; +#elif defined(__aarch64__) + static unsigned char brk_insn[] = { 0x00, 0x00, 0x20, 0xd4 }; +#else +#error unimplemented for this target +#endif for (i=0; i= num_breakpoints) num_breakpoints = i+1; } @@ -164,8 +189,8 @@ remove_breakpoint (CONTEXT_REG address) pending_breakpoints[i].address = 0; WriteProcessMemory (hProcess, (void *)address, - &(pending_breakpoints[i].real_byte), - 1, &rv); + pending_breakpoints[i].real_insn, + SW_BREAKPOINT_SIZE, &rv); return 1; } } @@ -200,10 +225,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 +249,13 @@ set_steps () for (i=0; i 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; @@ -512,6 +594,7 @@ run_program (char *cmdline) /* experimental - try to skip kernel calls for speed */ add_breakpoint (retaddr); set_step_threads (event.dwThreadId, 0); +#endif #endif } } @@ -520,6 +603,9 @@ run_program (char *cmdline) 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 +620,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 +633,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; @@ -918,8 +1029,23 @@ main (int argc, char **argv) fprintf (stderr, "prun: [" CONTEXT_REG_FMT "," CONTEXT_REG_FMT "] Running '%s'\n", low_pc, high_pc, argv[optind]); - - run_program (argv[optind]); + { + /* CreateProcess (called below with lpApplicationName == NULL) is + documented to modify the lpCommandLine buffer in place. argv[optind] + points into our own argv, so passing it directly lets CreateProcess + scribble on it; this was observed on aarch64-cygwin as the command + line coming back mangled (e.g. 'test_hello.exe' -> 'st_hello.exxee') + on later use. Pass a private writable copy instead. It is not freed + because run_program() stores it in dll_info[0].name, which is read + later when printing the DLL-profile table. */ + 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 +1061,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