Blame SOURCES/gdb-rhbz1347993-aarch64-hw-watchpoint.patch

41ba02
commit a3b60e4588606354b93508a0008a5ca04b68fad8
41ba02
Author: Jan Kratochvil <jan.kratochvil@redhat.com>
41ba02
Date:   Fri May 4 22:22:04 2018 +0200
41ba02
41ba02
    aarch64: PR 19806: watchpoints: false negatives + PR 20207 contiguous ones
41ba02
    
41ba02
    Some unaligned watchpoints were currently missed.
41ba02
    
41ba02
    On old kernels as specified in
41ba02
            kernel RFE: aarch64: ptrace: BAS: Support any contiguous range (edit)
41ba02
            https://sourceware.org/bugzilla/show_bug.cgi?id=20207
41ba02
    after this patch some other unaligned watchpoints will get reported as false
41ba02
    positives.
41ba02
    
41ba02
    With new kernels all the watchpoints should work exactly.
41ba02
    
41ba02
    There may be a regresion that it now less merges watchpoints so that with
41ba02
    multiple overlapping watchpoints it may run out of the 4 hardware watchpoint
41ba02
    registers.  But as discussed in the original thread GDB needs some generic
41ba02
    watchpoints merging framework to be used by all the target specific code.
41ba02
    Even current FSF GDB code does not merge it perfectly.  Also with the more
41ba02
    precise watchpoints one can technically merge them less.  And I do not think
41ba02
    it matters too much to improve mergeability only for old kernels.
41ba02
    Still even on new kernels some better merging logic would make sense.
41ba02
    
41ba02
    There remains one issue:
41ba02
            kernel-4.15.14-300.fc27.armv7hl
41ba02
            FAIL: gdb.base/watchpoint-unaligned.exp: continue
41ba02
            FAIL: gdb.base/watchpoint-unaligned.exp: continue
41ba02
            (gdb) continue
41ba02
            Continuing.
41ba02
            Unexpected error setting watchpoint: Invalid argument.
41ba02
            (gdb) FAIL: gdb.base/watchpoint-unaligned.exp: continue
41ba02
    But that looks as a kernel bug to me.
41ba02
    (1) It is not a regression by this patch.
41ba02
    (2) It is unrelated to this patch.
41ba02
    
41ba02
    gdb/ChangeLog
41ba02
    2018-05-04  Jan Kratochvil  <jan.kratochvil@redhat.com>
41ba02
                Pedro Alves <palves@redhat.com>
41ba02
    
41ba02
            PR breakpoints/19806 and support for PR external/20207.
41ba02
            * NEWS: Mention Aarch64 watchpoint improvements.
41ba02
            * aarch64-linux-nat.c (aarch64_linux_stopped_data_address): Fix missed
41ba02
            watchpoints and PR external/20207 watchpoints.
41ba02
            * nat/aarch64-linux-hw-point.c
41ba02
            (kernel_supports_any_contiguous_range): New.
41ba02
            (aarch64_watchpoint_offset): New.
41ba02
            (aarch64_watchpoint_length): Support PR external/20207 watchpoints.
41ba02
            (aarch64_point_encode_ctrl_reg): New parameter offset, new asserts.
41ba02
            (aarch64_point_is_aligned): Support PR external/20207 watchpoints.
41ba02
            (aarch64_align_watchpoint): New parameters aligned_offset_p and
41ba02
            next_addr_orig_p.  Support PR external/20207 watchpoints.
41ba02
            (aarch64_downgrade_regs): New.
41ba02
            (aarch64_dr_state_insert_one_point): New parameters offset and
41ba02
            addr_orig.
41ba02
            (aarch64_dr_state_remove_one_point): Likewise.
41ba02
            (aarch64_handle_breakpoint): Update caller.
41ba02
            (aarch64_handle_aligned_watchpoint): Likewise.
41ba02
            (aarch64_handle_unaligned_watchpoint): Support addr_orig and
41ba02
            aligned_offset.
41ba02
            (aarch64_linux_set_debug_regs): Remove const from state.  Call
41ba02
            aarch64_downgrade_regs.
41ba02
            (aarch64_show_debug_reg_state): Print also dr_addr_orig_wp.
41ba02
            * nat/aarch64-linux-hw-point.h (DR_CONTROL_LENGTH): Rename to ...
41ba02
            (DR_CONTROL_MASK): ... this.
41ba02
            (struct aarch64_debug_reg_state): New field dr_addr_orig_wp.
41ba02
            (unsigned int aarch64_watchpoint_offset): New prototype.
41ba02
            (aarch64_linux_set_debug_regs): Remove const from state.
41ba02
            * utils.c (align_up, align_down): Move to ...
41ba02
            * common/common-utils.c (align_up, align_down): ... here.
41ba02
            * utils.h (align_up, align_down): Move to ...
41ba02
            * common/common-utils.h (align_up, align_down): ... here.
41ba02
    
41ba02
    gdb/gdbserver/ChangeLog
41ba02
    2018-05-04  Jan Kratochvil  <jan.kratochvil@redhat.com>
41ba02
                Pedro Alves <palves@redhat.com>
41ba02
    
41ba02
            * linux-aarch64-low.c (aarch64_stopped_data_address):
41ba02
            Likewise.
41ba02
    
41ba02
    gdb/testsuite/ChangeLog
41ba02
    2018-05-04  Jan Kratochvil  <jan.kratochvil@redhat.com>
41ba02
                Pedro Alves <palves@redhat.com>
41ba02
    
41ba02
            PR breakpoints/19806 and support for PR external/20207.
41ba02
            * gdb.base/watchpoint-unaligned.c: New file.
41ba02
            * gdb.base/watchpoint-unaligned.exp: New file.
41ba02
41ba02
### a/gdb/ChangeLog
41ba02
### b/gdb/ChangeLog
41ba02
## -1,3 +1,39 @@
41ba02
+2018-05-04  Jan Kratochvil  <jan.kratochvil@redhat.com>
41ba02
+	    Pedro Alves <palves@redhat.com>
41ba02
+
41ba02
+	PR breakpoints/19806 and support for PR external/20207.
41ba02
+	* NEWS: Mention Aarch64 watchpoint improvements.
41ba02
+	* aarch64-linux-nat.c (aarch64_linux_stopped_data_address): Fix missed
41ba02
+	watchpoints and PR external/20207 watchpoints.
41ba02
+	* nat/aarch64-linux-hw-point.c
41ba02
+	(kernel_supports_any_contiguous_range): New.
41ba02
+	(aarch64_watchpoint_offset): New.
41ba02
+	(aarch64_watchpoint_length): Support PR external/20207 watchpoints.
41ba02
+	(aarch64_point_encode_ctrl_reg): New parameter offset, new asserts.
41ba02
+	(aarch64_point_is_aligned): Support PR external/20207 watchpoints.
41ba02
+	(aarch64_align_watchpoint): New parameters aligned_offset_p and
41ba02
+	next_addr_orig_p.  Support PR external/20207 watchpoints.
41ba02
+	(aarch64_downgrade_regs): New.
41ba02
+	(aarch64_dr_state_insert_one_point): New parameters offset and
41ba02
+	addr_orig.
41ba02
+	(aarch64_dr_state_remove_one_point): Likewise.
41ba02
+	(aarch64_handle_breakpoint): Update caller.
41ba02
+	(aarch64_handle_aligned_watchpoint): Likewise.
41ba02
+	(aarch64_handle_unaligned_watchpoint): Support addr_orig and
41ba02
+	aligned_offset.
41ba02
+	(aarch64_linux_set_debug_regs): Remove const from state.  Call
41ba02
+	aarch64_downgrade_regs.
41ba02
+	(aarch64_show_debug_reg_state): Print also dr_addr_orig_wp.
41ba02
+	* nat/aarch64-linux-hw-point.h (DR_CONTROL_LENGTH): Rename to ...
41ba02
+	(DR_CONTROL_MASK): ... this.
41ba02
+	(struct aarch64_debug_reg_state): New field dr_addr_orig_wp.
41ba02
+	(unsigned int aarch64_watchpoint_offset): New prototype.
41ba02
+	(aarch64_linux_set_debug_regs): Remove const from state.
41ba02
+	* utils.c (align_up, align_down): Move to ...
41ba02
+	* common/common-utils.c (align_up, align_down): ... here.
41ba02
+	* utils.h (align_up, align_down): Move to ...
41ba02
+	* common/common-utils.h (align_up, align_down): ... here.
41ba02
+
41ba02
 2018-05-04  Joel Brobecker  <brobecker@adacore.com>
41ba02
 
41ba02
 	* sparc-tdep.c (sparc_structure_return_p): Re-implement to
41ba02
Index: gdb-7.6.1/gdb/NEWS
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/NEWS	2018-05-05 17:12:48.170769374 +0200
41ba02
+++ gdb-7.6.1/gdb/NEWS	2018-05-05 17:12:49.569781872 +0200
41ba02
@@ -4,6 +4,16 @@
41ba02
 * Newly installed $prefix/bin/gcore acts as a shell interface for the
41ba02
   GDB command gcore.
41ba02
 
41ba02
+* Aarch64/Linux hardware watchpoints improvements
41ba02
+
41ba02
+  Hardware watchpoints on unaligned addresses are now properly
41ba02
+  supported when running Linux kernel 4.10 or higher: read and access
41ba02
+  watchpoints are no longer spuriously missed, and all watchpoints
41ba02
+  lengths between 1 and 8 bytes are supported.  On older kernels,
41ba02
+  watchpoints set on unaligned addresses are no longer missed, with
41ba02
+  the tradeoff that there is a possibility of false hits being
41ba02
+  reported.
41ba02
+
41ba02
 *** Changes in GDB 7.6
41ba02
 
41ba02
 * Target record has been renamed to record-full.
41ba02
Index: gdb-7.6.1/gdb/aarch64-linux-nat.c
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/aarch64-linux-nat.c	2018-05-05 17:12:47.474763150 +0200
41ba02
+++ gdb-7.6.1/gdb/aarch64-linux-nat.c	2018-05-05 17:12:49.570781881 +0200
41ba02
@@ -45,6 +45,18 @@
41ba02
 #define TRAP_HWBKPT 0x0004
41ba02
 #endif
41ba02
 
