Message ID | 20240730143936.2428794-1-gustavo.romero@linaro.org |
---|---|
State | New |
Headers | show |
Series | [v4] gdb: aarch64: Support MTE on baremetal | expand |
> From: Gustavo Romero <gustavo.romero@linaro.org> > Cc: thiago.bauermann@linaro.org, > gustavo.romero@linaro.org > Date: Tue, 30 Jul 2024 14:39:36 +0000 > > This commit moves aarch64_linux_memtag_matches_p, > aarch64_linux_set_memtags, aarch64_linux_get_memtag, and > aarch64_linux_memtag_to_string hooks (plus aarch64_mte_get_atag > function used by them), along with the setting of the memtag granule > size, from aarch64-linux-tdep.c to aarch64-tdep.c, making MTE available > on baremetal targets. Since the aarch64-linux-tdep.c layer inherits > these hooks from aarch64-tdep.c, there is no effective change for > aarch64-linux targets. > > Helpers used both by aarch64-tdep.c and by aarch64-linux-tdep.c were > moved from arch/aarch64-mte-linux.{c,h} to new arch/aarch64-mte.{c,h} > files. > > Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> > Tested-By: Luis Machado <luis.machado@arm.com> > Approved-By: Luis Machado <luis.machado@arm.com> > --- > gdb/Makefile.in | 3 + > gdb/NEWS | 3 + > gdb/aarch64-linux-tdep.c | 168 +---------------------------- > gdb/aarch64-tdep.c | 168 +++++++++++++++++++++++++++++ > gdb/aarch64-tdep.h | 2 + > gdb/arch/aarch64-mte-linux.c | 56 ---------- > gdb/arch/aarch64-mte-linux.h | 27 ----- > gdb/arch/aarch64-mte.c | 77 +++++++++++++ > gdb/arch/aarch64-mte.h | 51 +++++++++ > gdb/configure.tgt | 3 +- > gdb/nat/aarch64-mte-linux-ptrace.c | 1 + > gdbserver/configure.srv | 1 + > 12 files changed, 309 insertions(+), 251 deletions(-) > create mode 100644 gdb/arch/aarch64-mte.c > create mode 100644 gdb/arch/aarch64-mte.h Thanks, the NEWS part is okay. Reviewed-By: Eli Zaretskii <eliz@gnu.org>
On 7/30/24 15:39, Gustavo Romero wrote: > This commit moves aarch64_linux_memtag_matches_p, > aarch64_linux_set_memtags, aarch64_linux_get_memtag, and > aarch64_linux_memtag_to_string hooks (plus aarch64_mte_get_atag > function used by them), along with the setting of the memtag granule > size, from aarch64-linux-tdep.c to aarch64-tdep.c, making MTE available > on baremetal targets. Since the aarch64-linux-tdep.c layer inherits > these hooks from aarch64-tdep.c, there is no effective change for > aarch64-linux targets. > > Helpers used both by aarch64-tdep.c and by aarch64-linux-tdep.c were > moved from arch/aarch64-mte-linux.{c,h} to new arch/aarch64-mte.{c,h} > files. > > Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> > Tested-By: Luis Machado <luis.machado@arm.com> > Approved-By: Luis Machado <luis.machado@arm.com> > --- > gdb/Makefile.in | 3 + > gdb/NEWS | 3 + > gdb/aarch64-linux-tdep.c | 168 +---------------------------- > gdb/aarch64-tdep.c | 168 +++++++++++++++++++++++++++++ > gdb/aarch64-tdep.h | 2 + > gdb/arch/aarch64-mte-linux.c | 56 ---------- > gdb/arch/aarch64-mte-linux.h | 27 ----- > gdb/arch/aarch64-mte.c | 77 +++++++++++++ > gdb/arch/aarch64-mte.h | 51 +++++++++ > gdb/configure.tgt | 3 +- > gdb/nat/aarch64-mte-linux-ptrace.c | 1 + > gdbserver/configure.srv | 1 + > 12 files changed, 309 insertions(+), 251 deletions(-) > create mode 100644 gdb/arch/aarch64-mte.c > create mode 100644 gdb/arch/aarch64-mte.h > > diff --git a/gdb/Makefile.in b/gdb/Makefile.in > index 1c697bf0ab1..6744b8218ec 100644 > --- a/gdb/Makefile.in > +++ b/gdb/Makefile.in > @@ -746,6 +746,7 @@ ALL_64_TARGET_OBS = \ > amd64-windows-tdep.o \ > arch/aarch64.o \ > arch/aarch64-insn.o \ > + arch/aarch64-mte.o \ > arch/aarch64-mte-linux.o \ > arch/aarch64-scalable-linux.o \ > arch/amd64-linux-tdesc.o \ > @@ -1551,6 +1552,7 @@ HFILES_NO_SRCDIR = \ > arch/aarch32.h \ > arch/aarch64.h \ > arch/aarch64-insn.h \ > + arch/aarch64-mte.h \ > arch/aarch64-mte-linux.h \ > arch/aarch64-scalable-linux.h \ > arch/amd64-linux-tdesc.h \ > @@ -1664,6 +1666,7 @@ ALLDEPFILES = \ > arch/aarch32.c \ > arch/aarch64.c \ > arch/aarch64-insn.c \ > + arch/aarch64-mte.c \ > arch/aarch64-mte-linux.c \ > arch/aarch64-scalable-linux.c \ > arch/amd64.c \ > diff --git a/gdb/NEWS b/gdb/NEWS > index b56ba9b36ce..7ce62a6911d 100644 > --- a/gdb/NEWS > +++ b/gdb/NEWS > @@ -13,6 +13,9 @@ > This may cause breakage when using an incompatible libc, like uclibc or > newlib, or an older glibc. > > +* MTE (Memory Tagging Extension) debugging is now supported on Aarch64 baremetal > + targets. > + > *** Changes in GDB 15 > > * The MPX commands "show/set mpx bound" have been deprecated, as Intel > diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c > index a1296a8f0c7..ada9614673e 100644 > --- a/gdb/aarch64-linux-tdep.c > +++ b/gdb/aarch64-linux-tdep.c > @@ -48,6 +48,7 @@ > #include "record-full.h" > #include "linux-record.h" > > +#include "arch/aarch64-mte.h" > #include "arch/aarch64-mte-linux.h" > #include "arch/aarch64-scalable-linux.h" > > @@ -2427,29 +2428,6 @@ aarch64_linux_gcc_target_options (struct gdbarch *gdbarch) > return {}; > } > > -/* Helper to get the allocation tag from a 64-bit ADDRESS. > - > - Return the allocation tag if successful and nullopt otherwise. */ > - > -static std::optional<CORE_ADDR> > -aarch64_mte_get_atag (CORE_ADDR address) > -{ > - gdb::byte_vector tags; > - > - /* Attempt to fetch the allocation tag. */ > - if (!target_fetch_memtags (address, 1, tags, > - static_cast<int> (memtag_type::allocation))) > - return {}; > - > - /* Only one tag should've been returned. Make sure we got exactly that. */ > - if (tags.size () != 1) > - error (_("Target returned an unexpected number of tags.")); > - > - /* Although our tags are 4 bits in size, they are stored in a > - byte. */ > - return tags[0]; > -} > - > /* Implement the tagged_address_p gdbarch method. */ > > static bool > @@ -2466,132 +2444,6 @@ aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, CORE_ADDR address) > return true; > } > > -/* Implement the memtag_matches_p gdbarch method. */ > - > -static bool > -aarch64_linux_memtag_matches_p (struct gdbarch *gdbarch, > - struct value *address) > -{ > - gdb_assert (address != nullptr); > - > - CORE_ADDR addr = value_as_address (address); > - > - /* Fetch the allocation tag for ADDRESS. */ > - std::optional<CORE_ADDR> atag > - = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr)); > - > - if (!atag.has_value ()) > - return true; > - > - /* Fetch the logical tag for ADDRESS. */ > - gdb_byte ltag = aarch64_mte_get_ltag (addr); > - > - /* Are the tags the same? */ > - return ltag == *atag; > -} > - > -/* Implement the set_memtags gdbarch method. */ > - > -static bool > -aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address, > - size_t length, const gdb::byte_vector &tags, > - memtag_type tag_type) > -{ > - gdb_assert (!tags.empty ()); > - gdb_assert (address != nullptr); > - > - CORE_ADDR addr = value_as_address (address); > - > - /* Set the logical tag or the allocation tag. */ > - if (tag_type == memtag_type::logical) > - { > - /* When setting logical tags, we don't care about the length, since > - we are only setting a single logical tag. */ > - addr = aarch64_mte_set_ltag (addr, tags[0]); > - > - /* Update the value's content with the tag. */ > - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); > - gdb_byte *srcbuf = address->contents_raw ().data (); > - store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr); > - } > - else > - { > - /* Remove the top byte. */ > - addr = gdbarch_remove_non_address_bits (gdbarch, addr); > - > - /* With G being the number of tag granules and N the number of tags > - passed in, we can have the following cases: > - > - 1 - G == N: Store all the N tags to memory. > - > - 2 - G < N : Warn about having more tags than granules, but write G > - tags. > - > - 3 - G > N : This is a "fill tags" operation. We should use the tags > - as a pattern to fill the granules repeatedly until we have > - written G tags to memory. > - */ > - > - size_t g = aarch64_mte_get_tag_granules (addr, length, > - AARCH64_MTE_GRANULE_SIZE); > - size_t n = tags.size (); > - > - if (g < n) > - warning (_("Got more tags than memory granules. Tags will be " > - "truncated.")); > - else if (g > n) > - warning (_("Using tag pattern to fill memory range.")); > - > - if (!target_store_memtags (addr, length, tags, > - static_cast<int> (memtag_type::allocation))) > - return false; > - } > - return true; > -} > - > -/* Implement the get_memtag gdbarch method. */ > - > -static struct value * > -aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address, > - memtag_type tag_type) > -{ > - gdb_assert (address != nullptr); > - > - CORE_ADDR addr = value_as_address (address); > - CORE_ADDR tag = 0; > - > - /* Get the logical tag or the allocation tag. */ > - if (tag_type == memtag_type::logical) > - tag = aarch64_mte_get_ltag (addr); > - else > - { > - /* Remove the top byte. */ > - addr = gdbarch_remove_non_address_bits (gdbarch, addr); > - std::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr); > - > - if (!atag.has_value ()) > - return nullptr; > - > - tag = *atag; > - } > - > - /* Convert the tag to a value. */ > - return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int, > - tag); > -} > - > -/* Implement the memtag_to_string gdbarch method. */ > - > -static std::string > -aarch64_linux_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value) > -{ > - if (tag_value == nullptr) > - return ""; > - > - CORE_ADDR tag = value_as_address (tag_value); > - > - return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); > -} > > /* AArch64 Linux implementation of the report_signal_info gdbarch > hook. Displays information about possible memory tag violations. */ > @@ -2900,24 +2752,6 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) > /* Register a hook for checking if an address is tagged or not. */ > set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p); > > - /* Register a hook for checking if there is a memory tag match. */ > - set_gdbarch_memtag_matches_p (gdbarch, > - aarch64_linux_memtag_matches_p); > - > - /* Register a hook for setting the logical/allocation tags for > - a range of addresses. */ > - set_gdbarch_set_memtags (gdbarch, aarch64_linux_set_memtags); > - > - /* Register a hook for extracting the logical/allocation tag from an > - address. */ > - set_gdbarch_get_memtag (gdbarch, aarch64_linux_get_memtag); > - > - /* Set the allocation tag granule size to 16 bytes. */ > - set_gdbarch_memtag_granule_size (gdbarch, AARCH64_MTE_GRANULE_SIZE); > - > - /* Register a hook for converting a memory tag to a string. */ > - set_gdbarch_memtag_to_string (gdbarch, aarch64_linux_memtag_to_string); > - > set_gdbarch_report_signal_info (gdbarch, > aarch64_linux_report_signal_info); > > diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c > index e4bca6c6632..865b1c0b13b 100644 > --- a/gdb/aarch64-tdep.c > +++ b/gdb/aarch64-tdep.c > @@ -45,6 +45,7 @@ > > #include "aarch64-tdep.h" > #include "aarch64-ravenscar-thread.h" > +#include "arch/aarch64-mte.h" > > #include "record.h" > #include "record-full.h" > @@ -4088,6 +4089,156 @@ aarch64_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) > return streq (inst.opcode->name, "ret"); > } > > +/* Helper to get the allocation tag from a 64-bit ADDRESS. > + > + Return the allocation tag if successful and nullopt otherwise. */ > + > +std::optional<CORE_ADDR> > +aarch64_mte_get_atag (CORE_ADDR address) > +{ > + gdb::byte_vector tags; > + > + /* Attempt to fetch the allocation tag. */ > + if (!target_fetch_memtags (address, 1, tags, > + static_cast<int> (memtag_type::allocation))) > + return {}; > + > + /* Only one tag should've been returned. Make sure we got exactly that. */ > + if (tags.size () != 1) > + error (_("Target returned an unexpected number of tags.")); > + > + /* Although our tags are 4 bits in size, they are stored in a > + byte. */ > + return tags[0]; > +} > + > +/* Implement the memtag_matches_p gdbarch method. */ > + > +static bool > +aarch64_memtag_matches_p (struct gdbarch *gdbarch, > + struct value *address) > +{ > + gdb_assert (address != nullptr); > + > + CORE_ADDR addr = value_as_address (address); > + > + /* Fetch the allocation tag for ADDRESS. */ > + std::optional<CORE_ADDR> atag > + = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr)); > + > + if (!atag.has_value ()) > + return true; > + > + /* Fetch the logical tag for ADDRESS. */ > + gdb_byte ltag = aarch64_mte_get_ltag (addr); > + > + /* Are the tags the same? */ > + return ltag == *atag; > +} > + > +/* Implement the set_memtags gdbarch method. */ > + > +static bool > +aarch64_set_memtags (struct gdbarch *gdbarch, struct value *address, > + size_t length, const gdb::byte_vector &tags, > + memtag_type tag_type) > +{ > + gdb_assert (!tags.empty ()); > + gdb_assert (address != nullptr); > + > + CORE_ADDR addr = value_as_address (address); > + > + /* Set the logical tag or the allocation tag. */ > + if (tag_type == memtag_type::logical) > + { > + /* When setting logical tags, we don't care about the length, since > + we are only setting a single logical tag. */ > + addr = aarch64_mte_set_ltag (addr, tags[0]); > + > + /* Update the value's content with the tag. */ > + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); > + gdb_byte *srcbuf = address->contents_raw ().data (); > + store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr); > + } > + else > + { > + /* Remove the top byte. */ > + addr = gdbarch_remove_non_address_bits (gdbarch, addr); > + > + /* With G being the number of tag granules and N the number of tags > + passed in, we can have the following cases: > + > + 1 - G == N: Store all the N tags to memory. > + > + 2 - G < N : Warn about having more tags than granules, but write G > + tags. > + > + 3 - G > N : This is a "fill tags" operation. We should use the tags > + as a pattern to fill the granules repeatedly until we have > + written G tags to memory. > + */ > + > + size_t g = aarch64_mte_get_tag_granules (addr, length, > + AARCH64_MTE_GRANULE_SIZE); > + size_t n = tags.size (); > + > + if (g < n) > + warning (_("Got more tags than memory granules. Tags will be " > + "truncated.")); > + else if (g > n) > + warning (_("Using tag pattern to fill memory range.")); > + > + if (!target_store_memtags (addr, length, tags, > + static_cast<int> (memtag_type::allocation))) > + return false; > + } > + return true; > +} > + > +/* Implement the get_memtag gdbarch method. */ > + > +static struct value * > +aarch64_get_memtag (struct gdbarch *gdbarch, struct value *address, > + memtag_type tag_type) > +{ > + gdb_assert (address != nullptr); > + > + CORE_ADDR addr = value_as_address (address); > + CORE_ADDR tag = 0; > + > + /* Get the logical tag or the allocation tag. */ > + if (tag_type == memtag_type::logical) > + tag = aarch64_mte_get_ltag (addr); > + else > + { > + /* Remove the top byte. */ > + addr = gdbarch_remove_non_address_bits (gdbarch, addr); > + std::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr); > + > + if (!atag.has_value ()) > + return nullptr; > + > + tag = *atag; > + } > + > + /* Convert the tag to a value. */ > + return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int, > + tag); > +} > + > +/* Implement the memtag_to_string gdbarch method. */ > + > +static std::string > +aarch64_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value) > +{ > + if (tag_value == nullptr) > + return ""; > + > + CORE_ADDR tag = value_as_address (tag_value); > + > + return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); > +} > + > /* AArch64 implementation of the remove_non_address_bits gdbarch hook. Remove > non address bits from a pointer value. */ > > @@ -4504,6 +4655,23 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) > aarch64_pseudo_register_reggroup_p); > set_gdbarch_cannot_store_register (gdbarch, aarch64_cannot_store_register); > > + /* Set the allocation tag granule size to 16 bytes. */ > + set_gdbarch_memtag_granule_size (gdbarch, AARCH64_MTE_GRANULE_SIZE); > + > + /* Register a hook for checking if there is a memory tag match. */ > + set_gdbarch_memtag_matches_p (gdbarch, aarch64_memtag_matches_p); > + > + /* Register a hook for setting the logical/allocation tags for > + a range of addresses. */ > + set_gdbarch_set_memtags (gdbarch, aarch64_set_memtags); > + > + /* Register a hook for extracting the logical/allocation tag from an > + address. */ > + set_gdbarch_get_memtag (gdbarch, aarch64_get_memtag); > + > + /* Register a hook for converting a memory tag to a string. */ > + set_gdbarch_memtag_to_string (gdbarch, aarch64_memtag_to_string); > + > /* ABI */ > set_gdbarch_short_bit (gdbarch, 16); > set_gdbarch_int_bit (gdbarch, 32); > diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h > index 0e6024bfcbc..50166fb4f24 100644 > --- a/gdb/aarch64-tdep.h > +++ b/gdb/aarch64-tdep.h > @@ -203,4 +203,6 @@ void aarch64_displaced_step_fixup (struct gdbarch *gdbarch, > > bool aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch); > > +std::optional<CORE_ADDR> aarch64_mte_get_atag (CORE_ADDR address); > + > #endif /* aarch64-tdep.h */ > diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-mte-linux.c > index e0b441e3859..49426d7751c 100644 > --- a/gdb/arch/aarch64-mte-linux.c > +++ b/gdb/arch/aarch64-mte-linux.c > @@ -75,59 +75,3 @@ aarch64_mte_unpack_tags (gdb::byte_vector &tags, bool skip_first) > tags = std::move (unpacked_tags); > } > > -/* See arch/aarch64-mte-linux.h */ > - > -size_t > -aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size) > -{ > - /* An empty range has 0 tag granules. */ > - if (len == 0) > - return 0; > - > - /* Start address */ > - CORE_ADDR s_addr = align_down (addr, granule_size); > - /* End address */ > - CORE_ADDR e_addr = align_down (addr + len - 1, granule_size); > - > - /* We always have at least 1 granule because len is non-zero at this > - point. */ > - return 1 + (e_addr - s_addr) / granule_size; > -} > - > -/* See arch/aarch64-mte-linux.h */ > - > -CORE_ADDR > -aarch64_mte_make_ltag_bits (CORE_ADDR value) > -{ > - return value & AARCH64_MTE_LOGICAL_MAX_VALUE; > -} > - > -/* See arch/aarch64-mte-linux.h */ > - > -CORE_ADDR > -aarch64_mte_make_ltag (CORE_ADDR value) > -{ > - return (aarch64_mte_make_ltag_bits (value) > - << AARCH64_MTE_LOGICAL_TAG_START_BIT); > -} > - > -/* See arch/aarch64-mte-linux.h */ > - > -CORE_ADDR > -aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag) > -{ > - /* Remove the existing tag. */ > - address &= ~aarch64_mte_make_ltag (AARCH64_MTE_LOGICAL_MAX_VALUE); > - > - /* Return the new tagged address. */ > - return address | aarch64_mte_make_ltag (tag); > -} > - > -/* See arch/aarch64-mte-linux.h */ > - > -CORE_ADDR > -aarch64_mte_get_ltag (CORE_ADDR address) > -{ > - CORE_ADDR ltag_addr = address >> AARCH64_MTE_LOGICAL_TAG_START_BIT; > - return aarch64_mte_make_ltag_bits (ltag_addr); > -} > diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h > index 460b10e05c9..686749f547a 100644 > --- a/gdb/arch/aarch64-mte-linux.h > +++ b/gdb/arch/aarch64-mte-linux.h > @@ -29,12 +29,6 @@ > /* The MTE regset consists of a single 64-bit register. */ > #define AARCH64_LINUX_SIZEOF_MTE 8 > > -/* We have one tag per 16 bytes of memory. */ > -#define AARCH64_MTE_GRANULE_SIZE 16 > -#define AARCH64_MTE_TAG_BIT_SIZE 4 > -#define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 > -#define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf > - > /* Memory tagging definitions. */ > #ifndef SEGV_MTEAERR > # define SEGV_MTEAERR 8 > @@ -50,27 +44,6 @@ enum class aarch64_memtag_type > mte_allocation > }; > > -/* Return the number of tag granules in the memory range > - [ADDR, ADDR + LEN) given GRANULE_SIZE. */ > -extern size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, > - size_t granule_size); > - > -/* Return the 4-bit tag made from VALUE. */ > -extern CORE_ADDR aarch64_mte_make_ltag_bits (CORE_ADDR value); > - > -/* Return the 4-bit tag that can be OR-ed to an address. */ > -extern CORE_ADDR aarch64_mte_make_ltag (CORE_ADDR value); > - > -/* Helper to set the logical TAG for a 64-bit ADDRESS. > - > - It is always possible to set the logical tag. */ > -extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); > - > -/* Helper to get the logical tag from a 64-bit ADDRESS. > - > - It is always possible to get the logical tag. */ > -extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); > - > /* Given a TAGS vector containing 1 MTE tag per byte, pack the data as > 2 tags per byte and resize the vector. */ > extern void aarch64_mte_pack_tags (gdb::byte_vector &tags); > diff --git a/gdb/arch/aarch64-mte.c b/gdb/arch/aarch64-mte.c > new file mode 100644 > index 00000000000..908e12cdf3b > --- /dev/null > +++ b/gdb/arch/aarch64-mte.c > @@ -0,0 +1,77 @@ > +/* Common AArch64 functionality for MTE > + > + Copyright (C) 2021-2024 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + 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 "arch/aarch64-mte.h" > + > +/* See arch/aarch64-mte.h */ > + > +size_t > +aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size) > +{ > + /* An empty range has 0 tag granules. */ > + if (len == 0) > + return 0; > + > + /* Start address */ > + CORE_ADDR s_addr = align_down (addr, granule_size); > + /* End address */ > + CORE_ADDR e_addr = align_down (addr + len - 1, granule_size); > + > + /* We always have at least 1 granule because len is non-zero at this > + point. */ > + return 1 + (e_addr - s_addr) / granule_size; > +} > + > +/* See arch/aarch64-mte.h */ > + > +CORE_ADDR > +aarch64_mte_make_ltag_bits (CORE_ADDR value) > +{ > + return value & AARCH64_MTE_LOGICAL_MAX_VALUE; > +} > + > +/* See arch/aarch64-mte.h */ > + > +CORE_ADDR > +aarch64_mte_make_ltag (CORE_ADDR value) > +{ > + return (aarch64_mte_make_ltag_bits (value) > + << AARCH64_MTE_LOGICAL_TAG_START_BIT); > +} > + > +/* See arch/aarch64-mte.h */ > + > +CORE_ADDR > +aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag) > +{ > + /* Remove the existing tag. */ > + address &= ~aarch64_mte_make_ltag (AARCH64_MTE_LOGICAL_MAX_VALUE); > + > + /* Return the new tagged address. */ > + return address | aarch64_mte_make_ltag (tag); > +} > + > +/* See arch/aarch64-mte.h */ > + > +CORE_ADDR > +aarch64_mte_get_ltag (CORE_ADDR address) > +{ > + CORE_ADDR ltag_addr = address >> AARCH64_MTE_LOGICAL_TAG_START_BIT; > + return aarch64_mte_make_ltag_bits (ltag_addr); > +} > diff --git a/gdb/arch/aarch64-mte.h b/gdb/arch/aarch64-mte.h > new file mode 100644 > index 00000000000..7d8445ee89f > --- /dev/null > +++ b/gdb/arch/aarch64-mte.h > @@ -0,0 +1,51 @@ > +/* Common AArch64 definitions for MTE > + > + Copyright (C) 2021-2024 Free Software Foundation, Inc. > + > + This file is part of GDB. > + > + 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/>. */ > + > +#ifndef ARCH_AARCH64_MTE_H > +#define ARCH_AARCH64_MTE_H > + > + > +/* We have one tag per 16 bytes of memory. */ > +#define AARCH64_MTE_GRANULE_SIZE 16 > +#define AARCH64_MTE_TAG_BIT_SIZE 4 > +#define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 > +#define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf > + > +/* Return the number of tag granules in the memory range > + [ADDR, ADDR + LEN) given GRANULE_SIZE. */ > +extern size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, > + size_t granule_size); > + > +/* Return the 4-bit tag made from VALUE. */ > +extern CORE_ADDR aarch64_mte_make_ltag_bits (CORE_ADDR value); > + > +/* Return the 4-bit tag that can be OR-ed to an address. */ > +extern CORE_ADDR aarch64_mte_make_ltag (CORE_ADDR value); > + > +/* Helper to set the logical TAG for a 64-bit ADDRESS. > + > + It is always possible to set the logical tag. */ > +extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); > + > +/* Helper to get the logical tag from a 64-bit ADDRESS. > + > + It is always possible to get the logical tag. */ > +extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); > + > +#endif /* ARCH_AARCH64_MTE_H */ > diff --git a/gdb/configure.tgt b/gdb/configure.tgt > index 8326c458eb1..5fb14b65426 100644 > --- a/gdb/configure.tgt > +++ b/gdb/configure.tgt > @@ -59,7 +59,8 @@ amd64_tobjs="ravenscar-thread.o amd64-ravenscar-thread.o \ > case "${targ}" in > aarch64*-*-*) > cpu_obs="aarch32-tdep.o aarch64-tdep.o arch/aarch32.o \ > - arch/aarch64-insn.o arch/aarch64.o ravenscar-thread.o \ > + arch/aarch64-insn.o arch/aarch64.o arch/aarch64-mte.o \ > + ravenscar-thread.o \ > aarch64-ravenscar-thread.o";; > > alpha*-*-*) > diff --git a/gdb/nat/aarch64-mte-linux-ptrace.c b/gdb/nat/aarch64-mte-linux-ptrace.c > index 9e2a2ce1ab5..c2e70293a69 100644 > --- a/gdb/nat/aarch64-mte-linux-ptrace.c > +++ b/gdb/nat/aarch64-mte-linux-ptrace.c > @@ -22,6 +22,7 @@ > #include "linux-ptrace.h" > > #include "arch/aarch64.h" > +#include "arch/aarch64-mte.h" > #include "arch/aarch64-mte-linux.h" > #include "nat/aarch64-linux.h" > #include "nat/aarch64-mte-linux-ptrace.h" > diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv > index e24e40e85c1..95359137f4f 100644 > --- a/gdbserver/configure.srv > +++ b/gdbserver/configure.srv > @@ -48,6 +48,7 @@ case "${gdbserver_host}" in > srv_tgtobj="$srv_tgtobj nat/aarch64-linux.o" > srv_tgtobj="$srv_tgtobj arch/aarch64-insn.o" > srv_tgtobj="$srv_tgtobj arch/aarch64.o" > + srv_tgtobj="$srv_tgtobj arch/aarch64-mte.o" > srv_tgtobj="$srv_tgtobj arch/aarch64-mte-linux.o" > srv_tgtobj="$srv_tgtobj arch/aarch64-scalable-linux.o" > srv_tgtobj="$srv_tgtobj linux-aarch64-tdesc.o" Thanks. v4 is OK with the NEWS entry. Approved-By: Luis Machado <luis.machado@arm.com>
On 7/31/24 07:50, Luis Machado wrote: > On 7/30/24 15:39, Gustavo Romero wrote: >> This commit moves aarch64_linux_memtag_matches_p, >> aarch64_linux_set_memtags, aarch64_linux_get_memtag, and >> aarch64_linux_memtag_to_string hooks (plus aarch64_mte_get_atag >> function used by them), along with the setting of the memtag granule >> size, from aarch64-linux-tdep.c to aarch64-tdep.c, making MTE available >> on baremetal targets. Since the aarch64-linux-tdep.c layer inherits >> these hooks from aarch64-tdep.c, there is no effective change for >> aarch64-linux targets. >> >> Helpers used both by aarch64-tdep.c and by aarch64-linux-tdep.c were >> moved from arch/aarch64-mte-linux.{c,h} to new arch/aarch64-mte.{c,h} >> files. >> >> Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> >> Tested-By: Luis Machado <luis.machado@arm.com> >> Approved-By: Luis Machado <luis.machado@arm.com> >> --- >> gdb/Makefile.in | 3 + >> gdb/NEWS | 3 + >> gdb/aarch64-linux-tdep.c | 168 +---------------------------- >> gdb/aarch64-tdep.c | 168 +++++++++++++++++++++++++++++ >> gdb/aarch64-tdep.h | 2 + >> gdb/arch/aarch64-mte-linux.c | 56 ---------- >> gdb/arch/aarch64-mte-linux.h | 27 ----- >> gdb/arch/aarch64-mte.c | 77 +++++++++++++ >> gdb/arch/aarch64-mte.h | 51 +++++++++ >> gdb/configure.tgt | 3 +- >> gdb/nat/aarch64-mte-linux-ptrace.c | 1 + >> gdbserver/configure.srv | 1 + >> 12 files changed, 309 insertions(+), 251 deletions(-) >> create mode 100644 gdb/arch/aarch64-mte.c >> create mode 100644 gdb/arch/aarch64-mte.h >> >> diff --git a/gdb/Makefile.in b/gdb/Makefile.in >> index 1c697bf0ab1..6744b8218ec 100644 >> --- a/gdb/Makefile.in >> +++ b/gdb/Makefile.in >> @@ -746,6 +746,7 @@ ALL_64_TARGET_OBS = \ >> amd64-windows-tdep.o \ >> arch/aarch64.o \ >> arch/aarch64-insn.o \ >> + arch/aarch64-mte.o \ >> arch/aarch64-mte-linux.o \ >> arch/aarch64-scalable-linux.o \ >> arch/amd64-linux-tdesc.o \ >> @@ -1551,6 +1552,7 @@ HFILES_NO_SRCDIR = \ >> arch/aarch32.h \ >> arch/aarch64.h \ >> arch/aarch64-insn.h \ >> + arch/aarch64-mte.h \ >> arch/aarch64-mte-linux.h \ >> arch/aarch64-scalable-linux.h \ >> arch/amd64-linux-tdesc.h \ >> @@ -1664,6 +1666,7 @@ ALLDEPFILES = \ >> arch/aarch32.c \ >> arch/aarch64.c \ >> arch/aarch64-insn.c \ >> + arch/aarch64-mte.c \ >> arch/aarch64-mte-linux.c \ >> arch/aarch64-scalable-linux.c \ >> arch/amd64.c \ >> diff --git a/gdb/NEWS b/gdb/NEWS >> index b56ba9b36ce..7ce62a6911d 100644 >> --- a/gdb/NEWS >> +++ b/gdb/NEWS >> @@ -13,6 +13,9 @@ >> This may cause breakage when using an incompatible libc, like uclibc or >> newlib, or an older glibc. >> >> +* MTE (Memory Tagging Extension) debugging is now supported on Aarch64 baremetal >> + targets. >> + >> *** Changes in GDB 15 >> >> * The MPX commands "show/set mpx bound" have been deprecated, as Intel >> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c >> index a1296a8f0c7..ada9614673e 100644 >> --- a/gdb/aarch64-linux-tdep.c >> +++ b/gdb/aarch64-linux-tdep.c >> @@ -48,6 +48,7 @@ >> #include "record-full.h" >> #include "linux-record.h" >> >> +#include "arch/aarch64-mte.h" >> #include "arch/aarch64-mte-linux.h" >> #include "arch/aarch64-scalable-linux.h" >> >> @@ -2427,29 +2428,6 @@ aarch64_linux_gcc_target_options (struct gdbarch *gdbarch) >> return {}; >> } >> >> -/* Helper to get the allocation tag from a 64-bit ADDRESS. >> - >> - Return the allocation tag if successful and nullopt otherwise. */ >> - >> -static std::optional<CORE_ADDR> >> -aarch64_mte_get_atag (CORE_ADDR address) >> -{ >> - gdb::byte_vector tags; >> - >> - /* Attempt to fetch the allocation tag. */ >> - if (!target_fetch_memtags (address, 1, tags, >> - static_cast<int> (memtag_type::allocation))) >> - return {}; >> - >> - /* Only one tag should've been returned. Make sure we got exactly that. */ >> - if (tags.size () != 1) >> - error (_("Target returned an unexpected number of tags.")); >> - >> - /* Although our tags are 4 bits in size, they are stored in a >> - byte. */ >> - return tags[0]; >> -} >> - >> /* Implement the tagged_address_p gdbarch method. */ >> >> static bool >> @@ -2466,132 +2444,6 @@ aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, CORE_ADDR address) >> return true; >> } >> >> -/* Implement the memtag_matches_p gdbarch method. */ >> - >> -static bool >> -aarch64_linux_memtag_matches_p (struct gdbarch *gdbarch, >> - struct value *address) >> -{ >> - gdb_assert (address != nullptr); >> - >> - CORE_ADDR addr = value_as_address (address); >> - >> - /* Fetch the allocation tag for ADDRESS. */ >> - std::optional<CORE_ADDR> atag >> - = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr)); >> - >> - if (!atag.has_value ()) >> - return true; >> - >> - /* Fetch the logical tag for ADDRESS. */ >> - gdb_byte ltag = aarch64_mte_get_ltag (addr); >> - >> - /* Are the tags the same? */ >> - return ltag == *atag; >> -} >> - >> -/* Implement the set_memtags gdbarch method. */ >> - >> -static bool >> -aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address, >> - size_t length, const gdb::byte_vector &tags, >> - memtag_type tag_type) >> -{ >> - gdb_assert (!tags.empty ()); >> - gdb_assert (address != nullptr); >> - >> - CORE_ADDR addr = value_as_address (address); >> - >> - /* Set the logical tag or the allocation tag. */ >> - if (tag_type == memtag_type::logical) >> - { >> - /* When setting logical tags, we don't care about the length, since >> - we are only setting a single logical tag. */ >> - addr = aarch64_mte_set_ltag (addr, tags[0]); >> - >> - /* Update the value's content with the tag. */ >> - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); >> - gdb_byte *srcbuf = address->contents_raw ().data (); >> - store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr); >> - } >> - else >> - { >> - /* Remove the top byte. */ >> - addr = gdbarch_remove_non_address_bits (gdbarch, addr); >> - >> - /* With G being the number of tag granules and N the number of tags >> - passed in, we can have the following cases: >> - >> - 1 - G == N: Store all the N tags to memory. >> - >> - 2 - G < N : Warn about having more tags than granules, but write G >> - tags. >> - >> - 3 - G > N : This is a "fill tags" operation. We should use the tags >> - as a pattern to fill the granules repeatedly until we have >> - written G tags to memory. >> - */ >> - >> - size_t g = aarch64_mte_get_tag_granules (addr, length, >> - AARCH64_MTE_GRANULE_SIZE); >> - size_t n = tags.size (); >> - >> - if (g < n) >> - warning (_("Got more tags than memory granules. Tags will be " >> - "truncated.")); >> - else if (g > n) >> - warning (_("Using tag pattern to fill memory range.")); >> - >> - if (!target_store_memtags (addr, length, tags, >> - static_cast<int> (memtag_type::allocation))) >> - return false; >> - } >> - return true; >> -} >> - >> -/* Implement the get_memtag gdbarch method. */ >> - >> -static struct value * >> -aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address, >> - memtag_type tag_type) >> -{ >> - gdb_assert (address != nullptr); >> - >> - CORE_ADDR addr = value_as_address (address); >> - CORE_ADDR tag = 0; >> - >> - /* Get the logical tag or the allocation tag. */ >> - if (tag_type == memtag_type::logical) >> - tag = aarch64_mte_get_ltag (addr); >> - else >> - { >> - /* Remove the top byte. */ >> - addr = gdbarch_remove_non_address_bits (gdbarch, addr); >> - std::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr); >> - >> - if (!atag.has_value ()) >> - return nullptr; >> - >> - tag = *atag; >> - } >> - >> - /* Convert the tag to a value. */ >> - return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int, >> - tag); >> -} >> - >> -/* Implement the memtag_to_string gdbarch method. */ >> - >> -static std::string >> -aarch64_linux_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value) >> -{ >> - if (tag_value == nullptr) >> - return ""; >> - >> - CORE_ADDR tag = value_as_address (tag_value); >> - >> - return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); >> -} >> >> /* AArch64 Linux implementation of the report_signal_info gdbarch >> hook. Displays information about possible memory tag violations. */ >> @@ -2900,24 +2752,6 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) >> /* Register a hook for checking if an address is tagged or not. */ >> set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p); >> >> - /* Register a hook for checking if there is a memory tag match. */ >> - set_gdbarch_memtag_matches_p (gdbarch, >> - aarch64_linux_memtag_matches_p); >> - >> - /* Register a hook for setting the logical/allocation tags for >> - a range of addresses. */ >> - set_gdbarch_set_memtags (gdbarch, aarch64_linux_set_memtags); >> - >> - /* Register a hook for extracting the logical/allocation tag from an >> - address. */ >> - set_gdbarch_get_memtag (gdbarch, aarch64_linux_get_memtag); >> - >> - /* Set the allocation tag granule size to 16 bytes. */ >> - set_gdbarch_memtag_granule_size (gdbarch, AARCH64_MTE_GRANULE_SIZE); >> - >> - /* Register a hook for converting a memory tag to a string. */ >> - set_gdbarch_memtag_to_string (gdbarch, aarch64_linux_memtag_to_string); >> - >> set_gdbarch_report_signal_info (gdbarch, >> aarch64_linux_report_signal_info); >> >> diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c >> index e4bca6c6632..865b1c0b13b 100644 >> --- a/gdb/aarch64-tdep.c >> +++ b/gdb/aarch64-tdep.c >> @@ -45,6 +45,7 @@ >> >> #include "aarch64-tdep.h" >> #include "aarch64-ravenscar-thread.h" >> +#include "arch/aarch64-mte.h" >> >> #include "record.h" >> #include "record-full.h" >> @@ -4088,6 +4089,156 @@ aarch64_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) >> return streq (inst.opcode->name, "ret"); >> } >> >> +/* Helper to get the allocation tag from a 64-bit ADDRESS. >> + >> + Return the allocation tag if successful and nullopt otherwise. */ >> + >> +std::optional<CORE_ADDR> >> +aarch64_mte_get_atag (CORE_ADDR address) >> +{ >> + gdb::byte_vector tags; >> + >> + /* Attempt to fetch the allocation tag. */ >> + if (!target_fetch_memtags (address, 1, tags, >> + static_cast<int> (memtag_type::allocation))) >> + return {}; >> + >> + /* Only one tag should've been returned. Make sure we got exactly that. */ >> + if (tags.size () != 1) >> + error (_("Target returned an unexpected number of tags.")); >> + >> + /* Although our tags are 4 bits in size, they are stored in a >> + byte. */ >> + return tags[0]; >> +} >> + >> +/* Implement the memtag_matches_p gdbarch method. */ >> + >> +static bool >> +aarch64_memtag_matches_p (struct gdbarch *gdbarch, >> + struct value *address) >> +{ >> + gdb_assert (address != nullptr); >> + >> + CORE_ADDR addr = value_as_address (address); >> + >> + /* Fetch the allocation tag for ADDRESS. */ >> + std::optional<CORE_ADDR> atag >> + = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr)); >> + >> + if (!atag.has_value ()) >> + return true; >> + >> + /* Fetch the logical tag for ADDRESS. */ >> + gdb_byte ltag = aarch64_mte_get_ltag (addr); >> + >> + /* Are the tags the same? */ >> + return ltag == *atag; >> +} >> + >> +/* Implement the set_memtags gdbarch method. */ >> + >> +static bool >> +aarch64_set_memtags (struct gdbarch *gdbarch, struct value *address, >> + size_t length, const gdb::byte_vector &tags, >> + memtag_type tag_type) >> +{ >> + gdb_assert (!tags.empty ()); >> + gdb_assert (address != nullptr); >> + >> + CORE_ADDR addr = value_as_address (address); >> + >> + /* Set the logical tag or the allocation tag. */ >> + if (tag_type == memtag_type::logical) >> + { >> + /* When setting logical tags, we don't care about the length, since >> + we are only setting a single logical tag. */ >> + addr = aarch64_mte_set_ltag (addr, tags[0]); >> + >> + /* Update the value's content with the tag. */ >> + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); >> + gdb_byte *srcbuf = address->contents_raw ().data (); >> + store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr); >> + } >> + else >> + { >> + /* Remove the top byte. */ >> + addr = gdbarch_remove_non_address_bits (gdbarch, addr); >> + >> + /* With G being the number of tag granules and N the number of tags >> + passed in, we can have the following cases: >> + >> + 1 - G == N: Store all the N tags to memory. >> + >> + 2 - G < N : Warn about having more tags than granules, but write G >> + tags. >> + >> + 3 - G > N : This is a "fill tags" operation. We should use the tags >> + as a pattern to fill the granules repeatedly until we have >> + written G tags to memory. >> + */ >> + >> + size_t g = aarch64_mte_get_tag_granules (addr, length, >> + AARCH64_MTE_GRANULE_SIZE); >> + size_t n = tags.size (); >> + >> + if (g < n) >> + warning (_("Got more tags than memory granules. Tags will be " >> + "truncated.")); >> + else if (g > n) >> + warning (_("Using tag pattern to fill memory range.")); >> + >> + if (!target_store_memtags (addr, length, tags, >> + static_cast<int> (memtag_type::allocation))) >> + return false; >> + } >> + return true; >> +} >> + >> +/* Implement the get_memtag gdbarch method. */ >> + >> +static struct value * >> +aarch64_get_memtag (struct gdbarch *gdbarch, struct value *address, >> + memtag_type tag_type) >> +{ >> + gdb_assert (address != nullptr); >> + >> + CORE_ADDR addr = value_as_address (address); >> + CORE_ADDR tag = 0; >> + >> + /* Get the logical tag or the allocation tag. */ >> + if (tag_type == memtag_type::logical) >> + tag = aarch64_mte_get_ltag (addr); >> + else >> + { >> + /* Remove the top byte. */ >> + addr = gdbarch_remove_non_address_bits (gdbarch, addr); >> + std::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr); >> + >> + if (!atag.has_value ()) >> + return nullptr; >> + >> + tag = *atag; >> + } >> + >> + /* Convert the tag to a value. */ >> + return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int, >> + tag); >> +} >> + >> +/* Implement the memtag_to_string gdbarch method. */ >> + >> +static std::string >> +aarch64_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value) >> +{ >> + if (tag_value == nullptr) >> + return ""; >> + >> + CORE_ADDR tag = value_as_address (tag_value); >> + >> + return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); >> +} >> + >> /* AArch64 implementation of the remove_non_address_bits gdbarch hook. Remove >> non address bits from a pointer value. */ >> >> @@ -4504,6 +4655,23 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) >> aarch64_pseudo_register_reggroup_p); >> set_gdbarch_cannot_store_register (gdbarch, aarch64_cannot_store_register); >> >> + /* Set the allocation tag granule size to 16 bytes. */ >> + set_gdbarch_memtag_granule_size (gdbarch, AARCH64_MTE_GRANULE_SIZE); >> + >> + /* Register a hook for checking if there is a memory tag match. */ >> + set_gdbarch_memtag_matches_p (gdbarch, aarch64_memtag_matches_p); >> + >> + /* Register a hook for setting the logical/allocation tags for >> + a range of addresses. */ >> + set_gdbarch_set_memtags (gdbarch, aarch64_set_memtags); >> + >> + /* Register a hook for extracting the logical/allocation tag from an >> + address. */ >> + set_gdbarch_get_memtag (gdbarch, aarch64_get_memtag); >> + >> + /* Register a hook for converting a memory tag to a string. */ >> + set_gdbarch_memtag_to_string (gdbarch, aarch64_memtag_to_string); >> + >> /* ABI */ >> set_gdbarch_short_bit (gdbarch, 16); >> set_gdbarch_int_bit (gdbarch, 32); >> diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h >> index 0e6024bfcbc..50166fb4f24 100644 >> --- a/gdb/aarch64-tdep.h >> +++ b/gdb/aarch64-tdep.h >> @@ -203,4 +203,6 @@ void aarch64_displaced_step_fixup (struct gdbarch *gdbarch, >> >> bool aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch); >> >> +std::optional<CORE_ADDR> aarch64_mte_get_atag (CORE_ADDR address); >> + >> #endif /* aarch64-tdep.h */ >> diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-mte-linux.c >> index e0b441e3859..49426d7751c 100644 >> --- a/gdb/arch/aarch64-mte-linux.c >> +++ b/gdb/arch/aarch64-mte-linux.c >> @@ -75,59 +75,3 @@ aarch64_mte_unpack_tags (gdb::byte_vector &tags, bool skip_first) >> tags = std::move (unpacked_tags); >> } >> >> -/* See arch/aarch64-mte-linux.h */ >> - >> -size_t >> -aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size) >> -{ >> - /* An empty range has 0 tag granules. */ >> - if (len == 0) >> - return 0; >> - >> - /* Start address */ >> - CORE_ADDR s_addr = align_down (addr, granule_size); >> - /* End address */ >> - CORE_ADDR e_addr = align_down (addr + len - 1, granule_size); >> - >> - /* We always have at least 1 granule because len is non-zero at this >> - point. */ >> - return 1 + (e_addr - s_addr) / granule_size; >> -} >> - >> -/* See arch/aarch64-mte-linux.h */ >> - >> -CORE_ADDR >> -aarch64_mte_make_ltag_bits (CORE_ADDR value) >> -{ >> - return value & AARCH64_MTE_LOGICAL_MAX_VALUE; >> -} >> - >> -/* See arch/aarch64-mte-linux.h */ >> - >> -CORE_ADDR >> -aarch64_mte_make_ltag (CORE_ADDR value) >> -{ >> - return (aarch64_mte_make_ltag_bits (value) >> - << AARCH64_MTE_LOGICAL_TAG_START_BIT); >> -} >> - >> -/* See arch/aarch64-mte-linux.h */ >> - >> -CORE_ADDR >> -aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag) >> -{ >> - /* Remove the existing tag. */ >> - address &= ~aarch64_mte_make_ltag (AARCH64_MTE_LOGICAL_MAX_VALUE); >> - >> - /* Return the new tagged address. */ >> - return address | aarch64_mte_make_ltag (tag); >> -} >> - >> -/* See arch/aarch64-mte-linux.h */ >> - >> -CORE_ADDR >> -aarch64_mte_get_ltag (CORE_ADDR address) >> -{ >> - CORE_ADDR ltag_addr = address >> AARCH64_MTE_LOGICAL_TAG_START_BIT; >> - return aarch64_mte_make_ltag_bits (ltag_addr); >> -} >> diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h >> index 460b10e05c9..686749f547a 100644 >> --- a/gdb/arch/aarch64-mte-linux.h >> +++ b/gdb/arch/aarch64-mte-linux.h >> @@ -29,12 +29,6 @@ >> /* The MTE regset consists of a single 64-bit register. */ >> #define AARCH64_LINUX_SIZEOF_MTE 8 >> >> -/* We have one tag per 16 bytes of memory. */ >> -#define AARCH64_MTE_GRANULE_SIZE 16 >> -#define AARCH64_MTE_TAG_BIT_SIZE 4 >> -#define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 >> -#define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf >> - >> /* Memory tagging definitions. */ >> #ifndef SEGV_MTEAERR >> # define SEGV_MTEAERR 8 >> @@ -50,27 +44,6 @@ enum class aarch64_memtag_type >> mte_allocation >> }; >> >> -/* Return the number of tag granules in the memory range >> - [ADDR, ADDR + LEN) given GRANULE_SIZE. */ >> -extern size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, >> - size_t granule_size); >> - >> -/* Return the 4-bit tag made from VALUE. */ >> -extern CORE_ADDR aarch64_mte_make_ltag_bits (CORE_ADDR value); >> - >> -/* Return the 4-bit tag that can be OR-ed to an address. */ >> -extern CORE_ADDR aarch64_mte_make_ltag (CORE_ADDR value); >> - >> -/* Helper to set the logical TAG for a 64-bit ADDRESS. >> - >> - It is always possible to set the logical tag. */ >> -extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); >> - >> -/* Helper to get the logical tag from a 64-bit ADDRESS. >> - >> - It is always possible to get the logical tag. */ >> -extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); >> - >> /* Given a TAGS vector containing 1 MTE tag per byte, pack the data as >> 2 tags per byte and resize the vector. */ >> extern void aarch64_mte_pack_tags (gdb::byte_vector &tags); >> diff --git a/gdb/arch/aarch64-mte.c b/gdb/arch/aarch64-mte.c >> new file mode 100644 >> index 00000000000..908e12cdf3b >> --- /dev/null >> +++ b/gdb/arch/aarch64-mte.c >> @@ -0,0 +1,77 @@ >> +/* Common AArch64 functionality for MTE >> + >> + Copyright (C) 2021-2024 Free Software Foundation, Inc. >> + >> + This file is part of GDB. >> + >> + 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 "arch/aarch64-mte.h" >> + >> +/* See arch/aarch64-mte.h */ >> + >> +size_t >> +aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size) >> +{ >> + /* An empty range has 0 tag granules. */ >> + if (len == 0) >> + return 0; >> + >> + /* Start address */ >> + CORE_ADDR s_addr = align_down (addr, granule_size); >> + /* End address */ >> + CORE_ADDR e_addr = align_down (addr + len - 1, granule_size); >> + >> + /* We always have at least 1 granule because len is non-zero at this >> + point. */ >> + return 1 + (e_addr - s_addr) / granule_size; >> +} >> + >> +/* See arch/aarch64-mte.h */ >> + >> +CORE_ADDR >> +aarch64_mte_make_ltag_bits (CORE_ADDR value) >> +{ >> + return value & AARCH64_MTE_LOGICAL_MAX_VALUE; >> +} >> + >> +/* See arch/aarch64-mte.h */ >> + >> +CORE_ADDR >> +aarch64_mte_make_ltag (CORE_ADDR value) >> +{ >> + return (aarch64_mte_make_ltag_bits (value) >> + << AARCH64_MTE_LOGICAL_TAG_START_BIT); >> +} >> + >> +/* See arch/aarch64-mte.h */ >> + >> +CORE_ADDR >> +aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag) >> +{ >> + /* Remove the existing tag. */ >> + address &= ~aarch64_mte_make_ltag (AARCH64_MTE_LOGICAL_MAX_VALUE); >> + >> + /* Return the new tagged address. */ >> + return address | aarch64_mte_make_ltag (tag); >> +} >> + >> +/* See arch/aarch64-mte.h */ >> + >> +CORE_ADDR >> +aarch64_mte_get_ltag (CORE_ADDR address) >> +{ >> + CORE_ADDR ltag_addr = address >> AARCH64_MTE_LOGICAL_TAG_START_BIT; >> + return aarch64_mte_make_ltag_bits (ltag_addr); >> +} >> diff --git a/gdb/arch/aarch64-mte.h b/gdb/arch/aarch64-mte.h >> new file mode 100644 >> index 00000000000..7d8445ee89f >> --- /dev/null >> +++ b/gdb/arch/aarch64-mte.h >> @@ -0,0 +1,51 @@ >> +/* Common AArch64 definitions for MTE >> + >> + Copyright (C) 2021-2024 Free Software Foundation, Inc. >> + >> + This file is part of GDB. >> + >> + 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/>. */ >> + >> +#ifndef ARCH_AARCH64_MTE_H >> +#define ARCH_AARCH64_MTE_H >> + >> + >> +/* We have one tag per 16 bytes of memory. */ >> +#define AARCH64_MTE_GRANULE_SIZE 16 >> +#define AARCH64_MTE_TAG_BIT_SIZE 4 >> +#define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 >> +#define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf >> + >> +/* Return the number of tag granules in the memory range >> + [ADDR, ADDR + LEN) given GRANULE_SIZE. */ >> +extern size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, >> + size_t granule_size); >> + >> +/* Return the 4-bit tag made from VALUE. */ >> +extern CORE_ADDR aarch64_mte_make_ltag_bits (CORE_ADDR value); >> + >> +/* Return the 4-bit tag that can be OR-ed to an address. */ >> +extern CORE_ADDR aarch64_mte_make_ltag (CORE_ADDR value); >> + >> +/* Helper to set the logical TAG for a 64-bit ADDRESS. >> + >> + It is always possible to set the logical tag. */ >> +extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); >> + >> +/* Helper to get the logical tag from a 64-bit ADDRESS. >> + >> + It is always possible to get the logical tag. */ >> +extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); >> + >> +#endif /* ARCH_AARCH64_MTE_H */ >> diff --git a/gdb/configure.tgt b/gdb/configure.tgt >> index 8326c458eb1..5fb14b65426 100644 >> --- a/gdb/configure.tgt >> +++ b/gdb/configure.tgt >> @@ -59,7 +59,8 @@ amd64_tobjs="ravenscar-thread.o amd64-ravenscar-thread.o \ >> case "${targ}" in >> aarch64*-*-*) >> cpu_obs="aarch32-tdep.o aarch64-tdep.o arch/aarch32.o \ >> - arch/aarch64-insn.o arch/aarch64.o ravenscar-thread.o \ >> + arch/aarch64-insn.o arch/aarch64.o arch/aarch64-mte.o \ >> + ravenscar-thread.o \ >> aarch64-ravenscar-thread.o";; >> >> alpha*-*-*) >> diff --git a/gdb/nat/aarch64-mte-linux-ptrace.c b/gdb/nat/aarch64-mte-linux-ptrace.c >> index 9e2a2ce1ab5..c2e70293a69 100644 >> --- a/gdb/nat/aarch64-mte-linux-ptrace.c >> +++ b/gdb/nat/aarch64-mte-linux-ptrace.c >> @@ -22,6 +22,7 @@ >> #include "linux-ptrace.h" >> >> #include "arch/aarch64.h" >> +#include "arch/aarch64-mte.h" >> #include "arch/aarch64-mte-linux.h" >> #include "nat/aarch64-linux.h" >> #include "nat/aarch64-mte-linux-ptrace.h" >> diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv >> index e24e40e85c1..95359137f4f 100644 >> --- a/gdbserver/configure.srv >> +++ b/gdbserver/configure.srv >> @@ -48,6 +48,7 @@ case "${gdbserver_host}" in >> srv_tgtobj="$srv_tgtobj nat/aarch64-linux.o" >> srv_tgtobj="$srv_tgtobj arch/aarch64-insn.o" >> srv_tgtobj="$srv_tgtobj arch/aarch64.o" >> + srv_tgtobj="$srv_tgtobj arch/aarch64-mte.o" >> srv_tgtobj="$srv_tgtobj arch/aarch64-mte-linux.o" >> srv_tgtobj="$srv_tgtobj arch/aarch64-scalable-linux.o" >> srv_tgtobj="$srv_tgtobj linux-aarch64-tdesc.o" > > > Thanks. v4 is OK with the NEWS entry. > > Approved-By: Luis Machado <luis.machado@arm.com> Pushed. Please add your name to the Write After Approval section of gdb/MAINTAINERS when convenient.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 1c697bf0ab1..6744b8218ec 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -746,6 +746,7 @@ ALL_64_TARGET_OBS = \ amd64-windows-tdep.o \ arch/aarch64.o \ arch/aarch64-insn.o \ + arch/aarch64-mte.o \ arch/aarch64-mte-linux.o \ arch/aarch64-scalable-linux.o \ arch/amd64-linux-tdesc.o \ @@ -1551,6 +1552,7 @@ HFILES_NO_SRCDIR = \ arch/aarch32.h \ arch/aarch64.h \ arch/aarch64-insn.h \ + arch/aarch64-mte.h \ arch/aarch64-mte-linux.h \ arch/aarch64-scalable-linux.h \ arch/amd64-linux-tdesc.h \ @@ -1664,6 +1666,7 @@ ALLDEPFILES = \ arch/aarch32.c \ arch/aarch64.c \ arch/aarch64-insn.c \ + arch/aarch64-mte.c \ arch/aarch64-mte-linux.c \ arch/aarch64-scalable-linux.c \ arch/amd64.c \ diff --git a/gdb/NEWS b/gdb/NEWS index b56ba9b36ce..7ce62a6911d 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -13,6 +13,9 @@ This may cause breakage when using an incompatible libc, like uclibc or newlib, or an older glibc. +* MTE (Memory Tagging Extension) debugging is now supported on Aarch64 baremetal + targets. + *** Changes in GDB 15 * The MPX commands "show/set mpx bound" have been deprecated, as Intel diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c index a1296a8f0c7..ada9614673e 100644 --- a/gdb/aarch64-linux-tdep.c +++ b/gdb/aarch64-linux-tdep.c @@ -48,6 +48,7 @@ #include "record-full.h" #include "linux-record.h" +#include "arch/aarch64-mte.h" #include "arch/aarch64-mte-linux.h" #include "arch/aarch64-scalable-linux.h" @@ -2427,29 +2428,6 @@ aarch64_linux_gcc_target_options (struct gdbarch *gdbarch) return {}; } -/* Helper to get the allocation tag from a 64-bit ADDRESS. - - Return the allocation tag if successful and nullopt otherwise. */ - -static std::optional<CORE_ADDR> -aarch64_mte_get_atag (CORE_ADDR address) -{ - gdb::byte_vector tags; - - /* Attempt to fetch the allocation tag. */ - if (!target_fetch_memtags (address, 1, tags, - static_cast<int> (memtag_type::allocation))) - return {}; - - /* Only one tag should've been returned. Make sure we got exactly that. */ - if (tags.size () != 1) - error (_("Target returned an unexpected number of tags.")); - - /* Although our tags are 4 bits in size, they are stored in a - byte. */ - return tags[0]; -} - /* Implement the tagged_address_p gdbarch method. */ static bool @@ -2466,132 +2444,6 @@ aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, CORE_ADDR address) return true; } -/* Implement the memtag_matches_p gdbarch method. */ - -static bool -aarch64_linux_memtag_matches_p (struct gdbarch *gdbarch, - struct value *address) -{ - gdb_assert (address != nullptr); - - CORE_ADDR addr = value_as_address (address); - - /* Fetch the allocation tag for ADDRESS. */ - std::optional<CORE_ADDR> atag - = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr)); - - if (!atag.has_value ()) - return true; - - /* Fetch the logical tag for ADDRESS. */ - gdb_byte ltag = aarch64_mte_get_ltag (addr); - - /* Are the tags the same? */ - return ltag == *atag; -} - -/* Implement the set_memtags gdbarch method. */ - -static bool -aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address, - size_t length, const gdb::byte_vector &tags, - memtag_type tag_type) -{ - gdb_assert (!tags.empty ()); - gdb_assert (address != nullptr); - - CORE_ADDR addr = value_as_address (address); - - /* Set the logical tag or the allocation tag. */ - if (tag_type == memtag_type::logical) - { - /* When setting logical tags, we don't care about the length, since - we are only setting a single logical tag. */ - addr = aarch64_mte_set_ltag (addr, tags[0]); - - /* Update the value's content with the tag. */ - enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); - gdb_byte *srcbuf = address->contents_raw ().data (); - store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr); - } - else - { - /* Remove the top byte. */ - addr = gdbarch_remove_non_address_bits (gdbarch, addr); - - /* With G being the number of tag granules and N the number of tags - passed in, we can have the following cases: - - 1 - G == N: Store all the N tags to memory. - - 2 - G < N : Warn about having more tags than granules, but write G - tags. - - 3 - G > N : This is a "fill tags" operation. We should use the tags - as a pattern to fill the granules repeatedly until we have - written G tags to memory. - */ - - size_t g = aarch64_mte_get_tag_granules (addr, length, - AARCH64_MTE_GRANULE_SIZE); - size_t n = tags.size (); - - if (g < n) - warning (_("Got more tags than memory granules. Tags will be " - "truncated.")); - else if (g > n) - warning (_("Using tag pattern to fill memory range.")); - - if (!target_store_memtags (addr, length, tags, - static_cast<int> (memtag_type::allocation))) - return false; - } - return true; -} - -/* Implement the get_memtag gdbarch method. */ - -static struct value * -aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address, - memtag_type tag_type) -{ - gdb_assert (address != nullptr); - - CORE_ADDR addr = value_as_address (address); - CORE_ADDR tag = 0; - - /* Get the logical tag or the allocation tag. */ - if (tag_type == memtag_type::logical) - tag = aarch64_mte_get_ltag (addr); - else - { - /* Remove the top byte. */ - addr = gdbarch_remove_non_address_bits (gdbarch, addr); - std::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr); - - if (!atag.has_value ()) - return nullptr; - - tag = *atag; - } - - /* Convert the tag to a value. */ - return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int, - tag); -} - -/* Implement the memtag_to_string gdbarch method. */ - -static std::string -aarch64_linux_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value) -{ - if (tag_value == nullptr) - return ""; - - CORE_ADDR tag = value_as_address (tag_value); - - return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); -} /* AArch64 Linux implementation of the report_signal_info gdbarch hook. Displays information about possible memory tag violations. */ @@ -2900,24 +2752,6 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Register a hook for checking if an address is tagged or not. */ set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p); - /* Register a hook for checking if there is a memory tag match. */ - set_gdbarch_memtag_matches_p (gdbarch, - aarch64_linux_memtag_matches_p); - - /* Register a hook for setting the logical/allocation tags for - a range of addresses. */ - set_gdbarch_set_memtags (gdbarch, aarch64_linux_set_memtags); - - /* Register a hook for extracting the logical/allocation tag from an - address. */ - set_gdbarch_get_memtag (gdbarch, aarch64_linux_get_memtag); - - /* Set the allocation tag granule size to 16 bytes. */ - set_gdbarch_memtag_granule_size (gdbarch, AARCH64_MTE_GRANULE_SIZE); - - /* Register a hook for converting a memory tag to a string. */ - set_gdbarch_memtag_to_string (gdbarch, aarch64_linux_memtag_to_string); - set_gdbarch_report_signal_info (gdbarch, aarch64_linux_report_signal_info); diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c index e4bca6c6632..865b1c0b13b 100644 --- a/gdb/aarch64-tdep.c +++ b/gdb/aarch64-tdep.c @@ -45,6 +45,7 @@ #include "aarch64-tdep.h" #include "aarch64-ravenscar-thread.h" +#include "arch/aarch64-mte.h" #include "record.h" #include "record-full.h" @@ -4088,6 +4089,156 @@ aarch64_stack_frame_destroyed_p (struct gdbarch *gdbarch, CORE_ADDR pc) return streq (inst.opcode->name, "ret"); } +/* Helper to get the allocation tag from a 64-bit ADDRESS. + + Return the allocation tag if successful and nullopt otherwise. */ + +std::optional<CORE_ADDR> +aarch64_mte_get_atag (CORE_ADDR address) +{ + gdb::byte_vector tags; + + /* Attempt to fetch the allocation tag. */ + if (!target_fetch_memtags (address, 1, tags, + static_cast<int> (memtag_type::allocation))) + return {}; + + /* Only one tag should've been returned. Make sure we got exactly that. */ + if (tags.size () != 1) + error (_("Target returned an unexpected number of tags.")); + + /* Although our tags are 4 bits in size, they are stored in a + byte. */ + return tags[0]; +} + +/* Implement the memtag_matches_p gdbarch method. */ + +static bool +aarch64_memtag_matches_p (struct gdbarch *gdbarch, + struct value *address) +{ + gdb_assert (address != nullptr); + + CORE_ADDR addr = value_as_address (address); + + /* Fetch the allocation tag for ADDRESS. */ + std::optional<CORE_ADDR> atag + = aarch64_mte_get_atag (gdbarch_remove_non_address_bits (gdbarch, addr)); + + if (!atag.has_value ()) + return true; + + /* Fetch the logical tag for ADDRESS. */ + gdb_byte ltag = aarch64_mte_get_ltag (addr); + + /* Are the tags the same? */ + return ltag == *atag; +} + +/* Implement the set_memtags gdbarch method. */ + +static bool +aarch64_set_memtags (struct gdbarch *gdbarch, struct value *address, + size_t length, const gdb::byte_vector &tags, + memtag_type tag_type) +{ + gdb_assert (!tags.empty ()); + gdb_assert (address != nullptr); + + CORE_ADDR addr = value_as_address (address); + + /* Set the logical tag or the allocation tag. */ + if (tag_type == memtag_type::logical) + { + /* When setting logical tags, we don't care about the length, since + we are only setting a single logical tag. */ + addr = aarch64_mte_set_ltag (addr, tags[0]); + + /* Update the value's content with the tag. */ + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + gdb_byte *srcbuf = address->contents_raw ().data (); + store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr); + } + else + { + /* Remove the top byte. */ + addr = gdbarch_remove_non_address_bits (gdbarch, addr); + + /* With G being the number of tag granules and N the number of tags + passed in, we can have the following cases: + + 1 - G == N: Store all the N tags to memory. + + 2 - G < N : Warn about having more tags than granules, but write G + tags. + + 3 - G > N : This is a "fill tags" operation. We should use the tags + as a pattern to fill the granules repeatedly until we have + written G tags to memory. + */ + + size_t g = aarch64_mte_get_tag_granules (addr, length, + AARCH64_MTE_GRANULE_SIZE); + size_t n = tags.size (); + + if (g < n) + warning (_("Got more tags than memory granules. Tags will be " + "truncated.")); + else if (g > n) + warning (_("Using tag pattern to fill memory range.")); + + if (!target_store_memtags (addr, length, tags, + static_cast<int> (memtag_type::allocation))) + return false; + } + return true; +} + +/* Implement the get_memtag gdbarch method. */ + +static struct value * +aarch64_get_memtag (struct gdbarch *gdbarch, struct value *address, + memtag_type tag_type) +{ + gdb_assert (address != nullptr); + + CORE_ADDR addr = value_as_address (address); + CORE_ADDR tag = 0; + + /* Get the logical tag or the allocation tag. */ + if (tag_type == memtag_type::logical) + tag = aarch64_mte_get_ltag (addr); + else + { + /* Remove the top byte. */ + addr = gdbarch_remove_non_address_bits (gdbarch, addr); + std::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr); + + if (!atag.has_value ()) + return nullptr; + + tag = *atag; + } + + /* Convert the tag to a value. */ + return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int, + tag); +} + +/* Implement the memtag_to_string gdbarch method. */ + +static std::string +aarch64_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value) +{ + if (tag_value == nullptr) + return ""; + + CORE_ADDR tag = value_as_address (tag_value); + + return string_printf ("0x%s", phex_nz (tag, sizeof (tag))); +} + /* AArch64 implementation of the remove_non_address_bits gdbarch hook. Remove non address bits from a pointer value. */ @@ -4504,6 +4655,23 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) aarch64_pseudo_register_reggroup_p); set_gdbarch_cannot_store_register (gdbarch, aarch64_cannot_store_register); + /* Set the allocation tag granule size to 16 bytes. */ + set_gdbarch_memtag_granule_size (gdbarch, AARCH64_MTE_GRANULE_SIZE); + + /* Register a hook for checking if there is a memory tag match. */ + set_gdbarch_memtag_matches_p (gdbarch, aarch64_memtag_matches_p); + + /* Register a hook for setting the logical/allocation tags for + a range of addresses. */ + set_gdbarch_set_memtags (gdbarch, aarch64_set_memtags); + + /* Register a hook for extracting the logical/allocation tag from an + address. */ + set_gdbarch_get_memtag (gdbarch, aarch64_get_memtag); + + /* Register a hook for converting a memory tag to a string. */ + set_gdbarch_memtag_to_string (gdbarch, aarch64_memtag_to_string); + /* ABI */ set_gdbarch_short_bit (gdbarch, 16); set_gdbarch_int_bit (gdbarch, 32); diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h index 0e6024bfcbc..50166fb4f24 100644 --- a/gdb/aarch64-tdep.h +++ b/gdb/aarch64-tdep.h @@ -203,4 +203,6 @@ void aarch64_displaced_step_fixup (struct gdbarch *gdbarch, bool aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch); +std::optional<CORE_ADDR> aarch64_mte_get_atag (CORE_ADDR address); + #endif /* aarch64-tdep.h */ diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-mte-linux.c index e0b441e3859..49426d7751c 100644 --- a/gdb/arch/aarch64-mte-linux.c +++ b/gdb/arch/aarch64-mte-linux.c @@ -75,59 +75,3 @@ aarch64_mte_unpack_tags (gdb::byte_vector &tags, bool skip_first) tags = std::move (unpacked_tags); } -/* See arch/aarch64-mte-linux.h */ - -size_t -aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size) -{ - /* An empty range has 0 tag granules. */ - if (len == 0) - return 0; - - /* Start address */ - CORE_ADDR s_addr = align_down (addr, granule_size); - /* End address */ - CORE_ADDR e_addr = align_down (addr + len - 1, granule_size); - - /* We always have at least 1 granule because len is non-zero at this - point. */ - return 1 + (e_addr - s_addr) / granule_size; -} - -/* See arch/aarch64-mte-linux.h */ - -CORE_ADDR -aarch64_mte_make_ltag_bits (CORE_ADDR value) -{ - return value & AARCH64_MTE_LOGICAL_MAX_VALUE; -} - -/* See arch/aarch64-mte-linux.h */ - -CORE_ADDR -aarch64_mte_make_ltag (CORE_ADDR value) -{ - return (aarch64_mte_make_ltag_bits (value) - << AARCH64_MTE_LOGICAL_TAG_START_BIT); -} - -/* See arch/aarch64-mte-linux.h */ - -CORE_ADDR -aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag) -{ - /* Remove the existing tag. */ - address &= ~aarch64_mte_make_ltag (AARCH64_MTE_LOGICAL_MAX_VALUE); - - /* Return the new tagged address. */ - return address | aarch64_mte_make_ltag (tag); -} - -/* See arch/aarch64-mte-linux.h */ - -CORE_ADDR -aarch64_mte_get_ltag (CORE_ADDR address) -{ - CORE_ADDR ltag_addr = address >> AARCH64_MTE_LOGICAL_TAG_START_BIT; - return aarch64_mte_make_ltag_bits (ltag_addr); -} diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h index 460b10e05c9..686749f547a 100644 --- a/gdb/arch/aarch64-mte-linux.h +++ b/gdb/arch/aarch64-mte-linux.h @@ -29,12 +29,6 @@ /* The MTE regset consists of a single 64-bit register. */ #define AARCH64_LINUX_SIZEOF_MTE 8 -/* We have one tag per 16 bytes of memory. */ -#define AARCH64_MTE_GRANULE_SIZE 16 -#define AARCH64_MTE_TAG_BIT_SIZE 4 -#define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 -#define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf - /* Memory tagging definitions. */ #ifndef SEGV_MTEAERR # define SEGV_MTEAERR 8 @@ -50,27 +44,6 @@ enum class aarch64_memtag_type mte_allocation }; -/* Return the number of tag granules in the memory range - [ADDR, ADDR + LEN) given GRANULE_SIZE. */ -extern size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, - size_t granule_size); - -/* Return the 4-bit tag made from VALUE. */ -extern CORE_ADDR aarch64_mte_make_ltag_bits (CORE_ADDR value); - -/* Return the 4-bit tag that can be OR-ed to an address. */ -extern CORE_ADDR aarch64_mte_make_ltag (CORE_ADDR value); - -/* Helper to set the logical TAG for a 64-bit ADDRESS. - - It is always possible to set the logical tag. */ -extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); - -/* Helper to get the logical tag from a 64-bit ADDRESS. - - It is always possible to get the logical tag. */ -extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); - /* Given a TAGS vector containing 1 MTE tag per byte, pack the data as 2 tags per byte and resize the vector. */ extern void aarch64_mte_pack_tags (gdb::byte_vector &tags); diff --git a/gdb/arch/aarch64-mte.c b/gdb/arch/aarch64-mte.c new file mode 100644 index 00000000000..908e12cdf3b --- /dev/null +++ b/gdb/arch/aarch64-mte.c @@ -0,0 +1,77 @@ +/* Common AArch64 functionality for MTE + + Copyright (C) 2021-2024 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 "arch/aarch64-mte.h" + +/* See arch/aarch64-mte.h */ + +size_t +aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size) +{ + /* An empty range has 0 tag granules. */ + if (len == 0) + return 0; + + /* Start address */ + CORE_ADDR s_addr = align_down (addr, granule_size); + /* End address */ + CORE_ADDR e_addr = align_down (addr + len - 1, granule_size); + + /* We always have at least 1 granule because len is non-zero at this + point. */ + return 1 + (e_addr - s_addr) / granule_size; +} + +/* See arch/aarch64-mte.h */ + +CORE_ADDR +aarch64_mte_make_ltag_bits (CORE_ADDR value) +{ + return value & AARCH64_MTE_LOGICAL_MAX_VALUE; +} + +/* See arch/aarch64-mte.h */ + +CORE_ADDR +aarch64_mte_make_ltag (CORE_ADDR value) +{ + return (aarch64_mte_make_ltag_bits (value) + << AARCH64_MTE_LOGICAL_TAG_START_BIT); +} + +/* See arch/aarch64-mte.h */ + +CORE_ADDR +aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag) +{ + /* Remove the existing tag. */ + address &= ~aarch64_mte_make_ltag (AARCH64_MTE_LOGICAL_MAX_VALUE); + + /* Return the new tagged address. */ + return address | aarch64_mte_make_ltag (tag); +} + +/* See arch/aarch64-mte.h */ + +CORE_ADDR +aarch64_mte_get_ltag (CORE_ADDR address) +{ + CORE_ADDR ltag_addr = address >> AARCH64_MTE_LOGICAL_TAG_START_BIT; + return aarch64_mte_make_ltag_bits (ltag_addr); +} diff --git a/gdb/arch/aarch64-mte.h b/gdb/arch/aarch64-mte.h new file mode 100644 index 00000000000..7d8445ee89f --- /dev/null +++ b/gdb/arch/aarch64-mte.h @@ -0,0 +1,51 @@ +/* Common AArch64 definitions for MTE + + Copyright (C) 2021-2024 Free Software Foundation, Inc. + + This file is part of GDB. + + 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/>. */ + +#ifndef ARCH_AARCH64_MTE_H +#define ARCH_AARCH64_MTE_H + + +/* We have one tag per 16 bytes of memory. */ +#define AARCH64_MTE_GRANULE_SIZE 16 +#define AARCH64_MTE_TAG_BIT_SIZE 4 +#define AARCH64_MTE_LOGICAL_TAG_START_BIT 56 +#define AARCH64_MTE_LOGICAL_MAX_VALUE 0xf + +/* Return the number of tag granules in the memory range + [ADDR, ADDR + LEN) given GRANULE_SIZE. */ +extern size_t aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len, + size_t granule_size); + +/* Return the 4-bit tag made from VALUE. */ +extern CORE_ADDR aarch64_mte_make_ltag_bits (CORE_ADDR value); + +/* Return the 4-bit tag that can be OR-ed to an address. */ +extern CORE_ADDR aarch64_mte_make_ltag (CORE_ADDR value); + +/* Helper to set the logical TAG for a 64-bit ADDRESS. + + It is always possible to set the logical tag. */ +extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag); + +/* Helper to get the logical tag from a 64-bit ADDRESS. + + It is always possible to get the logical tag. */ +extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address); + +#endif /* ARCH_AARCH64_MTE_H */ diff --git a/gdb/configure.tgt b/gdb/configure.tgt index 8326c458eb1..5fb14b65426 100644 --- a/gdb/configure.tgt +++ b/gdb/configure.tgt @@ -59,7 +59,8 @@ amd64_tobjs="ravenscar-thread.o amd64-ravenscar-thread.o \ case "${targ}" in aarch64*-*-*) cpu_obs="aarch32-tdep.o aarch64-tdep.o arch/aarch32.o \ - arch/aarch64-insn.o arch/aarch64.o ravenscar-thread.o \ + arch/aarch64-insn.o arch/aarch64.o arch/aarch64-mte.o \ + ravenscar-thread.o \ aarch64-ravenscar-thread.o";; alpha*-*-*) diff --git a/gdb/nat/aarch64-mte-linux-ptrace.c b/gdb/nat/aarch64-mte-linux-ptrace.c index 9e2a2ce1ab5..c2e70293a69 100644 --- a/gdb/nat/aarch64-mte-linux-ptrace.c +++ b/gdb/nat/aarch64-mte-linux-ptrace.c @@ -22,6 +22,7 @@ #include "linux-ptrace.h" #include "arch/aarch64.h" +#include "arch/aarch64-mte.h" #include "arch/aarch64-mte-linux.h" #include "nat/aarch64-linux.h" #include "nat/aarch64-mte-linux-ptrace.h" diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv index e24e40e85c1..95359137f4f 100644 --- a/gdbserver/configure.srv +++ b/gdbserver/configure.srv @@ -48,6 +48,7 @@ case "${gdbserver_host}" in srv_tgtobj="$srv_tgtobj nat/aarch64-linux.o" srv_tgtobj="$srv_tgtobj arch/aarch64-insn.o" srv_tgtobj="$srv_tgtobj arch/aarch64.o" + srv_tgtobj="$srv_tgtobj arch/aarch64-mte.o" srv_tgtobj="$srv_tgtobj arch/aarch64-mte-linux.o" srv_tgtobj="$srv_tgtobj arch/aarch64-scalable-linux.o" srv_tgtobj="$srv_tgtobj linux-aarch64-tdesc.o"