diff options
author | Luis Machado <luis.machado@linaro.org> | 2020-06-19 17:33:13 -0300 |
---|---|---|
committer | Luis Machado <luis.machado@linaro.org> | 2021-03-24 14:53:56 -0300 |
commit | 4601818e8c06bb8a5bf4b63fa527c69d3f81c6f0 (patch) | |
tree | a8650e0d1beb52fdf709a0198be63a61d29f4b49 /gdb/nat | |
parent | 3f3bd8b8c14d844533b70b25c7f1a8cbdbac2639 (diff) | |
download | gdb-4601818e8c06bb8a5bf4b63fa527c69d3f81c6f0.zip gdb-4601818e8c06bb8a5bf4b63fa527c69d3f81c6f0.tar.gz gdb-4601818e8c06bb8a5bf4b63fa527c69d3f81c6f0.tar.bz2 |
AArch64: Implement memory tagging target methods for AArch64
The patch implements the memory tagging target hooks for AArch64, so we
can handle MTE.
gdb/ChangeLog:
2021-03-24 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.
(AARCH64_MTE_GRANULE_SIZE): Define.
(aarch64_memtag_type): New enum.
(aarch64_mte_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.
Diffstat (limited to 'gdb/nat')
-rw-r--r-- | gdb/nat/aarch64-mte-linux-ptrace.c | 210 | ||||
-rw-r--r-- | gdb/nat/aarch64-mte-linux-ptrace.h | 17 |
2 files changed, 227 insertions, 0 deletions
diff --git a/gdb/nat/aarch64-mte-linux-ptrace.c b/gdb/nat/aarch64-mte-linux-ptrace.c new file mode 100644 index 0000000..47f4ab6 --- /dev/null +++ b/gdb/nat/aarch64-mte-linux-ptrace.c @@ -0,0 +1,210 @@ +/* Common Linux native ptrace code for AArch64 MTE. + + Copyright (C) 2021 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 ATTRIBUTE_NORETURN +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 ATTRIBUTE_NORETURN +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) + return t; + + gdb_assert (tags.size () > 0 && max_size > 0); + + if (granules > AARCH64_MTE_TAGS_MAX_SIZE) + t.resize (AARCH64_MTE_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 */ + +bool +aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len, + gdb::byte_vector &tags) +{ + size_t ntags = aarch64_mte_get_tag_granules (address, len, + AARCH64_MTE_GRANULE_SIZE); + + /* If the memory range contains no tags, nothing left to do. */ + if (ntags == 0) + return true; + + 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 false; + } + + /* 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 * AARCH64_MTE_GRANULE_SIZE; + iovec.iov_len = ntags - iovec.iov_len; + } + } + return true; +} + +/* See nat/aarch64-mte-linux-ptrace.h */ + +bool +aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len, + const gdb::byte_vector &tags) +{ + if (tags.size () == 0) + return true; + + /* Get the number of tags we need to write. */ + size_t ntags = aarch64_mte_get_tag_granules (address, len, + AARCH64_MTE_GRANULE_SIZE); + + /* If the memory range contains no tags, nothing left to do. */ + if (ntags == 0) + return true; + + bool done_writing = false; + size_t tags_written = 0; + + /* Write all the tags, AARCH64_MTE_TAGS_MAX_SIZE blocks at a time. */ + while (!done_writing) + { + gdb::byte_vector t = prepare_tag_vector (ntags - tags_written, tags, + tags_written, + AARCH64_MTE_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 false; + + tags_written += iovec.iov_len; + + /* Are we done writing tags? */ + if (tags_written == ntags) + done_writing = true; + else + address += iovec.iov_len * AARCH64_MTE_GRANULE_SIZE; + } + + return true; +} diff --git a/gdb/nat/aarch64-mte-linux-ptrace.h b/gdb/nat/aarch64-mte-linux-ptrace.h index 65ac141..f8c203b 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 AARCH64_MTE_TAGS_MAX_SIZE 4096 + +/* Read the allocation tags from memory range [ADDRESS, ADDRESS + LEN) + into TAGS. + + Returns true if successful and false otherwise. */ +extern bool aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len, + gdb::byte_vector &tags); + +/* Write the allocation tags contained in TAGS into the memory range + [ADDRESS, ADDRESS + LEN). + + Returns true if successful and false otherwise. */ +extern bool aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len, + const gdb::byte_vector &tags); + #endif /* NAT_AARCH64_MTE_LINUX_PTRACE_H */ |