public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: David Spickett <david.spickett@linaro.org>
To: Luis Machado <luis.machado@linaro.org>
Cc: gdb-patches@sourceware.org, alan.hayward@arm.com,
	 John Baldwin <jhb@freebsd.org>,
	Catalin Marinas <catalin.marinas@arm.com>
Subject: Re: [PATCH] [AArch64] MTE corefile support
Date: Wed, 19 May 2021 11:01:16 +0100	[thread overview]
Message-ID: <CACBW-2L3RPAK+oE9Thjn4+ShebLSdBs5BpmOTWbGNV6V2HQ7fA@mail.gmail.com> (raw)
In-Reply-To: <20210518202047.3492211-1-luis.machado@linaro.org>

> +/* Maximum number of tags to request.  */
> +#define MAX_TAGS_TO_TRANSFER 1024

Is this 1024 derived from something? Random guess the max note size
minus the size of the note header.

> +/* AArch64 Linux implementation of the decode_memtag_note gdbarch
> +   hook.  Decode a memory tag note and return the request tags.  */
> +
> +static gdb::byte_vector
> +aarch64_linux_decode_memtag_note (struct gdbarch *gdbarch,
> +                                 gdb::array_view <const gdb_byte> note,
> +                                 CORE_ADDR address, size_t length)

Would be worth commenting that this assumes that address is within the
range of the note given. (I see later that it's only called if that's
true)

