Clear non-significant bits of address on memory access
authorYao Qi <yao.qi@linaro.org>
Fri, 8 Dec 2017 17:27:03 +0000 (17:27 +0000)
committerYao Qi <yao.qi@linaro.org>
Fri, 8 Dec 2017 17:27:03 +0000 (17:27 +0000)
ARMv8 supports tagged address, that is, the top one byte in address
is ignored.  It is always enabled on aarch64-linux.  See
https://www.kernel.org/doc/Documentation/arm64/tagged-pointers.txt

The tag in the tagged address is modeled as non-significant bits in
address, so this patch adds a new gdbarch method significant_addr_bit and
clear the non-significant bits (the top byte in ARMv8) of the virtual
address at the point before passing address to target cache layer.  IOW,
the address used in the target cache layer is already cleared.

Before this patch,
(gdb) x/x 0x0000000000411030
0x411030 <global>: 0x00000000
(gdb) x/x 0xf000000000411030
0xf000000000411030: Cannot access memory at address 0xf000000000411030

After this patch,

(gdb) x/x 0x0000000000411030
0x411030 <global>: 0x00000000
(gdb) x/x 0xf000000000411030
0xf000000000411030: 0x00000000

Note that I used address_significant in paddress, but it causes a
regression gdb.base/long_long.exp, because gdb clears the non-significant
bits in address, but test still expects them.

p/a val.oct^M
$24 = 0x2ee53977053977^M
(gdb) FAIL: gdb.base/long_long.exp: p/a val.oct

so I defer the change there.

gdb:

2017-12-08  Yao Qi  <yao.qi@linaro.org>

* aarch64-tdep.c (aarch64_gdbarch_init): Install gdbarch
significant_addr_bit.
* gdbarch.sh (significant_addr_bit): New.
* gdbarch.c, gdbarch.h: Re-generated.
* target.c (memory_xfer_partial): Call address_significant.
* utils.c (address_significant): New function.
* utils.h (address_significant): Declare.

2017-12-08  Yao Qi  <yao.qi@linaro.org>

gdb/testsuite:

* gdb.arch/aarch64-tagged-pointer.c: New file.
* gdb.arch/aarch64-tagged-pointer.exp: New file.

gdb/ChangeLog
gdb/aarch64-tdep.c
gdb/gdbarch.c
gdb/gdbarch.h
gdb/gdbarch.sh
gdb/target.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c [new file with mode: 0644]
gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp [new file with mode: 0644]
gdb/utils.c
gdb/utils.h

index 21ddce0454160f8b91fa6339e8161f213bddd63b..952a2fffbfe5ff5ba31e9062dbb84636620f7a2f 100644 (file)
@@ -1,3 +1,13 @@
+2017-12-08  Yao Qi  <yao.qi@linaro.org>
+
+       * aarch64-tdep.c (aarch64_gdbarch_init): Install gdbarch
+       significant_addr_bit.
+       * gdbarch.sh (significant_addr_bit): New.
+       * gdbarch.c, gdbarch.h: Re-generated.
+       * target.c (memory_xfer_partial): Call address_significant.
+       * utils.c (address_significant): New function.
+       * utils.h (address_significant): Declare.
+
 2017-12-08  Tom Tromey  <tom@tromey.com>
 
        * printcmd.c (ui_printf): Update.  Use std::vector.
index 6a0d4b744c89c39d5b62b7dcc12273fccb0c32e6..383d584b5266459ca6d6722507a700acc92b56d6 100644 (file)
@@ -2970,6 +2970,11 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_tdesc_pseudo_register_reggroup_p (gdbarch,
                                        aarch64_pseudo_register_reggroup_p);
 
+  /* The top byte of an address is known as the "tag" and is
+     ignored by the kernel, the hardware, etc. and can be regarded
+     as additional data associated with the address.  */
+  set_gdbarch_significant_addr_bit (gdbarch, 56);
+
   /* ABI */
   set_gdbarch_short_bit (gdbarch, 16);
   set_gdbarch_int_bit (gdbarch, 32);
index 007392cd6a7f8438401e8e16e1ca317c9740f4ac..8177f0539c1160921011116c579f43ce97439be8 100644 (file)
@@ -259,6 +259,7 @@ struct gdbarch
   int frame_red_zone_size;
   gdbarch_convert_from_func_ptr_addr_ftype *convert_from_func_ptr_addr;
   gdbarch_addr_bits_remove_ftype *addr_bits_remove;
+  int significant_addr_bit;
   gdbarch_software_single_step_ftype *software_single_step;
   gdbarch_single_step_through_delay_ftype *single_step_through_delay;
   gdbarch_print_insn_ftype *print_insn;
