[PATCH, v4] [AArch64] MTE corefile support
John Baldwin
jhb@FreeBSD.org
Wed May 18 13:58:00 GMT 2022
From my perspective this looks ok.
On 5/18/22 5:46 AM, Luis Machado via Gdb-patches wrote:
> Ping? The binutils patch has been approved, but I'd like to push both at the same time.
>
> 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_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;
>> +
>> + 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_GRANULE_SIZE);
>> + /* 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_signal_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_memtag_section);
>> }
>>
>> /* 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_section_callback,
>> + 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_section_ftype 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_ftype 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_section_ftype 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 <http://www.gnu.org/licenses/>. */
>> +
>> +#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 <http://www.gnu.org/licenses/>. */
>> +
>> +#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 <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..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 <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
>> +
>> +# 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"
>> +}
>
--
John Baldwin
More information about the Gdb-patches
mailing list