[PATCH v2] Cygwin: ssp: Add AArch64 implementation
Chandru Kumaresan <[email protected]> Mon, 8 Jun 2026 06:08:34 +0000
| Newsgroups | gmane.os.cygwin.patches |
|---|---|
| Message-ID | <PN0P287MB0295F1D93B25FA3B90293E35921C2@PN0P287MB0295.INDP287.PROD.OUTLOOK.COM> |
Thanks for the review!
> did you consider writing something like (untested): [SW_BREAKPOINT_SIZE refactor]
Yes, adopted in v2. I introduced SW_BREAKPOINT_SIZE as a macro (1 for x86/x86_64, 4
for aarch64, #error otherwise), unified the PendingBreakpoints struct to use
real_insn[SW_BREAKPOINT_SIZE] throughout, and renamed the variable to brk_insn on both
architectures. This eliminates the per-arch #ifdef blocks in add_breakpoint and remove_breakpoint.
> Hmmm... this is probably just generically right, I guess.
> (re: only setting running=0 on !dwFirstChance for aarch64)
On Windows/AArch64, the single-step mechanism (PSTATE.SS) triggers a first-chance STATUS_SINGLE_STEP
exception for every instruction we step through. If we set running=0 on first-chance exceptions,
the profiler would exit on the very first step. Limiting it to second-chance (i.e. unhandled) exceptions
means we only stop on genuine faults, matching the intent of the original x86 code.
All other issues (whitespace regression, spurious fprintf indent, missing cmdline_copy comment) are fixed in v2 as well.
Thanks and regards
K Chandru
Inline patch
---
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; i++)
{
if (pending_breakpoints[i].address == address)
@@ -142,12 +168,11 @@ add_breakpoint (CONTEXT_REG address)
pending_breakpoints[i].address = address;
ReadProcessMemory (hProcess,
(void *)address,
- &(pending_breakpoints[i].real_byte),
- 1, &rv);
-
+ pending_breakpoints[i].real_insn,
+ SW_BREAKPOINT_SIZE, &rv);
WriteProcessMemory (hProcess,
(void *)address,
- (LPVOID)int3, 1, &rv);
+ (LPVOID)brk_insn, SW_BREAKPOINT_SIZE, &rv);
if (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<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 +297,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 +509,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 +527,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)
@@ -493,13 +561,27 @@ run_program (char *cmdline)
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;
@@ -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
Cygwin-ssp-Add-AArch64-implementation.patch
(application/octet-stream, 11 KB)
From e55dec2bd7fb60c4b552a26896db4e9e8a598fad Mon Sep 17 00:00:00 2001 From: chandru-mcw <[email protected]> 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Ċ <[email protected]> Signed-off-by: Thirumalai Nagalingam <[email protected]> Signed-off-by: Chandru Kumaresan <[email protected]> --- 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; i++) { if (pending_breakpoints[i].address == address) @@ -142,12 +168,11 @@ add_breakpoint (CONTEXT_REG address) pending_breakpoints[i].address = address; ReadProcessMemory (hProcess, (void *)address, - &(pending_breakpoints[i].real_byte), - 1, &rv); - + pending_breakpoints[i].real_insn, + SW_BREAKPOINT_SIZE, &rv); WriteProcessMemory (hProcess, (void *)address, - (LPVOID)int3, 1, &rv); + (LPVOID)brk_insn, SW_BREAKPOINT_SIZE, &rv); if (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<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 +297,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 +509,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 +527,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) @@ -493,13 +561,27 @@ run_program (char *cmdline) 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; @@ -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