[PATCH 1/3] [AArch64 Linux] Get rid of top byte from tagged address on memory access

Yao Qi qiyaoltc@gmail.com
Thu Oct 26 08:30:00 GMT 2017


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 patch clear the top byte of the virtual address, at the point before
GDB/GDBserver pass the address to /proc or ptrace syscall on memory access.
The top byte of address is  still retained in the rest of GDB, because
these bits can be used by different applications in different ways.
That is reason I didn't implement gdbarch method addr_bits_remove to get
rid of them.

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

With the tagged address, the variables/memory can be access via different
addresses (or tags), but GDB uses cache for stack variable access and code
access to speed up remote debugging.  Fortunately, tagged address and
GDB stack/code cache can coexist, because,

 - 'x' command doesn't go through cache, so we don't have to worry,
 - gdb only uses stack cache when it believes the variable is on stack,

   int i;
   int *p = &i;

   when print 'i' or 'p', gdb uses stack caches, but when print '*p', gdb
   only uses stack caches to get 'p', and get '*p' without cache, because
   gdb doesn't know the address p points to is on stack or not.
 - gdb uses code caches to access code, do disassembly for example, when
   gdb does disassembly for a function (without tag) and a tagged function
   pointer, gdb creates thinks they are different addresses, and creates
   two different cache lines, but we only have cache when inferior stops,
   and code caches are regarded read-only.

gdb:

2017-10-19  Yao Qi  <yao.qi@linaro.org>

	* aarch64-linux-nat.c (super_xfer_partial): New function pointer.
	(aarch64_linux_xfer_partial): New function.
	(_initialize_aarch64_linux_nat): Initialize super_xfer_partial,
	and update to_xfer_partial.

gdb/gdbserver:

2017-10-19  Yao Qi  <yao.qi@linaro.org>

	* linux-aarch64-low.c (super_read_memory): New function pointer.
	(super_write_memory): Likewise.
	(aarch64_linux_read_memory): New function.
	(aarch64_linux_write_memory): New function.
	(initialize_low_arch): Initialize super_read_memory and
	super_write_memory.  Update read_memory and write_memory.

2017-10-19  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/aarch64-linux-nat.c                           | 25 +++++++++
 gdb/gdbserver/linux-aarch64-low.c                 | 33 +++++++++++
 gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c   | 48 ++++++++++++++++
 gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp | 68 +++++++++++++++++++++++
 4 files changed, 174 insertions(+)
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index de18edd..78c038b 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -780,6 +780,27 @@ aarch64_linux_can_do_single_step (struct target_ops *target)
   return 1;
 }
 
+static target_xfer_partial_ftype *super_xfer_partial;
+
+/* Implement the "to_xfer_partial" target_ops method.  */
+
+static enum target_xfer_status
+aarch64_linux_xfer_partial (struct target_ops *ops, enum target_object object,
+			    const char *annex, gdb_byte *readbuf,
+			    const gdb_byte *writebuf, ULONGEST offset,
+			    ULONGEST len, ULONGEST *xfered_len)
+{
+  if (object == TARGET_OBJECT_MEMORY)
+    {
+      /* ARMv8 supports tagged address, that is, the top one byte in
+	 virtual address is ignored.  */
+      offset &= 0x0fffffffffffffffULL;
+    }
+
+  return super_xfer_partial (ops, object, annex, readbuf, writebuf,
+			     offset, len, xfered_len);
+}
+
 /* Define AArch64 maintenance commands.  */
 
 static void
@@ -834,6 +855,10 @@ _initialize_aarch64_linux_nat (void)
   super_post_startup_inferior = t->to_post_startup_inferior;
   t->to_post_startup_inferior = aarch64_linux_child_post_startup_inferior;
 
+  /* Override the default to_xfer_partial.  */
+  super_xfer_partial = t->to_xfer_partial;
+  t->to_xfer_partial = aarch64_linux_xfer_partial;
+
   /* Register the target.  */
   linux_nat_add_target (t);
   linux_nat_set_new_thread (t, aarch64_linux_new_thread);
diff --git a/gdb/gdbserver/linux-aarch64-low.c b/gdb/gdbserver/linux-aarch64-low.c
index b00d5c5..bc44092 100644
--- a/gdb/gdbserver/linux-aarch64-low.c
+++ b/gdb/gdbserver/linux-aarch64-low.c
@@ -3004,6 +3004,31 @@ struct linux_target_ops the_low_target =
   aarch64_get_syscall_trapinfo,
 };
 
+static int (*super_read_memory) (CORE_ADDR memaddr, unsigned char *myaddr,
+				 int len);
+
+static  int (*super_write_memory) (CORE_ADDR memaddr,
+				   const unsigned char *myaddr, int len);
+
+static int
+aarch64_linux_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+  /* ARMv8 supports tagged address, that is, the top one byte in
+     virtual address is ignored.  */
+  memaddr &= 0x0fffffffffffffffULL;
+  return super_read_memory (memaddr, myaddr, len);
+}
+
+static int
+aarch64_linux_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
+			    int len)
+{
+  /* ARMv8 supports tagged address, that is, the top one byte in
+     virtual address is ignored.  */
+  memaddr &= 0x0fffffffffffffffULL;
+  return super_write_memory (memaddr, myaddr, len);
+}
+
 void
 initialize_low_arch (void)
 {
@@ -3012,4 +3037,12 @@ initialize_low_arch (void)
   initialize_low_arch_aarch32 ();
 
   initialize_regsets_info (&aarch64_regsets_info);
+
+  /* Override the default read_memory.  */
+  super_read_memory = the_target->read_memory;
+  the_target->read_memory = aarch64_linux_read_memory;
+
+  /* Override the default write_memory.  */
+  super_write_memory = the_target->write_memory;
+  the_target->write_memory = aarch64_linux_write_memory;
 }
diff --git a/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
new file mode 100644
index 0000000..7c90132
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.c
@@ -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
index 0000000..4d36d09
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp
@@ -0,0 +1,68 @@
+# 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+# 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.*"
-- 
1.9.1



More information about the Gdb-patches mailing list