public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support
@ 2022-03-16 20:19 John Baldwin
  2022-03-16 20:19 ` [PATCH v2 01/12] Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs support John Baldwin
                   ` (12 more replies)
  0 siblings, 13 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

Changes since V1:

- The unordered_map<>'s in x86-nat.c and aarch64-nat.c both now store
  objects directly rather than pointers to objects.

- Trimmed "Contributed by" notices from new files.

- I have compiled and (very lightly) tested this on Linux Aarch64.
  By light testing I mean that I ran a test program with a harware
  breakpoint set on main and it stopped correctly.  I haven't run a
  full test suite as my Aarch64 test box is a lowly Raspberry Pi
  for which such a run would take a fairly long time.

I still have some open questions about Patch 6 from the first
version:

Patch 6 has an open question about how best to handle having a
platform-specific hook for when debug registers have been changed.
Right now we require the platform to supply the function that
nat/aarch64-hw-point.c calls.  I did not choose to create an
equivalent to x86_dr_low, but perhaps that sort of structure, or at
least a function pointer should be used instead?

There is also some messiness around the Linux-specific
kernel_supports_any_contiguous_range workaround in patch 6.

OTOH, some of the FreeBSD/x86 cleanups in the first half of the series
(such as adding x86-fbsd-nat.*) might be nice to reuse in my XSAVE
series, so if that half of the series is ok (first 5 patches), it
might be nice to push that in sooner.

John Baldwin (12):
  Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs
    support.
  x86-nat: Use an unordered_map to store per-pid debug reg state.
  x86-nat: Add x86_lookup_debug_reg_state.
  Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets.
  fbsd-nat: Add a low_new_fork virtual method.
  x86-fbsd-nat: Copy debug register state on fork.
  nat: Split out platform-independent aarch64 debug register support.
  aarch64: Add an aarch64_nat_target mixin class.
  fbsd-nat: Add helper routine to fetch siginfo_t for a ptid.
  fbsd-nat: Add a low_delete_thread virtual method.
  fbsd-nat: Add a low_prepare_to_resume virtual method.
  Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64.

 gdb/NEWS                         |   2 +
 gdb/aarch64-fbsd-nat.c           | 260 ++++++++++++-
 gdb/aarch64-linux-nat.c          | 352 +----------------
 gdb/aarch64-nat.c                | 302 +++++++++++++++
 gdb/aarch64-nat.h                | 109 ++++++
 gdb/amd64-fbsd-nat.c             |  20 +-
 gdb/configure.nat                |  12 +-
 gdb/fbsd-nat.c                   |  28 +-
 gdb/fbsd-nat.h                   |  18 +
 gdb/i386-fbsd-nat.c              |  20 +-
 gdb/nat/aarch64-hw-point.c       | 624 +++++++++++++++++++++++++++++++
 gdb/nat/aarch64-hw-point.h       | 126 +++++++
 gdb/nat/aarch64-linux-hw-point.c | 605 +-----------------------------
 gdb/nat/aarch64-linux-hw-point.h | 105 +-----
 gdb/nat/aarch64-linux.c          |   4 +-
 gdb/x86-fbsd-nat.c               |  45 +++
 gdb/x86-fbsd-nat.h               |  36 ++
 gdb/x86-nat.c                    |  92 +----
 gdb/x86-nat.h                    |   5 +
 gdbserver/configure.srv          |   1 +
 gdbserver/linux-aarch64-low.cc   |  13 +-
 21 files changed, 1612 insertions(+), 1167 deletions(-)
 create mode 100644 gdb/aarch64-nat.c
 create mode 100644 gdb/aarch64-nat.h
 create mode 100644 gdb/nat/aarch64-hw-point.c
 create mode 100644 gdb/nat/aarch64-hw-point.h
 create mode 100644 gdb/x86-fbsd-nat.c
 create mode 100644 gdb/x86-fbsd-nat.h

-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 01/12] Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs support.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-16 20:19 ` [PATCH v2 02/12] x86-nat: Use an unordered_map to store per-pid debug reg state John Baldwin
                   ` (11 subsequent siblings)
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

For BSD x86 targets, stopped_by_hw_breakpoint doesn't check siginfo_t
but inspects the DR6 register directly via PT_GETDBREGS.
---
 gdb/amd64-fbsd-nat.c | 4 ++--
 gdb/i386-fbsd-nat.c  | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/gdb/amd64-fbsd-nat.c b/gdb/amd64-fbsd-nat.c
index 98a1af03a66..368f4c10786 100644
--- a/gdb/amd64-fbsd-nat.c
+++ b/gdb/amd64-fbsd-nat.c
@@ -46,7 +46,7 @@ class amd64_fbsd_nat_target final
 
   const struct target_desc *read_description () override;
 
-#if defined(HAVE_PT_GETDBREGS) && defined(USE_SIGTRAP_SIGINFO)
+#if defined(HAVE_PT_GETDBREGS)
   bool supports_stopped_by_hw_breakpoint () override;
 #endif
 };
@@ -348,7 +348,7 @@ amd64_fbsd_nat_target::read_description ()
     return i386_target_description (X86_XSTATE_SSE_MASK, true);
 }
 
-#if defined(HAVE_PT_GETDBREGS) && defined(USE_SIGTRAP_SIGINFO)
+#if defined(HAVE_PT_GETDBREGS)
 /* Implement the supports_stopped_by_hw_breakpoints method.  */
 
 bool
diff --git a/gdb/i386-fbsd-nat.c b/gdb/i386-fbsd-nat.c
index a6ced66250c..023f24bab37 100644
--- a/gdb/i386-fbsd-nat.c
+++ b/gdb/i386-fbsd-nat.c
@@ -46,7 +46,7 @@ class i386_fbsd_nat_target final
 
   void resume (ptid_t, int, enum gdb_signal) override;
 
-#if defined(HAVE_PT_GETDBREGS) && defined(USE_SIGTRAP_SIGINFO)
+#if defined(HAVE_PT_GETDBREGS)
   bool supports_stopped_by_hw_breakpoint () override;
 #endif
 };
@@ -361,7 +361,7 @@ i386_fbsd_nat_target::read_description ()
   return i386_target_description (X86_XSTATE_X87_MASK, true);
 }
 
-#if defined(HAVE_PT_GETDBREGS) && defined(USE_SIGTRAP_SIGINFO)
+#if defined(HAVE_PT_GETDBREGS)
 /* Implement the supports_stopped_by_hw_breakpoints method.  */
 
 bool
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 02/12] x86-nat: Use an unordered_map to store per-pid debug reg state.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
  2022-03-16 20:19 ` [PATCH v2 01/12] Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs support John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-21 18:07   ` Pedro Alves
  2022-03-16 20:19 ` [PATCH v2 03/12] x86-nat: Add x86_lookup_debug_reg_state John Baldwin
                   ` (10 subsequent siblings)
  12 siblings, 1 reply; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This replaces a manual linked list which used O(n) lookup and
removal.
---
 gdb/x86-nat.c | 90 ++++++---------------------------------------------
 1 file changed, 10 insertions(+), 80 deletions(-)

diff --git a/gdb/x86-nat.c b/gdb/x86-nat.c
index d0d52a00265..c1e892bf564 100644
--- a/gdb/x86-nat.c
+++ b/gdb/x86-nat.c
@@ -22,6 +22,8 @@
 #include "gdbcmd.h"
 #include "inferior.h"
 
+#include <unordered_map>
+
 /* Support for hardware watchpoints and breakpoints using the x86
    debug registers.
 
@@ -36,75 +38,20 @@
 /* Low-level function vector.  */
 struct x86_dr_low_type x86_dr_low;
 
-/* Per-process data.  We don't bind this to a per-inferior registry
-   because of targets like x86 GNU/Linux that need to keep track of
-   processes that aren't bound to any inferior (e.g., fork children,
-   checkpoints).  */
+/* Hash table storing per-process data.  We don't bind this to a
+   per-inferior registry because of targets like x86 GNU/Linux that
+   need to keep track of processes that aren't bound to any inferior
+   (e.g., fork children, checkpoints).  */
 
-struct x86_process_info
-{
-  /* Linked list.  */
-  struct x86_process_info *next;
-
-  /* The process identifier.  */
-  pid_t pid;
-
-  /* Copy of x86 hardware debug registers.  */
-  struct x86_debug_reg_state state;
-};
-
-static struct x86_process_info *x86_process_list = NULL;
-
-/* Find process data for process PID.  */
-
-static struct x86_process_info *
-x86_find_process_pid (pid_t pid)
-{
-  struct x86_process_info *proc;
-
-  for (proc = x86_process_list; proc; proc = proc->next)
-    if (proc->pid == pid)
-      return proc;
-
-  return NULL;
-}
-
-/* Add process data for process PID.  Returns newly allocated info
-   object.  */
-
-static struct x86_process_info *
-x86_add_process (pid_t pid)
-{
-  struct x86_process_info *proc = XCNEW (struct x86_process_info);
-
-  proc->pid = pid;
-  proc->next = x86_process_list;
-  x86_process_list = proc;
-
-  return proc;
-}
-
-/* Get data specific info for process PID, creating it if necessary.
-   Never returns NULL.  */
-
-static struct x86_process_info *
-x86_process_info_get (pid_t pid)
-{
-  struct x86_process_info *proc;
-
-  proc = x86_find_process_pid (pid);
-  if (proc == NULL)
-    proc = x86_add_process (pid);
-
-  return proc;
-}
+static std::unordered_map<pid_t,
+			  struct x86_debug_reg_state> x86_debug_process_state;
 
 /* Get debug registers state for process PID.  */
 
 struct x86_debug_reg_state *
 x86_debug_reg_state (pid_t pid)
 {
-  return &x86_process_info_get (pid)->state;
+  return &x86_debug_process_state[pid];
 }
 
 /* See declaration in x86-nat.h.  */
@@ -112,24 +59,7 @@ x86_debug_reg_state (pid_t pid)
 void
 x86_forget_process (pid_t pid)
 {
-  struct x86_process_info *proc, **proc_link;
-
-  proc = x86_process_list;
-  proc_link = &x86_process_list;
-
-  while (proc != NULL)
-    {
-      if (proc->pid == pid)
-	{
-	  *proc_link = proc->next;
-
-	  xfree (proc);
-	  return;
-	}
-
-      proc_link = &proc->next;
-      proc = *proc_link;
-    }
+  x86_debug_process_state.erase (pid);
 }
 
 /* Clear the reference counts and forget everything we knew about the
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 03/12] x86-nat: Add x86_lookup_debug_reg_state.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
  2022-03-16 20:19 ` [PATCH v2 01/12] Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs support John Baldwin
  2022-03-16 20:19 ` [PATCH v2 02/12] x86-nat: Use an unordered_map to store per-pid debug reg state John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-21 18:10   ` Pedro Alves
  2022-03-16 20:19 ` [PATCH v2 04/12] Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets John Baldwin
                   ` (9 subsequent siblings)
  12 siblings, 1 reply; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This function returns nullptr if debug register state does not
yet exist for a given process rather than creating new state.
---
 gdb/x86-nat.c | 12 ++++++++++++
 gdb/x86-nat.h |  5 +++++
 2 files changed, 17 insertions(+)

diff --git a/gdb/x86-nat.c b/gdb/x86-nat.c
index c1e892bf564..36513dd8cfb 100644
--- a/gdb/x86-nat.c
+++ b/gdb/x86-nat.c
@@ -46,6 +46,18 @@ struct x86_dr_low_type x86_dr_low;
 static std::unordered_map<pid_t,
 			  struct x86_debug_reg_state> x86_debug_process_state;
 
+/* See x86-nat.h.  */
+
+struct x86_debug_reg_state *
+x86_lookup_debug_reg_state (pid_t pid)
+{
+  auto it = x86_debug_process_state.find (pid);
+  if (it != x86_debug_process_state.end ())
+    return &it->second;
+
+  return nullptr;
+}
+
 /* Get debug registers state for process PID.  */
 
 struct x86_debug_reg_state *
diff --git a/gdb/x86-nat.h b/gdb/x86-nat.h
index 913291a2305..d9c2a3f6e14 100644
--- a/gdb/x86-nat.h
+++ b/gdb/x86-nat.h
@@ -40,6 +40,11 @@ extern void x86_set_debug_register_length (int len);
 
 extern void x86_cleanup_dregs (void);
 
+/* Return the debug register state for process PID.  If no existing
+   state is found for this process, return nullptr.  */
+
+struct x86_debug_reg_state *x86_lookup_debug_reg_state (pid_t pid);
+
 /* Called whenever GDB is no longer debugging process PID.  It deletes
    data structures that keep track of debug register state.  */
 
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 04/12] Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (2 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 03/12] x86-nat: Add x86_lookup_debug_reg_state John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-16 20:19 ` [PATCH v2 05/12] fbsd-nat: Add a low_new_fork virtual method John Baldwin
                   ` (8 subsequent siblings)
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This class implements debug register support common between the i386
and amd64 native targets.

While here, remove #ifdef's for HAVE_PT_GETDBREGS in FreeBSD-specific
code.  The ptrace request has been present on FreeBSD x86
architectures since 4.0 (released in March 2000).  The last FreeBSD
release without this support is 3.5 released in June 2000.
---
 gdb/amd64-fbsd-nat.c | 20 ++------------------
 gdb/i386-fbsd-nat.c  | 20 ++------------------
 gdb/x86-fbsd-nat.h   | 34 ++++++++++++++++++++++++++++++++++
 3 files changed, 38 insertions(+), 36 deletions(-)
 create mode 100644 gdb/x86-fbsd-nat.h

diff --git a/gdb/amd64-fbsd-nat.c b/gdb/amd64-fbsd-nat.c
index 368f4c10786..d125d582a21 100644
--- a/gdb/amd64-fbsd-nat.c
+++ b/gdb/amd64-fbsd-nat.c
@@ -29,26 +29,20 @@
 #include <sys/user.h>
 #include <machine/reg.h>
 
-#include "fbsd-nat.h"
 #include "amd64-tdep.h"
 #include "amd64-fbsd-tdep.h"
 #include "amd64-nat.h"
 #include "x86-nat.h"
 #include "gdbsupport/x86-xstate.h"
-#include "x86-bsd-nat.h"
+#include "x86-fbsd-nat.h"
 
-class amd64_fbsd_nat_target final
-  : public x86bsd_nat_target<fbsd_nat_target>
+class amd64_fbsd_nat_target final : public x86_fbsd_nat_target
 {
 public:
   void fetch_registers (struct regcache *, int) override;
   void store_registers (struct regcache *, int) override;
 
   const struct target_desc *read_description () override;
-
-#if defined(HAVE_PT_GETDBREGS)
-  bool supports_stopped_by_hw_breakpoint () override;
-#endif
 };
 
 static amd64_fbsd_nat_target the_amd64_fbsd_nat_target;
@@ -348,16 +342,6 @@ amd64_fbsd_nat_target::read_description ()
     return i386_target_description (X86_XSTATE_SSE_MASK, true);
 }
 
-#if defined(HAVE_PT_GETDBREGS)
-/* Implement the supports_stopped_by_hw_breakpoints method.  */
-
-bool
-amd64_fbsd_nat_target::supports_stopped_by_hw_breakpoint ()
-{
-  return true;
-}
-#endif
-
 void _initialize_amd64fbsd_nat ();
 void
 _initialize_amd64fbsd_nat ()
diff --git a/gdb/i386-fbsd-nat.c b/gdb/i386-fbsd-nat.c
index 023f24bab37..4b8ba8b598f 100644
--- a/gdb/i386-fbsd-nat.c
+++ b/gdb/i386-fbsd-nat.c
@@ -27,16 +27,14 @@
 #include <sys/sysctl.h>
 #include <sys/user.h>
 
-#include "fbsd-nat.h"
 #include "i386-tdep.h"
 #include "i386-fbsd-tdep.h"
 #include "i387-tdep.h"
 #include "x86-nat.h"
 #include "gdbsupport/x86-xstate.h"
-#include "x86-bsd-nat.h"
+#include "x86-fbsd-nat.h"
 
-class i386_fbsd_nat_target final
-  : public x86bsd_nat_target<fbsd_nat_target>
+class i386_fbsd_nat_target final : public x86_fbsd_nat_target
 {
 public:
   void fetch_registers (struct regcache *, int) override;
@@ -45,10 +43,6 @@ class i386_fbsd_nat_target final
   const struct target_desc *read_description () override;
 
   void resume (ptid_t, int, enum gdb_signal) override;
-
-#if defined(HAVE_PT_GETDBREGS)
-  bool supports_stopped_by_hw_breakpoint () override;
-#endif
 };
 
 static i386_fbsd_nat_target the_i386_fbsd_nat_target;
@@ -361,16 +355,6 @@ i386_fbsd_nat_target::read_description ()
   return i386_target_description (X86_XSTATE_X87_MASK, true);
 }
 
-#if defined(HAVE_PT_GETDBREGS)
-/* Implement the supports_stopped_by_hw_breakpoints method.  */
-
-bool
-i386_fbsd_nat_target::supports_stopped_by_hw_breakpoint ()
-{
-  return true;
-}
-#endif
-
 void _initialize_i386fbsd_nat ();
 void
 _initialize_i386fbsd_nat ()
diff --git a/gdb/x86-fbsd-nat.h b/gdb/x86-fbsd-nat.h
new file mode 100644
index 00000000000..f9d3514aab4
--- /dev/null
+++ b/gdb/x86-fbsd-nat.h
@@ -0,0 +1,34 @@
+/* Native-dependent code for FreeBSD x86.
+
+   Copyright (C) 2022 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/>.  */
+
+#ifndef X86_FBSD_NAT_H
+#define X86_FBSD_NAT_H
+
+#include "fbsd-nat.h"
+#include "x86-bsd-nat.h"
+
+/* A prototype FreeBSD/x86 target.  */
+
+class x86_fbsd_nat_target : public x86bsd_nat_target<fbsd_nat_target>
+{
+  bool supports_stopped_by_hw_breakpoint () override
+  { return true; }
+};
+
+#endif /* x86-bsd-nat.h */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 05/12] fbsd-nat: Add a low_new_fork virtual method.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (3 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 04/12] Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-16 20:19 ` [PATCH v2 06/12] x86-fbsd-nat: Copy debug register state on fork John Baldwin
                   ` (7 subsequent siblings)
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This method can be overridden by architecture-specific targets to
perform additional work when a new child process is forked.
---
 gdb/fbsd-nat.c | 2 ++
 gdb/fbsd-nat.h | 6 ++++++
 2 files changed, 8 insertions(+)

diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index ba84265dd58..6d76c8234d5 100644
--- a/gdb/fbsd-nat.c
+++ b/gdb/fbsd-nat.c
@@ -1380,6 +1380,8 @@ fbsd_nat_target::wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
 		warning (_("Failed to fetch process information"));
 #endif
 
+	      low_new_fork (wptid, child);
+
 	      if (is_vfork)
 		ourstatus->set_vforked (child_ptid);
 	      else
diff --git a/gdb/fbsd-nat.h b/gdb/fbsd-nat.h
index 2d9c6e19a2c..2f17be5a8f0 100644
--- a/gdb/fbsd-nat.h
+++ b/gdb/fbsd-nat.h
@@ -109,6 +109,12 @@ class fbsd_nat_target : public inf_ptrace_target
 
   bool supports_disable_randomization () override;
 
+  /* Methods meant to be overridden by arch-specific target
+     classes.  */
+
+  virtual void low_new_fork (ptid_t parent, pid_t child)
+  {}
+
 protected:
 
   void post_startup_inferior (ptid_t) override;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 06/12] x86-fbsd-nat: Copy debug register state on fork.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (4 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 05/12] fbsd-nat: Add a low_new_fork virtual method John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-16 20:19 ` [PATCH v2 07/12] nat: Split out platform-independent aarch64 debug register support John Baldwin
                   ` (6 subsequent siblings)
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

Use the FreeBSD native target low_new_fork hook to copy the
per-process debug state from the parent to the child on fork.
---
 gdb/configure.nat  |  4 ++--
 gdb/x86-fbsd-nat.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
 gdb/x86-fbsd-nat.h |  2 ++
 3 files changed, 49 insertions(+), 2 deletions(-)
 create mode 100644 gdb/x86-fbsd-nat.c

diff --git a/gdb/configure.nat b/gdb/configure.nat
index b45519fd116..92ad4a6522b 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -165,7 +165,7 @@ case ${gdb_host} in
 	    i386)
 		# Host: FreeBSD/i386
 		NATDEPFILES="${NATDEPFILES} x86-nat.o nat/x86-dregs.o \
-		x86-bsd-nat.o i386-fbsd-nat.o bsd-kvm.o"
+		x86-bsd-nat.o x86-fbsd-nat.o i386-fbsd-nat.o bsd-kvm.o"
 		;;
 	    mips)
 		# Host: FreeBSD/mips
@@ -194,7 +194,7 @@ case ${gdb_host} in
 		# Host: FreeBSD/amd64
 		NATDEPFILES="${NATDEPFILES} amd64-nat.o \
 		amd64-fbsd-nat.o bsd-kvm.o x86-nat.o nat/x86-dregs.o \
-		x86-bsd-nat.o"
+		x86-bsd-nat.o x86-fbsd-nat.o"
 		;;
 	esac
 	;;
diff --git a/gdb/x86-fbsd-nat.c b/gdb/x86-fbsd-nat.c
new file mode 100644
index 00000000000..ad8c693b68e
--- /dev/null
+++ b/gdb/x86-fbsd-nat.c
@@ -0,0 +1,45 @@
+/* Native-dependent code for FreeBSD x86.
+
+   Copyright (C) 2022 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 "defs.h"
+#include "x86-fbsd-nat.h"
+
+/* Implement the virtual fbsd_nat_target::low_new_fork method.  */
+
+void
+x86_fbsd_nat_target::low_new_fork (ptid_t parent, pid_t child)
+{
+  struct x86_debug_reg_state *parent_state, *child_state;
+
+  /* If there is no parent state, no watchpoints nor breakpoints have
+     been set, so there is nothing to do.  */
+  parent_state = x86_lookup_debug_reg_state (parent.pid ());
+  if (parent_state == nullptr)
+    return;
+
+  /* The kernel clears debug registers in the new child process after
+     fork, but GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  */
+
+  child_state = x86_debug_reg_state (child);
+  *child_state = *parent_state;
+}
diff --git a/gdb/x86-fbsd-nat.h b/gdb/x86-fbsd-nat.h
index f9d3514aab4..cdb8cd36a4c 100644
--- a/gdb/x86-fbsd-nat.h
+++ b/gdb/x86-fbsd-nat.h
@@ -29,6 +29,8 @@ class x86_fbsd_nat_target : public x86bsd_nat_target<fbsd_nat_target>
 {
   bool supports_stopped_by_hw_breakpoint () override
   { return true; }
+
+  void low_new_fork (ptid_t parent, pid_t child) override;
 };
 
 #endif /* x86-bsd-nat.h */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 07/12] nat: Split out platform-independent aarch64 debug register support.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (5 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 06/12] x86-fbsd-nat: Copy debug register state on fork John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-17 15:37   ` Luis Machado
  2022-03-16 20:19 ` [PATCH v2 08/12] aarch64: Add an aarch64_nat_target mixin class John Baldwin
                   ` (5 subsequent siblings)
  12 siblings, 1 reply; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

Move non-Linux-specific support for hardware break/watchpoints from
nat/aarch64-linux-hw-point.c to nat/aarch64-hw-point.c.  Changes beyond
a simple split of the code are:

- aarch64_linux_region_ok_for_watchpoint and
  aarch64_linux_any_set_debug_regs_state renamed to drop linux_ as they
  are not platform specific.

- Platforms must implement the aarch64_notify_debug_reg_change function
  which is invoked from the platform-independent code when a debug
  register changes for a given debug register state.  This does not
  use the indirection of a 'low' structure as is done for x86.
  Possibly this function should be renamed to aarch64_dr_low_notify
  or some such if we wish to use aarch64_dr_low_* as a namespace for
  platform-specific low routines?

- The handling for kernel_supports_any_contiguous_range is not
  pristine.  For non-Linux it is simply defined to true.  Some uses
  of this could perhaps be implemented as new 'low' routines for the
  various places that check it instead?

- Pass down ptid into aarch64_handle_breakpoint and aarch64_handle_watchpoint
  rather than using current_lwp_ptid which is only defined on Linux.
  In addition, pass the ptid on to aarch64_notify_debug_reg_change instead
  of the unused state argument.
---
 gdb/aarch64-linux-nat.c          |  14 +-
 gdb/configure.nat                |   3 +-
 gdb/nat/aarch64-hw-point.c       | 624 +++++++++++++++++++++++++++++++
 gdb/nat/aarch64-hw-point.h       | 126 +++++++
 gdb/nat/aarch64-linux-hw-point.c | 605 +-----------------------------
 gdb/nat/aarch64-linux-hw-point.h | 105 +-----
 gdb/nat/aarch64-linux.c          |   4 +-
 gdbserver/configure.srv          |   1 +
 gdbserver/linux-aarch64-low.cc   |  13 +-
 9 files changed, 787 insertions(+), 708 deletions(-)
 create mode 100644 gdb/nat/aarch64-hw-point.c
 create mode 100644 gdb/nat/aarch64-hw-point.h

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index db764975207..dd072d9315e 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -834,7 +834,8 @@ aarch64_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
        "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
        (unsigned long) addr, len);
 
-  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */, state);
+  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */,
+				   inferior_ptid, state);
 
   if (show_debug_regs)
     {
@@ -866,7 +867,8 @@ aarch64_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
       (gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
        (unsigned long) addr, len);
 
-  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */, state);
+  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */,
+				   inferior_ptid, state);
 
   if (show_debug_regs)
     {
@@ -899,7 +901,8 @@ aarch64_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len,
 
   gdb_assert (type != hw_execute);
 
-  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */, state);
+  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */,
+				   inferior_ptid, state);
 
   if (show_debug_regs)
     {
@@ -931,7 +934,8 @@ aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
 
   gdb_assert (type != hw_execute);
 
-  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */, state);
+  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */,
+				   inferior_ptid, state);
 
   if (show_debug_regs)
     {
@@ -947,7 +951,7 @@ aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
 int
 aarch64_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
 {
-  return aarch64_linux_region_ok_for_watchpoint (addr, len);
+  return aarch64_region_ok_for_watchpoint (addr, len);
 }
 
 /* Implement the "stopped_data_address" target_ops method.  */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 92ad4a6522b..ad6d35babc2 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -234,7 +234,8 @@ case ${gdb_host} in
 	    aarch64)
 		#  Host: AArch64 based machine running GNU/Linux
 		NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
-		aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
+		aarch32-linux-nat.o nat/aarch64-hw-point.o \
+		nat/aarch64-linux-hw-point.o \
 		nat/aarch64-linux.o \
 		nat/aarch64-sve-linux-ptrace.o \
 		nat/aarch64-mte-linux-ptrace.o"
diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c
new file mode 100644
index 00000000000..f0418f7eef8
--- /dev/null
+++ b/gdb/nat/aarch64-hw-point.c
@@ -0,0 +1,624 @@
+/* Copyright (C) 2009-2022 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 "gdbsupport/common-defs.h"
+#include "gdbsupport/break-common.h"
+#include "gdbsupport/common-regcache.h"
+#include "aarch64-hw-point.h"
+
+#ifdef __linux__
+/* For kernel_supports_any_contiguous_range.  */
+#include "aarch64-linux-hw-point.h"
+#else
+#define	kernel_supports_any_contiguous_range	true
+#endif
+
+/* Number of hardware breakpoints/watchpoints the target supports.
+   They are initialized with values obtained via ptrace.  */
+
+int aarch64_num_bp_regs;
+int aarch64_num_wp_regs;
+
+/* Return starting byte 0..7 incl. of a watchpoint encoded by CTRL.  */
+
+unsigned int
+aarch64_watchpoint_offset (unsigned int ctrl)
+{
+  uint8_t mask = DR_CONTROL_MASK (ctrl);
+  unsigned retval;
+
+  /* Shift out bottom zeros.  */
+  for (retval = 0; mask && (mask & 1) == 0; ++retval)
+    mask >>= 1;
+
+  return retval;
+}
+
+/* Utility function that returns the length in bytes of a watchpoint
+   according to the content of a hardware debug control register CTRL.
+   Any contiguous range of bytes in CTRL is supported.  The returned
+   value can be between 0..8 (inclusive).  */
+
+unsigned int
+aarch64_watchpoint_length (unsigned int ctrl)
+{
+  uint8_t mask = DR_CONTROL_MASK (ctrl);
+  unsigned retval;
+
+  /* Shift out bottom zeros.  */
+  mask >>= aarch64_watchpoint_offset (ctrl);
+
+  /* Count bottom ones.  */
+  for (retval = 0; (mask & 1) != 0; ++retval)
+    mask >>= 1;
+
+  if (mask != 0)
+    error (_("Unexpected hardware watchpoint length register value 0x%x"),
+	   DR_CONTROL_MASK (ctrl));
+
+  return retval;
+}
+
+/* Given the hardware breakpoint or watchpoint type TYPE and its
+   length LEN, return the expected encoding for a hardware
+   breakpoint/watchpoint control register.  */
+
+static unsigned int
+aarch64_point_encode_ctrl_reg (enum target_hw_bp_type type, int offset, int len)
+{
+  unsigned int ctrl, ttype;
+
+  gdb_assert (offset == 0 || kernel_supports_any_contiguous_range);
+  gdb_assert (offset + len <= AARCH64_HWP_MAX_LEN_PER_REG);
+
+  /* type */
+  switch (type)
+    {
+    case hw_write:
+      ttype = 2;
+      break;
+    case hw_read:
+      ttype = 1;
+      break;
+    case hw_access:
+      ttype = 3;
+      break;
+    case hw_execute:
+      ttype = 0;
+      break;
+    default:
+      perror_with_name (_("Unrecognized breakpoint/watchpoint type"));
+    }
+
+  ctrl = ttype << 3;
+
+  /* offset and length bitmask */
+  ctrl |= ((1 << len) - 1) << (5 + offset);
+  /* enabled at el0 */
+  ctrl |= (2 << 1) | 1;
+
+  return ctrl;
+}
+
+/* Addresses to be written to the hardware breakpoint and watchpoint
+   value registers need to be aligned; the alignment is 4-byte and
+   8-type respectively.  Linux kernel rejects any non-aligned address
+   it receives from the related ptrace call.  Furthermore, the kernel
+   currently only supports the following Byte Address Select (BAS)
+   values: 0x1, 0x3, 0xf and 0xff, which means that for a hardware
+   watchpoint to be accepted by the kernel (via ptrace call), its
+   valid length can only be 1 byte, 2 bytes, 4 bytes or 8 bytes.
+   Despite these limitations, the unaligned watchpoint is supported in
+   this port.
+
+   Return 0 for any non-compliant ADDR and/or LEN; return 1 otherwise.  */
+
+static int
+aarch64_point_is_aligned (ptid_t ptid, int is_watchpoint, CORE_ADDR addr,
+			  int len)
+{
+  unsigned int alignment = 0;
+
+  if (is_watchpoint)
+    alignment = AARCH64_HWP_ALIGNMENT;
+  else
+    {
+      struct regcache *regcache
+	= get_thread_regcache_for_ptid (ptid);
+
+      /* Set alignment to 2 only if the current process is 32-bit,
+	 since thumb instruction can be 2-byte aligned.  Otherwise, set
+	 alignment to AARCH64_HBP_ALIGNMENT.  */
+      if (regcache_register_size (regcache, 0) == 8)
+	alignment = AARCH64_HBP_ALIGNMENT;
+      else
+	alignment = 2;
+    }
+
+  if (addr & (alignment - 1))
+    return 0;
+
+  if ((!kernel_supports_any_contiguous_range
+       && len != 8 && len != 4 && len != 2 && len != 1)
+      || (kernel_supports_any_contiguous_range
+	  && (len < 1 || len > 8)))
+    return 0;
+
+  return 1;
+}
+
+/* Given the (potentially unaligned) watchpoint address in ADDR and
+   length in LEN, return the aligned address, offset from that base
+   address, and aligned length in *ALIGNED_ADDR_P, *ALIGNED_OFFSET_P
+   and *ALIGNED_LEN_P, respectively.  The returned values will be
+   valid values to write to the hardware watchpoint value and control
+   registers.
+
+   The given watchpoint may get truncated if more than one hardware
+   register is needed to cover the watched region.  *NEXT_ADDR_P
+   and *NEXT_LEN_P, if non-NULL, will return the address and length
+   of the remaining part of the watchpoint (which can be processed
+   by calling this routine again to generate another aligned address,
+   offset and length tuple.
+
+   Essentially, unaligned watchpoint is achieved by minimally
+   enlarging the watched area to meet the alignment requirement, and
+   if necessary, splitting the watchpoint over several hardware
+   watchpoint registers.
+
+   On kernels that predate the support for Byte Address Select (BAS)
+   in the hardware watchpoint control register, the offset from the
+   base address is always zero, and so in that case the trade-off is
+   that there will be false-positive hits for the read-type or the
+   access-type hardware watchpoints; for the write type, which is more
+   commonly used, there will be no such issues, as the higher-level
+   breakpoint management in gdb always examines the exact watched
+   region for any content change, and transparently resumes a thread
+   from a watchpoint trap if there is no change to the watched region.
+
+   Another limitation is that because the watched region is enlarged,
+   the watchpoint fault address discovered by
+   aarch64_stopped_data_address may be outside of the original watched
+   region, especially when the triggering instruction is accessing a
+   larger region.  When the fault address is not within any known
+   range, watchpoints_triggered in gdb will get confused, as the
+   higher-level watchpoint management is only aware of original
+   watched regions, and will think that some unknown watchpoint has
+   been triggered.  To prevent such a case,
+   aarch64_stopped_data_address implementations in gdb and gdbserver
+   try to match the trapped address with a watched region, and return
+   an address within the latter. */
+
+static void
+aarch64_align_watchpoint (CORE_ADDR addr, int len, CORE_ADDR *aligned_addr_p,
+			  int *aligned_offset_p, int *aligned_len_p,
+			  CORE_ADDR *next_addr_p, int *next_len_p,
+			  CORE_ADDR *next_addr_orig_p)
+{
+  int aligned_len;
+  unsigned int offset, aligned_offset;
+  CORE_ADDR aligned_addr;
+  const unsigned int alignment = AARCH64_HWP_ALIGNMENT;
+  const unsigned int max_wp_len = AARCH64_HWP_MAX_LEN_PER_REG;
+
+  /* As assumed by the algorithm.  */
+  gdb_assert (alignment == max_wp_len);
+
+  if (len <= 0)
+    return;
+
+  /* The address put into the hardware watchpoint value register must
+     be aligned.  */
+  offset = addr & (alignment - 1);
+  aligned_addr = addr - offset;
+  aligned_offset
+    = kernel_supports_any_contiguous_range ? addr & (alignment - 1) : 0;
+
+  gdb_assert (offset >= 0 && offset < alignment);
+  gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
+  gdb_assert (offset + len > 0);
+
+  if (offset + len >= max_wp_len)
+    {
+      /* Need more than one watchpoint register; truncate at the
+	 alignment boundary.  */
+      aligned_len
+	= max_wp_len - (kernel_supports_any_contiguous_range ? offset : 0);
+      len -= (max_wp_len - offset);
+      addr += (max_wp_len - offset);
+      gdb_assert ((addr & (alignment - 1)) == 0);
+    }
+  else
+    {
+      /* Find the smallest valid length that is large enough to
+	 accommodate this watchpoint.  */
+      static const unsigned char
+	aligned_len_array[AARCH64_HWP_MAX_LEN_PER_REG] =
+	{ 1, 2, 4, 4, 8, 8, 8, 8 };
+
+      aligned_len = (kernel_supports_any_contiguous_range
+		     ? len : aligned_len_array[offset + len - 1]);
+      addr += len;
+      len = 0;
+    }
+
+  if (aligned_addr_p)
+    *aligned_addr_p = aligned_addr;
+  if (aligned_offset_p)
+    *aligned_offset_p = aligned_offset;
+  if (aligned_len_p)
+    *aligned_len_p = aligned_len;
+  if (next_addr_p)
+    *next_addr_p = addr;
+  if (next_len_p)
+    *next_len_p = len;
+  if (next_addr_orig_p)
+    *next_addr_orig_p = align_down (*next_addr_orig_p + alignment, alignment);
+}
+
+/* Record the insertion of one breakpoint/watchpoint, as represented
+   by ADDR and CTRL, in the process' arch-specific data area *STATE.  */
+
+static int
+aarch64_dr_state_insert_one_point (ptid_t ptid,
+				   struct aarch64_debug_reg_state *state,
+				   enum target_hw_bp_type type,
+				   CORE_ADDR addr, int offset, int len,
+				   CORE_ADDR addr_orig)
+{
+  int i, idx, num_regs, is_watchpoint;
+  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
+  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
+
+  /* Set up state pointers.  */
+  is_watchpoint = (type != hw_execute);
+  gdb_assert (aarch64_point_is_aligned (ptid, is_watchpoint, addr, len));
+  if (is_watchpoint)
+    {
+      num_regs = aarch64_num_wp_regs;
+      dr_addr_p = state->dr_addr_wp;
+      dr_addr_orig_p = state->dr_addr_orig_wp;
+      dr_ctrl_p = state->dr_ctrl_wp;
+      dr_ref_count = state->dr_ref_count_wp;
+    }
+  else
+    {
+      num_regs = aarch64_num_bp_regs;
+      dr_addr_p = state->dr_addr_bp;
+      dr_addr_orig_p = nullptr;
+      dr_ctrl_p = state->dr_ctrl_bp;
+      dr_ref_count = state->dr_ref_count_bp;
+    }
+
+  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
+
+  /* Find an existing or free register in our cache.  */
+  idx = -1;
+  for (i = 0; i < num_regs; ++i)
+    {
+      if ((dr_ctrl_p[i] & 1) == 0)
+	{
+	  gdb_assert (dr_ref_count[i] == 0);
+	  idx = i;
+	  /* no break; continue hunting for an exising one.  */
+	}
+      else if (dr_addr_p[i] == addr
+	       && (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
+	       && dr_ctrl_p[i] == ctrl)
+	{
+	  gdb_assert (dr_ref_count[i] != 0);
+	  idx = i;
+	  break;
+	}
+    }
+
+  /* No space.  */
+  if (idx == -1)
+    return -1;
+
+  /* Update our cache.  */
+  if ((dr_ctrl_p[idx] & 1) == 0)
+    {
+      /* new entry */
+      dr_addr_p[idx] = addr;
+      if (dr_addr_orig_p != nullptr)
+	dr_addr_orig_p[idx] = addr_orig;
+      dr_ctrl_p[idx] = ctrl;
+      dr_ref_count[idx] = 1;
+      /* Notify the change.  */
+      aarch64_notify_debug_reg_change (ptid, is_watchpoint, idx);
+    }
+  else
+    {
+      /* existing entry */
+      dr_ref_count[idx]++;
+    }
+
+  return 0;
+}
+
+/* Record the removal of one breakpoint/watchpoint, as represented by
+   ADDR and CTRL, in the process' arch-specific data area *STATE.  */
+
+static int
+aarch64_dr_state_remove_one_point (ptid_t ptid,
+				   struct aarch64_debug_reg_state *state,
+				   enum target_hw_bp_type type,
+				   CORE_ADDR addr, int offset, int len,
+				   CORE_ADDR addr_orig)
+{
+  int i, num_regs, is_watchpoint;
+  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
+  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
+
+  /* Set up state pointers.  */
+  is_watchpoint = (type != hw_execute);
+  if (is_watchpoint)
+    {
+      num_regs = aarch64_num_wp_regs;
+      dr_addr_p = state->dr_addr_wp;
+      dr_addr_orig_p = state->dr_addr_orig_wp;
+      dr_ctrl_p = state->dr_ctrl_wp;
+      dr_ref_count = state->dr_ref_count_wp;
+    }
+  else
+    {
+      num_regs = aarch64_num_bp_regs;
+      dr_addr_p = state->dr_addr_bp;
+      dr_addr_orig_p = nullptr;
+      dr_ctrl_p = state->dr_ctrl_bp;
+      dr_ref_count = state->dr_ref_count_bp;
+    }
+
+  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
+
+  /* Find the entry that matches the ADDR and CTRL.  */
+  for (i = 0; i < num_regs; ++i)
+    if (dr_addr_p[i] == addr
+	&& (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
+	&& dr_ctrl_p[i] == ctrl)
+      {
+	gdb_assert (dr_ref_count[i] != 0);
+	break;
+      }
+
+  /* Not found.  */
+  if (i == num_regs)
+    return -1;
+
+  /* Clear our cache.  */
+  if (--dr_ref_count[i] == 0)
+    {
+      /* Clear the enable bit.  */
+      ctrl &= ~1;
+      dr_addr_p[i] = 0;
+      if (dr_addr_orig_p != nullptr)
+	dr_addr_orig_p[i] = 0;
+      dr_ctrl_p[i] = ctrl;
+      /* Notify the change.  */
+      aarch64_notify_debug_reg_change (ptid, is_watchpoint, i);
+    }
+
+  return 0;
+}
+
+int
+aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+			   int len, int is_insert, ptid_t ptid,
+			   struct aarch64_debug_reg_state *state)
+{
+  if (is_insert)
+    {
+      /* The hardware breakpoint on AArch64 should always be 4-byte
+	 aligned, but on AArch32, it can be 2-byte aligned.  Note that
+	 we only check the alignment on inserting breakpoint because
+	 aarch64_point_is_aligned needs the inferior_ptid inferior's
+	 regcache to decide whether the inferior is 32-bit or 64-bit.
+	 However when GDB follows the parent process and detach breakpoints
+	 from child process, inferior_ptid is the child ptid, but the
+	 child inferior doesn't exist in GDB's view yet.  */
+      if (!aarch64_point_is_aligned (ptid, 0 /* is_watchpoint */ , addr, len))
+	return -1;
+
+      return aarch64_dr_state_insert_one_point (ptid, state, type, addr, 0, len,
+						-1);
+    }
+  else
+    return aarch64_dr_state_remove_one_point (ptid, state, type, addr, 0, len,
+					      -1);
+}
+
+/* This is essentially the same as aarch64_handle_breakpoint, apart
+   from that it is an aligned watchpoint to be handled.  */
+
+static int
+aarch64_handle_aligned_watchpoint (enum target_hw_bp_type type,
+				   CORE_ADDR addr, int len, int is_insert,
+				   ptid_t ptid,
+				   struct aarch64_debug_reg_state *state)
+{
+  if (is_insert)
+    return aarch64_dr_state_insert_one_point (ptid, state, type, addr, 0, len,
+					      addr);
+  else
+    return aarch64_dr_state_remove_one_point (ptid, state, type, addr, 0, len,
+					      addr);
+}
+
+/* Insert/remove unaligned watchpoint by calling
+   aarch64_align_watchpoint repeatedly until the whole watched region,
+   as represented by ADDR and LEN, has been properly aligned and ready
+   to be written to one or more hardware watchpoint registers.
+   IS_INSERT indicates whether this is an insertion or a deletion.
+   Return 0 if succeed.  */
+
+static int
+aarch64_handle_unaligned_watchpoint (enum target_hw_bp_type type,
+				     CORE_ADDR addr, int len, int is_insert,
+				     ptid_t ptid,
+				     struct aarch64_debug_reg_state *state)
+{
+  CORE_ADDR addr_orig = addr;
+
+  while (len > 0)
+    {
+      CORE_ADDR aligned_addr;
+      int aligned_offset, aligned_len, ret;
+      CORE_ADDR addr_orig_next = addr_orig;
+
+      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_offset,
+				&aligned_len, &addr, &len, &addr_orig_next);
+
+      if (is_insert)
+	ret = aarch64_dr_state_insert_one_point (ptid, state, type,
+						 aligned_addr, aligned_offset,
+						 aligned_len, addr_orig);
+      else
+	ret = aarch64_dr_state_remove_one_point (ptid, state, type,
+						 aligned_addr, aligned_offset,
+						 aligned_len, addr_orig);
+
+      if (show_debug_regs)
+	debug_printf ("handle_unaligned_watchpoint: is_insert: %d\n"
+		      "                             "
+		      "aligned_addr: %s, aligned_len: %d\n"
+		      "                                "
+		      "addr_orig: %s\n"
+		      "                                "
+		      "next_addr: %s,    next_len: %d\n"
+		      "                           "
+		      "addr_orig_next: %s\n",
+		      is_insert, core_addr_to_string_nz (aligned_addr),
+		      aligned_len, core_addr_to_string_nz (addr_orig),
+		      core_addr_to_string_nz (addr), len,
+		      core_addr_to_string_nz (addr_orig_next));
+
+      addr_orig = addr_orig_next;
+
+      if (ret != 0)
+	return ret;
+    }
+
+  return 0;
+}
+
+int
+aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+			   int len, int is_insert, ptid_t ptid,
+			   struct aarch64_debug_reg_state *state)
+{
+  if (aarch64_point_is_aligned (ptid, 1 /* is_watchpoint */ , addr, len))
+    return aarch64_handle_aligned_watchpoint (type, addr, len, is_insert, ptid,
+					      state);
+  else
+    return aarch64_handle_unaligned_watchpoint (type, addr, len, is_insert,
+						ptid, state);
+}
+
+/* See nat/aarch64-hw-point.h.  */
+
+bool
+aarch64_any_set_debug_regs_state (aarch64_debug_reg_state *state,
+				  bool watchpoint)
+{
+  int count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
+  if (count == 0)
+    return false;
+
+  const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
+  const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
+
+  for (int i = 0; i < count; i++)
+    if (addr[i] != 0 || ctrl[i] != 0)
+      return true;
+
+  return false;
+}
+
+/* Print the values of the cached breakpoint/watchpoint registers.  */
+
+void
+aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
+			      const char *func, CORE_ADDR addr,
+			      int len, enum target_hw_bp_type type)
+{
+  int i;
+
+  debug_printf ("%s", func);
+  if (addr || len)
+    debug_printf (" (addr=0x%08lx, len=%d, type=%s)",
+		  (unsigned long) addr, len,
+		  type == hw_write ? "hw-write-watchpoint"
+		  : (type == hw_read ? "hw-read-watchpoint"
+		     : (type == hw_access ? "hw-access-watchpoint"
+			: (type == hw_execute ? "hw-breakpoint"
+			   : "??unknown??"))));
+  debug_printf (":\n");
+
+  debug_printf ("\tBREAKPOINTs:\n");
+  for (i = 0; i < aarch64_num_bp_regs; i++)
+    debug_printf ("\tBP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
+		  i, core_addr_to_string_nz (state->dr_addr_bp[i]),
+		  state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
+
+  debug_printf ("\tWATCHPOINTs:\n");
+  for (i = 0; i < aarch64_num_wp_regs; i++)
+    debug_printf ("\tWP%d: addr=%s (orig=%s), ctrl=0x%08x, ref.count=%d\n",
+		  i, core_addr_to_string_nz (state->dr_addr_wp[i]),
+		  core_addr_to_string_nz (state->dr_addr_orig_wp[i]),
+		  state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
+}
+
+/* Return true if we can watch a memory region that starts address
+   ADDR and whose length is LEN in bytes.  */
+
+int
+aarch64_region_ok_for_watchpoint (CORE_ADDR addr, int len)
+{
+  CORE_ADDR aligned_addr;
+
+  /* Can not set watchpoints for zero or negative lengths.  */
+  if (len <= 0)
+    return 0;
+
+  /* Must have hardware watchpoint debug register(s).  */
+  if (aarch64_num_wp_regs == 0)
+    return 0;
+
+  /* We support unaligned watchpoint address and arbitrary length,
+     as long as the size of the whole watched area after alignment
+     doesn't exceed size of the total area that all watchpoint debug
+     registers can watch cooperatively.
+
+     This is a very relaxed rule, but unfortunately there are
+     limitations, e.g. false-positive hits, due to limited support of
+     hardware debug registers in the kernel.  See comment above
+     aarch64_align_watchpoint for more information.  */
+
+  aligned_addr = addr & ~(AARCH64_HWP_MAX_LEN_PER_REG - 1);
+  if (aligned_addr + aarch64_num_wp_regs * AARCH64_HWP_MAX_LEN_PER_REG
+      < addr + len)
+    return 0;
+
+  /* All tests passed so we are likely to be able to set the watchpoint.
+     The reason that it is 'likely' rather than 'must' is because
+     we don't check the current usage of the watchpoint registers, and
+     there may not be enough registers available for this watchpoint.
+     Ideally we should check the cached debug register state, however
+     the checking is costly.  */
+  return 1;
+}
diff --git a/gdb/nat/aarch64-hw-point.h b/gdb/nat/aarch64-hw-point.h
new file mode 100644
index 00000000000..97b37d537c2
--- /dev/null
+++ b/gdb/nat/aarch64-hw-point.h
@@ -0,0 +1,126 @@
+/* Copyright (C) 2009-2022 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/>.  */
+
+#ifndef NAT_AARCH64_HW_POINT_H
+#define NAT_AARCH64_HW_POINT_H
+
+/* Macro definitions, data structures, and code for the hardware
+   breakpoint and hardware watchpoint support follow.  We use the
+   following abbreviations throughout the code:
+
+   hw - hardware
+   bp - breakpoint
+   wp - watchpoint  */
+
+/* Maximum number of hardware breakpoint and watchpoint registers.
+   Neither of these values may exceed the width of dr_changed_t
+   measured in bits.  */
+
+#define AARCH64_HBP_MAX_NUM 16
+#define AARCH64_HWP_MAX_NUM 16
+
+/* Alignment requirement in bytes for addresses written to
+   hardware breakpoint and watchpoint value registers.
+
+   A ptrace call attempting to set an address that does not meet the
+   alignment criteria will fail.  Limited support has been provided in
+   this port for unaligned watchpoints, such that from a GDB user
+   perspective, an unaligned watchpoint may be requested.
+
+   This is achieved by minimally enlarging the watched area to meet the
+   alignment requirement, and if necessary, splitting the watchpoint
+   over several hardware watchpoint registers.  */
+
+#define AARCH64_HBP_ALIGNMENT 4
+#define AARCH64_HWP_ALIGNMENT 8
+
+/* The maximum length of a memory region that can be watched by one
+   hardware watchpoint register.  */
+
+#define AARCH64_HWP_MAX_LEN_PER_REG 8
+
+/* Macro for the expected version of the ARMv8-A debug architecture.  */
+#define AARCH64_DEBUG_ARCH_V8 0x6
+#define AARCH64_DEBUG_ARCH_V8_1 0x7
+#define AARCH64_DEBUG_ARCH_V8_2 0x8
+#define AARCH64_DEBUG_ARCH_V8_4 0x9
+
+/* ptrace expects control registers to be formatted as follows:
+
+   31                             13          5      3      1     0
+   +--------------------------------+----------+------+------+----+
+   |         RESERVED (SBZ)         |   MASK   | TYPE | PRIV | EN |
+   +--------------------------------+----------+------+------+----+
+
+   The TYPE field is ignored for breakpoints.  */
+
+#define DR_CONTROL_ENABLED(ctrl)	(((ctrl) & 0x1) == 1)
+#define DR_CONTROL_MASK(ctrl)		(((ctrl) >> 5) & 0xff)
+
+/* Structure for managing the hardware breakpoint/watchpoint resources.
+   DR_ADDR_* stores the address, DR_CTRL_* stores the control register
+   content, and DR_REF_COUNT_* counts the numbers of references to the
+   corresponding bp/wp, by which way the limited hardware resources
+   are not wasted on duplicated bp/wp settings (though so far gdb has
+   done a good job by not sending duplicated bp/wp requests).  */
+
+struct aarch64_debug_reg_state
+{
+  /* hardware breakpoint */
+  CORE_ADDR dr_addr_bp[AARCH64_HBP_MAX_NUM];
+  unsigned int dr_ctrl_bp[AARCH64_HBP_MAX_NUM];
+  unsigned int dr_ref_count_bp[AARCH64_HBP_MAX_NUM];
+
+  /* hardware watchpoint */
+  /* Address aligned down to AARCH64_HWP_ALIGNMENT.  */
+  CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM];
+  /* Address as entered by user without any forced alignment.  */
+  CORE_ADDR dr_addr_orig_wp[AARCH64_HWP_MAX_NUM];
+  unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM];
+  unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
+};
+
+extern int aarch64_num_bp_regs;
+extern int aarch64_num_wp_regs;
+
+/* Invoked when IDXth breakpoint/watchpoint register pair needs to be
+   updated.  */
+void aarch64_notify_debug_reg_change (ptid_t ptid, int is_watchpoint,
+				      unsigned int idx);
+
+unsigned int aarch64_watchpoint_offset (unsigned int ctrl);
+unsigned int aarch64_watchpoint_length (unsigned int ctrl);
+
+int aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+			       int len, int is_insert, ptid_t ptid,
+			       struct aarch64_debug_reg_state *state);
+int aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+			       int len, int is_insert, ptid_t ptid,
+			       struct aarch64_debug_reg_state *state);
+
+/* Return TRUE if there are any hardware breakpoints.  If WATCHPOINT is TRUE,
+   check hardware watchpoints instead.  */
+bool aarch64_any_set_debug_regs_state (aarch64_debug_reg_state *state,
+				       bool watchpoint);
+
+void aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
+				   const char *func, CORE_ADDR addr,
+				   int len, enum target_hw_bp_type type);
+
+int aarch64_region_ok_for_watchpoint (CORE_ADDR addr, int len);
+
+#endif /* NAT_AARCH64_HW_POINT_H */
diff --git a/gdb/nat/aarch64-linux-hw-point.c b/gdb/nat/aarch64-linux-hw-point.c
index f5dd3b2be2c..a6d91a367b7 100644
--- a/gdb/nat/aarch64-linux-hw-point.c
+++ b/gdb/nat/aarch64-linux-hw-point.c
@@ -34,256 +34,9 @@
 
 #include <elf.h>
 
-/* Number of hardware breakpoints/watchpoints the target supports.
-   They are initialized with values obtained via the ptrace calls
-   with NT_ARM_HW_BREAK and NT_ARM_HW_WATCH respectively.  */
+/* See aarch64-linux-hw-point.h  */
 
-int aarch64_num_bp_regs;
-int aarch64_num_wp_regs;
-
-/* True if this kernel does not have the bug described by PR
-   external/20207 (Linux >= 4.10).  A fixed kernel supports any
-   contiguous range of bits in 8-bit byte DR_CONTROL_MASK.  A buggy
-   kernel supports only 0x01, 0x03, 0x0f and 0xff.  We start by
-   assuming the bug is fixed, and then detect the bug at
-   PTRACE_SETREGSET time.  */
-static bool kernel_supports_any_contiguous_range = true;
-
-/* Return starting byte 0..7 incl. of a watchpoint encoded by CTRL.  */
-
-unsigned int
-aarch64_watchpoint_offset (unsigned int ctrl)
-{
-  uint8_t mask = DR_CONTROL_MASK (ctrl);
-  unsigned retval;
-
-  /* Shift out bottom zeros.  */
-  for (retval = 0; mask && (mask & 1) == 0; ++retval)
-    mask >>= 1;
-
-  return retval;
-}
-
-/* Utility function that returns the length in bytes of a watchpoint
-   according to the content of a hardware debug control register CTRL.
-   Any contiguous range of bytes in CTRL is supported.  The returned
-   value can be between 0..8 (inclusive).  */
-
-unsigned int
-aarch64_watchpoint_length (unsigned int ctrl)
-{
-  uint8_t mask = DR_CONTROL_MASK (ctrl);
-  unsigned retval;
-
-  /* Shift out bottom zeros.  */
-  mask >>= aarch64_watchpoint_offset (ctrl);
-
-  /* Count bottom ones.  */
-  for (retval = 0; (mask & 1) != 0; ++retval)
-    mask >>= 1;
-
-  if (mask != 0)
-    error (_("Unexpected hardware watchpoint length register value 0x%x"),
-	   DR_CONTROL_MASK (ctrl));
-
-  return retval;
-}
-
-/* Given the hardware breakpoint or watchpoint type TYPE and its
-   length LEN, return the expected encoding for a hardware
-   breakpoint/watchpoint control register.  */
-
-static unsigned int
-aarch64_point_encode_ctrl_reg (enum target_hw_bp_type type, int offset, int len)
-{
-  unsigned int ctrl, ttype;
-
-  gdb_assert (offset == 0 || kernel_supports_any_contiguous_range);
-  gdb_assert (offset + len <= AARCH64_HWP_MAX_LEN_PER_REG);
-
-  /* type */
-  switch (type)
-    {
-    case hw_write:
-      ttype = 2;
-      break;
-    case hw_read:
-      ttype = 1;
-      break;
-    case hw_access:
-      ttype = 3;
-      break;
-    case hw_execute:
-      ttype = 0;
-      break;
-    default:
-      perror_with_name (_("Unrecognized breakpoint/watchpoint type"));
-    }
-
-  ctrl = ttype << 3;
-
-  /* offset and length bitmask */
-  ctrl |= ((1 << len) - 1) << (5 + offset);
-  /* enabled at el0 */
-  ctrl |= (2 << 1) | 1;
-
-  return ctrl;
-}
-
-/* Addresses to be written to the hardware breakpoint and watchpoint
-   value registers need to be aligned; the alignment is 4-byte and
-   8-type respectively.  Linux kernel rejects any non-aligned address
-   it receives from the related ptrace call.  Furthermore, the kernel
-   currently only supports the following Byte Address Select (BAS)
-   values: 0x1, 0x3, 0xf and 0xff, which means that for a hardware
-   watchpoint to be accepted by the kernel (via ptrace call), its
-   valid length can only be 1 byte, 2 bytes, 4 bytes or 8 bytes.
-   Despite these limitations, the unaligned watchpoint is supported in
-   this port.
-
-   Return 0 for any non-compliant ADDR and/or LEN; return 1 otherwise.  */
-
-static int
-aarch64_point_is_aligned (int is_watchpoint, CORE_ADDR addr, int len)
-{
-  unsigned int alignment = 0;
-
-  if (is_watchpoint)
-    alignment = AARCH64_HWP_ALIGNMENT;
-  else
-    {
-      struct regcache *regcache
-	= get_thread_regcache_for_ptid (current_lwp_ptid ());
-
-      /* Set alignment to 2 only if the current process is 32-bit,
-	 since thumb instruction can be 2-byte aligned.  Otherwise, set
-	 alignment to AARCH64_HBP_ALIGNMENT.  */
-      if (regcache_register_size (regcache, 0) == 8)
-	alignment = AARCH64_HBP_ALIGNMENT;
-      else
-	alignment = 2;
-    }
-
-  if (addr & (alignment - 1))
-    return 0;
-
-  if ((!kernel_supports_any_contiguous_range
-       && len != 8 && len != 4 && len != 2 && len != 1)
-      || (kernel_supports_any_contiguous_range
-	  && (len < 1 || len > 8)))
-    return 0;
-
-  return 1;
-}
-
-/* Given the (potentially unaligned) watchpoint address in ADDR and
-   length in LEN, return the aligned address, offset from that base
-   address, and aligned length in *ALIGNED_ADDR_P, *ALIGNED_OFFSET_P
-   and *ALIGNED_LEN_P, respectively.  The returned values will be
-   valid values to write to the hardware watchpoint value and control
-   registers.
-
-   The given watchpoint may get truncated if more than one hardware
-   register is needed to cover the watched region.  *NEXT_ADDR_P
-   and *NEXT_LEN_P, if non-NULL, will return the address and length
-   of the remaining part of the watchpoint (which can be processed
-   by calling this routine again to generate another aligned address,
-   offset and length tuple.
-
-   Essentially, unaligned watchpoint is achieved by minimally
-   enlarging the watched area to meet the alignment requirement, and
-   if necessary, splitting the watchpoint over several hardware
-   watchpoint registers.
-
-   On kernels that predate the support for Byte Address Select (BAS)
-   in the hardware watchpoint control register, the offset from the
-   base address is always zero, and so in that case the trade-off is
-   that there will be false-positive hits for the read-type or the
-   access-type hardware watchpoints; for the write type, which is more
-   commonly used, there will be no such issues, as the higher-level
-   breakpoint management in gdb always examines the exact watched
-   region for any content change, and transparently resumes a thread
-   from a watchpoint trap if there is no change to the watched region.
-
-   Another limitation is that because the watched region is enlarged,
-   the watchpoint fault address discovered by
-   aarch64_stopped_data_address may be outside of the original watched
-   region, especially when the triggering instruction is accessing a
-   larger region.  When the fault address is not within any known
-   range, watchpoints_triggered in gdb will get confused, as the
-   higher-level watchpoint management is only aware of original
-   watched regions, and will think that some unknown watchpoint has
-   been triggered.  To prevent such a case,
-   aarch64_stopped_data_address implementations in gdb and gdbserver
-   try to match the trapped address with a watched region, and return
-   an address within the latter. */
-
-static void
-aarch64_align_watchpoint (CORE_ADDR addr, int len, CORE_ADDR *aligned_addr_p,
-			  int *aligned_offset_p, int *aligned_len_p,
-			  CORE_ADDR *next_addr_p, int *next_len_p,
-			  CORE_ADDR *next_addr_orig_p)
-{
-  int aligned_len;
-  unsigned int offset, aligned_offset;
-  CORE_ADDR aligned_addr;
-  const unsigned int alignment = AARCH64_HWP_ALIGNMENT;
-  const unsigned int max_wp_len = AARCH64_HWP_MAX_LEN_PER_REG;
-
-  /* As assumed by the algorithm.  */
-  gdb_assert (alignment == max_wp_len);
-
-  if (len <= 0)
-    return;
-
-  /* The address put into the hardware watchpoint value register must
-     be aligned.  */
-  offset = addr & (alignment - 1);
-  aligned_addr = addr - offset;
-  aligned_offset
-    = kernel_supports_any_contiguous_range ? addr & (alignment - 1) : 0;
-
-  gdb_assert (offset >= 0 && offset < alignment);
-  gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
-  gdb_assert (offset + len > 0);
-
-  if (offset + len >= max_wp_len)
-    {
-      /* Need more than one watchpoint register; truncate at the
-	 alignment boundary.  */
-      aligned_len
-	= max_wp_len - (kernel_supports_any_contiguous_range ? offset : 0);
-      len -= (max_wp_len - offset);
-      addr += (max_wp_len - offset);
-      gdb_assert ((addr & (alignment - 1)) == 0);
-    }
-  else
-    {
-      /* Find the smallest valid length that is large enough to
-	 accommodate this watchpoint.  */
-      static const unsigned char
-	aligned_len_array[AARCH64_HWP_MAX_LEN_PER_REG] =
-	{ 1, 2, 4, 4, 8, 8, 8, 8 };
-
-      aligned_len = (kernel_supports_any_contiguous_range
-		     ? len : aligned_len_array[offset + len - 1]);
-      addr += len;
-      len = 0;
-    }
-
-  if (aligned_addr_p)
-    *aligned_addr_p = aligned_addr;
-  if (aligned_offset_p)
-    *aligned_offset_p = aligned_offset;
-  if (aligned_len_p)
-    *aligned_len_p = aligned_len;
-  if (next_addr_p)
-    *next_addr_p = addr;
-  if (next_len_p)
-    *next_len_p = len;
-  if (next_addr_orig_p)
-    *next_addr_orig_p = align_down (*next_addr_orig_p + alignment, alignment);
-}
+bool kernel_supports_any_contiguous_range = true;
 
 /* Helper for aarch64_notify_debug_reg_change.  Records the
    information about the change of one hardware breakpoint/watchpoint
@@ -349,11 +102,11 @@ debug_reg_change_callback (struct lwp_info *lwp, int is_watchpoint,
    thread's arch-specific data area, the actual updating will be done
    when the thread is resumed.  */
 
