[v5,AArch64] MTE corefile support

Message ID 20210601174519.4157316-1-luis.machado@linaro.org
State New
Headers show
Series
  • [v5,AArch64] MTE corefile support
Related show

Commit Message

Philippe Waroquiers via Gdb-patches June 1, 2021, 5:45 p.m.
Updates on v5:

- Fixed format warning output.

Updates on v4:

- Calculate sizes based on individual struct field sizes.

Updates on v3:

- Addressed review comments.
- New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.
- Updated code documentation.
- Removed code duplication.

Updates on v2:

- Reworked core_target::fetch_memtags to handle cases where address + len runs
  over the NT_MEMTAG note.
- Turned a few magic numbers into constants. There is an unfortunate duplication
  of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is
  no file generic enough that gets included by both corelow and linux-tdep.
- More sanity checks to make sure the note format is correct.
- Documented aarch64_linux_decode_memtag_note a little more.

---

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>

	* Makefile.in (COMMON_SFILES): Add memtag.c.
	* NEWS: Mention core file support for memory tagging.
	* aarch64-linux-tdep.c: Include elf/common.h.
	Include gdbsupport/memtag.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.
	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.
	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.
	(AARCH64_MTE_TAG_BIT_SIZE): New constant.
	* corelow.c: Include gdbsupport/memtag.h and memtag.h.
	(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: Include gdbsupport/memtag.h and memtag.h.
	(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.
	* memtag.c: New file.
	* memtag.h: New file.

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.

gdbsupport/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

	* memtag.h: New file.
---
 gdb/Makefile.in                              |   1 +
 gdb/NEWS                                     |   4 +
 gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++
 gdb/arch/aarch64-mte-linux.h                 |  17 ++
 gdb/corelow.c                                |  63 ++++++
 gdb/doc/gdb.texinfo                          |   4 +
 gdb/gdbarch.c                                |  64 ++++++
 gdb/gdbarch.h                                |  16 ++
 gdb/gdbarch.sh                               |   6 +
 gdb/linux-tdep.c                             |  97 +++++++-
 gdb/memtag.c                                 |  88 ++++++++
 gdb/memtag.h                                 |  46 ++++
 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++
 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++
 gdbsupport/memtag.h                          |  39 ++++
 15 files changed, 867 insertions(+), 3 deletions(-)
 create mode 100644 gdb/memtag.c
 create mode 100644 gdb/memtag.h
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
 create mode 100644 gdbsupport/memtag.h

-- 
2.25.1

Comments

Philippe Waroquiers via Gdb-patches June 15, 2021, 2:10 p.m. | #1
On 6/1/21 2:45 PM, Luis Machado wrote:
> Updates on v5:

> 

> - Fixed format warning output.

> 

> Updates on v4:

> 

> - Calculate sizes based on individual struct field sizes.

> 

> Updates on v3:

> 

> - Addressed review comments.

> - New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.

> - Updated code documentation.

> - Removed code duplication.

> 

> Updates on v2:

> 

> - Reworked core_target::fetch_memtags to handle cases where address + len runs

>    over the NT_MEMTAG note.

> - Turned a few magic numbers into constants. There is an unfortunate duplication

>    of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is

>    no file generic enough that gets included by both corelow and linux-tdep.

> - More sanity checks to make sure the note format is correct.

> - Documented aarch64_linux_decode_memtag_note a little more.

> 

> ---

> 

> 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>

> 

> 	* Makefile.in (COMMON_SFILES): Add memtag.c.

> 	* NEWS: Mention core file support for memory tagging.

> 	* aarch64-linux-tdep.c: Include elf/common.h.

> 	Include gdbsupport/memtag.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.

> 	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.

> 	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.

> 	(AARCH64_MTE_TAG_BIT_SIZE): New constant.

> 	* corelow.c: Include gdbsupport/memtag.h and memtag.h.

> 	(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: Include gdbsupport/memtag.h and memtag.h.

> 	(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.

> 	* memtag.c: New file.

> 	* memtag.h: New file.

> 

> 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.

> 

> gdbsupport/ChangeLog:

> 

> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

> 

> 	* memtag.h: New file.

> ---

>   gdb/Makefile.in                              |   1 +

>   gdb/NEWS                                     |   4 +

>   gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++

>   gdb/arch/aarch64-mte-linux.h                 |  17 ++

>   gdb/corelow.c                                |  63 ++++++

>   gdb/doc/gdb.texinfo                          |   4 +

>   gdb/gdbarch.c                                |  64 ++++++

>   gdb/gdbarch.h                                |  16 ++

>   gdb/gdbarch.sh                               |   6 +

>   gdb/linux-tdep.c                             |  97 +++++++-

>   gdb/memtag.c                                 |  88 ++++++++

>   gdb/memtag.h                                 |  46 ++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++

>   gdbsupport/memtag.h                          |  39 ++++

>   15 files changed, 867 insertions(+), 3 deletions(-)

>   create mode 100644 gdb/memtag.c

>   create mode 100644 gdb/memtag.h

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

>   create mode 100644 gdbsupport/memtag.h

> 

> diff --git a/gdb/Makefile.in b/gdb/Makefile.in

> index f664d964536..12fb3b390b1 100644

> --- a/gdb/Makefile.in

> +++ b/gdb/Makefile.in

> @@ -1100,6 +1100,7 @@ COMMON_SFILES = \

>   	memattr.c \

>   	memory-map.c \

>   	memrange.c \

> +	memtag.c \

>   	minidebug.c \

>   	minsyms.c \

>   	mipsread.c \

> 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..04498f3b6c0 100644

> --- a/gdb/aarch64-linux-tdep.c

> +++ b/gdb/aarch64-linux-tdep.c

> @@ -52,6 +52,9 @@

>   #include "value.h"

>   

>   #include "gdbsupport/selftest.h"

> +#include "gdbsupport/memtag.h"

> +

> +#include "elf/common.h"

>   

>   /* Signal frame handling.

>   

> @@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,

>       }

>   }

>   

> +/* Memory tag note header size.  Includes both the generic and the

> +   arch-specific parts.  */

> +#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \

> +				     + NT_MEMTAG_MTE_HEADER_SIZE)

> +

> +/* 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.  */

> +  size_t 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 (NT_MEMTAG_TOTAL_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 (tag_dump_header::format),

> +			  byte_order, NT_MEMTAG_TYPE_AARCH_MTE);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  /* Start address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +			  byte_order, start_address);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  /* End address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +			  byte_order, end_address);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* MTE-specific header.  */

> +  /* Granule byte size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::granule_byte_size),

> +			  byte_order, AARCH64_MTE_GRANULE_SIZE);

> +  buf += sizeof (tag_dump_mte::granule_byte_size);

> +

> +  /* Tag bit size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::tag_bit_size),

> +			  byte_order, AARCH64_MTE_TAG_BIT_SIZE);

> +  buf += sizeof (tag_dump_mte::tag_bit_size);

> +

> +  /* Unused value */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::__unused), byte_order, 0);

> +

> +  /* Store the tags.  */

> +  memcpy (notes[0].data () + NT_MEMTAG_TOTAL_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 requested tags.

> +

> +   The note is guaranteed to cover the [ADDRESS, ADDRESS + length)

> +   range.  */

> +

> +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;

> +

> +  /* Sanity check.  */

> +  if (note.size () < NT_MEMTAG_TOTAL_HEADER_SIZE)

> +    {

> +      warning (_("Malformed core note - too short for MTE header.\n"

> +		 "Expected %s bytes but got %s bytes."),

> +	       pulongest (NT_MEMTAG_TOTAL_HEADER_SIZE),

> +	       pulongest (note.size ()));

> +      return tags;

> +    }

> +

> +  /* The amount of memory tag granules we need to fetch.  */

> +  size_t 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;

> +

> +  /* Read the generic header.  */

> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

> +  const gdb_byte *buf = note.data ();

> +

> +  unsigned int format

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::format),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  CORE_ADDR start_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  CORE_ADDR end_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* Validate that ADDRESS + LENGTH doesn't fall outside of this note's

> +     range of addresses.  */

> +  gdb_assert (address + length < end_vma);

> +

> +  /* Is the tag header format correct for this note?  */

> +  if (format != NT_MEMTAG_TYPE_AARCH_MTE)

> +    {

> +      warning (_("Unexpected memory tag note format.\n"

> +		 "Expected %x but got %x."), NT_MEMTAG_TYPE_AARCH_MTE,

> +	       format);

> +      return tags;

> +    }

> +

> +  size_t expected_tag_bytes = (end_vma - start_vma)/AARCH64_MTE_GRANULE_SIZE;

> +

> +  /* Does the number of tag bytes in this note match the expected number

> +     of tag bytes the note says it has?  */

> +  if (note.size () < (NT_MEMTAG_TOTAL_HEADER_SIZE + expected_tag_bytes))

> +    {

> +      warning (_("Unexpected tag data size.\n"

> +		 "Expected %s but got %s."), pulongest (expected_tag_bytes),

> +	       pulongest (note.size () - NT_MEMTAG_TOTAL_HEADER_SIZE));

> +      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 (start_vma, AARCH64_MTE_GRANULE_SIZE);

> +  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);

> +

> +  size_t skipped_granules

> +    = aarch64_mte_get_tag_granules (aligned_start_address,

> +				    aligned_address - aligned_start_address,

> +				    AARCH64_MTE_GRANULE_SIZE);

> +

> +  /* Point to the block of data that contains the first granule we are

> +     interested in.  */

> +  const gdb::array_view<const gdb_byte> tags_data

> +    = note.slice (NT_MEMTAG_TOTAL_HEADER_SIZE + skipped_granules, granules);

> +

> +  /* Read the tag granules.  */

> +  for (size_t 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 +2072,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..7da9de4aefb 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,20 @@ 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);

>   

> +/* NT_MEMTAG header for MTE tags.  */

> +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;

> +};

> +

> +/* Size of the MTE header for a NT_MEMTAG note.  */

> +#define NT_MEMTAG_MTE_HEADER_SIZE (sizeof (tag_dump_mte::granule_byte_size) \

> +				   + sizeof (tag_dump_mte::tag_bit_size) \

> +				   + sizeof (tag_dump_mte::__unused))

> +

>   #endif /* ARCH_AARCH64_LINUX_H */

> diff --git a/gdb/corelow.c b/gdb/corelow.c

> index 452b4dd4f9a..24c5bf29a11 100644

> --- a/gdb/corelow.c

> +++ b/gdb/corelow.c

> @@ -51,6 +51,8 @@

>   #include "gdbcmd.h"

>   #include "xml-tdesc.h"

>   #include "observable.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #ifndef O_LARGEFILE

>   #define O_LARGEFILE 0

> @@ -100,6 +102,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 +1124,60 @@ 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.  */

> +

> +  return (bfd_get_section_by_name (core_bfd, ".memtag") != nullptr);

> +}

> +

> +/* 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))

> +    error (_("gdbarch_decode_memtag_note not implemented for this "

> +	     "architecture."));

> +

> +  memtag_note_info info;

> +  info.memtag_section = nullptr;

> +

> +  while (get_next_core_memtag_section (core_bfd, info.memtag_section,

> +				       address, info))

> +  {

> +    size_t adjusted_length

> +      = (address + len < info.end_address)? len : (info.end_address - address);

> +

> +    /* Decode the memory tag note and return the tags.  */

> +    gdb::byte_vector tags_read

> +      = gdbarch_decode_memtag_note (gdbarch, info.note, address,

> +				    adjusted_length);

> +

> +    /* Transfer over the tags that have been read.  */

> +    tags.insert (tags.end (), tags_read.begin (), tags_read.end ());

> +

> +    /* ADDRESS + LEN may cross the boundaries of a particular NT_MEMTAG

> +       note.  Check if we need to fetch tags from a different section.  */

> +    if (address + len < info.end_address)

> +      return true;

> +

> +    /* There are more tags to fetch.  Update ADDRESS and LEN.  */

> +    len -= (info.end_address - address);

> +    address = info.end_address;

> +  }

> +

> +  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 90d827a50e7..1b001e6cacb 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..6192cc4421b 100644

> --- a/gdb/linux-tdep.c

> +++ b/gdb/linux-tdep.c

> @@ -41,6 +41,8 @@

>   #include "gdbsupport/gdb_optional.h"

>   #include "gcore.h"

>   #include "gcore-elf.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #include <ctype.h>

>   

> @@ -1438,10 +1440,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 +1476,91 @@ 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;

> +

> +  memtag_note_info info;

> +  return get_next_core_memtag_section (core_bfd, nullptr, address, info);

> +}

> +

> +/* 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;

> +

> +  /* Parse the contents of smaps into a vector.  */

> +  std::vector<struct smaps_data> 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

> +	= gdbarch_create_memtag_notes_from_range (gdbarch,

> +						  map.start_address,

> +						  map.end_address);

> +      /* Write notes to the core file.  */

> +      for (const 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 +2139,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/memtag.c b/gdb/memtag.c

> new file mode 100644

> index 00000000000..4d92ecde84a

> --- /dev/null

> +++ b/gdb/memtag.c

> @@ -0,0 +1,88 @@

> +/* GDB generic memory tagging functions.

> +

> +   Copyright (C) 2021 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 "defs.h"

> +#include "memtag.h"

> +#include "gdbsupport/memtag.h"

> +#include "bfd.h"

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return a pointer to a .memtag section containing ADDRESS or nullptr

> +   of none are found.

> +

> +   If SECTION is provided, search from that section onwards.  */

> +

> +bool

> +get_next_core_memtag_section (bfd *abfd, asection *section,

> +			      CORE_ADDR address, memtag_note_info &info)

> +{

> +  /* If SECTION is nullptr, start a fresh lookup.  */

> +  if (section == nullptr)

> +    section = bfd_get_section_by_name (abfd, ".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 < NT_MEMTAG_GENERIC_HEADER_SIZE)

> +	{

> +	  warning (_("Malformed core note - too short for NT_MEMTAG generic "

> +		     "header.\n"

> +		     "Expected %s bytes but got %s bytes."),

> +		   pulongest (NT_MEMTAG_GENERIC_HEADER_SIZE),

> +		   pulongest (note_size));

> +	  return false;

> +	}

> +

> +      gdb::byte_vector note (note_size);

> +

> +      /* Fetch the contents of this particular memtag note.  */

> +      if (!bfd_get_section_contents (abfd, section,

> +				     note.data (), 0, note_size))

> +	{

> +	  warning (_("could not get core note contents."));

> +	  return false;

> +	}

> +

> +      /* Read the generic header of the note.  It contains the format,

> +	 start address and end address.  */

> +      uint64_t start_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format));

> +      uint64_t end_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format)

> +			    + sizeof (tag_dump_header::start_vma));

> +

> +      /* Is the address within [start_address, end_address)?  */

> +      if (address >= start_address

> +	  && address < end_address)

> +	{

> +	  info.start_address = start_address;

> +	  info.end_address = end_address;

> +	  info.note = note;

> +	  info.memtag_section = section;

> +	  return true;

> +	}

> +    }

> +  return false;

> +}

> diff --git a/gdb/memtag.h b/gdb/memtag.h

> new file mode 100644

> index 00000000000..43c9efb39a3

> --- /dev/null

> +++ b/gdb/memtag.h

> @@ -0,0 +1,46 @@

