]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gdbserver: Add aarch64-windows support
authorHannes Domani <ssbssa@yahoo.de>
Mon, 26 Jan 2026 18:34:16 +0000 (19:34 +0100)
committerHannes Domani <ssbssa@yahoo.de>
Mon, 26 Jan 2026 18:34:16 +0000 (19:34 +0100)
Approved-By: Tom Tromey <tom@tromey.com>
gdbserver/configure.srv
gdbserver/win32-aarch64-low.cc [new file with mode: 0644]
gdbserver/win32-low.cc

index 7de03929bc287942a592bd3c68222560518b7c72..7bdd92e3f8280c6b8afc5fc63e029b4260951c26 100644 (file)
@@ -61,6 +61,12 @@ case "${gdbserver_host}" in
                        ipa_obj="${ipa_obj} linux-aarch64-tdesc-ipa.o"
                        ipa_obj="${ipa_obj} arch/aarch64-ipa.o"
                        ;;
+  aarch64-*-mingw*)    srv_regobj=""
+                       srv_tgtobj="arch/aarch64.o nat/aarch64-hw-point.o"
+                       srv_tgtobj="${srv_tgtobj} win32-low.o win32-aarch64-low.o"
+                       srv_tgtobj="${srv_tgtobj} nat/windows-nat.o"
+                       srv_mingw=yes
+                       ;;
   aarch64*-*-netbsd*)  srv_regobj=""
                        srv_tgtobj="netbsd-low.o netbsd-aarch64-low.o fork-child.o"
                        srv_tgtobj="${srv_tgtobj} nat/fork-inferior.o"