@@ -618,6 +619,8 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of stabs_argument_has_addr, invalid_p == 0 */
   /* Skip verify of convert_from_func_ptr_addr, invalid_p == 0 */
   /* Skip verify of addr_bits_remove, invalid_p == 0 */
+  if (gdbarch->significant_addr_bit == 0)
+    gdbarch->significant_addr_bit = gdbarch_addr_bit (gdbarch);
   /* Skip verify of software_single_step, has predicate.  */
   /* Skip verify of single_step_through_delay, has predicate.  */
   /* Skip verify of print_insn, invalid_p == 0 */
@@ -1324,6 +1327,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   fprintf_unfiltered (file,
                       "gdbarch_dump: short_bit = %s\n",
                       plongest (gdbarch->short_bit));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: significant_addr_bit = %s\n",
+                      plongest (gdbarch->significant_addr_bit));
   fprintf_unfiltered (file,
                       "gdbarch_dump: gdbarch_single_step_through_delay_p() = %d\n",
                       gdbarch_single_step_through_delay_p (gdbarch));
@@ -3215,6 +3221,22 @@ set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch,
   gdbarch->addr_bits_remove = addr_bits_remove;
 }
 
+int
+gdbarch_significant_addr_bit (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_significant_addr_bit called\n");
+  return gdbarch->significant_addr_bit;
+}
+
+void
+set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch,
+                                  int significant_addr_bit)
+{
+  gdbarch->significant_addr_bit = significant_addr_bit;
+}
+
 int
 gdbarch_software_single_step_p (struct gdbarch *gdbarch)
 {
index d2e6b6f63ecd6de28deb032f259549b20e9411a1..1a654b625000a887e2545b604855d0d371a400f5 100644 (file)
@@ -677,6 +677,14 @@ typedef CORE_ADDR (gdbarch_addr_bits_remove_ftype) (struct gdbarch *gdbarch, COR
 extern CORE_ADDR gdbarch_addr_bits_remove (struct gdbarch *gdbarch, CORE_ADDR addr);
 extern void set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch, gdbarch_addr_bits_remove_ftype *addr_bits_remove);
 
+/* On some machines, not all bits of an address word are significant.
+   For example, on AArch64, the top bits of an address known as the "tag"
+   are ignored by the kernel, the hardware, etc. and can be regarded as
+   additional data associated with the address. */
+
+extern int gdbarch_significant_addr_bit (struct gdbarch *gdbarch);
+extern void set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch, int significant_addr_bit);
+
 /* FIXME/cagney/2001-01-18: This should be split in two.  A target method that
    indicates if the target needs software single step.  An ISA method to
    implement it.
index 6459b12747bb29109c621140f9b853d843308ef6..1f165cf11baa8b15dcba38b2d9c2d5b3483a9497 100755 (executable)
@@ -621,6 +621,12 @@ m;CORE_ADDR;convert_from_func_ptr_addr;CORE_ADDR addr, struct target_ops *targ;a
 # possible it should be in TARGET_READ_PC instead).
 m;CORE_ADDR;addr_bits_remove;CORE_ADDR addr;addr;;core_addr_identity;;0
 
+# On some machines, not all bits of an address word are significant.
+# For example, on AArch64, the top bits of an address known as the "tag"
+# are ignored by the kernel, the hardware, etc. and can be regarded as
+# additional data associated with the address.
+v;int;significant_addr_bit;;;;;gdbarch_addr_bit (gdbarch);
+
 # FIXME/cagney/2001-01-18: This should be split in two.  A target method that
 # indicates if the target needs software single step.  An ISA method to
 # implement it.
index 3b8b8ea2186ba61dc38b6af6b8e7c387e1a98f05..767a2ad45ac135b1c5150ea98481b53588f365c2 100644 (file)
@@ -1214,6 +1214,8 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object,
   if (len == 0)
     return TARGET_XFER_EOF;
 
+  memaddr = address_significant (target_gdbarch (), memaddr);
+
   /* Fill in READBUF with breakpoint shadows, or WRITEBUF with
      breakpoint insns, thus hiding out from higher layers whether
      there are software breakpoints inserted in the code stream.  */
index 720a9ecdd39a85e37c6df7386c3603b7b0c911d6..cc834de616a4e7ee2bf537883110e6f49fc456be 100644 (file)
@@ -1,3 +1,8 @@
+2017-12-08  Yao Qi  <yao.qi@linaro.org>
+
+       * gdb.arch/aarch64-tagged-pointer.c: New file.
+       * gdb.arch/aarch64-tagged-pointer.exp: New file.
+
 2017-12-08  Sergio Durigan Junior  <sergiodj@redhat.com>
 
        * gdb.arch/i386-sse-stack-align.exp: Cast "print" function call
diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
new file mode 100644 (file)
index 0000000..7c90132
--- /dev/null
@@ -0,0 +1,48 @@
+/* This file is part of GDB, the GNU debugger.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   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 <stdint.h>
+
+struct s
+{
+  int i;
+};
+
+static void
+foo (void)
+{
+}
+
+int
+main (void)
+{
+  struct s s1;
+  struct s *sp1, *sp2;
+  int i = 1234;
+  int *p1, *p2;
+
+  s1.i = 1234;
+  sp1 = &s1;
+  p1 = &i;
+  /* SP1 and SP2 have different tags, but point to the same address.  */
+  sp2 = (struct s *) ((uintptr_t) sp1 | 0xf000000000000000ULL);
+  p2 = (int *) ((uintptr_t) p1 | 0xf000000000000000ULL);
+
+  void (*func_ptr) (void) = foo;
+  func_ptr = (void (*) (void)) ((uintptr_t) func_ptr | 0xf000000000000000ULL);
+  sp2->i = 4321; /* breakpoint here.  */
+}
diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
new file mode 100644 (file)
index 0000000..4f2b44c
--- /dev/null
@@ -0,0 +1,67 @@
+# Copyright 2017 Free Software Foundation, Inc.
+#
+# 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/>.
+#
+# This file is part of the gdb testsuite.
+
+if {![is_aarch64_target]} {
+    verbose "Skipping ${gdb_test_file_name}."
+    return
+}
+
+standard_testfile
+if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
+    return -1
+}
+
+if ![runto_main] {
+    untested "could not run to main"
+    return -1
+}
+
+gdb_breakpoint [gdb_get_line_number "breakpoint here"]
+gdb_continue_to_breakpoint "breakpoint here"
+
+# Test that GDB manages caches correctly for tagged address.
+# Read from P2,
+gdb_test "x p2" "$hex:\[\t \]+0x000004d2"
+gdb_test_no_output "set variable i = 5678"
+# Test that *P2 is updated.
+gdb_test "x p2" "$hex:\[\t \]+0x0000162e"
+
+# Read from SP1->i,
+gdb_test "print sp1->i" " = 1234"
+# Write to SP2->i,
+gdb_test_no_output "set variable sp2->i = 5678"
+# Test that SP1->i is updated.
+gdb_test "print sp1->i" " = 5678"
+
+gdb_test "x/d &sp2->i" "$hex:\[\t \]+5678"
+gdb_test "x/d &sp1->i" "$hex:\[\t \]+5678"
+
+# Test that the same disassembly is got when disassembling function vs
+# tagged function pointer.
+set insn1 ""
+set insn2 ""
+set test "disassemble foo,+8"
+gdb_test_multiple $test $test {
+    -re ":\[\t \]+(\[a-z\]*)\[ \r\n\]+.*:\[\t \]+(\[a-z\]*).*$gdb_prompt $" {
+       set insn1 $expect_out(1,string)
+       set insn2 $expect_out(2,string)
+       pass $test
+    }
+}
+
+gdb_test "disassemble func_ptr,+8" \
+    ":\[\t \]+$insn1\[ \r\n\]+.*:\[\t \]+$insn2.*"
index b95dcfd5ce91e8871fd4ded48c69f2c613ddd8b1..2f8f06fb5a340cc74a73195618c12edd34eff7c3 100644 (file)
@@ -2724,6 +2724,23 @@ When set, debugging messages will be marked with seconds and microseconds."),
                           &setdebuglist, &showdebuglist);
 }
 
+/* See utils.h.  */
+
+CORE_ADDR
+address_significant (gdbarch *gdbarch, CORE_ADDR addr)
+{
+  /* Truncate address to the significant bits of a target address,
+     avoiding shifts larger or equal than the width of a CORE_ADDR.
+     The local variable ADDR_BIT stops the compiler reporting a shift
+     overflow when it won't occur.  */
+  int addr_bit = gdbarch_significant_addr_bit (gdbarch);
+
+  if (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))
+    addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
+
+  return addr;
+}
+
 const char *
 paddress (struct gdbarch *gdbarch, CORE_ADDR addr)
 {
index 349ab93cce3a9cf638115c239b8e841a0e73de14..b2e3d571ddcec71eeabf6cb5db85cbb4a3d12f59 100644 (file)
@@ -438,6 +438,9 @@ extern void gdb_print_host_address_1 (const void *addr, struct ui_file *stream);
 #define gdb_print_host_address(ADDR, STREAM) \
   gdb_print_host_address_1 ((const void *) ADDR, STREAM)
 
+/* Return the address only having significant bits.  */
+extern CORE_ADDR address_significant (gdbarch *gdbarch, CORE_ADDR addr);
+
 /* Convert CORE_ADDR to string in platform-specific manner.
    This is usually formatted similar to 0x%lx.  */
 extern const char *paddress (struct gdbarch *gdbarch, CORE_ADDR addr);