[PATCH,v5][AArch64] MTE corefile support
Alan Hayward
Alan.Hayward@arm.com
Thu Jun 24 15:18:26 GMT 2021
On 24 Jun 2021, at 15:37, Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>> wrote:
On 6/24/21 11:00 AM, Alan Hayward wrote:
On 1 Jun 2021, at 18:45, Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>> wrote:
Updates on v5:
- Fixed format warning output.
Updates on v4:
- Calculate sizes based on individual struct field sizes.
Updates on v3:
- Addressed review comments.
- New files gdbsupport/memtag.h, gdb/memtag.h and gdb/memtag.c.
- Updated code documentation.
- Removed code duplication.
Updates on v2:
- Reworked core_target::fetch_memtags to handle cases where address + len runs
over the NT_MEMTAG note.
- Turned a few magic numbers into constants. There is an unfortunate duplication
of a constant (NT_MEMTAG_HEADER_SIZE). It is a generic constant, but there is
no file generic enough that gets included by both corelow and linux-tdep.
- More sanity checks to make sure the note format is correct.
- Documented aarch64_linux_decode_memtag_note a little more.
---
Teach GDB how to dump memory tags when using the gcore command and how
to read them back from a core file generated via gcore or the kernel.
Each tagged memory range (listed in /proc/<pid>/smaps) gets dumped to its
own NT_MEMTAG note. A section named ".memtag" is created for each of those
when reading the core file back.
Dumping memory tags
-
When using the gcore command to dump a core file, GDB will go through the maps
in /proc/<pid>/smaps looking for tagged ranges. Each of those entries gets
passed to an arch-specific gdbarch hook that generates a vector of blobs of
memory tag data that are blindly put into a NT_MEMTAG note.
The vector is used because we may have, in the future, multiple tag types for
a particular memory range.
Each of the NT_MEMTAG notes have a generic header and a arch-specific header,
like so:
struct tag_dump_header
{
uint16_t format; // Only NT_MEMTAG_TYPE_AARCH_MTE at present
uint64_t start_vma;
uint64_t end_vma;
};
struct tag_dump_mte
{
uint16_t granule_byte_size;
uint16_t tag_bit_size;
uint16_t __unused;
};
The only bits meant to be generic are the tag_dump_format, start_vma and
end_vma fields.
The format-specific data is supposed to be opaque and only useful for the
arch-specific code.
We can extend the format in the future to make room for other memory tag
layouts.
Reading memory tags
-
When reading a core file that contains NT_MEMTAG entries, GDB will use
a different approach to check for tagged memory range. Rather than looking
at /proc/<pid>/smaps, it will now look for ".memtag" sections with the right
memory range.
When reading tags, GDB will now use the core target's implementation of
fetch_memtags (store_memtags doesn't exist for core targets). Then the data
is fed into an arch-specific hook that will decode the memory tag format and
return a vector of tags.
I've added a test to exercise writing and reading of memory tags in core
files.
gdb/ChangeLog:
YYYY-MM-DD Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>>
* Makefile.in (COMMON_SFILES): Add memtag.c.
* NEWS: Mention core file support for memory tagging.
* aarch64-linux-tdep.c: Include elf/common.h.
Include gdbsupport/memtag.h.
(MAX_TAGS_TO_TRANSFER): New constant.
(aarch64_linux_create_memtag_notes_from_range): New function.
(aarch64_linux_decode_memtag_note): Likewise.
(aarch64_linux_init_abi): Register new core file hooks.
(NT_MEMTAG_TOTAL_HEADER_SIZE): New constant.
* arch/aarch64-mte-linux.h (tag_dump_mte): New struct.
(AARCH64_MTE_TAG_BIT_SIZE): New constant.
* corelow.c: Include gdbsupport/memtag.h and memtag.h.
(core_target) <supports_memory_tagging, fetch_memtags>: New
method overrides.
* gdbarch.c: Regenerate.
* gdbarch.h: Likewise.
* gdbarch.sh (create_memtag_notes_from_range): New hook.
(decode_memtag_note): Likewise.
* linux-tdep.c: Include gdbsupport/memtag.h and memtag.h.
(linux_address_in_memtag_page): Renamed to...
(linux_process_address_in_memtag_page): ... this.
(linux_core_file_address_in_memtag_page): New function.
(linux_address_in_memtag_page): Likewise.
(linux_make_memtag_corefile_notes): Likewise.
(linux_make_corefile_notes): Handle memory tag notes.
* memtag.c: New file.
* memtag.h: New file.
gdb/doc/ChangeLog:
YYYY-MM-DD Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>>
* gdb.texinfo (AArch64 Memory Tagging Extension): Mention support
for memory tagging in core files.
gdb/testsuite/ChangeLog:
YYYY-MM-DD Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>>
* gdb.arch/aarch64-mte-gcore.c: New file.
* gdb.arch/aarch64-mte-gcore.exp: New file.
gdbsupport/ChangeLog:
YYYY-MM-DD Luis Machado <luis.machado@linaro.org<mailto:luis.machado@linaro.org>>
* memtag.h: New file.
---
gdb/Makefile.in | 1 +
gdb/NEWS | 4 +
gdb/aarch64-linux-tdep.c | 221 +++++++++++++++++++
gdb/arch/aarch64-mte-linux.h | 17 ++
gdb/corelow.c | 63 ++++++
gdb/doc/gdb.texinfo | 4 +
gdb/gdbarch.c | 64 ++++++
gdb/gdbarch.h | 16 ++
gdb/gdbarch.sh | 6 +
gdb/linux-tdep.c | 97 +++++++-
gdb/memtag.c | 88 ++++++++
gdb/memtag.h | 46 ++++
gdb/testsuite/gdb.arch/aarch64-mte-gcore.c | 93 ++++++++
gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp | 111 ++++++++++
gdbsupport/memtag.h | 39 ++++
15 files changed, 867 insertions(+), 3 deletions(-)
create mode 100644 gdb/memtag.c
create mode 100644 gdb/memtag.h
create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.c
create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte-gcore.exp
create mode 100644 gdbsupport/memtag.h
Note there were a few minor merge conflicts, nothing to worry about though.
I'll check again and will refresh the patch.
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f664d964536..12fb3b390b1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1100,6 +1100,7 @@ COMMON_SFILES = \
memattr.c \
memory-map.c \
memrange.c \
+ memtag.c \
minidebug.c \
minsyms.c \
mipsread.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index ab678acec8b..58b9f739d4f 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -3,6 +3,10 @@
*** Changes since GDB 10
+* GDB now supports dumping memory tag data for AArch64 MTE. It also supports
+ reading memory tag data for AArch64 MTE from core files generated by
+ the gcore command or the Linux kernel.
+
* GDB now supports general memory tagging functionality if the underlying
architecture supports the proper primitives and hooks. Currently this is
enabled only for AArch64 MTE.
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index e9761ed2189..04498f3b6c0 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -52,6 +52,9 @@
#include "value.h"
#include "gdbsupport/selftest.h"
+#include "gdbsupport/memtag.h"
+
+#include "elf/common.h"
/* Signal frame handling.
@@ -1779,6 +1782,213 @@ aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
}
}
+/* Memory tag note header size. Includes both the generic and the
+ arch-specific parts. */
+#define NT_MEMTAG_TOTAL_HEADER_SIZE (NT_MEMTAG_GENERIC_HEADER_SIZE \
+ + NT_MEMTAG_MTE_HEADER_SIZE)
+
+/* Maximum number of tags to request. */
+#define MAX_TAGS_TO_TRANSFER 1024
+
+/* AArch64 Linux implementation of the aarch64_create_memtag_notes_from_range
+ gdbarch hook. Create core file notes for memory tags. */
+
+static std::vector<gdb::byte_vector>
+aarch64_linux_create_memtag_notes_from_range (struct gdbarch *gdbarch,
+ CORE_ADDR start_address,
+ CORE_ADDR end_address)
+{
+ /* We only handle MTE tags for now. */
+
+ /* Figure out how many tags we need to store in this memory range. */
+ size_t granules = aarch64_mte_get_tag_granules (start_address,
+ end_address - start_address,
+ AARCH64_MTE_GRANULE_SIZE);
+
+ /* Vector of memory tag notes. Add the MTE note (we only have MTE tags
+ at the moment). */
+ std::vector<gdb::byte_vector> notes (1);
+
+ /* If there are no tag granules to fetch, just return. */
+ if (granules == 0)
+ return notes;
+
+ /* Adjust the MTE note size to hold the header + tags. */
+ notes[0].resize (NT_MEMTAG_TOTAL_HEADER_SIZE + granules);
Should this be NT_MEMTAG_TOTAL_HEADER_SIZE + (granules * AARCH64_MTE_GRANULE_SIZE)
Get_granules has already divided by the granule size.
We are storing 1 tag per byte. So the number of granules is the number of tags. If we multiply by AARCH64_MTE_GRANULE_SIZE, we will have 16 times more tags.
Ahh, right.
(Of course - this sort of buffer overrun is exactly what mte prevents :) )
+
+ CORE_ADDR address = start_address;
+ /* Vector of tags. */
+ gdb::byte_vector tags;
+
+ while (granules > 0)
+ {
+ /* Transfer tags in chunks. */
+ gdb::byte_vector tags_read;
+ size_t xfer_len
+ = (granules >= MAX_TAGS_TO_TRANSFER)?
+ MAX_TAGS_TO_TRANSFER * AARCH64_MTE_GRANULE_SIZE :
+ granules * AARCH64_MTE_GRANULE_SIZE;
+
+ if (!target_fetch_memtags (address, xfer_len, tags_read,
+ static_cast<int> (memtag_type::allocation)))
+ {
+ warning (_("Failed to read MTE tags from memory range [%s,%s]."),
+ phex_nz (start_address, sizeof (start_address)),
+ phex_nz (end_address, sizeof (end_address)));
+ notes.resize (0);
If you do the original resize after the while loop, then there would be no need to resize here.
I'm not sure I understand. Where do you suggest the resizing to be placed?
So something like:
std::vector<gdb::byte_vector> notes (1);
while (granules > 0)
{
...
If (!fetch())
return notes
...
}
notes[0].resize (NT_MEMTAG_TOTAL_HEADER_SIZE + all_granules);
Now I write it out, you’ll need to keep a copy of the original number of granules.
Given this is only going to happen on a rare failure, then maybe its not worth the change.
Ok, happy with all those then.
Rest of the patch looks good to me too.
Alan.
More information about the Gdb-patches
mailing list