public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Pedro Alves <pedro@palves.net>
To: Luis Machado <luis.machado@arm.com>, gdb-patches@sourceware.org
Subject: Re: [PATCH, v4] [AArch64] MTE corefile support
Date: Mon, 27 Jun 2022 15:51:00 +0100	[thread overview]
Message-ID: <c529aa29-1942-b196-7387-3f8c84f3158c@palves.net> (raw)
In-Reply-To: <20220503215632.914608-1-luis.machado@arm.com>

Hi Luis,

On 2022-05-03 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.

It's "memtag", not ".memtag", AFAICT.

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

Shouldn't the /2 division round up, rather than down, to account for odd number
of tags?  I.e., this:

   bfd_set_section_size (mte_section, (size / AARCH64_MTE_GRANULE_SIZE + 1) / 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);

How about using bfd_make_section_anyway_with_flags further above?

> +
> +  /* Store program header information.  */
> +  bfd_record_phdr (obfd, PT_AARCH64_MEMTAG_MTE, 1, 0, 0, 0, 0, 0, 1,
> +		   &mte_section);
> +
> +  return mte_section;
> +}
> +


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

'?' and ':' go on the next line, like other operators.  Wrap expression in
parens for proper alignment, per GNU standards.  So:

  = ((granules >= MAX_TAGS_TO_TRANSFER)
     ? MAX_TAGS_TO_TRANSFER * AARCH64_MTE_GRANULE_SIZE
     : granules * AARCH64_MTE_GRANULE_SIZE);

(note how you can read "?" as "then" and ":" as "else", this way.)

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

(Just a passing comment, feel free to ignore: If target_fetch_memtags were adjusted to
append to the buffer instead of clearing it, you could pass "tags" directly to
target_fetch_memtags and get rid of this copying.)

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

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

All other declarations in the header have explicit "extern".

> +
>  #endif /* ARCH_AARCH64_LINUX_H */


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

Space before "?".

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


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

Seems like this could happen with corrupted input?  GDB should not assert
on bad input.

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


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

2022 missing.


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

2021 -> 2022.

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

I'd suggest also making sure gdb is able to read kernel-generated core properly.
You can use core_find to produce one.

  parent reply	other threads:[~2022-06-27 14:51 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
2022-06-06 10:49     ` Eli Zaretskii
2022-06-22  9:04   ` Luis Machado
2022-06-27 14:51   ` Pedro Alves [this message]
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=c529aa29-1942-b196-7387-3f8c84f3158c@palves.net \
    --to=pedro@palves.net \
    --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).