[AArch64] MTE corefile support

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)
This commit is contained in:
Luis Machado
2022-03-31 11:42:35 +01:00
parent d0ff5ca959
commit 68cffbbd44
17 changed files with 1048 additions and 8 deletions

View File

@@ -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 @@ public:
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. */
@@ -1177,6 +1185,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. */