Re: [PATCH v2 12/24] AArch64: Implement memory tagging target methods for AArch64

Alan Hayward via Gdb-patches
2020-10-29T14:22:14Z

> On 22 Oct 2020, at 21:00, Luis Machado <luis.machado@linaro.org> wrote:
>
> Updates on v2:
>
> - Added type parameter to the target method implementations.
>
> --
>
> The patch implements the memory tagging target hooks for AArch64, so we
> can handle MTE.
>
> gdb/ChangeLog:
>
> YYYY-MM-DD Luis Machado <luis.machado@linaro.org>
>
> * Makefile.in (ALL_64_TARGET_OBS): Add arch/aarch64-mte-linux.o.
> (HFILES_NO_SRCDIR): Add arch/aarch64-mte-linux.h and
> nat/aarch64-mte-linux-ptrace.h.
> * aarch64-linux-nat.c: Include nat/aarch64-mte-linux-ptrace.h.
> (aarch64_linux_nat_target) <supports_memory_tagging>: New method
> override.
> <fetch_memtags>: New method override.
> <store_memtags>: New method override.
> (aarch64_linux_nat_target::supports_memory_tagging): New method.
> (aarch64_linux_nat_target::fetch_memtags): New method.
> (aarch64_linux_nat_target::store_memtags): New method.
> * arch/aarch64-mte-linux.c: New file.
> * arch/aarch64-mte-linux.h: Include gdbsupport/common-defs.h.
> (MTE_GRANULE_SIZE): Define.
> (get_tag_granules): New prototype.
> * configure.nat (NATDEPFILES): Add nat/aarch64-mte-linux-ptrace.o.
> * configure.tgt (aarch64*-*-linux*): Add arch/aarch64-mte-linux.o.
> * nat/aarch64-mte-linux-ptrace.c: New file.
> * nat/aarch64-mte-linux-ptrace.h: New file.
> ---
> gdb/Makefile.in | 1 +
> gdb/aarch64-linux-nat.c | 50 ++++++++
> gdb/arch/aarch64-mte-linux.c | 34 +++++
> gdb/arch/aarch64-mte-linux.h | 10 ++
> gdb/configure.nat | 3 +-
> gdb/configure.tgt | 1 +
> gdb/nat/aarch64-mte-linux-ptrace.c | 200 +++++++++++++++++++++++++++++
> gdb/nat/aarch64-mte-linux-ptrace.h | 17 +++
> 8 files changed, 315 insertions(+), 1 deletion(-)
> create mode 100644 gdb/arch/aarch64-mte-linux.c
> create mode 100644 gdb/nat/aarch64-mte-linux-ptrace.c
>
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index 8c9e6c9f6c..33a08a2288 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -692,6 +692,7 @@ ALL_64_TARGET_OBS = \
> amd64-windows-tdep.o \
> arch/aarch64.o \
> arch/aarch64-insn.o \
> + arch/aarch64-mte-linux.o \
> arch/amd64.o \
> ia64-linux-tdep.o \
> ia64-tdep.o \
> diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
> index dea34da669..4edf5a0454 100644
> --- a/gdb/aarch64-linux-nat.c
> +++ b/gdb/aarch64-linux-nat.c
> @@ -52,6 +52,8 @@
>
> #include "arch/aarch64-mte-linux.h"
>
> +#include "nat/aarch64-mte-linux-ptrace.h"
> +
> #ifndef TRAP_HWBKPT
> #define TRAP_HWBKPT 0x0004
> #endif
> @@ -102,6 +104,16 @@ class aarch64_linux_nat_target final : public linux_nat_target
> override;
>
> struct gdbarch *thread_architecture (ptid_t) override;
> +
> + bool supports_memory_tagging () override;
> +
> + /* Read memory allocation tags from memory via PTRACE. */
> + int fetch_memtags (CORE_ADDR address, size_t len,
> + gdb::byte_vector &tags, int type) override;
> +
> + /* Write allocation tags to memory via PTRACE. */
> + int store_memtags (CORE_ADDR address, size_t len,
> + const gdb::byte_vector &tags, int type) override;
> };
>
> static aarch64_linux_nat_target the_aarch64_linux_nat_target;
> @@ -1050,6 +1062,44 @@ aarch64_linux_nat_target::thread_architecture (ptid_t ptid)
> return gdbarch_find_by_info (info);
> }
>
> +/* Implement the "supports_memory_tagging" target_ops method. */
> +
> +bool
> +aarch64_linux_nat_target::supports_memory_tagging ()
> +{
> + return (linux_get_hwcap2 (this) & HWCAP2_MTE) != 0;
> +}
> +
> +/* Implement the "fetch_memtags" target_ops method. */
> +
> +int
> +aarch64_linux_nat_target::fetch_memtags (CORE_ADDR address, size_t len,
> + gdb::byte_vector &tags, int type)

