Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .github/checks/aarch64-register-context.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/_pthreadtypes.h>
#include <time.h>
#include <windows.h>
#include <cygwin/signal.h>
#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;
}
61 changes: 60 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,66 @@ permissions:
contents: read

jobs:
aarch64-register-context:
runs-on: ubuntu-24.04-arm
env:
TOOLCHAIN_DIR: ${{ github.workspace }}/aarch64-pc-cygwin-toolchain

steps:
- name: Checkout code
uses: actions/checkout@v7

- 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"

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

Expand Down Expand Up @@ -92,4 +152,3 @@ jobs:

- name: Run tests
uses: msys2/msys2-tests@main

2 changes: 2 additions & 0 deletions winsup/configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -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])],,
Expand Down
51 changes: 28 additions & 23 deletions winsup/cygwin/exceptions.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 ();
}
}

Expand Down
77 changes: 76 additions & 1 deletion winsup/cygwin/include/cygwin/signal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions winsup/cygwin/local_includes/register.h
Original file line number Diff line number Diff line change
@@ -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
7 changes: 2 additions & 5 deletions winsup/cygwin/profil.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <errno.h>
#include <pthread.h>
#include "profil.h"
#include "register.h"

#define SLEEPTIME (1000 / PROF_HZ)

Expand All @@ -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;
Expand Down
14 changes: 10 additions & 4 deletions winsup/cygwin/thread.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
Loading
Loading