> +/* GDB generic memory tagging definitions.

> +   Copyright (C) 2021 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 MEMTAG_H

> +#define MEMTAG_H

> +

> +#include "bfd.h"

> +

> +struct memtag_note_info

> +{

> +  CORE_ADDR start_address;

> +  CORE_ADDR end_address;

> +  gdb::byte_vector note;

> +  asection *memtag_section;

> +};

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return TRUE if there is a .memtag section containing ADDRESS.  Return FALSE

> +   otherwise.

> +

> +   If SECTION is provided, search from that section onwards. If SECTION is

> +   nullptr, then start a new search.

> +

> +   If a .memtag section containing ADDRESS is found, fill INFO with data

> +   about such section.  Otherwise leave it unchanged.  */

> +

> +bool get_next_core_memtag_section (bfd *abfd, asection *section,

> +				   CORE_ADDR address, memtag_note_info &info);

> +

> +#endif /* MEMTAG_H */

> 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..d0bcd036972

> --- /dev/null

> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> @@ -0,0 +1,111 @@

> +# 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 $binfile

> +

> +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"

> +}

> diff --git a/gdbsupport/memtag.h b/gdbsupport/memtag.h

> new file mode 100644

> index 00000000000..bb47eed220b

> --- /dev/null

> +++ b/gdbsupport/memtag.h

> @@ -0,0 +1,39 @@

> +/* Generic memory tagging definitions.

> +   Copyright (C) 2021 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 GDBSUPPORT_MEMTAG_H

> +#define GDBSUPPORT_MEMTAG_H

> +

> +/* 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;

> +};

> +

> +/* Size of the generic header for the NT_MEMTAG note.  This is OS-independent

> +   and should be shared with OS-specific and arch-specific code.  */

> +#define NT_MEMTAG_GENERIC_HEADER_SIZE (sizeof (tag_dump_header::format) \

> +				       + sizeof (tag_dump_header::start_vma) \

> +				       + sizeof (tag_dump_header::end_vma))

> +

> +#endif /* GDBSUPPORT_MEMTAG_H */

>
Philippe Waroquiers via Gdb-patches June 24, 2021, 2 p.m. | #2
> On 1 Jun 2021, at 18:45, Luis Machado <luis.machado@linaro.org> wrote:

> 

> Updates on v5:

> 

> - Fixed format warning output.

> 

> Updates on v4:

> 

> - Calculate sizes based on individual struct field sizes.

> 

> Updates on v3:

> 

> - Addressed review comments.

> - New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.

> - Updated code documentation.

> - Removed code duplication.

> 

> Updates on v2:

> 

> - Reworked core_target::fetch_memtags to handle cases where address + len runs

>  over the NT_MEMTAG note.

> - Turned a few magic numbers into constants. There is an unfortunate duplication

>  of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is

>  no file generic enough that gets included by both corelow and linux-tdep.

> - More sanity checks to make sure the note format is correct.

> - Documented aarch64_linux_decode_memtag_note a little more.

> 

> ---

> 

> 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>

> 

> 	* Makefile.in (COMMON_SFILES): Add memtag.c.

> 	* NEWS: Mention core file support for memory tagging.

> 	* aarch64-linux-tdep.c: Include elf/common.h.

> 	Include gdbsupport/memtag.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.

> 	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.

> 	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.

> 	(AARCH64_MTE_TAG_BIT_SIZE): New constant.

> 	* corelow.c: Include gdbsupport/memtag.h and memtag.h.

> 	(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: Include gdbsupport/memtag.h and memtag.h.

> 	(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.

> 	* memtag.c: New file.

> 	* memtag.h: New file.

> 

> 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.

> 

> gdbsupport/ChangeLog:

> 

> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

> 

> 	* memtag.h: New file.

> ---

> gdb/Makefile.in                              |   1 +

> gdb/NEWS                                     |   4 +

> gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++

> gdb/arch/aarch64-mte-linux.h                 |  17 ++

> gdb/corelow.c                                |  63 ++++++

> gdb/doc/gdb.texinfo                          |   4 +

> gdb/gdbarch.c                                |  64 ++++++

> gdb/gdbarch.h                                |  16 ++

> gdb/gdbarch.sh                               |   6 +

> gdb/linux-tdep.c                             |  97 +++++++-

> gdb/memtag.c                                 |  88 ++++++++

> gdb/memtag.h                                 |  46 ++++

> gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++

> gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++

> gdbsupport/memtag.h                          |  39 ++++

> 15 files changed, 867 insertions(+), 3 deletions(-)

> create mode 100644 gdb/memtag.c

> create mode 100644 gdb/memtag.h

> create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c

> create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> create mode 100644 gdbsupport/memtag.h

> 


Note there were a few minor merge conflicts, nothing to worry about though.


> diff --git a/gdb/Makefile.in b/gdb/Makefile.in

> index f664d964536..12fb3b390b1 100644

> --- a/gdb/Makefile.in

> +++ b/gdb/Makefile.in

> @@ -1100,6 +1100,7 @@ COMMON_SFILES = \

> 	memattr.c \

> 	memory-map.c \

> 	memrange.c \

> +	memtag.c \

> 	minidebug.c \

> 	minsyms.c \

> 	mipsread.c \

> 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..04498f3b6c0 100644

> --- a/gdb/aarch64-linux-tdep.c

> +++ b/gdb/aarch64-linux-tdep.c

> @@ -52,6 +52,9 @@

> #include "value.h"

> 

> #include "gdbsupport/selftest.h"

> +#include "gdbsupport/memtag.h"

> +

> +#include "elf/common.h"

> 

> /* Signal frame handling.

> 

> @@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,

>     }

> }

> 

> +/* Memory tag note header size.  Includes both the generic and the

> +   arch-specific parts.  */

> +#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \

> +				     + NT_MEMTAG_MTE_HEADER_SIZE)

> +

> +/* 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.  */

> +  size_t 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 (NT_MEMTAG_TOTAL_HEADER_SIZE + granules);


Should this be NT_MEMTAG_TOTAL_HEADER_SIZE + (granules * AARCH64_MTE_GRANULE_SIZE)
Get_granules has already divided by the granule size.

> +

> +  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);


If you do the original resize after the while loop, then there would be no need to resize here.

> +	  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 (tag_dump_header::format),

> +			  byte_order, NT_MEMTAG_TYPE_AARCH_MTE);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  /* Start address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +			  byte_order, start_address);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  /* End address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +			  byte_order, end_address);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* MTE-specific header.  */

> +  /* Granule byte size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::granule_byte_size),

> +			  byte_order, AARCH64_MTE_GRANULE_SIZE);

> +  buf += sizeof (tag_dump_mte::granule_byte_size);

> +

> +  /* Tag bit size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::tag_bit_size),

> +			  byte_order, AARCH64_MTE_TAG_BIT_SIZE);

> +  buf += sizeof (tag_dump_mte::tag_bit_size);

> +

> +  /* Unused value */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::__unused), byte_order, 0);

> +

> +  /* Store the tags.  */

> +  memcpy (notes[0].data () + NT_MEMTAG_TOTAL_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 requested tags.

> +

> +   The note is guaranteed to cover the [ADDRESS, ADDRESS + length)

> +   range.  */

> +

> +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;

> +

> +  /* Sanity check.  */

> +  if (note.size () < NT_MEMTAG_TOTAL_HEADER_SIZE)

> +    {

> +      warning (_("Malformed core note - too short for MTE header.\n"

> +		 "Expected %s bytes but got %s bytes."),

> +	       pulongest (NT_MEMTAG_TOTAL_HEADER_SIZE),

> +	       pulongest (note.size ()));

> +      return tags;

> +    }

> +

> +  /* The amount of memory tag granules we need to fetch.  */

> +  size_t 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;

> +

> +  /* Read the generic header.  */

> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

> +  const gdb_byte *buf = note.data ();

> +

> +  unsigned int format

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::format),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  CORE_ADDR start_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  CORE_ADDR end_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* Validate that ADDRESS + LENGTH doesn't fall outside of this note's

> +     range of addresses.  */

> +  gdb_assert (address + length < end_vma);

> +

> +  /* Is the tag header format correct for this note?  */

> +  if (format != NT_MEMTAG_TYPE_AARCH_MTE)

> +    {

> +      warning (_("Unexpected memory tag note format.\n"

> +		 "Expected %x but got %x."), NT_MEMTAG_TYPE_AARCH_MTE,

> +	       format);

> +      return tags;

> +    }

> +

> +  size_t expected_tag_bytes = (end_vma - start_vma)/AARCH64_MTE_GRANULE_SIZE;

> +

> +  /* Does the number of tag bytes in this note match the expected number

> +     of tag bytes the note says it has?  */

> +  if (note.size () < (NT_MEMTAG_TOTAL_HEADER_SIZE + expected_tag_bytes))

> +    {

> +      warning (_("Unexpected tag data size.\n"

> +		 "Expected %s but got %s."), pulongest (expected_tag_bytes),

> +	       pulongest (note.size () - NT_MEMTAG_TOTAL_HEADER_SIZE));

> +      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 (start_vma, AARCH64_MTE_GRANULE_SIZE);

> +  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);

> +

> +  size_t skipped_granules

> +    = aarch64_mte_get_tag_granules (aligned_start_address,

> +				    aligned_address - aligned_start_address,

> +				    AARCH64_MTE_GRANULE_SIZE);

> +

> +  /* Point to the block of data that contains the first granule we are

> +     interested in.  */

> +  const gdb::array_view<const gdb_byte> tags_data

> +    = note.slice (NT_MEMTAG_TOTAL_HEADER_SIZE + skipped_granules, granules);

> +

> +  /* Read the tag granules.  */

> +  for (size_t 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 +2072,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..7da9de4aefb 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,20 @@ 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);

> 

> +/* NT_MEMTAG header for MTE tags.  */

> +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;

> +};

> +

> +/* Size of the MTE header for a NT_MEMTAG note.  */

> +#define NT_MEMTAG_MTE_HEADER_SIZE (sizeof (tag_dump_mte::granule_byte_size) \

> +				   + sizeof (tag_dump_mte::tag_bit_size) \

> +				   + sizeof (tag_dump_mte::__unused))

> +

> #endif /* ARCH_AARCH64_LINUX_H */

> diff --git a/gdb/corelow.c b/gdb/corelow.c

> index 452b4dd4f9a..24c5bf29a11 100644

> --- a/gdb/corelow.c

> +++ b/gdb/corelow.c

> @@ -51,6 +51,8 @@

> #include "gdbcmd.h"

> #include "xml-tdesc.h"

> #include "observable.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

> 

> #ifndef O_LARGEFILE

> #define O_LARGEFILE 0

> @@ -100,6 +102,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 +1124,60 @@ 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.  */

> +

> +  return (bfd_get_section_by_name (core_bfd, ".memtag") != nullptr);

> +}

> +

> +/* 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))

> +    error (_("gdbarch_decode_memtag_note not implemented for this "

> +	     "architecture."));

> +

> +  memtag_note_info info;

> +  info.memtag_section = nullptr;

> +

> +  while (get_next_core_memtag_section (core_bfd, info.memtag_section,

> +				       address, info))

> +  {

> +    size_t adjusted_length

> +      = (address + len < info.end_address)? len : (info.end_address - address);

> +

> +    /* Decode the memory tag note and return the tags.  */

> +    gdb::byte_vector tags_read

> +      = gdbarch_decode_memtag_note (gdbarch, info.note, address,

> +				    adjusted_length);

> +

> +    /* Transfer over the tags that have been read.  */

> +    tags.insert (tags.end (), tags_read.begin (), tags_read.end ());

> +

> +    /* ADDRESS + LEN may cross the boundaries of a particular NT_MEMTAG

> +       note.  Check if we need to fetch tags from a different section.  */

> +    if (address + len < info.end_address)

> +      return true;

> +

> +    /* There are more tags to fetch.  Update ADDRESS and LEN.  */

> +    len -= (info.end_address - address);

> +    address = info.end_address;

> +  }

> +

> +  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 90d827a50e7..1b001e6cacb 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..6192cc4421b 100644

> --- a/gdb/linux-tdep.c

> +++ b/gdb/linux-tdep.c

> @@ -41,6 +41,8 @@

> #include "gdbsupport/gdb_optional.h"

> #include "gcore.h"

> #include "gcore-elf.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

> 

> #include <ctype.h>

> 

> @@ -1438,10 +1440,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 +1476,91 @@ 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;

> +

> +  memtag_note_info info;

> +  return get_next_core_memtag_section (core_bfd, nullptr, address, info);

> +}

> +

> +/* 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;

> +

> +  /* Parse the contents of smaps into a vector.  */

> +  std::vector<struct smaps_data> 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

> +	= gdbarch_create_memtag_notes_from_range (gdbarch,

> +						  map.start_address,

> +						  map.end_address);

> +      /* Write notes to the core file.  */

> +      for (const 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 +2139,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/memtag.c b/gdb/memtag.c

> new file mode 100644

> index 00000000000..4d92ecde84a

> --- /dev/null

> +++ b/gdb/memtag.c

> @@ -0,0 +1,88 @@

> +/* GDB generic memory tagging functions.

> +

> +   Copyright (C) 2021 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 "defs.h"

> +#include "memtag.h"

> +#include "gdbsupport/memtag.h"

> +#include "bfd.h"

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return a pointer to a .memtag section containing ADDRESS or nullptr

> +   of none are found.

> +

> +   If SECTION is provided, search from that section onwards.  */

> +

> +bool

> +get_next_core_memtag_section (bfd *abfd, asection *section,

> +			      CORE_ADDR address, memtag_note_info &info)

> +{

> +  /* If SECTION is nullptr, start a fresh lookup.  */

> +  if (section == nullptr)

> +    section = bfd_get_section_by_name (abfd, ".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 < NT_MEMTAG_GENERIC_HEADER_SIZE)

> +	{

> +	  warning (_("Malformed core note - too short for NT_MEMTAG generic "

> +		     "header.\n"

> +		     "Expected %s bytes but got %s bytes."),

> +		   pulongest (NT_MEMTAG_GENERIC_HEADER_SIZE),

> +		   pulongest (note_size));

> +	  return false;

> +	}

> +

> +      gdb::byte_vector note (note_size);

> +

> +      /* Fetch the contents of this particular memtag note.  */

> +      if (!bfd_get_section_contents (abfd, section,

> +				     note.data (), 0, note_size))

> +	{

> +	  warning (_("could not get core note contents."));

> +	  return false;

> +	}

> +

> +      /* Read the generic header of the note.  It contains the format,

> +	 start address and end address.  */

> +      uint64_t start_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format));

> +      uint64_t end_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format)

> +			    + sizeof (tag_dump_header::start_vma));

> +

> +      /* Is the address within [start_address, end_address)?  */

> +      if (address >= start_address

> +	  && address < end_address)

> +	{

> +	  info.start_address = start_address;

> +	  info.end_address = end_address;

> +	  info.note = note;

> +	  info.memtag_section = section;

> +	  return true;

> +	}

> +    }

> +  return false;

> +}

> diff --git a/gdb/memtag.h b/gdb/memtag.h

> new file mode 100644

> index 00000000000..43c9efb39a3

> --- /dev/null

> +++ b/gdb/memtag.h

> @@ -0,0 +1,46 @@

> +/* GDB generic memory tagging definitions.

> +   Copyright (C) 2021 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 MEMTAG_H

> +#define MEMTAG_H

> +

> +#include "bfd.h"

> +

> +struct memtag_note_info

> +{

> +  CORE_ADDR start_address;

> +  CORE_ADDR end_address;

> +  gdb::byte_vector note;

> +  asection *memtag_section;

> +};

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return TRUE if there is a .memtag section containing ADDRESS.  Return FALSE

> +   otherwise.

> +

> +   If SECTION is provided, search from that section onwards. If SECTION is

> +   nullptr, then start a new search.

> +

> +   If a .memtag section containing ADDRESS is found, fill INFO with data

> +   about such section.  Otherwise leave it unchanged.  */