41ba02
+/* ptrace expects control registers to be formatted as follows:
41ba02
+
41ba02
+   31                             13          5      3      1     0
41ba02
+   +--------------------------------+----------+------+------+----+
41ba02
+   |         RESERVED (SBZ)         |   MASK   | TYPE | PRIV | EN |
41ba02
+   +--------------------------------+----------+------+------+----+
41ba02
+
41ba02
+   The TYPE field is ignored for breakpoints.  */
41ba02
+
41ba02
+#define DR_CONTROL_ENABLED(ctrl)	(((ctrl) & 0x1) == 1)
41ba02
+#define DR_CONTROL_MASK(ctrl)		(((ctrl) >> 5) & 0xff)
41ba02
+
41ba02
 /* On GNU/Linux, threads are implemented as pseudo-processes, in which
41ba02
    case we may be tracing more than one process at a time.  In that
41ba02
    case, inferior_ptid will contain the main process ID and the
41ba02
@@ -118,6 +130,29 @@
41ba02
 static int aarch64_num_bp_regs;
41ba02
 static int aarch64_num_wp_regs;
41ba02
 
41ba02
+/* True if this kernel does not have the bug described by PR
41ba02
+   external/20207 (Linux >= 4.10).  A fixed kernel supports any
41ba02
+   contiguous range of bits in 8-bit byte DR_CONTROL_MASK.  A buggy
41ba02
+   kernel supports only 0x01, 0x03, 0x0f and 0xff.  We start by
41ba02
+   assuming the bug is fixed, and then detect the bug at
41ba02
+   PTRACE_SETREGSET time.  */
41ba02
+static int kernel_supports_any_contiguous_range = 1;
41ba02
+
41ba02
+/* Return starting byte 0..7 incl. of a watchpoint encoded by CTRL.  */
41ba02
+
41ba02
+static unsigned int
41ba02
+aarch64_watchpoint_offset (unsigned int ctrl)
41ba02
+{
41ba02
+  uint8_t mask = DR_CONTROL_MASK (ctrl);
41ba02
+  unsigned retval;
41ba02
+
41ba02
+  /* Shift out bottom zeros.  */
41ba02
+  for (retval = 0; mask && (mask & 1) == 0; ++retval)
41ba02
+    mask >>= 1;
41ba02
+
41ba02
+  return retval;
41ba02
+}
41ba02
+
41ba02
 /* Debugging of hardware breakpoint/watchpoint support.  */
41ba02
 
41ba02
 static int debug_hw_points;
41ba02
@@ -184,7 +219,10 @@
41ba02
   unsigned int dr_ref_count_bp[AARCH64_HBP_MAX_NUM];
41ba02
 
41ba02
   /* hardware watchpoint */
41ba02
+  /* Address aligned down to AARCH64_HWP_ALIGNMENT.  */
41ba02
   CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM];
41ba02
+  /* Address as entered by user without any forced alignment.  */
41ba02
+  CORE_ADDR dr_addr_orig_wp[AARCH64_HWP_MAX_NUM];
41ba02
   unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM];
41ba02
   unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
41ba02
 };
41ba02
@@ -299,11 +337,76 @@
41ba02
   dr_changed_t dr_changed_wp;
41ba02
 };
41ba02
 
41ba02
+/* Reconfigure STATE to be compatible with Linux kernels with the PR
41ba02
+   external/20207 bug.  This is called when
41ba02
+   KERNEL_SUPPORTS_ANY_CONTIGUOUS_RANGE transitions to false.  Note we
41ba02
+   don't try to support combining watchpoints with matching (and thus
41ba02
+   shared) masks, as it's too late when we get here.  On buggy
41ba02
+   kernels, GDB will try to first setup the perfect matching ranges,
41ba02
+   which will run out of registers before this function can merge
41ba02
+   them.  It doesn't look like worth the effort to improve that, given
41ba02
+   eventually buggy kernels will be phased out.  */
41ba02
+
41ba02
+static void aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state, int is_watchpoint, unsigned int idx);
41ba02
+
41ba02
+static void
41ba02
+aarch64_downgrade_regs (struct aarch64_debug_reg_state *state)
41ba02
+{
41ba02
+  int i;
41ba02
+
41ba02
+  for (i = 0; i < aarch64_num_wp_regs; ++i)
41ba02
+    if ((state->dr_ctrl_wp[i] & 1) != 0)
41ba02
+      {
41ba02
+	uint8_t mask_orig;
41ba02
+	static const uint8_t old_valid[] = { 0x01, 0x03, 0x0f, 0xff };
41ba02
+	int old_validi;
41ba02
+	uint8_t mask = 0;
41ba02
+	int j;
41ba02
+
41ba02
+	gdb_assert (state->dr_ref_count_wp[i] != 0);
41ba02
+	mask_orig = (state->dr_ctrl_wp[i] >> 5) & 0xff;
41ba02
+	gdb_assert (mask_orig != 0);
41ba02
+	for (old_validi = 0; old_validi < sizeof(old_valid) / sizeof (*old_valid); old_validi++) {
41ba02
+	  const uint8_t old_mask = old_valid[old_validi];
41ba02
+	  if (mask_orig <= old_mask)
41ba02
+	    {
41ba02
+	      mask = old_mask;
41ba02
+	      break;
41ba02
+	    }
41ba02
+	}
41ba02
+	gdb_assert (mask != 0);
41ba02
+
41ba02
+	/* No update needed for this watchpoint?  */
41ba02
+	if (mask == mask_orig)
41ba02
+	  continue;
41ba02
+	state->dr_ctrl_wp[i] |= mask << 5;
41ba02
+	state->dr_addr_wp[i]
41ba02
+	  = align_down (state->dr_addr_wp[i], AARCH64_HWP_ALIGNMENT);
41ba02
+
41ba02
+	/* Try to match duplicate entries.  */
41ba02
+	for (j = 0; j < i; ++j)
41ba02
+	  if ((state->dr_ctrl_wp[j] & 1) != 0
41ba02
+	      && state->dr_addr_wp[j] == state->dr_addr_wp[i]
41ba02
+	      && state->dr_addr_orig_wp[j] == state->dr_addr_orig_wp[i]
41ba02
+	      && state->dr_ctrl_wp[j] == state->dr_ctrl_wp[i])
41ba02
+	    {
41ba02
+	      state->dr_ref_count_wp[j] += state->dr_ref_count_wp[i];
41ba02
+	      state->dr_ref_count_wp[i] = 0;
41ba02
+	      state->dr_addr_wp[i] = 0;
41ba02
+	      state->dr_addr_orig_wp[i] = 0;
41ba02
+	      state->dr_ctrl_wp[i] &= ~1;
41ba02
+	      break;
41ba02
+	    }
41ba02
+
41ba02
+	aarch64_notify_debug_reg_change (state, 1 /* is_watchpoint */, i);
41ba02
+      }
41ba02
+}
41ba02
+
41ba02
 /* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
41ba02
    registers with data from *STATE.  */
41ba02
 
41ba02
 static void
41ba02
-aarch64_linux_set_debug_regs (const struct aarch64_debug_reg_state *state,
41ba02
+aarch64_linux_set_debug_regs (struct aarch64_debug_reg_state *state,
41ba02
 			      int tid, int watchpoint)
41ba02
 {
41ba02
   int i, count;
41ba02
@@ -331,7 +434,18 @@
41ba02
   if (ptrace (PTRACE_SETREGSET, tid,
41ba02
 	      watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK,
41ba02
 	      (void *) &iov))
41ba02
-    error (_("Unexpected error setting hardware debug registers"));
41ba02
+    {
41ba02
+      /* Handle Linux kernels with the PR external/20207 bug.  */
41ba02
+      if (watchpoint && errno == EINVAL
41ba02
+	  && kernel_supports_any_contiguous_range)
41ba02
+	{
41ba02
+	  kernel_supports_any_contiguous_range = 0;
41ba02
+	  aarch64_downgrade_regs (state);
41ba02
+	  aarch64_linux_set_debug_regs (state, tid, watchpoint);
41ba02
+	  return;
41ba02
+	}
41ba02
+      error (_("Unexpected error setting hardware debug registers"));
41ba02
+    }
41ba02
 }
41ba02
 
41ba02
 struct aarch64_dr_update_callback_param
41ba02
@@ -452,8 +566,8 @@
41ba02
   fprintf_unfiltered (gdb_stdlog, "\tWATCHPOINTs:\n");
41ba02
   for (i = 0; i < aarch64_num_wp_regs; i++)
41ba02
     fprintf_unfiltered (gdb_stdlog,
41ba02
-			"\tWP%d: addr=0x%08lx, ctrl=0x%08x, ref.count=%d\n",
41ba02
-			i, state->dr_addr_wp[i],
41ba02
+			"\tWP%d: addr=0x%08lx (orig=0x%08lx), ctrl=0x%08x, ref.count=%d\n",
41ba02
+			i, state->dr_addr_wp[i], state->dr_addr_orig_wp[i],
41ba02
 			state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
41ba02
 }
41ba02
 
41ba02
@@ -850,28 +964,30 @@
41ba02
 }
41ba02
 
41ba02
 /* Given the (potentially unaligned) watchpoint address in ADDR and
41ba02
-   length in LEN, return the aligned address and aligned length in
41ba02
-   *ALIGNED_ADDR_P and *ALIGNED_LEN_P, respectively.  The returned
41ba02
-   aligned address and length will be valid values to write to the
41ba02
-   hardware watchpoint value and control registers.
41ba02
+   length in LEN, return the aligned address, offset from that base
41ba02
+   address, and aligned length in *ALIGNED_ADDR_P, *ALIGNED_OFFSET_P
41ba02
+   and *ALIGNED_LEN_P, respectively.  The returned values will be
41ba02
+   valid values to write to the hardware watchpoint value and control
41ba02
+   registers.
41ba02
 
41ba02
    The given watchpoint may get truncated if more than one hardware
41ba02
    register is needed to cover the watched region.  *NEXT_ADDR_P
41ba02
    and *NEXT_LEN_P, if non-NULL, will return the address and length
41ba02
    of the remaining part of the watchpoint (which can be processed
41ba02
-   by calling this routine again to generate another aligned address
41ba02
-   and length pair.
41ba02
+   by calling this routine again to generate another aligned address,
41ba02
+   offset and length tuple.
41ba02
 
41ba02
    See the comment above the function of the same name in
41ba02
    gdbserver/linux-aarch64-low.c for more information.  */
41ba02
 
41ba02
 static void
41ba02
 aarch64_align_watchpoint (CORE_ADDR addr, int len, CORE_ADDR *aligned_addr_p,
41ba02
-			  int *aligned_len_p, CORE_ADDR *next_addr_p,
41ba02
-			  int *next_len_p)
41ba02
+			  int *aligned_offset_p, int *aligned_len_p,
41ba02
+			  CORE_ADDR *next_addr_p, int *next_len_p,
41ba02
+			  CORE_ADDR *next_addr_orig_p)
41ba02
 {
41ba02
   int aligned_len;
41ba02
-  unsigned int offset;
41ba02
+  unsigned int offset, aligned_offset;
41ba02
   CORE_ADDR aligned_addr;
41ba02
   const unsigned int alignment = AARCH64_HWP_ALIGNMENT;
41ba02
   const unsigned int max_wp_len = AARCH64_HWP_MAX_LEN_PER_REG;
41ba02
@@ -882,10 +998,12 @@
41ba02
   if (len <= 0)
41ba02
     return;
41ba02
 
41ba02
-  /* Address to be put into the hardware watchpoint value register
41ba02
-     must be aligned.  */
41ba02
+  /* The address put into the hardware watchpoint value register must
41ba02
+     be aligned.  */
41ba02
   offset = addr & (alignment - 1);
41ba02
   aligned_addr = addr - offset;
41ba02
+  aligned_offset
41ba02
+    = kernel_supports_any_contiguous_range ? addr & (alignment - 1) : 0;
41ba02
 
41ba02
   gdb_assert (offset >= 0 && offset < alignment);
41ba02
   gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
41ba02
@@ -893,9 +1011,10 @@
41ba02
 
41ba02
   if (offset + len >= max_wp_len)
41ba02
     {
41ba02
-      /* Need more than one watchpoint registers; truncate it at the
41ba02
+      /* Need more than one watchpoint register; truncate at the
41ba02
          alignment boundary.  */
41ba02
-      aligned_len = max_wp_len;
41ba02
+      aligned_len
41ba02
+	= max_wp_len - (kernel_supports_any_contiguous_range ? offset : 0);
41ba02
       len -= (max_wp_len - offset);
41ba02
       addr += (max_wp_len - offset);
41ba02
       gdb_assert ((addr & (alignment - 1)) == 0);
41ba02
@@ -908,19 +1027,24 @@
41ba02
 	aligned_len_array[AARCH64_HWP_MAX_LEN_PER_REG] =
41ba02
 	{ 1, 2, 4, 4, 8, 8, 8, 8 };
41ba02
 
41ba02
-      aligned_len = aligned_len_array[offset + len - 1];
41ba02
+      aligned_len = (kernel_supports_any_contiguous_range
41ba02
+		     ? len : aligned_len_array[offset + len - 1]);
41ba02
       addr += len;
41ba02
       len = 0;
41ba02
     }
41ba02
 
41ba02
   if (aligned_addr_p)
41ba02
     *aligned_addr_p = aligned_addr;
41ba02
+  if (aligned_offset_p)
41ba02
+    *aligned_offset_p = aligned_offset;
41ba02
   if (aligned_len_p)
41ba02
     *aligned_len_p = aligned_len;
41ba02
   if (next_addr_p)
41ba02
     *next_addr_p = addr;
41ba02
   if (next_len_p)
41ba02
     *next_len_p = len;
41ba02
+  if (next_addr_orig_p)
41ba02
+    *next_addr_orig_p = align_down (*next_addr_orig_p + alignment, alignment);
41ba02
 }