On Tue, 18 May 2021 at 21:20, Luis Machado <luis.machado@linaro.org> wrote:
>
> Teach GDB how to dump memory tags when using the gcore command and how
> to read them back from a core file generated via gcore or the kernel.
>
> Each tagged memory range (listed in /proc/<pid>/smaps) gets dumped to its
> own NT_MEMTAG note. A section named ".memtag" is created for each of those
> when reading the core file back.
>
> Dumping memory tags
> -
>
> When using the gcore command to dump a core file, GDB will go through the maps
> in /proc/<pid>/smaps looking for tagged ranges. Each of those entries gets
> passed to an arch-specific gdbarch hook that generates a vector of blobs of
> memory tag data that are blindly put into a NT_MEMTAG note.
>
> The vector is used because we may have, in the future,  multiple tag types for
> a particular memory range.
>
> Each of the NT_MEMTAG notes have a generic header and a arch-specific header,
> like so:
>
> struct tag_dump_header
> {
>   uint16_t format; // Only NT_MEMTAG_TYPE_AARCH_MTE at present
>   uint64_t start_vma;
>   uint64_t end_vma;
> };
>
> struct tag_dump_mte
> {
>   uint16_t granule_byte_size;
>   uint16_t tag_bit_size;
>   uint16_t __unused;
> };
>
> The only bits meant to be generic are the tag_dump_format, start_vma and
> end_vma fields.
>
> The format-specific data is supposed to be opaque and only useful for the
> arch-specific code.
>
> We can extend the format in the future to make room for other memory tag
> layouts.
>
> Reading memory tags
> -
>
> When reading a core file that contains NT_MEMTAG entries, GDB will use
> a different approach to check for tagged memory range. Rather than looking
> at /proc/<pid>/smaps, it will now look for ".memtag" sections with the right
> memory range.
>
> When reading tags, GDB will now use the core target's implementation of
> fetch_memtags (store_memtags doesn't exist for core targets). Then the data
> is fed into an arch-specific hook that will decode the memory tag format and
> return a vector of tags.
>
> I've added a test to exercise writing and reading of memory tags in core
> files.
>
> gdb/ChangeLog:
>
> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
>
>         * aarch64-linux-tdep.c: Include elf/common.h.
>         (MAX_TAGS_TO_TRANSFER): New constant.
>         (aarch64_linux_create_memtag_notes_from_range): New function.
>         (aarch64_linux_decode_memtag_note): Likewise.
>         (aarch64_linux_init_abi): Register new core file hooks.
>         * arch/aarch64-mte-linux.h (AARCH64_MTE_TAG_BIT_SIZE): New constant.
>         (struct tag_dump_header): New struct.
>         (struct tag_dump_mte): New struct.
>         (MEMTAG_NOTE_HEADER_SIZE): New constant.
>         * corelow.c (core_target) <supports_memory_tagging, fetch_memtags>: New
>         method overrides.
>         * gdbarch.c: Regenerate.
>         * gdbarch.h: Likewise.
>         * gdbarch.sh (create_memtag_notes_from_range): New hook.
>         (decode_memtag_note): Likewise.
>         * linux-tdep.c (linux_address_in_memtag_page): Renamed to...
>         (linux_process_address_in_memtag_page): ... this.
>         (linux_core_file_address_in_memtag_page): New function.
>         (linux_address_in_memtag_page): Likewise.
>         (linux_make_memtag_corefile_notes): Likewise.
>         (linux_make_corefile_notes): Handle memory tag notes.
>         * NEWS: Mention core file support for memory tagging.
>
> gdb/doc/ChangeLog:
>
> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
>
>         * gdb.texinfo (AArch64 Memory Tagging Extension): Mention support
>         for memory tagging in core files.
>
> gdb/testsuite/ChangeLog:
>
> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>
>
>         * gdb.arch/aarch64-mte-gcore.c: New file.
>         * gdb.arch/aarch64-mte-gcore.exp: New file.
> ---
>  gdb/NEWS                                     |   4 +
>  gdb/aarch64-linux-tdep.c                     | 179 +++++++++++++++++++
>  gdb/arch/aarch64-mte-linux.h                 |  29 +++
>  gdb/corelow.c                                |  88 +++++++++
>  gdb/doc/gdb.texinfo                          |   4 +
>  gdb/gdbarch.c                                |  64 +++++++
>  gdb/gdbarch.h                                |  16 ++
>  gdb/gdbarch.sh                               |   6 +
>  gdb/linux-tdep.c                             | 141 ++++++++++++++-
>  gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++++
>  gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 115 ++++++++++++
>  11 files changed, 736 insertions(+), 3 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c
>  create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
>
> diff --git a/gdb/NEWS b/gdb/NEWS
> index ab678acec8b..58b9f739d4f 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -3,6 +3,10 @@
>
>  *** Changes since GDB 10
>
> +* GDB now supports dumping memory tag data for AArch64 MTE.  It also supports
> +  reading memory tag data for AArch64 MTE from core files generated by
> +  the gcore command or the Linux kernel.
> +
>  * GDB now supports general memory tagging functionality if the underlying
>    architecture supports the proper primitives and hooks.  Currently this is
>    enabled only for AArch64 MTE.
> diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
> index e9761ed2189..663d0e1a215 100644
> --- a/gdb/aarch64-linux-tdep.c
> +++ b/gdb/aarch64-linux-tdep.c
> @@ -53,6 +53,8 @@
>
>  #include "gdbsupport/selftest.h"
>
> +#include "elf/common.h"
> +
>  /* Signal frame handling.
>
>        +------------+  ^
> @@ -1779,6 +1781,172 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
>      }
>  }
>
> +/* Maximum number of tags to request.  */
> +#define MAX_TAGS_TO_TRANSFER 1024
> +
> +/* AArch64 Linux implementation of the aarch64_create_memtag_notes_from_range
> +   gdbarch hook.  Create core file notes for memory tags.  */
> +
> +static std::vector<gdb::byte_vector>
> +aarch64_linux_create_memtag_notes_from_range (struct gdbarch *gdbarch,
> +                                             CORE_ADDR start_address,
> +                                             CORE_ADDR end_address)
> +{
> +  /* We only handle MTE tags for now.  */
> +
> +  /* Figure out how many tags we need to store in this memory range.  */
> +  int granules = aarch64_mte_get_tag_granules (start_address,
> +                                              end_address - start_address,
> +                                              AARCH64_MTE_GRANULE_SIZE);
> +
> +  /* Vector of memory tag notes. Add the MTE note (we only have MTE tags
> +     at the moment).  */
> +  std::vector<gdb::byte_vector> notes (1);
> +
> +  /* If there are no tag granules to fetch, just return.  */
> +  if (granules == 0)
> +    return notes;
> +
> +  /* Adjust the MTE note size to hold the header + tags.  */
> +  notes[0].resize (MEMTAG_NOTE_HEADER_SIZE + granules);
> +
> +  CORE_ADDR address = start_address;
> +  /* Vector of tags.  */
> +  gdb::byte_vector tags;
> +
> +  while (granules > 0)
> +    {
> +      /* Transfer tags in chunks.  */
> +      gdb::byte_vector tags_read;
> +      size_t xfer_len
> +       = (granules >= MAX_TAGS_TO_TRANSFER)?
> +         MAX_TAGS_TO_TRANSFER * AARCH64_MTE_GRANULE_SIZE :
> +         granules * AARCH64_MTE_GRANULE_SIZE;
> +
> +      if (!target_fetch_memtags (address, xfer_len, tags_read,
> +                                static_cast<int> (memtag_type::allocation)))
> +       {
> +         warning (_("Failed to read MTE tags from memory range [%s,%s]."),
> +                    phex_nz (start_address, sizeof (start_address)),
> +                    phex_nz (end_address, sizeof (end_address)));
> +         notes.resize (0);
> +         return notes;
> +       }
> +
> +      /* Transfer over the tags that have been read.  */
> +      tags.insert (tags.end (), tags_read.begin (), tags_read.end ());
> +
> +      /* Adjust the remaining granules and starting address.  */
> +      granules -= tags_read.size ();
> +      address += tags_read.size () * AARCH64_MTE_GRANULE_SIZE;
> +    }
> +
> +  /* Create the header.  */
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  gdb_byte *buf = notes[0].data ();
> +
> +  /* Generic header.  */
> +  /* Tag dump format */
> +  store_unsigned_integer (buf, sizeof (uint16_t), byte_order,
> +                         NT_MEMTAG_TYPE_AARCH_MTE);
> +  buf += sizeof (uint16_t);
> +
> +  /* Start address */
> +  store_unsigned_integer (buf, sizeof (uint64_t), byte_order, start_address);
> +  buf += sizeof (uint64_t);
> +
> +  /* End address */
> +  store_unsigned_integer (buf, sizeof (uint64_t), byte_order, end_address);
> +  buf += sizeof (uint64_t);
> +
> +  /* MTE-specific header.  */
> +  /* Granule byte size */
> +  store_unsigned_integer (buf, sizeof (uint16_t), byte_order,
> +                         AARCH64_MTE_GRANULE_SIZE);
> +  buf += sizeof (uint16_t);
> +
> +  /* Tag bit size */
> +  store_unsigned_integer (buf, sizeof (uint16_t), byte_order,
> +                         AARCH64_MTE_TAG_BIT_SIZE);
> +  buf += sizeof (uint16_t);
> +
> +  /* Unused value */
> +  store_unsigned_integer (buf, sizeof (uint16_t), byte_order, 0);
> +
> +  /* Store the tags.  */
> +  memcpy (notes[0].data () + MEMTAG_NOTE_HEADER_SIZE, tags.data (),
> +         tags.size ());
> +
> +  return notes;
> +}
> +
> +/* AArch64 Linux implementation of the decode_memtag_note gdbarch
> +   hook.  Decode a memory tag note and return the request tags.  */
> +
> +static gdb::byte_vector
> +aarch64_linux_decode_memtag_note (struct gdbarch *gdbarch,
> +                                 gdb::array_view <const gdb_byte> note,
> +                                 CORE_ADDR address, size_t length)
> +{
> +  gdb::byte_vector tags;
> +
> +  /* Read the generic header.  */
> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
> +  struct tag_dump_header header;
> +  const gdb_byte *buf = note.data ();
> +
> +  header.format
> +    = extract_unsigned_integer (buf, sizeof (uint16_t), byte_order);
> +  buf += sizeof (uint16_t);
> +
> +  header.start_vma
> +    = extract_unsigned_integer (buf, sizeof (uint64_t), byte_order);
> +  buf += sizeof (uint64_t);
> +
> +  header.end_vma
> +    = extract_unsigned_integer (buf, sizeof (uint64_t), byte_order);
> +  buf += sizeof (uint64_t);
> +
> +  /* Sanity check  */
> +  if (header.format != NT_MEMTAG_TYPE_AARCH_MTE)
> +    {
> +      warning (_("Unexpected memory tag note format (%x).\n"), header.format);
> +      return tags;
> +    }
> +
> +  /* Calculate how many granules we need to skip to get to the granule of
> +     ADDRESS.  Align both the start address and the requested address
> +     so it is easier to get the number of granules to skip.  This way we
> +     don't need to consider cases where ADDRESS falls in the middle of a
> +     tag granule range.  */
> +  CORE_ADDR aligned_start_address
> +    = align_down (header.start_vma, AARCH64_MTE_GRANULE_SIZE);
> +  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);
> +
> +  int skipped_granules
> +    = aarch64_mte_get_tag_granules (aligned_start_address,
> +                                   aligned_address - aligned_start_address,
> +                                   AARCH64_MTE_GRANULE_SIZE);
> +
> +  /* The amount of memory tag granules we need to fetch.  */
> +  int granules
> +    = aarch64_mte_get_tag_granules (address, length, AARCH64_MTE_GRANULE_SIZE);
> +
> +  /* If there are no tag granules to decode, just return.  */
> +  if (granules == 0)
> +    return tags;
> +
> +  /* Point to the block of data that contains the first granule we are
> +     interested in.  */
> +  const gdb_byte *tags_data = note.data () + sizeof (header) + skipped_granules;
> +
> +  /* Read the tag granules.  */
> +  for (unsigned int i = 0; i < granules; i++)
> +      tags.push_back (tags_data[i]);
> +
> +  return tags;
> +}
> +
>  static void
>  aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>  {
> @@ -1862,6 +2030,17 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
>
>        set_gdbarch_report_signal_info (gdbarch,
>                                       aarch64_linux_report_signal_info);
> +
> +      /* Core file helpers.  */
> +
> +      /* Core file helper to create memory tag notes for a particular range of
> +        addresses.  */
> +      set_gdbarch_create_memtag_notes_from_range (gdbarch,
> +                                 aarch64_linux_create_memtag_notes_from_range);
> +
> +      /* Core file helper to decode a memory tag note.  */
> +      set_gdbarch_decode_memtag_note (gdbarch,
> +                                     aarch64_linux_decode_memtag_note);
>      }
>
>    /* Initialize the aarch64_linux_record_tdep.  */
> diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h
> index 2aa97eb861a..a4a26628847 100644
> --- a/gdb/arch/aarch64-mte-linux.h
> +++ b/gdb/arch/aarch64-mte-linux.h
> @@ -32,6 +32,7 @@
>
>  /* 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
>
> @@ -71,4 +72,32 @@ extern CORE_ADDR aarch64_mte_set_ltag (CORE_ADDR address, CORE_ADDR tag);
>     It is always possible to get the logical tag.  */
>  extern CORE_ADDR aarch64_mte_get_ltag (CORE_ADDR address);
>
> +/* Headers for the NT_MEMTAG notes.  */
> +
> +/* Generic NT_MEMTAG header.  */
> +struct tag_dump_header
> +{
> +  /* Tag format.  */
> +  uint16_t format;
> +  /* Start address of the tagged range.  */
> +  uint64_t start_vma;
> +  /* End address of the tagged range.  */
> +  uint64_t end_vma;
> +};
> +
> +/* MTE-specific NT_MEMTAG header.  */
> +struct tag_dump_mte
> +{
> +  /* Size of the tag granule in bytes.  */
> +  uint16_t granule_byte_size;
> +  /* Size of the tag in bits.  */
> +  uint16_t tag_bit_size;
> +  /* Reserved field for the future.  */
> +  uint16_t __unused;
> +};
> +
> +/* Memory tag note header size.  Includes both the generic and the
> +   arch-specific parts.  */
> +#define MEMTAG_NOTE_HEADER_SIZE (2 + 8 + 8 + 2 + 2 + 2)
> +
>  #endif /* ARCH_AARCH64_LINUX_H */
> diff --git a/gdb/corelow.c b/gdb/corelow.c
> index 452b4dd4f9a..33d16b7220a 100644
> --- a/gdb/corelow.c
> +++ b/gdb/corelow.c
> @@ -100,6 +100,13 @@ class core_target final : public process_stratum_target
>
>    bool info_proc (const char *, enum info_proc_what) override;
>
> +  bool supports_memory_tagging () override;
> +
> +  /* Core file implementation of fetch_memtags.  Fetch the memory tags from
> +     core file notes.  */
> +  bool fetch_memtags (CORE_ADDR address, size_t len,
> +                     gdb::byte_vector &tags, int type) override;
> +
>    /* A few helpers.  */
>
>    /* Getter, see variable definition.  */
> @@ -1115,6 +1122,87 @@ core_target::info_proc (const char *args, enum info_proc_what request)
>    return true;
>  }
>
> +/* Implementation of the "supports_memory_tagging" target_ops method.  */
> +
> +bool
> +core_target::supports_memory_tagging ()
> +{
> +  /* Look for memory tag notes.  If they exist, that means this core file
> +     supports memory tagging.  */
> +  if (bfd_get_section_by_name (core_bfd, ".memtag") == nullptr)
> +    return false;
> +
> +  return true;
> +}
> +
> +/* Implementation of the "fetch_memtags" target_ops method.  */
> +
> +bool
> +core_target::fetch_memtags (CORE_ADDR address, size_t len,
> +                           gdb::byte_vector &tags, int type)
> +{
> +  struct gdbarch *gdbarch = target_gdbarch ();
> +
> +  /* Make sure we have a way to decode the memory tag notes.  */
> +  if (!gdbarch_decode_memtag_note_p (gdbarch))
> +    warning (_("gdbarch_decode_memtag_note not implemented for this "
> +              "architecture."));
> +
> +  asection *section
> +    = bfd_get_section_by_name (core_bfd, ".memtag");
> +
> +  /* Remove the top byte for the memory range check.  */
> +  address = address_significant (gdbarch, address);
> +
> +  /* Go through all the memtag sections and figure out if ADDRESS
> +     falls within one of the memory ranges that contain tags.  */
> +  while (section != nullptr)
> +    {
> +      size_t note_size = bfd_section_size (section);
> +
> +      /* If the note is smaller than the size of the header, this core note
> +        is malformed.  */
> +      if (note_size < 2 * sizeof (uint64_t) + sizeof (uint16_t))
> +       {
> +         warning (_("malformed core note - too short for header"));
> +         return false;
> +       }
> +
> +      gdb::byte_vector note (note_size);
> +
> +      /* Fetch the contents of this particular memtag note.  */
> +      if (!bfd_get_section_contents (core_bfd, section,
> +                                    note.data (), 0, note_size))
> +       {
> +         warning (_("could not get core note contents."));
> +         return false;
> +       }
> +
> +      /* Read the generic header of the note.  Thos contains the format,
> +        start address and end address.  */
> +      uint64_t start_address
> +       = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t));
> +      uint64_t end_address
> +       = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t)
> +                               + sizeof (uint64_t));
> +
> +      /* Is the address within [start_address, end_address)?  */
> +      if (address >= start_address
> +         && address < end_address)
> +       {
> +         /* Decode the memory tag note and return the tags.  */
> +         tags = gdbarch_decode_memtag_note (gdbarch, note, address, len);
> +         return true;
> +       }
> +
> +      /* The requested address lies outside this particular memtag note.  Keep
> +        looking and get the next section.  */
> +      section = bfd_get_next_section_by_name (core_bfd, section);
> +    }
> +
> +  return false;
> +}
> +
>  /* Get a pointer to the current core target.  If not connected to a
>     core target, return NULL.  */
>
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 56f37eb2288..738cb3b56eb 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -25257,6 +25257,10 @@ options that can be controlled at runtime and emulates the @code{prctl}
>  option @code{PR_SET_TAGGED_ADDR_CTRL}.  For further information, see the
>  documentation in the Linux kernel.
>
> +@value{GDBN} supports dumping memory tag data to core files through the
> +@command{gcore} command and reading memory tag data from core files generated
> +by the @command{gcore} command or the Linux kernel.
> +
>  @node i386
>  @subsection x86 Architecture-specific Issues
>
> diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
> index 208cf4b5aaa..de384da2e9a 100644
> --- a/gdb/gdbarch.c
> +++ b/gdb/gdbarch.c
> @@ -283,6 +283,8 @@ struct gdbarch
>    gdbarch_iterate_over_regset_sections_ftype *iterate_over_regset_sections;
>    gdbarch_make_corefile_notes_ftype *make_corefile_notes;
>    gdbarch_find_memory_regions_ftype *find_memory_regions;
> +  gdbarch_create_memtag_notes_from_range_ftype *create_memtag_notes_from_range;
> +  gdbarch_decode_memtag_note_ftype *decode_memtag_note;
>    gdbarch_core_xfer_shared_libraries_ftype *core_xfer_shared_libraries;
>    gdbarch_core_xfer_shared_libraries_aix_ftype *core_xfer_shared_libraries_aix;
>    gdbarch_core_pid_to_str_ftype *core_pid_to_str;
> @@ -667,6 +669,8 @@ verify_gdbarch (struct gdbarch *gdbarch)
>    /* Skip verify of iterate_over_regset_sections, has predicate.  */
>    /* Skip verify of make_corefile_notes, has predicate.  */
>    /* Skip verify of find_memory_regions, has predicate.  */
> +  /* Skip verify of create_memtag_notes_from_range, has predicate.  */
> +  /* Skip verify of decode_memtag_note, has predicate.  */
>    /* Skip verify of core_xfer_shared_libraries, has predicate.  */
>    /* Skip verify of core_xfer_shared_libraries_aix, has predicate.  */
>    /* Skip verify of core_pid_to_str, has predicate.  */
> @@ -925,6 +929,18 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
>    fprintf_unfiltered (file,
>                        "gdbarch_dump: core_xfer_siginfo = <%s>\n",
>                        host_address_to_string (gdbarch->core_xfer_siginfo));
> +  fprintf_unfiltered (file,
> +                      "gdbarch_dump: gdbarch_create_memtag_notes_from_range_p() = %d\n",
> +                      gdbarch_create_memtag_notes_from_range_p (gdbarch));
> +  fprintf_unfiltered (file,
> +                      "gdbarch_dump: create_memtag_notes_from_range = <%s>\n",
> +                      host_address_to_string (gdbarch->create_memtag_notes_from_range));
> +  fprintf_unfiltered (file,
> +                      "gdbarch_dump: gdbarch_decode_memtag_note_p() = %d\n",
> +                      gdbarch_decode_memtag_note_p (gdbarch));
> +  fprintf_unfiltered (file,
> +                      "gdbarch_dump: decode_memtag_note = <%s>\n",
> +                      host_address_to_string (gdbarch->decode_memtag_note));
>    fprintf_unfiltered (file,
>                        "gdbarch_dump: decr_pc_after_break = %s\n",
>                        core_addr_to_string_nz (gdbarch->decr_pc_after_break));
> @@ -3898,6 +3914,54 @@ set_gdbarch_find_memory_regions (struct gdbarch *gdbarch,
>    gdbarch->find_memory_regions = find_memory_regions;
>  }
>
> +bool
> +gdbarch_create_memtag_notes_from_range_p (struct gdbarch *gdbarch)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  return gdbarch->create_memtag_notes_from_range != NULL;
> +}
> +
> +std::vector<gdb::byte_vector>
> +gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch, CORE_ADDR start_address, CORE_ADDR end_address)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  gdb_assert (gdbarch->create_memtag_notes_from_range != NULL);
> +  if (gdbarch_debug >= 2)
> +    fprintf_unfiltered (gdb_stdlog, "gdbarch_create_memtag_notes_from_range called\n");
> +  return gdbarch->create_memtag_notes_from_range (gdbarch, start_address, end_address);
> +}
> +
> +void
> +set_gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch,
> +                                            gdbarch_create_memtag_notes_from_range_ftype create_memtag_notes_from_range)
> +{
> +  gdbarch->create_memtag_notes_from_range = create_memtag_notes_from_range;
> +}
> +
> +bool
> +gdbarch_decode_memtag_note_p (struct gdbarch *gdbarch)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  return gdbarch->decode_memtag_note != NULL;
> +}
> +
> +gdb::byte_vector
> +gdbarch_decode_memtag_note (struct gdbarch *gdbarch, gdb::array_view<const gdb_byte> note, CORE_ADDR address, size_t length)
> +{
> +  gdb_assert (gdbarch != NULL);
> +  gdb_assert (gdbarch->decode_memtag_note != NULL);
> +  if (gdbarch_debug >= 2)
> +    fprintf_unfiltered (gdb_stdlog, "gdbarch_decode_memtag_note called\n");
> +  return gdbarch->decode_memtag_note (gdbarch, note, address, length);
> +}
> +
> +void
> +set_gdbarch_decode_memtag_note (struct gdbarch *gdbarch,
> +                                gdbarch_decode_memtag_note_ftype decode_memtag_note)
> +{
> +  gdbarch->decode_memtag_note = decode_memtag_note;
> +}
> +
>  bool
>  gdbarch_core_xfer_shared_libraries_p (struct gdbarch *gdbarch)
>  {
> diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
> index 7157e5596fd..80e244624de 100644
> --- a/gdb/gdbarch.h
> +++ b/gdb/gdbarch.h
> @@ -980,6 +980,22 @@ typedef int (gdbarch_find_memory_regions_ftype) (struct gdbarch *gdbarch, find_m
>  extern int gdbarch_find_memory_regions (struct gdbarch *gdbarch, find_memory_region_ftype func, void *data);
>  extern void set_gdbarch_find_memory_regions (struct gdbarch *gdbarch, gdbarch_find_memory_regions_ftype *find_memory_regions);
>
> +/* Create memory tag core file notes given a range of addresses. */
> +
> +extern bool gdbarch_create_memtag_notes_from_range_p (struct gdbarch *gdbarch);
> +
> +typedef std::vector<gdb::byte_vector> (gdbarch_create_memtag_notes_from_range_ftype) (struct gdbarch *gdbarch, CORE_ADDR start_address, CORE_ADDR end_address);
> +extern std::vector<gdb::byte_vector> gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch, CORE_ADDR start_address, CORE_ADDR end_address);
> +extern void set_gdbarch_create_memtag_notes_from_range (struct gdbarch *gdbarch, gdbarch_create_memtag_notes_from_range_ftype *create_memtag_notes_from_range);
> +
> +/* Decode a memory tag note and return the tags that it contains. */
> +
> +extern bool gdbarch_decode_memtag_note_p (struct gdbarch *gdbarch);
> +
> +typedef gdb::byte_vector (gdbarch_decode_memtag_note_ftype) (struct gdbarch *gdbarch, gdb::array_view<const gdb_byte> note, CORE_ADDR address, size_t length);
> +extern gdb::byte_vector gdbarch_decode_memtag_note (struct gdbarch *gdbarch, gdb::array_view<const gdb_byte> note, CORE_ADDR address, size_t length);
> +extern void set_gdbarch_decode_memtag_note (struct gdbarch *gdbarch, gdbarch_decode_memtag_note_ftype *decode_memtag_note);
> +
>  /* Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from
>     core file into buffer READBUF with length LEN.  Return the number of bytes read
>     (zero indicates failure).
> diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
> index 43e51341f97..7c5eed0780c 100755
> --- a/gdb/gdbarch.sh
> +++ b/gdb/gdbarch.sh
> @@ -745,6 +745,12 @@ M;gdb::unique_xmalloc_ptr<char>;make_corefile_notes;bfd *obfd, int *note_size;ob
>  # Find core file memory regions
>  M;int;find_memory_regions;find_memory_region_ftype func, void *data;func, data
>
> +# Create memory tag core file notes given a range of addresses.
> +M;std::vector<gdb::byte_vector>;create_memtag_notes_from_range;CORE_ADDR start_address, CORE_ADDR end_address;start_address, end_address
> +
> +# Decode a memory tag note and return the tags that it contains.
> +M;gdb::byte_vector;decode_memtag_note;gdb::array_view<const gdb_byte> note, CORE_ADDR address, size_t length;note, address, length
> +
>  # Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from
>  # core file into buffer READBUF with length LEN.  Return the number of bytes read
>  # (zero indicates failure).
> diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
> index 927e69bf1e1..f4ff57dec87 100644
> --- a/gdb/linux-tdep.c
> +++ b/gdb/linux-tdep.c
> @@ -1438,10 +1438,11 @@ parse_smaps_data (const char *data,
>    return smaps;
>  }
>
> -/* See linux-tdep.h.  */
> +/* Helper that checks if an address is in a memory tag page for a live
> +   process.  */
>
> -bool
> -linux_address_in_memtag_page (CORE_ADDR address)
> +static bool
> +linux_process_address_in_memtag_page (CORE_ADDR address)
>  {
>    if (current_inferior ()->fake_pid_p)
>      return false;
> @@ -1473,6 +1474,137 @@ linux_address_in_memtag_page (CORE_ADDR address)
>    return false;
>  }
>
> +/* Helper that checks if an address is in a memory tag page for a core file
> +   process.  */
> +
> +static bool
> +linux_core_file_address_in_memtag_page (CORE_ADDR address)
> +{
> +  if (core_bfd == nullptr)
> +    return false;
> +
> +  asection *section
> +    = bfd_get_section_by_name (core_bfd, ".memtag");
> +
> +  /* Go through all the memtag sections and figure out if ADDRESS
> +     falls within one of the memory ranges that contain tags.  */
> +  while (section != nullptr)
> +    {
> +      size_t note_size = bfd_section_size (section);
> +
> +      /* If the note is smaller than the size of the header, this core note
> +        is malformed.  */
> +      if (note_size < 2 * sizeof (uint64_t) + sizeof (uint16_t))
> +       {
> +         warning (_("malformed core note - too short for header"));
> +         return false;
> +       }
> +
> +      gdb::byte_vector note (note_size);
> +
> +      /* Fetch the contents of this particular memtag note.  */
> +      if (!bfd_get_section_contents (core_bfd, section,
> +                                    note.data (), 0, note_size))
> +       {
> +         warning (_("could not get core note contents."));
> +         return false;
> +       }
> +
> +      /* Read the generic header of the note.  Those contain the format,
> +        start address and end address.  */
> +      uint64_t start_address
> +       = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t));
> +      uint64_t end_address
> +       = bfd_get_64 (core_bfd, note.data () + sizeof (uint16_t)
> +                               + sizeof (uint64_t));
> +
> +      /* Is the address within [start_address, end_address)?  */
> +      if (address >= start_address
> +         && address < end_address)
> +       return true;
> +
> +      /* The requested address lies outside this particular memtag note.  Keep
> +        looking and get the next section.  */
> +      section = bfd_get_next_section_by_name (core_bfd, section);
> +    }
> +
> +  return false;
> +}
> +
> +/* See linux-tdep.h.  */
> +
> +bool
> +linux_address_in_memtag_page (CORE_ADDR address)
> +{
> +  if (!target_has_execution ())
> +    return linux_core_file_address_in_memtag_page (address);
> +
> +  return linux_process_address_in_memtag_page (address);
> +}
> +
> +/* For each memory map entry that has memory tagging enabled, create a new
> +   core file note that contains all of its memory tags.  Save the data to
> +   NOTE_DATA and update NOTE_SIZE accordingly.  */
> +
> +static void
> +linux_make_memtag_corefile_notes (struct gdbarch *gdbarch, bfd *obfd,
> +                                 gdb::unique_xmalloc_ptr<char> &note_data,
> +                                 int *note_size)
> +{
> +  if (current_inferior ()->fake_pid_p)
> +    return;
> +
> +  /* If the architecture doesn't have a hook to return memory tag notes,
> +     there is nothing left to do.  */
> +  if (!gdbarch_create_memtag_notes_from_range_p (gdbarch))
> +    return;
> +
> +  pid_t pid = current_inferior ()->pid;
> +
> +  std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
> +
> +  gdb::unique_xmalloc_ptr<char> data
> +    = target_fileio_read_stralloc (NULL, smaps_file.c_str ());
> +
> +  if (data == nullptr)
> +    return;
> +
> +  std::vector<struct smaps_data> smaps;
> +
> +  /* Parse the contents of smaps into a vector.  */
> +  smaps = parse_smaps_data (data.get (), smaps_file);
> +
> +  for (const smaps_data &map : smaps)
> +    {
> +      /* Does this mapping have memory tagging enabled? If so, save the
> +        memory tags to the core file note.  */
> +      if (map.vmflags.memory_tagging == 0)
> +       continue;
> +
> +      /* Ask the architecture to create (one or more) NT_MEMTAG notes for
> +        this particular memory range, including the header.
> +
> +        If the notes are too big, we may need to break up the transfer
> +        into smaller chunks.
> +
> +        If the architecture returns an empty vector, that means there are
> +        no memory tag notes to write.  */
> +      std::vector<gdb::byte_vector> memory_tag_notes;
> +      memory_tag_notes
> +       = gdbarch_create_memtag_notes_from_range (gdbarch,
> +                                                 map.start_address,
> +                                                 map.end_address);
> +      /* Write notes to the core file.  */
> +      for (gdb::byte_vector note : memory_tag_notes)
> +       {
> +         note_data.reset (elfcore_write_note (obfd, note_data.release (),
> +                                              note_size, "CORE",
> +                                              NT_MEMTAG, note.data (),
> +                                              note.size ()));
> +       }
> +    }
> +}
> +
>  /* List memory regions in the inferior for a corefile.  */
>
>  static int
> @@ -2051,6 +2183,9 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size)
>         return NULL;
>      }
>
> +  /* Dump the memory tags, if any.  */
> +  linux_make_memtag_corefile_notes (gdbarch, obfd, note_data, note_size);
> +
>    /* File mappings.  */
>    linux_make_mappings_corefile_notes (gdbarch, obfd, note_data, note_size);
>
> diff --git a/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c
> new file mode 100644
> index 00000000000..b20ebcff424
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.c
> @@ -0,0 +1,93 @@
> +/* This test program is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 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/>.  */
> +
> +/* Exercise AArch64's Memory Tagging Extension with tagged pointers.  */
> +
> +/* This test was based on the documentation for the AArch64 Memory Tagging
> +   Extension from the Linux Kernel, found in the sources in
> +   Documentation/arm64/memory-tagging-extension.rst.  */
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <unistd.h>
> +#include <sys/auxv.h>
> +#include <sys/mman.h>
> +#include <sys/prctl.h>
> +
> +/* From arch/arm64/include/uapi/asm/hwcap.h */
> +#define HWCAP2_MTE              (1 << 18)
> +
> +/* From arch/arm64/include/uapi/asm/mman.h */
> +#define PROT_MTE  0x20
> +
> +/* From include/uapi/linux/prctl.h */
> +#define PR_SET_TAGGED_ADDR_CTRL 55
> +#define PR_GET_TAGGED_ADDR_CTRL 56
> +#define PR_TAGGED_ADDR_ENABLE  (1UL << 0)
> +#define PR_MTE_TCF_SHIFT       1
> +#define PR_MTE_TCF_SYNC                (1UL << PR_MTE_TCF_SHIFT)
> +#define PR_MTE_TAG_SHIFT       3
> +
> +void
> +access_memory (unsigned char *tagged_ptr)
> +{
> +  tagged_ptr[0] = 'a';
> +}
> +
> +int
> +main (int argc, char **argv)
> +{
> +  unsigned char *tagged_ptr;
> +  unsigned long page_sz = sysconf (_SC_PAGESIZE);
> +  unsigned long hwcap2 = getauxval(AT_HWCAP2);
> +
> +  /* Bail out if MTE is not supported.  */
> +  if (!(hwcap2 & HWCAP2_MTE))
> +    return 1;
> +
> +  /* Enable the tagged address ABI, synchronous MTE tag check faults and
> +     allow all non-zero tags in the randomly generated set.  */
> +  if (prctl (PR_SET_TAGGED_ADDR_CTRL,
> +            PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC
> +            | (0xfffe << PR_MTE_TAG_SHIFT),
> +            0, 0, 0))
> +    {
> +      perror ("prctl () failed");
> +      return 1;
> +    }
> +
> +  /* Create a mapping that will have PROT_MTE set.  */
> +  tagged_ptr = mmap (0, page_sz, PROT_READ | PROT_WRITE,
> +                    MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
> +  if (tagged_ptr == MAP_FAILED)
> +    {
> +      perror ("mmap () failed");
> +      return 1;
> +    }
> +
> +  /* Enable MTE on the above anonymous mmap.  */
> +  if (mprotect (tagged_ptr, page_sz, PROT_READ | PROT_WRITE | PROT_MTE))
> +    {
> +      perror ("mprotect () failed");
> +      return 1;
> +    }
> +
> +  access_memory (tagged_ptr);
> +
> +  return 0;
> +}
> diff --git a/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
> new file mode 100644
> index 00000000000..bb529a8b369
> --- /dev/null
> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
> @@ -0,0 +1,115 @@
> +# Copyright (C) 2018-2021 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 generating and reading a core file with MTE memory tags.
> +
> +if {![is_aarch64_target]} {
> +    verbose "Skipping ${gdb_test_file_name}."
> +    return
> +}
> +
> +standard_testfile
> +if { [prepare_for_testing "failed to prepare" ${testfile} ${srcfile}] } {
> +    return -1
> +}
> +
> +if ![runto_main] {
> +    untested "could not run to main"
> +    return -1
> +}
> +
> +# Targets that don't support memory tagging should not execute the
> +# runtime memory tagging tests.
> +if {![supports_memtag]} {
> +    unsupported "memory tagging unsupported"
> +    return -1
> +}
> +
> +gdb_breakpoint "access_memory"
> +
> +if [gdb_continue "access_memory"] {
> +    return -1
> +}
> +
> +# Set each tag granule to a different tag value, from 0x0 to 0xf.
> +set atag_msg "Allocation tag\\(s\\) updated successfully\."
> +for {set i 15} {$i >= 0} {incr i -1} {
> +    set index [expr [expr 15 - $i] * 16]
> +    set tag [format "%02x" $i]
> +    gdb_test "memory-tag set-allocation-tag &tagged_ptr\[$index\] 1 $tag" \
> +            $atag_msg \
> +            "set memory tag of &tagged_ptr\[$index\] to $tag"
> +}
> +
> +# Run until a crash and confirm GDB displays memory tag violation
> +# information.
> +gdb_test "continue" \
> +    [multi_line \
> +       "Program received signal SIGSEGV, Segmentation fault" \
> +       "Memory tag violation while accessing address $hex" \
> +       "Allocation tag $hex" \
> +       "Logical tag $hex\." \
> +       "$hex in access_memory \\(.*\\) at .*" \
> +       ".*tagged_ptr\\\[0\\\] = 'a';"] \
> +        "display tag violation information for live process"
> +
> +# Generate the core file.
> +set core_filename [standard_output_file "$testfile.core"]
> +set core_generated [gdb_gcore_cmd "$core_filename" "generate core file"]
> +
> +if { !$core_generated } {
> +    return -1
> +}
> +
> +clean_restart
> +
> +# Load the program file.
> +set program_filename [standard_output_file $testfile]
> +set program_loaded [gdb_file_cmd $program_filename]
> +
> +if { $program_loaded } {
> +    return -1
> +}
> +
> +# Load the core file and make sure we see the tag violation fault
> +# information.
> +gdb_test "core $core_filename" \
> +    [multi_line \
> +       "Core was generated by.*\." \
> +       "Program terminated with signal SIGSEGV, Segmentation fault" \
> +       "Memory tag violation while accessing address $hex" \
> +       "Allocation tag 0xf" \
> +       "Logical tag 0x0\." \
> +       "#0.*$hex in access_memory \\(.*\\) at .*" \
> +       ".*tagged_ptr\\\[0\\\] = 'a';"] \
> +        "core file shows tag violation information"
> +
> +# Make sure we have the tag_ctl register.
> +gdb_test "info register tag_ctl" \
> +        "tag_ctl.*$hex.*${::decimal}" \
> +        "tag_ctl is available"
> +
> +# Check if the tag granules have the expected values.  If they do, that
> +# means the core file saved the tags properly and GDB has read them
> +# correctly.
> +for {set i 15} {$i >= 0} {incr i -1} {
> +    set index [expr [expr 15 - $i] * 16]
> +    set tag [format "%x" $i]
> +    gdb_test "memory-tag print-allocation-tag &tagged_ptr\[$index\]" \
> +            "= 0x$tag" \
> +            "memory tag of &tagged_ptr\[$index\] is correct"
> +}
> --
> 2.25.1
>

  reply	other threads:[~2021-05-19 10:01 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-05-18 20:20 Luis Machado
2021-05-19 10:01 ` David Spickett [this message]
2021-05-19 11:11   ` Luis Machado
2021-05-19 12:13 ` Eli Zaretskii
2021-05-21 15:12 ` Alan Hayward
2021-05-21 15:30   ` Luis Machado
2021-05-21 17:20     ` John Baldwin
2021-05-24 13:41       ` Luis Machado
2021-05-24  8:07     ` Alan Hayward
2021-05-24 12:45       ` Luis Machado
2021-05-26 14:08 ` [PATCH,v2] " Luis Machado
2021-05-29  3:14   ` Simon Marchi
2021-05-31 14:12     ` Luis Machado
2021-05-31 14:49       ` Simon Marchi
2021-05-31 14:56         ` Luis Machado
2021-05-31 14:15   ` [PATCH,v3][AArch64] " Luis Machado
2021-05-31 16:44 ` [PATCH,v4][AArch64] " Luis Machado
2021-06-01 17:45 ` [PATCH,v5][AArch64] " Luis Machado
2021-06-15 14:10   ` [Ping][PATCH,v5][AArch64] " Luis Machado
2021-06-24 14:00   ` [PATCH,v5][AArch64] " Alan Hayward
2021-06-24 14:37     ` Luis Machado
2021-06-24 15:18       ` Alan Hayward
2021-07-01 13:50   ` [PING][PATCH,v5][AArch64] " Luis Machado
2021-07-11 14:22     ` Joel Brobecker
2021-07-14 13:07       ` Catalin Marinas
2021-07-29  2:26         ` Simon Marchi
2021-07-29 16:03           ` John Baldwin
2021-07-29 18:10           ` Catalin Marinas
2021-07-29 18:20             ` Simon Marchi
2021-08-01 15:44               ` Joel Brobecker
2021-08-02 12:06                 ` Luis Machado
2021-07-19 19:05   ` Luis Machado
2021-07-27 16:10   ` Luis Machado

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CACBW-2L3RPAK+oE9Thjn4+ShebLSdBs5BpmOTWbGNV6V2HQ7fA@mail.gmail.com \
    --to=david.spickett@linaro.org \
    --cc=alan.hayward@arm.com \
    --cc=catalin.marinas@arm.com \
    --cc=gdb-patches@sourceware.org \
    --cc=jhb@freebsd.org \
    --cc=luis.machado@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).