> +

> +bool get_next_core_memtag_section (bfd *abfd, asection *section,

> +				   CORE_ADDR address, memtag_note_info &info);

> +

> +#endif /* MEMTAG_H */

> 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..d0bcd036972

> --- /dev/null

> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> @@ -0,0 +1,111 @@

> +# 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 $binfile

> +

> +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"

> +}

> diff --git a/gdbsupport/memtag.h b/gdbsupport/memtag.h

> new file mode 100644

> index 00000000000..bb47eed220b

> --- /dev/null

> +++ b/gdbsupport/memtag.h

> @@ -0,0 +1,39 @@

> +/* Generic memory tagging definitions.

> +   Copyright (C) 2021 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 GDBSUPPORT_MEMTAG_H

> +#define GDBSUPPORT_MEMTAG_H

> +

> +/* 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;

> +};

> +

> +/* Size of the generic header for the NT_MEMTAG note.  This is OS-independent

> +   and should be shared with OS-specific and arch-specific code.  */

> +#define NT_MEMTAG_GENERIC_HEADER_SIZE (sizeof (tag_dump_header::format) \

> +				       + sizeof (tag_dump_header::start_vma) \

> +				       + sizeof (tag_dump_header::end_vma))

> +

> +#endif /* GDBSUPPORT_MEMTAG_H */

> -- 

> 2.25.1

>
Philippe Waroquiers via Gdb-patches June 24, 2021, 2:37 p.m. | #3
On 6/24/21 11:00 AM, Alan Hayward wrote:
> 

> 

>> On 1 Jun 2021, at 18:45, Luis Machado <luis.machado@linaro.org> wrote:

>>

>> Updates on v5:

>>

>> - Fixed format warning output.

>>

>> Updates on v4:

>>

>> - Calculate sizes based on individual struct field sizes.

>>

>> Updates on v3:

>>

>> - Addressed review comments.

>> - New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.

>> - Updated code documentation.

>> - Removed code duplication.

>>

>> Updates on v2:

>>

>> - Reworked core_target::fetch_memtags to handle cases where address + len runs

>>   over the NT_MEMTAG note.

>> - Turned a few magic numbers into constants. There is an unfortunate duplication

>>   of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is

>>   no file generic enough that gets included by both corelow and linux-tdep.

>> - More sanity checks to make sure the note format is correct.

>> - Documented aarch64_linux_decode_memtag_note a little more.

>>

>> ---

>>

>> 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>

>>

>> 	* Makefile.in (COMMON_SFILES): Add memtag.c.

>> 	* NEWS: Mention core file support for memory tagging.

>> 	* aarch64-linux-tdep.c: Include elf/common.h.

>> 	Include gdbsupport/memtag.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.

>> 	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.

>> 	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.

>> 	(AARCH64_MTE_TAG_BIT_SIZE): New constant.

>> 	* corelow.c: Include gdbsupport/memtag.h and memtag.h.

>> 	(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: Include gdbsupport/memtag.h and memtag.h.

>> 	(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.

>> 	* memtag.c: New file.

>> 	* memtag.h: New file.

>>

>> 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.

>>

>> gdbsupport/ChangeLog:

>>

>> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

>>

>> 	* memtag.h: New file.

>> ---

>> gdb/Makefile.in                              |   1 +

>> gdb/NEWS                                     |   4 +

>> gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++

>> gdb/arch/aarch64-mte-linux.h                 |  17 ++

>> gdb/corelow.c                                |  63 ++++++

>> gdb/doc/gdb.texinfo                          |   4 +

>> gdb/gdbarch.c                                |  64 ++++++

>> gdb/gdbarch.h                                |  16 ++

>> gdb/gdbarch.sh                               |   6 +

>> gdb/linux-tdep.c                             |  97 +++++++-

>> gdb/memtag.c                                 |  88 ++++++++

>> gdb/memtag.h                                 |  46 ++++

>> gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++

>> gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++

>> gdbsupport/memtag.h                          |  39 ++++

>> 15 files changed, 867 insertions(+), 3 deletions(-)

>> create mode 100644 gdb/memtag.c

>> create mode 100644 gdb/memtag.h

>> create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c

>> create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

>> create mode 100644 gdbsupport/memtag.h

>>

> 

> Note there were a few minor merge conflicts, nothing to worry about though.


I'll check again and will refresh the patch.

> 

> 

>> diff --git a/gdb/Makefile.in b/gdb/Makefile.in

>> index f664d964536..12fb3b390b1 100644

>> --- a/gdb/Makefile.in

>> +++ b/gdb/Makefile.in

>> @@ -1100,6 +1100,7 @@ COMMON_SFILES = \

>> 	memattr.c \

>> 	memory-map.c \

>> 	memrange.c \

>> +	memtag.c \

>> 	minidebug.c \

>> 	minsyms.c \

>> 	mipsread.c \

>> 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..04498f3b6c0 100644

>> --- a/gdb/aarch64-linux-tdep.c

>> +++ b/gdb/aarch64-linux-tdep.c

>> @@ -52,6 +52,9 @@

>> #include "value.h"

>>

>> #include "gdbsupport/selftest.h"

>> +#include "gdbsupport/memtag.h"

>> +

>> +#include "elf/common.h"

>>

>> /* Signal frame handling.

>>

>> @@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,

>>      }

>> }

>>

>> +/* Memory tag note header size.  Includes both the generic and the

>> +   arch-specific parts.  */

>> +#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \

>> +				     + NT_MEMTAG_MTE_HEADER_SIZE)

>> +

>> +/* 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.  */

>> +  size_t 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 (NT_MEMTAG_TOTAL_HEADER_SIZE + granules);

> 

> Should this be NT_MEMTAG_TOTAL_HEADER_SIZE + (granules * AARCH64_MTE_GRANULE_SIZE)

> Get_granules has already divided by the granule size.

> 


We are storing 1 tag per byte. So the number of granules is the number 
of tags. If we multiply by AARCH64_MTE_GRANULE_SIZE, we will have 16 
times more tags.

>> +

>> +  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);

> 

> If you do the original resize after the while loop, then there would be no need to resize here.

> 


I'm not sure I understand. Where do you suggest the resizing to be placed?
Philippe Waroquiers via Gdb-patches June 24, 2021, 3:18 p.m. | #4
On 24 Jun 2021, at 15:37, Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>> wrote:

On 6/24/21 11:00 AM, Alan Hayward wrote:
On 1 Jun 2021, at 18:45, Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>> wrote:

Updates on v5:

- Fixed format warning output.

Updates on v4:

- Calculate sizes based on individual struct field sizes.

Updates on v3:

- Addressed review comments.
- New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.
- Updated code documentation.
- Removed code duplication.

Updates on v2:

- Reworked core_target::fetch_memtags to handle cases where address + len runs
 over the NT_MEMTAG note.
- Turned a few magic numbers into constants. There is an unfortunate duplication
 of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is
 no file generic enough that gets included by both corelow and linux-tdep.
- More sanity checks to make sure the note format is correct.
- Documented aarch64_linux_decode_memtag_note a little more.

---

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<mailto:luis.machado@linaro.org>>

* Makefile.in (COMMON_SFILES): Add memtag.c.
* NEWS: Mention core file support for memory tagging.
* aarch64-linux-tdep.c: Include elf/common.h.
Include gdbsupport/memtag.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.
(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.
* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.
(AARCH64_MTE_TAG_BIT_SIZE): New constant.
* corelow.c: Include gdbsupport/memtag.h and memtag.h.
(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: Include gdbsupport/memtag.h and memtag.h.
(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.
* memtag.c: New file.
* memtag.h: New file.

gdb/doc/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org<mailto: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<mailto:luis.machado@linaro.org>>

* gdb.arch/aarch64-mte-gcore.c: New file.
* gdb.arch/aarch64-mte-gcore.exp: New file.

gdbsupport/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org<mailto:luis.machado@linaro.org>>

* memtag.h: New file.
---
gdb/Makefile.in                              |   1 +
gdb/NEWS                                     |   4 +
gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++
gdb/arch/aarch64-mte-linux.h                 |  17 ++
gdb/corelow.c                                |  63 ++++++
gdb/doc/gdb.texinfo                          |   4 +
gdb/gdbarch.c                                |  64 ++++++
gdb/gdbarch.h                                |  16 ++
gdb/gdbarch.sh                               |   6 +
gdb/linux-tdep.c                             |  97 +++++++-
gdb/memtag.c                                 |  88 ++++++++
gdb/memtag.h                                 |  46 ++++
gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++
gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++
gdbsupport/memtag.h                          |  39 ++++
15 files changed, 867 insertions(+), 3 deletions(-)
create mode 100644 gdb/memtag.c
create mode 100644 gdb/memtag.h
create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c
create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
create mode 100644 gdbsupport/memtag.h

Note there were a few minor merge conflicts, nothing to worry about though.

I'll check again and will refresh the patch.

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f664d964536..12fb3b390b1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1100,6 +1100,7 @@ COMMON_SFILES = \
memattr.c \
memory-map.c \
memrange.c \
+ memtag.c \
minidebug.c \
minsyms.c \
mipsread.c \
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..04498f3b6c0 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -52,6 +52,9 @@
#include "value.h"

#include "gdbsupport/selftest.h"
+#include "gdbsupport/memtag.h"
+
+#include "elf/common.h"

/* Signal frame handling.

@@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
    }
}

+/* Memory tag note header size.  Includes both the generic and the
+   arch-specific parts.  */
+#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \
+      + NT_MEMTAG_MTE_HEADER_SIZE)
+
+/* 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.  */
+  size_t 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 (NT_MEMTAG_TOTAL_HEADER_SIZE + granules);
Should this be NT_MEMTAG_TOTAL_HEADER_SIZE + (granules * AARCH64_MTE_GRANULE_SIZE)
Get_granules has already divided by the granule size.

We are storing 1 tag per byte. So the number of granules is the number of tags. If we multiply by AARCH64_MTE_GRANULE_SIZE, we will have 16 times more tags.

Ahh, right.
(Of course - this sort of buffer overrun is exactly what mte prevents :) )


+
+  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);
If you do the original resize after the while loop, then there would be no need to resize here.

I'm not sure I understand. Where do you suggest the resizing to be placed?

So something like:

std::vector<gdb::byte_vector> notes (1);

while (granules > 0)
{
...
  If (!fetch())
   return notes
...
}

notes[0].resize (NT_MEMTAG_TOTAL_HEADER_SIZE + all_granules);

Now I write it out, you’ll need to keep a copy of the original number of granules.
Given this is only going to happen on a rare failure, then maybe its not worth the change.


Ok, happy with all those then.
Rest of the patch looks good to me too.


Alan.
Philippe Waroquiers via Gdb-patches July 1, 2021, 1:50 p.m. | #5
On 6/1/21 2:45 PM, Luis Machado wrote:
> Updates on v5:

> 

> - Fixed format warning output.

> 

> Updates on v4:

> 

> - Calculate sizes based on individual struct field sizes.

> 

> Updates on v3:

> 

> - Addressed review comments.

> - New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.

> - Updated code documentation.

> - Removed code duplication.

> 

> Updates on v2:

> 

> - Reworked core_target::fetch_memtags to handle cases where address + len runs

>    over the NT_MEMTAG note.

> - Turned a few magic numbers into constants. There is an unfortunate duplication

>    of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is

>    no file generic enough that gets included by both corelow and linux-tdep.

> - More sanity checks to make sure the note format is correct.

> - Documented aarch64_linux_decode_memtag_note a little more.

> 

> ---

> 

> 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>

> 

> 	* Makefile.in (COMMON_SFILES): Add memtag.c.

> 	* NEWS: Mention core file support for memory tagging.

> 	* aarch64-linux-tdep.c: Include elf/common.h.

> 	Include gdbsupport/memtag.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.

> 	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.

> 	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.

> 	(AARCH64_MTE_TAG_BIT_SIZE): New constant.

> 	* corelow.c: Include gdbsupport/memtag.h and memtag.h.

> 	(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: Include gdbsupport/memtag.h and memtag.h.

> 	(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.

> 	* memtag.c: New file.

> 	* memtag.h: New file.

> 

> 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.

> 

> gdbsupport/ChangeLog:

> 

> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

> 

> 	* memtag.h: New file.

> ---

>   gdb/Makefile.in                              |   1 +

>   gdb/NEWS                                     |   4 +

>   gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++

>   gdb/arch/aarch64-mte-linux.h                 |  17 ++

>   gdb/corelow.c                                |  63 ++++++

>   gdb/doc/gdb.texinfo                          |   4 +

>   gdb/gdbarch.c                                |  64 ++++++

>   gdb/gdbarch.h                                |  16 ++

>   gdb/gdbarch.sh                               |   6 +

>   gdb/linux-tdep.c                             |  97 +++++++-

>   gdb/memtag.c                                 |  88 ++++++++

>   gdb/memtag.h                                 |  46 ++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++

>   gdbsupport/memtag.h                          |  39 ++++

>   15 files changed, 867 insertions(+), 3 deletions(-)

>   create mode 100644 gdb/memtag.c

>   create mode 100644 gdb/memtag.h

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

>   create mode 100644 gdbsupport/memtag.h

> 

> diff --git a/gdb/Makefile.in b/gdb/Makefile.in

> index f664d964536..12fb3b390b1 100644

> --- a/gdb/Makefile.in

> +++ b/gdb/Makefile.in

> @@ -1100,6 +1100,7 @@ COMMON_SFILES = \

>   	memattr.c \

>   	memory-map.c \

>   	memrange.c \

> +	memtag.c \

>   	minidebug.c \

>   	minsyms.c \

>   	mipsread.c \

> 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..04498f3b6c0 100644

> --- a/gdb/aarch64-linux-tdep.c

> +++ b/gdb/aarch64-linux-tdep.c

> @@ -52,6 +52,9 @@

>   #include "value.h"

>   

>   #include "gdbsupport/selftest.h"

> +#include "gdbsupport/memtag.h"

> +

> +#include "elf/common.h"

>   

>   /* Signal frame handling.

>   

> @@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,

>       }

>   }

>   

> +/* Memory tag note header size.  Includes both the generic and the

> +   arch-specific parts.  */

> +#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \

> +				     + NT_MEMTAG_MTE_HEADER_SIZE)

> +

> +/* 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.  */

> +  size_t 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 (NT_MEMTAG_TOTAL_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 (tag_dump_header::format),

> +			  byte_order, NT_MEMTAG_TYPE_AARCH_MTE);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  /* Start address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +			  byte_order, start_address);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  /* End address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +			  byte_order, end_address);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* MTE-specific header.  */

> +  /* Granule byte size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::granule_byte_size),

> +			  byte_order, AARCH64_MTE_GRANULE_SIZE);

> +  buf += sizeof (tag_dump_mte::granule_byte_size);

> +

> +  /* Tag bit size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::tag_bit_size),

> +			  byte_order, AARCH64_MTE_TAG_BIT_SIZE);

> +  buf += sizeof (tag_dump_mte::tag_bit_size);

> +

> +  /* Unused value */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::__unused), byte_order, 0);

> +

> +  /* Store the tags.  */