41ba02
 
41ba02
 /* Returns the number of hardware watchpoints of type TYPE that we can
41ba02
@@ -946,41 +1070,29 @@
41ba02
   return 1;
41ba02
 }
41ba02
 
41ba02
-/* ptrace expects control registers to be formatted as follows:
41ba02
-
41ba02
-   31                             13          5      3      1     0
41ba02
-   +--------------------------------+----------+------+------+----+
41ba02
-   |         RESERVED (SBZ)         |  LENGTH  | TYPE | PRIV | EN |
41ba02
-   +--------------------------------+----------+------+------+----+
41ba02
-
41ba02
-   The TYPE field is ignored for breakpoints.  */
41ba02
-
41ba02
-#define DR_CONTROL_ENABLED(ctrl)	(((ctrl) & 0x1) == 1)
41ba02
-#define DR_CONTROL_LENGTH(ctrl)		(((ctrl) >> 5) & 0xff)
41ba02
-
41ba02
 /* Utility function that returns the length in bytes of a watchpoint
41ba02
    according to the content of a hardware debug control register CTRL.
41ba02
-   Note that the kernel currently only supports the following Byte
41ba02
-   Address Select (BAS) values: 0x1, 0x3, 0xf and 0xff, which means
41ba02
-   that for a hardware watchpoint, its valid length can only be 1
41ba02
-   byte, 2 bytes, 4 bytes or 8 bytes.  */
41ba02
+   Any contiguous range of bytes in CTRL is supported.  The returned
41ba02
+   value can be between 0..8 (inclusive).  */
41ba02
 
41ba02
 static inline unsigned int
41ba02
 aarch64_watchpoint_length (unsigned int ctrl)
41ba02
 {
41ba02
-  switch (DR_CONTROL_LENGTH (ctrl))
41ba02
-    {
41ba02
-    case 0x01:
41ba02
-      return 1;
41ba02
-    case 0x03:
41ba02
-      return 2;
41ba02
-    case 0x0f:
41ba02
-      return 4;
41ba02
-    case 0xff:
41ba02
-      return 8;
41ba02
-    default:
41ba02
-      return 0;
41ba02
-    }
41ba02
+  uint8_t mask = DR_CONTROL_MASK (ctrl);
41ba02
+  unsigned retval;
41ba02
+
41ba02
+  /* Shift out bottom zeros.  */
41ba02
+  mask >>= aarch64_watchpoint_offset (ctrl);
41ba02
+
41ba02
+  /* Count bottom ones.  */
41ba02
+  for (retval = 0; (mask & 1) != 0; ++retval)
41ba02
+    mask >>= 1;
41ba02
+
41ba02
+  if (mask != 0)
41ba02
+    error (_("Unexpected hardware watchpoint length register value 0x%x"),
41ba02
+	   DR_CONTROL_MASK (ctrl));
41ba02
+
41ba02
+  return retval;
41ba02
 }
41ba02
 
41ba02
 /* Given the hardware breakpoint or watchpoint type TYPE and its
41ba02
@@ -988,10 +1100,13 @@
41ba02
    breakpoint/watchpoint control register.  */
41ba02
 
41ba02
 static unsigned int
41ba02
-aarch64_point_encode_ctrl_reg (int type, int len)
41ba02
+aarch64_point_encode_ctrl_reg (int type, int offset, int len)
41ba02
 {
41ba02
   unsigned int ctrl, ttype;
41ba02
 
41ba02
+  gdb_assert (offset == 0 || kernel_supports_any_contiguous_range);
41ba02
+  gdb_assert (offset + len <= AARCH64_HWP_MAX_LEN_PER_REG);
41ba02
+
41ba02
   /* type */
41ba02
   switch (type)
41ba02
     {
41ba02
@@ -1012,8 +1127,8 @@
41ba02
     }
41ba02
   ctrl = ttype << 3;
41ba02
 
41ba02
-  /* length bitmask */
41ba02
-  ctrl |= ((1 << len) - 1) << 5;
41ba02
+  /* offset and length bitmask */
41ba02
+  ctrl |= ((1 << len) - 1) << (5 + offset);
41ba02
   /* enabled at el0 */
41ba02
   ctrl |= (2 << 1) | 1;
41ba02
 
41ba02
@@ -1042,7 +1157,10 @@
41ba02
   if (addr & (alignment - 1))
41ba02
     return 0;
41ba02
 
41ba02
-  if (len != 8 && len != 4 && len != 2 && len != 1)
41ba02
+  if ((!kernel_supports_any_contiguous_range
41ba02
+       && len != 8 && len != 4 && len != 2 && len != 1)
41ba02
+      || (kernel_supports_any_contiguous_range
41ba02
+	  && (len < 1 || len > 8)))
41ba02
     return 0;
41ba02
 
41ba02
   return 1;
41ba02
@@ -1053,11 +1171,12 @@
41ba02
 
41ba02
 static int
41ba02
 aarch64_dr_state_insert_one_point (struct aarch64_debug_reg_state *state,
41ba02
-				   int type, CORE_ADDR addr, int len)
41ba02
+				   int type, CORE_ADDR addr, int offset, int len,
41ba02
+				   CORE_ADDR addr_orig)
41ba02
 {
41ba02
   int i, idx, num_regs, is_watchpoint;
41ba02
   unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
41ba02
-  CORE_ADDR *dr_addr_p;
41ba02
+  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
41ba02
 
41ba02
   /* Set up state pointers.  */
41ba02
   is_watchpoint = (type != hw_execute);
41ba02
@@ -1066,6 +1185,7 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_wp_regs;
41ba02
       dr_addr_p = state->dr_addr_wp;
41ba02
+      dr_addr_orig_p = state->dr_addr_orig_wp;
41ba02
       dr_ctrl_p = state->dr_ctrl_wp;
41ba02
       dr_ref_count = state->dr_ref_count_wp;
41ba02
     }
41ba02
@@ -1073,11 +1193,12 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_bp_regs;
41ba02
       dr_addr_p = state->dr_addr_bp;
41ba02
+      dr_addr_orig_p = NULL;
41ba02
       dr_ctrl_p = state->dr_ctrl_bp;
41ba02
       dr_ref_count = state->dr_ref_count_bp;
41ba02
     }
41ba02
 
41ba02
-  ctrl = aarch64_point_encode_ctrl_reg (type, len);
41ba02
+  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
41ba02
 
41ba02
   /* Find an existing or free register in our cache.  */
41ba02
   idx = -1;
41ba02
@@ -1089,7 +1210,9 @@
41ba02
 	  idx = i;
41ba02
 	  /* no break; continue hunting for an existing one.  */
41ba02
 	}
41ba02
-      else if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
41ba02
+      else if (dr_addr_p[i] == addr
41ba02
+	       && (dr_addr_orig_p == NULL || dr_addr_orig_p[i] == addr_orig)
41ba02
+	       && dr_ctrl_p[i] == ctrl)
41ba02
 	{
41ba02
 	  gdb_assert (dr_ref_count[i] != 0);
41ba02
 	  idx = i;
41ba02
@@ -1106,6 +1229,8 @@
41ba02
     {
41ba02
       /* new entry */
41ba02
       dr_addr_p[idx] = addr;
41ba02
+      if (dr_addr_orig_p != NULL)
41ba02
+	dr_addr_orig_p[idx] = addr_orig;
41ba02
       dr_ctrl_p[idx] = ctrl;
41ba02
       dr_ref_count[idx] = 1;
41ba02
       /* Notify the change.  */
41ba02
@@ -1125,11 +1250,12 @@
41ba02
 
41ba02
 static int
41ba02
 aarch64_dr_state_remove_one_point (struct aarch64_debug_reg_state *state,
41ba02
-				   int type, CORE_ADDR addr, int len)
41ba02
+				   int type, CORE_ADDR addr, int offset, int len,
41ba02
+				   CORE_ADDR addr_orig)
41ba02
 {
41ba02
   int i, num_regs, is_watchpoint;
41ba02
   unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
41ba02
-  CORE_ADDR *dr_addr_p;
41ba02
+  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
41ba02
 
41ba02
   /* Set up state pointers.  */
41ba02
   is_watchpoint = (type != hw_execute);
41ba02
@@ -1138,6 +1264,7 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_wp_regs;
41ba02
       dr_addr_p = state->dr_addr_wp;
41ba02
+      dr_addr_orig_p = state->dr_addr_orig_wp;
41ba02
       dr_ctrl_p = state->dr_ctrl_wp;
41ba02
       dr_ref_count = state->dr_ref_count_wp;
41ba02
     }
41ba02
@@ -1145,15 +1272,18 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_bp_regs;
41ba02
       dr_addr_p = state->dr_addr_bp;
41ba02
+      dr_addr_orig_p = NULL;
41ba02
       dr_ctrl_p = state->dr_ctrl_bp;
41ba02
       dr_ref_count = state->dr_ref_count_bp;
41ba02
     }
41ba02
 
41ba02
-  ctrl = aarch64_point_encode_ctrl_reg (type, len);
41ba02
+  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
41ba02
 
41ba02
   /* Find the entry that matches the ADDR and CTRL.  */
41ba02
   for (i = 0; i < num_regs; ++i)
