public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
To: Luis Machado <luis.machado@arm.com>,
	"gdb-patches@sourceware.org" <gdb-patches@sourceware.org>
Subject: Re: [PATCH, v4] [AArch64] MTE corefile support
Date: Mon, 6 Jun 2022 17:54:31 +0800	[thread overview]
Message-ID: <54830e42a402b2f3c694be25c51670c12a4bd181.camel@mediatek.com> (raw)
In-Reply-To: <fd1bfc2a-7ba5-59df-867f-bb3960a90435@arm.com>

On Mon, 2022-06-06 at 17:47 +0800, Luis Machado wrote:
> On 6/6/22 10:42, Kuan-Ying Lee wrote:
> > On Mon, 2022-06-06 at 17:28 +0800, Luis Machado via Gdb-patches
> > wrote:
> > > Ping?
> > > 
> > 
> > Hi Luis,
> > 
> > I don't see PT_AARCH64_MEMTAG_MTE in include/elf/common.h.
> > 
> > I think we need to add below in include/elf/common.h.
> 
> Based on binutils@ feedback, this constant was moved to
> include/elf/aarch64.h, and is
> part of the binutils patch.

Got it.
Thanks. :)

> 
> > 
> > #define PT_AARCH64_MEMTAG_MTE (PT_LOPROC + 0x2)
> > 
> > Or do I miss something?
> > 
> > Best regards,
> > Kuan-Ying Lee
> > 
> > > On 5/3/22 22:56, Luis Machado via Gdb-patches wrote:
> > > > v4:
> > > > 
> > > > - Updated documentation (added cross-references).
> > > > - Updated the segment name from PT_ARM_MEMTAG_MTE to
> > > >     PT_AARCH64_MEMTAG_MTE.
> > > > 
> > > > v3:
> > > > 
> > > > - Updated NEWS and documentation to be more thorough.
> > > > 
> > > > v2:
> > > > 
> > > > - Rework memory tag section handling to use generic section
> > > > fields.
> > > > 
> > > > --
> > > > 
> > > > Teach GDB how to dump memory tags for AArch64 when using the
> > > > gcore
> > > > command
> > > > and how to read memory tag data back from a core file generated
> > > > by
> > > > GDB
> > > > (via gcore) or by the Linux kernel.
> > > > 
> > > > The format is documented in the Linux Kernel documentation [1].
> > > > 
> > > > Each tagged memory range (listed in /proc/<pid>/smaps) gets
> > > > dumped
> > > > to its
> > > > own PT_AARCH64_MEMTAG_MTE segment. A section named ".memtag" is
> > > > created for each
> > > > of those segments when reading the core file back.
> > > > 
> > > > To save a little bit of space, given MTE tags only take 4 bits,
> > > > the
> > > > memory tags
> > > > are stored packed as 2 tags per byte.
> > > > 
> > > > When reading the data back, the tags are unpacked.
> > > > 
> > > > I've added a new testcase to exercise the feature.
> > > > 
> > > > Build-tested with --enable-targets=all and regression tested on
> > > > aarch64-linux
> > > > Ubuntu 20.04.
> > > > 
> > > > [1] Documentation/arm64/memory-tagging-extension.rst (Core Dump
> > > > Support)
> > > > ---
> > > >    gdb/Makefile.in                              |   1 +
> > > >    gdb/NEWS                                     |  10 ++
> > > >    gdb/aarch64-linux-tdep.c                     | 167
> > > > +++++++++++++++++++
> > > >    gdb/arch/aarch64-mte-linux.c                 |  56 +++++++
> > > >    gdb/arch/aarch64-mte-linux.h                 |  10 ++
> > > >    gdb/corelow.c                                |  62 +++++++
> > > >    gdb/defs.h                                   |   3 +-
> > > >    gdb/doc/gdb.texinfo                          |  19 +++
> > > >    gdb/gcore.c                                  |  83 ++++++++-
> > > >    gdb/gdbarch-components.py                    |  35 ++++
> > > >    gdb/gdbarch-gen.h                            |  26 +++
> > > >    gdb/gdbarch.c                                |  96
> > > > +++++++++++
> > > >    gdb/linux-tdep.c                             |  39 ++++-
> > > >    gdb/memtag.c                                 |  61 +++++++
> > > >    gdb/memtag.h                                 |  50 ++++++
> > > >    gdb/testsuite/gdb.arch/aarch64-mte-gcore.c   |  93
> > > > +++++++++++
> > > >    gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 107
> > > > ++++++++++++
> > > >    17 files changed, 910 insertions(+), 8 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
> > > > 
> > > > diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> > > > index 418094775a5..fac9364bea4 100644
> > > > --- a/gdb/Makefile.in
> > > > +++ b/gdb/Makefile.in
> > > > @@ -1120,6 +1120,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 982f4a1a18c..3d925dc3663 100644
> > > > --- a/gdb/NEWS
> > > > +++ b/gdb/NEWS
> > > > @@ -3,6 +3,16 @@
> > > >    
> > > >    *** Changes since GDB 12
> > > >    
> > > > +* 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.
> > > > +
> > > > +  When a process uses memory-mapped pages protected by memory
> > > > tags
> > > > (for
> > > > +  example, AArch64 MTE), this additional information will be
> > > > recorded in
> > > > +  the core file in the event of a crash or if GDB generates a
> > > > core
> > > > file
> > > > +  from the current process state.  GDB will show this
> > > > additional
> > > > information
> > > > +  automatically, or through one of the memory-tag subcommands.
> > > > +
> > > >    * GDB now supports hardware watchpoints on FreeBSD/Aarch64.
> > > >    
> > > >    * Remove support for building against Python 2, it is now
> > > > only
> > > > possible to
> > > > diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-
> > > > tdep.c
> > > > index 55094b3d88b..12d98e71796 100644
> > > > --- a/gdb/aarch64-linux-tdep.c
> > > > +++ b/gdb/aarch64-linux-tdep.c
> > > > @@ -53,6 +53,9 @@
> > > >    
> > > >    #include "gdbsupport/selftest.h"
> > > >    
> > > > +#include "elf/common.h"
> > > > +#include "elf/aarch64.h"
> > > > +
> > > >    /* Signal frame handling.
> > > >    
> > > >          +------------+  ^
> > > > @@ -1781,6 +1784,155 @@ aarch64_linux_report_signal_info
> > > > (struct
> > > > gdbarch *gdbarch,
> > > >        }
> > > >    }
> > > >    
> > > > +/* AArch64 Linux implementation of the
> > > > gdbarch_create_memtag_section hook.  */
> > > > +
> > > > +static asection *
> > > > +aarch64_linux_create_memtag_section (struct gdbarch *gdbarch,
> > > > bfd
> > > > *obfd,
> > > > +				     CORE_ADDR address, size_t
> > > > size)
> > > > +{
> > > > +  gdb_assert (obfd != nullptr);
> > > > +  gdb_assert (size > 0);
> > > > +
> > > > +  /* Create the section and associated program header.  */
> > > > +  asection *mte_section = bfd_make_section_anyway (obfd,
> > > > "memtag");
> > > > +
> > > > +  if (mte_section == nullptr)
> > > > +    return nullptr;
> > > > +
> > > > +  bfd_set_section_vma (mte_section, address);
> > > > +  /* The size of the memory range covered by the memory
> > > > tags.  We
> > > > reuse the
> > > > +     section's rawsize field for this purpose.  */
> > > > +  mte_section->rawsize = size;
> > > > +  /* Tags are stored packed as 2 tags per byte.  */
> > > > +  bfd_set_section_size (mte_section, (size /
> > > > AARCH64_MTE_GRANULE_SIZE) / 2);
> > > > +  /* Make sure the section's flags has SEC_HAS_CONTENTS,
> > > > otherwise
> > > > BFD will
> > > > +     refuse to write data to this section.  */
> > > > +  bfd_set_section_flags (mte_section, SEC_HAS_CONTENTS);
> > > > +
> > > > +  /* Store program header information.  */
> > > > +  bfd_record_phdr (obfd, PT_AARCH64_MEMTAG_MTE, 1, 0, 0, 0, 0,
> > > > 0,
> > > > 1,
> > > > +		   &mte_section);
> > > > +
> > > > +  return mte_section;
> > > > +}
> > > > +
> > > > +/* Maximum number of tags to request.  */
> > > > +#define MAX_TAGS_TO_TRANSFER 1024
> > > > +
> > > > +/* AArch64 Linux implementation of the
> > > > gdbarch_fill_memtag_section
> > > > hook.  */
> > > > +
> > > > +static bool
> > > > +aarch64_linux_fill_memtag_section (struct gdbarch *gdbarch,
> > > > asection *osec)
> > > > +{
> > > > +  /* We only handle MTE tags for now.  */
> > > > +
> > > > +  size_t segment_size = osec->rawsize;
> > > > +  CORE_ADDR start_address = bfd_section_vma (osec);
> > > > +  CORE_ADDR end_address = start_address + segment_size;
> > > > +
> > > > +  /* Figure out how many tags we need to store in this memory
> > > > range.  */
> > > > +  size_t granules = aarch64_mte_get_tag_granules
> > > > (start_address,
> > > > segment_size,
> > > > +						  AARCH64_MTE_G
> > > > RANULE_S
> > > > IZE);
> > > > +
> > > > +  /* If there are no tag granules to fetch, just return.  */
> > > > +  if (granules == 0)
> > > > +    return true;
> > > > +
> > > > +  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)));
> > > > +	  return false;
> > > > +	}
> > > > +
> > > > +      /* 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;
> > > > +    }
> > > > +
> > > > +  /* Pack the MTE tag bits.  */
> > > > +  aarch64_mte_pack_tags (tags);
> > > > +
> > > > +  if (!bfd_set_section_contents (osec->owner, osec, tags.data
> > > > (),
> > > > +				 0, tags.size ()))
> > > > +    {
> > > > +      warning (_("Failed to write %s bytes of corefile memory
> > > > "
> > > > +		 "tag content (%s)."),
> > > > +	       pulongest (tags.size ()),
> > > > +	       bfd_errmsg (bfd_get_error ()));
> > > > +    }
> > > > +  return true;
> > > > +}
> > > > +
> > > > +/* AArch64 Linux implementation of the
> > > > gdbarch_decode_memtag_section
> > > > +   hook.  Decode a memory tag section and return the requested
> > > > tags.
> > > > +
> > > > +   The section is guaranteed to cover the [ADDRESS, ADDRESS +
> > > > length)
> > > > +   range.  */
> > > > +
> > > > +static gdb::byte_vector
> > > > +aarch64_linux_decode_memtag_section (struct gdbarch *gdbarch,
> > > > +				     bfd_section *section,
> > > > +				     int type,
> > > > +				     CORE_ADDR address, size_t
> > > > length)
> > > > +{
> > > > +  gdb_assert (section != nullptr);
> > > > +
> > > > +  /* The requested address must not be less than section-
> > > > >vma.  */
> > > > +  gdb_assert (section->vma <= address);
> > > > +
> > > > +  /* Figure out how many tags we need to fetch in this memory
> > > > range.  */
> > > > +  size_t granules = aarch64_mte_get_tag_granules (address,
> > > > length,
> > > > +						  AARCH64_MTE_G
> > > > RANULE_S
> > > > IZE);
> > > > +  /* Sanity check.  */
> > > > +  gdb_assert (granules > 0);
> > > > +
> > > > +  /* Fetch the total number of tags in the range [VMA, address
> > > > +
> > > > length).  */
> > > > +  size_t granules_from_vma
> > > > +    = aarch64_mte_get_tag_granules (section->vma,
> > > > +				    address - section->vma +
> > > > length,
> > > > +				    AARCH64_MTE_GRANULE_SIZE);
> > > > +
> > > > +  /* Adjust the tags vector to contain the exact number of
> > > > packed
> > > > bytes.  */
> > > > +  gdb::byte_vector tags (((granules - 1) >> 1) + 1);
> > > > +
> > > > +  /* Figure out the starting offset into the packed tags
> > > > data.  */
> > > > +  file_ptr offset = ((granules_from_vma - granules) >> 1);
> > > > +
> > > > +  if (!bfd_get_section_contents (section->owner, section,
> > > > tags.data (),
> > > > +				 offset, tags.size ()))
> > > > +    error (_("Couldn't read contents from memtag section."));
> > > > +
> > > > +  /* At this point, the tags are packed 2 per byte.  Unpack
> > > > them
> > > > before
> > > > +     returning.  */
> > > > +  bool skip_first = ((granules_from_vma - granules) % 2) != 0;
> > > > +  aarch64_mte_unpack_tags (tags, skip_first);
> > > > +
> > > > +  /* Resize to the exact number of tags that was
> > > > requested.  */
> > > > +  tags.resize (granules);
> > > > +
> > > > +  return tags;
> > > > +}
> > > > +
> > > >    static void
> > > >    aarch64_linux_init_abi (struct gdbarch_info info, struct
> > > > gdbarch
> > > > *gdbarch)
> > > >    {
> > > > @@ -1864,6 +2016,21 @@ aarch64_linux_init_abi (struct
> > > > gdbarch_info
> > > > info, struct gdbarch *gdbarch)
> > > >    
> > > >          set_gdbarch_report_signal_info (gdbarch,
> > > >    				      aarch64_linux_report_sign
> > > > al_info)
> > > > ;
> > > > +
> > > > +      /* Core file helpers.  */
> > > > +
> > > > +      /* Core file helper to create a memory tag section for a
> > > > particular
> > > > +	 PT_LOAD segment.  */
> > > > +      set_gdbarch_create_memtag_section
> > > > +	(gdbarch, aarch64_linux_create_memtag_section);
> > > > +
> > > > +      /* Core file helper to fill a memory tag section with
> > > > tag
> > > > data.  */
> > > > +      set_gdbarch_fill_memtag_section
> > > > +	(gdbarch, aarch64_linux_fill_memtag_section);
> > > > +
> > > > +      /* Core file helper to decode a memory tag section.  */
> > > > +      set_gdbarch_decode_memtag_section (gdbarch,
> > > > +					 aarch64_linux_decode_m
> > > > emtag_se
> > > > ction);
> > > >        }
> > > >    
> > > >      /* Initialize the aarch64_linux_record_tdep.  */
> > > > diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-
> > > > mte-
> > > > linux.c
> > > > index fc7a8cc00f7..3af6f364e91 100644
> > > > --- a/gdb/arch/aarch64-mte-linux.c
> > > > +++ b/gdb/arch/aarch64-mte-linux.c
> > > > @@ -21,6 +21,62 @@
> > > >    
> > > >    /* See arch/aarch64-mte-linux.h */
> > > >    
> > > > +void
> > > > +aarch64_mte_pack_tags (gdb::byte_vector &tags)
> > > > +{
> > > > +  /* Nothing to pack?  */
> > > > +  if (tags.empty ())
> > > > +    return;
> > > > +
> > > > +  /* If the tags vector has an odd number of elements, add
> > > > another
> > > > +     zeroed-out element to make it even.  This facilitates
> > > > packing.  */
> > > > +  if ((tags.size () % 2) != 0)
> > > > +    tags.emplace_back (0);
> > > > +
> > > > +  for (int unpacked = 0, packed = 0; unpacked < tags.size ();
> > > > +       unpacked += 2, packed++)
> > > > +    tags[packed] = (tags[unpacked + 1] << 4) | tags[unpacked];
> > > > +
> > > > +  /* Now we have half the size.  */
> > > > +  tags.resize (tags.size () / 2);
> > > > +}
> > > > +
> > > > +/* See arch/aarch64-mte-linux.h */
> > > > +
> > > > +void
> > > > +aarch64_mte_unpack_tags (gdb::byte_vector &tags, bool
> > > > skip_first)
> > > > +{
> > > > +  /* Nothing to unpack?  */
> > > > +  if (tags.empty ())
> > > > +    return;
> > > > +
> > > > +  /* An unpacked MTE tags vector will have twice the number of
> > > > elements
> > > > +     compared to an unpacked one.  */
> > > > +  gdb::byte_vector unpacked_tags (tags.size () * 2);
> > > > +
> > > > +  int unpacked = 0, packed = 0;
> > > > +
> > > > +  if (skip_first)
> > > > +    {
> > > > +      /* We are not interested in the first unpacked element,
> > > > just
> > > > discard
> > > > +	 it.  */
> > > > +      unpacked_tags[unpacked] = (tags[packed] >> 4) & 0xf;
> > > > +      unpacked++;
> > > > +      packed++;
> > > > +    }
> > > > +
> > > > +  for (; packed < tags.size (); unpacked += 2, packed++)
> > > > +    {
> > > > +      unpacked_tags[unpacked] = tags[packed] & 0xf;
> > > > +      unpacked_tags[unpacked + 1] = (tags[packed] >> 4) & 0xf;
> > > > +    }
> > > > +
> > > > +  /* Update the original tags vector.  */
> > > > +  tags = std::move (unpacked_tags);
> > > > +}
> > > > +
> > > > +/* See arch/aarch64-mte-linux.h */
> > > > +
> > > >    size_t
> > > >    aarch64_mte_get_tag_granules (CORE_ADDR addr, size_t len,
> > > > size_t
> > > > granule_size)
> > > >    {
> > > > diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-
> > > > mte-
> > > > linux.h
> > > > index d158926feff..8a145b447aa 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,13 @@ 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);
> > > >    
> > > > +/* Given a TAGS vector containing 1 MTE tag per byte, pack the
> > > > data as
> > > > +   2 tags per byte and resize the vector.  */
> > > > +void aarch64_mte_pack_tags (gdb::byte_vector &tags);
> > > > +
> > > > +/* Given a TAGS vector containing 2 MTE tags per byte, unpack
> > > > the
> > > > data as
> > > > +   1 tag per byte and resize the vector.  If SKIP_FIRST is
> > > > TRUE,
> > > > skip the
> > > > +   first unpacked element.  Otherwise leave it in the unpacked
> > > > vector.  */
> > > > +void aarch64_mte_unpack_tags (gdb::byte_vector &tags, bool
> > > > skip_first);
> > > > +
> > > >    #endif /* ARCH_AARCH64_LINUX_H */
> > > > diff --git a/gdb/corelow.c b/gdb/corelow.c
> > > > index 8c33fb7ebb2..8b8994f80db 100644
> > > > --- a/gdb/corelow.c
> > > > +++ b/gdb/corelow.c
> > > > @@ -52,6 +52,7 @@
> > > >    #include <unordered_set>
> > > >    #include "gdbcmd.h"
> > > >    #include "xml-tdesc.h"
> > > > +#include "memtag.h"
> > > >    
> > > >    #ifndef O_LARGEFILE
> > > >    #define O_LARGEFILE 0
> > > > @@ -101,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.  */
> > > > @@ -1162,6 +1170,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 sections.  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_section_p (gdbarch))
> > > > +    error (_("gdbarch_decode_memtag_section not implemented
> > > > for
> > > > this "
> > > > +	     "architecture."));
> > > > +
> > > > +  memtag_section_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_section (gdbarch,
> > > > info.memtag_section, type,
> > > > +				       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
> > > > memory tag
> > > > +       segment.  Check if we need to fetch tags from a
> > > > different
> > > > section.  */
> > > > +    if (!tags_read.empty () && (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/defs.h b/gdb/defs.h
> > > > index 99bfdd526ff..51a7576a56a 100644
> > > > --- a/gdb/defs.h
> > > > +++ b/gdb/defs.h
> > > > @@ -344,7 +344,8 @@ extern const char *pc_prefix (CORE_ADDR);
> > > >    
> > > >    typedef int (*find_memory_region_ftype) (CORE_ADDR addr,
> > > > unsigned
> > > > long size,
> > > >    					 int read, int write,
> > > > int exec,
> > > > -					 int modified, void
> > > > *data);
> > > > +					 int modified, bool
> > > > memory_tagged,
> > > > +					 void *data);
> > > >    
> > > >    /* * Possible lvalue types.  Like enum language, this should
> > > > be
> > > > in
> > > >       value.h, but needs to be here for the same reason.  */
> > > > diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> > > > index 38ad2ac32b0..36f10f20cfb 100644
> > > > --- a/gdb/doc/gdb.texinfo
> > > > +++ b/gdb/doc/gdb.texinfo
> > > > @@ -25555,6 +25555,25 @@ 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.
> > > > +
> > > > +When a process uses memory-mapped pages protected by memory
> > > > tags
> > > > (for
> > > > +example, AArch64 MTE), this additional information will be
> > > > recorded in
> > > > +the core file in the event of a crash or if @value{GDBN}
> > > > generates
> > > > a core file
> > > > +from the current process state.
> > > > +
> > > > +The memory tag data will be used so developers can display the
> > > > memory
> > > > +tags from a particular memory region (using the @samp{m}
> > > > modifier
> > > > to the
> > > > +@command{x} command, using the @command{print} command or
> > > > using
> > > > the various
> > > > +@command{memory-tag} subcommands.
> > > > +
> > > > +In the case of a crash, @value{GDBN} will attempt to retrieve
> > > > the
> > > > memory tag
> > > > +information automatically from the core file, and will show
> > > > one of
> > > > the above
> > > > +messages depending on whether the synchronous or asynchronous
> > > > mode
> > > > is selected.
> > > > +@xref{Memory Tagging}. @xref{Memory}.
> > > > +
> > > >    @node i386
> > > >    @subsection x86 Architecture-specific Issues
> > > >    
> > > > diff --git a/gdb/gcore.c b/gdb/gcore.c
> > > > index fdb22b72a07..b81ef81ab84 100644
> > > > --- a/gdb/gcore.c
> > > > +++ b/gdb/gcore.c
> > > > @@ -349,6 +349,12 @@ make_output_phdrs (bfd *obfd, asection
> > > > *osec)
> > > >      int p_flags = 0;
> > > >      int p_type = 0;
> > > >    
> > > > +  /* Memory tag segments have already been handled by the
> > > > architecture, as
> > > > +     those contain arch-specific information.  If we have one
> > > > of
> > > > those, just
> > > > +     return.  */
> > > > +  if (startswith (bfd_section_name (osec), "memtag"))
> > > > +    return;
> > > > +
> > > >      /* FIXME: these constants may only be applicable for
> > > > ELF.  */
> > > >      if (startswith (bfd_section_name (osec), "load"))
> > > >        p_type = PT_LOAD;
> > > > @@ -371,7 +377,8 @@ make_output_phdrs (bfd *obfd, asection
> > > > *osec)
> > > >    
> > > >    static int
> > > >    gcore_create_callback (CORE_ADDR vaddr, unsigned long size,
> > > > int
> > > > read,
> > > > -		       int write, int exec, int modified, void
> > > > *data)
> > > > +		       int write, int exec, int modified, bool
> > > > memory_tagged,
> > > > +		       void *data)
> > > >    {
> > > >      bfd *obfd = (bfd *) data;
> > > >      asection *osec;
> > > > @@ -454,6 +461,45 @@ gcore_create_callback (CORE_ADDR vaddr,
> > > > unsigned long size, int read,
> > > >      return 0;
> > > >    }
> > > >    
> > > > +/* gdbarch_find_memory_region callback for creating a memory
> > > > tag
> > > > section.
> > > > +   DATA is 'bfd *' for the core file GDB is creating.  */
> > > > +
> > > > +static int
> > > > +gcore_create_memtag_section_callback (CORE_ADDR vaddr,
> > > > unsigned
> > > > long size,
> > > > +				      int read, int write, int
> > > > exec,
> > > > +				      int modified, bool
> > > > memory_tagged,
> > > > +				      void *data)
> > > > +{
> > > > +  /* Are there memory tags in this particular memory map
> > > > entry?  */
> > > > +  if (!memory_tagged)
> > > > +    return 0;
> > > > +
> > > > +  bfd *obfd = (bfd *) data;
> > > > +
> > > > +  /* Ask the architecture to create a memory tag section for
> > > > this
> > > > particular
> > > > +     memory map entry.  It will be populated with contents
> > > > later,
> > > > as we can't
> > > > +     start writing the contents before we have all the
> > > > sections
> > > > sorted out.  */
> > > > +  asection *memtag_section
> > > > +    = gdbarch_create_memtag_section (target_gdbarch (), obfd,
> > > > vaddr, size);
> > > > +
> > > > +  if (memtag_section == nullptr)
> > > > +    {
> > > > +      warning (_("Couldn't make gcore memory tag segment:
> > > > %s"),
> > > > +	       bfd_errmsg (bfd_get_error ()));
> > > > +      return 1;
> > > > +    }
> > > > +
> > > > +  if (info_verbose)
> > > > +    {
> > > > +      gdb_printf (gdb_stdout, "Saved memory tag segment, %s
> > > > bytes
> > > > "
> > > > +			      "at %s\n",
> > > > +		  plongest (bfd_section_size (memtag_section)),
> > > > +		  paddress (target_gdbarch (), vaddr));
> > > > +    }
> > > > +
> > > > +  return 0;
> > > > +}
> > > > +
> > > >    int
> > > >    objfile_find_memory_regions (struct target_ops *self,
> > > >    			     find_memory_region_ftype func,
> > > > void *obfd)
> > > > @@ -483,6 +529,7 @@ objfile_find_memory_regions (struct
> > > > target_ops
> > > > *self,
> > > >    			   (flags & SEC_READONLY) == 0, /*
> > > > Writable.  */
> > > >    			   (flags & SEC_CODE) != 0, /*
> > > > Executable.  */
> > > >    			   1, /* MODIFIED is unknown, pass it
> > > > as
> > > > true.  */
> > > > +			   false, /* No memory tags in the
> > > > object
> > > > file.  */
> > > >    			   obfd);
> > > >    	    if (ret != 0)
> > > >    	      return ret;
> > > > @@ -496,6 +543,7 @@ objfile_find_memory_regions (struct
> > > > target_ops
> > > > *self,
> > > >    	     1, /* Stack section will be writable.  */
> > > >    	     0, /* Stack section will not be executable.  */
> > > >    	     1, /* Stack section will be modified.  */
> > > > +	     false, /* No memory tags in the object file.  */
> > > >    	     obfd);
> > > >    
> > > >      /* Make a heap segment.  */
> > > > @@ -506,6 +554,7 @@ objfile_find_memory_regions (struct
> > > > target_ops
> > > > *self,
> > > >    	     1, /* Heap section will be writable.  */
> > > >    	     0, /* Heap section will not be executable.  */
> > > >    	     1, /* Heap section will be modified.  */
> > > > +	     false, /* No memory tags in the object file.  */
> > > >    	     obfd);
> > > >    
> > > >      return 0;
> > > > @@ -555,6 +604,20 @@ gcore_copy_callback (bfd *obfd, asection
> > > > *osec)
> > > >        }
> > > >    }
> > > >    
> > > > +/* Callback to copy contents to a particular memory tag
> > > > section.  */
> > > > +
> > > > +static void
> > > > +gcore_copy_memtag_section_callback (bfd *obfd, asection *osec)
> > > > +{
> > > > +  /* We are only interested in "memtag" sections.  */
> > > > +  if (!startswith (bfd_section_name (osec), "memtag"))
> > > > +    return;
> > > > +
> > > > +  /* Fill the section with memory tag contents.  */
> > > > +  if (!gdbarch_fill_memtag_section (target_gdbarch (), osec))
> > > > +    error (_("Failed to fill memory tag section for core
> > > > file."));
> > > > +}
> > > > +
> > > >    static int
> > > >    gcore_memory_sections (bfd *obfd)
> > > >    {
> > > > @@ -567,13 +630,27 @@ gcore_memory_sections (bfd *obfd)
> > > >    	return 0;			/* FIXME: error
> > > > return/msg?  */
> > > >        }
> > > >    
> > > > +  /* Take care of dumping memory tags, if there are any.  */
> > > > +  if (!gdbarch_find_memory_regions_p (target_gdbarch ())
> > > > +      || gdbarch_find_memory_regions (target_gdbarch (),
> > > > +				      gcore_create_memtag_secti
> > > > on_callb
> > > > ack,
> > > > +				      obfd) != 0)
> > > > +    {
> > > > +      if (target_find_memory_regions
> > > > (gcore_create_memtag_section_callback,
> > > > +				      obfd) != 0)
> > > > +	return 0;
> > > > +    }
> > > > +
> > > >      /* Record phdrs for section-to-segment mapping.  */
> > > >      for (asection *sect : gdb_bfd_sections (obfd))
> > > >        make_output_phdrs (obfd, sect);
> > > >    
> > > > -  /* Copy memory region contents.  */
> > > > +  /* Copy memory region and memory tag contents.  */
> > > >      for (asection *sect : gdb_bfd_sections (obfd))
> > > > -    gcore_copy_callback (obfd, sect);
> > > > +    {
> > > > +      gcore_copy_callback (obfd, sect);
> > > > +      gcore_copy_memtag_section_callback (obfd, sect);
> > > > +    }
> > > >    
> > > >      return 1;
> > > >    }
> > > > diff --git a/gdb/gdbarch-components.py b/gdb/gdbarch-
> > > > components.py
> > > > index e8f20c83ff0..6fa1b7591db 100644
> > > > --- a/gdb/gdbarch-components.py
> > > > +++ b/gdb/gdbarch-components.py
> > > > @@ -1522,6 +1522,41 @@ Find core file memory regions
> > > >        invalid=True,
> > > >    )
> > > >    
> > > > +Method(
> > > > +    comment="""
> > > > +Given a bfd OBFD, segment ADDRESS and SIZE, create a memory
> > > > tag
> > > > section to be dumped to a core file
> > > > +""",
> > > > +    type="asection *",
> > > > +    name="create_memtag_section",
> > > > +    params=[("bfd *", "obfd"), ("CORE_ADDR", "address"),
> > > > ("size_t", "size")],
> > > > +    predicate=True,
> > > > +    invalid=True,
> > > > +)
> > > > +
> > > > +Method(
> > > > +    comment="""
> > > > +Given a memory tag section OSEC, fill OSEC's contents with the
> > > > appropriate tag data
> > > > +""",
> > > > +    type="bool",
> > > > +    name="fill_memtag_section",
> > > > +    params=[("asection *", "osec")],
> > > > +    predicate=True,
> > > > +    invalid=True,
> > > > +)
> > > > +
> > > > +Method(
> > > > +    comment="""
> > > > +Decode a memory tag SECTION and return the tags of type TYPE
> > > > contained in
> > > > +the memory range [ADDRESS, ADDRESS + LENGTH).
> > > > +If no tags were found, return an empty vector.
> > > > +""",
> > > > +    type="gdb::byte_vector",
> > > > +    name="decode_memtag_section",
> > > > +    params=[("bfd_section *", "section"), ("int", "type"),
> > > > ("CORE_ADDR", "address"), ("size_t", "length")],
> > > > +    predicate=True,
> > > > +    invalid=True,
> > > > +)
> > > > +
> > > >    Method(
> > > >        comment="""
> > > >    Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted
> > > > shared
> > > > libraries list from
> > > > diff --git a/gdb/gdbarch-gen.h b/gdb/gdbarch-gen.h
> > > > index 882b9057b1a..1d19f51f21d 100644
> > > > --- a/gdb/gdbarch-gen.h
> > > > +++ b/gdb/gdbarch-gen.h
> > > > @@ -874,6 +874,32 @@ 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);
> > > >    
> > > > +/* Given a bfd OBFD, segment ADDRESS and SIZE, create a memory
> > > > tag
> > > > section to be dumped to a core file */
> > > > +
> > > > +extern bool gdbarch_create_memtag_section_p (struct gdbarch
> > > > *gdbarch);
> > > > +
> > > > +typedef asection * (gdbarch_create_memtag_section_ftype)
> > > > (struct
> > > > gdbarch *gdbarch, bfd *obfd, CORE_ADDR address, size_t size);
> > > > +extern asection * gdbarch_create_memtag_section (struct
> > > > gdbarch
> > > > *gdbarch, bfd *obfd, CORE_ADDR address, size_t size);
> > > > +extern void set_gdbarch_create_memtag_section (struct gdbarch
> > > > *gdbarch, gdbarch_create_memtag_section_ftype
> > > > *create_memtag_section);
> > > > +
> > > > +/* Given a memory tag section OSEC, fill OSEC's contents with
> > > > the
> > > > appropriate tag data */
> > > > +
> > > > +extern bool gdbarch_fill_memtag_section_p (struct gdbarch
> > > > *gdbarch);
> > > > +
> > > > +typedef bool (gdbarch_fill_memtag_section_ftype) (struct
> > > > gdbarch
> > > > *gdbarch, asection *osec);
> > > > +extern bool gdbarch_fill_memtag_section (struct gdbarch
> > > > *gdbarch,
> > > > asection *osec);
> > > > +extern void set_gdbarch_fill_memtag_section (struct gdbarch
> > > > *gdbarch, gdbarch_fill_memtag_section_ftype
> > > > *fill_memtag_section);
> > > > +
> > > > +/* Decode a memory tag SECTION and return the tags of type
> > > > TYPE
> > > > contained in
> > > > +   the memory range [ADDRESS, ADDRESS + LENGTH).
> > > > +   If no tags were found, return an empty vector. */
> > > > +
> > > > +extern bool gdbarch_decode_memtag_section_p (struct gdbarch
> > > > *gdbarch);
> > > > +
> > > > +typedef gdb::byte_vector (gdbarch_decode_memtag_section_ftype)
> > > > (struct gdbarch *gdbarch, bfd_section *section, int type,
> > > > CORE_ADDR
> > > > address, size_t length);
> > > > +extern gdb::byte_vector gdbarch_decode_memtag_section (struct
> > > > gdbarch *gdbarch, bfd_section *section, int type, CORE_ADDR
> > > > address, size_t length);
> > > > +extern void set_gdbarch_decode_memtag_section (struct gdbarch
> > > > *gdbarch, gdbarch_decode_memtag_section_ftype
> > > > *decode_memtag_section);
> > > > +
> > > >    /* 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.c b/gdb/gdbarch.c
> > > > index a588bdef61a..f5dbacb14e7 100644
> > > > --- a/gdb/gdbarch.c
> > > > +++ b/gdb/gdbarch.c
> > > > @@ -171,6 +171,9 @@ 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_section_ftype *create_memtag_section;
> > > > +  gdbarch_fill_memtag_section_ftype *fill_memtag_section;
> > > > +  gdbarch_decode_memtag_section_ftype *decode_memtag_section;
> > > >      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;
> > > > @@ -527,6 +530,9 @@ 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_section, has predicate.  */
> > > > +  /* Skip verify of fill_memtag_section, has predicate.  */
> > > > +  /* Skip verify of decode_memtag_section, 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.  */
> > > > @@ -1096,6 +1102,24 @@ gdbarch_dump (struct gdbarch *gdbarch,
> > > > struct ui_file *file)
> > > >      gdb_printf (file,
> > > >                          "gdbarch_dump: find_memory_regions =
> > > > <%s>\n",
> > > >                          host_address_to_string (gdbarch-
> > > > > find_memory_regions));
> > > > 
> > > > +  gdb_printf (file,
> > > > +                      "gdbarch_dump:
> > > > gdbarch_create_memtag_section_p() = %d\n",
> > > > +                      gdbarch_create_memtag_section_p
> > > > (gdbarch));
> > > > +  gdb_printf (file,
> > > > +                      "gdbarch_dump: create_memtag_section =
> > > > <%s>\n",
> > > > +                      host_address_to_string (gdbarch-
> > > > > create_memtag_section));
> > > > 
> > > > +  gdb_printf (file,
> > > > +                      "gdbarch_dump:
> > > > gdbarch_fill_memtag_section_p() = %d\n",
> > > > +                      gdbarch_fill_memtag_section_p
> > > > (gdbarch));
> > > > +  gdb_printf (file,
> > > > +                      "gdbarch_dump: fill_memtag_section =
> > > > <%s>\n",
> > > > +                      host_address_to_string (gdbarch-
> > > > > fill_memtag_section));
> > > > 
> > > > +  gdb_printf (file,
> > > > +                      "gdbarch_dump:
> > > > gdbarch_decode_memtag_section_p() = %d\n",
> > > > +                      gdbarch_decode_memtag_section_p
> > > > (gdbarch));
> > > > +  gdb_printf (file,
> > > > +                      "gdbarch_dump: decode_memtag_section =
> > > > <%s>\n",
> > > > +                      host_address_to_string (gdbarch-
> > > > > decode_memtag_section));
> > > > 
> > > >      gdb_printf (file,
> > > >                          "gdbarch_dump:
> > > > gdbarch_core_xfer_shared_libraries_p() = %d\n",
> > > >                          gdbarch_core_xfer_shared_libraries_p
> > > > (gdbarch));
> > > > @@ -3744,6 +3768,78 @@ set_gdbarch_find_memory_regions (struct
> > > > gdbarch *gdbarch,
> > > >      gdbarch->find_memory_regions = find_memory_regions;
> > > >    }
> > > >    
> > > > +bool
> > > > +gdbarch_create_memtag_section_p (struct gdbarch *gdbarch)
> > > > +{
> > > > +  gdb_assert (gdbarch != NULL);
> > > > +  return gdbarch->create_memtag_section != NULL;
> > > > +}
> > > > +
> > > > +asection *
> > > > +gdbarch_create_memtag_section (struct gdbarch *gdbarch, bfd
> > > > *obfd,
> > > > CORE_ADDR address, size_t size)
> > > > +{
> > > > +  gdb_assert (gdbarch != NULL);
> > > > +  gdb_assert (gdbarch->create_memtag_section != NULL);
> > > > +  if (gdbarch_debug >= 2)
> > > > +    gdb_printf (gdb_stdlog, "gdbarch_create_memtag_section
> > > > called\n");
> > > > +  return gdbarch->create_memtag_section (gdbarch, obfd,
> > > > address,
> > > > size);
> > > > +}
> > > > +
> > > > +void
> > > > +set_gdbarch_create_memtag_section (struct gdbarch *gdbarch,
> > > > +                                   gdbarch_create_memtag_secti
> > > > on_f
> > > > type create_memtag_section)
> > > > +{
> > > > +  gdbarch->create_memtag_section = create_memtag_section;
> > > > +}
> > > > +
> > > > +bool
> > > > +gdbarch_fill_memtag_section_p (struct gdbarch *gdbarch)
> > > > +{
> > > > +  gdb_assert (gdbarch != NULL);
> > > > +  return gdbarch->fill_memtag_section != NULL;
> > > > +}
> > > > +
> > > > +bool
> > > > +gdbarch_fill_memtag_section (struct gdbarch *gdbarch, asection
> > > > *osec)
> > > > +{
> > > > +  gdb_assert (gdbarch != NULL);
> > > > +  gdb_assert (gdbarch->fill_memtag_section != NULL);
> > > > +  if (gdbarch_debug >= 2)
> > > > +    gdb_printf (gdb_stdlog, "gdbarch_fill_memtag_section
> > > > called\n");
> > > > +  return gdbarch->fill_memtag_section (gdbarch, osec);
> > > > +}
> > > > +
> > > > +void
> > > > +set_gdbarch_fill_memtag_section (struct gdbarch *gdbarch,
> > > > +                                 gdbarch_fill_memtag_section_f
> > > > type
> > > > fill_memtag_section)
> > > > +{
> > > > +  gdbarch->fill_memtag_section = fill_memtag_section;
> > > > +}
> > > > +
> > > > +bool
> > > > +gdbarch_decode_memtag_section_p (struct gdbarch *gdbarch)
> > > > +{
> > > > +  gdb_assert (gdbarch != NULL);
> > > > +  return gdbarch->decode_memtag_section != NULL;
> > > > +}
> > > > +
> > > > +gdb::byte_vector
> > > > +gdbarch_decode_memtag_section (struct gdbarch *gdbarch,
> > > > bfd_section *section, int type, CORE_ADDR address, size_t
> > > > length)
> > > > +{
> > > > +  gdb_assert (gdbarch != NULL);
> > > > +  gdb_assert (gdbarch->decode_memtag_section != NULL);
> > > > +  if (gdbarch_debug >= 2)
> > > > +    gdb_printf (gdb_stdlog, "gdbarch_decode_memtag_section
> > > > called\n");
> > > > +  return gdbarch->decode_memtag_section (gdbarch, section,
> > > > type,
> > > > address, length);
> > > > +}
> > > > +
> > > > +void
> > > > +set_gdbarch_decode_memtag_section (struct gdbarch *gdbarch,
> > > > +                                   gdbarch_decode_memtag_secti
> > > > on_f
> > > > type decode_memtag_section)
> > > > +{
> > > > +  gdbarch->decode_memtag_section = decode_memtag_section;
> > > > +}
> > > > +
> > > >    bool
> > > >    gdbarch_core_xfer_shared_libraries_p (struct gdbarch
> > > > *gdbarch)
> > > >    {
> > > > diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
> > > > index 4e728a06e7e..8a83ed320cf 100644
> > > > --- a/gdb/linux-tdep.c
> > > > +++ b/gdb/linux-tdep.c
> > > > @@ -42,6 +42,7 @@
> > > >    #include "gcore.h"
> > > >    #include "gcore-elf.h"
> > > >    #include "solib-svr4.h"
> > > > +#include "memtag.h"
> > > >    
> > > >    #include <ctype.h>
> > > >    #include <unordered_map>
> > > > @@ -1320,6 +1321,7 @@ typedef int
> > > > linux_find_memory_region_ftype
> > > > (ULONGEST vaddr, ULONGEST size,
> > > >    					    ULONGEST offset,
> > > > ULONGEST
> > > > inode,
> > > >    					    int read, int
> > > > write,
> > > >    					    int exec, int
> > > > modified,
> > > > +					    bool memory_tagged,
> > > >    					    const char
> > > > *filename,
> > > >    					    void *data);
> > > >    
> > > > @@ -1470,10 +1472,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;
> > > > @@ -1505,6 +1508,30 @@ 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_section_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);
> > > > +}
> > > > +
> > > >    /* List memory regions in the inferior for a corefile.  */
> > > >    
> > > >    static int
> > > > @@ -1593,6 +1620,7 @@ linux_find_memory_regions_full (struct
> > > > gdbarch *gdbarch,
> > > >    		map.offset, map.inode, map.read, map.write,
> > > > map.exec,
> > > >    		1, /* MODIFIED is true because we want to dump
> > > >    		      the mapping.  */
> > > > +		map.vmflags.memory_tagging != 0,
> > > >    		map.filename.c_str (), obfd);
> > > >    	}
> > > >        }
> > > > @@ -1621,12 +1649,14 @@ static int
> > > >    linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST
> > > > size,
> > > >    				 ULONGEST offset, ULONGEST
> > > > inode,
> > > >    				 int read, int write, int exec,
> > > > int
> > > > modified,
> > > > +				 bool memory_tagged,
> > > >    				 const char *filename, void
> > > > *arg)
> > > >    {
> > > >      struct linux_find_memory_regions_data *data
> > > >        = (struct linux_find_memory_regions_data *) arg;
> > > >    
> > > > -  return data->func (vaddr, size, read, write, exec, modified,
> > > > data->obfd);
> > > > +  return data->func (vaddr, size, read, write, exec, modified,
> > > > memory_tagged,
> > > > +		     data->obfd);
> > > >    }
> > > >    
> > > >    /* A variant of linux_find_memory_regions_full that is
> > > > suitable
> > > > as the
> > > > @@ -1675,6 +1705,7 @@ static int
> > > >    linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size,
> > > >    			      ULONGEST offset, ULONGEST inode,
> > > >    			      int read, int write, int exec,
> > > > int
> > > > modified,
> > > > +			      bool memory_tagged,
> > > >    			      const char *filename, void *data)
> > > >    {
> > > >      struct linux_make_mappings_data *map_data
> > > > diff --git a/gdb/memtag.c b/gdb/memtag.c
> > > > new file mode 100644
> > > > index 00000000000..af86137c49d
> > > > --- /dev/null
> > > > +++ b/gdb/memtag.c
> > > > @@ -0,0 +1,61 @@
> > > > +/* GDB generic memory tagging functions.
> > > > +
> > > > +   Copyright (C) 2022 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 <
> > > > 
https://urldefense.com/v3/__http://www.gnu.org/licenses/__;!!CTRNKA9wMg0ARbw!0jBVgsoE-RaTGT098YRLfC3gcQJlvfuMujbUqmtqoh3JjeD2BI4JNxmWCGaefncNfKBbmw$
> > > >   >.  */
> > > > +
> > > > +#include "defs.h"
> > > > +#include "memtag.h"
> > > > +#include "bfd.h"
> > > > +
> > > > +/* See memtag.h */
> > > > +
> > > > +bool
> > > > +get_next_core_memtag_section (bfd *abfd, asection *section,
> > > > +			      CORE_ADDR address,
> > > > memtag_section_info
> > > > &info)
> > > > +{
> > > > +  /* If the caller provided no SECTION to start from, search
> > > > from
> > > > the
> > > > +     beginning.  */
> > > > +  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 memtag_range_size = section->rawsize;
> > > > +      size_t tags_size = bfd_section_size (section);
> > > > +
> > > > +      /* Empty memory range and empty tag dump should not
> > > > happen.  */
> > > > +      gdb_assert (memtag_range_size != 0);
> > > > +      gdb_assert (tags_size != 0);
> > > > +
> > > > +      CORE_ADDR start_address = bfd_section_vma (section);
> > > > +      CORE_ADDR end_address = start_address +
> > > > memtag_range_size;
> > > > +
> > > > +      /* 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.memtag_section = section;
> > > > +	  return true;
> > > > +	}
> > > > +      section = bfd_get_next_section_by_name (abfd, section);
> > > > +    }
> > > > +  return false;
> > > > +}
> > > > diff --git a/gdb/memtag.h b/gdb/memtag.h
> > > > new file mode 100644
> > > > index 00000000000..fe908c1e5e3
> > > > --- /dev/null
> > > > +++ b/gdb/memtag.h
> > > > @@ -0,0 +1,50 @@
> > > > +/* GDB generic memory tagging definitions.
> > > > +   Copyright (C) 2022 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 <
> > > > 
https://urldefense.com/v3/__http://www.gnu.org/licenses/__;!!CTRNKA9wMg0ARbw!0jBVgsoE-RaTGT098YRLfC3gcQJlvfuMujbUqmtqoh3JjeD2BI4JNxmWCGaefncNfKBbmw$
> > > >   >.  */
> > > > +
> > > > +#ifndef MEMTAG_H
> > > > +#define MEMTAG_H
> > > > +
> > > > +#include "bfd.h"
> > > > +
> > > > +struct memtag_section_info
> > > > +{
> > > > +  /* The start address of the tagged memory range.  */
> > > > +  CORE_ADDR start_address;
> > > > +  /* The final address of the tagged memory range.  */
> > > > +  CORE_ADDR end_address;
> > > > +  /* The section containing tags for the memory range
> > > > +     [start_address, end_address).  */
> > > > +  asection *memtag_section;
> > > > +};
> > > > +
> > > > +/* Helper function to walk through memory tag sections 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_section_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 <
> > > > 
https://urldefense.com/v3/__http://www.gnu.org/licenses/__;!!CTRNKA9wMg0ARbw!0jBVgsoE-RaTGT098YRLfC3gcQJlvfuMujbUqmtqoh3JjeD2BI4JNxmWCGaefncNfKBbmw$
> > > >   >.  */
> > > > +
> > > > +/* 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..8a19c4b449e
> > > > --- /dev/null
> > > > +++ b/gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
> > > > @@ -0,0 +1,107 @@
> > > > +# 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 <
> > > > 
https://urldefense.com/v3/__http://www.gnu.org/licenses/__;!!CTRNKA9wMg0ARbw!0jBVgsoE-RaTGT098YRLfC3gcQJlvfuMujbUqmtqoh3JjeD2BI4JNxmWCGaefncNfKBbmw$
> > > >   >.
> > > > +
> > > > +# 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
> > > > +
> > > > +# 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"
> > > > +}


  reply	other threads:[~2022-06-06  9:54 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-31 14:03 Luis Machado
2022-04-21 15:20 ` [PATCH, v2] " Luis Machado
2022-04-21 15:52   ` Eli Zaretskii
2022-04-22  8:12     ` Luis Machado
2022-04-22  8:30       ` Eli Zaretskii
2022-04-22  8:37         ` Luis Machado
2022-04-22  8:43           ` Eli Zaretskii
2022-04-22  8:44             ` Luis Machado
2022-04-22 13:27 ` [PATCH 1/2] " Luis Machado
2022-04-22 13:33   ` Eli Zaretskii
2022-04-22 13:30 ` [PATCH, v3] " Luis Machado
2022-05-03 21:56 ` [PATCH, v4] " Luis Machado
2022-05-12 10:36   ` Luis Machado
2022-05-18 12:46   ` Luis Machado
2022-05-18 13:58     ` John Baldwin
2022-05-23  9:50       ` Luis Machado
2022-05-23  9:49     ` Luis Machado
2022-06-06  9:28   ` Luis Machado
2022-06-06  9:42     ` Kuan-Ying Lee
2022-06-06  9:47       ` Luis Machado
2022-06-06  9:54         ` Kuan-Ying Lee [this message]
2022-06-06 10:49     ` Eli Zaretskii
2022-06-22  9:04   ` Luis Machado
2022-06-27 14:51   ` Pedro Alves
2022-07-11 10:13     ` Luis Machado
2022-07-11 10:57   ` [PATCH] [AArch64,v5] " Luis Machado
2022-07-18 13:54     ` Pedro Alves
2022-07-19 14:25       ` Luis Machado
  -- strict thread matches above, loose matches on Subject: below --
2021-05-18 20:20 [PATCH] [AArch64] " Luis Machado
2021-05-31 16:44 ` [PATCH,v4][AArch64] " Luis Machado

Reply instructions:

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

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

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

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

  git send-email \
    --in-reply-to=54830e42a402b2f3c694be25c51670c12a4bd181.camel@mediatek.com \
    --to=kuan-ying.lee@mediatek.com \
    --cc=gdb-patches@sourceware.org \
    --cc=luis.machado@arm.com \
    /path/to/YOUR_REPLY

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

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