> +  memcpy (notes[0].data () + NT_MEMTAG_TOTAL_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 requested tags.

> +

> +   The note is guaranteed to cover the [ADDRESS, ADDRESS + length)

> +   range.  */

> +

> +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;

> +

> +  /* Sanity check.  */

> +  if (note.size () < NT_MEMTAG_TOTAL_HEADER_SIZE)

> +    {

> +      warning (_("Malformed core note - too short for MTE header.\n"

> +		 "Expected %s bytes but got %s bytes."),

> +	       pulongest (NT_MEMTAG_TOTAL_HEADER_SIZE),

> +	       pulongest (note.size ()));

> +      return tags;

> +    }

> +

> +  /* The amount of memory tag granules we need to fetch.  */

> +  size_t 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;

> +

> +  /* Read the generic header.  */

> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

> +  const gdb_byte *buf = note.data ();

> +

> +  unsigned int format

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::format),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  CORE_ADDR start_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  CORE_ADDR end_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* Validate that ADDRESS + LENGTH doesn't fall outside of this note's

> +     range of addresses.  */

> +  gdb_assert (address + length < end_vma);

> +

> +  /* Is the tag header format correct for this note?  */

> +  if (format != NT_MEMTAG_TYPE_AARCH_MTE)

> +    {

> +      warning (_("Unexpected memory tag note format.\n"

> +		 "Expected %x but got %x."), NT_MEMTAG_TYPE_AARCH_MTE,

> +	       format);

> +      return tags;

> +    }

> +

> +  size_t expected_tag_bytes = (end_vma - start_vma)/AARCH64_MTE_GRANULE_SIZE;

> +

> +  /* Does the number of tag bytes in this note match the expected number

> +     of tag bytes the note says it has?  */

> +  if (note.size () < (NT_MEMTAG_TOTAL_HEADER_SIZE + expected_tag_bytes))

> +    {

> +      warning (_("Unexpected tag data size.\n"

> +		 "Expected %s but got %s."), pulongest (expected_tag_bytes),

> +	       pulongest (note.size () - NT_MEMTAG_TOTAL_HEADER_SIZE));

> +      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 (start_vma, AARCH64_MTE_GRANULE_SIZE);

> +  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);

> +

> +  size_t skipped_granules

> +    = aarch64_mte_get_tag_granules (aligned_start_address,

> +				    aligned_address - aligned_start_address,

> +				    AARCH64_MTE_GRANULE_SIZE);

> +

> +  /* Point to the block of data that contains the first granule we are

> +     interested in.  */

> +  const gdb::array_view<const gdb_byte> tags_data

> +    = note.slice (NT_MEMTAG_TOTAL_HEADER_SIZE + skipped_granules, granules);

> +

> +  /* Read the tag granules.  */

> +  for (size_t 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 +2072,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..7da9de4aefb 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,20 @@ 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);

>   

> +/* NT_MEMTAG header for MTE tags.  */

> +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;

> +};

> +

> +/* Size of the MTE header for a NT_MEMTAG note.  */

> +#define NT_MEMTAG_MTE_HEADER_SIZE (sizeof (tag_dump_mte::granule_byte_size) \

> +				   + sizeof (tag_dump_mte::tag_bit_size) \

> +				   + sizeof (tag_dump_mte::__unused))

> +

>   #endif /* ARCH_AARCH64_LINUX_H */

> diff --git a/gdb/corelow.c b/gdb/corelow.c

> index 452b4dd4f9a..24c5bf29a11 100644

> --- a/gdb/corelow.c

> +++ b/gdb/corelow.c

> @@ -51,6 +51,8 @@

>   #include "gdbcmd.h"

>   #include "xml-tdesc.h"

>   #include "observable.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #ifndef O_LARGEFILE

>   #define O_LARGEFILE 0

> @@ -100,6 +102,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 +1124,60 @@ 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.  */

> +

> +  return (bfd_get_section_by_name (core_bfd, ".memtag") != nullptr);

> +}

> +

> +/* 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))

> +    error (_("gdbarch_decode_memtag_note not implemented for this "

> +	     "architecture."));

> +

> +  memtag_note_info info;

> +  info.memtag_section = nullptr;

> +

> +  while (get_next_core_memtag_section (core_bfd, info.memtag_section,

> +				       address, info))

> +  {

> +    size_t adjusted_length

> +      = (address + len < info.end_address)? len : (info.end_address - address);

> +

> +    /* Decode the memory tag note and return the tags.  */

> +    gdb::byte_vector tags_read

> +      = gdbarch_decode_memtag_note (gdbarch, info.note, address,

> +				    adjusted_length);

> +

> +    /* Transfer over the tags that have been read.  */

> +    tags.insert (tags.end (), tags_read.begin (), tags_read.end ());

> +

> +    /* ADDRESS + LEN may cross the boundaries of a particular NT_MEMTAG

> +       note.  Check if we need to fetch tags from a different section.  */

> +    if (address + len < info.end_address)

> +      return true;

> +

> +    /* There are more tags to fetch.  Update ADDRESS and LEN.  */

> +    len -= (info.end_address - address);

> +    address = info.end_address;

> +  }

> +

> +  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 90d827a50e7..1b001e6cacb 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..6192cc4421b 100644

> --- a/gdb/linux-tdep.c

> +++ b/gdb/linux-tdep.c

> @@ -41,6 +41,8 @@

>   #include "gdbsupport/gdb_optional.h"

>   #include "gcore.h"

>   #include "gcore-elf.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #include <ctype.h>

>   

> @@ -1438,10 +1440,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 +1476,91 @@ 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;

> +

> +  memtag_note_info info;

> +  return get_next_core_memtag_section (core_bfd, nullptr, address, info);

> +}

> +

> +/* 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;

> +

> +  /* Parse the contents of smaps into a vector.  */

> +  std::vector<struct smaps_data> 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

> +	= gdbarch_create_memtag_notes_from_range (gdbarch,

> +						  map.start_address,

> +						  map.end_address);

> +      /* Write notes to the core file.  */

> +      for (const 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 +2139,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/memtag.c b/gdb/memtag.c

> new file mode 100644

> index 00000000000..4d92ecde84a

> --- /dev/null

> +++ b/gdb/memtag.c

> @@ -0,0 +1,88 @@

> +/* GDB generic memory tagging functions.

> +

> +   Copyright (C) 2021 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 "defs.h"

> +#include "memtag.h"

> +#include "gdbsupport/memtag.h"

> +#include "bfd.h"

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return a pointer to a .memtag section containing ADDRESS or nullptr

> +   of none are found.

> +

> +   If SECTION is provided, search from that section onwards.  */

> +

> +bool

> +get_next_core_memtag_section (bfd *abfd, asection *section,

> +			      CORE_ADDR address, memtag_note_info &info)

> +{

> +  /* If SECTION is nullptr, start a fresh lookup.  */

> +  if (section == nullptr)

> +    section = bfd_get_section_by_name (abfd, ".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 < NT_MEMTAG_GENERIC_HEADER_SIZE)

> +	{

> +	  warning (_("Malformed core note - too short for NT_MEMTAG generic "

> +		     "header.\n"

> +		     "Expected %s bytes but got %s bytes."),

> +		   pulongest (NT_MEMTAG_GENERIC_HEADER_SIZE),

> +		   pulongest (note_size));

> +	  return false;

> +	}

> +

> +      gdb::byte_vector note (note_size);

> +

> +      /* Fetch the contents of this particular memtag note.  */

> +      if (!bfd_get_section_contents (abfd, section,

> +				     note.data (), 0, note_size))

> +	{

> +	  warning (_("could not get core note contents."));

> +	  return false;

> +	}

> +

> +      /* Read the generic header of the note.  It contains the format,

> +	 start address and end address.  */

> +      uint64_t start_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format));

> +      uint64_t end_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format)

> +			    + sizeof (tag_dump_header::start_vma));

> +

> +      /* Is the address within [start_address, end_address)?  */

> +      if (address >= start_address

> +	  && address < end_address)

> +	{

> +	  info.start_address = start_address;

> +	  info.end_address = end_address;

> +	  info.note = note;

> +	  info.memtag_section = section;

> +	  return true;

> +	}

> +    }

> +  return false;

> +}

> diff --git a/gdb/memtag.h b/gdb/memtag.h

> new file mode 100644

> index 00000000000..43c9efb39a3

> --- /dev/null

> +++ b/gdb/memtag.h

> @@ -0,0 +1,46 @@

> +/* GDB generic memory tagging definitions.

> +   Copyright (C) 2021 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 MEMTAG_H

> +#define MEMTAG_H

> +

> +#include "bfd.h"

> +

> +struct memtag_note_info

> +{

> +  CORE_ADDR start_address;

> +  CORE_ADDR end_address;

> +  gdb::byte_vector note;

> +  asection *memtag_section;

> +};

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return TRUE if there is a .memtag section containing ADDRESS.  Return FALSE

> +   otherwise.

> +

> +   If SECTION is provided, search from that section onwards. If SECTION is

> +   nullptr, then start a new search.

> +

> +   If a .memtag section containing ADDRESS is found, fill INFO with data

> +   about such section.  Otherwise leave it unchanged.  */

> +

> +bool get_next_core_memtag_section (bfd *abfd, asection *section,

> +				   CORE_ADDR address, memtag_note_info &info);

> +

> +#endif /* MEMTAG_H */

> 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..d0bcd036972

> --- /dev/null

> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> @@ -0,0 +1,111 @@

> +# 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 $binfile

> +

> +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"

> +}

> diff --git a/gdbsupport/memtag.h b/gdbsupport/memtag.h

> new file mode 100644

> index 00000000000..bb47eed220b

> --- /dev/null

> +++ b/gdbsupport/memtag.h

> @@ -0,0 +1,39 @@

> +/* Generic memory tagging definitions.

> +   Copyright (C) 2021 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 GDBSUPPORT_MEMTAG_H

> +#define GDBSUPPORT_MEMTAG_H

> +

> +/* 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;

> +};

> +

> +/* Size of the generic header for the NT_MEMTAG note.  This is OS-independent

> +   and should be shared with OS-specific and arch-specific code.  */

> +#define NT_MEMTAG_GENERIC_HEADER_SIZE (sizeof (tag_dump_header::format) \

> +				       + sizeof (tag_dump_header::start_vma) \

> +				       + sizeof (tag_dump_header::end_vma))

> +

> +#endif /* GDBSUPPORT_MEMTAG_H */

>
Joel Brobecker July 11, 2021, 2:22 p.m. | #6
Hi everyone,

Luis is trying to have this new feature ready for the GDB 11.1
release. As I understand it, the AArch64-specific part has already
been approved, so he's waiting for someone to review the rest of
the changes. Is there a Global Maintainer who would feel comfortable
doing so and who would have the time?

Thank you!

On Thu, Jul 01, 2021 at 10:50:44AM -0300, Luis Machado via Gdb-patches wrote:
> 

> 

> On 6/1/21 2:45 PM, Luis Machado wrote:

> > Updates on v5:

> > 

> > - Fixed format warning output.

> > 

> > Updates on v4:

> > 

> > - Calculate sizes based on individual struct field sizes.

> > 

> > Updates on v3:

> > 

> > - Addressed review comments.

> > - New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.

> > - Updated code documentation.

> > - Removed code duplication.

> > 

> > Updates on v2:

> > 

> > - Reworked core_target::fetch_memtags to handle cases where address + len runs

> >    over the NT_MEMTAG note.

> > - Turned a few magic numbers into constants. There is an unfortunate duplication

> >    of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is

> >    no file generic enough that gets included by both corelow and linux-tdep.

> > - More sanity checks to make sure the note format is correct.

> > - Documented aarch64_linux_decode_memtag_note a little more.

> > 

> > ---

> > 

> > 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>

> > 

> > 	* Makefile.in (COMMON_SFILES): Add memtag.c.

> > 	* NEWS: Mention core file support for memory tagging.

> > 	* aarch64-linux-tdep.c: Include elf/common.h.

> > 	Include gdbsupport/memtag.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.

> > 	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.

> > 	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.

> > 	(AARCH64_MTE_TAG_BIT_SIZE): New constant.

> > 	* corelow.c: Include gdbsupport/memtag.h and memtag.h.

> > 	(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: Include gdbsupport/memtag.h and memtag.h.

> > 	(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.

> > 	* memtag.c: New file.

> > 	* memtag.h: New file.

> > 

> > 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.

> > 

> > gdbsupport/ChangeLog:

> > 

> > YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

> > 

> > 	* memtag.h: New file.

> > ---

> >   gdb/Makefile.in                              |   1 +

> >   gdb/NEWS                                     |   4 +

> >   gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++

> >   gdb/arch/aarch64-mte-linux.h                 |  17 ++

> >   gdb/corelow.c                                |  63 ++++++

> >   gdb/doc/gdb.texinfo                          |   4 +

> >   gdb/gdbarch.c                                |  64 ++++++

> >   gdb/gdbarch.h                                |  16 ++

> >   gdb/gdbarch.sh                               |   6 +

> >   gdb/linux-tdep.c                             |  97 +++++++-

> >   gdb/memtag.c                                 |  88 ++++++++

> >   gdb/memtag.h                                 |  46 ++++

> >   gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++

> >   gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++

> >   gdbsupport/memtag.h                          |  39 ++++

> >   15 files changed, 867 insertions(+), 3 deletions(-)

> >   create mode 100644 gdb/memtag.c

> >   create mode 100644 gdb/memtag.h

> >   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c

> >   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> >   create mode 100644 gdbsupport/memtag.h

> > 

> > diff --git a/gdb/Makefile.in b/gdb/Makefile.in

> > index f664d964536..12fb3b390b1 100644

> > --- a/gdb/Makefile.in

> > +++ b/gdb/Makefile.in

> > @@ -1100,6 +1100,7 @@ COMMON_SFILES = \

> >   	memattr.c \

> >   	memory-map.c \

> >   	memrange.c \

> > +	memtag.c \

> >   	minidebug.c \

> >   	minsyms.c \

> >   	mipsread.c \

> > 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..04498f3b6c0 100644

> > --- a/gdb/aarch64-linux-tdep.c

> > +++ b/gdb/aarch64-linux-tdep.c

> > @@ -52,6 +52,9 @@

> >   #include "value.h"

> >   #include "gdbsupport/selftest.h"

> > +#include "gdbsupport/memtag.h"

> > +

> > +#include "elf/common.h"

> >   /* Signal frame handling.

> > @@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,

> >       }

> >   }

> > +/* Memory tag note header size.  Includes both the generic and the

> > +   arch-specific parts.  */

> > +#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \

> > +				     + NT_MEMTAG_MTE_HEADER_SIZE)

> > +

> > +/* 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.  */

> > +  size_t 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 (NT_MEMTAG_TOTAL_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 (tag_dump_header::format),

> > +			  byte_order, NT_MEMTAG_TYPE_AARCH_MTE);

> > +  buf += sizeof (tag_dump_header::format);

> > +

> > +  /* Start address */

> > +  store_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> > +			  byte_order, start_address);

> > +  buf += sizeof (tag_dump_header::start_vma);

> > +

> > +  /* End address */

> > +  store_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> > +			  byte_order, end_address);

> > +  buf += sizeof (tag_dump_header::end_vma);

> > +

> > +  /* MTE-specific header.  */

> > +  /* Granule byte size */

> > +  store_unsigned_integer (buf, sizeof (tag_dump_mte::granule_byte_size),

> > +			  byte_order, AARCH64_MTE_GRANULE_SIZE);

> > +  buf += sizeof (tag_dump_mte::granule_byte_size);

> > +

> > +  /* Tag bit size */

> > +  store_unsigned_integer (buf, sizeof (tag_dump_mte::tag_bit_size),

> > +			  byte_order, AARCH64_MTE_TAG_BIT_SIZE);