diff --git a/gdbserver/win32-aarch64-low.cc b/gdbserver/win32-aarch64-low.cc
new file mode 100644 (file)
index 0000000..ac8c117
--- /dev/null
@@ -0,0 +1,455 @@
+/* Copyright (C) 2025-2026 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "win32-low.h"
+#include "arch/aarch64.h"
+#include "nat/aarch64-hw-point.h"
+#include "tdesc.h"
+
+using namespace windows_nat;
+
+static struct aarch64_debug_reg_state debug_reg_state;
+
+/* The inferior's target description.  This is a global because the
+   Windows ports support neither bi-arch nor multi-process.  */
+static const_target_desc_up aarch64_tdesc;
+
+static void
+update_debug_registers (thread_info *thread)
+{
+  auto th = static_cast<windows_thread_info *> (thread->target_data ());
+
+  /* The actual update is done later just before resuming the lwp,
+     we just mark that the registers need updating.  */
+  th->debug_registers_changed = true;
+}
+
+void
+aarch64_notify_debug_reg_change (ptid_t ptid,
+                                int is_watchpoint, unsigned int idx)
+{
+  /* Only update the threads of this process.  */
+  current_process ()->for_each_thread (update_debug_registers);
+}
+
+/* Breakpoint/watchpoint support.  */
+
+static int
+aarch64_supports_z_point_type (char z_type)
+{
+  switch (z_type)
+    {
+    case Z_PACKET_HW_BP:
+    case Z_PACKET_WRITE_WP:
+    case Z_PACKET_ACCESS_WP:
+      return 1;
+    default:
+      return 0;
+    }
+}
+
+static int
+aarch64_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+                     int len, struct raw_breakpoint *bp)
+{
+  int ret;
+  enum target_hw_bp_type targ_type;
+
+  /* Determine the type from the raw breakpoint type.  */
+  targ_type = raw_bkpt_type_to_target_hw_bp_type (type);
+
+  if (targ_type != hw_execute)
+    {
+      if (aarch64_region_ok_for_watchpoint (addr, len))
+       ret = aarch64_handle_watchpoint (targ_type, addr, len,
+                                        1 /* is_insert */, current_thread->id,
+                                        &debug_reg_state);
+      else
+       ret = -1;
+    }
+  else
+    {
+      if (len == 3)
+       {
+         /* LEN is 3 means the breakpoint is set on a 32-bit thumb
+            instruction.   Set it to 2 to correctly encode length bit
+            mask in hardware/watchpoint control register.  */
+         len = 2;
+       }
+      ret = aarch64_handle_breakpoint (targ_type, addr, len,
+                                      1 /* is_insert */, current_thread->id,
+                                      &debug_reg_state);
+    }
+
+  return ret;
+}
+
+static int
+aarch64_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+                     int len, struct raw_breakpoint *bp)
+{
+  int ret;
+  enum target_hw_bp_type targ_type;
+
+  /* Determine the type from the raw breakpoint type.  */
+  targ_type = raw_bkpt_type_to_target_hw_bp_type (type);
+
+  /* Set up state pointers.  */
+  if (targ_type != hw_execute)
+    ret =
+      aarch64_handle_watchpoint (targ_type, addr, len, 0 /* is_insert */,
+                                current_thread->id, &debug_reg_state);
+  else
+    {
+      if (len == 3)
+       {
+         /* LEN is 3 means the breakpoint is set on a 32-bit thumb
+            instruction.   Set it to 2 to correctly encode length bit
+            mask in hardware/watchpoint control register.  */
+         len = 2;
+       }
+      ret = aarch64_handle_breakpoint (targ_type, addr, len,
+                                      0 /* is_insert */,  current_thread->id,
+                                      &debug_reg_state);
+    }
+
+  return ret;
+}
+
+static std::vector<CORE_ADDR>
+aarch64_stopped_data_addresses ()
+{
+  if (windows_process.siginfo_er.ExceptionCode != EXCEPTION_BREAKPOINT ||
+      windows_process.siginfo_er.NumberParameters != 2)
+    return {};
+
+  const CORE_ADDR addr_trap
+    = (CORE_ADDR) windows_process.siginfo_er.ExceptionInformation[1];
+
+  return aarch64_stopped_data_addresses (&debug_reg_state, addr_trap);
+}
+
+static int
+aarch64_stopped_by_watchpoint ()
+{
+  return !aarch64_stopped_data_addresses ().empty ();
+}
+
+/* Implement win32_target_ops "initial_stuff" method.  */
+
+static void
+aarch64_initial_stuff (process_info *proc)
+{
+  proc->tdesc = aarch64_tdesc.get ();
+
+  memset (&debug_reg_state, 0, sizeof (debug_reg_state));
+}
+
+/* Implement win32_target_ops "get_thread_context" method.  */
+
+static void
+aarch64_get_thread_context (windows_thread_info *th)
+{
+  CONTEXT *context = &th->context;
+
+  context->ContextFlags = (WindowsContext<decltype(context)>::full
+                          | WindowsContext<decltype(context)>::floating
+                          | WindowsContext<decltype(context)>::debug
+                          | WindowsContext<decltype(context)>::extended);
+
+  BOOL ret = get_thread_context (th->h, context);
+  if (!ret)
+    {
+      DWORD e = GetLastError ();
+      error ("GetThreadContext failure %ld\n", (long) e);
+    }
+}
+
+/* Implement win32_target_ops "prepare_to_resume" method.  */
+
+static void
+aarch64_prepare_to_resume (windows_thread_info *th)
+{
+  if (th->debug_registers_changed)
+    {
+      win32_require_context (th);
+
+      CONTEXT *context = &th->context;
+
+      for (int i = 0; i < aarch64_num_bp_regs; i++)
+       {
+         context->Bvr[i] = debug_reg_state.dr_addr_bp[i];
+         context->Bcr[i] = debug_reg_state.dr_ctrl_bp[i];
+       }
+      for (int i = 0; i < aarch64_num_wp_regs; i++)
+       {
+         context->Wvr[i] = debug_reg_state.dr_addr_wp[i];
+         context->Wcr[i] = debug_reg_state.dr_ctrl_wp[i];
+       }
+
+      th->debug_registers_changed = false;
+    }
+}
+
+/* Implement win32_target_ops "thread_added" method.  */
+
+static void
+aarch64_thread_added (windows_thread_info *th)
+{
+  th->debug_registers_changed = true;
+}
+
+/* Implement win32_target_ops "single_step" method.  */
+
+static void
+aarch64_single_step (windows_thread_info *th)
+{
+  th->context.Cpsr |= 0x200000;
+}
+
+/* An array of offset mappings into a Win32 Context structure.
+   This is a one-to-one mapping which is indexed by gdb's register
+   numbers.  It retrieves an offset into the context structure where
+   the 4 byte register is located.
+   An offset value of -1 indicates that Win32 does not provide this
+   register in it's CONTEXT structure.  In this case regptr will return
+   a pointer into a dummy register.  */
+#define context_offset(x) (offsetof (CONTEXT, x))
+static const int aarch64_mappings[] = {
+  context_offset (X0),
+  context_offset (X1),
+  context_offset (X2),
+  context_offset (X3),
+  context_offset (X4),
+  context_offset (X5),
+  context_offset (X6),
+  context_offset (X7),
+  context_offset (X8),
+  context_offset (X9),
+  context_offset (X10),
+  context_offset (X11),
+  context_offset (X12),
+  context_offset (X13),
+  context_offset (X14),
+  context_offset (X15),
+  context_offset (X16),
+  context_offset (X17),
+  context_offset (X18),
+  context_offset (X19),
+  context_offset (X20),
+  context_offset (X21),
+  context_offset (X22),
+  context_offset (X23),
+  context_offset (X24),
+  context_offset (X25),
+  context_offset (X26),
+  context_offset (X27),
+  context_offset (X28),
+  context_offset (Fp),
+  context_offset (Lr),
+  context_offset (Sp),
+  context_offset (Pc),
+  context_offset (Cpsr),
+  context_offset (V[0]),
+  context_offset (V[1]),
+  context_offset (V[2]),
+  context_offset (V[3]),
+  context_offset (V[4]),
+  context_offset (V[5]),
+  context_offset (V[6]),
+  context_offset (V[7]),
+  context_offset (V[8]),
+  context_offset (V[9]),
+  context_offset (V[10]),
+  context_offset (V[11]),
+  context_offset (V[12]),
+  context_offset (V[13]),
+  context_offset (V[14]),
+  context_offset (V[15]),
+  context_offset (V[16]),
+  context_offset (V[17]),
+  context_offset (V[18]),
+  context_offset (V[19]),
+  context_offset (V[20]),
+  context_offset (V[21]),
+  context_offset (V[22]),
+  context_offset (V[23]),
+  context_offset (V[24]),
+  context_offset (V[25]),
+  context_offset (V[26]),
+  context_offset (V[27]),
+  context_offset (V[28]),
+  context_offset (V[29]),
+  context_offset (V[30]),
+  context_offset (V[31]),
+  context_offset (Fpsr),
+  context_offset (Fpcr),
+};
+#undef context_offset
+
+static inline void
+get_mappings (const int *&mappings, int &mappings_count)
+{
+  mappings = aarch64_mappings;
+  mappings_count = sizeof (aarch64_mappings) / sizeof (aarch64_mappings[0]);
+}
+
+/* Fetch register from gdbserver regcache data.  */
+static void
+aarch64_fetch_inferior_register (struct regcache *regcache,
+                                windows_thread_info *th, int r)
+{
+  const int *mappings;
+  int mappings_count;
+  get_mappings (mappings, mappings_count);
+
+  char *context_ptr = (char *) &th->context;
+  char *context_offset;
+  if (r < mappings_count)
+    context_offset = context_ptr + mappings[r];
+  else
+    gdb_assert_not_reached ("invalid register number %d", r);
+
+  supply_register (regcache, r, context_offset);
+}
+
+/* Store a new register value into the thread context of TH.  */
+static void
+aarch64_store_inferior_register (struct regcache *regcache,
+                                windows_thread_info *th, int r)
+{
+  const int *mappings;
+  int mappings_count;
+  get_mappings (mappings, mappings_count);
+
+  char *context_ptr = (char *) &th->context;
+  char *context_offset;
+  if (r < mappings_count)
+    context_offset = context_ptr + mappings[r];
+  else
+    gdb_assert_not_reached ("invalid register number %d", r);
+
+  collect_register (regcache, r, context_offset);
+}
+
+/* Windows uses the various BRK instruction variants for special operations,
+   and BRK #0xf000 triggers a breakpoint exception in the debugger.  */
+static const unsigned char aarch64_breakpoint[] = {0x00, 0x00, 0x3e, 0xd4};
+#define aarch64_breakpoint_len 4
+
+/* Implement win32_target_ops "arch_setup" method.  */
+
+static void
+aarch64_arch_setup ()
+{
+  target_desc_up tdesc;
+
+  /* Get ID_AA64DFR0_EL1 value (CP 4028) from registry.  */
+  aarch64_num_bp_regs = 0;
+  uint64_t cp4028;
+  DWORD cp4028_size = sizeof(cp4028);
+  if (RegGetValueA (HKEY_LOCAL_MACHINE,
+                   "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0",
+                   "CP 4028", RRF_RT_REG_QWORD, NULL, &cp4028, &cp4028_size)
+      == ERROR_SUCCESS)
+    {
+      /* Bits 12-15 are the number of breakpoints, minus 1.  */
+      aarch64_num_bp_regs = ((cp4028 & 0xf000) >> 12) + 1;
+      if (aarch64_num_bp_regs > ARM64_MAX_BREAKPOINTS)
+       aarch64_num_bp_regs = ARM64_MAX_BREAKPOINTS;
+    }
+
+  /* ARM64_MAX_WATCHPOINTS is 2, but only 1 works.  */
+  aarch64_num_wp_regs = 1;
+
+  tdesc = aarch64_create_target_description ({});
+
+  std::vector<const char *> expedited_registers;
+  expedited_registers.push_back ("x29");
+  expedited_registers.push_back ("sp");
+  expedited_registers.push_back ("pc");
+  expedited_registers.push_back (nullptr);
+
+  init_target_desc (tdesc.get (), (const char **) expedited_registers.data (),
+                   WINDOWS_OSABI);
+  aarch64_tdesc = std::move (tdesc);
+}
+
+/* Implement win32_target_ops "num_regs" method.  */
+
+static int
+aarch64_win32_num_regs ()
+{
+  int num_regs = sizeof (aarch64_mappings) / sizeof (aarch64_mappings[0]);
+  return num_regs;
+}
+
+/* Implement win32_target_ops "get_pc" method.  */
+
+static CORE_ADDR
+aarch64_win32_get_pc (struct regcache *regcache)
+{
+  uint64_t pc;
+
+  collect_register_by_name (regcache, "pc", &pc);
+  return (CORE_ADDR) pc;
+}
+
+/* Implement win32_target_ops "set_pc" method.  */
+
+static void
+aarch64_win32_set_pc (struct regcache *regcache, CORE_ADDR pc)
+{
+  uint64_t newpc = pc;
+
+  supply_register_by_name (regcache, "pc", &newpc);
+}
+
+/* Implement win32_target_ops "is_sw_breakpoint" method.  */
+
+static bool
+aarch64_is_sw_breakpoint (const EXCEPTION_RECORD *er)
+{
+  /* On aarch64, hardware breakpoints also get EXCEPTION_BREAKPOINT,
+     but they can be recognized with ExceptionInformation.  */
+  return (er->ExceptionCode == EXCEPTION_BREAKPOINT
+         && er->NumberParameters == 1
+         && er->ExceptionInformation[0] == 0);
+}
+
+struct win32_target_ops the_low_target = {
+  aarch64_arch_setup,
+  aarch64_win32_num_regs,
+  aarch64_initial_stuff,
+  aarch64_get_thread_context,
+  aarch64_prepare_to_resume,
+  aarch64_thread_added,
+  aarch64_fetch_inferior_register,
+  aarch64_store_inferior_register,
+  aarch64_single_step,
+  aarch64_breakpoint,
+  aarch64_breakpoint_len,
+  aarch64_breakpoint_len,
+  aarch64_win32_get_pc,
+  aarch64_win32_set_pc,
+  aarch64_supports_z_point_type,
+  aarch64_insert_point,
+  aarch64_remove_point,
+  aarch64_stopped_by_watchpoint,
+  aarch64_stopped_data_addresses,
+  aarch64_is_sw_breakpoint,
+};
index ceee69db22303fcf77b04a8fe49757ca2aee16a5..b05f6af6b56b7ab41b6abc9d1bb48f1bdceed329 100644 (file)
@@ -909,6 +909,13 @@ fake_breakpoint_event (void)
   windows_process.current_event.u.Exception.ExceptionRecord.ExceptionCode
     = EXCEPTION_BREAKPOINT;
 
+  /* On aarch64, hardware breakpoints also get EXCEPTION_BREAKPOINT,
+     but they can be recognized with ExceptionInformation.  */
+  windows_process.current_event.u.Exception.ExceptionRecord.NumberParameters
+    = 1;
+  windows_process.current_event.u.Exception.ExceptionRecord
+    .ExceptionInformation[0] = 0;
+
   for_each_thread (suspend_one_thread);
 }