-static void
-aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state,
+void
+aarch64_notify_debug_reg_change (ptid_t ptid,
 				 int is_watchpoint, unsigned int idx)
 {
-  ptid_t pid_ptid = ptid_t (current_lwp_ptid ().pid ());
+  ptid_t pid_ptid = ptid_t (ptid.pid ());
 
   iterate_over_lwps (pid_ptid, [=] (struct lwp_info *info)
 			       {
@@ -414,261 +167,11 @@ aarch64_downgrade_regs (struct aarch64_debug_reg_state *state)
 	      break;
 	    }
 
-	aarch64_notify_debug_reg_change (state, 1 /* is_watchpoint */, i);
+	aarch64_notify_debug_reg_change (current_lwp_ptid (),
+					 1 /* is_watchpoint */, i);
       }
 }
 
-/* Record the insertion of one breakpoint/watchpoint, as represented
-   by ADDR and CTRL, in the process' arch-specific data area *STATE.  */
-
-static int
-aarch64_dr_state_insert_one_point (struct aarch64_debug_reg_state *state,
-				   enum target_hw_bp_type type,
-				   CORE_ADDR addr, int offset, int len,
-				   CORE_ADDR addr_orig)
-{
-  int i, idx, num_regs, is_watchpoint;
-  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
-  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
-
-  /* Set up state pointers.  */
-  is_watchpoint = (type != hw_execute);
-  gdb_assert (aarch64_point_is_aligned (is_watchpoint, addr, len));
-  if (is_watchpoint)
-    {
-      num_regs = aarch64_num_wp_regs;
-      dr_addr_p = state->dr_addr_wp;
-      dr_addr_orig_p = state->dr_addr_orig_wp;
-      dr_ctrl_p = state->dr_ctrl_wp;
-      dr_ref_count = state->dr_ref_count_wp;
-    }
-  else
-    {
-      num_regs = aarch64_num_bp_regs;
-      dr_addr_p = state->dr_addr_bp;
-      dr_addr_orig_p = nullptr;
-      dr_ctrl_p = state->dr_ctrl_bp;
-      dr_ref_count = state->dr_ref_count_bp;
-    }
-
-  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
-
-  /* Find an existing or free register in our cache.  */
-  idx = -1;
-  for (i = 0; i < num_regs; ++i)
-    {
-      if ((dr_ctrl_p[i] & 1) == 0)
-	{
-	  gdb_assert (dr_ref_count[i] == 0);
-	  idx = i;
-	  /* no break; continue hunting for an exising one.  */
-	}
-      else if (dr_addr_p[i] == addr
-	       && (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
-	       && dr_ctrl_p[i] == ctrl)
-	{
-	  gdb_assert (dr_ref_count[i] != 0);
-	  idx = i;
-	  break;
-	}
-    }
-
-  /* No space.  */
-  if (idx == -1)
-    return -1;
-
-  /* Update our cache.  */
-  if ((dr_ctrl_p[idx] & 1) == 0)
-    {
-      /* new entry */
-      dr_addr_p[idx] = addr;
-      if (dr_addr_orig_p != nullptr)
-	dr_addr_orig_p[idx] = addr_orig;
-      dr_ctrl_p[idx] = ctrl;
-      dr_ref_count[idx] = 1;
-      /* Notify the change.  */
-      aarch64_notify_debug_reg_change (state, is_watchpoint, idx);
-    }
-  else
-    {
-      /* existing entry */
-      dr_ref_count[idx]++;
-    }
-
-  return 0;
-}
-
-/* Record the removal of one breakpoint/watchpoint, as represented by
-   ADDR and CTRL, in the process' arch-specific data area *STATE.  */
-
-static int
-aarch64_dr_state_remove_one_point (struct aarch64_debug_reg_state *state,
-				   enum target_hw_bp_type type,
-				   CORE_ADDR addr, int offset, int len,
-				   CORE_ADDR addr_orig)
-{
-  int i, num_regs, is_watchpoint;
-  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
-  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
-
-  /* Set up state pointers.  */
-  is_watchpoint = (type != hw_execute);
-  if (is_watchpoint)
-    {
-      num_regs = aarch64_num_wp_regs;
-      dr_addr_p = state->dr_addr_wp;
-      dr_addr_orig_p = state->dr_addr_orig_wp;
-      dr_ctrl_p = state->dr_ctrl_wp;
-      dr_ref_count = state->dr_ref_count_wp;
-    }
-  else
-    {
-      num_regs = aarch64_num_bp_regs;
-      dr_addr_p = state->dr_addr_bp;
-      dr_addr_orig_p = nullptr;
-      dr_ctrl_p = state->dr_ctrl_bp;
-      dr_ref_count = state->dr_ref_count_bp;
-    }
-
-  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
-
-  /* Find the entry that matches the ADDR and CTRL.  */
-  for (i = 0; i < num_regs; ++i)
-    if (dr_addr_p[i] == addr
-	&& (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
-	&& dr_ctrl_p[i] == ctrl)
-      {
-	gdb_assert (dr_ref_count[i] != 0);
-	break;
-      }
-
-  /* Not found.  */
-  if (i == num_regs)
-    return -1;
-
-  /* Clear our cache.  */
-  if (--dr_ref_count[i] == 0)
-    {
-      /* Clear the enable bit.  */
-      ctrl &= ~1;
-      dr_addr_p[i] = 0;
-      if (dr_addr_orig_p != nullptr)
-	dr_addr_orig_p[i] = 0;
-      dr_ctrl_p[i] = ctrl;
-      /* Notify the change.  */
-      aarch64_notify_debug_reg_change (state, is_watchpoint, i);
-    }
-
-  return 0;
-}
-
-int
-aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
-			   int len, int is_insert,
-			   struct aarch64_debug_reg_state *state)
-{
-  if (is_insert)
-    {
-      /* The hardware breakpoint on AArch64 should always be 4-byte
-	 aligned, but on AArch32, it can be 2-byte aligned.  Note that
-	 we only check the alignment on inserting breakpoint because
-	 aarch64_point_is_aligned needs the inferior_ptid inferior's
-	 regcache to decide whether the inferior is 32-bit or 64-bit.
-	 However when GDB follows the parent process and detach breakpoints
-	 from child process, inferior_ptid is the child ptid, but the
-	 child inferior doesn't exist in GDB's view yet.  */
-      if (!aarch64_point_is_aligned (0 /* is_watchpoint */ , addr, len))
-	return -1;
-
-      return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, -1);
-    }
-  else
-    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, -1);
-}
-
-/* This is essentially the same as aarch64_handle_breakpoint, apart
-   from that it is an aligned watchpoint to be handled.  */
-
-static int
-aarch64_handle_aligned_watchpoint (enum target_hw_bp_type type,
-				   CORE_ADDR addr, int len, int is_insert,
-				   struct aarch64_debug_reg_state *state)
-{
-  if (is_insert)
-    return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, addr);
-  else
-    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, addr);
-}
-
-/* Insert/remove unaligned watchpoint by calling
-   aarch64_align_watchpoint repeatedly until the whole watched region,
-   as represented by ADDR and LEN, has been properly aligned and ready
-   to be written to one or more hardware watchpoint registers.
-   IS_INSERT indicates whether this is an insertion or a deletion.
-   Return 0 if succeed.  */
-
-static int
-aarch64_handle_unaligned_watchpoint (enum target_hw_bp_type type,
-				     CORE_ADDR addr, int len, int is_insert,
-				     struct aarch64_debug_reg_state *state)
-{
-  CORE_ADDR addr_orig = addr;
-
-  while (len > 0)
-    {
-      CORE_ADDR aligned_addr;
-      int aligned_offset, aligned_len, ret;
-      CORE_ADDR addr_orig_next = addr_orig;
-
-      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_offset,
-				&aligned_len, &addr, &len, &addr_orig_next);
-
-      if (is_insert)
-	ret = aarch64_dr_state_insert_one_point (state, type, aligned_addr,
-						 aligned_offset,
-						 aligned_len, addr_orig);
-      else
-	ret = aarch64_dr_state_remove_one_point (state, type, aligned_addr,
-						 aligned_offset,
-						 aligned_len, addr_orig);
-
-      if (show_debug_regs)
-	debug_printf ("handle_unaligned_watchpoint: is_insert: %d\n"
-		      "                             "
-		      "aligned_addr: %s, aligned_len: %d\n"
-		      "                                "
-		      "addr_orig: %s\n"
-		      "                                "
-		      "next_addr: %s,    next_len: %d\n"
-		      "                           "
-		      "addr_orig_next: %s\n",
-		      is_insert, core_addr_to_string_nz (aligned_addr),
-		      aligned_len, core_addr_to_string_nz (addr_orig),
-		      core_addr_to_string_nz (addr), len,
-		      core_addr_to_string_nz (addr_orig_next));
-
-      addr_orig = addr_orig_next;
-
-      if (ret != 0)
-	return ret;
-    }
-
-  return 0;
-}
-
-int
-aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
-			   int len, int is_insert,
-			   struct aarch64_debug_reg_state *state)
-{
-  if (aarch64_point_is_aligned (1 /* is_watchpoint */ , addr, len))
-    return aarch64_handle_aligned_watchpoint (type, addr, len, is_insert,
-					      state);
-  else
-    return aarch64_handle_unaligned_watchpoint (type, addr, len, is_insert,
-						state);
-}
-
 /* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
    registers with data from *STATE.  */
 
@@ -715,60 +218,6 @@ aarch64_linux_set_debug_regs (struct aarch64_debug_reg_state *state,
     }
 }
 
-/* See nat/aarch64-linux-hw-point.h.  */
-
-bool
-aarch64_linux_any_set_debug_regs_state (aarch64_debug_reg_state *state,
-					bool watchpoint)
-{
-  int count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
-  if (count == 0)
-    return false;
-
-  const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
-  const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
-
-  for (int i = 0; i < count; i++)
-    if (addr[i] != 0 || ctrl[i] != 0)
-      return true;
-
-  return false;
-}
-
-/* Print the values of the cached breakpoint/watchpoint registers.  */
-
-void
-aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
-			      const char *func, CORE_ADDR addr,
-			      int len, enum target_hw_bp_type type)
-{
-  int i;
-
-  debug_printf ("%s", func);
-  if (addr || len)
-    debug_printf (" (addr=0x%08lx, len=%d, type=%s)",
-		  (unsigned long) addr, len,
-		  type == hw_write ? "hw-write-watchpoint"
-		  : (type == hw_read ? "hw-read-watchpoint"
-		     : (type == hw_access ? "hw-access-watchpoint"
-			: (type == hw_execute ? "hw-breakpoint"
-			   : "??unknown??"))));
-  debug_printf (":\n");
-
-  debug_printf ("\tBREAKPOINTs:\n");
-  for (i = 0; i < aarch64_num_bp_regs; i++)
-    debug_printf ("\tBP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
-		  i, core_addr_to_string_nz (state->dr_addr_bp[i]),
-		  state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
-
-  debug_printf ("\tWATCHPOINTs:\n");
-  for (i = 0; i < aarch64_num_wp_regs; i++)
-    debug_printf ("\tWP%d: addr=%s (orig=%s), ctrl=0x%08x, ref.count=%d\n",
-		  i, core_addr_to_string_nz (state->dr_addr_wp[i]),
-		  core_addr_to_string_nz (state->dr_addr_orig_wp[i]),
-		  state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
-}
-
 /* Return true if debug arch level is compatible for hw watchpoints
    and breakpoints.  */
 
@@ -839,43 +288,3 @@ aarch64_linux_get_debug_reg_capacity (int tid)
       aarch64_num_bp_regs = 0;
     }
 }
-
-/* Return true if we can watch a memory region that starts address
-   ADDR and whose length is LEN in bytes.  */
-
-int
-aarch64_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len)
-{
-  CORE_ADDR aligned_addr;
-
-  /* Can not set watchpoints for zero or negative lengths.  */
-  if (len <= 0)
-    return 0;
-
-  /* Must have hardware watchpoint debug register(s).  */
-  if (aarch64_num_wp_regs == 0)
-    return 0;
-
-  /* We support unaligned watchpoint address and arbitrary length,
-     as long as the size of the whole watched area after alignment
-     doesn't exceed size of the total area that all watchpoint debug
-     registers can watch cooperatively.
-
-     This is a very relaxed rule, but unfortunately there are
-     limitations, e.g. false-positive hits, due to limited support of
-     hardware debug registers in the kernel.  See comment above
-     aarch64_align_watchpoint for more information.  */
-
-  aligned_addr = addr & ~(AARCH64_HWP_MAX_LEN_PER_REG - 1);
-  if (aligned_addr + aarch64_num_wp_regs * AARCH64_HWP_MAX_LEN_PER_REG
-      < addr + len)
-    return 0;
-
-  /* All tests passed so we are likely to be able to set the watchpoint.
-     The reason that it is 'likely' rather than 'must' is because
-     we don't check the current usage of the watchpoint registers, and
-     there may not be enough registers available for this watchpoint.
-     Ideally we should check the cached debug register state, however
-     the checking is costly.  */
-  return 1;
-}
diff --git a/gdb/nat/aarch64-linux-hw-point.h b/gdb/nat/aarch64-linux-hw-point.h
index c746a7622a0..7c694ff0882 100644
--- a/gdb/nat/aarch64-linux-hw-point.h
+++ b/gdb/nat/aarch64-linux-hw-point.h
@@ -21,40 +21,7 @@
 
 #include "gdbsupport/break-common.h" /* For enum target_hw_bp_type.  */
 
-/* Macro definitions, data structures, and code for the hardware
-   breakpoint and hardware watchpoint support follow.  We use the
-   following abbreviations throughout the code:
-
-   hw - hardware
-   bp - breakpoint
-   wp - watchpoint  */
-
-/* Maximum number of hardware breakpoint and watchpoint registers.
-   Neither of these values may exceed the width of dr_changed_t
-   measured in bits.  */
-
-#define AARCH64_HBP_MAX_NUM 16
-#define AARCH64_HWP_MAX_NUM 16
-
-/* Alignment requirement in bytes for addresses written to
-   hardware breakpoint and watchpoint value registers.
-
-   A ptrace call attempting to set an address that does not meet the
-   alignment criteria will fail.  Limited support has been provided in
-   this port for unaligned watchpoints, such that from a GDB user
-   perspective, an unaligned watchpoint may be requested.
-
-   This is achieved by minimally enlarging the watched area to meet the
-   alignment requirement, and if necessary, splitting the watchpoint
-   over several hardware watchpoint registers.  */
-
-#define AARCH64_HBP_ALIGNMENT 4
-#define AARCH64_HWP_ALIGNMENT 8
-
-/* The maximum length of a memory region that can be watched by one
-   hardware watchpoint register.  */
-
-#define AARCH64_HWP_MAX_LEN_PER_REG 8
+#include "nat/aarch64-hw-point.h"
 
 /* ptrace hardware breakpoint resource info is formatted as follows:
 
@@ -68,24 +35,6 @@
 #define AARCH64_DEBUG_NUM_SLOTS(x) ((x) & 0xff)
 #define AARCH64_DEBUG_ARCH(x) (((x) >> 8) & 0xff)
 
-/* Macro for the expected version of the ARMv8-A debug architecture.  */
-#define AARCH64_DEBUG_ARCH_V8 0x6
-#define AARCH64_DEBUG_ARCH_V8_1 0x7
-#define AARCH64_DEBUG_ARCH_V8_2 0x8
-#define AARCH64_DEBUG_ARCH_V8_4 0x9
-
-/* ptrace expects control registers to be formatted as follows:
-
-   31                             13          5      3      1     0
-   +--------------------------------+----------+------+------+----+
-   |         RESERVED (SBZ)         |   MASK   | TYPE | PRIV | EN |
-   +--------------------------------+----------+------+------+----+
-
-   The TYPE field is ignored for breakpoints.  */
-
-#define DR_CONTROL_ENABLED(ctrl)	(((ctrl) & 0x1) == 1)
-#define DR_CONTROL_MASK(ctrl)		(((ctrl) >> 5) & 0xff)
-
 /* Each bit of a variable of this type is used to indicate whether a
    hardware breakpoint or watchpoint setting has been changed since
    the last update.
@@ -133,29 +82,6 @@ typedef ULONGEST dr_changed_t;
 #define DR_HAS_CHANGED(x) ((x) != 0)
 #define DR_N_HAS_CHANGED(x, n) ((x) & ((dr_changed_t)1 << (n)))
 
-/* Structure for managing the hardware breakpoint/watchpoint resources.
-   DR_ADDR_* stores the address, DR_CTRL_* stores the control register
-   content, and DR_REF_COUNT_* counts the numbers of references to the
-   corresponding bp/wp, by which way the limited hardware resources
-   are not wasted on duplicated bp/wp settings (though so far gdb has
-   done a good job by not sending duplicated bp/wp requests).  */
-
-struct aarch64_debug_reg_state
-{
-  /* hardware breakpoint */
-  CORE_ADDR dr_addr_bp[AARCH64_HBP_MAX_NUM];
-  unsigned int dr_ctrl_bp[AARCH64_HBP_MAX_NUM];
-  unsigned int dr_ref_count_bp[AARCH64_HBP_MAX_NUM];
-
-  /* hardware watchpoint */
-  /* Address aligned down to AARCH64_HWP_ALIGNMENT.  */
-  CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM];
-  /* Address as entered by user without any forced alignment.  */
-  CORE_ADDR dr_addr_orig_wp[AARCH64_HWP_MAX_NUM];
-  unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM];
-  unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
-};
-
 /* Per-thread arch-specific data we want to keep.  */
 
 struct arch_lwp_info
@@ -167,35 +93,20 @@ struct arch_lwp_info
   dr_changed_t dr_changed_wp;
 };
 
-extern int aarch64_num_bp_regs;
-extern int aarch64_num_wp_regs;
+/* True if this kernel does not have the bug described by PR
+   external/20207 (Linux >= 4.10).  A fixed kernel supports any
+   contiguous range of bits in 8-bit byte DR_CONTROL_MASK.  A buggy
+   kernel supports only 0x01, 0x03, 0x0f and 0xff.  We start by
+   assuming the bug is fixed, and then detect the bug at
+   PTRACE_SETREGSET time.  */
 
-unsigned int aarch64_watchpoint_offset (unsigned int ctrl);
-unsigned int aarch64_watchpoint_length (unsigned int ctrl);
-
-int aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
-			       int len, int is_insert,
-			       struct aarch64_debug_reg_state *state);
-int aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
-			       int len, int is_insert,
-			       struct aarch64_debug_reg_state *state);
+extern bool kernel_supports_any_contiguous_range;
 
 void aarch64_linux_set_debug_regs (struct aarch64_debug_reg_state *state,
 				   int tid, int watchpoint);
 
-/* Return TRUE if there are any hardware breakpoints.  If WATCHPOINT is TRUE,
-   check hardware watchpoints instead.  */
-bool aarch64_linux_any_set_debug_regs_state (aarch64_debug_reg_state *state,
-					     bool watchpoint);
-
-void aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
-				   const char *func, CORE_ADDR addr,
-				   int len, enum target_hw_bp_type type);
-
 void aarch64_linux_get_debug_reg_capacity (int tid);
 
 struct aarch64_debug_reg_state *aarch64_get_debug_reg_state (pid_t pid);
 
-int aarch64_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len);
-
 #endif /* NAT_AARCH64_LINUX_HW_POINT_H */
diff --git a/gdb/nat/aarch64-linux.c b/gdb/nat/aarch64-linux.c
index b2ed8f9a2a5..421d1ecb53c 100644
--- a/gdb/nat/aarch64-linux.c
+++ b/gdb/nat/aarch64-linux.c
@@ -81,9 +81,9 @@ aarch64_linux_new_thread (struct lwp_info *lwp)
   /* If there are hardware breakpoints/watchpoints in the process then mark that
      all the hardware breakpoint/watchpoint register pairs for this thread need
      to be initialized (with data from aarch_process_info.debug_reg_state).  */
-  if (aarch64_linux_any_set_debug_regs_state (state, false))
+  if (aarch64_any_set_debug_regs_state (state, false))
     DR_MARK_ALL_CHANGED (info->dr_changed_bp, aarch64_num_bp_regs);
-  if (aarch64_linux_any_set_debug_regs_state (state, true))
+  if (aarch64_any_set_debug_regs_state (state, true))
     DR_MARK_ALL_CHANGED (info->dr_changed_wp, aarch64_num_wp_regs);
 
   lwp_set_arch_private_info (lwp, info);
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 6e09b0eeb79..d37053628fc 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -39,6 +39,7 @@ fi
 
 case "${gdbserver_host}" in
   aarch64*-*-linux*)	srv_tgtobj="linux-aarch64-low.o"
+			srv_tgtobj="$srv_tgtobj nat/aarch64-hw-point.o"
 			srv_tgtobj="$srv_tgtobj nat/aarch64-linux-hw-point.o"
 			srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
 			srv_tgtobj="$srv_tgtobj linux-aarch32-tdesc.o"
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index aef69b34525..0091f998c63 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -413,9 +413,10 @@ aarch64_target::low_insert_point (raw_bkpt_type type, CORE_ADDR addr,
 
   if (targ_type != hw_execute)
     {
-      if (aarch64_linux_region_ok_for_watchpoint (addr, len))
+      if (aarch64_region_ok_for_watchpoint (addr, len))
 	ret = aarch64_handle_watchpoint (targ_type, addr, len,
-					 1 /* is_insert */, state);
+					 1 /* is_insert */,
+					 current_lwp_ptid (), state);
       else
 	ret = -1;
     }
@@ -429,7 +430,8 @@ aarch64_target::low_insert_point (raw_bkpt_type type, CORE_ADDR addr,
 	  len = 2;
 	}
       ret = aarch64_handle_breakpoint (targ_type, addr, len,
-				       1 /* is_insert */, state);
+				       1 /* is_insert */, current_lwp_ptid (),
+				       state);
     }
 
   if (show_debug_regs)
@@ -464,7 +466,7 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
   if (targ_type != hw_execute)
     ret =
       aarch64_handle_watchpoint (targ_type, addr, len, 0 /* is_insert */,
-				 state);
+				 current_lwp_ptid (), state);
   else
     {
       if (len == 3)
@@ -475,7 +477,8 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
 	  len = 2;
 	}
       ret = aarch64_handle_breakpoint (targ_type, addr, len,
-				       0 /* is_insert */,  state);
+				       0 /* is_insert */,  current_lwp_ptid (),
+				       state);
     }
 
   if (show_debug_regs)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 08/12] aarch64: Add an aarch64_nat_target mixin class.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (6 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 07/12] nat: Split out platform-independent aarch64 debug register support John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-17 15:35   ` Luis Machado
  2022-03-16 20:19 ` [PATCH v2 09/12] fbsd-nat: Add helper routine to fetch siginfo_t for a ptid John Baldwin
                   ` (4 subsequent siblings)
  12 siblings, 1 reply; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This class includes platform-independent target methods for hardware
breakpoints and watchpoints using routines from nat/aarch64-hw-point.c.

stopped_data_address is not platform-independent since the FAR register
holding the address for a breakpoint hit must be fetched in a
platform-specific manner.  However, aarch64_stopped_data_address is
provided as a helper routine which performs platform-independent
validation given the value of the FAR register.

For tracking the per-process debug register mirror state, use an
unordered_map indexed by pid as recently adopted in x86-nat.c rather
than a manual linked-list.
---
 gdb/aarch64-linux-nat.c | 356 +---------------------------------------
 gdb/aarch64-nat.c       | 302 ++++++++++++++++++++++++++++++++++
 gdb/aarch64-nat.h       | 109 ++++++++++++
 gdb/configure.nat       |   2 +-
 4 files changed, 418 insertions(+), 351 deletions(-)
 create mode 100644 gdb/aarch64-nat.c
 create mode 100644 gdb/aarch64-nat.h

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index dd072d9315e..7bb82d17cc8 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -27,6 +27,7 @@
 #include "target-descriptions.h"
 #include "auxv.h"
 #include "gdbcmd.h"
+#include "aarch64-nat.h"
 #include "aarch64-tdep.h"
 #include "aarch64-linux-tdep.h"
 #include "aarch32-linux-nat.h"
@@ -58,7 +59,8 @@
 #define TRAP_HWBKPT 0x0004
 #endif
 
-class aarch64_linux_nat_target final : public linux_nat_target
+class aarch64_linux_nat_target final
+  : public aarch64_nat_target<linux_nat_target>
 {
 public:
   /* Add our register access methods.  */
@@ -68,17 +70,8 @@ class aarch64_linux_nat_target final : public linux_nat_target
   const struct target_desc *read_description () override;
 
   /* Add our hardware breakpoint and watchpoint implementation.  */
-  int can_use_hw_breakpoint (enum bptype, int, int) override;
-  int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override;
-  int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override;
-  int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
-  int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
-			 struct expression *) override;
-  int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
-			 struct expression *) override;
   bool stopped_by_watchpoint () override;
   bool stopped_data_address (CORE_ADDR *) override;
-  bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
 
   int can_do_single_step () override;
 
@@ -118,103 +111,13 @@ class aarch64_linux_nat_target final : public linux_nat_target
 
 static aarch64_linux_nat_target the_aarch64_linux_nat_target;
 
-/* Per-process data.  We don't bind this to a per-inferior registry
-   because of targets like x86 GNU/Linux that need to keep track of
-   processes that aren't bound to any inferior (e.g., fork children,
-   checkpoints).  */
-
-struct aarch64_process_info
-{
-  /* Linked list.  */
-  struct aarch64_process_info *next;
-
-  /* The process identifier.  */
-  pid_t pid;
-
-  /* Copy of aarch64 hardware debug registers.  */
-  struct aarch64_debug_reg_state state;
-};
-
-static struct aarch64_process_info *aarch64_process_list = NULL;
-
-/* Find process data for process PID.  */
-
-static struct aarch64_process_info *
-aarch64_find_process_pid (pid_t pid)
-{
-  struct aarch64_process_info *proc;
-
-  for (proc = aarch64_process_list; proc; proc = proc->next)
-    if (proc->pid == pid)
-      return proc;
-
-  return NULL;
-}
-
-/* Add process data for process PID.  Returns newly allocated info
-   object.  */
-
-static struct aarch64_process_info *
-aarch64_add_process (pid_t pid)
-{
-  struct aarch64_process_info *proc;
-
-  proc = XCNEW (struct aarch64_process_info);
-  proc->pid = pid;
-
-  proc->next = aarch64_process_list;
-  aarch64_process_list = proc;
-
-  return proc;
-}
-
-/* Get data specific info for process PID, creating it if necessary.
-   Never returns NULL.  */
-
-static struct aarch64_process_info *
-aarch64_process_info_get (pid_t pid)
-{
-  struct aarch64_process_info *proc;
-
-  proc = aarch64_find_process_pid (pid);
-  if (proc == NULL)
-    proc = aarch64_add_process (pid);
-
-  return proc;
-}
-
 /* Called whenever GDB is no longer debugging process PID.  It deletes
    data structures that keep track of debug register state.  */
 
 void
 aarch64_linux_nat_target::low_forget_process (pid_t pid)
 {
-  struct aarch64_process_info *proc, **proc_link;
-
-  proc = aarch64_process_list;
-  proc_link = &aarch64_process_list;
-
-  while (proc != NULL)
-    {
-      if (proc->pid == pid)
-	{
-	  *proc_link = proc->next;
-
-	  xfree (proc);
-	  return;
-	}
-
-      proc_link = &proc->next;
-      proc = *proc_link;
-    }
-}
-
-/* Get debug registers state for process PID.  */
-
-struct aarch64_debug_reg_state *
-aarch64_get_debug_reg_state (pid_t pid)
-{
-  return &aarch64_process_info_get (pid)->state;
+  aarch64_remove_debug_reg_state (pid);
 }
 
 /* Fill GDB's register array with the general-purpose register values
@@ -775,192 +678,12 @@ aarch64_linux_nat_target::low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
   return false;
 }
 
-/* Returns the number of hardware watchpoints of type TYPE that we can
-   set.  Value is positive if we can set CNT watchpoints, zero if
-   setting watchpoints of type TYPE is not supported, and negative if
-   CNT is more than the maximum number of watchpoints of type TYPE
-   that we can support.  TYPE is one of bp_hardware_watchpoint,
-   bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint.
-   CNT is the number of such watchpoints used so far (including this
-   one).  OTHERTYPE is non-zero if other types of watchpoints are
-   currently enabled.  */
-
-int
-aarch64_linux_nat_target::can_use_hw_breakpoint (enum bptype type,
-						 int cnt, int othertype)
-{
-  if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
-      || type == bp_access_watchpoint || type == bp_watchpoint)
-    {
-      if (aarch64_num_wp_regs == 0)
-	return 0;
-    }
-  else if (type == bp_hardware_breakpoint)
-    {
-      if (aarch64_num_bp_regs == 0)
-	return 0;
-    }
-  else
-    gdb_assert_not_reached ("unexpected breakpoint type");
-
-  /* We always return 1 here because we don't have enough information
-     about possible overlap of addresses that they want to watch.  As an
-     extreme example, consider the case where all the watchpoints watch
-     the same address and the same region length: then we can handle a
-     virtually unlimited number of watchpoints, due to debug register
-     sharing implemented via reference counts.  */
-  return 1;
-}
-
-/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
-   Return 0 on success, -1 on failure.  */
-
-int
-aarch64_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
-						struct bp_target_info *bp_tgt)
-{
-  int ret;
-  CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
-  int len;
-  const enum target_hw_bp_type type = hw_execute;
-  struct aarch64_debug_reg_state *state
-    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
-
-  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
-
-  if (show_debug_regs)
-    fprintf_unfiltered
-      (gdb_stdlog,
-       "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
-       (unsigned long) addr, len);
-
-  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */,
-				   inferior_ptid, state);
-
-  if (show_debug_regs)
-    {
-      aarch64_show_debug_reg_state (state,
-				    "insert_hw_breakpoint", addr, len, type);
-    }
-
-  return ret;
-}
-
-/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address.
-   Return 0 on success, -1 on failure.  */
-
-int
-aarch64_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
-						struct bp_target_info *bp_tgt)
-{
-  int ret;
-  CORE_ADDR addr = bp_tgt->placed_address;
-  int len = 4;
-  const enum target_hw_bp_type type = hw_execute;
-  struct aarch64_debug_reg_state *state
-    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
-
-  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
-
-  if (show_debug_regs)
-    fprintf_unfiltered
-      (gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
-       (unsigned long) addr, len);
-
-  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */,
-				   inferior_ptid, state);
-
-  if (show_debug_regs)
-    {
-      aarch64_show_debug_reg_state (state,
-				    "remove_hw_watchpoint", addr, len, type);
-    }
-
-  return ret;
-}
-
-/* Implement the "insert_watchpoint" target_ops method.
-
-   Insert a watchpoint to watch a memory region which starts at
-   address ADDR and whose length is LEN bytes.  Watch memory accesses
-   of the type TYPE.  Return 0 on success, -1 on failure.  */
-
-int
-aarch64_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len,
-					     enum target_hw_bp_type type,
-					     struct expression *cond)
-{
-  int ret;
-  struct aarch64_debug_reg_state *state
-    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
-
-  if (show_debug_regs)
-    fprintf_unfiltered (gdb_stdlog,
-			"insert_watchpoint on entry (addr=0x%08lx, len=%d)\n",
-			(unsigned long) addr, len);
-
-  gdb_assert (type != hw_execute);
-
-  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */,
-				   inferior_ptid, state);
-
-  if (show_debug_regs)
-    {
-      aarch64_show_debug_reg_state (state,
-				    "insert_watchpoint", addr, len, type);
-    }
-
-  return ret;
-}
-
-/* Implement the "remove_watchpoint" target_ops method.
-   Remove a watchpoint that watched the memory region which starts at
-   address ADDR, whose length is LEN bytes, and for accesses of the
-   type TYPE.  Return 0 on success, -1 on failure.  */
-
-int
-aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
-					     enum target_hw_bp_type type,
-					     struct expression *cond)
-{
-  int ret;
-  struct aarch64_debug_reg_state *state
-    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
-
-  if (show_debug_regs)
-    fprintf_unfiltered (gdb_stdlog,
-			"remove_watchpoint on entry (addr=0x%08lx, len=%d)\n",
-			(unsigned long) addr, len);
-
-  gdb_assert (type != hw_execute);
-
-  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */,
-				   inferior_ptid, state);
-
-  if (show_debug_regs)
-    {
-      aarch64_show_debug_reg_state (state,
-				    "remove_watchpoint", addr, len, type);
-    }
-
-  return ret;
-}
-
-/* Implement the "region_ok_for_hw_watchpoint" target_ops method.  */
-
-int
-aarch64_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
-{
-  return aarch64_region_ok_for_watchpoint (addr, len);
-}
-
 /* Implement the "stopped_data_address" target_ops method.  */
 
 bool
 aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
 {
   siginfo_t siginfo;
-  int i;
   struct aarch64_debug_reg_state *state;
 
   if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
@@ -980,44 +703,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
 
   /* Check if the address matches any watched address.  */
   state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
-  for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
-    {
-      const unsigned int offset
-	= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
-      const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
-      const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
-      const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
-      const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
-
-      if (state->dr_ref_count_wp[i]
-	  && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
-	  && addr_trap >= addr_watch_aligned
-	  && addr_trap < addr_watch + len)
-	{
-	  /* ADDR_TRAP reports the first address of the memory range
-	     accessed by the CPU, regardless of what was the memory
-	     range watched.  Thus, a large CPU access that straddles
-	     the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
-	     ADDR_TRAP that is lower than the
-	     ADDR_WATCH..ADDR_WATCH+LEN range.  E.g.:
-
-	     addr: |   4   |   5   |   6   |   7   |   8   |
-				   |---- range watched ----|
-		   |----------- range accessed ------------|
-
-	     In this case, ADDR_TRAP will be 4.
-
-	     To match a watchpoint known to GDB core, we must never
-	     report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
-	     range.  ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
-	     positive on kernels older than 4.10.  See PR
-	     external/20207.  */
-	  *addr_p = addr_orig;
-	  return true;
-	}
-    }
-
-  return false;
+  return aarch64_stopped_data_address (state, addr_trap, addr_p);
 }
 
 /* Implement the "stopped_by_watchpoint" target_ops method.  */
@@ -1030,15 +716,6 @@ aarch64_linux_nat_target::stopped_by_watchpoint ()
   return stopped_data_address (&addr);
 }
 
-/* Implement the "watchpoint_addr_within_range" target_ops method.  */
-
-bool
-aarch64_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr,
-							CORE_ADDR start, int length)
-{
-  return start <= addr && start + length - 1 >= addr;
-}
-
 /* Implement the "can_do_single_step" target_ops method.  */
 
 int
@@ -1114,32 +791,11 @@ aarch64_linux_nat_target::store_memtags (CORE_ADDR address, size_t len,
   return false;
 }
 
-/* Define AArch64 maintenance commands.  */
-
-static void
-add_show_debug_regs_command (void)
-{
-  /* A maintenance command to enable printing the internal DRi mirror
-     variables.  */
-  add_setshow_boolean_cmd ("show-debug-regs", class_maintenance,
-			   &show_debug_regs, _("\
-Set whether to show variables that mirror the AArch64 debug registers."), _("\
-Show whether to show variables that mirror the AArch64 debug registers."), _("\
-Use \"on\" to enable, \"off\" to disable.\n\
-If enabled, the debug registers values are shown when GDB inserts\n\
-or removes a hardware breakpoint or watchpoint, and when the inferior\n\
-triggers a breakpoint or watchpoint."),
-			   NULL,
-			   NULL,
-			   &maintenance_set_cmdlist,
-			   &maintenance_show_cmdlist);
-}
-
 void _initialize_aarch64_linux_nat ();
 void
 _initialize_aarch64_linux_nat ()
 {
-  add_show_debug_regs_command ();
+  aarch64_initialize_hw_point ();
 
   /* Register the target.  */
   linux_target = &the_aarch64_linux_nat_target;
diff --git a/gdb/aarch64-nat.c b/gdb/aarch64-nat.c
new file mode 100644
index 00000000000..85cf7f2011a
--- /dev/null
+++ b/gdb/aarch64-nat.c
@@ -0,0 +1,302 @@
+/* Native-dependent code for AArch64.
+
+   Copyright (C) 2011-2022 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 "defs.h"
+#include "gdbarch.h"
+#include "inferior.h"
+#include "cli/cli-cmds.h"
+#include "aarch64-nat.h"
+
+#include <unordered_map>
+
+/* Hash table storing per-process data.  We don't bind this to a
+   per-inferior registry because of targets like x86 GNU/Linux that
+   need to keep track of processes that aren't bound to any inferior
+   (e.g., fork children, checkpoints).  */
+
+static std::unordered_map<pid_t, aarch64_debug_reg_state>
+aarch64_debug_process_state;
+
+/* See aarch64-nat.h.  */
+
+struct aarch64_debug_reg_state *
+aarch64_lookup_debug_reg_state (pid_t pid)
+{
+  auto it = aarch64_debug_process_state.find (pid);
+  if (it != aarch64_debug_process_state.end ())
+    return &it->second;
+
+  return nullptr;
+}
+
+/* See aarch64-nat.h.  */
+
+struct aarch64_debug_reg_state *
+aarch64_get_debug_reg_state (pid_t pid)
+{
+  return &aarch64_debug_process_state[pid];
+}
+
+/* See aarch64-nat.h.  */
+
+void
+aarch64_remove_debug_reg_state (pid_t pid)
+{
+  aarch64_debug_process_state.erase (pid);
+}
+
+/* Returns the number of hardware watchpoints of type TYPE that we can
+   set.  Value is positive if we can set CNT watchpoints, zero if
+   setting watchpoints of type TYPE is not supported, and negative if
+   CNT is more than the maximum number of watchpoints of type TYPE
+   that we can support.  TYPE is one of bp_hardware_watchpoint,
+   bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint.
+   CNT is the number of such watchpoints used so far (including this
+   one).  OTHERTYPE is non-zero if other types of watchpoints are
+   currently enabled.  */
+
+int
+aarch64_can_use_hw_breakpoint (enum bptype type, int cnt, int othertype)
+{
+  if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
+      || type == bp_access_watchpoint || type == bp_watchpoint)
+    {
+      if (aarch64_num_wp_regs == 0)
+	return 0;
+    }
+  else if (type == bp_hardware_breakpoint)
+    {
+      if (aarch64_num_bp_regs == 0)
+	return 0;
+    }
+  else
+    gdb_assert_not_reached ("unexpected breakpoint type");
+
+  /* We always return 1 here because we don't have enough information
+     about possible overlap of addresses that they want to watch.  As an
+     extreme example, consider the case where all the watchpoints watch
+     the same address and the same region length: then we can handle a
+     virtually unlimited number of watchpoints, due to debug register
+     sharing implemented via reference counts.  */
+  return 1;
+}
+
+/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
+   Return 0 on success, -1 on failure.  */
+
+int
+aarch64_insert_hw_breakpoint (struct gdbarch *gdbarch,
+			      struct bp_target_info *bp_tgt)
+{
+  int ret;
+  CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
+  int len;
+  const enum target_hw_bp_type type = hw_execute;
+  struct aarch64_debug_reg_state *state
+    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
+
+  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+  if (show_debug_regs)
+    fprintf_unfiltered
+      (gdb_stdlog,
+       "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+       (unsigned long) addr, len);
+
+  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */,
+				   inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      aarch64_show_debug_reg_state (state,
+				    "insert_hw_breakpoint", addr, len, type);
+    }
+
+  return ret;
+}
+
+/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address.
+   Return 0 on success, -1 on failure.  */
+
+int
+aarch64_remove_hw_breakpoint (struct gdbarch *gdbarch,
+			      struct bp_target_info *bp_tgt)
+{
+  int ret;
+  CORE_ADDR addr = bp_tgt->placed_address;
+  int len = 4;
+  const enum target_hw_bp_type type = hw_execute;
+  struct aarch64_debug_reg_state *state
+    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
+
+  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+  if (show_debug_regs)
+    fprintf_unfiltered
+      (gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+       (unsigned long) addr, len);
+
+  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */,
+				   inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      aarch64_show_debug_reg_state (state,
+				    "remove_hw_watchpoint", addr, len, type);
+    }
+
+  return ret;
+}
+
+/* Insert a watchpoint to watch a memory region which starts at
+   address ADDR and whose length is LEN bytes.  Watch memory accesses
+   of the type TYPE.  Return 0 on success, -1 on failure.  */
+
+int
+aarch64_insert_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type,
+			   struct expression *cond)
+{
+  int ret;
+  struct aarch64_debug_reg_state *state
+    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
+
+  if (show_debug_regs)
+    fprintf_unfiltered (gdb_stdlog,
+			"insert_watchpoint on entry (addr=0x%08lx, len=%d)\n",
+			(unsigned long) addr, len);
+
+  gdb_assert (type != hw_execute);
+
+  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */,
+				   inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      aarch64_show_debug_reg_state (state,
+				    "insert_watchpoint", addr, len, type);
+    }
+
+  return ret;
+}
+
+/* Remove a watchpoint that watched the memory region which starts at
+   address ADDR, whose length is LEN bytes, and for accesses of the
+   type TYPE.  Return 0 on success, -1 on failure.  */
+
+int
+aarch64_remove_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type,
+			   struct expression *cond)
+{
+  int ret;
+  struct aarch64_debug_reg_state *state
+    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
+
+  if (show_debug_regs)
+    fprintf_unfiltered (gdb_stdlog,
+			"remove_watchpoint on entry (addr=0x%08lx, len=%d)\n",
+			(unsigned long) addr, len);
+
+  gdb_assert (type != hw_execute);
+
+  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */,
+				   inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      aarch64_show_debug_reg_state (state,
+				    "remove_watchpoint", addr, len, type);
+    }
+
+  return ret;
+}
+
+/* See aarch64-nat.h.  */
+
+bool
+aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
+			      CORE_ADDR addr_trap, CORE_ADDR *addr_p)
+{
+  int i;
+
+  for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
+    {
+      const unsigned int offset
+	= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
+      const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
+      const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
+      const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
+      const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
+
+      if (state->dr_ref_count_wp[i]
+	  && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
+	  && addr_trap >= addr_watch_aligned
+	  && addr_trap < addr_watch + len)
+	{
+	  /* ADDR_TRAP reports the first address of the memory range
+	     accessed by the CPU, regardless of what was the memory
+	     range watched.  Thus, a large CPU access that straddles
+	     the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
+	     ADDR_TRAP that is lower than the
+	     ADDR_WATCH..ADDR_WATCH+LEN range.  E.g.:
+
+	     addr: |   4   |   5   |   6   |   7   |   8   |
+				   |---- range watched ----|
+		   |----------- range accessed ------------|
+
+	     In this case, ADDR_TRAP will be 4.
+
+	     To match a watchpoint known to GDB core, we must never
+	     report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
+	     range.  ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
+	     positive on kernels older than 4.10.  See PR
+	     external/20207.  */
+	  *addr_p = addr_orig;
+	  return true;
+	}
+    }
+
+  return false;
+}
+
+/* Define AArch64 maintenance commands.  */
+
+static void
+add_show_debug_regs_command (void)
+{
+  /* A maintenance command to enable printing the internal DRi mirror
+     variables.  */
+  add_setshow_boolean_cmd ("show-debug-regs", class_maintenance,
+			   &show_debug_regs, _("\
+Set whether to show variables that mirror the AArch64 debug registers."), _("\
+Show whether to show variables that mirror the AArch64 debug registers."), _("\
+Use \"on\" to enable, \"off\" to disable.\n\
+If enabled, the debug registers values are shown when GDB inserts\n\
+or removes a hardware breakpoint or watchpoint, and when the inferior\n\
+triggers a breakpoint or watchpoint."),
+			   NULL,
+			   NULL,
+			   &maintenance_set_cmdlist,
+			   &maintenance_show_cmdlist);
+}
+
+void
+aarch64_initialize_hw_point ()
+{
+  add_show_debug_regs_command ();
+}
diff --git a/gdb/aarch64-nat.h b/gdb/aarch64-nat.h
new file mode 100644
index 00000000000..56e720f02ee
--- /dev/null
+++ b/gdb/aarch64-nat.h
@@ -0,0 +1,109 @@
+/* Native-dependent code for AArch64.
+
+   Copyright (C) 2011-2022 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/>.  */
+
+#ifndef AARCH64_NAT_H
+#define AARCH64_NAT_H
+
+#include "breakpoint.h"
+#include "nat/aarch64-hw-point.h"
+#include "target.h"
+
+/* Hardware-assisted breakpoints and watchpoints.  */
+
+/* Initialize platform-independent state for hardware-assisted
+   breakpoints and watchpoints.  */
+
+void aarch64_initialize_hw_point ();
+
+/* Return the debug register state for process PID.  If no existing
+   state is found for this process, return nullptr.  */
+
+struct aarch64_debug_reg_state *aarch64_lookup_debug_reg_state (pid_t pid);
+
+/* Return the debug register state for process PID.  If no existing
+   state is found for this process, create new state.  */
+
+struct aarch64_debug_reg_state *aarch64_get_debug_reg_state (pid_t pid);
+
+/* Remove any existing per-process debug state for process PID.  */
+
+void aarch64_remove_debug_reg_state (pid_t pid);
+
+/* Helper for the "stopped_data_address" target method.  Returns TRUE
+   if a hardware watchpoint trap at ADDR_TRAP matches a set
+   watchpoint.  The address of the matched watchpoint is returned in
+   *ADDR_P.  */
+
+bool aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
+				   CORE_ADDR addr_trap, CORE_ADDR *addr_p);
+
+/* Helper functions used by aarch64_nat_target below.  See their
+   definitions.  */
+
+int aarch64_can_use_hw_breakpoint (enum bptype type, int cnt, int othertype);
+int aarch64_insert_watchpoint (CORE_ADDR addr, int len,
+			       enum target_hw_bp_type type,
+			       struct expression *cond);
+int aarch64_remove_watchpoint (CORE_ADDR addr, int len,
+			       enum target_hw_bp_type type,
+			       struct expression *cond);
+int aarch64_insert_hw_breakpoint (struct gdbarch *gdbarch,
+				  struct bp_target_info *bp_tgt);
+int aarch64_remove_hw_breakpoint (struct gdbarch *gdbarch,
+				  struct bp_target_info *bp_tgt);
+int aarch64_stopped_by_hw_breakpoint ();
+
+/* Convenience template mixin used to add aarch64 watchpoints support to a
+   target.  */
+
+template <typename BaseTarget>
+struct aarch64_nat_target : public BaseTarget
+{
+  /* Hook in common aarch64 hardware watchpoints/breakpoints support.  */
+
+  int can_use_hw_breakpoint (enum bptype type, int cnt, int othertype) override
+  { return aarch64_can_use_hw_breakpoint (type, cnt, othertype); }
+
+  int region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) override
+  { return aarch64_region_ok_for_watchpoint (addr, len); }
+
+  int insert_watchpoint (CORE_ADDR addr, int len,
+			 enum target_hw_bp_type type,
+			 struct expression *cond) override
+  { return aarch64_insert_watchpoint (addr, len, type, cond); }
+
+  int remove_watchpoint (CORE_ADDR addr, int len,
+			 enum target_hw_bp_type type,
+			 struct expression *cond) override
+  { return aarch64_remove_watchpoint (addr, len, type, cond); }
+
+  int insert_hw_breakpoint (struct gdbarch *gdbarch,
+			    struct bp_target_info *bp_tgt) override
+  { return aarch64_insert_hw_breakpoint (gdbarch, bp_tgt); }
+
+  int remove_hw_breakpoint (struct gdbarch *gdbarch,
+			    struct bp_target_info *bp_tgt) override
+  { return aarch64_remove_hw_breakpoint (gdbarch, bp_tgt); }
+
+  bool watchpoint_addr_within_range (CORE_ADDR addr, CORE_ADDR start,
+				     int length) override
+  { return start <= addr && start + length - 1 >= addr; }
+};
+
+#endif /* AARCH64_NAT_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index ad6d35babc2..4f5850dd595 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -233,7 +233,7 @@ case ${gdb_host} in
 	case ${gdb_host_cpu} in
 	    aarch64)
 		#  Host: AArch64 based machine running GNU/Linux
-		NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
+		NATDEPFILES="${NATDEPFILES} aarch64-nat.o aarch64-linux-nat.o \
 		aarch32-linux-nat.o nat/aarch64-hw-point.o \
 		nat/aarch64-linux-hw-point.o \
 		nat/aarch64-linux.o \
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 09/12] fbsd-nat: Add helper routine to fetch siginfo_t for a ptid.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (7 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 08/12] aarch64: Add an aarch64_nat_target mixin class John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-16 20:19 ` [PATCH v2 10/12] fbsd-nat: Add a low_delete_thread virtual method John Baldwin
                   ` (3 subsequent siblings)
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

---
 gdb/fbsd-nat.c | 16 ++++++++++++++++
 gdb/fbsd-nat.h |  4 ++++
 2 files changed, 20 insertions(+)

diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index 6d76c8234d5..51234eaa6c9 100644
--- a/gdb/fbsd-nat.c
+++ b/gdb/fbsd-nat.c
@@ -1766,6 +1766,22 @@ fbsd_nat_target::store_register_set (struct regcache *regcache, int regnum,
   return false;
 }
 
+/* See fbsd-nat.h.  */
+
+bool
+fbsd_nat_get_siginfo (ptid_t ptid, siginfo_t *siginfo)
+{
+  struct ptrace_lwpinfo pl;
+  pid_t pid = get_ptrace_pid (ptid);
+
+  if (ptrace (PT_LWPINFO, pid, (caddr_t) &pl, sizeof pl) == -1)
+    return false;
+  if (!(pl.pl_flags & PL_FLAG_SI))
+    return false;;
+  *siginfo = pl.pl_siginfo;
+  return (true);
+}
+
 void _initialize_fbsd_nat ();
 void
 _initialize_fbsd_nat ()
diff --git a/gdb/fbsd-nat.h b/gdb/fbsd-nat.h
index 2f17be5a8f0..d7c8eb81e96 100644
--- a/gdb/fbsd-nat.h
+++ b/gdb/fbsd-nat.h
@@ -166,4 +166,8 @@ class fbsd_nat_target : public inf_ptrace_target
   }
 };
 
+/* Fetch the signal information for PTID and store it in *SIGINFO.
+   Return true if successful.  */
+bool fbsd_nat_get_siginfo (ptid_t ptid, siginfo_t *siginfo);
+
 #endif /* fbsd-nat.h */
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 10/12] fbsd-nat: Add a low_delete_thread virtual method.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (8 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 09/12] fbsd-nat: Add helper routine to fetch siginfo_t for a ptid John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-16 20:19 ` [PATCH v2 11/12] fbsd-nat: Add a low_prepare_to_resume " John Baldwin
                   ` (2 subsequent siblings)
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This method can be overridden by architecture-specific targets to
perform additional work when a thread is deleted.

Note that this method is only invoked on systems supporting LWP
events, but the pending use case (aarch64 debug registers) is not
supported on older kernels that do not support LWP events.
---
 gdb/fbsd-nat.c | 1 +
 gdb/fbsd-nat.h | 4 ++++
 2 files changed, 5 insertions(+)

diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index 51234eaa6c9..2bc7937a38b 100644
--- a/gdb/fbsd-nat.c
+++ b/gdb/fbsd-nat.c
@@ -1293,6 +1293,7 @@ fbsd_nat_target::wait_1 (ptid_t ptid, struct target_waitstatus *ourstatus,
 		  if (print_thread_events)
 		    printf_unfiltered (_("[%s exited]\n"),
 				       target_pid_to_str (wptid).c_str ());
+		  low_delete_thread (thr);
 		  delete_thread (thr);
 		}
 	      if (ptrace (PT_CONTINUE, pid, (caddr_t) 1, 0) == -1)
diff --git a/gdb/fbsd-nat.h b/gdb/fbsd-nat.h
index d7c8eb81e96..6028aebfccc 100644
--- a/gdb/fbsd-nat.h
+++ b/gdb/fbsd-nat.h
@@ -115,6 +115,10 @@ class fbsd_nat_target : public inf_ptrace_target
   virtual void low_new_fork (ptid_t parent, pid_t child)
   {}
 
+  /* The method to call, if any, when a thread is destroyed.  */
+  virtual void low_delete_thread (thread_info *)
+  {}
+
 protected:
 
   void post_startup_inferior (ptid_t) override;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 11/12] fbsd-nat: Add a low_prepare_to_resume virtual method.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (9 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 10/12] fbsd-nat: Add a low_delete_thread virtual method John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-16 20:19 ` [PATCH v2 12/12] Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64 John Baldwin
  2022-03-30 15:23 ` [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support Luis Machado
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This method can be overridden by architecture-specific targets to
perform additional work before a thread is resumed.
---
 gdb/fbsd-nat.c | 9 +++++++--
 gdb/fbsd-nat.h | 4 ++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/gdb/fbsd-nat.c b/gdb/fbsd-nat.c
index 2bc7937a38b..934fdbad6ef 100644
--- a/gdb/fbsd-nat.c
+++ b/gdb/fbsd-nat.c
@@ -1138,6 +1138,8 @@ fbsd_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
 	    perror_with_name (request == PT_RESUME ?
 			      ("ptrace (PT_RESUME)") :
 			      ("ptrace (PT_SUSPEND)"));
+	  if (request == PT_RESUME)
+	    low_prepare_to_resume (tp);
 	}
     }
   else
@@ -1145,8 +1147,11 @@ fbsd_nat_target::resume (ptid_t ptid, int step, enum gdb_signal signo)
       /* If ptid is a wildcard, resume all matching threads (they won't run
 	 until the process is continued however).  */
       for (thread_info *tp : all_non_exited_threads (this, ptid))
-	if (ptrace (PT_RESUME, tp->ptid.lwp (), NULL, 0) == -1)
-	  perror_with_name (("ptrace (PT_RESUME)"));
+	{
+	  if (ptrace (PT_RESUME, tp->ptid.lwp (), NULL, 0) == -1)
+	    perror_with_name (("ptrace (PT_RESUME)"));
+	  low_prepare_to_resume (tp);
+	}
       ptid = inferior_ptid;
     }
 
diff --git a/gdb/fbsd-nat.h b/gdb/fbsd-nat.h
index 6028aebfccc..82f7ee47949 100644
--- a/gdb/fbsd-nat.h
+++ b/gdb/fbsd-nat.h
@@ -119,6 +119,10 @@ class fbsd_nat_target : public inf_ptrace_target
   virtual void low_delete_thread (thread_info *)
   {}
 
+  /* Hook to call prior to resuming a thread.  */
+  virtual void low_prepare_to_resume (thread_info *)
+  {}
+
 protected:
 
   void post_startup_inferior (ptid_t) override;
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* [PATCH v2 12/12] Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64.
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (10 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 11/12] fbsd-nat: Add a low_prepare_to_resume " John Baldwin
@ 2022-03-16 20:19 ` John Baldwin
  2022-03-30 15:23 ` [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support Luis Machado
  12 siblings, 0 replies; 19+ messages in thread
From: John Baldwin @ 2022-03-16 20:19 UTC (permalink / raw)
  To: gdb-patches

This shares aarch64-nat.c and nat/aarch64-hw-point.c with the Linux
native target.  Since FreeBSD writes all of the debug registers in one
ptrace op, use an unordered_set<> to track the "dirty" state for
threads rather than bitmasks of modified registers.
---
 gdb/NEWS               |   2 +
 gdb/aarch64-fbsd-nat.c | 260 ++++++++++++++++++++++++++++++++++++++++-
 gdb/configure.nat      |   3 +-
 3 files changed, 263 insertions(+), 2 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index dc2cac1871b..4d05feb4429 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,8 @@
 
 *** Changes since GDB 11
 
+* GDB now supports hardware watchpoints on FreeBSD/Aarch64.
+
 * Improved C++ template support
 
   GDB now treats functions/types involving C++ templates like it does function
diff --git a/gdb/aarch64-fbsd-nat.c b/gdb/aarch64-fbsd-nat.c
index e6ca1196139..99e2bf35276 100644
--- a/gdb/aarch64-fbsd-nat.c
+++ b/gdb/aarch64-fbsd-nat.c
@@ -18,24 +18,60 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "arch-utils.h"
+#include "inferior.h"
 #include "regcache.h"
 #include "target.h"
+#include "nat/aarch64-hw-point.h"
 
-#include <sys/types.h>
+#include <sys/param.h>
 #include <sys/ptrace.h>
+#include <machine/armreg.h>
 #include <machine/reg.h>
 
 #include "fbsd-nat.h"
 #include "aarch64-fbsd-tdep.h"
+#include "aarch64-nat.h"
 #include "inf-ptrace.h"
 
+#if __FreeBSD_version >= 1400005
+#define	HAVE_DBREG
+
+#include <unordered_set>
+#endif
+
+#ifdef HAVE_DBREG
+struct aarch64_fbsd_nat_target final
+  : public aarch64_nat_target<fbsd_nat_target>
+#else
 struct aarch64_fbsd_nat_target final : public fbsd_nat_target
+#endif
 {
   void fetch_registers (struct regcache *, int) override;
   void store_registers (struct regcache *, int) override;
+
+#ifdef HAVE_DBREG
+  /* Hardware breakpoints and watchpoints.  */
+  bool stopped_by_watchpoint () override;
+  bool stopped_data_address (CORE_ADDR *) override;
+  bool stopped_by_hw_breakpoint () override;
+  bool supports_stopped_by_hw_breakpoint () override;
+
+  void post_startup_inferior (ptid_t) override;
+  void post_attach (int pid) override;
+
+  void low_new_fork (ptid_t parent, pid_t child) override;
+  void low_delete_thread (thread_info *) override;
+  void low_prepare_to_resume (thread_info *) override;
+
+private:
+  void probe_debug_regs (int pid);
+  static bool debug_regs_probed;
+#endif
 };
 
 static aarch64_fbsd_nat_target the_aarch64_fbsd_nat_target;
+bool aarch64_fbsd_nat_target::debug_regs_probed;
 
 /* Fetch register REGNUM from the inferior.  If REGNUM is -1, do this
    for all registers.  */
@@ -63,9 +99,231 @@ aarch64_fbsd_nat_target::store_registers (struct regcache *regcache,
 				    PT_SETFPREGS, &aarch64_fbsd_fpregset);
 }
 
+#ifdef HAVE_DBREG
+/* Set of threads which need to update debug registers on next resume.  */
+
+static std::unordered_set<lwpid_t> aarch64_debug_pending_threads;
+
+/* Implement the "stopped_data_address" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::stopped_data_address (CORE_ADDR *addr_p)
+{
+  siginfo_t siginfo;
+  struct aarch64_debug_reg_state *state;
+
+  if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
+    return false;
+
+  /* This must be a hardware breakpoint.  */
+  if (siginfo.si_signo != SIGTRAP
+      || siginfo.si_code != TRAP_TRACE
+      || siginfo.si_trapno != EXCP_WATCHPT_EL0)
+    return false;
+
+  const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
+
+  /* Check if the address matches any watched address.  */
+  state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
+  return aarch64_stopped_data_address (state, addr_trap, addr_p);
+}
+
+/* Implement the "stopped_by_watchpoint" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::stopped_by_watchpoint ()
+{
+  CORE_ADDR addr;
+
+  return stopped_data_address (&addr);
+}
+
+/* Implement the "stopped_by_hw_breakpoint" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::stopped_by_hw_breakpoint ()
+{
+  siginfo_t siginfo;
+  struct aarch64_debug_reg_state *state;
+
+  if (!fbsd_nat_get_siginfo (inferior_ptid, &siginfo))
+    return false;
+
+  /* This must be a hardware breakpoint.  */
+  if (siginfo.si_signo != SIGTRAP
+      || siginfo.si_code != TRAP_TRACE
+      || siginfo.si_trapno != EXCP_WATCHPT_EL0)
+    return false;
+
+  return !stopped_by_watchpoint();
+}
+
+/* Implement the "supports_stopped_by_hw_breakpoint" target_ops method.  */
+
+bool
+aarch64_fbsd_nat_target::supports_stopped_by_hw_breakpoint ()
+{
+  return true;
+}
+
+/* Fetch the hardware debug register capability information.  */
+
+void
+aarch64_fbsd_nat_target::probe_debug_regs (int pid)
+{
+  if (!debug_regs_probed)
+    {
+      struct dbreg reg;
+
+      debug_regs_probed = true;
+      aarch64_num_bp_regs = 0;
+      aarch64_num_wp_regs = 0;
+
+      if (ptrace(PT_GETDBREGS, pid, (PTRACE_TYPE_ARG3) &reg, 0) == 0)
+	{
+	  switch (reg.db_debug_ver)
+	    {
+	    case AARCH64_DEBUG_ARCH_V8:
+	    case AARCH64_DEBUG_ARCH_V8_1:
+	    case AARCH64_DEBUG_ARCH_V8_2:
+	    case AARCH64_DEBUG_ARCH_V8_4:
+	      break;
+	    default:
+	      return;
+	    }
+
+	  aarch64_num_bp_regs = reg.db_nbkpts;
+	  if (aarch64_num_bp_regs > AARCH64_HBP_MAX_NUM)
+	    {
+	      warning (_("Unexpected number of hardware breakpoint registers"
+			 " reported by ptrace, got %d, expected %d."),
+		       aarch64_num_bp_regs, AARCH64_HBP_MAX_NUM);
+	      aarch64_num_bp_regs = AARCH64_HBP_MAX_NUM;
+	    }
+	  aarch64_num_wp_regs = reg.db_nwtpts;
+	  if (aarch64_num_wp_regs > AARCH64_HWP_MAX_NUM)
+	    {
+	      warning (_("Unexpected number of hardware watchpoint registers"
+			 " reported by ptrace, got %d, expected %d."),
+		       aarch64_num_wp_regs, AARCH64_HWP_MAX_NUM);
+	      aarch64_num_wp_regs = AARCH64_HWP_MAX_NUM;
+	    }
+	}
+    }
+}
+
+/* Implement the virtual inf_ptrace_target::post_startup_inferior method.  */
+
+void
+aarch64_fbsd_nat_target::post_startup_inferior (ptid_t ptid)
+{
+  aarch64_remove_debug_reg_state (ptid.pid ());
+  probe_debug_regs (ptid.pid ());
+  fbsd_nat_target::post_startup_inferior (ptid);
+}
+
+/* Implement the "post_attach" target_ops method.  */
+
+void
+aarch64_fbsd_nat_target::post_attach (int pid)
+{
+  aarch64_remove_debug_reg_state (pid);
+  probe_debug_regs (pid);
+  fbsd_nat_target::post_attach (pid);
+}
+
+/* Implement the virtual fbsd_nat_target::low_new_fork method.  */
+
+void
+aarch64_fbsd_nat_target::low_new_fork (ptid_t parent, pid_t child)
+{
+  struct aarch64_debug_reg_state *parent_state, *child_state;
+
+  /* If there is no parent state, no watchpoints nor breakpoints have
+     been set, so there is nothing to do.  */
+  parent_state = aarch64_lookup_debug_reg_state (parent.pid ());
+  if (parent_state == nullptr)
+    return;
+
+  /* The kernel clears debug registers in the new child process after
+     fork, but GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  */
+
+  child_state = aarch64_get_debug_reg_state (child);
+  *child_state = *parent_state;
+}
+
+/* Mark debug register state "dirty" for all threads belonging to the
+   current inferior.  */
+
+void
+aarch64_notify_debug_reg_change (ptid_t ptid,
+				 int is_watchpoint, unsigned int idx)
+{
+  for (thread_info *tp : current_inferior ()->non_exited_threads ())
+    {
+      if (tp->ptid.lwp_p ())
+	aarch64_debug_pending_threads.emplace (tp->ptid.lwp ());
+    }
+}
+
+/* Implement the virtual fbsd_nat_target::low_delete_thread method.  */
+
+void
+aarch64_fbsd_nat_target::low_delete_thread (thread_info *tp)
+{
+  gdb_assert(tp->ptid.lwp_p ());
+  aarch64_debug_pending_threads.erase (tp->ptid.lwp ());
+}
+
+/* Implement the virtual fbsd_nat_target::low_prepare_to_resume method.  */
+
+void
+aarch64_fbsd_nat_target::low_prepare_to_resume (thread_info *tp)
+{
+  gdb_assert(tp->ptid.lwp_p ());
+
+  if (aarch64_debug_pending_threads.erase (tp->ptid.lwp ()) == 0)
+    return;
+
+  struct aarch64_debug_reg_state *state =
+    aarch64_lookup_debug_reg_state (tp->ptid.pid ());
+  gdb_assert(state != nullptr);
+
+  struct dbreg reg;
+  memset (&reg, 0, sizeof(reg));
+  for (int i = 0; i < aarch64_num_bp_regs; i++)
+    {
+      reg.db_breakregs[i].dbr_addr = state->dr_addr_bp[i];
+      reg.db_breakregs[i].dbr_ctrl = state->dr_ctrl_bp[i];
+    }
+  for (int i = 0; i < aarch64_num_wp_regs; i++)
+    {
+      reg.db_watchregs[i].dbw_addr = state->dr_addr_wp[i];
+      reg.db_watchregs[i].dbw_ctrl = state->dr_ctrl_wp[i];
+    }
+  if (ptrace(PT_SETDBREGS, tp->ptid.lwp (), (PTRACE_TYPE_ARG3) &reg, 0) != 0)
+    error (_("Failed to set hardware debug registers"));
+}
+#else
+/* A stub that should never be called.  */
+void
+aarch64_notify_debug_reg_change (ptid_t ptid,
+				 int is_watchpoint, unsigned int idx)
+{
+  gdb_assert (true);
+}
+#endif
+
 void _initialize_aarch64_fbsd_nat ();
 void
 _initialize_aarch64_fbsd_nat ()
 {
+#ifdef HAVE_DBREG
+  aarch64_initialize_hw_point ();
+#endif
   add_inf_child_target (&the_aarch64_fbsd_nat_target);
 }
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 4f5850dd595..d219d6a960c 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -154,7 +154,8 @@ case ${gdb_host} in
 	case ${gdb_host_cpu} in
 	    aarch64)
 		# Host: FreeBSD/aarch64
-		NATDEPFILES="${NATDEPFILES} aarch64-fbsd-nat.o"
+		NATDEPFILES="${NATDEPFILES} aarch64-nat.o \
+		nat/aarch64-hw-point.o aarch64-fbsd-nat.o"
 		LOADLIBES=
 		;;
 	    arm)
-- 
2.34.1


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 08/12] aarch64: Add an aarch64_nat_target mixin class.
  2022-03-16 20:19 ` [PATCH v2 08/12] aarch64: Add an aarch64_nat_target mixin class John Baldwin
@ 2022-03-17 15:35   ` Luis Machado
  0 siblings, 0 replies; 19+ messages in thread
From: Luis Machado @ 2022-03-17 15:35 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

Hi,

LGTM from aarch64's side. Just a nit below, if you'd like to address it.

On 3/16/22 20:19, John Baldwin wrote:
> This class includes platform-independent target methods for hardware
> breakpoints and watchpoints using routines from nat/aarch64-hw-point.c.
> 
> stopped_data_address is not platform-independent since the FAR register
> holding the address for a breakpoint hit must be fetched in a
> platform-specific manner.  However, aarch64_stopped_data_address is
> provided as a helper routine which performs platform-independent
> validation given the value of the FAR register.
> 
> For tracking the per-process debug register mirror state, use an
> unordered_map indexed by pid as recently adopted in x86-nat.c rather
> than a manual linked-list.
> ---
>   gdb/aarch64-linux-nat.c | 356 +---------------------------------------
>   gdb/aarch64-nat.c       | 302 ++++++++++++++++++++++++++++++++++
>   gdb/aarch64-nat.h       | 109 ++++++++++++
>   gdb/configure.nat       |   2 +-
>   4 files changed, 418 insertions(+), 351 deletions(-)
>   create mode 100644 gdb/aarch64-nat.c
>   create mode 100644 gdb/aarch64-nat.h
> 
> diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
> index dd072d9315e..7bb82d17cc8 100644
> --- a/gdb/aarch64-linux-nat.c
> +++ b/gdb/aarch64-linux-nat.c
> @@ -27,6 +27,7 @@
>   #include "target-descriptions.h"
>   #include "auxv.h"
>   #include "gdbcmd.h"
> +#include "aarch64-nat.h"
>   #include "aarch64-tdep.h"
>   #include "aarch64-linux-tdep.h"
>   #include "aarch32-linux-nat.h"
> @@ -58,7 +59,8 @@
>   #define TRAP_HWBKPT 0x0004
>   #endif
>   
> -class aarch64_linux_nat_target final : public linux_nat_target
> +class aarch64_linux_nat_target final
> +  : public aarch64_nat_target<linux_nat_target>
>   {
>   public:
>     /* Add our register access methods.  */
> @@ -68,17 +70,8 @@ class aarch64_linux_nat_target final : public linux_nat_target
>     const struct target_desc *read_description () override;
>   
>     /* Add our hardware breakpoint and watchpoint implementation.  */
> -  int can_use_hw_breakpoint (enum bptype, int, int) override;
> -  int insert_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override;
> -  int remove_hw_breakpoint (struct gdbarch *, struct bp_target_info *) override;
> -  int region_ok_for_hw_watchpoint (CORE_ADDR, int) override;
> -  int insert_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
> -			 struct expression *) override;
> -  int remove_watchpoint (CORE_ADDR, int, enum target_hw_bp_type,
> -			 struct expression *) override;
>     bool stopped_by_watchpoint () override;
>     bool stopped_data_address (CORE_ADDR *) override;
> -  bool watchpoint_addr_within_range (CORE_ADDR, CORE_ADDR, int) override;
>   
>     int can_do_single_step () override;
>   
> @@ -118,103 +111,13 @@ class aarch64_linux_nat_target final : public linux_nat_target
>   
>   static aarch64_linux_nat_target the_aarch64_linux_nat_target;
>   
> -/* Per-process data.  We don't bind this to a per-inferior registry
> -   because of targets like x86 GNU/Linux that need to keep track of
> -   processes that aren't bound to any inferior (e.g., fork children,
> -   checkpoints).  */
> -
> -struct aarch64_process_info
> -{
> -  /* Linked list.  */
> -  struct aarch64_process_info *next;
> -
> -  /* The process identifier.  */
> -  pid_t pid;
> -
> -  /* Copy of aarch64 hardware debug registers.  */
> -  struct aarch64_debug_reg_state state;
> -};
> -
> -static struct aarch64_process_info *aarch64_process_list = NULL;
> -
> -/* Find process data for process PID.  */
> -
> -static struct aarch64_process_info *
> -aarch64_find_process_pid (pid_t pid)
> -{
> -  struct aarch64_process_info *proc;
> -
> -  for (proc = aarch64_process_list; proc; proc = proc->next)
> -    if (proc->pid == pid)
> -      return proc;
> -
> -  return NULL;
> -}
> -
> -/* Add process data for process PID.  Returns newly allocated info
> -   object.  */
> -
> -static struct aarch64_process_info *
> -aarch64_add_process (pid_t pid)
> -{
> -  struct aarch64_process_info *proc;
> -
> -  proc = XCNEW (struct aarch64_process_info);
> -  proc->pid = pid;
> -
> -  proc->next = aarch64_process_list;
> -  aarch64_process_list = proc;
> -
> -  return proc;
> -}
> -
> -/* Get data specific info for process PID, creating it if necessary.
> -   Never returns NULL.  */
> -
> -static struct aarch64_process_info *
> -aarch64_process_info_get (pid_t pid)
> -{
> -  struct aarch64_process_info *proc;
> -
> -  proc = aarch64_find_process_pid (pid);
> -  if (proc == NULL)
> -    proc = aarch64_add_process (pid);
> -
> -  return proc;
> -}
> -
>   /* Called whenever GDB is no longer debugging process PID.  It deletes
>      data structures that keep track of debug register state.  */
>   
>   void
>   aarch64_linux_nat_target::low_forget_process (pid_t pid)
>   {
> -  struct aarch64_process_info *proc, **proc_link;
> -
> -  proc = aarch64_process_list;
> -  proc_link = &aarch64_process_list;
> -
> -  while (proc != NULL)
> -    {
> -      if (proc->pid == pid)
> -	{
> -	  *proc_link = proc->next;
> -
> -	  xfree (proc);
> -	  return;
> -	}
> -
> -      proc_link = &proc->next;
> -      proc = *proc_link;
> -    }
> -}
> -
> -/* Get debug registers state for process PID.  */
> -
> -struct aarch64_debug_reg_state *
> -aarch64_get_debug_reg_state (pid_t pid)
> -{
> -  return &aarch64_process_info_get (pid)->state;
> +  aarch64_remove_debug_reg_state (pid);
>   }
>   
>   /* Fill GDB's register array with the general-purpose register values
> @@ -775,192 +678,12 @@ aarch64_linux_nat_target::low_siginfo_fixup (siginfo_t *native, gdb_byte *inf,
>     return false;
>   }
>   
> -/* Returns the number of hardware watchpoints of type TYPE that we can
> -   set.  Value is positive if we can set CNT watchpoints, zero if
> -   setting watchpoints of type TYPE is not supported, and negative if
> -   CNT is more than the maximum number of watchpoints of type TYPE
> -   that we can support.  TYPE is one of bp_hardware_watchpoint,
> -   bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint.
> -   CNT is the number of such watchpoints used so far (including this
> -   one).  OTHERTYPE is non-zero if other types of watchpoints are
> -   currently enabled.  */
> -
> -int
> -aarch64_linux_nat_target::can_use_hw_breakpoint (enum bptype type,
> -						 int cnt, int othertype)
> -{
> -  if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
> -      || type == bp_access_watchpoint || type == bp_watchpoint)
> -    {
> -      if (aarch64_num_wp_regs == 0)
> -	return 0;
> -    }
> -  else if (type == bp_hardware_breakpoint)
> -    {
> -      if (aarch64_num_bp_regs == 0)
> -	return 0;
> -    }
> -  else
> -    gdb_assert_not_reached ("unexpected breakpoint type");
> -
> -  /* We always return 1 here because we don't have enough information
> -     about possible overlap of addresses that they want to watch.  As an
> -     extreme example, consider the case where all the watchpoints watch
> -     the same address and the same region length: then we can handle a
> -     virtually unlimited number of watchpoints, due to debug register
> -     sharing implemented via reference counts.  */
> -  return 1;
> -}
> -
> -/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
> -   Return 0 on success, -1 on failure.  */
> -
> -int
> -aarch64_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
> -						struct bp_target_info *bp_tgt)
> -{
> -  int ret;
> -  CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
> -  int len;
> -  const enum target_hw_bp_type type = hw_execute;
> -  struct aarch64_debug_reg_state *state
> -    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> -
> -  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
> -
> -  if (show_debug_regs)
> -    fprintf_unfiltered
> -      (gdb_stdlog,
> -       "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
> -       (unsigned long) addr, len);
> -
> -  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */,
> -				   inferior_ptid, state);
> -
> -  if (show_debug_regs)
> -    {
> -      aarch64_show_debug_reg_state (state,
> -				    "insert_hw_breakpoint", addr, len, type);
> -    }
> -
> -  return ret;
> -}
> -
> -/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address.
> -   Return 0 on success, -1 on failure.  */
> -
> -int
> -aarch64_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
> -						struct bp_target_info *bp_tgt)
> -{
> -  int ret;
> -  CORE_ADDR addr = bp_tgt->placed_address;
> -  int len = 4;
> -  const enum target_hw_bp_type type = hw_execute;
> -  struct aarch64_debug_reg_state *state
> -    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> -
> -  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
> -
> -  if (show_debug_regs)
> -    fprintf_unfiltered
> -      (gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
> -       (unsigned long) addr, len);
> -
> -  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */,
> -				   inferior_ptid, state);
> -
> -  if (show_debug_regs)
> -    {
> -      aarch64_show_debug_reg_state (state,
> -				    "remove_hw_watchpoint", addr, len, type);
> -    }
> -
> -  return ret;
> -}
> -
> -/* Implement the "insert_watchpoint" target_ops method.
> -
> -   Insert a watchpoint to watch a memory region which starts at
> -   address ADDR and whose length is LEN bytes.  Watch memory accesses
> -   of the type TYPE.  Return 0 on success, -1 on failure.  */
> -
> -int
> -aarch64_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len,
> -					     enum target_hw_bp_type type,
> -					     struct expression *cond)
> -{
> -  int ret;
> -  struct aarch64_debug_reg_state *state
> -    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> -
> -  if (show_debug_regs)
> -    fprintf_unfiltered (gdb_stdlog,
> -			"insert_watchpoint on entry (addr=0x%08lx, len=%d)\n",
> -			(unsigned long) addr, len);
> -
> -  gdb_assert (type != hw_execute);
> -
> -  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */,
> -				   inferior_ptid, state);
> -
> -  if (show_debug_regs)
> -    {
> -      aarch64_show_debug_reg_state (state,
> -				    "insert_watchpoint", addr, len, type);
> -    }
> -
> -  return ret;
> -}
> -
> -/* Implement the "remove_watchpoint" target_ops method.
> -   Remove a watchpoint that watched the memory region which starts at
> -   address ADDR, whose length is LEN bytes, and for accesses of the
> -   type TYPE.  Return 0 on success, -1 on failure.  */
> -
> -int
> -aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
> -					     enum target_hw_bp_type type,
> -					     struct expression *cond)
> -{
> -  int ret;
> -  struct aarch64_debug_reg_state *state
> -    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> -
> -  if (show_debug_regs)
> -    fprintf_unfiltered (gdb_stdlog,
> -			"remove_watchpoint on entry (addr=0x%08lx, len=%d)\n",
> -			(unsigned long) addr, len);
> -
> -  gdb_assert (type != hw_execute);
> -
> -  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */,
> -				   inferior_ptid, state);
> -
> -  if (show_debug_regs)
> -    {
> -      aarch64_show_debug_reg_state (state,
> -				    "remove_watchpoint", addr, len, type);
> -    }
> -
> -  return ret;
> -}
> -
> -/* Implement the "region_ok_for_hw_watchpoint" target_ops method.  */
> -
> -int
> -aarch64_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
> -{
> -  return aarch64_region_ok_for_watchpoint (addr, len);
> -}
> -
>   /* Implement the "stopped_data_address" target_ops method.  */
>   
>   bool
>   aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
>   {
>     siginfo_t siginfo;
> -  int i;
>     struct aarch64_debug_reg_state *state;
>   
>     if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
> @@ -980,44 +703,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
>   
>     /* Check if the address matches any watched address.  */
>     state = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> -  for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
> -    {
> -      const unsigned int offset
> -	= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
> -      const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
> -      const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
> -      const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
> -      const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
> -
> -      if (state->dr_ref_count_wp[i]
> -	  && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
> -	  && addr_trap >= addr_watch_aligned
> -	  && addr_trap < addr_watch + len)
> -	{
> -	  /* ADDR_TRAP reports the first address of the memory range
> -	     accessed by the CPU, regardless of what was the memory
> -	     range watched.  Thus, a large CPU access that straddles
> -	     the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
> -	     ADDR_TRAP that is lower than the
> -	     ADDR_WATCH..ADDR_WATCH+LEN range.  E.g.:
> -
> -	     addr: |   4   |   5   |   6   |   7   |   8   |
> -				   |---- range watched ----|
> -		   |----------- range accessed ------------|
> -
> -	     In this case, ADDR_TRAP will be 4.
> -
> -	     To match a watchpoint known to GDB core, we must never
> -	     report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
> -	     range.  ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
> -	     positive on kernels older than 4.10.  See PR
> -	     external/20207.  */
> -	  *addr_p = addr_orig;
> -	  return true;
> -	}
> -    }
> -
> -  return false;
> +  return aarch64_stopped_data_address (state, addr_trap, addr_p);
>   }
>   
>   /* Implement the "stopped_by_watchpoint" target_ops method.  */
> @@ -1030,15 +716,6 @@ aarch64_linux_nat_target::stopped_by_watchpoint ()
>     return stopped_data_address (&addr);
>   }
>   
> -/* Implement the "watchpoint_addr_within_range" target_ops method.  */
> -
> -bool
> -aarch64_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr,
> -							CORE_ADDR start, int length)
> -{
> -  return start <= addr && start + length - 1 >= addr;
> -}
> -
>   /* Implement the "can_do_single_step" target_ops method.  */
>   
>   int
> @@ -1114,32 +791,11 @@ aarch64_linux_nat_target::store_memtags (CORE_ADDR address, size_t len,
>     return false;
>   }
>   
> -/* Define AArch64 maintenance commands.  */
> -
> -static void
> -add_show_debug_regs_command (void)
> -{
> -  /* A maintenance command to enable printing the internal DRi mirror
> -     variables.  */
> -  add_setshow_boolean_cmd ("show-debug-regs", class_maintenance,
> -			   &show_debug_regs, _("\
> -Set whether to show variables that mirror the AArch64 debug registers."), _("\
> -Show whether to show variables that mirror the AArch64 debug registers."), _("\
> -Use \"on\" to enable, \"off\" to disable.\n\
> -If enabled, the debug registers values are shown when GDB inserts\n\
> -or removes a hardware breakpoint or watchpoint, and when the inferior\n\
> -triggers a breakpoint or watchpoint."),
> -			   NULL,
> -			   NULL,
> -			   &maintenance_set_cmdlist,
> -			   &maintenance_show_cmdlist);
> -}
> -
>   void _initialize_aarch64_linux_nat ();
>   void
>   _initialize_aarch64_linux_nat ()
>   {
> -  add_show_debug_regs_command ();
> +  aarch64_initialize_hw_point ();
>   
>     /* Register the target.  */
>     linux_target = &the_aarch64_linux_nat_target;
> diff --git a/gdb/aarch64-nat.c b/gdb/aarch64-nat.c
> new file mode 100644
> index 00000000000..85cf7f2011a
> --- /dev/null
> +++ b/gdb/aarch64-nat.c
> @@ -0,0 +1,302 @@
> +/* Native-dependent code for AArch64.
> +
> +   Copyright (C) 2011-2022 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 "defs.h"
> +#include "gdbarch.h"
> +#include "inferior.h"
> +#include "cli/cli-cmds.h"
> +#include "aarch64-nat.h"
> +
> +#include <unordered_map>
> +
> +/* Hash table storing per-process data.  We don't bind this to a
> +   per-inferior registry because of targets like x86 GNU/Linux that
> +   need to keep track of processes that aren't bound to any inferior
> +   (e.g., fork children, checkpoints).  */
> +
> +static std::unordered_map<pid_t, aarch64_debug_reg_state>
> +aarch64_debug_process_state;
> +
> +/* See aarch64-nat.h.  */
> +
> +struct aarch64_debug_reg_state *
> +aarch64_lookup_debug_reg_state (pid_t pid)
> +{
> +  auto it = aarch64_debug_process_state.find (pid);
> +  if (it != aarch64_debug_process_state.end ())
> +    return &it->second;
> +
> +  return nullptr;
> +}
> +
> +/* See aarch64-nat.h.  */
> +
> +struct aarch64_debug_reg_state *
> +aarch64_get_debug_reg_state (pid_t pid)
> +{
> +  return &aarch64_debug_process_state[pid];
> +}
> +
> +/* See aarch64-nat.h.  */
> +
> +void
> +aarch64_remove_debug_reg_state (pid_t pid)
> +{
> +  aarch64_debug_process_state.erase (pid);
> +}
> +
> +/* Returns the number of hardware watchpoints of type TYPE that we can
> +   set.  Value is positive if we can set CNT watchpoints, zero if
> +   setting watchpoints of type TYPE is not supported, and negative if
> +   CNT is more than the maximum number of watchpoints of type TYPE
> +   that we can support.  TYPE is one of bp_hardware_watchpoint,
> +   bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint.
> +   CNT is the number of such watchpoints used so far (including this
> +   one).  OTHERTYPE is non-zero if other types of watchpoints are
> +   currently enabled.  */
> +
> +int
> +aarch64_can_use_hw_breakpoint (enum bptype type, int cnt, int othertype)
> +{
> +  if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
> +      || type == bp_access_watchpoint || type == bp_watchpoint)
> +    {
> +      if (aarch64_num_wp_regs == 0)
> +	return 0;
> +    }
> +  else if (type == bp_hardware_breakpoint)
> +    {
> +      if (aarch64_num_bp_regs == 0)
> +	return 0;
> +    }
> +  else
> +    gdb_assert_not_reached ("unexpected breakpoint type");
> +
> +  /* We always return 1 here because we don't have enough information
> +     about possible overlap of addresses that they want to watch.  As an
> +     extreme example, consider the case where all the watchpoints watch
> +     the same address and the same region length: then we can handle a
> +     virtually unlimited number of watchpoints, due to debug register
> +     sharing implemented via reference counts.  */
> +  return 1;
> +}
> +
> +/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
> +   Return 0 on success, -1 on failure.  */
> +
> +int
> +aarch64_insert_hw_breakpoint (struct gdbarch *gdbarch,
> +			      struct bp_target_info *bp_tgt)
> +{
> +  int ret;
> +  CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
> +  int len;
> +  const enum target_hw_bp_type type = hw_execute;
> +  struct aarch64_debug_reg_state *state
> +    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> +
> +  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
> +
> +  if (show_debug_regs)
> +    fprintf_unfiltered
> +      (gdb_stdlog,
> +       "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
> +       (unsigned long) addr, len);
> +
> +  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */,
> +				   inferior_ptid, state);
> +
> +  if (show_debug_regs)
> +    {
> +      aarch64_show_debug_reg_state (state,
> +				    "insert_hw_breakpoint", addr, len, type);
> +    }
> +
> +  return ret;
> +}
> +
> +/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address.
> +   Return 0 on success, -1 on failure.  */
> +
> +int
> +aarch64_remove_hw_breakpoint (struct gdbarch *gdbarch,
> +			      struct bp_target_info *bp_tgt)
> +{
> +  int ret;
> +  CORE_ADDR addr = bp_tgt->placed_address;
> +  int len = 4;
> +  const enum target_hw_bp_type type = hw_execute;
> +  struct aarch64_debug_reg_state *state
> +    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> +
> +  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
> +
> +  if (show_debug_regs)
> +    fprintf_unfiltered
> +      (gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
> +       (unsigned long) addr, len);
> +
> +  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */,
> +				   inferior_ptid, state);
> +
> +  if (show_debug_regs)
> +    {
> +      aarch64_show_debug_reg_state (state,
> +				    "remove_hw_watchpoint", addr, len, type);
> +    }
> +
> +  return ret;
> +}
> +
> +/* Insert a watchpoint to watch a memory region which starts at
> +   address ADDR and whose length is LEN bytes.  Watch memory accesses
> +   of the type TYPE.  Return 0 on success, -1 on failure.  */
> +
> +int
> +aarch64_insert_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type,
> +			   struct expression *cond)
> +{
> +  int ret;
> +  struct aarch64_debug_reg_state *state
> +    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> +
> +  if (show_debug_regs)
> +    fprintf_unfiltered (gdb_stdlog,
> +			"insert_watchpoint on entry (addr=0x%08lx, len=%d)\n",
> +			(unsigned long) addr, len);
> +
> +  gdb_assert (type != hw_execute);
> +
> +  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */,
> +				   inferior_ptid, state);
> +
> +  if (show_debug_regs)
> +    {
> +      aarch64_show_debug_reg_state (state,
> +				    "insert_watchpoint", addr, len, type);
> +    }
> +
> +  return ret;
> +}
> +
> +/* Remove a watchpoint that watched the memory region which starts at
> +   address ADDR, whose length is LEN bytes, and for accesses of the
> +   type TYPE.  Return 0 on success, -1 on failure.  */
> +
> +int
> +aarch64_remove_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type,
> +			   struct expression *cond)
> +{
> +  int ret;
> +  struct aarch64_debug_reg_state *state
> +    = aarch64_get_debug_reg_state (inferior_ptid.pid ());
> +
> +  if (show_debug_regs)
> +    fprintf_unfiltered (gdb_stdlog,
> +			"remove_watchpoint on entry (addr=0x%08lx, len=%d)\n",
> +			(unsigned long) addr, len);
> +
> +  gdb_assert (type != hw_execute);
> +
> +  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */,
> +				   inferior_ptid, state);
> +
> +  if (show_debug_regs)
> +    {
> +      aarch64_show_debug_reg_state (state,
> +				    "remove_watchpoint", addr, len, type);
> +    }
> +
> +  return ret;
> +}
> +
> +/* See aarch64-nat.h.  */
> +
> +bool
> +aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
> +			      CORE_ADDR addr_trap, CORE_ADDR *addr_p)
> +{
> +  int i;

Nit: We could declare i in the for loop below.

> +
> +  for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
> +    {
> +      const unsigned int offset
> +	= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
> +      const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
> +      const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
> +      const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
> +      const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
> +
> +      if (state->dr_ref_count_wp[i]
> +	  && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
> +	  && addr_trap >= addr_watch_aligned
> +	  && addr_trap < addr_watch + len)
> +	{
> +	  /* ADDR_TRAP reports the first address of the memory range
> +	     accessed by the CPU, regardless of what was the memory
> +	     range watched.  Thus, a large CPU access that straddles
> +	     the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
> +	     ADDR_TRAP that is lower than the
> +	     ADDR_WATCH..ADDR_WATCH+LEN range.  E.g.:
> +
> +	     addr: |   4   |   5   |   6   |   7   |   8   |
> +				   |---- range watched ----|
> +		   |----------- range accessed ------------|
> +
> +	     In this case, ADDR_TRAP will be 4.
> +
> +	     To match a watchpoint known to GDB core, we must never
> +	     report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
> +	     range.  ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
> +	     positive on kernels older than 4.10.  See PR
> +	     external/20207.  */
> +	  *addr_p = addr_orig;
> +	  return true;
> +	}
> +    }
> +
> +  return false;
> +}
> +
> +/* Define AArch64 maintenance commands.  */
> +
> +static void
> +add_show_debug_regs_command (void)
> +{
> +  /* A maintenance command to enable printing the internal DRi mirror
> +     variables.  */
> +  add_setshow_boolean_cmd ("show-debug-regs", class_maintenance,
> +			   &show_debug_regs, _("\
> +Set whether to show variables that mirror the AArch64 debug registers."), _("\
> +Show whether to show variables that mirror the AArch64 debug registers."), _("\
> +Use \"on\" to enable, \"off\" to disable.\n\
> +If enabled, the debug registers values are shown when GDB inserts\n\
> +or removes a hardware breakpoint or watchpoint, and when the inferior\n\
> +triggers a breakpoint or watchpoint."),
> +			   NULL,
> +			   NULL,
> +			   &maintenance_set_cmdlist,
> +			   &maintenance_show_cmdlist);
> +}
> +
> +void
> +aarch64_initialize_hw_point ()
> +{
> +  add_show_debug_regs_command ();
> +}
> diff --git a/gdb/aarch64-nat.h b/gdb/aarch64-nat.h
> new file mode 100644
> index 00000000000..56e720f02ee
> --- /dev/null
> +++ b/gdb/aarch64-nat.h
> @@ -0,0 +1,109 @@
> +/* Native-dependent code for AArch64.
> +
> +   Copyright (C) 2011-2022 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/>.  */
> +
> +#ifndef AARCH64_NAT_H
> +#define AARCH64_NAT_H
> +
> +#include "breakpoint.h"
> +#include "nat/aarch64-hw-point.h"
> +#include "target.h"
> +
> +/* Hardware-assisted breakpoints and watchpoints.  */
> +
> +/* Initialize platform-independent state for hardware-assisted
> +   breakpoints and watchpoints.  */
> +
> +void aarch64_initialize_hw_point ();
> +
> +/* Return the debug register state for process PID.  If no existing
> +   state is found for this process, return nullptr.  */
> +
> +struct aarch64_debug_reg_state *aarch64_lookup_debug_reg_state (pid_t pid);
> +
> +/* Return the debug register state for process PID.  If no existing
> +   state is found for this process, create new state.  */
> +
> +struct aarch64_debug_reg_state *aarch64_get_debug_reg_state (pid_t pid);
> +
> +/* Remove any existing per-process debug state for process PID.  */
> +
> +void aarch64_remove_debug_reg_state (pid_t pid);
> +
> +/* Helper for the "stopped_data_address" target method.  Returns TRUE
> +   if a hardware watchpoint trap at ADDR_TRAP matches a set
> +   watchpoint.  The address of the matched watchpoint is returned in
> +   *ADDR_P.  */
> +
> +bool aarch64_stopped_data_address (const struct aarch64_debug_reg_state *state,
> +				   CORE_ADDR addr_trap, CORE_ADDR *addr_p);
> +
> +/* Helper functions used by aarch64_nat_target below.  See their
> +   definitions.  */
> +
> +int aarch64_can_use_hw_breakpoint (enum bptype type, int cnt, int othertype);
> +int aarch64_insert_watchpoint (CORE_ADDR addr, int len,
> +			       enum target_hw_bp_type type,
> +			       struct expression *cond);
> +int aarch64_remove_watchpoint (CORE_ADDR addr, int len,
> +			       enum target_hw_bp_type type,
> +			       struct expression *cond);
> +int aarch64_insert_hw_breakpoint (struct gdbarch *gdbarch,
> +				  struct bp_target_info *bp_tgt);
> +int aarch64_remove_hw_breakpoint (struct gdbarch *gdbarch,
> +				  struct bp_target_info *bp_tgt);
> +int aarch64_stopped_by_hw_breakpoint ();
> +
> +/* Convenience template mixin used to add aarch64 watchpoints support to a
> +   target.  */
> +
> +template <typename BaseTarget>
> +struct aarch64_nat_target : public BaseTarget
> +{
> +  /* Hook in common aarch64 hardware watchpoints/breakpoints support.  */
> +
> +  int can_use_hw_breakpoint (enum bptype type, int cnt, int othertype) override
> +  { return aarch64_can_use_hw_breakpoint (type, cnt, othertype); }
> +
> +  int region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) override
> +  { return aarch64_region_ok_for_watchpoint (addr, len); }
> +
> +  int insert_watchpoint (CORE_ADDR addr, int len,
> +			 enum target_hw_bp_type type,
> +			 struct expression *cond) override
> +  { return aarch64_insert_watchpoint (addr, len, type, cond); }
> +
> +  int remove_watchpoint (CORE_ADDR addr, int len,
> +			 enum target_hw_bp_type type,
> +			 struct expression *cond) override
> +  { return aarch64_remove_watchpoint (addr, len, type, cond); }
> +
> +  int insert_hw_breakpoint (struct gdbarch *gdbarch,
> +			    struct bp_target_info *bp_tgt) override
> +  { return aarch64_insert_hw_breakpoint (gdbarch, bp_tgt); }
> +
> +  int remove_hw_breakpoint (struct gdbarch *gdbarch,
> +			    struct bp_target_info *bp_tgt) override
> +  { return aarch64_remove_hw_breakpoint (gdbarch, bp_tgt); }
> +
> +  bool watchpoint_addr_within_range (CORE_ADDR addr, CORE_ADDR start,
> +				     int length) override
> +  { return start <= addr && start + length - 1 >= addr; }
> +};
> +
> +#endif /* AARCH64_NAT_H */
> diff --git a/gdb/configure.nat b/gdb/configure.nat
> index ad6d35babc2..4f5850dd595 100644
> --- a/gdb/configure.nat
> +++ b/gdb/configure.nat
> @@ -233,7 +233,7 @@ case ${gdb_host} in
>   	case ${gdb_host_cpu} in
>   	    aarch64)
>   		#  Host: AArch64 based machine running GNU/Linux
> -		NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
> +		NATDEPFILES="${NATDEPFILES} aarch64-nat.o aarch64-linux-nat.o \
>   		aarch32-linux-nat.o nat/aarch64-hw-point.o \
>   		nat/aarch64-linux-hw-point.o \
>   		nat/aarch64-linux.o \


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 07/12] nat: Split out platform-independent aarch64 debug register support.
  2022-03-16 20:19 ` [PATCH v2 07/12] nat: Split out platform-independent aarch64 debug register support John Baldwin
@ 2022-03-17 15:37   ` Luis Machado
  0 siblings, 0 replies; 19+ messages in thread
From: Luis Machado @ 2022-03-17 15:37 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

Hi John,

LGTM as well. That's a good refactoring.

Thanks.

On 3/16/22 20:19, John Baldwin wrote:
> Move non-Linux-specific support for hardware break/watchpoints from
> nat/aarch64-linux-hw-point.c to nat/aarch64-hw-point.c.  Changes beyond
> a simple split of the code are:
> 
> - aarch64_linux_region_ok_for_watchpoint and
>    aarch64_linux_any_set_debug_regs_state renamed to drop linux_ as they
>    are not platform specific.
> 
> - Platforms must implement the aarch64_notify_debug_reg_change function
>    which is invoked from the platform-independent code when a debug
>    register changes for a given debug register state.  This does not
>    use the indirection of a 'low' structure as is done for x86.
>    Possibly this function should be renamed to aarch64_dr_low_notify
>    or some such if we wish to use aarch64_dr_low_* as a namespace for
>    platform-specific low routines?
> 
> - The handling for kernel_supports_any_contiguous_range is not
>    pristine.  For non-Linux it is simply defined to true.  Some uses
>    of this could perhaps be implemented as new 'low' routines for the
>    various places that check it instead?
> 
> - Pass down ptid into aarch64_handle_breakpoint and aarch64_handle_watchpoint
>    rather than using current_lwp_ptid which is only defined on Linux.
>    In addition, pass the ptid on to aarch64_notify_debug_reg_change instead
>    of the unused state argument.
> ---
>   gdb/aarch64-linux-nat.c          |  14 +-
>   gdb/configure.nat                |   3 +-
>   gdb/nat/aarch64-hw-point.c       | 624 +++++++++++++++++++++++++++++++
>   gdb/nat/aarch64-hw-point.h       | 126 +++++++
>   gdb/nat/aarch64-linux-hw-point.c | 605 +-----------------------------
>   gdb/nat/aarch64-linux-hw-point.h | 105 +-----
>   gdb/nat/aarch64-linux.c          |   4 +-
>   gdbserver/configure.srv          |   1 +
>   gdbserver/linux-aarch64-low.cc   |  13 +-
>   9 files changed, 787 insertions(+), 708 deletions(-)
>   create mode 100644 gdb/nat/aarch64-hw-point.c
>   create mode 100644 gdb/nat/aarch64-hw-point.h
> 
> diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
> index db764975207..dd072d9315e 100644
> --- a/gdb/aarch64-linux-nat.c
> +++ b/gdb/aarch64-linux-nat.c
> @@ -834,7 +834,8 @@ aarch64_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
>          "insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
>          (unsigned long) addr, len);
>   
> -  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */, state);
> +  ret = aarch64_handle_breakpoint (type, addr, len, 1 /* is_insert */,
> +				   inferior_ptid, state);
>   
>     if (show_debug_regs)
>       {
> @@ -866,7 +867,8 @@ aarch64_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
>         (gdb_stdlog, "remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
>          (unsigned long) addr, len);
>   
> -  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */, state);
> +  ret = aarch64_handle_breakpoint (type, addr, len, 0 /* is_insert */,
> +				   inferior_ptid, state);
>   
>     if (show_debug_regs)
>       {
> @@ -899,7 +901,8 @@ aarch64_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len,
>   
>     gdb_assert (type != hw_execute);
>   
> -  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */, state);
> +  ret = aarch64_handle_watchpoint (type, addr, len, 1 /* is_insert */,
> +				   inferior_ptid, state);
>   
>     if (show_debug_regs)
>       {
> @@ -931,7 +934,8 @@ aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
>   
>     gdb_assert (type != hw_execute);
>   
> -  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */, state);
> +  ret = aarch64_handle_watchpoint (type, addr, len, 0 /* is_insert */,
> +				   inferior_ptid, state);
>   
>     if (show_debug_regs)
>       {
> @@ -947,7 +951,7 @@ aarch64_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
>   int
>   aarch64_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
>   {
> -  return aarch64_linux_region_ok_for_watchpoint (addr, len);
> +  return aarch64_region_ok_for_watchpoint (addr, len);
>   }
>   
>   /* Implement the "stopped_data_address" target_ops method.  */
> diff --git a/gdb/configure.nat b/gdb/configure.nat
> index 92ad4a6522b..ad6d35babc2 100644
> --- a/gdb/configure.nat
> +++ b/gdb/configure.nat
> @@ -234,7 +234,8 @@ case ${gdb_host} in
>   	    aarch64)
>   		#  Host: AArch64 based machine running GNU/Linux
>   		NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
> -		aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
> +		aarch32-linux-nat.o nat/aarch64-hw-point.o \
> +		nat/aarch64-linux-hw-point.o \
>   		nat/aarch64-linux.o \
>   		nat/aarch64-sve-linux-ptrace.o \
>   		nat/aarch64-mte-linux-ptrace.o"
> diff --git a/gdb/nat/aarch64-hw-point.c b/gdb/nat/aarch64-hw-point.c
> new file mode 100644
> index 00000000000..f0418f7eef8
> --- /dev/null
> +++ b/gdb/nat/aarch64-hw-point.c
> @@ -0,0 +1,624 @@
> +/* Copyright (C) 2009-2022 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 "gdbsupport/common-defs.h"
> +#include "gdbsupport/break-common.h"
> +#include "gdbsupport/common-regcache.h"
> +#include "aarch64-hw-point.h"
> +
> +#ifdef __linux__
> +/* For kernel_supports_any_contiguous_range.  */
> +#include "aarch64-linux-hw-point.h"
> +#else
> +#define	kernel_supports_any_contiguous_range	true
> +#endif
> +
> +/* Number of hardware breakpoints/watchpoints the target supports.
> +   They are initialized with values obtained via ptrace.  */
> +
> +int aarch64_num_bp_regs;
> +int aarch64_num_wp_regs;
> +
> +/* Return starting byte 0..7 incl. of a watchpoint encoded by CTRL.  */
> +
> +unsigned int
> +aarch64_watchpoint_offset (unsigned int ctrl)
> +{
> +  uint8_t mask = DR_CONTROL_MASK (ctrl);
> +  unsigned retval;
> +
> +  /* Shift out bottom zeros.  */
> +  for (retval = 0; mask && (mask & 1) == 0; ++retval)
> +    mask >>= 1;
> +
> +  return retval;
> +}
> +
> +/* Utility function that returns the length in bytes of a watchpoint
> +   according to the content of a hardware debug control register CTRL.
> +   Any contiguous range of bytes in CTRL is supported.  The returned
> +   value can be between 0..8 (inclusive).  */
> +
> +unsigned int
> +aarch64_watchpoint_length (unsigned int ctrl)
> +{
> +  uint8_t mask = DR_CONTROL_MASK (ctrl);
> +  unsigned retval;
> +
> +  /* Shift out bottom zeros.  */
> +  mask >>= aarch64_watchpoint_offset (ctrl);
> +
> +  /* Count bottom ones.  */
> +  for (retval = 0; (mask & 1) != 0; ++retval)
> +    mask >>= 1;
> +
> +  if (mask != 0)
> +    error (_("Unexpected hardware watchpoint length register value 0x%x"),
> +	   DR_CONTROL_MASK (ctrl));
> +
> +  return retval;
> +}
> +
> +/* Given the hardware breakpoint or watchpoint type TYPE and its
> +   length LEN, return the expected encoding for a hardware
> +   breakpoint/watchpoint control register.  */
> +
> +static unsigned int
> +aarch64_point_encode_ctrl_reg (enum target_hw_bp_type type, int offset, int len)
> +{
> +  unsigned int ctrl, ttype;
> +
> +  gdb_assert (offset == 0 || kernel_supports_any_contiguous_range);
> +  gdb_assert (offset + len <= AARCH64_HWP_MAX_LEN_PER_REG);
> +
> +  /* type */
> +  switch (type)
> +    {
> +    case hw_write:
> +      ttype = 2;
> +      break;
> +    case hw_read:
> +      ttype = 1;
> +      break;
> +    case hw_access:
> +      ttype = 3;
> +      break;
> +    case hw_execute:
> +      ttype = 0;
> +      break;
> +    default:
> +      perror_with_name (_("Unrecognized breakpoint/watchpoint type"));
> +    }
> +
> +  ctrl = ttype << 3;
> +
> +  /* offset and length bitmask */
> +  ctrl |= ((1 << len) - 1) << (5 + offset);
> +  /* enabled at el0 */
> +  ctrl |= (2 << 1) | 1;
> +
> +  return ctrl;
> +}
> +
> +/* Addresses to be written to the hardware breakpoint and watchpoint
> +   value registers need to be aligned; the alignment is 4-byte and
> +   8-type respectively.  Linux kernel rejects any non-aligned address
> +   it receives from the related ptrace call.  Furthermore, the kernel
> +   currently only supports the following Byte Address Select (BAS)
> +   values: 0x1, 0x3, 0xf and 0xff, which means that for a hardware
> +   watchpoint to be accepted by the kernel (via ptrace call), its
> +   valid length can only be 1 byte, 2 bytes, 4 bytes or 8 bytes.
> +   Despite these limitations, the unaligned watchpoint is supported in
> +   this port.
> +
> +   Return 0 for any non-compliant ADDR and/or LEN; return 1 otherwise.  */
> +
> +static int
> +aarch64_point_is_aligned (ptid_t ptid, int is_watchpoint, CORE_ADDR addr,
> +			  int len)
> +{
> +  unsigned int alignment = 0;
> +
> +  if (is_watchpoint)
> +    alignment = AARCH64_HWP_ALIGNMENT;
> +  else
> +    {
> +      struct regcache *regcache
> +	= get_thread_regcache_for_ptid (ptid);
> +
> +      /* Set alignment to 2 only if the current process is 32-bit,
> +	 since thumb instruction can be 2-byte aligned.  Otherwise, set
> +	 alignment to AARCH64_HBP_ALIGNMENT.  */
> +      if (regcache_register_size (regcache, 0) == 8)
> +	alignment = AARCH64_HBP_ALIGNMENT;
> +      else
> +	alignment = 2;
> +    }
> +
> +  if (addr & (alignment - 1))
> +    return 0;
> +
> +  if ((!kernel_supports_any_contiguous_range
> +       && len != 8 && len != 4 && len != 2 && len != 1)
> +      || (kernel_supports_any_contiguous_range
> +	  && (len < 1 || len > 8)))
> +    return 0;
> +
> +  return 1;
> +}
> +
> +/* Given the (potentially unaligned) watchpoint address in ADDR and
> +   length in LEN, return the aligned address, offset from that base
> +   address, and aligned length in *ALIGNED_ADDR_P, *ALIGNED_OFFSET_P
> +   and *ALIGNED_LEN_P, respectively.  The returned values will be
> +   valid values to write to the hardware watchpoint value and control
> +   registers.
> +
> +   The given watchpoint may get truncated if more than one hardware
> +   register is needed to cover the watched region.  *NEXT_ADDR_P
> +   and *NEXT_LEN_P, if non-NULL, will return the address and length
> +   of the remaining part of the watchpoint (which can be processed
> +   by calling this routine again to generate another aligned address,
> +   offset and length tuple.
> +
> +   Essentially, unaligned watchpoint is achieved by minimally
> +   enlarging the watched area to meet the alignment requirement, and
> +   if necessary, splitting the watchpoint over several hardware
> +   watchpoint registers.
> +
> +   On kernels that predate the support for Byte Address Select (BAS)
> +   in the hardware watchpoint control register, the offset from the
> +   base address is always zero, and so in that case the trade-off is
> +   that there will be false-positive hits for the read-type or the
> +   access-type hardware watchpoints; for the write type, which is more
> +   commonly used, there will be no such issues, as the higher-level
> +   breakpoint management in gdb always examines the exact watched
> +   region for any content change, and transparently resumes a thread
> +   from a watchpoint trap if there is no change to the watched region.
> +
> +   Another limitation is that because the watched region is enlarged,
> +   the watchpoint fault address discovered by
> +   aarch64_stopped_data_address may be outside of the original watched
> +   region, especially when the triggering instruction is accessing a
> +   larger region.  When the fault address is not within any known
> +   range, watchpoints_triggered in gdb will get confused, as the
> +   higher-level watchpoint management is only aware of original
> +   watched regions, and will think that some unknown watchpoint has
> +   been triggered.  To prevent such a case,
> +   aarch64_stopped_data_address implementations in gdb and gdbserver
> +   try to match the trapped address with a watched region, and return
> +   an address within the latter. */
> +
> +static void
> +aarch64_align_watchpoint (CORE_ADDR addr, int len, CORE_ADDR *aligned_addr_p,
> +			  int *aligned_offset_p, int *aligned_len_p,
> +			  CORE_ADDR *next_addr_p, int *next_len_p,
> +			  CORE_ADDR *next_addr_orig_p)
> +{
> +  int aligned_len;
> +  unsigned int offset, aligned_offset;
> +  CORE_ADDR aligned_addr;
> +  const unsigned int alignment = AARCH64_HWP_ALIGNMENT;
> +  const unsigned int max_wp_len = AARCH64_HWP_MAX_LEN_PER_REG;
> +
> +  /* As assumed by the algorithm.  */
> +  gdb_assert (alignment == max_wp_len);
> +
> +  if (len <= 0)
> +    return;
> +
> +  /* The address put into the hardware watchpoint value register must
> +     be aligned.  */
> +  offset = addr & (alignment - 1);
> +  aligned_addr = addr - offset;
> +  aligned_offset
> +    = kernel_supports_any_contiguous_range ? addr & (alignment - 1) : 0;
> +
> +  gdb_assert (offset >= 0 && offset < alignment);
> +  gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
> +  gdb_assert (offset + len > 0);
> +
> +  if (offset + len >= max_wp_len)
> +    {
> +      /* Need more than one watchpoint register; truncate at the
> +	 alignment boundary.  */
> +      aligned_len
> +	= max_wp_len - (kernel_supports_any_contiguous_range ? offset : 0);
> +      len -= (max_wp_len - offset);
> +      addr += (max_wp_len - offset);
> +      gdb_assert ((addr & (alignment - 1)) == 0);
> +    }
> +  else
> +    {
> +      /* Find the smallest valid length that is large enough to
> +	 accommodate this watchpoint.  */
> +      static const unsigned char
> +	aligned_len_array[AARCH64_HWP_MAX_LEN_PER_REG] =
> +	{ 1, 2, 4, 4, 8, 8, 8, 8 };
> +
> +      aligned_len = (kernel_supports_any_contiguous_range
> +		     ? len : aligned_len_array[offset + len - 1]);
> +      addr += len;
> +      len = 0;
> +    }
> +
> +  if (aligned_addr_p)
> +    *aligned_addr_p = aligned_addr;
> +  if (aligned_offset_p)
> +    *aligned_offset_p = aligned_offset;
> +  if (aligned_len_p)
> +    *aligned_len_p = aligned_len;
> +  if (next_addr_p)
> +    *next_addr_p = addr;
> +  if (next_len_p)
> +    *next_len_p = len;
> +  if (next_addr_orig_p)
> +    *next_addr_orig_p = align_down (*next_addr_orig_p + alignment, alignment);
> +}
> +
> +/* Record the insertion of one breakpoint/watchpoint, as represented
> +   by ADDR and CTRL, in the process' arch-specific data area *STATE.  */
> +
> +static int
> +aarch64_dr_state_insert_one_point (ptid_t ptid,
> +				   struct aarch64_debug_reg_state *state,
> +				   enum target_hw_bp_type type,
> +				   CORE_ADDR addr, int offset, int len,
> +				   CORE_ADDR addr_orig)
> +{
> +  int i, idx, num_regs, is_watchpoint;
> +  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
> +  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
> +
> +  /* Set up state pointers.  */
> +  is_watchpoint = (type != hw_execute);
> +  gdb_assert (aarch64_point_is_aligned (ptid, is_watchpoint, addr, len));
> +  if (is_watchpoint)
> +    {
> +      num_regs = aarch64_num_wp_regs;
> +      dr_addr_p = state->dr_addr_wp;
> +      dr_addr_orig_p = state->dr_addr_orig_wp;
> +      dr_ctrl_p = state->dr_ctrl_wp;
> +      dr_ref_count = state->dr_ref_count_wp;
> +    }
> +  else
> +    {
> +      num_regs = aarch64_num_bp_regs;
> +      dr_addr_p = state->dr_addr_bp;
> +      dr_addr_orig_p = nullptr;
> +      dr_ctrl_p = state->dr_ctrl_bp;
> +      dr_ref_count = state->dr_ref_count_bp;
> +    }
> +
> +  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
> +
> +  /* Find an existing or free register in our cache.  */
> +  idx = -1;
> +  for (i = 0; i < num_regs; ++i)
> +    {
> +      if ((dr_ctrl_p[i] & 1) == 0)
> +	{
> +	  gdb_assert (dr_ref_count[i] == 0);
> +	  idx = i;
> +	  /* no break; continue hunting for an exising one.  */
> +	}
> +      else if (dr_addr_p[i] == addr
> +	       && (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
> +	       && dr_ctrl_p[i] == ctrl)
> +	{
> +	  gdb_assert (dr_ref_count[i] != 0);
> +	  idx = i;
> +	  break;
> +	}
> +    }
> +
> +  /* No space.  */
> +  if (idx == -1)
> +    return -1;
> +
> +  /* Update our cache.  */
> +  if ((dr_ctrl_p[idx] & 1) == 0)
> +    {
> +      /* new entry */
> +      dr_addr_p[idx] = addr;
> +      if (dr_addr_orig_p != nullptr)
> +	dr_addr_orig_p[idx] = addr_orig;
> +      dr_ctrl_p[idx] = ctrl;
> +      dr_ref_count[idx] = 1;
> +      /* Notify the change.  */
> +      aarch64_notify_debug_reg_change (ptid, is_watchpoint, idx);
> +    }
> +  else
> +    {
> +      /* existing entry */
> +      dr_ref_count[idx]++;
> +    }
> +
> +  return 0;
> +}
> +
> +/* Record the removal of one breakpoint/watchpoint, as represented by
> +   ADDR and CTRL, in the process' arch-specific data area *STATE.  */
> +
> +static int
> +aarch64_dr_state_remove_one_point (ptid_t ptid,
> +				   struct aarch64_debug_reg_state *state,
> +				   enum target_hw_bp_type type,
> +				   CORE_ADDR addr, int offset, int len,
> +				   CORE_ADDR addr_orig)
> +{
> +  int i, num_regs, is_watchpoint;
> +  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
> +  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
> +
> +  /* Set up state pointers.  */
> +  is_watchpoint = (type != hw_execute);
> +  if (is_watchpoint)
> +    {
> +      num_regs = aarch64_num_wp_regs;
> +      dr_addr_p = state->dr_addr_wp;
> +      dr_addr_orig_p = state->dr_addr_orig_wp;
> +      dr_ctrl_p = state->dr_ctrl_wp;
> +      dr_ref_count = state->dr_ref_count_wp;
> +    }
> +  else
> +    {
> +      num_regs = aarch64_num_bp_regs;
> +      dr_addr_p = state->dr_addr_bp;
> +      dr_addr_orig_p = nullptr;
> +      dr_ctrl_p = state->dr_ctrl_bp;
> +      dr_ref_count = state->dr_ref_count_bp;
> +    }
> +
> +  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
> +
> +  /* Find the entry that matches the ADDR and CTRL.  */
> +  for (i = 0; i < num_regs; ++i)
> +    if (dr_addr_p[i] == addr
> +	&& (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
> +	&& dr_ctrl_p[i] == ctrl)
> +      {
> +	gdb_assert (dr_ref_count[i] != 0);
> +	break;
> +      }
> +
> +  /* Not found.  */
> +  if (i == num_regs)
> +    return -1;
> +
> +  /* Clear our cache.  */
> +  if (--dr_ref_count[i] == 0)
> +    {
> +      /* Clear the enable bit.  */
> +      ctrl &= ~1;
> +      dr_addr_p[i] = 0;
> +      if (dr_addr_orig_p != nullptr)
> +	dr_addr_orig_p[i] = 0;
> +      dr_ctrl_p[i] = ctrl;
> +      /* Notify the change.  */
> +      aarch64_notify_debug_reg_change (ptid, is_watchpoint, i);
> +    }
> +
> +  return 0;
> +}
> +
> +int
> +aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> +			   int len, int is_insert, ptid_t ptid,
> +			   struct aarch64_debug_reg_state *state)
> +{
> +  if (is_insert)
> +    {
> +      /* The hardware breakpoint on AArch64 should always be 4-byte
> +	 aligned, but on AArch32, it can be 2-byte aligned.  Note that
> +	 we only check the alignment on inserting breakpoint because
> +	 aarch64_point_is_aligned needs the inferior_ptid inferior's
> +	 regcache to decide whether the inferior is 32-bit or 64-bit.
> +	 However when GDB follows the parent process and detach breakpoints
> +	 from child process, inferior_ptid is the child ptid, but the
> +	 child inferior doesn't exist in GDB's view yet.  */
> +      if (!aarch64_point_is_aligned (ptid, 0 /* is_watchpoint */ , addr, len))
> +	return -1;
> +
> +      return aarch64_dr_state_insert_one_point (ptid, state, type, addr, 0, len,
> +						-1);
> +    }
> +  else
> +    return aarch64_dr_state_remove_one_point (ptid, state, type, addr, 0, len,
> +					      -1);
> +}
> +
> +/* This is essentially the same as aarch64_handle_breakpoint, apart
> +   from that it is an aligned watchpoint to be handled.  */
> +
> +static int
> +aarch64_handle_aligned_watchpoint (enum target_hw_bp_type type,
> +				   CORE_ADDR addr, int len, int is_insert,
> +				   ptid_t ptid,
> +				   struct aarch64_debug_reg_state *state)
> +{
> +  if (is_insert)
> +    return aarch64_dr_state_insert_one_point (ptid, state, type, addr, 0, len,
> +					      addr);
> +  else
> +    return aarch64_dr_state_remove_one_point (ptid, state, type, addr, 0, len,
> +					      addr);
> +}
> +
> +/* Insert/remove unaligned watchpoint by calling
> +   aarch64_align_watchpoint repeatedly until the whole watched region,
> +   as represented by ADDR and LEN, has been properly aligned and ready
> +   to be written to one or more hardware watchpoint registers.
> +   IS_INSERT indicates whether this is an insertion or a deletion.
> +   Return 0 if succeed.  */
> +
> +static int
> +aarch64_handle_unaligned_watchpoint (enum target_hw_bp_type type,
> +				     CORE_ADDR addr, int len, int is_insert,
> +				     ptid_t ptid,
> +				     struct aarch64_debug_reg_state *state)
> +{
> +  CORE_ADDR addr_orig = addr;
> +
> +  while (len > 0)
> +    {
> +      CORE_ADDR aligned_addr;
> +      int aligned_offset, aligned_len, ret;
> +      CORE_ADDR addr_orig_next = addr_orig;
> +
> +      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_offset,
> +				&aligned_len, &addr, &len, &addr_orig_next);
> +
> +      if (is_insert)
> +	ret = aarch64_dr_state_insert_one_point (ptid, state, type,
> +						 aligned_addr, aligned_offset,
> +						 aligned_len, addr_orig);
> +      else
> +	ret = aarch64_dr_state_remove_one_point (ptid, state, type,
> +						 aligned_addr, aligned_offset,
> +						 aligned_len, addr_orig);
> +
> +      if (show_debug_regs)
> +	debug_printf ("handle_unaligned_watchpoint: is_insert: %d\n"
> +		      "                             "
> +		      "aligned_addr: %s, aligned_len: %d\n"
> +		      "                                "
> +		      "addr_orig: %s\n"
> +		      "                                "
> +		      "next_addr: %s,    next_len: %d\n"
> +		      "                           "
> +		      "addr_orig_next: %s\n",
> +		      is_insert, core_addr_to_string_nz (aligned_addr),
> +		      aligned_len, core_addr_to_string_nz (addr_orig),
> +		      core_addr_to_string_nz (addr), len,
> +		      core_addr_to_string_nz (addr_orig_next));
> +
> +      addr_orig = addr_orig_next;
> +
> +      if (ret != 0)
> +	return ret;
> +    }
> +
> +  return 0;
> +}
> +
> +int
> +aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> +			   int len, int is_insert, ptid_t ptid,
> +			   struct aarch64_debug_reg_state *state)
> +{
> +  if (aarch64_point_is_aligned (ptid, 1 /* is_watchpoint */ , addr, len))
> +    return aarch64_handle_aligned_watchpoint (type, addr, len, is_insert, ptid,
> +					      state);
> +  else
> +    return aarch64_handle_unaligned_watchpoint (type, addr, len, is_insert,
> +						ptid, state);
> +}
> +
> +/* See nat/aarch64-hw-point.h.  */
> +
> +bool
> +aarch64_any_set_debug_regs_state (aarch64_debug_reg_state *state,
> +				  bool watchpoint)
> +{
> +  int count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
> +  if (count == 0)
> +    return false;
> +
> +  const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
> +  const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
> +
> +  for (int i = 0; i < count; i++)
> +    if (addr[i] != 0 || ctrl[i] != 0)
> +      return true;
> +
> +  return false;
> +}
> +
> +/* Print the values of the cached breakpoint/watchpoint registers.  */
> +
> +void
> +aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
> +			      const char *func, CORE_ADDR addr,
> +			      int len, enum target_hw_bp_type type)
> +{
> +  int i;
> +
> +  debug_printf ("%s", func);
> +  if (addr || len)
> +    debug_printf (" (addr=0x%08lx, len=%d, type=%s)",
> +		  (unsigned long) addr, len,
> +		  type == hw_write ? "hw-write-watchpoint"
> +		  : (type == hw_read ? "hw-read-watchpoint"
> +		     : (type == hw_access ? "hw-access-watchpoint"
> +			: (type == hw_execute ? "hw-breakpoint"
> +			   : "??unknown??"))));
> +  debug_printf (":\n");
> +
> +  debug_printf ("\tBREAKPOINTs:\n");
> +  for (i = 0; i < aarch64_num_bp_regs; i++)
> +    debug_printf ("\tBP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
> +		  i, core_addr_to_string_nz (state->dr_addr_bp[i]),
> +		  state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
> +
> +  debug_printf ("\tWATCHPOINTs:\n");
> +  for (i = 0; i < aarch64_num_wp_regs; i++)
> +    debug_printf ("\tWP%d: addr=%s (orig=%s), ctrl=0x%08x, ref.count=%d\n",
> +		  i, core_addr_to_string_nz (state->dr_addr_wp[i]),
> +		  core_addr_to_string_nz (state->dr_addr_orig_wp[i]),
> +		  state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
> +}
> +
> +/* Return true if we can watch a memory region that starts address
> +   ADDR and whose length is LEN in bytes.  */
> +
> +int
> +aarch64_region_ok_for_watchpoint (CORE_ADDR addr, int len)
> +{
> +  CORE_ADDR aligned_addr;
> +
> +  /* Can not set watchpoints for zero or negative lengths.  */
> +  if (len <= 0)
> +    return 0;
> +
> +  /* Must have hardware watchpoint debug register(s).  */
> +  if (aarch64_num_wp_regs == 0)
> +    return 0;
> +
> +  /* We support unaligned watchpoint address and arbitrary length,
> +     as long as the size of the whole watched area after alignment
> +     doesn't exceed size of the total area that all watchpoint debug
> +     registers can watch cooperatively.
> +
> +     This is a very relaxed rule, but unfortunately there are
> +     limitations, e.g. false-positive hits, due to limited support of
> +     hardware debug registers in the kernel.  See comment above
> +     aarch64_align_watchpoint for more information.  */
> +
> +  aligned_addr = addr & ~(AARCH64_HWP_MAX_LEN_PER_REG - 1);
> +  if (aligned_addr + aarch64_num_wp_regs * AARCH64_HWP_MAX_LEN_PER_REG
> +      < addr + len)
> +    return 0;
> +
> +  /* All tests passed so we are likely to be able to set the watchpoint.
> +     The reason that it is 'likely' rather than 'must' is because
> +     we don't check the current usage of the watchpoint registers, and
> +     there may not be enough registers available for this watchpoint.
> +     Ideally we should check the cached debug register state, however
> +     the checking is costly.  */
> +  return 1;
> +}
> diff --git a/gdb/nat/aarch64-hw-point.h b/gdb/nat/aarch64-hw-point.h
> new file mode 100644
> index 00000000000..97b37d537c2
> --- /dev/null
> +++ b/gdb/nat/aarch64-hw-point.h
> @@ -0,0 +1,126 @@
> +/* Copyright (C) 2009-2022 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/>.  */
> +
> +#ifndef NAT_AARCH64_HW_POINT_H
> +#define NAT_AARCH64_HW_POINT_H
> +
> +/* Macro definitions, data structures, and code for the hardware
> +   breakpoint and hardware watchpoint support follow.  We use the
> +   following abbreviations throughout the code:
> +
> +   hw - hardware
> +   bp - breakpoint
> +   wp - watchpoint  */
> +
> +/* Maximum number of hardware breakpoint and watchpoint registers.
> +   Neither of these values may exceed the width of dr_changed_t
> +   measured in bits.  */
> +
> +#define AARCH64_HBP_MAX_NUM 16
> +#define AARCH64_HWP_MAX_NUM 16
> +
> +/* Alignment requirement in bytes for addresses written to
> +   hardware breakpoint and watchpoint value registers.
> +
> +   A ptrace call attempting to set an address that does not meet the
> +   alignment criteria will fail.  Limited support has been provided in
> +   this port for unaligned watchpoints, such that from a GDB user
> +   perspective, an unaligned watchpoint may be requested.
> +
> +   This is achieved by minimally enlarging the watched area to meet the
> +   alignment requirement, and if necessary, splitting the watchpoint
> +   over several hardware watchpoint registers.  */
> +
> +#define AARCH64_HBP_ALIGNMENT 4
> +#define AARCH64_HWP_ALIGNMENT 8
> +
> +/* The maximum length of a memory region that can be watched by one
> +   hardware watchpoint register.  */
> +
> +#define AARCH64_HWP_MAX_LEN_PER_REG 8
> +
> +/* Macro for the expected version of the ARMv8-A debug architecture.  */
> +#define AARCH64_DEBUG_ARCH_V8 0x6
> +#define AARCH64_DEBUG_ARCH_V8_1 0x7
> +#define AARCH64_DEBUG_ARCH_V8_2 0x8
> +#define AARCH64_DEBUG_ARCH_V8_4 0x9
> +
> +/* ptrace expects control registers to be formatted as follows:
> +
> +   31                             13          5      3      1     0
> +   +--------------------------------+----------+------+------+----+
> +   |         RESERVED (SBZ)         |   MASK   | TYPE | PRIV | EN |
> +   +--------------------------------+----------+------+------+----+
> +
> +   The TYPE field is ignored for breakpoints.  */
> +
> +#define DR_CONTROL_ENABLED(ctrl)	(((ctrl) & 0x1) == 1)
> +#define DR_CONTROL_MASK(ctrl)		(((ctrl) >> 5) & 0xff)
> +
> +/* Structure for managing the hardware breakpoint/watchpoint resources.
> +   DR_ADDR_* stores the address, DR_CTRL_* stores the control register
> +   content, and DR_REF_COUNT_* counts the numbers of references to the
> +   corresponding bp/wp, by which way the limited hardware resources
> +   are not wasted on duplicated bp/wp settings (though so far gdb has
> +   done a good job by not sending duplicated bp/wp requests).  */
> +
> +struct aarch64_debug_reg_state
> +{
> +  /* hardware breakpoint */
> +  CORE_ADDR dr_addr_bp[AARCH64_HBP_MAX_NUM];
> +  unsigned int dr_ctrl_bp[AARCH64_HBP_MAX_NUM];
> +  unsigned int dr_ref_count_bp[AARCH64_HBP_MAX_NUM];
> +
> +  /* hardware watchpoint */
> +  /* Address aligned down to AARCH64_HWP_ALIGNMENT.  */
> +  CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM];
> +  /* Address as entered by user without any forced alignment.  */
> +  CORE_ADDR dr_addr_orig_wp[AARCH64_HWP_MAX_NUM];
> +  unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM];
> +  unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
> +};
> +
> +extern int aarch64_num_bp_regs;
> +extern int aarch64_num_wp_regs;
> +
> +/* Invoked when IDXth breakpoint/watchpoint register pair needs to be
> +   updated.  */
> +void aarch64_notify_debug_reg_change (ptid_t ptid, int is_watchpoint,
> +				      unsigned int idx);
> +
> +unsigned int aarch64_watchpoint_offset (unsigned int ctrl);
> +unsigned int aarch64_watchpoint_length (unsigned int ctrl);
> +
> +int aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> +			       int len, int is_insert, ptid_t ptid,
> +			       struct aarch64_debug_reg_state *state);
> +int aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> +			       int len, int is_insert, ptid_t ptid,
> +			       struct aarch64_debug_reg_state *state);
> +
> +/* Return TRUE if there are any hardware breakpoints.  If WATCHPOINT is TRUE,
> +   check hardware watchpoints instead.  */
> +bool aarch64_any_set_debug_regs_state (aarch64_debug_reg_state *state,
> +				       bool watchpoint);
> +
> +void aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
> +				   const char *func, CORE_ADDR addr,
> +				   int len, enum target_hw_bp_type type);
> +
> +int aarch64_region_ok_for_watchpoint (CORE_ADDR addr, int len);
> +
> +#endif /* NAT_AARCH64_HW_POINT_H */
> diff --git a/gdb/nat/aarch64-linux-hw-point.c b/gdb/nat/aarch64-linux-hw-point.c
> index f5dd3b2be2c..a6d91a367b7 100644
> --- a/gdb/nat/aarch64-linux-hw-point.c
> +++ b/gdb/nat/aarch64-linux-hw-point.c
> @@ -34,256 +34,9 @@
>   
>   #include <elf.h>
>   
> -/* Number of hardware breakpoints/watchpoints the target supports.
> -   They are initialized with values obtained via the ptrace calls
> -   with NT_ARM_HW_BREAK and NT_ARM_HW_WATCH respectively.  */
> +/* See aarch64-linux-hw-point.h  */
>   
> -int aarch64_num_bp_regs;
> -int aarch64_num_wp_regs;
> -
> -/* True if this kernel does not have the bug described by PR
> -   external/20207 (Linux >= 4.10).  A fixed kernel supports any
> -   contiguous range of bits in 8-bit byte DR_CONTROL_MASK.  A buggy
> -   kernel supports only 0x01, 0x03, 0x0f and 0xff.  We start by
> -   assuming the bug is fixed, and then detect the bug at
> -   PTRACE_SETREGSET time.  */
> -static bool kernel_supports_any_contiguous_range = true;
> -
> -/* Return starting byte 0..7 incl. of a watchpoint encoded by CTRL.  */
> -
> -unsigned int
> -aarch64_watchpoint_offset (unsigned int ctrl)
> -{
> -  uint8_t mask = DR_CONTROL_MASK (ctrl);
> -  unsigned retval;
> -
> -  /* Shift out bottom zeros.  */
> -  for (retval = 0; mask && (mask & 1) == 0; ++retval)
> -    mask >>= 1;
> -
> -  return retval;
> -}
> -
> -/* Utility function that returns the length in bytes of a watchpoint
> -   according to the content of a hardware debug control register CTRL.
> -   Any contiguous range of bytes in CTRL is supported.  The returned
> -   value can be between 0..8 (inclusive).  */
> -
> -unsigned int
> -aarch64_watchpoint_length (unsigned int ctrl)
> -{
> -  uint8_t mask = DR_CONTROL_MASK (ctrl);
> -  unsigned retval;
> -
> -  /* Shift out bottom zeros.  */
> -  mask >>= aarch64_watchpoint_offset (ctrl);
> -
> -  /* Count bottom ones.  */
> -  for (retval = 0; (mask & 1) != 0; ++retval)
> -    mask >>= 1;
> -
> -  if (mask != 0)
> -    error (_("Unexpected hardware watchpoint length register value 0x%x"),
> -	   DR_CONTROL_MASK (ctrl));
> -
> -  return retval;
> -}
> -
> -/* Given the hardware breakpoint or watchpoint type TYPE and its
> -   length LEN, return the expected encoding for a hardware
> -   breakpoint/watchpoint control register.  */
> -
> -static unsigned int
> -aarch64_point_encode_ctrl_reg (enum target_hw_bp_type type, int offset, int len)
> -{
> -  unsigned int ctrl, ttype;
> -
> -  gdb_assert (offset == 0 || kernel_supports_any_contiguous_range);
> -  gdb_assert (offset + len <= AARCH64_HWP_MAX_LEN_PER_REG);
> -
> -  /* type */
> -  switch (type)
> -    {
> -    case hw_write:
> -      ttype = 2;
> -      break;
> -    case hw_read:
> -      ttype = 1;
> -      break;
> -    case hw_access:
> -      ttype = 3;
> -      break;
> -    case hw_execute:
> -      ttype = 0;
> -      break;
> -    default:
> -      perror_with_name (_("Unrecognized breakpoint/watchpoint type"));
> -    }
> -
> -  ctrl = ttype << 3;
> -
> -  /* offset and length bitmask */
> -  ctrl |= ((1 << len) - 1) << (5 + offset);
> -  /* enabled at el0 */
> -  ctrl |= (2 << 1) | 1;
> -
> -  return ctrl;
> -}
> -
> -/* Addresses to be written to the hardware breakpoint and watchpoint
> -   value registers need to be aligned; the alignment is 4-byte and
> -   8-type respectively.  Linux kernel rejects any non-aligned address
> -   it receives from the related ptrace call.  Furthermore, the kernel
> -   currently only supports the following Byte Address Select (BAS)
> -   values: 0x1, 0x3, 0xf and 0xff, which means that for a hardware
> -   watchpoint to be accepted by the kernel (via ptrace call), its
> -   valid length can only be 1 byte, 2 bytes, 4 bytes or 8 bytes.
> -   Despite these limitations, the unaligned watchpoint is supported in
> -   this port.
> -
> -   Return 0 for any non-compliant ADDR and/or LEN; return 1 otherwise.  */
> -
> -static int
> -aarch64_point_is_aligned (int is_watchpoint, CORE_ADDR addr, int len)
> -{
> -  unsigned int alignment = 0;
> -
> -  if (is_watchpoint)
> -    alignment = AARCH64_HWP_ALIGNMENT;
> -  else
> -    {
> -      struct regcache *regcache
> -	= get_thread_regcache_for_ptid (current_lwp_ptid ());
> -
> -      /* Set alignment to 2 only if the current process is 32-bit,
> -	 since thumb instruction can be 2-byte aligned.  Otherwise, set
> -	 alignment to AARCH64_HBP_ALIGNMENT.  */
> -      if (regcache_register_size (regcache, 0) == 8)
> -	alignment = AARCH64_HBP_ALIGNMENT;
> -      else
> -	alignment = 2;
> -    }
> -
> -  if (addr & (alignment - 1))
> -    return 0;
> -
> -  if ((!kernel_supports_any_contiguous_range
> -       && len != 8 && len != 4 && len != 2 && len != 1)
> -      || (kernel_supports_any_contiguous_range
> -	  && (len < 1 || len > 8)))
> -    return 0;
> -
> -  return 1;
> -}
> -
> -/* Given the (potentially unaligned) watchpoint address in ADDR and
> -   length in LEN, return the aligned address, offset from that base
> -   address, and aligned length in *ALIGNED_ADDR_P, *ALIGNED_OFFSET_P
> -   and *ALIGNED_LEN_P, respectively.  The returned values will be
> -   valid values to write to the hardware watchpoint value and control
> -   registers.
> -
> -   The given watchpoint may get truncated if more than one hardware
> -   register is needed to cover the watched region.  *NEXT_ADDR_P
> -   and *NEXT_LEN_P, if non-NULL, will return the address and length
> -   of the remaining part of the watchpoint (which can be processed
> -   by calling this routine again to generate another aligned address,
> -   offset and length tuple.
> -
> -   Essentially, unaligned watchpoint is achieved by minimally
> -   enlarging the watched area to meet the alignment requirement, and
> -   if necessary, splitting the watchpoint over several hardware
> -   watchpoint registers.
> -
> -   On kernels that predate the support for Byte Address Select (BAS)
> -   in the hardware watchpoint control register, the offset from the
> -   base address is always zero, and so in that case the trade-off is
> -   that there will be false-positive hits for the read-type or the
> -   access-type hardware watchpoints; for the write type, which is more
> -   commonly used, there will be no such issues, as the higher-level
> -   breakpoint management in gdb always examines the exact watched
> -   region for any content change, and transparently resumes a thread
> -   from a watchpoint trap if there is no change to the watched region.
> -
> -   Another limitation is that because the watched region is enlarged,
> -   the watchpoint fault address discovered by
> -   aarch64_stopped_data_address may be outside of the original watched
> -   region, especially when the triggering instruction is accessing a
> -   larger region.  When the fault address is not within any known
> -   range, watchpoints_triggered in gdb will get confused, as the
> -   higher-level watchpoint management is only aware of original
> -   watched regions, and will think that some unknown watchpoint has
> -   been triggered.  To prevent such a case,
> -   aarch64_stopped_data_address implementations in gdb and gdbserver
> -   try to match the trapped address with a watched region, and return
> -   an address within the latter. */
> -
> -static void
> -aarch64_align_watchpoint (CORE_ADDR addr, int len, CORE_ADDR *aligned_addr_p,
> -			  int *aligned_offset_p, int *aligned_len_p,
> -			  CORE_ADDR *next_addr_p, int *next_len_p,
> -			  CORE_ADDR *next_addr_orig_p)
> -{
> -  int aligned_len;
> -  unsigned int offset, aligned_offset;
> -  CORE_ADDR aligned_addr;
> -  const unsigned int alignment = AARCH64_HWP_ALIGNMENT;
> -  const unsigned int max_wp_len = AARCH64_HWP_MAX_LEN_PER_REG;
> -
> -  /* As assumed by the algorithm.  */
> -  gdb_assert (alignment == max_wp_len);
> -
> -  if (len <= 0)
> -    return;
> -
> -  /* The address put into the hardware watchpoint value register must
> -     be aligned.  */
> -  offset = addr & (alignment - 1);
> -  aligned_addr = addr - offset;
> -  aligned_offset
> -    = kernel_supports_any_contiguous_range ? addr & (alignment - 1) : 0;
> -
> -  gdb_assert (offset >= 0 && offset < alignment);
> -  gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
> -  gdb_assert (offset + len > 0);
> -
> -  if (offset + len >= max_wp_len)
> -    {
> -      /* Need more than one watchpoint register; truncate at the
> -	 alignment boundary.  */
> -      aligned_len
> -	= max_wp_len - (kernel_supports_any_contiguous_range ? offset : 0);
> -      len -= (max_wp_len - offset);
> -      addr += (max_wp_len - offset);
> -      gdb_assert ((addr & (alignment - 1)) == 0);
> -    }
> -  else
> -    {
> -      /* Find the smallest valid length that is large enough to
> -	 accommodate this watchpoint.  */
> -      static const unsigned char
> -	aligned_len_array[AARCH64_HWP_MAX_LEN_PER_REG] =
> -	{ 1, 2, 4, 4, 8, 8, 8, 8 };
> -
> -      aligned_len = (kernel_supports_any_contiguous_range
> -		     ? len : aligned_len_array[offset + len - 1]);
> -      addr += len;
> -      len = 0;
> -    }
> -
> -  if (aligned_addr_p)
> -    *aligned_addr_p = aligned_addr;
> -  if (aligned_offset_p)
> -    *aligned_offset_p = aligned_offset;
> -  if (aligned_len_p)
> -    *aligned_len_p = aligned_len;
> -  if (next_addr_p)
> -    *next_addr_p = addr;
> -  if (next_len_p)
> -    *next_len_p = len;
> -  if (next_addr_orig_p)
> -    *next_addr_orig_p = align_down (*next_addr_orig_p + alignment, alignment);
> -}
> +bool kernel_supports_any_contiguous_range = true;
>   
>   /* Helper for aarch64_notify_debug_reg_change.  Records the
>      information about the change of one hardware breakpoint/watchpoint
> @@ -349,11 +102,11 @@ debug_reg_change_callback (struct lwp_info *lwp, int is_watchpoint,
>      thread's arch-specific data area, the actual updating will be done
>      when the thread is resumed.  */
>   
> -static void
> -aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state,
> +void
> +aarch64_notify_debug_reg_change (ptid_t ptid,
>   				 int is_watchpoint, unsigned int idx)
>   {
> -  ptid_t pid_ptid = ptid_t (current_lwp_ptid ().pid ());
> +  ptid_t pid_ptid = ptid_t (ptid.pid ());
>   
>     iterate_over_lwps (pid_ptid, [=] (struct lwp_info *info)
>   			       {
> @@ -414,261 +167,11 @@ aarch64_downgrade_regs (struct aarch64_debug_reg_state *state)
>   	      break;
>   	    }
>   
> -	aarch64_notify_debug_reg_change (state, 1 /* is_watchpoint */, i);
> +	aarch64_notify_debug_reg_change (current_lwp_ptid (),
> +					 1 /* is_watchpoint */, i);
>         }
>   }
>   
> -/* Record the insertion of one breakpoint/watchpoint, as represented
> -   by ADDR and CTRL, in the process' arch-specific data area *STATE.  */
> -
> -static int
> -aarch64_dr_state_insert_one_point (struct aarch64_debug_reg_state *state,
> -				   enum target_hw_bp_type type,
> -				   CORE_ADDR addr, int offset, int len,
> -				   CORE_ADDR addr_orig)
> -{
> -  int i, idx, num_regs, is_watchpoint;
> -  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
> -  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
> -
> -  /* Set up state pointers.  */
> -  is_watchpoint = (type != hw_execute);
> -  gdb_assert (aarch64_point_is_aligned (is_watchpoint, addr, len));
> -  if (is_watchpoint)
> -    {
> -      num_regs = aarch64_num_wp_regs;
> -      dr_addr_p = state->dr_addr_wp;
> -      dr_addr_orig_p = state->dr_addr_orig_wp;
> -      dr_ctrl_p = state->dr_ctrl_wp;
> -      dr_ref_count = state->dr_ref_count_wp;
> -    }
> -  else
> -    {
> -      num_regs = aarch64_num_bp_regs;
> -      dr_addr_p = state->dr_addr_bp;
> -      dr_addr_orig_p = nullptr;
> -      dr_ctrl_p = state->dr_ctrl_bp;
> -      dr_ref_count = state->dr_ref_count_bp;
> -    }
> -
> -  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
> -
> -  /* Find an existing or free register in our cache.  */
> -  idx = -1;
> -  for (i = 0; i < num_regs; ++i)
> -    {
> -      if ((dr_ctrl_p[i] & 1) == 0)
> -	{
> -	  gdb_assert (dr_ref_count[i] == 0);
> -	  idx = i;
> -	  /* no break; continue hunting for an exising one.  */
> -	}
> -      else if (dr_addr_p[i] == addr
> -	       && (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
> -	       && dr_ctrl_p[i] == ctrl)
> -	{
> -	  gdb_assert (dr_ref_count[i] != 0);
> -	  idx = i;
> -	  break;
> -	}
> -    }
> -
> -  /* No space.  */
> -  if (idx == -1)
> -    return -1;
> -
> -  /* Update our cache.  */
> -  if ((dr_ctrl_p[idx] & 1) == 0)
> -    {
> -      /* new entry */
> -      dr_addr_p[idx] = addr;
> -      if (dr_addr_orig_p != nullptr)
> -	dr_addr_orig_p[idx] = addr_orig;
> -      dr_ctrl_p[idx] = ctrl;
> -      dr_ref_count[idx] = 1;
> -      /* Notify the change.  */
> -      aarch64_notify_debug_reg_change (state, is_watchpoint, idx);
> -    }
> -  else
> -    {
> -      /* existing entry */
> -      dr_ref_count[idx]++;
> -    }
> -
> -  return 0;
> -}
> -
> -/* Record the removal of one breakpoint/watchpoint, as represented by
> -   ADDR and CTRL, in the process' arch-specific data area *STATE.  */
> -
> -static int
> -aarch64_dr_state_remove_one_point (struct aarch64_debug_reg_state *state,
> -				   enum target_hw_bp_type type,
> -				   CORE_ADDR addr, int offset, int len,
> -				   CORE_ADDR addr_orig)
> -{
> -  int i, num_regs, is_watchpoint;
> -  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
> -  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
> -
> -  /* Set up state pointers.  */
> -  is_watchpoint = (type != hw_execute);
> -  if (is_watchpoint)
> -    {
> -      num_regs = aarch64_num_wp_regs;
> -      dr_addr_p = state->dr_addr_wp;
> -      dr_addr_orig_p = state->dr_addr_orig_wp;
> -      dr_ctrl_p = state->dr_ctrl_wp;
> -      dr_ref_count = state->dr_ref_count_wp;
> -    }
> -  else
> -    {
> -      num_regs = aarch64_num_bp_regs;
> -      dr_addr_p = state->dr_addr_bp;
> -      dr_addr_orig_p = nullptr;
> -      dr_ctrl_p = state->dr_ctrl_bp;
> -      dr_ref_count = state->dr_ref_count_bp;
> -    }
> -
> -  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
> -
> -  /* Find the entry that matches the ADDR and CTRL.  */
> -  for (i = 0; i < num_regs; ++i)
> -    if (dr_addr_p[i] == addr
> -	&& (dr_addr_orig_p == nullptr || dr_addr_orig_p[i] == addr_orig)
> -	&& dr_ctrl_p[i] == ctrl)
> -      {
> -	gdb_assert (dr_ref_count[i] != 0);
> -	break;
> -      }
> -
> -  /* Not found.  */
> -  if (i == num_regs)
> -    return -1;
> -
> -  /* Clear our cache.  */
> -  if (--dr_ref_count[i] == 0)
> -    {
> -      /* Clear the enable bit.  */
> -      ctrl &= ~1;
> -      dr_addr_p[i] = 0;
> -      if (dr_addr_orig_p != nullptr)
> -	dr_addr_orig_p[i] = 0;
> -      dr_ctrl_p[i] = ctrl;
> -      /* Notify the change.  */
> -      aarch64_notify_debug_reg_change (state, is_watchpoint, i);
> -    }
> -
> -  return 0;
> -}
> -
> -int
> -aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> -			   int len, int is_insert,
> -			   struct aarch64_debug_reg_state *state)
> -{
> -  if (is_insert)
> -    {
> -      /* The hardware breakpoint on AArch64 should always be 4-byte
> -	 aligned, but on AArch32, it can be 2-byte aligned.  Note that
> -	 we only check the alignment on inserting breakpoint because
> -	 aarch64_point_is_aligned needs the inferior_ptid inferior's
> -	 regcache to decide whether the inferior is 32-bit or 64-bit.
> -	 However when GDB follows the parent process and detach breakpoints
> -	 from child process, inferior_ptid is the child ptid, but the
> -	 child inferior doesn't exist in GDB's view yet.  */
> -      if (!aarch64_point_is_aligned (0 /* is_watchpoint */ , addr, len))
> -	return -1;
> -
> -      return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, -1);
> -    }
> -  else
> -    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, -1);
> -}
> -
> -/* This is essentially the same as aarch64_handle_breakpoint, apart
> -   from that it is an aligned watchpoint to be handled.  */
> -
> -static int
> -aarch64_handle_aligned_watchpoint (enum target_hw_bp_type type,
> -				   CORE_ADDR addr, int len, int is_insert,
> -				   struct aarch64_debug_reg_state *state)
> -{
> -  if (is_insert)
> -    return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, addr);
> -  else
> -    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, addr);
> -}
> -
> -/* Insert/remove unaligned watchpoint by calling
> -   aarch64_align_watchpoint repeatedly until the whole watched region,
> -   as represented by ADDR and LEN, has been properly aligned and ready
> -   to be written to one or more hardware watchpoint registers.
> -   IS_INSERT indicates whether this is an insertion or a deletion.
> -   Return 0 if succeed.  */
> -
> -static int
> -aarch64_handle_unaligned_watchpoint (enum target_hw_bp_type type,
> -				     CORE_ADDR addr, int len, int is_insert,
> -				     struct aarch64_debug_reg_state *state)
> -{
> -  CORE_ADDR addr_orig = addr;
> -
> -  while (len > 0)
> -    {
> -      CORE_ADDR aligned_addr;
> -      int aligned_offset, aligned_len, ret;
> -      CORE_ADDR addr_orig_next = addr_orig;
> -
> -      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_offset,
> -				&aligned_len, &addr, &len, &addr_orig_next);
> -
> -      if (is_insert)
> -	ret = aarch64_dr_state_insert_one_point (state, type, aligned_addr,
> -						 aligned_offset,
> -						 aligned_len, addr_orig);
> -      else
> -	ret = aarch64_dr_state_remove_one_point (state, type, aligned_addr,
> -						 aligned_offset,
> -						 aligned_len, addr_orig);
> -
> -      if (show_debug_regs)
> -	debug_printf ("handle_unaligned_watchpoint: is_insert: %d\n"
> -		      "                             "
> -		      "aligned_addr: %s, aligned_len: %d\n"
> -		      "                                "
> -		      "addr_orig: %s\n"
> -		      "                                "
> -		      "next_addr: %s,    next_len: %d\n"
> -		      "                           "
> -		      "addr_orig_next: %s\n",
> -		      is_insert, core_addr_to_string_nz (aligned_addr),
> -		      aligned_len, core_addr_to_string_nz (addr_orig),
> -		      core_addr_to_string_nz (addr), len,
> -		      core_addr_to_string_nz (addr_orig_next));
> -
> -      addr_orig = addr_orig_next;
> -
> -      if (ret != 0)
> -	return ret;
> -    }
> -
> -  return 0;
> -}
> -
> -int
> -aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> -			   int len, int is_insert,
> -			   struct aarch64_debug_reg_state *state)
> -{
> -  if (aarch64_point_is_aligned (1 /* is_watchpoint */ , addr, len))
> -    return aarch64_handle_aligned_watchpoint (type, addr, len, is_insert,
> -					      state);
> -  else
> -    return aarch64_handle_unaligned_watchpoint (type, addr, len, is_insert,
> -						state);
> -}
> -
>   /* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
>      registers with data from *STATE.  */
>   
> @@ -715,60 +218,6 @@ aarch64_linux_set_debug_regs (struct aarch64_debug_reg_state *state,
>       }
>   }
>   
> -/* See nat/aarch64-linux-hw-point.h.  */
> -
> -bool
> -aarch64_linux_any_set_debug_regs_state (aarch64_debug_reg_state *state,
> -					bool watchpoint)
> -{
> -  int count = watchpoint ? aarch64_num_wp_regs : aarch64_num_bp_regs;
> -  if (count == 0)
> -    return false;
> -
> -  const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
> -  const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
> -
> -  for (int i = 0; i < count; i++)
> -    if (addr[i] != 0 || ctrl[i] != 0)
> -      return true;
> -
> -  return false;
> -}
> -
> -/* Print the values of the cached breakpoint/watchpoint registers.  */
> -
> -void
> -aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
> -			      const char *func, CORE_ADDR addr,
> -			      int len, enum target_hw_bp_type type)
> -{
> -  int i;
> -
> -  debug_printf ("%s", func);
> -  if (addr || len)
> -    debug_printf (" (addr=0x%08lx, len=%d, type=%s)",
> -		  (unsigned long) addr, len,
> -		  type == hw_write ? "hw-write-watchpoint"
> -		  : (type == hw_read ? "hw-read-watchpoint"
> -		     : (type == hw_access ? "hw-access-watchpoint"
> -			: (type == hw_execute ? "hw-breakpoint"
> -			   : "??unknown??"))));
> -  debug_printf (":\n");
> -
> -  debug_printf ("\tBREAKPOINTs:\n");
> -  for (i = 0; i < aarch64_num_bp_regs; i++)
> -    debug_printf ("\tBP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
> -		  i, core_addr_to_string_nz (state->dr_addr_bp[i]),
> -		  state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
> -
> -  debug_printf ("\tWATCHPOINTs:\n");
> -  for (i = 0; i < aarch64_num_wp_regs; i++)
> -    debug_printf ("\tWP%d: addr=%s (orig=%s), ctrl=0x%08x, ref.count=%d\n",
> -		  i, core_addr_to_string_nz (state->dr_addr_wp[i]),
> -		  core_addr_to_string_nz (state->dr_addr_orig_wp[i]),
> -		  state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
> -}
> -
>   /* Return true if debug arch level is compatible for hw watchpoints
>      and breakpoints.  */
>   
> @@ -839,43 +288,3 @@ aarch64_linux_get_debug_reg_capacity (int tid)
>         aarch64_num_bp_regs = 0;
>       }
>   }
> -
> -/* Return true if we can watch a memory region that starts address
> -   ADDR and whose length is LEN in bytes.  */
> -
> -int
> -aarch64_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len)
> -{
> -  CORE_ADDR aligned_addr;
> -
> -  /* Can not set watchpoints for zero or negative lengths.  */
> -  if (len <= 0)
> -    return 0;
> -
> -  /* Must have hardware watchpoint debug register(s).  */
> -  if (aarch64_num_wp_regs == 0)
> -    return 0;
> -
> -  /* We support unaligned watchpoint address and arbitrary length,
> -     as long as the size of the whole watched area after alignment
> -     doesn't exceed size of the total area that all watchpoint debug
> -     registers can watch cooperatively.
> -
> -     This is a very relaxed rule, but unfortunately there are
> -     limitations, e.g. false-positive hits, due to limited support of
> -     hardware debug registers in the kernel.  See comment above
> -     aarch64_align_watchpoint for more information.  */
> -
> -  aligned_addr = addr & ~(AARCH64_HWP_MAX_LEN_PER_REG - 1);
> -  if (aligned_addr + aarch64_num_wp_regs * AARCH64_HWP_MAX_LEN_PER_REG
> -      < addr + len)
> -    return 0;
> -
> -  /* All tests passed so we are likely to be able to set the watchpoint.
> -     The reason that it is 'likely' rather than 'must' is because
> -     we don't check the current usage of the watchpoint registers, and
> -     there may not be enough registers available for this watchpoint.
> -     Ideally we should check the cached debug register state, however
> -     the checking is costly.  */
> -  return 1;
> -}
> diff --git a/gdb/nat/aarch64-linux-hw-point.h b/gdb/nat/aarch64-linux-hw-point.h
> index c746a7622a0..7c694ff0882 100644
> --- a/gdb/nat/aarch64-linux-hw-point.h
> +++ b/gdb/nat/aarch64-linux-hw-point.h
> @@ -21,40 +21,7 @@
>   
>   #include "gdbsupport/break-common.h" /* For enum target_hw_bp_type.  */
>   
> -/* Macro definitions, data structures, and code for the hardware
> -   breakpoint and hardware watchpoint support follow.  We use the
> -   following abbreviations throughout the code:
> -
> -   hw - hardware
> -   bp - breakpoint
> -   wp - watchpoint  */
> -
> -/* Maximum number of hardware breakpoint and watchpoint registers.
> -   Neither of these values may exceed the width of dr_changed_t
> -   measured in bits.  */
> -
> -#define AARCH64_HBP_MAX_NUM 16
> -#define AARCH64_HWP_MAX_NUM 16
> -
> -/* Alignment requirement in bytes for addresses written to
> -   hardware breakpoint and watchpoint value registers.
> -
> -   A ptrace call attempting to set an address that does not meet the
> -   alignment criteria will fail.  Limited support has been provided in
> -   this port for unaligned watchpoints, such that from a GDB user
> -   perspective, an unaligned watchpoint may be requested.
> -
> -   This is achieved by minimally enlarging the watched area to meet the
> -   alignment requirement, and if necessary, splitting the watchpoint
> -   over several hardware watchpoint registers.  */
> -
> -#define AARCH64_HBP_ALIGNMENT 4
> -#define AARCH64_HWP_ALIGNMENT 8
> -
> -/* The maximum length of a memory region that can be watched by one
> -   hardware watchpoint register.  */
> -
> -#define AARCH64_HWP_MAX_LEN_PER_REG 8
> +#include "nat/aarch64-hw-point.h"
>   
>   /* ptrace hardware breakpoint resource info is formatted as follows:
>   
> @@ -68,24 +35,6 @@
>   #define AARCH64_DEBUG_NUM_SLOTS(x) ((x) & 0xff)
>   #define AARCH64_DEBUG_ARCH(x) (((x) >> 8) & 0xff)
>   
> -/* Macro for the expected version of the ARMv8-A debug architecture.  */
> -#define AARCH64_DEBUG_ARCH_V8 0x6
> -#define AARCH64_DEBUG_ARCH_V8_1 0x7
> -#define AARCH64_DEBUG_ARCH_V8_2 0x8
> -#define AARCH64_DEBUG_ARCH_V8_4 0x9
> -
> -/* ptrace expects control registers to be formatted as follows:
> -
> -   31                             13          5      3      1     0
> -   +--------------------------------+----------+------+------+----+
> -   |         RESERVED (SBZ)         |   MASK   | TYPE | PRIV | EN |
> -   +--------------------------------+----------+------+------+----+
> -
> -   The TYPE field is ignored for breakpoints.  */
> -
> -#define DR_CONTROL_ENABLED(ctrl)	(((ctrl) & 0x1) == 1)
> -#define DR_CONTROL_MASK(ctrl)		(((ctrl) >> 5) & 0xff)
> -
>   /* Each bit of a variable of this type is used to indicate whether a
>      hardware breakpoint or watchpoint setting has been changed since
>      the last update.
> @@ -133,29 +82,6 @@ typedef ULONGEST dr_changed_t;
>   #define DR_HAS_CHANGED(x) ((x) != 0)
>   #define DR_N_HAS_CHANGED(x, n) ((x) & ((dr_changed_t)1 << (n)))
>   
> -/* Structure for managing the hardware breakpoint/watchpoint resources.
> -   DR_ADDR_* stores the address, DR_CTRL_* stores the control register
> -   content, and DR_REF_COUNT_* counts the numbers of references to the
> -   corresponding bp/wp, by which way the limited hardware resources
> -   are not wasted on duplicated bp/wp settings (though so far gdb has
> -   done a good job by not sending duplicated bp/wp requests).  */
> -
> -struct aarch64_debug_reg_state
> -{
> -  /* hardware breakpoint */
> -  CORE_ADDR dr_addr_bp[AARCH64_HBP_MAX_NUM];
> -  unsigned int dr_ctrl_bp[AARCH64_HBP_MAX_NUM];
> -  unsigned int dr_ref_count_bp[AARCH64_HBP_MAX_NUM];
> -
> -  /* hardware watchpoint */
> -  /* Address aligned down to AARCH64_HWP_ALIGNMENT.  */
> -  CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM];
> -  /* Address as entered by user without any forced alignment.  */
> -  CORE_ADDR dr_addr_orig_wp[AARCH64_HWP_MAX_NUM];
> -  unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM];
> -  unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
> -};
> -
>   /* Per-thread arch-specific data we want to keep.  */
>   
>   struct arch_lwp_info
> @@ -167,35 +93,20 @@ struct arch_lwp_info
>     dr_changed_t dr_changed_wp;
>   };
>   
> -extern int aarch64_num_bp_regs;
> -extern int aarch64_num_wp_regs;
> +/* True if this kernel does not have the bug described by PR
> +   external/20207 (Linux >= 4.10).  A fixed kernel supports any
> +   contiguous range of bits in 8-bit byte DR_CONTROL_MASK.  A buggy
> +   kernel supports only 0x01, 0x03, 0x0f and 0xff.  We start by
> +   assuming the bug is fixed, and then detect the bug at
> +   PTRACE_SETREGSET time.  */
>   
> -unsigned int aarch64_watchpoint_offset (unsigned int ctrl);
> -unsigned int aarch64_watchpoint_length (unsigned int ctrl);
> -
> -int aarch64_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> -			       int len, int is_insert,
> -			       struct aarch64_debug_reg_state *state);
> -int aarch64_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
> -			       int len, int is_insert,
> -			       struct aarch64_debug_reg_state *state);
> +extern bool kernel_supports_any_contiguous_range;
>   
>   void aarch64_linux_set_debug_regs (struct aarch64_debug_reg_state *state,
>   				   int tid, int watchpoint);
>   
> -/* Return TRUE if there are any hardware breakpoints.  If WATCHPOINT is TRUE,
> -   check hardware watchpoints instead.  */
> -bool aarch64_linux_any_set_debug_regs_state (aarch64_debug_reg_state *state,
> -					     bool watchpoint);
> -
> -void aarch64_show_debug_reg_state (struct aarch64_debug_reg_state *state,
> -				   const char *func, CORE_ADDR addr,
> -				   int len, enum target_hw_bp_type type);
> -
>   void aarch64_linux_get_debug_reg_capacity (int tid);
>   
>   struct aarch64_debug_reg_state *aarch64_get_debug_reg_state (pid_t pid);
>   
> -int aarch64_linux_region_ok_for_watchpoint (CORE_ADDR addr, int len);
> -
>   #endif /* NAT_AARCH64_LINUX_HW_POINT_H */
> diff --git a/gdb/nat/aarch64-linux.c b/gdb/nat/aarch64-linux.c
> index b2ed8f9a2a5..421d1ecb53c 100644
> --- a/gdb/nat/aarch64-linux.c
> +++ b/gdb/nat/aarch64-linux.c
> @@ -81,9 +81,9 @@ aarch64_linux_new_thread (struct lwp_info *lwp)
>     /* If there are hardware breakpoints/watchpoints in the process then mark that
>        all the hardware breakpoint/watchpoint register pairs for this thread need
>        to be initialized (with data from aarch_process_info.debug_reg_state).  */
> -  if (aarch64_linux_any_set_debug_regs_state (state, false))
> +  if (aarch64_any_set_debug_regs_state (state, false))
>       DR_MARK_ALL_CHANGED (info->dr_changed_bp, aarch64_num_bp_regs);
> -  if (aarch64_linux_any_set_debug_regs_state (state, true))
> +  if (aarch64_any_set_debug_regs_state (state, true))
>       DR_MARK_ALL_CHANGED (info->dr_changed_wp, aarch64_num_wp_regs);
>   
>     lwp_set_arch_private_info (lwp, info);
> diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
> index 6e09b0eeb79..d37053628fc 100644
> --- a/gdbserver/configure.srv
> +++ b/gdbserver/configure.srv
> @@ -39,6 +39,7 @@ fi
>   
>   case "${gdbserver_host}" in
>     aarch64*-*-linux*)	srv_tgtobj="linux-aarch64-low.o"
> +			srv_tgtobj="$srv_tgtobj nat/aarch64-hw-point.o"
>   			srv_tgtobj="$srv_tgtobj nat/aarch64-linux-hw-point.o"
>   			srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
>   			srv_tgtobj="$srv_tgtobj linux-aarch32-tdesc.o"
> diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
> index aef69b34525..0091f998c63 100644
> --- a/gdbserver/linux-aarch64-low.cc
> +++ b/gdbserver/linux-aarch64-low.cc
> @@ -413,9 +413,10 @@ aarch64_target::low_insert_point (raw_bkpt_type type, CORE_ADDR addr,
>   
>     if (targ_type != hw_execute)
>       {
> -      if (aarch64_linux_region_ok_for_watchpoint (addr, len))
> +      if (aarch64_region_ok_for_watchpoint (addr, len))
>   	ret = aarch64_handle_watchpoint (targ_type, addr, len,
> -					 1 /* is_insert */, state);
> +					 1 /* is_insert */,
> +					 current_lwp_ptid (), state);
>         else
>   	ret = -1;
>       }
> @@ -429,7 +430,8 @@ aarch64_target::low_insert_point (raw_bkpt_type type, CORE_ADDR addr,
>   	  len = 2;
>   	}
>         ret = aarch64_handle_breakpoint (targ_type, addr, len,
> -				       1 /* is_insert */, state);
> +				       1 /* is_insert */, current_lwp_ptid (),
> +				       state);
>       }
>   
>     if (show_debug_regs)
> @@ -464,7 +466,7 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
>     if (targ_type != hw_execute)
>       ret =
>         aarch64_handle_watchpoint (targ_type, addr, len, 0 /* is_insert */,
> -				 state);
> +				 current_lwp_ptid (), state);
>     else
>       {
>         if (len == 3)
> @@ -475,7 +477,8 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
>   	  len = 2;
>   	}
>         ret = aarch64_handle_breakpoint (targ_type, addr, len,
> -				       0 /* is_insert */,  state);
> +				       0 /* is_insert */,  current_lwp_ptid (),
> +				       state);
>       }
>   
>     if (show_debug_regs)


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 02/12] x86-nat: Use an unordered_map to store per-pid debug reg state.
  2022-03-16 20:19 ` [PATCH v2 02/12] x86-nat: Use an unordered_map to store per-pid debug reg state John Baldwin
