From 874107553f519eca6cf6fdef4824e24fe0efaa40 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Thu, 13 Aug 2026 09:38:00 -0700 Subject: [PATCH 1/6] Cygwin: configure winsup for AArch64 Allow the accepted aarch64 target through winsup configury and define its Automake conditional. Add configure-only CI coverage for both the existing x86_64 target and AArch64.\n\nThe winsup change backports cygwin/cygwin@2138e8040baf931b666458396df9d3d153d92b39.\n\nCo-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build.yaml | 30 +++++++++++++++++++++++++++++- winsup/configure.ac | 2 ++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 4e219c3cea..c943fb230b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,6 +6,35 @@ permissions: contents: read jobs: + configure-winsup: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + target: + - x86_64-pc-cygwin + - aarch64-pc-cygwin + + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Generate winsup configure + working-directory: winsup + run: ./autogen.sh + + - name: Configure winsup for ${{ matrix.target }} + env: + ac_cv_lib_sframe_sframe_decode: "no" + ac_cv_lib_zstd_ZSTD_isError: "no" + run: | + mkdir build-winsup + cd build-winsup + ../winsup/configure \ + --target=${{ matrix.target }} \ + --with-cross-bootstrap \ + --disable-doc + build: runs-on: windows-latest @@ -92,4 +121,3 @@ jobs: - name: Run tests uses: msys2/msys2-tests@main - diff --git a/winsup/configure.ac b/winsup/configure.ac index 4dd5ccb9f9..cae3695ce0 100644 --- a/winsup/configure.ac +++ b/winsup/configure.ac @@ -103,12 +103,14 @@ DLL_ENTRY="dll_entry" case "$target_cpu" in x86_64) ;; + aarch64) ;; *) AC_MSG_ERROR([Invalid target processor "$target_cpu"]) ;; esac AC_SUBST(DLL_ENTRY) AM_CONDITIONAL(TARGET_X86_64, [test $target_cpu = "x86_64"]) +AM_CONDITIONAL(TARGET_AARCH64, [test $target_cpu = "aarch64"]) AC_ARG_ENABLE(doc, [AS_HELP_STRING([--disable-doc], [do not build documentation])],, From e75381137ff8cd6121a9b5f9d20c994fc22d0fa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Radek=20Barto=C5=88?= Date: Thu, 13 Aug 2026 10:05:49 -0700 Subject: [PATCH 2/6] Cygwin: make register context access portable Backport accepted Cygwin commit aeea9ae363f0f73af5eb393fcc6fee2316c31f21 for AArch64 CONTEXT and mcontext register mappings. Resolve the later pthread cancellation stack-alignment change to match current upstream: preserve the x86_64 path and align AArch64 SP to 16 bytes. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.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 | 14 +++-- winsup/utils/profiler.cc | 7 +-- 6 files changed, 143 insertions(+), 38 deletions(-) create mode 100644 winsup/cygwin/local_includes/register.h diff --git a/winsup/cygwin/exceptions.cc b/winsup/cygwin/exceptions.cc index ca86b36438..7af9f18aea 100644 --- a/winsup/cygwin/exceptions.cc +++ b/winsup/cygwin/exceptions.cc @@ -28,26 +28,10 @@ details. */ #include "ntdll.h" #include "exception.h" #include "posix_timer.h" +#include "register.h" #include "gcc_seh.h" #include "cygwin/exit_process.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 @@ -231,7 +215,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 @@ -251,6 +235,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 @@ -1814,11 +1823,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 de728bedec..4e9eafba79 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 0000000000..1ddfe2ec0b --- /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 30b37244af..9578ab1dfe 100644 --- a/winsup/cygwin/profil.c +++ b/winsup/cygwin/profil.c @@ -21,6 +21,7 @@ #include #include #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 d410b95756..eca8c7124a 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 @@ -643,13 +644,18 @@ pthread::cancel () an additional 8 bytes is required to emulate this behaviour. However, we do not need to push return address itself, because pthread::static_cancel_self() must not return. */ - context.Rsp &= ~0x07UL; - if ((context.Rsp & 8) == 0) - context.Rsp -= 8; - context.Rip = (ULONG_PTR) pthread::static_cancel_self; + context._CX_stackPtr &= ~0x07UL; + if ((context._CX_stackPtr & 8) == 0) + context._CX_stackPtr -= 8; +#elif defined(__aarch64__) + /* 16 bytes alignment required. Trim stack pointer just in case. + https://learn.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions?view=msvc-170 + */ + context._CX_stackPtr &= ~0x0fUL; #else #error unimplemented for this target #endif + 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 b5ce16cf22..018ab3420e 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 From b5bdb32ad7acee9fcf685e081afba06364c86bc0 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Thu, 13 Aug 2026 10:08:16 -0700 Subject: [PATCH 3/6] CI: compile AArch64 register context Compile the Cygwin mcontext layout and portable register aliases with the published aarch64-pc-cygwin toolchain. Temporarily probe the subsequent full-build failure so the next blocker can be identified precisely. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/checks/aarch64-register-context.cc | 41 +++++++++++++++ .github/workflows/build.yaml | 60 ++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 .github/checks/aarch64-register-context.cc diff --git a/.github/checks/aarch64-register-context.cc b/.github/checks/aarch64-register-context.cc new file mode 100644 index 0000000000..eccc635352 --- /dev/null +++ b/.github/checks/aarch64-register-context.cc @@ -0,0 +1,41 @@ +#include +#include +#include +#include "register.h" + +#if !defined(__aarch64__) +#error This check must be compiled for AArch64 +#endif + +#define CHECK_CONTEXT_OFFSET(cygwin_name, windows_name) \ + static_assert (offsetof (__mcontext, cygwin_name) \ + == offsetof (CONTEXT, windows_name)) + +CHECK_CONTEXT_OFFSET (ctxflags, ContextFlags); +CHECK_CONTEXT_OFFSET (cpsr, Cpsr); +CHECK_CONTEXT_OFFSET (x0, X0); +CHECK_CONTEXT_OFFSET (x19, X19); +CHECK_CONTEXT_OFFSET (fp, Fp); +CHECK_CONTEXT_OFFSET (lr, Lr); +CHECK_CONTEXT_OFFSET (sp, Sp); +CHECK_CONTEXT_OFFSET (pc, Pc); +CHECK_CONTEXT_OFFSET (v, V); +CHECK_CONTEXT_OFFSET (fpcr, Fpcr); +CHECK_CONTEXT_OFFSET (fpsr, Fpsr); +CHECK_CONTEXT_OFFSET (bcr, Bcr); +CHECK_CONTEXT_OFFSET (bvr, Bvr); +CHECK_CONTEXT_OFFSET (wcr, Wcr); +CHECK_CONTEXT_OFFSET (wvr, Wvr); + +static_assert (alignof (__mcontext) == alignof (CONTEXT)); +static_assert (offsetof (__mcontext, oldmask) == sizeof (CONTEXT)); +static_assert (sizeof (__mcontext) == sizeof (CONTEXT) + 16); + +void +check_register_aliases (CONTEXT &context, __mcontext &mcontext) +{ + context._CX_instPtr = mcontext._MC_instPtr; + context._CX_stackPtr = mcontext._MC_stackPtr; + context._CX_framePtr = mcontext._MC_uclinkReg; + mcontext._MC_retReg = context._CX_instPtr; +} diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index c943fb230b..fefd3cf5c4 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -6,6 +6,66 @@ permissions: contents: read jobs: + aarch64-register-context: + runs-on: ubuntu-latest + env: + TOOLCHAIN_DIR: ${{ github.workspace }}/aarch64-pc-cygwin-toolchain + + steps: + - name: Checkout code + uses: actions/checkout@v7 + + - name: Install build tools + run: | + sudo apt-get update + sudo apt-get install --yes autoconf automake gawk make + + - name: Download AArch64 Cygwin toolchain + run: | + curl --fail --location --retry 3 \ + --output aarch64-pc-cygwin-toolchain.tar.gz \ + https://github.com/Windows-on-ARM-Experiments/mingw-woarm64-build/releases/download/2025-07-15/aarch64-pc-cygwin-msvcrt-toolchain.tar.gz + echo "1ec7c5db7d6f2b9f8c9e9f631b05a5de27b83b98297b3a2043172f569820d1f3 aarch64-pc-cygwin-toolchain.tar.gz" \ + | sha256sum --check + mkdir -p "$TOOLCHAIN_DIR" + tar -xzf aarch64-pc-cygwin-toolchain.tar.gz -C "$TOOLCHAIN_DIR" + + - name: Compile AArch64 register context check + run: | + "$TOOLCHAIN_DIR/bin/aarch64-pc-cygwin-g++" \ + -std=gnu++17 \ + -Wall \ + -Werror \ + -fsyntax-only \ + -I"$GITHUB_WORKSPACE/winsup/cygwin/include" \ + -I"$GITHUB_WORKSPACE/winsup/cygwin/local_includes" \ + -I"$TOOLCHAIN_DIR/include" \ + "$GITHUB_WORKSPACE/.github/checks/aarch64-register-context.cc" + + - name: Probe the next AArch64 build blocker + continue-on-error: true + env: + CXXFLAGS_FOR_TARGET: -I${{ github.workspace }}/aarch64-pc-cygwin-toolchain/include + LDFLAGS_FOR_TARGET: -L${{ github.workspace }}/aarch64-pc-cygwin-toolchain/lib + PATH: ${{ github.workspace }}/aarch64-pc-cygwin-toolchain/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin + run: | + (cd winsup && ./autogen.sh) + mkdir build-aarch64 + cd build-aarch64 + ../configure \ + --target=aarch64-pc-cygwin \ + --disable-doc \ + --disable-dumper \ + --with-cross-bootstrap + make V=1 -j2 2>&1 | tee aarch64-build.log + + - name: Upload AArch64 build probe + if: always() + uses: actions/upload-artifact@v7 + with: + name: aarch64-build-probe + path: build-aarch64/aarch64-build.log + configure-winsup: runs-on: ubuntu-latest strategy: From 0752608ae4175a871ec61e380677313b57d13d1a Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Thu, 13 Aug 2026 10:20:17 -0700 Subject: [PATCH 4/6] CI: run Cygwin cross compiler on ARM64 The published aarch64-pc-cygwin toolchain contains ARM64-hosted binaries, so execute the register-context check on GitHub's ARM64 Ubuntu runner. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index fefd3cf5c4..02cb2b21ae 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -7,7 +7,7 @@ permissions: jobs: aarch64-register-context: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm env: TOOLCHAIN_DIR: ${{ github.workspace }}/aarch64-pc-cygwin-toolchain From 2d1a02459adc49224628d5ea85afc3a6af89b9fc Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Thu, 13 Aug 2026 10:31:56 -0700 Subject: [PATCH 5/6] CI: include Cygwin signal prerequisites Compile the internal cygwin/signal.h with the same foundational types supplied by the public signal header. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/checks/aarch64-register-context.cc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/checks/aarch64-register-context.cc b/.github/checks/aarch64-register-context.cc index eccc635352..7f01458376 100644 --- a/.github/checks/aarch64-register-context.cc +++ b/.github/checks/aarch64-register-context.cc @@ -1,4 +1,9 @@ #include +#include +#include +#include +#include +#include #include #include #include "register.h" From ee50e02239f3a10f5a5d7321c2ef9a40a756f2e0 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Thu, 13 Aug 2026 10:44:59 -0700 Subject: [PATCH 6/6] CI: keep AArch64 context check focused Remove the one-time full-build probe now that it identified the next blocker. Keep the digest-pinned AArch64 compile check as the permanent regression test. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- .github/workflows/build.yaml | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 02cb2b21ae..4864925531 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -15,11 +15,6 @@ jobs: - name: Checkout code uses: actions/checkout@v7 - - name: Install build tools - run: | - sudo apt-get update - sudo apt-get install --yes autoconf automake gawk make - - name: Download AArch64 Cygwin toolchain run: | curl --fail --location --retry 3 \ @@ -42,30 +37,6 @@ jobs: -I"$TOOLCHAIN_DIR/include" \ "$GITHUB_WORKSPACE/.github/checks/aarch64-register-context.cc" - - name: Probe the next AArch64 build blocker - continue-on-error: true - env: - CXXFLAGS_FOR_TARGET: -I${{ github.workspace }}/aarch64-pc-cygwin-toolchain/include - LDFLAGS_FOR_TARGET: -L${{ github.workspace }}/aarch64-pc-cygwin-toolchain/lib - PATH: ${{ github.workspace }}/aarch64-pc-cygwin-toolchain/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin - run: | - (cd winsup && ./autogen.sh) - mkdir build-aarch64 - cd build-aarch64 - ../configure \ - --target=aarch64-pc-cygwin \ - --disable-doc \ - --disable-dumper \ - --with-cross-bootstrap - make V=1 -j2 2>&1 | tee aarch64-build.log - - - name: Upload AArch64 build probe - if: always() - uses: actions/upload-artifact@v7 - with: - name: aarch64-build-probe - path: build-aarch64/aarch64-build.log - configure-winsup: runs-on: ubuntu-latest strategy: