From a738ea1d41daeec0cccb4ab6671f4f6d53bd9e18 Mon Sep 17 00:00:00 2001 From: Yao Qi Date: Fri, 8 Dec 2017 17:27:03 +0000 Subject: [PATCH] Clear non-significant bits of address on memory access 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 : 0x00000000 (gdb) x/x 0xf000000000411030 0xf000000000411030: Cannot access memory at address 0xf000000000411030 After this patch, (gdb) x/x 0x0000000000411030 0x411030 : 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 * 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 gdb/testsuite: * gdb.arch/aarch64-tagged-pointer.c: New file. * gdb.arch/aarch64-tagged-pointer.exp: New file. --- gdb/ChangeLog | 10 +++ gdb/aarch64-tdep.c | 5 ++ gdb/gdbarch.c | 22 ++++++ gdb/gdbarch.h | 8 +++ gdb/gdbarch.sh | 6 ++ gdb/target.c | 2 + gdb/testsuite/ChangeLog | 5 ++ .../gdb.arch/aarch64-tagged-pointer.c | 48 +++++++++++++ .../gdb.arch/aarch64-tagged-pointer.exp | 67 +++++++++++++++++++ gdb/utils.c | 17 +++++ gdb/utils.h | 3 + 11 files changed, 193 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/ChangeLog b/gdb/ChangeLog index 21ddce0454..952a2fffbf 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,13 @@ +2017-12-08 Yao Qi + + * 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 * printcmd.c (ui_printf): Update. Use std::vector. diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index 6a0d4b744c..383d584b52 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -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); diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 007392cd6a..8177f0539c 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -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) { diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index d2e6b6f63e..1a654b6250 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -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. diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index 6459b12747..1f165cf11b 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -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. diff --git a/gdb/target.c b/gdb/target.c index 3b8b8ea218..767a2ad45a 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -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. */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 720a9ecdd3..cc834de616 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-12-08 Yao Qi + + * gdb.arch/aarch64-tagged-pointer.c: New file. + * gdb.arch/aarch64-tagged-pointer.exp: New file. + 2017-12-08 Sergio Durigan Junior * 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 index 0000000000..7c901325fb --- /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 . */ + +#include + +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 0000000000..4f2b44c2c4 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-tagged-pointer.exp @@ -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 . +# +# 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.*" diff --git a/gdb/utils.c b/gdb/utils.c index b95dcfd5ce..2f8f06fb5a 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -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) { diff --git a/gdb/utils.h b/gdb/utils.h index 349ab93cce..b2e3d571dd 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -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); -- 2.34.1