41ba02
-    if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
41ba02
+    if (dr_addr_p[i] == addr
41ba02
+	&& (dr_addr_orig_p == NULL || dr_addr_orig_p[i] == addr_orig)
41ba02
+	&& dr_ctrl_p[i] == ctrl)
41ba02
       {
41ba02
 	gdb_assert (dr_ref_count[i] != 0);
41ba02
 	break;
41ba02
@@ -1169,6 +1299,8 @@
41ba02
       /* Clear the enable bit.  */
41ba02
       ctrl &= ~1;
41ba02
       dr_addr_p[i] = 0;
41ba02
+      if (dr_addr_orig_p != NULL)
41ba02
+	dr_addr_orig_p[i] = 0;
41ba02
       dr_ctrl_p[i] = ctrl;
41ba02
       /* Notify the change.  */
41ba02
       aarch64_notify_debug_reg_change (state, is_watchpoint, i);
41ba02
@@ -1192,9 +1324,9 @@
41ba02
   state = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
41ba02
 
41ba02
   if (is_insert)
41ba02
-    return aarch64_dr_state_insert_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, -1);
41ba02
   else
41ba02
-    return aarch64_dr_state_remove_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, -1);
41ba02
 }
41ba02
 
41ba02
 /* Insert a hardware-assisted breakpoint at BP_TGT->placed_address.
41ba02
@@ -1271,9 +1403,9 @@
41ba02
     = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
41ba02
 
41ba02
   if (is_insert)
41ba02
-    return aarch64_dr_state_insert_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, addr);
41ba02
   else
41ba02
-    return aarch64_dr_state_remove_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, addr);
41ba02
 }
41ba02
 
41ba02
 /* Insert/remove unaligned watchpoint by calling
41ba02
@@ -1289,28 +1421,41 @@
41ba02
 {
41ba02
   struct aarch64_debug_reg_state *state
41ba02
     = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
41ba02
+  CORE_ADDR addr_orig = addr;
41ba02
 
41ba02
   while (len > 0)
41ba02
     {
41ba02
       CORE_ADDR aligned_addr;
41ba02
-      int aligned_len, ret;
41ba02
+      int aligned_offset, aligned_len, ret;
41ba02
+      CORE_ADDR addr_orig_next = addr_orig;
41ba02
 
41ba02
-      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_len,
41ba02
-				&addr, &len;;
41ba02
+      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_offset,
41ba02
+				&aligned_len, &addr, &len, &addr_orig_next);
41ba02
 
41ba02
       if (is_insert)
41ba02
 	ret = aarch64_dr_state_insert_one_point (state, type, aligned_addr,
41ba02
-						 aligned_len);
41ba02
+						 aligned_offset,
41ba02
+						 aligned_len, addr_orig);
41ba02
       else
41ba02
 	ret = aarch64_dr_state_remove_one_point (state, type, aligned_addr,
41ba02
-						 aligned_len);
41ba02
+						 aligned_offset,
41ba02
+						 aligned_len, addr_orig);
41ba02
 
41ba02
       if (debug_hw_points)
41ba02
 	fprintf_unfiltered (gdb_stdlog,
41ba02
 "handle_unaligned_watchpoint: is_insert: %d\n"
41ba02
 "                             aligned_addr: 0x%08lx, aligned_len: %d\n"
41ba02
-"                                next_addr: 0x%08lx,    next_len: %d\n",
41ba02
-		 is_insert, aligned_addr, aligned_len, addr, len);
41ba02
+"                                addr_orig: %s\n"
41ba02
+"                                                                "
41ba02
+"                                next_addr: %s,    next_len: %d\n"
41ba02
+"                                                           "
41ba02
+"                                addr_orig_next: %s\n",
41ba02
+		 is_insert, aligned_addr,
41ba02
+		      aligned_len, core_addr_to_string_nz (addr_orig),
41ba02
+		      core_addr_to_string_nz (addr), len,
41ba02
+		      core_addr_to_string_nz (addr_orig_next));
41ba02
+
41ba02
+      addr_orig = addr_orig_next;
41ba02
 
41ba02
       if (ret != 0)
41ba02
 	return ret;
41ba02
@@ -1456,16 +1601,38 @@
41ba02
   state = aarch64_get_debug_reg_state (ptid_get_pid (inferior_ptid));
41ba02
   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
41ba02
     {
41ba02
+      const unsigned int offset
41ba02
+	= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
41ba02
       const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
41ba02
       const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
41ba02
-      const CORE_ADDR addr_watch = state->dr_addr_wp[i];
41ba02
+      const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
41ba02
+      const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
41ba02
+      const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
41ba02
 
41ba02
       if (state->dr_ref_count_wp[i]
41ba02
 	  && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
41ba02
-	  && addr_trap >= addr_watch
41ba02
+	  && addr_trap >= addr_watch_aligned
41ba02
 	  && addr_trap < addr_watch + len)
41ba02
 	{
41ba02
-	  *addr_p = addr_trap;
41ba02
+	  /* ADDR_TRAP reports the first address of the memory range
41ba02
+	     accessed by the CPU, regardless of what was the memory
41ba02
+	     range watched.  Thus, a large CPU access that straddles
41ba02
+	     the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
41ba02
+	     ADDR_TRAP that is lower than the
41ba02
+	     ADDR_WATCH..ADDR_WATCH+LEN range.  E.g.:
41ba02
+
41ba02
+	     addr: |   4   |   5   |   6   |   7   |   8   |
41ba02
+				   |---- range watched ----|
41ba02
+		   |----------- range accessed ------------|
41ba02
+
41ba02
+	     In this case, ADDR_TRAP will be 4.
41ba02
+
41ba02
+	     To match a watchpoint known to GDB core, we must never
41ba02
+	     report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
41ba02
+	     range.  ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
41ba02
+	     positive on kernels older than 4.10.  See PR
41ba02
+	     external/20207.  */
41ba02
+	  *addr_p = addr_orig;
41ba02
 	  return 1;
41ba02
 	}
41ba02
     }
41ba02
Index: gdb-7.6.1/gdb/common/common-utils.c
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/common/common-utils.c	2013-02-14 18:11:41.000000000 +0100
41ba02
+++ gdb-7.6.1/gdb/common/common-utils.c	2018-05-05 17:12:49.570781881 +0200
41ba02
@@ -161,3 +161,23 @@
41ba02
   p[len] = 0;
41ba02
   return p;
41ba02
 }
41ba02
+
41ba02
+/* See common/common-utils.h.  */
41ba02
+
41ba02
+ULONGEST
41ba02
+align_up (ULONGEST v, int n)
41ba02
+{
41ba02
+  /* Check that N is really a power of two.  */
41ba02
+  gdb_assert (n && (n & (n-1)) == 0);
41ba02
+  return (v + n - 1) & -n;
41ba02
+}
41ba02
+
41ba02
+/* See common/common-utils.h.  */
41ba02
+
41ba02
+ULONGEST
41ba02
+align_down (ULONGEST v, int n)
41ba02
+{
41ba02
+  /* Check that N is really a power of two.  */
41ba02
+  gdb_assert (n && (n & (n-1)) == 0);
41ba02
+  return (v & -n);
41ba02
+}
41ba02
Index: gdb-7.6.1/gdb/common/common-utils.h
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/common/common-utils.h	2013-02-14 18:11:41.000000000 +0100
41ba02
+++ gdb-7.6.1/gdb/common/common-utils.h	2018-05-05 17:12:49.570781881 +0200
41ba02
@@ -53,4 +53,36 @@
41ba02
 
41ba02
 char *savestring (const char *ptr, size_t len);
41ba02
 
41ba02
+/* Ensure that V is aligned to an N byte boundary (B's assumed to be a
41ba02
+   power of 2).  Round up/down when necessary.  Examples of correct
41ba02
+   use include:
41ba02
+
41ba02
+    addr = align_up (addr, 8); -- VALUE needs 8 byte alignment
41ba02
+    write_memory (addr, value, len);
41ba02
+    addr += len;
41ba02
+
41ba02
+   and:
41ba02
+
41ba02
+    sp = align_down (sp - len, 16); -- Keep SP 16 byte aligned
41ba02
+    write_memory (sp, value, len);
41ba02
+
41ba02
+   Note that uses such as:
41ba02
+
41ba02
+    write_memory (addr, value, len);
41ba02
+    addr += align_up (len, 8);
41ba02
+
41ba02
+   and:
41ba02
+
41ba02
+    sp -= align_up (len, 8);
41ba02
+    write_memory (sp, value, len);
41ba02
+
41ba02
+   are typically not correct as they don't ensure that the address (SP
41ba02
+   or ADDR) is correctly aligned (relying on previous alignment to
41ba02
+   keep things right).  This is also why the methods are called
41ba02
+   "align_..." instead of "round_..." as the latter reads better with
41ba02
+   this incorrect coding style.  */
41ba02
+
41ba02
+extern ULONGEST align_up (ULONGEST v, int n);
41ba02
+extern ULONGEST align_down (ULONGEST v, int n);
41ba02
+
41ba02
 #endif
41ba02
Index: gdb-7.6.1/gdb/gdbserver/linux-aarch64-low.c
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/gdbserver/linux-aarch64-low.c	2018-05-05 17:12:47.501763392 +0200
41ba02
+++ gdb-7.6.1/gdb/gdbserver/linux-aarch64-low.c	2018-05-05 17:12:49.570781881 +0200
41ba02
@@ -40,6 +40,18 @@
41ba02
 #include <sys/reg.h>
41ba02
 #endif
41ba02
 
41ba02
+/* ptrace expects control registers to be formatted as follows:
41ba02
+
41ba02
+   31                             13          5      3      1     0
41ba02
+   +--------------------------------+----------+------+------+----+
41ba02
+   |         RESERVED (SBZ)         |   MASK   | TYPE | PRIV | EN |
41ba02
+   +--------------------------------+----------+------+------+----+
41ba02
+
41ba02
+   The TYPE field is ignored for breakpoints.  */
41ba02
+
41ba02
+#define DR_CONTROL_ENABLED(ctrl)	(((ctrl) & 0x1) == 1)
41ba02
+#define DR_CONTROL_MASK(ctrl)		(((ctrl) >> 5) & 0xff)
41ba02
+
41ba02
 #define AARCH64_X_REGS_NUM 31
41ba02
 #define AARCH64_V_REGS_NUM 32
41ba02
 #define AARCH64_X0_REGNO    0
41ba02
@@ -170,7 +182,10 @@
41ba02
   unsigned int dr_ref_count_bp[AARCH64_HBP_MAX_NUM];
41ba02
 
41ba02
   /* hardware watchpoint */
41ba02
+  /* Address aligned down to AARCH64_HWP_ALIGNMENT.  */
41ba02
   CORE_ADDR dr_addr_wp[AARCH64_HWP_MAX_NUM];
41ba02
+  /* Address as entered by user without any forced alignment.  */
41ba02
+  CORE_ADDR dr_addr_orig_wp[AARCH64_HWP_MAX_NUM];
41ba02
   unsigned int dr_ctrl_wp[AARCH64_HWP_MAX_NUM];
41ba02
   unsigned int dr_ref_count_wp[AARCH64_HWP_MAX_NUM];
41ba02
 };
41ba02
@@ -311,6 +326,29 @@
41ba02
     supply_register (regcache, AARCH64_V0_REGNO + i, &regset->vregs[i]);
41ba02
 }
41ba02
 
41ba02
+/* True if this kernel does not have the bug described by PR
41ba02
+   external/20207 (Linux >= 4.10).  A fixed kernel supports any
41ba02
+   contiguous range of bits in 8-bit byte DR_CONTROL_MASK.  A buggy
41ba02
+   kernel supports only 0x01, 0x03, 0x0f and 0xff.  We start by
41ba02
+   assuming the bug is fixed, and then detect the bug at
41ba02
+   PTRACE_SETREGSET time.  */
41ba02
+static int kernel_supports_any_contiguous_range = 1;
41ba02
+
41ba02
+/* Return starting byte 0..7 incl. of a watchpoint encoded by CTRL.  */
41ba02
+
41ba02
+static unsigned int
41ba02
+aarch64_watchpoint_offset (unsigned int ctrl)
41ba02
+{
41ba02
+  uint8_t mask = DR_CONTROL_MASK (ctrl);
41ba02
+  unsigned retval;
41ba02
+
41ba02
+  /* Shift out bottom zeros.  */
41ba02
+  for (retval = 0; mask && (mask & 1) == 0; ++retval)
41ba02
+    mask >>= 1;
41ba02
+
41ba02
+  return retval;
41ba02
+}
41ba02
+
41ba02
 /* Debugging of hardware breakpoint/watchpoint support.  */
41ba02
 extern int debug_hw_points;
41ba02
 
41ba02
@@ -383,8 +421,8 @@
41ba02
 
41ba02
   fprintf (stderr, "\tWATCHPOINTs:\n");
41ba02
   for (i = 0; i < aarch64_num_wp_regs; i++)
41ba02
-    fprintf (stderr, "\tWP%d: addr=0x%s, ctrl=0x%08x, ref.count=%d\n",
41ba02
-	     i, paddress (state->dr_addr_wp[i]),
41ba02
+    fprintf (stderr, "\tWP%d: addr=0x%s (orig=0x%s), ctrl=0x%08x, ref.count=%d\n",
41ba02
+	     i, paddress (state->dr_addr_wp[i]), paddress (state->dr_addr_orig_wp[i]),
41ba02
 	     state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
41ba02
 }
41ba02
 
41ba02
@@ -422,27 +460,27 @@
41ba02
 
41ba02
 /* Utility function that returns the length in bytes of a watchpoint
41ba02
    according to the content of a hardware debug control register CTRL.
41ba02
-   Note that the kernel currently only supports the following Byte
41ba02
-   Address Select (BAS) values: 0x1, 0x3, 0xf and 0xff, which means
41ba02
-   that for a hardware watchpoint, its valid length can only be 1
41ba02
-   byte, 2 bytes, 4 bytes or 8 bytes.  */
41ba02
+   Any contiguous range of bytes in CTRL is supported.  The returned
41ba02
+   value can be between 0..8 (inclusive).  */
41ba02
 
41ba02
 static inline unsigned int
41ba02
 aarch64_watchpoint_length (unsigned int ctrl)
41ba02
 {
41ba02
-  switch (DR_CONTROL_LENGTH (ctrl))
41ba02
-    {
41ba02
-    case 0x01:
41ba02
-      return 1;
41ba02
-    case 0x03:
41ba02
-      return 2;
41ba02
-    case 0x0f:
41ba02
-      return 4;
41ba02
-    case 0xff:
41ba02
-      return 8;
41ba02
-    default:
41ba02
-      return 0;
41ba02
-    }
41ba02
+  uint8_t mask = DR_CONTROL_MASK (ctrl);
41ba02
+  unsigned retval;
41ba02
+
41ba02
+  /* Shift out bottom zeros.  */
41ba02
+  mask >>= aarch64_watchpoint_offset (ctrl);
41ba02
+
41ba02
+  /* Count bottom ones.  */
41ba02
+  for (retval = 0; (mask & 1) != 0; ++retval)
41ba02
+    mask >>= 1;
41ba02
+
41ba02
+  if (mask != 0)
41ba02
+    error (_("Unexpected hardware watchpoint length register value 0x%x"),
41ba02
+	   DR_CONTROL_MASK (ctrl));
41ba02
+
41ba02
+  return retval;
41ba02
 }
41ba02
 
41ba02
 /* Given the hardware breakpoint or watchpoint type TYPE and its
41ba02
@@ -450,14 +488,17 @@
41ba02
    breakpoint/watchpoint control register.  */
41ba02
 
41ba02
 static unsigned int
41ba02
-aarch64_point_encode_ctrl_reg (enum target_point_type type, int len)
41ba02
+aarch64_point_encode_ctrl_reg (enum target_point_type type, int offset, int len)
41ba02
 {
41ba02
   unsigned int ctrl;
41ba02
 
41ba02
+  gdb_assert (offset == 0 || kernel_supports_any_contiguous_range);
41ba02
+  gdb_assert (offset + len <= AARCH64_HWP_MAX_LEN_PER_REG);
41ba02
+
41ba02
   /* type */
41ba02
   ctrl = type << 3;
41ba02
-  /* length bitmask */
41ba02
-  ctrl |= ((1 << len) - 1) << 5;
41ba02
+  /* offset and length bitmask */
41ba02
+  ctrl |= ((1 << len) - 1) << (5 + offset);
41ba02
   /* enabled at el0 */
41ba02
   ctrl |= (2 << 1) | 1;
41ba02
 
41ba02
@@ -486,17 +527,23 @@
41ba02
   if (addr & (alignment - 1))
41ba02
     return 0;
41ba02
 
41ba02
-  if (len != 8 && len != 4 && len != 2 && len != 1)
41ba02
+  if ((!kernel_supports_any_contiguous_range
41ba02
+       && len != 8 && len != 4 && len != 2 && len != 1)
41ba02
+      || (kernel_supports_any_contiguous_range
41ba02
+	  && (len < 1 || len > 8)))
41ba02
     return 0;
41ba02
 
41ba02
   return 1;
41ba02
 }
41ba02
 
41ba02
 /* Given the (potentially unaligned) watchpoint address in ADDR and
41ba02
-   length in LEN, return the aligned address and aligned length in
41ba02
-   *ALIGNED_ADDR_P and *ALIGNED_LEN_P, respectively.  The returned
41ba02
-   aligned address and length will be valid to be written to the
41ba02
-   hardware watchpoint value and control registers.  See the comment
41ba02
+   length in LEN, return the aligned address, offset from that base
41ba02
+   address, and aligned length in *ALIGNED_ADDR_P, *ALIGNED_OFFSET_P
41ba02
+   and *ALIGNED_LEN_P, respectively.  The returned values will be
41ba02
+   valid values to write to the hardware watchpoint value and control
41ba02
+   registers.
41ba02
+
41ba02
+   See the comment
41ba02
    above aarch64_point_is_aligned for the information about the
41ba02
    alignment requirement.  The given watchpoint may get truncated if
41ba02
    more than one hardware register is needed to cover the watched
41ba02
@@ -533,11 +580,12 @@
41ba02
 
41ba02
 static void
41ba02
 aarch64_align_watchpoint (CORE_ADDR addr, int len, CORE_ADDR *aligned_addr_p,
41ba02
-			  int *aligned_len_p, CORE_ADDR *next_addr_p,
41ba02
-			  int *next_len_p)
41ba02
+			  int *aligned_offset_p, int *aligned_len_p,
41ba02
+			  CORE_ADDR *next_addr_p, int *next_len_p,
41ba02
+			  CORE_ADDR *next_addr_orig_p)
41ba02
 {
41ba02
   int aligned_len;
41ba02
-  unsigned int offset;
41ba02
+  unsigned int offset, aligned_offset;
41ba02
   CORE_ADDR aligned_addr;
41ba02
   const unsigned int alignment = AARCH64_HWP_ALIGNMENT;
41ba02
   const unsigned int max_wp_len = AARCH64_HWP_MAX_LEN_PER_REG;
41ba02
@@ -548,10 +596,12 @@
41ba02
   if (len <= 0)
41ba02
     return;
41ba02
 
41ba02
-  /* Address to be put into the hardware watchpoint value register
41ba02
-     must be aligned.  */
41ba02
+  /* The address put into the hardware watchpoint value register must
41ba02
+     be aligned.  */
41ba02
   offset = addr & (alignment - 1);
41ba02
   aligned_addr = addr - offset;
41ba02
+  aligned_offset
41ba02
+    = kernel_supports_any_contiguous_range ? addr & (alignment - 1) : 0;
41ba02
 
41ba02
   gdb_assert (offset >= 0 && offset < alignment);
41ba02
   gdb_assert (aligned_addr >= 0 && aligned_addr <= addr);
41ba02
@@ -559,9 +609,10 @@
41ba02
 
41ba02
   if (offset + len >= max_wp_len)
41ba02
     {
41ba02
-      /* Need more than one watchpoint registers; truncate it at the
41ba02
+      /* Need more than one watchpoint register; truncate at the
41ba02
 	 alignment boundary.  */
41ba02
-      aligned_len = max_wp_len;
41ba02
+      aligned_len
41ba02
+	= max_wp_len - (kernel_supports_any_contiguous_range ? offset : 0);
41ba02
       len -= (max_wp_len - offset);
41ba02
       addr += (max_wp_len - offset);
41ba02
       gdb_assert ((addr & (alignment - 1)) == 0);
41ba02
@@ -574,26 +625,96 @@
41ba02
 	aligned_len_array[AARCH64_HWP_MAX_LEN_PER_REG] =
41ba02
 	{ 1, 2, 4, 4, 8, 8, 8, 8 };
41ba02
 
41ba02
-      aligned_len = aligned_len_array[offset + len - 1];
41ba02
+      aligned_len = (kernel_supports_any_contiguous_range
41ba02
+		     ? len : aligned_len_array[offset + len - 1]);
41ba02
       addr += len;
41ba02
       len = 0;
41ba02
     }
41ba02
 
41ba02
   if (aligned_addr_p != NULL)
41ba02
     *aligned_addr_p = aligned_addr;
41ba02
+  if (aligned_offset_p)
41ba02
+    *aligned_offset_p = aligned_offset;
41ba02
   if (aligned_len_p != NULL)
41ba02
     *aligned_len_p = aligned_len;
41ba02
   if (next_addr_p != NULL)
41ba02
     *next_addr_p = addr;
41ba02
   if (next_len_p != NULL)
41ba02
     *next_len_p = len;
41ba02
+  if (next_addr_orig_p)
41ba02
+    *next_addr_orig_p = align_down (*next_addr_orig_p + alignment, alignment);
41ba02
+}
41ba02
+
41ba02
+/* Reconfigure STATE to be compatible with Linux kernels with the PR
41ba02
+   external/20207 bug.  This is called when
41ba02
+   KERNEL_SUPPORTS_ANY_CONTIGUOUS_RANGE transitions to false.  Note we
41ba02
+   don't try to support combining watchpoints with matching (and thus
41ba02
+   shared) masks, as it's too late when we get here.  On buggy
41ba02
+   kernels, GDB will try to first setup the perfect matching ranges,
41ba02
+   which will run out of registers before this function can merge
41ba02
+   them.  It doesn't look like worth the effort to improve that, given
41ba02
+   eventually buggy kernels will be phased out.  */
41ba02
+
41ba02
+static void aarch64_notify_debug_reg_change (const struct aarch64_debug_reg_state *state, int is_watchpoint, unsigned int idx);
41ba02
+
41ba02
+static void
41ba02
+aarch64_downgrade_regs (struct aarch64_debug_reg_state *state)
41ba02
+{
41ba02
+  int i;
41ba02
+
41ba02
+  for (i = 0; i < aarch64_num_wp_regs; ++i)
41ba02
+    if ((state->dr_ctrl_wp[i] & 1) != 0)
41ba02
+      {
41ba02
+	uint8_t mask_orig;
41ba02
+	static const uint8_t old_valid[] = { 0x01, 0x03, 0x0f, 0xff };
41ba02
+	int old_validi;
41ba02
+	uint8_t mask = 0;
41ba02
+	int j;
41ba02
+
41ba02
+	gdb_assert (state->dr_ref_count_wp[i] != 0);
41ba02
+	mask_orig = (state->dr_ctrl_wp[i] >> 5) & 0xff;
41ba02
+	gdb_assert (mask_orig != 0);
41ba02
+	for (old_validi = 0; old_validi < sizeof(old_valid) / sizeof (*old_valid); old_validi++) {
41ba02
+	  const uint8_t old_mask = old_valid[old_validi];
41ba02
+	  if (mask_orig <= old_mask)
41ba02
+	    {
41ba02
+	      mask = old_mask;
41ba02
+	      break;
41ba02
+	    }
41ba02
+	}
41ba02
+	gdb_assert (mask != 0);
41ba02
+
41ba02
+	/* No update needed for this watchpoint?  */
41ba02
+	if (mask == mask_orig)
41ba02
+	  continue;
41ba02
+	state->dr_ctrl_wp[i] |= mask << 5;
41ba02
+	state->dr_addr_wp[i]
41ba02
+	  = align_down (state->dr_addr_wp[i], AARCH64_HWP_ALIGNMENT);
41ba02
+
41ba02
+	/* Try to match duplicate entries.  */
41ba02
+	for (j = 0; j < i; ++j)
41ba02
+	  if ((state->dr_ctrl_wp[j] & 1) != 0
41ba02
+	      && state->dr_addr_wp[j] == state->dr_addr_wp[i]
41ba02
+	      && state->dr_addr_orig_wp[j] == state->dr_addr_orig_wp[i]
41ba02
+	      && state->dr_ctrl_wp[j] == state->dr_ctrl_wp[i])
41ba02
+	    {
41ba02
+	      state->dr_ref_count_wp[j] += state->dr_ref_count_wp[i];
41ba02
+	      state->dr_ref_count_wp[i] = 0;
41ba02
+	      state->dr_addr_wp[i] = 0;
41ba02
+	      state->dr_addr_orig_wp[i] = 0;
41ba02
+	      state->dr_ctrl_wp[i] &= ~1;
41ba02
+	      break;
41ba02
+	    }
41ba02
+
41ba02
+	aarch64_notify_debug_reg_change (state, 1 /* is_watchpoint */, i);
41ba02
+      }
41ba02
 }