> > +  buf += sizeof (tag_dump_mte::tag_bit_size);

> > +

> > +  /* Unused value */

> > +  store_unsigned_integer (buf, sizeof (tag_dump_mte::__unused), byte_order, 0);

> > +

> > +  /* Store the tags.  */

> > +  memcpy (notes[0].data () + NT_MEMTAG_TOTAL_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 requested tags.

> > +

> > +   The note is guaranteed to cover the [ADDRESS, ADDRESS + length)

> > +   range.  */

> > +

> > +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;

> > +

> > +  /* Sanity check.  */

> > +  if (note.size () < NT_MEMTAG_TOTAL_HEADER_SIZE)

> > +    {

> > +      warning (_("Malformed core note - too short for MTE header.\n"

> > +		 "Expected %s bytes but got %s bytes."),

> > +	       pulongest (NT_MEMTAG_TOTAL_HEADER_SIZE),

> > +	       pulongest (note.size ()));

> > +      return tags;

> > +    }

> > +

> > +  /* The amount of memory tag granules we need to fetch.  */

> > +  size_t 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;

> > +

> > +  /* Read the generic header.  */

> > +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

> > +  const gdb_byte *buf = note.data ();

> > +

> > +  unsigned int format

> > +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::format),

> > +				byte_order);

> > +  buf += sizeof (tag_dump_header::format);

> > +

> > +  CORE_ADDR start_vma

> > +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> > +				byte_order);

> > +  buf += sizeof (tag_dump_header::start_vma);

> > +

> > +  CORE_ADDR end_vma

> > +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> > +				byte_order);

> > +  buf += sizeof (tag_dump_header::end_vma);

> > +

> > +  /* Validate that ADDRESS + LENGTH doesn't fall outside of this note's

> > +     range of addresses.  */

> > +  gdb_assert (address + length < end_vma);

> > +

> > +  /* Is the tag header format correct for this note?  */

> > +  if (format != NT_MEMTAG_TYPE_AARCH_MTE)

> > +    {

> > +      warning (_("Unexpected memory tag note format.\n"

> > +		 "Expected %x but got %x."), NT_MEMTAG_TYPE_AARCH_MTE,

> > +	       format);

> > +      return tags;

> > +    }

> > +

> > +  size_t expected_tag_bytes = (end_vma - start_vma)/AARCH64_MTE_GRANULE_SIZE;

> > +

> > +  /* Does the number of tag bytes in this note match the expected number

> > +     of tag bytes the note says it has?  */

> > +  if (note.size () < (NT_MEMTAG_TOTAL_HEADER_SIZE + expected_tag_bytes))

> > +    {

> > +      warning (_("Unexpected tag data size.\n"

> > +		 "Expected %s but got %s."), pulongest (expected_tag_bytes),

> > +	       pulongest (note.size () - NT_MEMTAG_TOTAL_HEADER_SIZE));

> > +      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 (start_vma, AARCH64_MTE_GRANULE_SIZE);

> > +  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);

> > +

> > +  size_t skipped_granules

> > +    = aarch64_mte_get_tag_granules (aligned_start_address,

> > +				    aligned_address - aligned_start_address,

> > +				    AARCH64_MTE_GRANULE_SIZE);

> > +

> > +  /* Point to the block of data that contains the first granule we are

> > +     interested in.  */

> > +  const gdb::array_view<const gdb_byte> tags_data

> > +    = note.slice (NT_MEMTAG_TOTAL_HEADER_SIZE + skipped_granules, granules);

> > +

> > +  /* Read the tag granules.  */

> > +  for (size_t 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 +2072,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..7da9de4aefb 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,20 @@ 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);

> > +/* NT_MEMTAG header for MTE tags.  */

> > +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;

> > +};

> > +

> > +/* Size of the MTE header for a NT_MEMTAG note.  */

> > +#define NT_MEMTAG_MTE_HEADER_SIZE (sizeof (tag_dump_mte::granule_byte_size) \

> > +				   + sizeof (tag_dump_mte::tag_bit_size) \

> > +				   + sizeof (tag_dump_mte::__unused))

> > +

> >   #endif /* ARCH_AARCH64_LINUX_H */

> > diff --git a/gdb/corelow.c b/gdb/corelow.c

> > index 452b4dd4f9a..24c5bf29a11 100644

> > --- a/gdb/corelow.c

> > +++ b/gdb/corelow.c

> > @@ -51,6 +51,8 @@

> >   #include "gdbcmd.h"

> >   #include "xml-tdesc.h"

> >   #include "observable.h"

> > +#include "gdbsupport/memtag.h"

> > +#include "memtag.h"

> >   #ifndef O_LARGEFILE

> >   #define O_LARGEFILE 0

> > @@ -100,6 +102,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 +1124,60 @@ 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.  */

> > +

> > +  return (bfd_get_section_by_name (core_bfd, ".memtag") != nullptr);

> > +}

> > +

> > +/* 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))

> > +    error (_("gdbarch_decode_memtag_note not implemented for this "

> > +	     "architecture."));

> > +

> > +  memtag_note_info info;

> > +  info.memtag_section = nullptr;

> > +

> > +  while (get_next_core_memtag_section (core_bfd, info.memtag_section,

> > +				       address, info))

> > +  {

> > +    size_t adjusted_length

> > +      = (address + len < info.end_address)? len : (info.end_address - address);

> > +

> > +    /* Decode the memory tag note and return the tags.  */

> > +    gdb::byte_vector tags_read

> > +      = gdbarch_decode_memtag_note (gdbarch, info.note, address,

> > +				    adjusted_length);

> > +

> > +    /* Transfer over the tags that have been read.  */

> > +    tags.insert (tags.end (), tags_read.begin (), tags_read.end ());

> > +

> > +    /* ADDRESS + LEN may cross the boundaries of a particular NT_MEMTAG

> > +       note.  Check if we need to fetch tags from a different section.  */

> > +    if (address + len < info.end_address)

> > +      return true;

> > +

> > +    /* There are more tags to fetch.  Update ADDRESS and LEN.  */

> > +    len -= (info.end_address - address);

> > +    address = info.end_address;

> > +  }

> > +

> > +  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 90d827a50e7..1b001e6cacb 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..6192cc4421b 100644

> > --- a/gdb/linux-tdep.c

> > +++ b/gdb/linux-tdep.c

> > @@ -41,6 +41,8 @@

> >   #include "gdbsupport/gdb_optional.h"

> >   #include "gcore.h"

> >   #include "gcore-elf.h"

> > +#include "gdbsupport/memtag.h"

> > +#include "memtag.h"

> >   #include <ctype.h>

> > @@ -1438,10 +1440,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 +1476,91 @@ 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;

> > +

> > +  memtag_note_info info;

> > +  return get_next_core_memtag_section (core_bfd, nullptr, address, info);

> > +}

> > +

> > +/* 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;

> > +

> > +  /* Parse the contents of smaps into a vector.  */

> > +  std::vector<struct smaps_data> 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

> > +	= gdbarch_create_memtag_notes_from_range (gdbarch,

> > +						  map.start_address,

> > +						  map.end_address);

> > +      /* Write notes to the core file.  */

> > +      for (const 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 +2139,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/memtag.c b/gdb/memtag.c

> > new file mode 100644

> > index 00000000000..4d92ecde84a

> > --- /dev/null

> > +++ b/gdb/memtag.c

> > @@ -0,0 +1,88 @@

> > +/* GDB generic memory tagging functions.

> > +

> > +   Copyright (C) 2021 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 "defs.h"

> > +#include "memtag.h"

> > +#include "gdbsupport/memtag.h"

> > +#include "bfd.h"

> > +

> > +/* Helper function to walk through NT_MEMTAG notes in a core file.

> > +

> > +   Return a pointer to a .memtag section containing ADDRESS or nullptr

> > +   of none are found.

> > +

> > +   If SECTION is provided, search from that section onwards.  */

> > +

> > +bool

> > +get_next_core_memtag_section (bfd *abfd, asection *section,

> > +			      CORE_ADDR address, memtag_note_info &info)

> > +{

> > +  /* If SECTION is nullptr, start a fresh lookup.  */

> > +  if (section == nullptr)

> > +    section = bfd_get_section_by_name (abfd, ".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 < NT_MEMTAG_GENERIC_HEADER_SIZE)

> > +	{

> > +	  warning (_("Malformed core note - too short for NT_MEMTAG generic "

> > +		     "header.\n"

> > +		     "Expected %s bytes but got %s bytes."),

> > +		   pulongest (NT_MEMTAG_GENERIC_HEADER_SIZE),

> > +		   pulongest (note_size));

> > +	  return false;

> > +	}

> > +

> > +      gdb::byte_vector note (note_size);

> > +

> > +      /* Fetch the contents of this particular memtag note.  */

> > +      if (!bfd_get_section_contents (abfd, section,

> > +				     note.data (), 0, note_size))

> > +	{

> > +	  warning (_("could not get core note contents."));

> > +	  return false;

> > +	}

> > +

> > +      /* Read the generic header of the note.  It contains the format,

> > +	 start address and end address.  */

> > +      uint64_t start_address

> > +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format));

> > +      uint64_t end_address

> > +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format)

> > +			    + sizeof (tag_dump_header::start_vma));

> > +

> > +      /* Is the address within [start_address, end_address)?  */

> > +      if (address >= start_address

> > +	  && address < end_address)

> > +	{

> > +	  info.start_address = start_address;

> > +	  info.end_address = end_address;

> > +	  info.note = note;

> > +	  info.memtag_section = section;

> > +	  return true;

> > +	}

> > +    }

> > +  return false;

> > +}

> > diff --git a/gdb/memtag.h b/gdb/memtag.h

> > new file mode 100644

> > index 00000000000..43c9efb39a3

> > --- /dev/null

> > +++ b/gdb/memtag.h

> > @@ -0,0 +1,46 @@

> > +/* GDB generic memory tagging definitions.

> > +   Copyright (C) 2021 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 MEMTAG_H

> > +#define MEMTAG_H

> > +

> > +#include "bfd.h"

> > +

> > +struct memtag_note_info

> > +{

> > +  CORE_ADDR start_address;

> > +  CORE_ADDR end_address;

> > +  gdb::byte_vector note;

> > +  asection *memtag_section;

> > +};

> > +

> > +/* Helper function to walk through NT_MEMTAG notes in a core file.

> > +

> > +   Return TRUE if there is a .memtag section containing ADDRESS.  Return FALSE

> > +   otherwise.

> > +

> > +   If SECTION is provided, search from that section onwards. If SECTION is

> > +   nullptr, then start a new search.

> > +

> > +   If a .memtag section containing ADDRESS is found, fill INFO with data

> > +   about such section.  Otherwise leave it unchanged.  */

> > +

> > +bool get_next_core_memtag_section (bfd *abfd, asection *section,

> > +				   CORE_ADDR address, memtag_note_info &info);

> > +

> > +#endif /* MEMTAG_H */

> > 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..d0bcd036972

> > --- /dev/null

> > +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> > @@ -0,0 +1,111 @@

> > +# 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 $binfile

> > +

> > +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"

> > +}

> > diff --git a/gdbsupport/memtag.h b/gdbsupport/memtag.h

> > new file mode 100644

> > index 00000000000..bb47eed220b

> > --- /dev/null

> > +++ b/gdbsupport/memtag.h

> > @@ -0,0 +1,39 @@

> > +/* Generic memory tagging definitions.

> > +   Copyright (C) 2021 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 GDBSUPPORT_MEMTAG_H

> > +#define GDBSUPPORT_MEMTAG_H

> > +

> > +/* 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;

> > +};

> > +

> > +/* Size of the generic header for the NT_MEMTAG note.  This is OS-independent

> > +   and should be shared with OS-specific and arch-specific code.  */

> > +#define NT_MEMTAG_GENERIC_HEADER_SIZE (sizeof (tag_dump_header::format) \

> > +				       + sizeof (tag_dump_header::start_vma) \

> > +				       + sizeof (tag_dump_header::end_vma))

> > +

> > +#endif /* GDBSUPPORT_MEMTAG_H */

> > 


-- 
Joel
Philippe Waroquiers via Gdb-patches July 14, 2021, 1:07 p.m. | #7
On Sun, Jul 11, 2021 at 07:22:00AM -0700, Joel Brobecker wrote:
> Luis is trying to have this new feature ready for the GDB 11.1

> release. As I understand it, the AArch64-specific part has already

> been approved, so he's waiting for someone to review the rest of

> the changes. Is there a Global Maintainer who would feel comfortable

> doing so and who would have the time?


I'm a Linux kernel maintainer, so not much for me to review on the gdb
front. But feedback from the gdb maintainers would be appreciated.

W.r.t. upstreaming the feature in gdb, I'd wait until the Linux
counterpart is merged. We agreed in principle on the ABI but I wouldn't
consider it stable until the feature hits a kernel release.

-- 
Catalin
Philippe Waroquiers via Gdb-patches July 19, 2021, 7:05 p.m. | #8
On 6/1/21 2:45 PM, Luis Machado wrote:
> Updates on v5:

> 

> - Fixed format warning output.

> 

> Updates on v4:

> 

> - Calculate sizes based on individual struct field sizes.

> 

> Updates on v3:

> 

> - Addressed review comments.

> - New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.

> - Updated code documentation.

> - Removed code duplication.

> 

> Updates on v2:

> 

> - Reworked core_target::fetch_memtags to handle cases where address + len runs

>    over the NT_MEMTAG note.

> - Turned a few magic numbers into constants. There is an unfortunate duplication

>    of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is

>    no file generic enough that gets included by both corelow and linux-tdep.

> - More sanity checks to make sure the note format is correct.

> - Documented aarch64_linux_decode_memtag_note a little more.

> 

> ---

> 

> 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>

> 

> 	* Makefile.in (COMMON_SFILES): Add memtag.c.

> 	* NEWS: Mention core file support for memory tagging.

> 	* aarch64-linux-tdep.c: Include elf/common.h.

> 	Include gdbsupport/memtag.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.

> 	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.

> 	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.

> 	(AARCH64_MTE_TAG_BIT_SIZE): New constant.

> 	* corelow.c: Include gdbsupport/memtag.h and memtag.h.

