diff options
author | Luis Machado <luis.machado@arm.com> | 2022-03-31 11:42:35 +0100 |
---|---|---|
committer | Luis Machado <luis.machado@arm.com> | 2022-07-19 15:24:31 +0100 |
commit | 68cffbbd4406b4efe1aa6e18460b1d7ca02549f1 (patch) | |
tree | f8a61526011db5bf0c60314f38de6fc48cd82ca0 /gdb/gcore.c | |
parent | d0ff5ca959df91dcef16ec57154ff199fad5a4e4 (diff) | |
download | fsf-binutils-gdb-68cffbbd4406b4efe1aa6e18460b1d7ca02549f1.zip fsf-binutils-gdb-68cffbbd4406b4efe1aa6e18460b1d7ca02549f1.tar.gz fsf-binutils-gdb-68cffbbd4406b4efe1aa6e18460b1d7ca02549f1.tar.bz2 |
[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)
Diffstat (limited to 'gdb/gcore.c')
-rw-r--r-- | gdb/gcore.c | 83 |
1 files changed, 80 insertions, 3 deletions
diff --git a/gdb/gcore.c b/gdb/gcore.c index fdb22b7..b81ef81 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; } |