41ba02
 
41ba02
 /* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
41ba02
    registers with data from *STATE.  */
41ba02
 
41ba02
 static void
41ba02
-aarch64_linux_set_debug_regs (const struct aarch64_debug_reg_state *state,
41ba02
+aarch64_linux_set_debug_regs (struct aarch64_debug_reg_state *state,
41ba02
 			      int tid, int watchpoint)
41ba02
 {
41ba02
   int i, count;
41ba02
@@ -621,7 +742,18 @@
41ba02
   if (ptrace (PTRACE_SETREGSET, tid,
41ba02
 	      watchpoint ? NT_ARM_HW_WATCH : NT_ARM_HW_BREAK,
41ba02
 	      (void *) &iov))
41ba02
-    error (_("Unexpected error setting hardware debug registers"));
41ba02
+    {
41ba02
+      /* Handle Linux kernels with the PR external/20207 bug.  */
41ba02
+      if (watchpoint && errno == EINVAL
41ba02
+	  && kernel_supports_any_contiguous_range)
41ba02
+	{
41ba02
+	  kernel_supports_any_contiguous_range = 0;
41ba02
+	  aarch64_downgrade_regs (state);
41ba02
+	  aarch64_linux_set_debug_regs (state, tid, watchpoint);
41ba02
+	  return;
41ba02
+	}
41ba02
+      error (_("Unexpected error setting hardware debug registers"));
41ba02
+    }
41ba02
 }
41ba02
 
41ba02
 struct aarch64_dr_update_callback_param
41ba02
@@ -750,11 +882,12 @@
41ba02
 static int
41ba02
 aarch64_dr_state_insert_one_point (struct aarch64_debug_reg_state *state,
41ba02
 				   enum target_point_type type,
41ba02
-				   CORE_ADDR addr, int len)
41ba02
+				   CORE_ADDR addr, int offset, int len,
41ba02
+				   CORE_ADDR addr_orig)
41ba02
 {
41ba02
   int i, idx, num_regs, is_watchpoint;
41ba02
   unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
41ba02
-  CORE_ADDR *dr_addr_p;
41ba02
+  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
41ba02
 
41ba02
   /* Set up state pointers.  */
41ba02
   is_watchpoint = (type != hw_execute);
41ba02
@@ -763,6 +896,7 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_wp_regs;
41ba02
       dr_addr_p = state->dr_addr_wp;
41ba02
+      dr_addr_orig_p = state->dr_addr_orig_wp;
41ba02
       dr_ctrl_p = state->dr_ctrl_wp;
41ba02
       dr_ref_count = state->dr_ref_count_wp;
41ba02
     }
41ba02
@@ -770,11 +904,12 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_bp_regs;
41ba02
       dr_addr_p = state->dr_addr_bp;
41ba02
+      dr_addr_orig_p = NULL;
41ba02
       dr_ctrl_p = state->dr_ctrl_bp;
41ba02
       dr_ref_count = state->dr_ref_count_bp;
41ba02
     }
41ba02
 
41ba02
-  ctrl = aarch64_point_encode_ctrl_reg (type, len);
41ba02
+  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
41ba02
 
41ba02
   /* Find an existing or free register in our cache.  */
41ba02
   idx = -1;
41ba02
@@ -786,7 +921,9 @@
41ba02
 	  idx = i;
41ba02
 	  /* no break; continue hunting for an exising one.  */
41ba02
 	}
41ba02
-      else if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
41ba02
+      else if (dr_addr_p[i] == addr
41ba02
+	       && (dr_addr_orig_p == NULL || dr_addr_orig_p[i] == addr_orig)
41ba02
+	       && dr_ctrl_p[i] == ctrl)
41ba02
 	{
41ba02
 	  gdb_assert (dr_ref_count[i] != 0);
41ba02
 	  idx = i;
41ba02
@@ -803,6 +940,8 @@
41ba02
     {
41ba02
       /* new entry */
41ba02
       dr_addr_p[idx] = addr;
41ba02
+      if (dr_addr_orig_p != NULL)
41ba02
+	dr_addr_orig_p[idx] = addr_orig;
41ba02
       dr_ctrl_p[idx] = ctrl;
41ba02
       dr_ref_count[idx] = 1;
41ba02
       /* Notify the change.  */
41ba02
@@ -823,11 +962,12 @@
41ba02
 static int
41ba02
 aarch64_dr_state_remove_one_point (struct aarch64_debug_reg_state *state,
41ba02
 				   enum target_point_type type,
41ba02
-				   CORE_ADDR addr, int len)
41ba02
+				   CORE_ADDR addr, int offset, int len,
41ba02
+				   CORE_ADDR addr_orig)
41ba02
 {
41ba02
   int i, num_regs, is_watchpoint;
41ba02
   unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
41ba02
-  CORE_ADDR *dr_addr_p;
41ba02
+  CORE_ADDR *dr_addr_p, *dr_addr_orig_p;
41ba02
 
41ba02
   /* Set up state pointers.  */
41ba02
   is_watchpoint = (type != hw_execute);
41ba02
@@ -836,6 +976,7 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_wp_regs;
41ba02
       dr_addr_p = state->dr_addr_wp;
41ba02
+      dr_addr_orig_p = state->dr_addr_orig_wp;
41ba02
       dr_ctrl_p = state->dr_ctrl_wp;
41ba02
       dr_ref_count = state->dr_ref_count_wp;
41ba02
     }
41ba02
@@ -843,15 +984,18 @@
41ba02
     {
41ba02
       num_regs = aarch64_num_bp_regs;
41ba02
       dr_addr_p = state->dr_addr_bp;
41ba02
+      dr_addr_orig_p = NULL;
41ba02
       dr_ctrl_p = state->dr_ctrl_bp;
41ba02
       dr_ref_count = state->dr_ref_count_bp;
41ba02
     }
41ba02
 
41ba02
-  ctrl = aarch64_point_encode_ctrl_reg (type, len);
41ba02
+  ctrl = aarch64_point_encode_ctrl_reg (type, offset, len);
41ba02
 
41ba02
   /* Find the entry that matches the ADDR and CTRL.  */
41ba02
   for (i = 0; i < num_regs; ++i)
41ba02
-    if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
41ba02
+    if (dr_addr_p[i] == addr
41ba02
+	&& (dr_addr_orig_p == NULL || dr_addr_orig_p[i] == addr_orig)
41ba02
+	&& dr_ctrl_p[i] == ctrl)
41ba02
       {
41ba02
 	gdb_assert (dr_ref_count[i] != 0);
41ba02
 	break;
41ba02
@@ -867,6 +1011,8 @@
41ba02
       /* Clear the enable bit.  */
41ba02
       ctrl &= ~1;
41ba02
       dr_addr_p[i] = 0;
41ba02
+      if (dr_addr_orig_p != NULL)
41ba02
+	dr_addr_orig_p[i] = 0;
41ba02
       dr_ctrl_p[i] = ctrl;
41ba02
       /* Notify the change.  */
41ba02
       aarch64_notify_debug_reg_change (state, is_watchpoint, i);
41ba02
@@ -889,9 +1035,9 @@
41ba02
   state = aarch64_get_debug_reg_state ();
41ba02
 
41ba02
   if (is_insert)
41ba02
-    return aarch64_dr_state_insert_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, -1);
41ba02
   else
41ba02
-    return aarch64_dr_state_remove_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, -1);
41ba02
 }
41ba02
 
