diff options
-rw-r--r-- | gdb/aarch64-linux-nat.c | 2 | ||||
-rw-r--r-- | gdb/aarch64-linux-tdep.c | 48 | ||||
-rw-r--r-- | gdb/arch-utils.c | 8 | ||||
-rw-r--r-- | gdb/arch-utils.h | 4 | ||||
-rw-r--r-- | gdb/arch/aarch64.c | 31 | ||||
-rw-r--r-- | gdb/arch/aarch64.h | 18 | ||||
-rw-r--r-- | gdb/breakpoint.c | 6 | ||||
-rw-r--r-- | gdb/gdbarch-components.py | 22 | ||||
-rw-r--r-- | gdb/gdbarch-gen.h | 21 | ||||
-rw-r--r-- | gdb/gdbarch.c | 24 | ||||
-rw-r--r-- | gdb/target.c | 2 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-non-address-bits.c | 25 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/aarch64-non-address-bits.exp | 118 | ||||
-rw-r--r-- | gdb/utils.c | 22 | ||||
-rw-r--r-- | gdb/utils.h | 3 | ||||
-rw-r--r-- | gdbserver/linux-aarch64-low.cc | 33 |
16 files changed, 314 insertions, 73 deletions
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index 0f4d0af..03b364a 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -851,7 +851,7 @@ aarch64_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p) kernel can potentially be tagged addresses. */ struct gdbarch *gdbarch = thread_architecture (inferior_ptid); const CORE_ADDR addr_trap - = address_significant (gdbarch, (CORE_ADDR) siginfo.si_addr); + = gdbarch_remove_non_address_bits (gdbarch, (CORE_ADDR) siginfo.si_addr); /* Check if the address matches any watched address. */ state = aarch64_get_debug_reg_state (inferior_ptid.pid ()); diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index 69eb1b0..7e04bd9 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -1609,7 +1609,7 @@ aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, struct value *address) CORE_ADDR addr = value_as_address (address); /* Remove the top byte for the memory range check. */ - addr = address_significant (gdbarch, addr); + addr = gdbarch_remove_non_address_bits (gdbarch, addr); /* Check if the page that contains ADDRESS is mapped with PROT_MTE. */ if (!linux_address_in_memtag_page (addr)) @@ -1635,7 +1635,7 @@ aarch64_linux_memtag_matches_p (struct gdbarch *gdbarch, /* Fetch the allocation tag for ADDRESS. */ gdb::optional<CORE_ADDR> atag - = aarch64_mte_get_atag (address_significant (gdbarch, addr)); + = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr)); if (!atag.has_value ()) return true; @@ -1674,7 +1674,7 @@ aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address, else { /* Remove the top byte. */ - addr = address_significant (gdbarch, addr); + addr = gdbarch_remove_non_address_bits (gdbarch, addr); /* Make sure we are dealing with a tagged address to begin with. */ if (!aarch64_linux_tagged_address_p (gdbarch, address)) @@ -1731,7 +1731,7 @@ aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address, return nullptr; /* Remove the top byte. */ - addr = address_significant (gdbarch, addr); + addr = gdbarch_remove_non_address_bits (gdbarch, addr); gdb::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr); if (!atag.has_value ()) @@ -1805,7 +1805,8 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch, uiout->text ("\n"); gdb::optional<CORE_ADDR> atag - = aarch64_mte_get_atag (address_significant (gdbarch, fault_addr)); + = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, + fault_addr)); gdb_byte ltag = aarch64_mte_get_ltag (fault_addr); if (!atag.has_value ()) @@ -1979,6 +1980,40 @@ aarch64_linux_decode_memtag_section (struct gdbarch *gdbarch, return tags; } +/* AArch64 implementation of the remove_non_address_bits gdbarch hook. Remove + non address bits from a pointer value. */ + +static CORE_ADDR +aarch64_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer) +{ + aarch64_gdbarch_tdep *tdep = gdbarch_tdep<aarch64_gdbarch_tdep> (gdbarch); + + /* By default, we assume TBI and discard the top 8 bits plus the VA range + select bit (55). */ + CORE_ADDR mask = AARCH64_TOP_BITS_MASK; + + if (tdep->has_pauth ()) + { + /* Fetch the PAC masks. These masks are per-process, so we can just + fetch data from whatever thread we have at the moment. + + Also, we have both a code mask and a data mask. For now they are the + same, but this may change in the future. */ + struct regcache *regs = get_current_regcache (); + CORE_ADDR cmask, dmask; + + if (regs->cooked_read (tdep->pauth_reg_base, &dmask) != REG_VALID) + dmask = mask; + + if (regs->cooked_read (tdep->pauth_reg_base + 1, &cmask) != REG_VALID) + cmask = mask; + + mask |= aarch64_mask_from_pac_registers (cmask, dmask); + } + + return aarch64_remove_top_bits (pointer, mask); +} + static void aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -2034,7 +2069,8 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* The top byte of a user space address known as the "tag", is ignored by the kernel and can be regarded as additional data associated with the address. */ - set_gdbarch_significant_addr_bit (gdbarch, 56); + set_gdbarch_remove_non_address_bits (gdbarch, + aarch64_remove_non_address_bits); /* MTE-specific settings and hooks. */ if (tdep->has_mte ()) diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c index 60ffdc5..f84bf87 100644 --- a/gdb/arch-utils.c +++ b/gdb/arch-utils.c @@ -82,6 +82,14 @@ legacy_register_sim_regno (struct gdbarch *gdbarch, int regnum) return LEGACY_SIM_REGNO_IGNORE; } +/* See arch-utils.h */ + +CORE_ADDR +default_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer) +{ + /* By default, just return the pointer value. */ + return pointer; +} /* See arch-utils.h */ diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h index 46018a6..76c9dce 100644 --- a/gdb/arch-utils.h +++ b/gdb/arch-utils.h @@ -132,6 +132,10 @@ extern const struct floatformat ** default_floatformat_for_type (struct gdbarch *gdbarch, const char *name, int len); +/* Default implementation of gdbarch_remove_non_address_bits. */ +CORE_ADDR default_remove_non_address_bits (struct gdbarch *gdbarch, + CORE_ADDR pointer); + /* Default implementation of gdbarch_memtag_to_string. */ extern std::string default_memtag_to_string (struct gdbarch *gdbarch, struct value *tag); diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c index 565c5e7..0df4140 100644 --- a/gdb/arch/aarch64.c +++ b/gdb/arch/aarch64.c @@ -59,3 +59,34 @@ aarch64_create_target_description (const aarch64_features &features) return tdesc.release (); } + +/* See arch/aarch64.h. */ + +CORE_ADDR +aarch64_remove_top_bits (CORE_ADDR pointer, CORE_ADDR mask) +{ + /* The VA range select bit is 55. This bit tells us if we have a + kernel-space address or a user-space address. */ + bool kernel_address = (pointer & VA_RANGE_SELECT_BIT_MASK) != 0; + + /* Remove the top non-address bits. */ + pointer &= ~mask; + + /* Sign-extend if we have a kernel-space address. */ + if (kernel_address) + pointer |= mask; + + return pointer; +} + +/* See arch/aarch64.h. */ + +CORE_ADDR +aarch64_mask_from_pac_registers (const CORE_ADDR cmask, const CORE_ADDR dmask) +{ + /* If the masks differ, default to using the one with the most coverage. */ + if (dmask != cmask) + return dmask > cmask ? dmask : cmask; + + return cmask; +} diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h index b1a6ce3..6804819 100644 --- a/gdb/arch/aarch64.h +++ b/gdb/arch/aarch64.h @@ -71,6 +71,17 @@ namespace std target_desc * aarch64_create_target_description (const aarch64_features &features); +/* Given a pointer value POINTER and a MASK of non-address bits, remove the + non-address bits from the pointer and sign-extend the result if required. + The sign-extension is required so we can handle kernel addresses + correctly. */ +CORE_ADDR aarch64_remove_top_bits (CORE_ADDR pointer, CORE_ADDR mask); + +/* Given CMASK and DMASK the two PAC mask registers, return the correct PAC + mask to use for removing non-address bits from a pointer. */ +CORE_ADDR +aarch64_mask_from_pac_registers (const CORE_ADDR cmask, const CORE_ADDR dmask); + /* Register numbers of various important registers. Note that on SVE, the Z registers reuse the V register numbers and the V registers become pseudo registers. */ @@ -104,6 +115,13 @@ enum aarch64_regnum #define AARCH64_TLS_REGISTER_SIZE 8 #define V_REGISTER_SIZE 16 +/* PAC-related constants. */ +/* Bit 55 is used to select between a kernel-space and user-space address. */ +#define VA_RANGE_SELECT_BIT_MASK 0x80000000000000ULL +/* Mask with 1's in bits 55~63, used to remove the top byte of pointers + (Top Byte Ignore). */ +#define AARCH64_TOP_BITS_MASK 0xff80000000000000ULL + /* Pseudo register base numbers. */ #define AARCH64_Q0_REGNUM 0 #define AARCH64_D0_REGNUM (AARCH64_Q0_REGNUM + AARCH64_D_REGISTER_COUNT) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 7a245b6..c4384e5 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -2120,7 +2120,8 @@ update_watchpoint (struct watchpoint *b, bool reparse) loc->gdbarch = value_type (v)->arch (); loc->pspace = frame_pspace; - loc->address = address_significant (loc->gdbarch, addr); + loc->address + = gdbarch_remove_non_address_bits (loc->gdbarch, addr); if (bitsize != 0) { @@ -7303,7 +7304,8 @@ adjust_breakpoint_address (struct gdbarch *gdbarch, = gdbarch_adjust_breakpoint_address (gdbarch, bpaddr); } - adjusted_bpaddr = address_significant (gdbarch, adjusted_bpaddr); + adjusted_bpaddr + = gdbarch_remove_non_address_bits (gdbarch, adjusted_bpaddr); /* An adjusted breakpoint address can significantly alter a user's expectations. Print a warning if an adjustment diff --git a/gdb/gdbarch-components.py b/gdb/gdbarch-components.py index e723094..47a9413 100644 --- a/gdb/gdbarch-components.py +++ b/gdb/gdbarch-components.py @@ -1154,15 +1154,23 @@ possible it should be in TARGET_READ_PC instead). invalid=False, ) -Value( +Method( comment=""" -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. +On some architectures, not all bits of a pointer are significant. +On AArch64, for example, the top bits of a pointer may carry a "tag", which +can be ignored by the kernel and the hardware. The "tag" can be regarded as +additional data associated with the pointer, but it is not part of the address. + +Given a pointer for the architecture, this hook removes all the +non-significant bits and sign-extends things as needed. It gets used to remove +non-address bits from data pointers (for example, removing the AArch64 MTE tag +bits from a pointer) and from code pointers (removing the AArch64 PAC signature +from a pointer containing the return address). """, - type="int", - name="significant_addr_bit", + type="CORE_ADDR", + name="remove_non_address_bits", + params=[("CORE_ADDR", "pointer")], + predefault="default_remove_non_address_bits", invalid=False, ) diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h index a663316..5918de5 100644 --- a/gdb/gdbarch-gen.h +++ b/gdb/gdbarch-gen.h @@ -626,13 +626,20 @@ 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); +/* On some architectures, not all bits of a pointer are significant. + On AArch64, for example, the top bits of a pointer may carry a "tag", which + can be ignored by the kernel and the hardware. The "tag" can be regarded as + additional data associated with the pointer, but it is not part of the address. + + Given a pointer for the architecture, this hook removes all the + non-significant bits and sign-extends things as needed. It gets used to remove + non-address bits from data pointers (for example, removing the AArch64 MTE tag + bits from a pointer) and from code pointers (removing the AArch64 PAC signature + from a pointer containing the return address). */ + +typedef CORE_ADDR (gdbarch_remove_non_address_bits_ftype) (struct gdbarch *gdbarch, CORE_ADDR pointer); +extern CORE_ADDR gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer); +extern void set_gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, gdbarch_remove_non_address_bits_ftype *remove_non_address_bits); /* Return a string representation of the memory tag TAG. */ diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 74c12c5..2d4b116 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -143,7 +143,7 @@ struct gdbarch int frame_red_zone_size = 0; gdbarch_convert_from_func_ptr_addr_ftype *convert_from_func_ptr_addr = convert_from_func_ptr_addr_identity; gdbarch_addr_bits_remove_ftype *addr_bits_remove = core_addr_identity; - int significant_addr_bit = 0; + gdbarch_remove_non_address_bits_ftype *remove_non_address_bits = default_remove_non_address_bits; gdbarch_memtag_to_string_ftype *memtag_to_string = default_memtag_to_string; gdbarch_tagged_address_p_ftype *tagged_address_p = default_tagged_address_p; gdbarch_memtag_matches_p_ftype *memtag_matches_p = default_memtag_matches_p; @@ -400,7 +400,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of frame_red_zone_size, invalid_p == 0 */ /* Skip verify of convert_from_func_ptr_addr, invalid_p == 0 */ /* Skip verify of addr_bits_remove, invalid_p == 0 */ - /* Skip verify of significant_addr_bit, invalid_p == 0 */ + /* Skip verify of remove_non_address_bits, invalid_p == 0 */ /* Skip verify of memtag_to_string, invalid_p == 0 */ /* Skip verify of tagged_address_p, invalid_p == 0 */ /* Skip verify of memtag_matches_p, invalid_p == 0 */ @@ -885,8 +885,8 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: addr_bits_remove = <%s>\n", host_address_to_string (gdbarch->addr_bits_remove)); gdb_printf (file, - "gdbarch_dump: significant_addr_bit = %s\n", - plongest (gdbarch->significant_addr_bit)); + "gdbarch_dump: remove_non_address_bits = <%s>\n", + host_address_to_string (gdbarch->remove_non_address_bits)); gdb_printf (file, "gdbarch_dump: memtag_to_string = <%s>\n", host_address_to_string (gdbarch->memtag_to_string)); @@ -3100,21 +3100,21 @@ set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch, gdbarch->addr_bits_remove = addr_bits_remove; } -int -gdbarch_significant_addr_bit (struct gdbarch *gdbarch) +CORE_ADDR +gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, CORE_ADDR pointer) { gdb_assert (gdbarch != NULL); - /* Skip verify of significant_addr_bit, invalid_p == 0 */ + gdb_assert (gdbarch->remove_non_address_bits != NULL); if (gdbarch_debug >= 2) - gdb_printf (gdb_stdlog, "gdbarch_significant_addr_bit called\n"); - return gdbarch->significant_addr_bit; + gdb_printf (gdb_stdlog, "gdbarch_remove_non_address_bits called\n"); + return gdbarch->remove_non_address_bits (gdbarch, pointer); } void -set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch, - int significant_addr_bit) +set_gdbarch_remove_non_address_bits (struct gdbarch *gdbarch, + gdbarch_remove_non_address_bits_ftype remove_non_address_bits) { - gdbarch->significant_addr_bit = significant_addr_bit; + gdbarch->remove_non_address_bits = remove_non_address_bits; } std::string diff --git a/gdb/target.c b/gdb/target.c index b7cd3b9..8a2daa0 100644 --- a/gdb/target.c +++ b/gdb/target.c @@ -1617,7 +1617,7 @@ memory_xfer_partial (struct target_ops *ops, enum target_object object, if (len == 0) return TARGET_XFER_EOF; - memaddr = address_significant (target_gdbarch (), memaddr); + memaddr = gdbarch_remove_non_address_bits (target_gdbarch (), memaddr); /* Fill in READBUF with breakpoint shadows, or WRITEBUF with breakpoint insns, thus hiding out from higher layers whether diff --git a/gdb/testsuite/gdb.arch/aarch64-non-address-bits.c b/gdb/testsuite/gdb.arch/aarch64-non-address-bits.c new file mode 100644 index 0000000..3cf6d63 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-non-address-bits.c @@ -0,0 +1,25 @@ +/* This file is part of GDB, the GNU debugger. + + Copyright 2022 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/>. */ + +static long l = 0; +static long *l_ptr = &l; + +int +main (int argc, char **argv) +{ + return *l_ptr; +} diff --git a/gdb/testsuite/gdb.arch/aarch64-non-address-bits.exp b/gdb/testsuite/gdb.arch/aarch64-non-address-bits.exp new file mode 100644 index 0000000..9269d38 --- /dev/null +++ b/gdb/testsuite/gdb.arch/aarch64-non-address-bits.exp @@ -0,0 +1,118 @@ +# Copyright 2022 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. +# +# Test that GDB for AArch64/Linux can properly handle pointers with +# the upper 16 bits (PAC) or 8 bits (Tag) set, as well as the +# VA_RANGE_SELECT bit (55). + +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] { + return -1 +} + +# We need to iterate over two distinct ranges, separated by a single bit. +# This bit is 55 (VA_RANGE_SELECT) which tells us if we have a kernel-space +# address or a user-space address. + +# The tag field has 8 bits. +set tag_bits_count 8 + +# The pac field has 7 bits. +set pac_bits_count 7 + +# A couple patterns that we reuse for the tests later. One is for a successful +# memory read and the other is for a memory read failure. +set memory_read_ok_pattern "$::hex\( <l>\)?:\[ \t\]+$::hex" +set memory_read_fail_pattern "$::hex:\[ \t\]+Cannot access memory at address $::hex" + +set pac_enabled 0 + +# Check if PAC is enabled. +gdb_test_multiple "ptype \$pauth_cmask" "fetch PAC cmask" { + -re -wrap "type = long" { + set pac_enabled 1 + } + -re -wrap "type = void" { + } + -re ".*$gdb_prompt $" { + fail $gdb_test_name + return 1 + } +} + +# Value of the cmask register. +set cmask 0 + +# If there are PAC registers, GDB uses those to unmask the PAC bits. +if {$pac_enabled} { + set cmask [get_valueof "" "\$pauth_cmask >> 48" "0" "fetch PAC cmask"] +} + +# Cycle through the tag and pac bit ranges and check how GDB +# behaves when trying to access these addresses. +foreach_with_prefix upper_bits {"0x0" "0x1" "0x2" "0x4" "0x8" "0x10" "0x20" "0x40" "0x80"} { + foreach_with_prefix lower_bits {"0x0" "0x1" "0x2" "0x4" "0x8" "0x10" "0x20" "0x40"} { + + # A successful memory read pattern + set pattern $memory_read_ok_pattern + + if {!$pac_enabled} { + # If PAC is not supported, memory reads will fail if + # lower_bits != 0x0 + if {$lower_bits != "0x0"} { + set pattern $memory_read_fail_pattern + } + } else { + # Otherwise, figure out if the memory read will succeed or not by + # checking cmask. + gdb_test_multiple "p/x (~${cmask}ULL & (${lower_bits}ULL))" "" { + -re -wrap "= 0x0" { + # Either cmask is 0x7F or lower_bits is 0x0. + # Either way, the memory read should succeed. + } + -re -wrap "= $::hex" { + if {$lower_bits != "0x0"} { + # cmask doesn't mask off all the PAC bits, which + # results in a memory read failure, with the actual + # address being accessed differing from the one we + # passed. + set pattern $memory_read_fail_pattern + } + } + } + } + + # Test without the VA_RANGE_SELECT bit set. + gdb_test "x/gx ((unsigned long) l_ptr | ((${upper_bits}ULL << 56) | (${lower_bits}ULL << 48)))" \ + $pattern \ + "user-space memory access" + + # Now test with the VA_RANGE_SELECT bit set. + gdb_test "x/gx ((unsigned long) l_ptr | ((${upper_bits}ULL << 56) | (${lower_bits}ULL << 48) | (1ULL << 55))) " \ + $memory_read_fail_pattern \ + "kernel-space memory access" + } +} diff --git a/gdb/utils.c b/gdb/utils.c index 74917f2..4a715b9 100644 --- a/gdb/utils.c +++ b/gdb/utils.c @@ -3110,28 +3110,6 @@ show_debug_timestamp (struct ui_file *file, int from_tty, } -/* See utils.h. */ - -CORE_ADDR -address_significant (gdbarch *gdbarch, CORE_ADDR addr) -{ - /* Clear insignificant bits of a target address and sign extend resulting - 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. Skip updating of target address if current target - has not set gdbarch significant_addr_bit. */ - int addr_bit = gdbarch_significant_addr_bit (gdbarch); - - if (addr_bit && (addr_bit < (sizeof (CORE_ADDR) * HOST_CHAR_BIT))) - { - CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1); - addr &= ((CORE_ADDR) 1 << addr_bit) - 1; - addr = (addr ^ sign) - sign; - } - - return addr; -} - const char * paddress (struct gdbarch *gdbarch, CORE_ADDR addr) { diff --git a/gdb/utils.h b/gdb/utils.h index 509361d..59340f3 100644 --- a/gdb/utils.h +++ b/gdb/utils.h @@ -264,9 +264,6 @@ extern void fputs_styled (const char *linebuffer, extern void fputs_highlighted (const char *str, const compiled_regex &highlight, struct ui_file *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); diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc index b657a26..6f44bc6 100644 --- a/gdbserver/linux-aarch64-low.cc +++ b/gdbserver/linux-aarch64-low.cc @@ -522,21 +522,30 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr, return ret; } -/* Return the address only having significant bits. This is used to ignore - the top byte (TBI). */ - static CORE_ADDR -address_significant (CORE_ADDR addr) +aarch64_remove_non_address_bits (CORE_ADDR pointer) { - /* Clear insignificant bits of a target address and sign extend resulting - address. */ - int addr_bit = 56; + /* By default, we assume TBI and discard the top 8 bits plus the + VA range select bit (55). */ + CORE_ADDR mask = AARCH64_TOP_BITS_MASK; + + /* Check if PAC is available for this target. */ + if (tdesc_contains_feature (current_process ()->tdesc, + "org.gnu.gdb.aarch64.pauth")) + { + /* Fetch the PAC masks. These masks are per-process, so we can just + fetch data from whatever thread we have at the moment. - CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1); - addr &= ((CORE_ADDR) 1 << addr_bit) - 1; - addr = (addr ^ sign) - sign; + Also, we have both a code mask and a data mask. For now they are the + same, but this may change in the future. */ + + struct regcache *regs = get_thread_regcache (current_thread, 1); + CORE_ADDR dmask = regcache_raw_get_unsigned_by_name (regs, "pauth_dmask"); + CORE_ADDR cmask = regcache_raw_get_unsigned_by_name (regs, "pauth_cmask"); + mask |= aarch64_mask_from_pac_registers (cmask, dmask); + } - return addr; + return aarch64_remove_top_bits (pointer, mask); } /* Implementation of linux target ops method "low_stopped_data_address". */ @@ -563,7 +572,7 @@ aarch64_target::low_stopped_data_address () hardware watchpoint hit. The stopped data addresses coming from the kernel can potentially be tagged addresses. */ const CORE_ADDR addr_trap - = address_significant ((CORE_ADDR) siginfo.si_addr); + = aarch64_remove_non_address_bits ((CORE_ADDR) siginfo.si_addr); /* Check if the address matches any watched address. */ state = aarch64_get_debug_reg_state (pid_of (current_thread)); |