I’m a little unsure as to where the type is coming from. Who in the call stack
is explicitly passing the value 1?
It’s different from the LOGICAL and ALLOCATION enum values used elsewhere?

> +{
> + int tid = inferior_ptid.lwp ();
> +
> + /* Allocation tags? */
> + if (type == 1)
> + return aarch64_mte_fetch_memtags (tid, address, len, tags);
> +
> + return 1;
> +}
> +
> +/* Implement the "store_memtags" target_ops method. */
> +
> +int
> +aarch64_linux_nat_target::store_memtags (CORE_ADDR address, size_t len,
> + const gdb::byte_vector &tags, int type)
> +{
> + int tid = inferior_ptid.lwp ();
> +
> + /* Allocation tags? */
> + if (type == 1)
> + return aarch64_mte_store_memtags (tid, address, len, tags);
> +
> + return 1;
> +}
> +
> /* Define AArch64 maintenance commands. */
>
> static void
> diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-mte-linux.c
> new file mode 100644
> index 0000000000..ee162aef19
> --- /dev/null
> +++ b/gdb/arch/aarch64-mte-linux.c
> @@ -0,0 +1,34 @@
> +/* Common Linux target-dependent functionality for AArch64 MTE
> +
> + Copyright (C) 2020 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 "arch/aarch64-mte-linux.h"
> +
> +/* See arch/aarch64-mte-linux.h */
> +
> +size_t
> +get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size)
> +{
> + /* Start address */
> + CORE_ADDR s_addr = align_down (addr, granule_size);
> + /* End address */
> + CORE_ADDR e_addr = align_down (addr + len, granule_size);
> +
> + /* We always have at least 1 granule. */
> + return 1 + (e_addr - s_addr) / granule_size;
> +}
> diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h
> index 4124e80543..e555f0af19 100644
> --- a/gdb/arch/aarch64-mte-linux.h
> +++ b/gdb/arch/aarch64-mte-linux.h
> @@ -20,6 +20,8 @@
> #ifndef ARCH_AARCH64_LINUX_H
> #define ARCH_AARCH64_LINUX_H
>
> +#include "gdbsupport/common-defs.h"
> +
> /* Feature check for Memory Tagging Extension. */
> #ifndef HWCAP2_MTE
> #define HWCAP2_MTE (1 << 18)
> @@ -28,4 +30,12 @@
> /* The MTE regset consists of a single 64-bit register. */
> #define AARCH64_LINUX_SIZEOF_MTE 8
>
> +/* We have one tag per 16 bytes of memory. */
> +#define MTE_GRANULE_SIZE 16
> +
> +/* Return the number of tag granules in the memory range
> + [ADDR, ADDR + LEN) given GRANULE_SIZE. */
> +extern size_t get_tag_granules (CORE_ADDR addr, size_t len,
> + size_t granule_size);
> +
> #endif /* ARCH_AARCH64_LINUX_H */
> diff --git a/gdb/configure.nat b/gdb/configure.nat
> index ef2218f0b8..f973cc7f9b 100644
> --- a/gdb/configure.nat
> +++ b/gdb/configure.nat
> @@ -236,7 +236,8 @@ case ${gdb_host} in
> NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
> aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
> nat/aarch64-linux.o \
> - nat/aarch64-sve-linux-ptrace.o"
> + nat/aarch64-sve-linux-ptrace.o \
> + nat/aarch64-mte-linux-ptrace.o"
> ;;
> arm)
> # Host: ARM based machine running GNU/Linux
> diff --git a/gdb/configure.tgt b/gdb/configure.tgt
> index d865ecdcb6..11a26b8551 100644
> --- a/gdb/configure.tgt
> +++ b/gdb/configure.tgt
> @@ -124,6 +124,7 @@ aarch64*-*-freebsd*)
> aarch64*-*-linux*)
> # Target: AArch64 linux
> gdb_target_obs="aarch64-linux-tdep.o arch/aarch64.o\
> + arch/aarch64-mte-linux.o \
> arch/arm.o arch/arm-linux.o arch/arm-get-next-pcs.o \
> arm-tdep.o arm-linux-tdep.o \
> glibc-tdep.o linux-tdep.o solib-svr4.o \
> diff --git a/gdb/nat/aarch64-mte-linux-ptrace.c b/gdb/nat/aarch64-mte-linux-ptrace.c
> new file mode 100644
> index 0000000000..f92028176d
> --- /dev/null
> +++ b/gdb/nat/aarch64-mte-linux-ptrace.c
> @@ -0,0 +1,200 @@
> +/* Common Linux native ptrace code for AArch64 MTE.
> +
> + Copyright (C) 2020 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 "gdbsupport/common-defs.h"
> +#include "gdbsupport/byte-vector.h"
> +
> +#include "arch/aarch64.h"
> +#include "arch/aarch64-mte-linux.h"
> +#include "nat/aarch64-linux.h"
> +#include "nat/aarch64-mte-linux-ptrace.h"
> +
> +#include "linux-ptrace.h"
> +#include <sys/uio.h>
> +
> +/* Helper function to display various possible errors when reading
> + MTE tags. */
> +
> +static void
> +aarch64_mte_linux_peek_error (int error)
> +{
> + switch (error)
> + {
> + case EIO:
> + perror_with_name (_("PEEKMTETAGS not supported"));
> + break;
> + case EFAULT:
> + perror_with_name (_("Couldn't fetch allocation tags"));
> + break;
> + case EOPNOTSUPP:
> + perror_with_name (_("PROT_ME not enabled for requested address"));
> + default:
> + perror_with_name (_("Unknown MTE error"));
> + break;
> + }
> +}
> +
> +/* Helper function to display various possible errors when writing
> + MTE tags. */
> +
> +static void
> +aarch64_mte_linux_poke_error (int error)
> +{
> + switch (error)
> + {
> + case EIO:
> + perror_with_name (_("POKEMTETAGS not supported"));
> + break;
> + case EFAULT:
> + perror_with_name (_("Couldn't store allocation tags"));
> + break;
> + case EOPNOTSUPP:
> + perror_with_name (_("PROT_ME not enabled for requested address"));
> + default:
> + perror_with_name (_("Unknown MTE error"));
> + break;
> + }
> +}
> +
> +/* Helper to prepare a vector of tags to be passed on to the kernel. The
> + main purpose of this function is to optimize the number of calls to
> + ptrace if we're writing too many tags at once, like a pattern fill
> + request.
> +
> + Return a vector of tags of up to MAX_SIZE size, containing the tags that
> + must be passed on to the kernel, extracted from TAGS, starting at POS.
> + GRANULES is the number of tag granules to be modified. */
> +
> +static gdb::byte_vector
> +prepare_tag_vector (size_t granules, const gdb::byte_vector &tags, size_t pos,
> + size_t max_size)
> +{
> + gdb::byte_vector t;
> +
> + if (granules == 0)
> + {
> + t.clear ();
> + return t;
> + }
> +
> + gdb_assert (tags.size () > 0 && max_size > 0);
> +
> + if (granules > TAGS_MAX_SIZE)
> + t.resize (TAGS_MAX_SIZE);
> + else
> + t.resize (granules);
> +
> + size_t tag_count = tags.size ();
> +
> + for (size_t i = 0; i < t.size (); i++)
> + t[i] = tags[(pos + i) % tag_count];
> +
> + return t;
> +}
> +
> +/* See nat/aarch64-mte-linux-ptrace.h */
> +
> +int
> +aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len,
> + gdb::byte_vector &tags)
> +{
> + size_t ntags = get_tag_granules (address, len, MTE_GRANULE_SIZE);
> + gdb_byte tagbuf[ntags];
> +
> + struct iovec iovec;
> + iovec.iov_base = tagbuf;
> + iovec.iov_len = ntags;
> +
> + tags.clear ();
> + bool done_reading = false;
> +
> + /* The kernel may return less tags than we requested. Loop until we've read
> + all the requested tags or until we get an error. */
> + while (!done_reading)
> + {
> + /* Attempt to read ntags allocation tags from the kernel. */
> + if (ptrace (PTRACE_PEEKMTETAGS, tid, address, &iovec) < 0)
> + aarch64_mte_linux_peek_error (errno);
> +
> + /* Make sure the kernel returned at least one tag. */
> + if (iovec.iov_len <= 0)
> + {
> + tags.clear ();
> + return 1;
> + }
> +
> + /* Copy the tags the kernel returned. */
> + for (size_t i = 0; i < iovec.iov_len; i++)
> + tags.push_back (tagbuf[i]);
> +
> + /* Are we done reading tags? */
> + if (tags.size () == ntags)
> + done_reading = true;
> + else
> + {
> + address += iovec.iov_len * MTE_GRANULE_SIZE;
> + iovec.iov_len = ntags - iovec.iov_len;
> + }
> + }
> + return 0;
> +}
> +
> +/* See nat/aarch64-mte-linux-ptrace.h */
> +
> +int
> +aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len,
> + const gdb::byte_vector &tags)
> +{
> + if (tags.size () == 0)
> + return 0;
> +
> + /* Get the number of tags we need to write. */
> + size_t ntags = get_tag_granules (address, len, MTE_GRANULE_SIZE);
> + bool done_writing = false;
> + size_t tags_written = 0;
> +
> + /* Write all the tags, TAGS_MAX_SIZE blocks at a time. */
> + while (!done_writing)
> + {
> + gdb::byte_vector t = prepare_tag_vector (ntags - tags_written, tags,
> + tags_written, TAGS_MAX_SIZE);
> +
> + struct iovec iovec;
> + iovec.iov_base = t.data ();
> + iovec.iov_len = t.size ();
> +
> + /* Request the kernel to update the allocation tags. */
> + if (ptrace (PTRACE_POKEMTETAGS, tid, address, &iovec) < 0)
> + aarch64_mte_linux_poke_error (errno);
> +
> + /* Make sure the kernel wrote at least one tag. */
> + if (iovec.iov_len <= 0)
> + return 1;
> +
> + tags_written += iovec.iov_len;
> +
> + /* Are we done writing tags? */
> + if (tags_written == ntags)
> + done_writing = true;
> + else
> + address += iovec.iov_len * MTE_GRANULE_SIZE;
> + }
> +
> + return 0;
> +}
> diff --git a/gdb/nat/aarch64-mte-linux-ptrace.h b/gdb/nat/aarch64-mte-linux-ptrace.h
> index 099b6440ca..7ba6f014f6 100644
> --- a/gdb/nat/aarch64-mte-linux-ptrace.h
> +++ b/gdb/nat/aarch64-mte-linux-ptrace.h
> @@ -30,4 +30,21 @@
> #define PTRACE_POKEMTETAGS 34
> #endif
>
> +/* Maximum number of tags to pass at once to the kernel. */
> +#define TAGS_MAX_SIZE 4096
> +
> +/* Read the allocation tags from memory range [ADDRESS, ADDRESS + LEN)
> + into TAGS.
> +
> + Return 0 if successful and non-zero otherwise. */
> +extern int aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len,
> + gdb::byte_vector &tags);
> +
> +/* Write the TAGS allocation tags to the memory range
> + [ADDRESS, ADDRESS + LEN).
> +
> + Return 0 if successful and non-zero otherwise. */
> +extern int aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len,
> + const gdb::byte_vector &tags);
> +
> #endif /* NAT_AARCH64_MTE_LINUX_PTRACE_H */
> --
> 2.17.1
>