41ba02
 /* This is essentially the same as aarch64_handle_breakpoint, apart
41ba02
@@ -906,9 +1052,9 @@
41ba02
   state = aarch64_get_debug_reg_state ();
41ba02
 
41ba02
   if (is_insert)
41ba02
-    return aarch64_dr_state_insert_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_insert_one_point (state, type, addr, 0, len, addr);
41ba02
   else
41ba02
-    return aarch64_dr_state_remove_one_point (state, type, addr, len);
41ba02
+    return aarch64_dr_state_remove_one_point (state, type, addr, 0, len, addr);
41ba02
 }
41ba02
 
41ba02
 /* Insert/remove unaligned watchpoint by calling
41ba02
@@ -924,29 +1070,41 @@
41ba02
 {
41ba02
   struct aarch64_debug_reg_state *state
41ba02
     = aarch64_get_debug_reg_state ();
41ba02
+  CORE_ADDR addr_orig = addr;
41ba02
 
41ba02
   while (len > 0)
41ba02
     {
41ba02
       CORE_ADDR aligned_addr;
41ba02
-      int aligned_len, ret;
41ba02
+      int aligned_offset, aligned_len, ret;
41ba02
+      CORE_ADDR addr_orig_next = addr_orig;
41ba02
 
41ba02
-      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_len,
41ba02
-				&addr, &len;;
41ba02
+      aarch64_align_watchpoint (addr, len, &aligned_addr, &aligned_offset,
41ba02
+				&aligned_len, &addr, &len, &addr_orig_next);
41ba02
 
41ba02
       if (is_insert)
41ba02
 	ret = aarch64_dr_state_insert_one_point (state, type, aligned_addr,
41ba02
-						 aligned_len);
41ba02
+						 aligned_offset,
41ba02
+						 aligned_len, addr_orig);
41ba02
       else
41ba02
 	ret = aarch64_dr_state_remove_one_point (state, type, aligned_addr,
41ba02
-						 aligned_len);
41ba02
+						 aligned_offset,
41ba02
+						 aligned_len, addr_orig);
41ba02
 
41ba02
       if (debug_hw_points)
41ba02
 	fprintf (stderr,
41ba02
  "handle_unaligned_watchpoint: is_insert: %d\n"
41ba02
  "                             aligned_addr: 0x%s, aligned_len: %d\n"
41ba02
- "                                next_addr: 0x%s,    next_len: %d\n",
41ba02
-		 is_insert, paddress (aligned_addr), aligned_len,
41ba02
-		 paddress (addr), len);
41ba02
+ "                                addr_orig: %s\n"
41ba02
+ "                                                                "
41ba02
+ "                                next_addr: %s,    next_len: %d\n"
41ba02
+ "                                                           "
41ba02
+ "                                addr_orig_next: %s\n",
41ba02
+		 is_insert, paddress (aligned_addr),
41ba02
+		      aligned_len, paddress (addr_orig),
41ba02
+		      paddress (addr), len,
41ba02
+		      paddress (addr_orig_next));
41ba02
+ 
41ba02
+       addr_orig = addr_orig_next;
41ba02
 
41ba02
       if (ret != 0)
41ba02
 	return ret;
41ba02
@@ -1065,14 +1223,39 @@
41ba02
   state = aarch64_get_debug_reg_state ();
41ba02
   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
41ba02
     {
41ba02
+      const unsigned int offset
41ba02
+	= aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
41ba02
       const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
41ba02
       const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
41ba02
-      const CORE_ADDR addr_watch = state->dr_addr_wp[i];
41ba02
+      const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
41ba02
+      const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
41ba02
+      const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
41ba02
+
41ba02
       if (state->dr_ref_count_wp[i]
41ba02
 	  && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
41ba02
-	  && addr_trap >= addr_watch
41ba02
+	  && addr_trap >= addr_watch_aligned
41ba02
 	  && addr_trap < addr_watch + len)
41ba02
-	return addr_trap;
41ba02
+	{
41ba02
+	  /* ADDR_TRAP reports the first address of the memory range
41ba02
+	     accessed by the CPU, regardless of what was the memory
41ba02
+	     range watched.  Thus, a large CPU access that straddles
41ba02
+	     the ADDR_WATCH..ADDR_WATCH+LEN range may result in an
41ba02
+	     ADDR_TRAP that is lower than the
41ba02
+	     ADDR_WATCH..ADDR_WATCH+LEN range.  E.g.:
41ba02
+
41ba02
+	     addr: |   4   |   5   |   6   |   7   |   8   |
41ba02
+				   |---- range watched ----|
41ba02
+		   |----------- range accessed ------------|
41ba02
+
41ba02
+	     In this case, ADDR_TRAP will be 4.
41ba02
+
41ba02
+	     To match a watchpoint known to GDB core, we must never
41ba02
+	     report *ADDR_P outside of any ADDR_WATCH..ADDR_WATCH+LEN
41ba02
+	     range.  ADDR_WATCH <= ADDR_TRAP < ADDR_ORIG is a false
41ba02
+	     positive on kernels older than 4.10.  See PR
41ba02
+	     external/20207.  */
41ba02
+	  return addr_orig;
41ba02
+	}
41ba02
     }
41ba02
 
41ba02
   return (CORE_ADDR) 0;
41ba02
Index: gdb-7.6.1/gdb/testsuite/gdb.base/watchpoint-unaligned.c
41ba02
===================================================================
41ba02
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
41ba02
+++ gdb-7.6.1/gdb/testsuite/gdb.base/watchpoint-unaligned.c	2018-05-05 17:12:49.571781890 +0200
41ba02
@@ -0,0 +1,96 @@
41ba02
+/* This testcase is part of GDB, the GNU debugger.
41ba02
+
41ba02
+   Copyright 2017-2018 Free Software Foundation, Inc.
41ba02
+
41ba02
+   This program is free software; you can redistribute it and/or modify
41ba02
+   it under the terms of the GNU General Public License as published by
41ba02
+   the Free Software Foundation; either version 3 of the License, or
41ba02
+   (at your option) any later version.
41ba02
+
41ba02
+   This program is distributed in the hope that it will be useful,
41ba02
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
41ba02
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41ba02
+   GNU General Public License for more details.
41ba02
+
41ba02
+   You should have received a copy of the GNU General Public License
41ba02
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
41ba02
+
41ba02
+#include <stdint.h>
41ba02
+#include <assert.h>
41ba02
+
41ba02
+static int again;
41ba02
+
41ba02
+static volatile struct
41ba02
+{
41ba02
+  uint64_t alignment;
41ba02
+  union
41ba02
+    {
41ba02
+      uint64_t size8[1];
41ba02
+      uint32_t size4[2];
41ba02
+      uint16_t size2[4];
41ba02
+      uint8_t size1[8];
41ba02
+      uint64_t size8twice[2];
41ba02
+    }
41ba02
+  u;
41ba02
+} data;
41ba02
+
41ba02
+static int size = 0;
41ba02
+static int offset;
41ba02
+
41ba02
+static void
41ba02
+write_size8twice (void)
41ba02
+{
41ba02
+  static const uint64_t first = 1;
41ba02
+  static const uint64_t second = 2;
41ba02
+
41ba02
+#ifdef __aarch64__
41ba02
+  asm volatile ("stp %1, %2, [%0]"
41ba02
+		: /* output */
41ba02
+		: "r" (data.u.size8twice), "r" (first), "r" (second) /* input */
41ba02
+		: "memory" /* clobber */);
41ba02
+#else
41ba02
+  data.u.size8twice[0] = first;
41ba02
+  data.u.size8twice[1] = second;
41ba02
+#endif
41ba02
+}
41ba02
+
41ba02
+int
41ba02
+main (void)
41ba02
+{
41ba02
+  volatile uint64_t local;
41ba02
+
41ba02
+  assert (sizeof (data) == 8 + 2 * 8);
41ba02
+
41ba02
+  write_size8twice ();
41ba02
+
41ba02
+  while (size)
41ba02
+    {
41ba02
+      switch (size)
41ba02
+	{
41ba02
+/* __s390x__ also defines __s390__ */
41ba02
+#ifdef __s390__
41ba02
+# define ACCESS(var) var = ~var
41ba02
+#else
41ba02
+# define ACCESS(var) local = var
41ba02
+#endif
41ba02
+	case 8:
41ba02
+	  ACCESS (data.u.size8[offset]);
41ba02
+	  break;
41ba02
+	case 4:
41ba02
+	  ACCESS (data.u.size4[offset]);
41ba02
+	  break;
41ba02
+	case 2:
41ba02
+	  ACCESS (data.u.size2[offset]);
41ba02
+	  break;
41ba02
+	case 1:
41ba02
+	  ACCESS (data.u.size1[offset]);
41ba02
+	  break;
41ba02
+#undef ACCESS
41ba02
+	default:
41ba02
+	  assert (0);
41ba02
+	}
41ba02
+      size = 0;
41ba02
+      size = size; /* start_again */
41ba02
+    }
41ba02
+  return 0; /* final_return */
41ba02
+}
41ba02
Index: gdb-7.6.1/gdb/testsuite/gdb.base/watchpoint-unaligned.exp
41ba02
===================================================================
41ba02
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
41ba02
+++ gdb-7.6.1/gdb/testsuite/gdb.base/watchpoint-unaligned.exp	2018-05-05 17:12:49.571781890 +0200
41ba02
@@ -0,0 +1,184 @@
41ba02
+# Copyright 2017-2018 Free Software Foundation, Inc.
41ba02
+#
41ba02
+# This program is free software; you can redistribute it and/or modify
41ba02
+# it under the terms of the GNU General Public License as published by
41ba02
+# the Free Software Foundation; either version 3 of the License, or
41ba02
+# (at your option) any later version.
41ba02
+#
41ba02
+# This program is distributed in the hope that it will be useful,
41ba02
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
41ba02
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
41ba02
+# GNU General Public License for more details.
41ba02
+#
41ba02
+# You should have received a copy of the GNU General Public License
41ba02
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
41ba02
+#
41ba02
+# This file is part of the gdb testsuite.
41ba02
+
41ba02
+# Test inserting read watchpoints on unaligned addresses.
41ba02
+
41ba02
+standard_testfile
41ba02
+if { [prepare_for_testing ${testfile}.exp ${testfile} ${srcfile}] } {
41ba02
+    return -1
41ba02
+}
41ba02
+
41ba02
+if ![runto_main] {
41ba02
+    untested "could not run to main"
41ba02
+    return -1
41ba02
+}
41ba02
+
41ba02
+gdb_breakpoint [gdb_get_line_number "start_again"] "Breakpoint $decimal at $hex" "start_again"
41ba02
+
41ba02
+set sizes {1 2 4 8}
41ba02
+array set alignedend {1 1  2 2  3 4  4 4  5 8  6 8  7 8  8 8}
41ba02
+
41ba02
+set rwatch "rwatch"
41ba02
+set rwatch_exp "Hardware read watchpoint"
41ba02
+if {[istarget "s390*-*-*"]} {
41ba02
+    # Target does not support this type of hardware watchpoint."
41ba02
+    set rwatch "watch"
41ba02
+    set rwatch_exp "Hardware watchpoint"
41ba02
+}
41ba02
+
41ba02
+foreach wpsize $sizes {
41ba02
+    for {set wpoffset 0} {$wpoffset < 8 / $wpsize} {incr wpoffset} {
41ba02
+	set wpstart [expr $wpoffset * $wpsize]
41ba02
+	set wpend [expr ($wpoffset + 1) * $wpsize]
41ba02
+	set wpendaligned $alignedend($wpend)
41ba02
+	foreach rdsize $sizes {
41ba02
+	    for {set rdoffset 0} {$rdoffset < 8 / $rdsize} {incr rdoffset} {
41ba02
+		set rdstart [expr $rdoffset * $rdsize]
41ba02
+		set rdend [expr ($rdoffset + 1) * $rdsize]
41ba02
+		set expect_hit [expr max ($wpstart, $rdstart) < min ($wpend, $rdend)]
41ba02
+		set test "$rwatch data.u.size$wpsize\[$wpoffset\]"
41ba02
+		set wpnum ""
41ba02
+		gdb_test_multiple $test $test {
41ba02
+		    -re "$rwatch_exp (\[0-9\]+): .*\r\n$gdb_prompt $" {
41ba02
+			set wpnum $expect_out(1,string)
41ba02
+		    }
41ba02
+		    -re "Expression cannot be implemented with read/access watchpoint.\r\n$gdb_prompt $" {
41ba02
+			if {$wpsize == 8 && [istarget "arm*-*-*"]} {
41ba02
+			    untested $test
41ba02
+			    continue
41ba02
+			}
41ba02
+			fail $test
41ba02
+		    }
41ba02
+		}
41ba02
+		gdb_test_no_output "set variable size = $rdsize" ""
41ba02
+		gdb_test_no_output "set variable offset = $rdoffset" ""
41ba02
+		set test "continue"
41ba02
+		set got_hit 0
41ba02
+		gdb_test_multiple $test $test {
41ba02
+		    -re "$rwatch_exp $wpnum:.*alue = .*\r\n$gdb_prompt $" {
41ba02
+			set got_hit 1
41ba02
+			send_gdb "continue\n"
41ba02
+			exp_continue
41ba02
+		    }
41ba02
+		    -re " start_again .*\r\n$gdb_prompt $" {
41ba02
+		    }
41ba02
+		}
41ba02
+		gdb_test_no_output "delete $wpnum" ""
41ba02
+		set test "wp(size=$wpsize offset=$wpoffset) rd(size=$rdsize offset=$rdoffset) expect=$expect_hit"
41ba02
+		if {$expect_hit == $got_hit} {
41ba02
+		    pass $test
41ba02
+		} else {
41ba02
+		    # We do not know if we run on a fixed Linux kernel
41ba02
+		    # or not.  Report XFAIL only in the FAIL case.
41ba02
+		    if {$expect_hit == 0 && $rdstart < $wpendaligned} {
41ba02
+			setup_xfail external/20207 "aarch64*-*-linux*"
41ba02
+		    }
41ba02
+		    if {!$expect_hit && [expr max ($wpstart / 8, $rdstart / 8) < min (($wpend + 7) / 8, ($rdend + 7) / 8)]} {
41ba02
+			setup_xfail breakpoints/23131 "powerpc*-*-*"
41ba02
+		    }
41ba02
+		    fail $test
41ba02
+		}
41ba02
+	    }
41ba02
+	}
41ba02
+    }
41ba02
+}
41ba02
+
41ba02
+foreach wpcount {4 7} {
41ba02
+    array set wpoffset_to_wpnum {}
41ba02
+    for {set wpoffset 1} {$wpoffset <= $wpcount} {incr wpoffset} {
41ba02
+	set test "$rwatch data.u.size1\[$wpoffset\]"
41ba02
+	set wpnum ""
41ba02
+	gdb_test_multiple $test $test {
41ba02
+	    -re "$rwatch_exp (\[0-9\]+): .*\r\n$gdb_prompt $" {
41ba02
+		set wpoffset_to_wpnum($wpoffset) $expect_out(1,string)
41ba02
+	    }
41ba02
+	    -re "There are not enough available hardware resources for this watchpoint.\r\n$gdb_prompt $" {
41ba02
+		if {$wpoffset > 1} {
41ba02
+		    setup_xfail breakpoints/23131 "powerpc*-*-*"
41ba02
+		    setup_xfail breakpoints/23131 "arm*-*-*"
41ba02
+		}
41ba02
+		fail $test
41ba02
+		set wpoffset_to_wpnum($wpoffset) 0
41ba02
+	    }
41ba02
+	}
41ba02
+    }
41ba02
+    gdb_test_no_output "set variable size = 1" ""
41ba02
+    gdb_test_no_output "set variable offset = 1" ""
41ba02
+    set test "continue"
41ba02
+    set got_hit 0
41ba02
+    gdb_test_multiple $test $test {
41ba02
+	-re "\r\nCould not insert hardware watchpoint .*\r\n$gdb_prompt $" {
41ba02
+	}
41ba02
+	-re "$rwatch_exp $wpoffset_to_wpnum(1):.*alue = .*\r\n$gdb_prompt $" {
41ba02
+	    set got_hit 1
41ba02
+	    send_gdb "continue\n"
41ba02
+	    exp_continue
41ba02
+	}
41ba02
+	-re " start_again .*\r\n$gdb_prompt $" {
41ba02
+	}
41ba02
+    }
41ba02
+    for {set wpoffset 1} {$wpoffset <= $wpcount} {incr wpoffset} {
41ba02
+	if {$wpoffset_to_wpnum($wpoffset)} {
41ba02
+	    gdb_test_no_output "delete $wpoffset_to_wpnum($wpoffset)" ""
41ba02
+	}
41ba02
+    }
41ba02
+    set test "wpcount($wpcount)"
41ba02
+    if {!$wpoffset_to_wpnum([expr $wpcount - 1])} {
41ba02
+	untested $test
41ba02
+	continue
41ba02
+    }
41ba02
+    if {$wpcount > 4} {
41ba02
+	if {![istarget "s390*-*-*"]} {
41ba02
+	    setup_kfail tdep/22389 *-*-*
41ba02
+	}
41ba02
+    }
41ba02
+    gdb_assert $got_hit $test
41ba02
+}
41ba02
+
41ba02
+if ![runto_main] {
41ba02
+    return -1
41ba02
+}
41ba02
+gdb_breakpoint [gdb_get_line_number "final_return"] "Breakpoint $decimal at $hex" "final_return"
41ba02
+set test {watch data.u.size8twice[1]}
41ba02
+set wpnum ""
41ba02
+gdb_test_multiple $test $test {
41ba02
+    -re "Hardware watchpoint (\[0-9\]+): .*\r\n$gdb_prompt $" {
41ba02
+	set wpnum $expect_out(1,string)
41ba02
+    }
41ba02
+    -re "Watchpoint (\[0-9\]+): .*\r\n$gdb_prompt $" {
41ba02
+	if {[istarget "arm*-*-*"]} {
41ba02
+	    untested $test
41ba02
+	    set wpnum 0
41ba02
+	}
41ba02
+    }
41ba02
+}
41ba02
+if {$wpnum} {
41ba02
+    set test "continue"
41ba02
+    set got_hit 0
41ba02
+    gdb_test_multiple $test $test {
41ba02
+	-re "\r\nCould not insert hardware watchpoint .*\r\n$gdb_prompt $" {
41ba02
+	}
41ba02
+	-re "Hardware watchpoint $wpnum:.*New value = .*\r\n$gdb_prompt $" {
41ba02
+	    set got_hit 1
41ba02
+	    send_gdb "continue\n"
41ba02
+	    exp_continue
41ba02
+	}
41ba02
+	-re " final_return .*\r\n$gdb_prompt $" {
41ba02
+	}
41ba02
+    }
41ba02
+    gdb_assert $got_hit "size8twice write"
41ba02
+}
41ba02
Index: gdb-7.6.1/gdb/utils.c
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/utils.c	2018-05-05 17:12:47.002758937 +0200
41ba02
+++ gdb-7.6.1/gdb/utils.c	2018-05-05 17:12:49.571781890 +0200
41ba02
@@ -3316,22 +3316,6 @@
41ba02
   return xstrdup (filename);