> 	(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: Include gdbsupport/memtag.h and memtag.h.

> 	(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.

> 	* memtag.c: New file.

> 	* memtag.h: New file.

> 

> 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.

> 

> gdbsupport/ChangeLog:

> 

> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

> 

> 	* memtag.h: New file.

> ---

>   gdb/Makefile.in                              |   1 +

>   gdb/NEWS                                     |   4 +

>   gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++

>   gdb/arch/aarch64-mte-linux.h                 |  17 ++

>   gdb/corelow.c                                |  63 ++++++

>   gdb/doc/gdb.texinfo                          |   4 +

>   gdb/gdbarch.c                                |  64 ++++++

>   gdb/gdbarch.h                                |  16 ++

>   gdb/gdbarch.sh                               |   6 +

>   gdb/linux-tdep.c                             |  97 +++++++-

>   gdb/memtag.c                                 |  88 ++++++++

>   gdb/memtag.h                                 |  46 ++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++

>   gdbsupport/memtag.h                          |  39 ++++

>   15 files changed, 867 insertions(+), 3 deletions(-)

>   create mode 100644 gdb/memtag.c

>   create mode 100644 gdb/memtag.h

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

>   create mode 100644 gdbsupport/memtag.h

> 

> diff --git a/gdb/Makefile.in b/gdb/Makefile.in

> index f664d964536..12fb3b390b1 100644

> --- a/gdb/Makefile.in

> +++ b/gdb/Makefile.in

> @@ -1100,6 +1100,7 @@ COMMON_SFILES = \

>   	memattr.c \

>   	memory-map.c \

>   	memrange.c \

> +	memtag.c \

>   	minidebug.c \

>   	minsyms.c \

>   	mipsread.c \

> 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..04498f3b6c0 100644

> --- a/gdb/aarch64-linux-tdep.c

> +++ b/gdb/aarch64-linux-tdep.c

> @@ -52,6 +52,9 @@

>   #include "value.h"

>   

>   #include "gdbsupport/selftest.h"

> +#include "gdbsupport/memtag.h"

> +

> +#include "elf/common.h"

>   

>   /* Signal frame handling.

>   

> @@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,

>       }

>   }

>   

> +/* Memory tag note header size.  Includes both the generic and the

> +   arch-specific parts.  */

> +#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \

> +				     + NT_MEMTAG_MTE_HEADER_SIZE)

> +

> +/* 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.  */

> +  size_t 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 (NT_MEMTAG_TOTAL_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 (tag_dump_header::format),

> +			  byte_order, NT_MEMTAG_TYPE_AARCH_MTE);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  /* Start address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +			  byte_order, start_address);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  /* End address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +			  byte_order, end_address);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* MTE-specific header.  */

> +  /* Granule byte size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::granule_byte_size),

> +			  byte_order, AARCH64_MTE_GRANULE_SIZE);

> +  buf += sizeof (tag_dump_mte::granule_byte_size);

> +

> +  /* Tag bit size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::tag_bit_size),

> +			  byte_order, AARCH64_MTE_TAG_BIT_SIZE);

> +  buf += sizeof (tag_dump_mte::tag_bit_size);

> +

> +  /* Unused value */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::__unused), byte_order, 0);

> +

> +  /* Store the tags.  */

> +  memcpy (notes[0].data () + NT_MEMTAG_TOTAL_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 requested tags.

> +

> +   The note is guaranteed to cover the [ADDRESS, ADDRESS + length)

> +   range.  */

> +

> +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;

> +

> +  /* Sanity check.  */

> +  if (note.size () < NT_MEMTAG_TOTAL_HEADER_SIZE)

> +    {

> +      warning (_("Malformed core note - too short for MTE header.\n"

> +		 "Expected %s bytes but got %s bytes."),

> +	       pulongest (NT_MEMTAG_TOTAL_HEADER_SIZE),

> +	       pulongest (note.size ()));

> +      return tags;

> +    }

> +

> +  /* The amount of memory tag granules we need to fetch.  */

> +  size_t 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;

> +

> +  /* Read the generic header.  */

> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

> +  const gdb_byte *buf = note.data ();

> +

> +  unsigned int format

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::format),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  CORE_ADDR start_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  CORE_ADDR end_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* Validate that ADDRESS + LENGTH doesn't fall outside of this note's

> +     range of addresses.  */

> +  gdb_assert (address + length < end_vma);

> +

> +  /* Is the tag header format correct for this note?  */

> +  if (format != NT_MEMTAG_TYPE_AARCH_MTE)

> +    {

> +      warning (_("Unexpected memory tag note format.\n"

> +		 "Expected %x but got %x."), NT_MEMTAG_TYPE_AARCH_MTE,

> +	       format);

> +      return tags;

> +    }

> +

> +  size_t expected_tag_bytes = (end_vma - start_vma)/AARCH64_MTE_GRANULE_SIZE;

> +

> +  /* Does the number of tag bytes in this note match the expected number

> +     of tag bytes the note says it has?  */

> +  if (note.size () < (NT_MEMTAG_TOTAL_HEADER_SIZE + expected_tag_bytes))

> +    {

> +      warning (_("Unexpected tag data size.\n"

> +		 "Expected %s but got %s."), pulongest (expected_tag_bytes),

> +	       pulongest (note.size () - NT_MEMTAG_TOTAL_HEADER_SIZE));

> +      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 (start_vma, AARCH64_MTE_GRANULE_SIZE);

> +  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);

> +

> +  size_t skipped_granules

> +    = aarch64_mte_get_tag_granules (aligned_start_address,

> +				    aligned_address - aligned_start_address,

> +				    AARCH64_MTE_GRANULE_SIZE);

> +

> +  /* Point to the block of data that contains the first granule we are

> +     interested in.  */

> +  const gdb::array_view<const gdb_byte> tags_data

> +    = note.slice (NT_MEMTAG_TOTAL_HEADER_SIZE + skipped_granules, granules);

> +

> +  /* Read the tag granules.  */

> +  for (size_t 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 +2072,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..7da9de4aefb 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,20 @@ 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);

>   

> +/* NT_MEMTAG header for MTE tags.  */

> +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;

> +};

> +

> +/* Size of the MTE header for a NT_MEMTAG note.  */

> +#define NT_MEMTAG_MTE_HEADER_SIZE (sizeof (tag_dump_mte::granule_byte_size) \

> +				   + sizeof (tag_dump_mte::tag_bit_size) \

> +				   + sizeof (tag_dump_mte::__unused))

> +

>   #endif /* ARCH_AARCH64_LINUX_H */

> diff --git a/gdb/corelow.c b/gdb/corelow.c

> index 452b4dd4f9a..24c5bf29a11 100644

> --- a/gdb/corelow.c

> +++ b/gdb/corelow.c

> @@ -51,6 +51,8 @@

>   #include "gdbcmd.h"

>   #include "xml-tdesc.h"

>   #include "observable.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #ifndef O_LARGEFILE

>   #define O_LARGEFILE 0

> @@ -100,6 +102,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 +1124,60 @@ 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.  */

> +

> +  return (bfd_get_section_by_name (core_bfd, ".memtag") != nullptr);

> +}

> +

> +/* 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))

> +    error (_("gdbarch_decode_memtag_note not implemented for this "

> +	     "architecture."));

> +

> +  memtag_note_info info;

> +  info.memtag_section = nullptr;

> +

> +  while (get_next_core_memtag_section (core_bfd, info.memtag_section,

> +				       address, info))

> +  {

> +    size_t adjusted_length

> +      = (address + len < info.end_address)? len : (info.end_address - address);

> +

> +    /* Decode the memory tag note and return the tags.  */

> +    gdb::byte_vector tags_read

> +      = gdbarch_decode_memtag_note (gdbarch, info.note, address,

> +				    adjusted_length);

> +

> +    /* Transfer over the tags that have been read.  */

> +    tags.insert (tags.end (), tags_read.begin (), tags_read.end ());

> +

> +    /* ADDRESS + LEN may cross the boundaries of a particular NT_MEMTAG

> +       note.  Check if we need to fetch tags from a different section.  */

> +    if (address + len < info.end_address)

> +      return true;

> +

> +    /* There are more tags to fetch.  Update ADDRESS and LEN.  */

> +    len -= (info.end_address - address);

> +    address = info.end_address;

> +  }

> +

> +  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 90d827a50e7..1b001e6cacb 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..6192cc4421b 100644

> --- a/gdb/linux-tdep.c

> +++ b/gdb/linux-tdep.c

> @@ -41,6 +41,8 @@

>   #include "gdbsupport/gdb_optional.h"

>   #include "gcore.h"

>   #include "gcore-elf.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #include <ctype.h>

>   

> @@ -1438,10 +1440,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 +1476,91 @@ 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;

> +

> +  memtag_note_info info;

> +  return get_next_core_memtag_section (core_bfd, nullptr, address, info);

> +}

> +

> +/* 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;

> +

> +  /* Parse the contents of smaps into a vector.  */

> +  std::vector<struct smaps_data> 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

> +	= gdbarch_create_memtag_notes_from_range (gdbarch,

> +						  map.start_address,

> +						  map.end_address);

> +      /* Write notes to the core file.  */

> +      for (const 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 +2139,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/memtag.c b/gdb/memtag.c

> new file mode 100644

> index 00000000000..4d92ecde84a

> --- /dev/null

> +++ b/gdb/memtag.c

> @@ -0,0 +1,88 @@

> +/* GDB generic memory tagging functions.

> +

> +   Copyright (C) 2021 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 "defs.h"

> +#include "memtag.h"

> +#include "gdbsupport/memtag.h"

> +#include "bfd.h"

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return a pointer to a .memtag section containing ADDRESS or nullptr

> +   of none are found.

> +

> +   If SECTION is provided, search from that section onwards.  */

> +

> +bool

> +get_next_core_memtag_section (bfd *abfd, asection *section,

> +			      CORE_ADDR address, memtag_note_info &info)

> +{

> +  /* If SECTION is nullptr, start a fresh lookup.  */

> +  if (section == nullptr)

> +    section = bfd_get_section_by_name (abfd, ".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 < NT_MEMTAG_GENERIC_HEADER_SIZE)

> +	{

> +	  warning (_("Malformed core note - too short for NT_MEMTAG generic "

> +		     "header.\n"

> +		     "Expected %s bytes but got %s bytes."),

> +		   pulongest (NT_MEMTAG_GENERIC_HEADER_SIZE),

> +		   pulongest (note_size));

> +	  return false;

> +	}

> +

> +      gdb::byte_vector note (note_size);

> +

> +      /* Fetch the contents of this particular memtag note.  */

> +      if (!bfd_get_section_contents (abfd, section,

> +				     note.data (), 0, note_size))

> +	{

> +	  warning (_("could not get core note contents."));

> +	  return false;

> +	}

> +

> +      /* Read the generic header of the note.  It contains the format,

> +	 start address and end address.  */

> +      uint64_t start_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format));

> +      uint64_t end_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format)

> +			    + sizeof (tag_dump_header::start_vma));

> +

> +      /* Is the address within [start_address, end_address)?  */

> +      if (address >= start_address

> +	  && address < end_address)

> +	{

> +	  info.start_address = start_address;

> +	  info.end_address = end_address;

> +	  info.note = note;

> +	  info.memtag_section = section;

> +	  return true;

> +	}

> +    }

> +  return false;

> +}

> diff --git a/gdb/memtag.h b/gdb/memtag.h

> new file mode 100644

> index 00000000000..43c9efb39a3

> --- /dev/null

> +++ b/gdb/memtag.h

> @@ -0,0 +1,46 @@

> +/* GDB generic memory tagging definitions.

> +   Copyright (C) 2021 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 MEMTAG_H

> +#define MEMTAG_H

> +

> +#include "bfd.h"

> +

> +struct memtag_note_info

> +{

> +  CORE_ADDR start_address;

> +  CORE_ADDR end_address;

> +  gdb::byte_vector note;

> +  asection *memtag_section;

> +};

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return TRUE if there is a .memtag section containing ADDRESS.  Return FALSE

> +   otherwise.

> +

> +   If SECTION is provided, search from that section onwards. If SECTION is

> +   nullptr, then start a new search.

> +

> +   If a .memtag section containing ADDRESS is found, fill INFO with data

> +   about such section.  Otherwise leave it unchanged.  */

> +

> +bool get_next_core_memtag_section (bfd *abfd, asection *section,

> +				   CORE_ADDR address, memtag_note_info &info);

> +

> +#endif /* MEMTAG_H */

> 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..d0bcd036972

> --- /dev/null

> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> @@ -0,0 +1,111 @@

> +# 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 $binfile

> +

> +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"

> +}

> diff --git a/gdbsupport/memtag.h b/gdbsupport/memtag.h

> new file mode 100644

> index 00000000000..bb47eed220b

> --- /dev/null

> +++ b/gdbsupport/memtag.h

> @@ -0,0 +1,39 @@

> +/* Generic memory tagging definitions.

> +   Copyright (C) 2021 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 GDBSUPPORT_MEMTAG_H

> +#define GDBSUPPORT_MEMTAG_H

> +

> +/* 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;

> +};

> +

> +/* Size of the generic header for the NT_MEMTAG note.  This is OS-independent

> +   and should be shared with OS-specific and arch-specific code.  */

> +#define NT_MEMTAG_GENERIC_HEADER_SIZE (sizeof (tag_dump_header::format) \

> +				       + sizeof (tag_dump_header::start_vma) \

> +				       + sizeof (tag_dump_header::end_vma))

> +

> +#endif /* GDBSUPPORT_MEMTAG_H */

>
Philippe Waroquiers via Gdb-patches July 27, 2021, 4:10 p.m. | #9
On 6/1/21 2:45 PM, Luis Machado wrote:
> Updates on v5:

> 

> - Fixed format warning output.

> 

> Updates on v4:

> 

> - Calculate sizes based on individual struct field sizes.

> 

> Updates on v3:

> 

> - Addressed review comments.

> - New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.

> - Updated code documentation.

> - Removed code duplication.

> 

> Updates on v2:

> 

> - Reworked core_target::fetch_memtags to handle cases where address + len runs

>    over the NT_MEMTAG note.

> - Turned a few magic numbers into constants. There is an unfortunate duplication

>    of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is

>    no file generic enough that gets included by both corelow and linux-tdep.

> - More sanity checks to make sure the note format is correct.

> - Documented aarch64_linux_decode_memtag_note a little more.

> 

> ---

> 

> 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>

> 

> 	* Makefile.in (COMMON_SFILES): Add memtag.c.

> 	* NEWS: Mention core file support for memory tagging.

> 	* aarch64-linux-tdep.c: Include elf/common.h.

> 	Include gdbsupport/memtag.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.

> 	(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.

> 	* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.

> 	(AARCH64_MTE_TAG_BIT_SIZE): New constant.

> 	* corelow.c: Include gdbsupport/memtag.h and memtag.h.

> 	(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: Include gdbsupport/memtag.h and memtag.h.

> 	(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.

> 	* memtag.c: New file.

> 	* memtag.h: New file.

> 

> 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.

> 

> gdbsupport/ChangeLog:

> 

> YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

> 

> 	* memtag.h: New file.

> ---

>   gdb/Makefile.in                              |   1 +

>   gdb/NEWS                                     |   4 +

>   gdb/aarch64-linux-tdep.c                     | 221 +++++++++++++++++++

>   gdb/arch/aarch64-mte-linux.h                 |  17 ++

>   gdb/corelow.c                                |  63 ++++++

>   gdb/doc/gdb.texinfo                          |   4 +

>   gdb/gdbarch.c                                |  64 ++++++

>   gdb/gdbarch.h                                |  16 ++

>   gdb/gdbarch.sh                               |   6 +

>   gdb/linux-tdep.c                             |  97 +++++++-

>   gdb/memtag.c                                 |  88 ++++++++

>   gdb/memtag.h                                 |  46 ++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93 ++++++++

>   gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++

>   gdbsupport/memtag.h                          |  39 ++++

>   15 files changed, 867 insertions(+), 3 deletions(-)

>   create mode 100644 gdb/memtag.c

>   create mode 100644 gdb/memtag.h

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c

>   create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

>   create mode 100644 gdbsupport/memtag.h

> 

> diff --git a/gdb/Makefile.in b/gdb/Makefile.in

> index f664d964536..12fb3b390b1 100644

> --- a/gdb/Makefile.in

> +++ b/gdb/Makefile.in

> @@ -1100,6 +1100,7 @@ COMMON_SFILES = \

>   	memattr.c \

>   	memory-map.c \

>   	memrange.c \

> +	memtag.c \

>   	minidebug.c \

>   	minsyms.c \

>   	mipsread.c \

> 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..04498f3b6c0 100644

> --- a/gdb/aarch64-linux-tdep.c

> +++ b/gdb/aarch64-linux-tdep.c

> @@ -52,6 +52,9 @@

>   #include "value.h"

>   

>   #include "gdbsupport/selftest.h"

> +#include "gdbsupport/memtag.h"

> +

> +#include "elf/common.h"

>   

>   /* Signal frame handling.

>   

> @@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,

>       }

>   }

>   

> +/* Memory tag note header size.  Includes both the generic and the

> +   arch-specific parts.  */

> +#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \

> +				     + NT_MEMTAG_MTE_HEADER_SIZE)

> +

> +/* 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.  */

> +  size_t 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 (NT_MEMTAG_TOTAL_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 (tag_dump_header::format),

> +			  byte_order, NT_MEMTAG_TYPE_AARCH_MTE);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  /* Start address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +			  byte_order, start_address);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  /* End address */

> +  store_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +			  byte_order, end_address);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* MTE-specific header.  */

> +  /* Granule byte size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::granule_byte_size),

> +			  byte_order, AARCH64_MTE_GRANULE_SIZE);

> +  buf += sizeof (tag_dump_mte::granule_byte_size);

> +

> +  /* Tag bit size */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::tag_bit_size),

> +			  byte_order, AARCH64_MTE_TAG_BIT_SIZE);

> +  buf += sizeof (tag_dump_mte::tag_bit_size);

> +

> +  /* Unused value */

> +  store_unsigned_integer (buf, sizeof (tag_dump_mte::__unused), byte_order, 0);

> +

> +  /* Store the tags.  */

> +  memcpy (notes[0].data () + NT_MEMTAG_TOTAL_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 requested tags.

> +

> +   The note is guaranteed to cover the [ADDRESS, ADDRESS + length)

> +   range.  */

> +

> +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;

> +

> +  /* Sanity check.  */

> +  if (note.size () < NT_MEMTAG_TOTAL_HEADER_SIZE)

> +    {

> +      warning (_("Malformed core note - too short for MTE header.\n"

> +		 "Expected %s bytes but got %s bytes."),

> +	       pulongest (NT_MEMTAG_TOTAL_HEADER_SIZE),

> +	       pulongest (note.size ()));

> +      return tags;

> +    }

> +

> +  /* The amount of memory tag granules we need to fetch.  */

> +  size_t 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;

> +

> +  /* Read the generic header.  */

> +  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);

> +  const gdb_byte *buf = note.data ();

> +

> +  unsigned int format

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::format),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::format);

> +

> +  CORE_ADDR start_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::start_vma);

> +

> +  CORE_ADDR end_vma

> +    = extract_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),

> +				byte_order);

> +  buf += sizeof (tag_dump_header::end_vma);

> +

> +  /* Validate that ADDRESS + LENGTH doesn't fall outside of this note's

> +     range of addresses.  */

> +  gdb_assert (address + length < end_vma);

> +

> +  /* Is the tag header format correct for this note?  */

> +  if (format != NT_MEMTAG_TYPE_AARCH_MTE)

> +    {

> +      warning (_("Unexpected memory tag note format.\n"

> +		 "Expected %x but got %x."), NT_MEMTAG_TYPE_AARCH_MTE,

> +	       format);

> +      return tags;

> +    }

> +

> +  size_t expected_tag_bytes = (end_vma - start_vma)/AARCH64_MTE_GRANULE_SIZE;

> +

> +  /* Does the number of tag bytes in this note match the expected number

> +     of tag bytes the note says it has?  */

> +  if (note.size () < (NT_MEMTAG_TOTAL_HEADER_SIZE + expected_tag_bytes))

> +    {

> +      warning (_("Unexpected tag data size.\n"

> +		 "Expected %s but got %s."), pulongest (expected_tag_bytes),

> +	       pulongest (note.size () - NT_MEMTAG_TOTAL_HEADER_SIZE));

> +      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 (start_vma, AARCH64_MTE_GRANULE_SIZE);

> +  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);

> +

> +  size_t skipped_granules

> +    = aarch64_mte_get_tag_granules (aligned_start_address,

> +				    aligned_address - aligned_start_address,

> +				    AARCH64_MTE_GRANULE_SIZE);

> +

> +  /* Point to the block of data that contains the first granule we are

> +     interested in.  */

> +  const gdb::array_view<const gdb_byte> tags_data

> +    = note.slice (NT_MEMTAG_TOTAL_HEADER_SIZE + skipped_granules, granules);

> +

> +  /* Read the tag granules.  */

> +  for (size_t 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 +2072,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..7da9de4aefb 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,20 @@ 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);

>   

> +/* NT_MEMTAG header for MTE tags.  */

> +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;

> +};

> +

> +/* Size of the MTE header for a NT_MEMTAG note.  */

> +#define NT_MEMTAG_MTE_HEADER_SIZE (sizeof (tag_dump_mte::granule_byte_size) \

> +				   + sizeof (tag_dump_mte::tag_bit_size) \

> +				   + sizeof (tag_dump_mte::__unused))

> +

>   #endif /* ARCH_AARCH64_LINUX_H */

> diff --git a/gdb/corelow.c b/gdb/corelow.c

> index 452b4dd4f9a..24c5bf29a11 100644

> --- a/gdb/corelow.c

> +++ b/gdb/corelow.c

> @@ -51,6 +51,8 @@

>   #include "gdbcmd.h"

>   #include "xml-tdesc.h"

>   #include "observable.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #ifndef O_LARGEFILE

>   #define O_LARGEFILE 0

> @@ -100,6 +102,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 +1124,60 @@ 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.  */

> +

> +  return (bfd_get_section_by_name (core_bfd, ".memtag") != nullptr);

> +}

> +

> +/* 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))

> +    error (_("gdbarch_decode_memtag_note not implemented for this "

> +	     "architecture."));

> +

> +  memtag_note_info info;

> +  info.memtag_section = nullptr;

> +

> +  while (get_next_core_memtag_section (core_bfd, info.memtag_section,

> +				       address, info))

> +  {

> +    size_t adjusted_length

> +      = (address + len < info.end_address)? len : (info.end_address - address);

> +

> +    /* Decode the memory tag note and return the tags.  */

> +    gdb::byte_vector tags_read

> +      = gdbarch_decode_memtag_note (gdbarch, info.note, address,

> +				    adjusted_length);

> +

> +    /* Transfer over the tags that have been read.  */

> +    tags.insert (tags.end (), tags_read.begin (), tags_read.end ());

> +

> +    /* ADDRESS + LEN may cross the boundaries of a particular NT_MEMTAG

> +       note.  Check if we need to fetch tags from a different section.  */

> +    if (address + len < info.end_address)

> +      return true;

> +

> +    /* There are more tags to fetch.  Update ADDRESS and LEN.  */

> +    len -= (info.end_address - address);

> +    address = info.end_address;

> +  }

> +

> +  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 90d827a50e7..1b001e6cacb 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..6192cc4421b 100644

> --- a/gdb/linux-tdep.c

> +++ b/gdb/linux-tdep.c

> @@ -41,6 +41,8 @@

>   #include "gdbsupport/gdb_optional.h"

>   #include "gcore.h"

>   #include "gcore-elf.h"

> +#include "gdbsupport/memtag.h"

> +#include "memtag.h"

>   

>   #include <ctype.h>

>   

> @@ -1438,10 +1440,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 +1476,91 @@ 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;

> +

> +  memtag_note_info info;

> +  return get_next_core_memtag_section (core_bfd, nullptr, address, info);

> +}

> +

> +/* 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;

> +

> +  /* Parse the contents of smaps into a vector.  */

> +  std::vector<struct smaps_data> 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

> +	= gdbarch_create_memtag_notes_from_range (gdbarch,

> +						  map.start_address,

> +						  map.end_address);

> +      /* Write notes to the core file.  */

> +      for (const 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 +2139,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/memtag.c b/gdb/memtag.c

> new file mode 100644

> index 00000000000..4d92ecde84a

> --- /dev/null

> +++ b/gdb/memtag.c

> @@ -0,0 +1,88 @@

> +/* GDB generic memory tagging functions.

> +

> +   Copyright (C) 2021 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 "defs.h"

> +#include "memtag.h"

> +#include "gdbsupport/memtag.h"

> +#include "bfd.h"

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return a pointer to a .memtag section containing ADDRESS or nullptr

> +   of none are found.

> +

> +   If SECTION is provided, search from that section onwards.  */

> +

> +bool

> +get_next_core_memtag_section (bfd *abfd, asection *section,

> +			      CORE_ADDR address, memtag_note_info &info)

> +{

> +  /* If SECTION is nullptr, start a fresh lookup.  */

> +  if (section == nullptr)

> +    section = bfd_get_section_by_name (abfd, ".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 < NT_MEMTAG_GENERIC_HEADER_SIZE)

> +	{

> +	  warning (_("Malformed core note - too short for NT_MEMTAG generic "

> +		     "header.\n"

> +		     "Expected %s bytes but got %s bytes."),

> +		   pulongest (NT_MEMTAG_GENERIC_HEADER_SIZE),

> +		   pulongest (note_size));

> +	  return false;

> +	}

> +

> +      gdb::byte_vector note (note_size);

> +

> +      /* Fetch the contents of this particular memtag note.  */

> +      if (!bfd_get_section_contents (abfd, section,

> +				     note.data (), 0, note_size))

> +	{

> +	  warning (_("could not get core note contents."));

> +	  return false;

> +	}

> +

> +      /* Read the generic header of the note.  It contains the format,

> +	 start address and end address.  */

> +      uint64_t start_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format));

> +      uint64_t end_address

> +	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format)

> +			    + sizeof (tag_dump_header::start_vma));

> +

> +      /* Is the address within [start_address, end_address)?  */

> +      if (address >= start_address

> +	  && address < end_address)

> +	{

> +	  info.start_address = start_address;

> +	  info.end_address = end_address;

> +	  info.note = note;

> +	  info.memtag_section = section;

> +	  return true;

> +	}

> +    }

> +  return false;

> +}

> diff --git a/gdb/memtag.h b/gdb/memtag.h

> new file mode 100644

> index 00000000000..43c9efb39a3

> --- /dev/null

> +++ b/gdb/memtag.h

> @@ -0,0 +1,46 @@

> +/* GDB generic memory tagging definitions.

> +   Copyright (C) 2021 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 MEMTAG_H

> +#define MEMTAG_H

> +

> +#include "bfd.h"

> +

> +struct memtag_note_info

> +{

> +  CORE_ADDR start_address;

> +  CORE_ADDR end_address;

> +  gdb::byte_vector note;

> +  asection *memtag_section;

> +};

> +

> +/* Helper function to walk through NT_MEMTAG notes in a core file.

> +

> +   Return TRUE if there is a .memtag section containing ADDRESS.  Return FALSE

> +   otherwise.

> +

> +   If SECTION is provided, search from that section onwards. If SECTION is

> +   nullptr, then start a new search.

> +

> +   If a .memtag section containing ADDRESS is found, fill INFO with data

> +   about such section.  Otherwise leave it unchanged.  */

> +

> +bool get_next_core_memtag_section (bfd *abfd, asection *section,

> +				   CORE_ADDR address, memtag_note_info &info);

> +

> +#endif /* MEMTAG_H */

> 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..d0bcd036972

> --- /dev/null

> +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp

> @@ -0,0 +1,111 @@

> +# 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 $binfile

> +

> +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"

> +}

> diff --git a/gdbsupport/memtag.h b/gdbsupport/memtag.h

> new file mode 100644

> index 00000000000..bb47eed220b

> --- /dev/null

> +++ b/gdbsupport/memtag.h

> @@ -0,0 +1,39 @@

> +/* Generic memory tagging definitions.

> +   Copyright (C) 2021 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 GDBSUPPORT_MEMTAG_H

> +#define GDBSUPPORT_MEMTAG_H

> +

> +/* 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;

> +};

> +

> +/* Size of the generic header for the NT_MEMTAG note.  This is OS-independent

> +   and should be shared with OS-specific and arch-specific code.  */

> +#define NT_MEMTAG_GENERIC_HEADER_SIZE (sizeof (tag_dump_header::format) \

> +				       + sizeof (tag_dump_header::start_vma) \

> +				       + sizeof (tag_dump_header::end_vma))

> +

> +#endif /* GDBSUPPORT_MEMTAG_H */

>
Philippe Waroquiers via Gdb-patches July 29, 2021, 2:26 a.m. | #10
On 2021-07-14 9:07 a.m., Catalin Marinas wrote:
> On Sun, Jul 11, 2021 at 07:22:00AM -0700, Joel Brobecker wrote:

>> Luis is trying to have this new feature ready for the GDB 11.1

>> release. As I understand it, the AArch64-specific part has already

>> been approved, so he's waiting for someone to review the rest of

>> the changes. Is there a Global Maintainer who would feel comfortable

>> doing so and who would have the time?

> 

> I'm a Linux kernel maintainer, so not much for me to review on the gdb

> front. But feedback from the gdb maintainers would be appreciated.

> 

> W.r.t. upstreaming the feature in gdb, I'd wait until the Linux

> counterpart is merged. We agreed in principle on the ABI but I wouldn't

> consider it stable until the feature hits a kernel release.


I reviewed a few of the previous iterations, and there were only really
some nits remaining, so I am confident that this version is OK, from a
GDB point of view.

ABI-wise, I tend to agree with Catalin: as long as it's not in a
released kernel, the format *could* change.  But is it a problem in
practice?  Let's say this happens:

 - We merge support for MTE core files in GDB 11
 - The format gets changed, merged and released in a kernel
 - We fix GDB 12 based on the new format

Then GDB 11 users end up with a GDB that can't read MTE notes.  But had
we not merged the support for it, they would also end up with a GDB that
can't read MTE notes.  In both cases, we'll tell them "upgrade to GDB
12" (once it's released).

If we merge support for MTE in core files now and if the format
doesn't change, everybody is happy.  I guess it depends on how likely it
is that the format is going to change.

Simon
John Baldwin July 29, 2021, 4:03 p.m. | #11
On 7/28/21 7:26 PM, Simon Marchi via Gdb-patches wrote:
> On 2021-07-14 9:07 a.m., Catalin Marinas wrote:

>> On Sun, Jul 11, 2021 at 07:22:00AM -0700, Joel Brobecker wrote:

>>> Luis is trying to have this new feature ready for the GDB 11.1

>>> release. As I understand it, the AArch64-specific part has already

>>> been approved, so he's waiting for someone to review the rest of

>>> the changes. Is there a Global Maintainer who would feel comfortable

>>> doing so and who would have the time?

>>

>> I'm a Linux kernel maintainer, so not much for me to review on the gdb

>> front. But feedback from the gdb maintainers would be appreciated.

>>

>> W.r.t. upstreaming the feature in gdb, I'd wait until the Linux

>> counterpart is merged. We agreed in principle on the ABI but I wouldn't

>> consider it stable until the feature hits a kernel release.

> 

> I reviewed a few of the previous iterations, and there were only really

> some nits remaining, so I am confident that this version is OK, from a

> GDB point of view.

> 

> ABI-wise, I tend to agree with Catalin: as long as it's not in a

> released kernel, the format *could* change.  But is it a problem in

> practice?  Let's say this happens:

> 

>   - We merge support for MTE core files in GDB 11

>   - The format gets changed, merged and released in a kernel

>   - We fix GDB 12 based on the new format

> 

> Then GDB 11 users end up with a GDB that can't read MTE notes.  But had

> we not merged the support for it, they would also end up with a GDB that

> can't read MTE notes.  In both cases, we'll tell them "upgrade to GDB

> 12" (once it's released).

> 

> If we merge support for MTE in core files now and if the format

> doesn't change, everybody is happy.  I guess it depends on how likely it

> is that the format is going to change.