@ 2022-03-21 18:07   ` Pedro Alves
  0 siblings, 0 replies; 19+ messages in thread
From: Pedro Alves @ 2022-03-21 18:07 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

On 2022-03-16 20:19, John Baldwin wrote:
> This replaces a manual linked list which used O(n) lookup and
> removal.

This is OK, thanks.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 03/12] x86-nat: Add x86_lookup_debug_reg_state.
  2022-03-16 20:19 ` [PATCH v2 03/12] x86-nat: Add x86_lookup_debug_reg_state John Baldwin
@ 2022-03-21 18:10   ` Pedro Alves
  0 siblings, 0 replies; 19+ messages in thread
From: Pedro Alves @ 2022-03-21 18:10 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

On 2022-03-16 20:19, John Baldwin wrote:
> This function returns nullptr if debug register state does not
> yet exist for a given process rather than creating new state.

OK.

^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support
  2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
                   ` (11 preceding siblings ...)
  2022-03-16 20:19 ` [PATCH v2 12/12] Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64 John Baldwin
@ 2022-03-30 15:23 ` Luis Machado
  2022-03-30 15:31   ` Luis Machado
  12 siblings, 1 reply; 19+ messages in thread
From: Luis Machado @ 2022-03-30 15:23 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

Hi John,

This breaks aarch64's build. fprintf_unfiltered doesn't exist anymore, 
and this patch is still using it.

On 3/16/22 20:19, John Baldwin wrote:
> Changes since V1:
> 
> - The unordered_map<>'s in x86-nat.c and aarch64-nat.c both now store
>    objects directly rather than pointers to objects.
> 
> - Trimmed "Contributed by" notices from new files.
> 
> - I have compiled and (very lightly) tested this on Linux Aarch64.
>    By light testing I mean that I ran a test program with a harware
>    breakpoint set on main and it stopped correctly.  I haven't run a
>    full test suite as my Aarch64 test box is a lowly Raspberry Pi
>    for which such a run would take a fairly long time.
> 
> I still have some open questions about Patch 6 from the first
> version:
> 
> Patch 6 has an open question about how best to handle having a
> platform-specific hook for when debug registers have been changed.
> Right now we require the platform to supply the function that
> nat/aarch64-hw-point.c calls.  I did not choose to create an
> equivalent to x86_dr_low, but perhaps that sort of structure, or at
> least a function pointer should be used instead?
> 
> There is also some messiness around the Linux-specific
> kernel_supports_any_contiguous_range workaround in patch 6.
> 
> OTOH, some of the FreeBSD/x86 cleanups in the first half of the series
> (such as adding x86-fbsd-nat.*) might be nice to reuse in my XSAVE
> series, so if that half of the series is ok (first 5 patches), it
> might be nice to push that in sooner.
> 
> John Baldwin (12):
>    Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs
>      support.
>    x86-nat: Use an unordered_map to store per-pid debug reg state.
>    x86-nat: Add x86_lookup_debug_reg_state.
>    Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets.
>    fbsd-nat: Add a low_new_fork virtual method.
>    x86-fbsd-nat: Copy debug register state on fork.
>    nat: Split out platform-independent aarch64 debug register support.
>    aarch64: Add an aarch64_nat_target mixin class.
>    fbsd-nat: Add helper routine to fetch siginfo_t for a ptid.
>    fbsd-nat: Add a low_delete_thread virtual method.
>    fbsd-nat: Add a low_prepare_to_resume virtual method.
>    Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64.
> 
>   gdb/NEWS                         |   2 +
>   gdb/aarch64-fbsd-nat.c           | 260 ++++++++++++-
>   gdb/aarch64-linux-nat.c          | 352 +----------------
>   gdb/aarch64-nat.c                | 302 +++++++++++++++
>   gdb/aarch64-nat.h                | 109 ++++++
>   gdb/amd64-fbsd-nat.c             |  20 +-
>   gdb/configure.nat                |  12 +-
>   gdb/fbsd-nat.c                   |  28 +-
>   gdb/fbsd-nat.h                   |  18 +
>   gdb/i386-fbsd-nat.c              |  20 +-
>   gdb/nat/aarch64-hw-point.c       | 624 +++++++++++++++++++++++++++++++
>   gdb/nat/aarch64-hw-point.h       | 126 +++++++
>   gdb/nat/aarch64-linux-hw-point.c | 605 +-----------------------------
>   gdb/nat/aarch64-linux-hw-point.h | 105 +-----
>   gdb/nat/aarch64-linux.c          |   4 +-
>   gdb/x86-fbsd-nat.c               |  45 +++
>   gdb/x86-fbsd-nat.h               |  36 ++
>   gdb/x86-nat.c                    |  92 +----
>   gdb/x86-nat.h                    |   5 +
>   gdbserver/configure.srv          |   1 +
>   gdbserver/linux-aarch64-low.cc   |  13 +-
>   21 files changed, 1612 insertions(+), 1167 deletions(-)
>   create mode 100644 gdb/aarch64-nat.c
>   create mode 100644 gdb/aarch64-nat.h
>   create mode 100644 gdb/nat/aarch64-hw-point.c
>   create mode 100644 gdb/nat/aarch64-hw-point.h
>   create mode 100644 gdb/x86-fbsd-nat.c
>   create mode 100644 gdb/x86-fbsd-nat.h
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

* Re: [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support
  2022-03-30 15:23 ` [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support Luis Machado
@ 2022-03-30 15:31   ` Luis Machado
  0 siblings, 0 replies; 19+ messages in thread
From: Luis Machado @ 2022-03-30 15:31 UTC (permalink / raw)
  To: John Baldwin, gdb-patches

Nevermind. It was a later commit.

On 3/30/22 16:23, Luis Machado wrote:
> Hi John,
> 
> This breaks aarch64's build. fprintf_unfiltered doesn't exist anymore, 
> and this patch is still using it.
> 
> On 3/16/22 20:19, John Baldwin wrote:
>> Changes since V1:
>>
>> - The unordered_map<>'s in x86-nat.c and aarch64-nat.c both now store
>>    objects directly rather than pointers to objects.
>>
>> - Trimmed "Contributed by" notices from new files.
>>
>> - I have compiled and (very lightly) tested this on Linux Aarch64.
>>    By light testing I mean that I ran a test program with a harware
>>    breakpoint set on main and it stopped correctly.  I haven't run a
>>    full test suite as my Aarch64 test box is a lowly Raspberry Pi
>>    for which such a run would take a fairly long time.
>>
>> I still have some open questions about Patch 6 from the first
>> version:
>>
>> Patch 6 has an open question about how best to handle having a
>> platform-specific hook for when debug registers have been changed.
>> Right now we require the platform to supply the function that
>> nat/aarch64-hw-point.c calls.  I did not choose to create an
>> equivalent to x86_dr_low, but perhaps that sort of structure, or at
>> least a function pointer should be used instead?
>>
>> There is also some messiness around the Linux-specific
>> kernel_supports_any_contiguous_range workaround in patch 6.
>>
>> OTOH, some of the FreeBSD/x86 cleanups in the first half of the series
>> (such as adding x86-fbsd-nat.*) might be nice to reuse in my XSAVE
>> series, so if that half of the series is ok (first 5 patches), it
>> might be nice to push that in sooner.
>>
>> John Baldwin (12):
>>    Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs
>>      support.
>>    x86-nat: Use an unordered_map to store per-pid debug reg state.
>>    x86-nat: Add x86_lookup_debug_reg_state.
>>    Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets.
>>    fbsd-nat: Add a low_new_fork virtual method.
>>    x86-fbsd-nat: Copy debug register state on fork.
>>    nat: Split out platform-independent aarch64 debug register support.
>>    aarch64: Add an aarch64_nat_target mixin class.
>>    fbsd-nat: Add helper routine to fetch siginfo_t for a ptid.
>>    fbsd-nat: Add a low_delete_thread virtual method.
>>    fbsd-nat: Add a low_prepare_to_resume virtual method.
>>    Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64.
>>
>>   gdb/NEWS                         |   2 +
>>   gdb/aarch64-fbsd-nat.c           | 260 ++++++++++++-
>>   gdb/aarch64-linux-nat.c          | 352 +----------------
>>   gdb/aarch64-nat.c                | 302 +++++++++++++++
>>   gdb/aarch64-nat.h                | 109 ++++++
>>   gdb/amd64-fbsd-nat.c             |  20 +-
>>   gdb/configure.nat                |  12 +-
>>   gdb/fbsd-nat.c                   |  28 +-
>>   gdb/fbsd-nat.h                   |  18 +
>>   gdb/i386-fbsd-nat.c              |  20 +-
>>   gdb/nat/aarch64-hw-point.c       | 624 +++++++++++++++++++++++++++++++
>>   gdb/nat/aarch64-hw-point.h       | 126 +++++++
>>   gdb/nat/aarch64-linux-hw-point.c | 605 +-----------------------------
>>   gdb/nat/aarch64-linux-hw-point.h | 105 +-----
>>   gdb/nat/aarch64-linux.c          |   4 +-
>>   gdb/x86-fbsd-nat.c               |  45 +++
>>   gdb/x86-fbsd-nat.h               |  36 ++
>>   gdb/x86-nat.c                    |  92 +----
>>   gdb/x86-nat.h                    |   5 +
>>   gdbserver/configure.srv          |   1 +
>>   gdbserver/linux-aarch64-low.cc   |  13 +-
>>   21 files changed, 1612 insertions(+), 1167 deletions(-)
>>   create mode 100644 gdb/aarch64-nat.c
>>   create mode 100644 gdb/aarch64-nat.h
>>   create mode 100644 gdb/nat/aarch64-hw-point.c
>>   create mode 100644 gdb/nat/aarch64-hw-point.h
>>   create mode 100644 gdb/x86-fbsd-nat.c
>>   create mode 100644 gdb/x86-fbsd-nat.h
>>
> 


^ permalink raw reply	[flat|nested] 19+ messages in thread

end of thread, other threads:[~2022-03-30 15:31 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-16 20:19 [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support John Baldwin
2022-03-16 20:19 ` [PATCH v2 01/12] Remove USE_SIGTRAP_SIGINFO condition for FreeBSD/x86 debug regs support John Baldwin
2022-03-16 20:19 ` [PATCH v2 02/12] x86-nat: Use an unordered_map to store per-pid debug reg state John Baldwin
2022-03-21 18:07   ` Pedro Alves
2022-03-16 20:19 ` [PATCH v2 03/12] x86-nat: Add x86_lookup_debug_reg_state John Baldwin
2022-03-21 18:10   ` Pedro Alves
2022-03-16 20:19 ` [PATCH v2 04/12] Add an x86_fbsd_nat_target mixin class for FreeBSD x86 native targets John Baldwin
2022-03-16 20:19 ` [PATCH v2 05/12] fbsd-nat: Add a low_new_fork virtual method John Baldwin
2022-03-16 20:19 ` [PATCH v2 06/12] x86-fbsd-nat: Copy debug register state on fork John Baldwin
2022-03-16 20:19 ` [PATCH v2 07/12] nat: Split out platform-independent aarch64 debug register support John Baldwin
2022-03-17 15:37   ` Luis Machado
2022-03-16 20:19 ` [PATCH v2 08/12] aarch64: Add an aarch64_nat_target mixin class John Baldwin
2022-03-17 15:35   ` Luis Machado
2022-03-16 20:19 ` [PATCH v2 09/12] fbsd-nat: Add helper routine to fetch siginfo_t for a ptid John Baldwin
2022-03-16 20:19 ` [PATCH v2 10/12] fbsd-nat: Add a low_delete_thread virtual method John Baldwin
2022-03-16 20:19 ` [PATCH v2 11/12] fbsd-nat: Add a low_prepare_to_resume " John Baldwin
2022-03-16 20:19 ` [PATCH v2 12/12] Add support for hardware breakpoints/watchpoints on FreeBSD/Aarch64 John Baldwin
2022-03-30 15:23 ` [PATCH v2 00/12] FreeBSD/aarch64 hardware watchpoint support Luis Machado
2022-03-30 15:31   ` Luis Machado

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).