41ba02
 }
41ba02
 
41ba02
-ULONGEST
41ba02
-align_up (ULONGEST v, int n)
41ba02
-{
41ba02
-  /* Check that N is really a power of two.  */
41ba02
-  gdb_assert (n && (n & (n-1)) == 0);
41ba02
-  return (v + n - 1) & -n;
41ba02
-}
41ba02
-
41ba02
-ULONGEST
41ba02
-align_down (ULONGEST v, int n)
41ba02
-{
41ba02
-  /* Check that N is really a power of two.  */
41ba02
-  gdb_assert (n && (n & (n-1)) == 0);
41ba02
-  return (v & -n);
41ba02
-}
41ba02
-
41ba02
 /* Allocation function for the libiberty hash table which uses an
41ba02
    obstack.  The obstack is passed as DATA.  */
41ba02
 
41ba02
Index: gdb-7.6.1/gdb/utils.h
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/utils.h	2018-05-05 17:12:47.002758937 +0200
41ba02
+++ gdb-7.6.1/gdb/utils.h	2018-05-05 17:12:49.572781899 +0200
41ba02
@@ -346,38 +346,6 @@
41ba02
 
41ba02
 extern int myread (int, char *, int);
41ba02
 
41ba02
-/* Ensure that V is aligned to an N byte boundary (B's assumed to be a
41ba02
-   power of 2).  Round up/down when necessary.  Examples of correct
41ba02
-   use include:
41ba02
-
41ba02
-   addr = align_up (addr, 8); -- VALUE needs 8 byte alignment
41ba02
-   write_memory (addr, value, len);
41ba02
-   addr += len;
41ba02
-
41ba02
-   and:
41ba02
-
41ba02
-   sp = align_down (sp - len, 16); -- Keep SP 16 byte aligned
41ba02
-   write_memory (sp, value, len);
41ba02
-
41ba02
-   Note that uses such as:
41ba02
-
41ba02
-   write_memory (addr, value, len);
41ba02
-   addr += align_up (len, 8);
41ba02
-
41ba02
-   and:
41ba02
-
41ba02
-   sp -= align_up (len, 8);
41ba02
-   write_memory (sp, value, len);
41ba02
-
41ba02
-   are typically not correct as they don't ensure that the address (SP
41ba02
-   or ADDR) is correctly aligned (relying on previous alignment to
41ba02
-   keep things right).  This is also why the methods are called
41ba02
-   "align_..." instead of "round_..." as the latter reads better with
41ba02
-   this incorrect coding style.  */
41ba02
-
41ba02
-extern ULONGEST align_up (ULONGEST v, int n);
41ba02
-extern ULONGEST align_down (ULONGEST v, int n);
41ba02
-
41ba02
 extern struct cleanup *make_cleanup_restore_selected_frame (void);
41ba02
 
41ba02
 #endif /* UTILS_H */
41ba02
Index: gdb-7.6.1/gdb/testsuite/lib/gdb.exp
41ba02
===================================================================
41ba02
--- gdb-7.6.1.orig/gdb/testsuite/lib/gdb.exp	2018-05-05 17:12:47.001758928 +0200
41ba02
+++ gdb-7.6.1/gdb/testsuite/lib/gdb.exp	2018-05-05 17:13:20.200055570 +0200
41ba02
@@ -1209,6 +1209,26 @@
41ba02
     }
41ba02
 }
41ba02
 
41ba02
+# Issue a PASS and return true if evaluating CONDITION in the caller's
41ba02
+# frame returns true, and issue a FAIL and return false otherwise.
41ba02
+# MESSAGE is the pass/fail message to be printed.  If MESSAGE is
41ba02
+# omitted or is empty, then the pass/fail messages use the condition
41ba02
+# string as the message.
41ba02
+
41ba02
+proc gdb_assert { condition {message ""} } {
41ba02
+    if { $message == ""} {
41ba02
+        set message $condition
41ba02
+    }
41ba02
+
41ba02
+    set res [uplevel 1 expr $condition]
41ba02
+    if {!$res} {
41ba02
+        fail $message
41ba02
+    } else {
41ba02
+        pass $message
41ba02
+    }
41ba02
+    return $res
41ba02
+}
41ba02
+
41ba02
 proc gdb_reinitialize_dir { subdir } {
41ba02
     global gdb_prompt
41ba02