I think the format is suitable and probably won't need to change.  I have an
interest in using it for another type of memory tags (CHERI & Morello) and
this format will support those (and in fact it would even support an
architecture with multiple memory tags).

-- 
John Baldwin
Philippe Waroquiers via Gdb-patches July 29, 2021, 6:10 p.m. | #12
On Wed, Jul 28, 2021 at 10:26:58PM -0400, Simon Marchi wrote:
> On 2021-07-14 9:07 a.m., Catalin Marinas wrote:

> > On Sun, Jul 11, 2021 at 07:22:00AM -0700, Joel Brobecker wrote:

> >> Luis is trying to have this new feature ready for the GDB 11.1

> >> release. As I understand it, the AArch64-specific part has already

> >> been approved, so he's waiting for someone to review the rest of

> >> the changes. Is there a Global Maintainer who would feel comfortable

> >> doing so and who would have the time?

> > 

> > I'm a Linux kernel maintainer, so not much for me to review on the gdb

> > front. But feedback from the gdb maintainers would be appreciated.

> > 

> > W.r.t. upstreaming the feature in gdb, I'd wait until the Linux

> > counterpart is merged. We agreed in principle on the ABI but I wouldn't

> > consider it stable until the feature hits a kernel release.

> 

> I reviewed a few of the previous iterations, and there were only really

> some nits remaining, so I am confident that this version is OK, from a

> GDB point of view.

> 

> ABI-wise, I tend to agree with Catalin: as long as it's not in a

> released kernel, the format *could* change.  But is it a problem in

> practice?  Let's say this happens:

> 

>  - We merge support for MTE core files in GDB 11

>  - The format gets changed, merged and released in a kernel

>  - We fix GDB 12 based on the new format

> 

> Then GDB 11 users end up with a GDB that can't read MTE notes. But had

> we not merged the support for it, they would also end up with a GDB that

> can't read MTE notes.  In both cases, we'll tell them "upgrade to GDB

> 12" (once it's released).


I'm more worried about GDB 11 reading the MTE notes incorrectly rather
than not reading them at all.

> If we merge support for MTE in core files now and if the format

> doesn't change, everybody is happy.  I guess it depends on how likely it

> is that the format is going to change.


That's hard to say before we go through at least one Linux kernel
mailing review.

-- 
Catalin
Philippe Waroquiers via Gdb-patches July 29, 2021, 6:20 p.m. | #13
On 2021-07-29 2:10 p.m., Catalin Marinas wrote:
> I'm more worried about GDB 11 reading the MTE notes incorrectly rather

> than not reading them at all.


Yeah, if a GDB 11 that knows how to read the pre-standardized notes does
not fail gracefully when encountering the standardized notes, it would
be bad.  If it just skipped them and said "I don't recognize them", it
would be ok.

>> If we merge support for MTE in core files now and if the format

>> doesn't change, everybody is happy.  I guess it depends on how likely it

>> is that the format is going to change.

> That's hard to say before we go through at least one Linux kernel

> mailing review.


Oh, ok, I thought it had already gone through some review cycles.  Then
I'll leave it to you all to decide, those who have some stakes in this.

Simon

Patch

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f664d964536..12fb3b390b1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1100,6 +1100,7 @@  COMMON_SFILES = \
 	memattr.c \
 	memory-map.c \
 	memrange.c \
+	memtag.c \
 	minidebug.c \
 	minsyms.c \
 	mipsread.c \
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..04498f3b6c0 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -52,6 +52,9 @@ 
 #include "value.h"
 
 #include "gdbsupport/selftest.h"
+#include "gdbsupport/memtag.h"
+
+#include "elf/common.h"
 
 /* Signal frame handling.
 
@@ -1779,6 +1782,213 @@  aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
     }
 }
 
+/* Memory tag note header size.  Includes both the generic and the
+   arch-specific parts.  */
+#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \
+				     + NT_MEMTAG_MTE_HEADER_SIZE)
+
+/* 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.  */
+  size_t 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 (NT_MEMTAG_TOTAL_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 (tag_dump_header::format),
+			  byte_order, NT_MEMTAG_TYPE_AARCH_MTE);
+  buf += sizeof (tag_dump_header::format);
+
+  /* Start address */
+  store_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),
+			  byte_order, start_address);
+  buf += sizeof (tag_dump_header::start_vma);
+
+  /* End address */
+  store_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),
+			  byte_order, end_address);
+  buf += sizeof (tag_dump_header::end_vma);
+
+  /* MTE-specific header.  */
+  /* Granule byte size */
+  store_unsigned_integer (buf, sizeof (tag_dump_mte::granule_byte_size),
+			  byte_order, AARCH64_MTE_GRANULE_SIZE);
+  buf += sizeof (tag_dump_mte::granule_byte_size);
+
+  /* Tag bit size */
+  store_unsigned_integer (buf, sizeof (tag_dump_mte::tag_bit_size),
+			  byte_order, AARCH64_MTE_TAG_BIT_SIZE);
+  buf += sizeof (tag_dump_mte::tag_bit_size);
+
+  /* Unused value */
+  store_unsigned_integer (buf, sizeof (tag_dump_mte::__unused), byte_order, 0);
+
+  /* Store the tags.  */
+  memcpy (notes[0].data () + NT_MEMTAG_TOTAL_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 requested tags.
+
+   The note is guaranteed to cover the [ADDRESS, ADDRESS + length)
+   range.  */
+
+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;
+
+  /* Sanity check.  */
+  if (note.size () < NT_MEMTAG_TOTAL_HEADER_SIZE)
+    {
+      warning (_("Malformed core note - too short for MTE header.\n"
+		 "Expected %s bytes but got %s bytes."),
+	       pulongest (NT_MEMTAG_TOTAL_HEADER_SIZE),
+	       pulongest (note.size ()));
+      return tags;
+    }
+
+  /* The amount of memory tag granules we need to fetch.  */
+  size_t 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;
+
+  /* Read the generic header.  */
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  const gdb_byte *buf = note.data ();
+
+  unsigned int format
+    = extract_unsigned_integer (buf, sizeof (tag_dump_header::format),
+				byte_order);
+  buf += sizeof (tag_dump_header::format);
+
+  CORE_ADDR start_vma
+    = extract_unsigned_integer (buf, sizeof (tag_dump_header::start_vma),
+				byte_order);
+  buf += sizeof (tag_dump_header::start_vma);
+
+  CORE_ADDR end_vma
+    = extract_unsigned_integer (buf, sizeof (tag_dump_header::end_vma),
+				byte_order);
+  buf += sizeof (tag_dump_header::end_vma);
+
+  /* Validate that ADDRESS + LENGTH doesn't fall outside of this note's
+     range of addresses.  */
+  gdb_assert (address + length < end_vma);
+
+  /* Is the tag header format correct for this note?  */
+  if (format != NT_MEMTAG_TYPE_AARCH_MTE)
+    {
+      warning (_("Unexpected memory tag note format.\n"
+		 "Expected %x but got %x."), NT_MEMTAG_TYPE_AARCH_MTE,
+	       format);
+      return tags;
+    }
+
+  size_t expected_tag_bytes = (end_vma - start_vma)/AARCH64_MTE_GRANULE_SIZE;
+
+  /* Does the number of tag bytes in this note match the expected number
+     of tag bytes the note says it has?  */
+  if (note.size () < (NT_MEMTAG_TOTAL_HEADER_SIZE + expected_tag_bytes))
+    {
+      warning (_("Unexpected tag data size.\n"
+		 "Expected %s but got %s."), pulongest (expected_tag_bytes),
+	       pulongest (note.size () - NT_MEMTAG_TOTAL_HEADER_SIZE));
+      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 (start_vma, AARCH64_MTE_GRANULE_SIZE);
+  CORE_ADDR aligned_address = align_down (address, AARCH64_MTE_GRANULE_SIZE);
+
+  size_t skipped_granules
+    = aarch64_mte_get_tag_granules (aligned_start_address,
+				    aligned_address - aligned_start_address,
+				    AARCH64_MTE_GRANULE_SIZE);
+
+  /* Point to the block of data that contains the first granule we are
+     interested in.  */
+  const gdb::array_view<const gdb_byte> tags_data
+    = note.slice (NT_MEMTAG_TOTAL_HEADER_SIZE + skipped_granules, granules);
+
+  /* Read the tag granules.  */
+  for (size_t 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 +2072,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..7da9de4aefb 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,20 @@  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);
 
+/* NT_MEMTAG header for MTE tags.  */
+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;
+};
+
+/* Size of the MTE header for a NT_MEMTAG note.  */
+#define NT_MEMTAG_MTE_HEADER_SIZE (sizeof (tag_dump_mte::granule_byte_size) \
+				   + sizeof (tag_dump_mte::tag_bit_size) \
+				   + sizeof (tag_dump_mte::__unused))
+
 #endif /* ARCH_AARCH64_LINUX_H */
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 452b4dd4f9a..24c5bf29a11 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -51,6 +51,8 @@ 
 #include "gdbcmd.h"
 #include "xml-tdesc.h"
 #include "observable.h"
+#include "gdbsupport/memtag.h"
+#include "memtag.h"
 
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
@@ -100,6 +102,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 +1124,60 @@  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.  */
+
+  return (bfd_get_section_by_name (core_bfd, ".memtag") != nullptr);
+}
+
+/* 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))
+    error (_("gdbarch_decode_memtag_note not implemented for this "
+	     "architecture."));
+
+  memtag_note_info info;
+  info.memtag_section = nullptr;
+
+  while (get_next_core_memtag_section (core_bfd, info.memtag_section,
+				       address, info))
+  {
+    size_t adjusted_length
+      = (address + len < info.end_address)? len : (info.end_address - address);
+
+    /* Decode the memory tag note and return the tags.  */
+    gdb::byte_vector tags_read
+      = gdbarch_decode_memtag_note (gdbarch, info.note, address,
+				    adjusted_length);
+
+    /* Transfer over the tags that have been read.  */
+    tags.insert (tags.end (), tags_read.begin (), tags_read.end ());
+
+    /* ADDRESS + LEN may cross the boundaries of a particular NT_MEMTAG
+       note.  Check if we need to fetch tags from a different section.  */
+    if (address + len < info.end_address)
+      return true;
+
+    /* There are more tags to fetch.  Update ADDRESS and LEN.  */
+    len -= (info.end_address - address);
+    address = info.end_address;
+  }
+
+  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 90d827a50e7..1b001e6cacb 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..6192cc4421b 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -41,6 +41,8 @@ 
 #include "gdbsupport/gdb_optional.h"
 #include "gcore.h"
 #include "gcore-elf.h"
+#include "gdbsupport/memtag.h"
+#include "memtag.h"
 
 #include <ctype.h>
 
@@ -1438,10 +1440,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 +1476,91 @@  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;
+
+  memtag_note_info info;
+  return get_next_core_memtag_section (core_bfd, nullptr, address, info);
+}
+
+/* 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;
+
+  /* Parse the contents of smaps into a vector.  */
+  std::vector<struct smaps_data> 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
+	= gdbarch_create_memtag_notes_from_range (gdbarch,
+						  map.start_address,
+						  map.end_address);
+      /* Write notes to the core file.  */
+      for (const 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 +2139,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/memtag.c b/gdb/memtag.c
new file mode 100644
index 00000000000..4d92ecde84a
--- /dev/null
+++ b/gdb/memtag.c
@@ -0,0 +1,88 @@ 
+/* GDB generic memory tagging functions.
+
+   Copyright (C) 2021 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 "defs.h"
+#include "memtag.h"
+#include "gdbsupport/memtag.h"
+#include "bfd.h"
+
+/* Helper function to walk through NT_MEMTAG notes in a core file.
+
+   Return a pointer to a .memtag section containing ADDRESS or nullptr
+   of none are found.
+
+   If SECTION is provided, search from that section onwards.  */
+
+bool
+get_next_core_memtag_section (bfd *abfd, asection *section,
+			      CORE_ADDR address, memtag_note_info &info)
+{
+  /* If SECTION is nullptr, start a fresh lookup.  */
+  if (section == nullptr)
+    section = bfd_get_section_by_name (abfd, ".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 < NT_MEMTAG_GENERIC_HEADER_SIZE)
+	{
+	  warning (_("Malformed core note - too short for NT_MEMTAG generic "
+		     "header.\n"
+		     "Expected %s bytes but got %s bytes."),
+		   pulongest (NT_MEMTAG_GENERIC_HEADER_SIZE),
+		   pulongest (note_size));
+	  return false;
+	}
+
+      gdb::byte_vector note (note_size);
+
+      /* Fetch the contents of this particular memtag note.  */
+      if (!bfd_get_section_contents (abfd, section,
+				     note.data (), 0, note_size))
+	{
+	  warning (_("could not get core note contents."));
+	  return false;
+	}
+
+      /* Read the generic header of the note.  It contains the format,
+	 start address and end address.  */
+      uint64_t start_address
+	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format));
+      uint64_t end_address
+	= bfd_get_64 (abfd, note.data () + sizeof (tag_dump_header::format)
+			    + sizeof (tag_dump_header::start_vma));
+
+      /* Is the address within [start_address, end_address)?  */
+      if (address >= start_address
+	  && address < end_address)
+	{
+	  info.start_address = start_address;
+	  info.end_address = end_address;
+	  info.note = note;
+	  info.memtag_section = section;
+	  return true;
+	}
+    }
+  return false;
+}
diff --git a/gdb/memtag.h b/gdb/memtag.h
new file mode 100644
index 00000000000..43c9efb39a3
--- /dev/null
+++ b/gdb/memtag.h
@@ -0,0 +1,46 @@ 
+/* GDB generic memory tagging definitions.
+   Copyright (C) 2021 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 MEMTAG_H
+#define MEMTAG_H
+
+#include "bfd.h"
+
+struct memtag_note_info
+{
+  CORE_ADDR start_address;
+  CORE_ADDR end_address;
+  gdb::byte_vector note;
+  asection *memtag_section;
+};
+
+/* Helper function to walk through NT_MEMTAG notes in a core file.
+
+   Return TRUE if there is a .memtag section containing ADDRESS.  Return FALSE
+   otherwise.
+
+   If SECTION is provided, search from that section onwards. If SECTION is
+   nullptr, then start a new search.
+
+   If a .memtag section containing ADDRESS is found, fill INFO with data
+   about such section.  Otherwise leave it unchanged.  */
+
+bool get_next_core_memtag_section (bfd *abfd, asection *section,
+				   CORE_ADDR address, memtag_note_info &info);
+
+#endif /* MEMTAG_H */
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..d0bcd036972
--- /dev/null
+++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
@@ -0,0 +1,111 @@ 
+# 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 $binfile
+
+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"
+}
diff --git a/gdbsupport/memtag.h b/gdbsupport/memtag.h
new file mode 100644
index 00000000000..bb47eed220b
--- /dev/null
+++ b/gdbsupport/memtag.h
@@ -0,0 +1,39 @@ 
+/* Generic memory tagging definitions.
+   Copyright (C) 2021 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 GDBSUPPORT_MEMTAG_H
+#define GDBSUPPORT_MEMTAG_H
+
+/* 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;
+};
+
+/* Size of the generic header for the NT_MEMTAG note.  This is OS-independent
+   and should be shared with OS-specific and arch-specific code.  */
+#define NT_MEMTAG_GENERIC_HEADER_SIZE (sizeof (tag_dump_header::format) \
+				       + sizeof (tag_dump_header::start_vma) \
+				       + sizeof (tag_dump_header::end_vma))
+
+#endif /* GDBSUPPORT_MEMTAG_H */