Home | History | Annotate | Line # | Download | only in nat
      1      1.1  christos /* Common Linux native ptrace code for AArch64 MTE.
      2      1.1  christos 
      3  1.1.1.2  christos    Copyright (C) 2021-2024 Free Software Foundation, Inc.
      4      1.1  christos 
      5      1.1  christos    This file is part of GDB.
      6      1.1  christos 
      7      1.1  christos    This program is free software; you can redistribute it and/or modify
      8      1.1  christos    it under the terms of the GNU General Public License as published by
      9      1.1  christos    the Free Software Foundation; either version 3 of the License, or
     10      1.1  christos    (at your option) any later version.
     11      1.1  christos 
     12      1.1  christos    This program is distributed in the hope that it will be useful,
     13      1.1  christos    but WITHOUT ANY WARRANTY; without even the implied warranty of
     14      1.1  christos    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     15      1.1  christos    GNU General Public License for more details.
     16      1.1  christos 
     17      1.1  christos    You should have received a copy of the GNU General Public License
     18      1.1  christos    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
     19      1.1  christos 
     20      1.1  christos #include "gdbsupport/byte-vector.h"
     21      1.1  christos 
     22      1.1  christos #include "linux-ptrace.h"
     23      1.1  christos 
     24      1.1  christos #include "arch/aarch64.h"
     25      1.1  christos #include "arch/aarch64-mte-linux.h"
     26      1.1  christos #include "nat/aarch64-linux.h"
     27      1.1  christos #include "nat/aarch64-mte-linux-ptrace.h"
     28      1.1  christos 
     29      1.1  christos #include <sys/uio.h>
     30      1.1  christos 
     31      1.1  christos /* Helper function to display various possible errors when reading
     32      1.1  christos    MTE tags.  */
     33      1.1  christos 
     34      1.1  christos static void ATTRIBUTE_NORETURN
     35      1.1  christos aarch64_mte_linux_peek_error (int error)
     36      1.1  christos {
     37      1.1  christos   switch (error)
     38      1.1  christos     {
     39      1.1  christos     case EIO:
     40      1.1  christos       perror_with_name (_("PEEKMTETAGS not supported"));
     41      1.1  christos       break;
     42      1.1  christos     case EFAULT:
     43      1.1  christos       perror_with_name (_("Couldn't fetch allocation tags"));
     44      1.1  christos       break;
     45      1.1  christos     case EOPNOTSUPP:
     46      1.1  christos       perror_with_name (_("PROT_MTE not enabled for requested address"));
     47      1.1  christos     default:
     48      1.1  christos       perror_with_name (_("Unknown MTE error"));
     49      1.1  christos       break;
     50      1.1  christos     }
     51      1.1  christos }
     52      1.1  christos 
     53      1.1  christos /* Helper function to display various possible errors when writing
     54      1.1  christos    MTE tags.  */
     55      1.1  christos 
     56      1.1  christos static void ATTRIBUTE_NORETURN
     57      1.1  christos aarch64_mte_linux_poke_error (int error)
     58      1.1  christos {
     59      1.1  christos   switch (error)
     60      1.1  christos     {
     61      1.1  christos     case EIO:
     62      1.1  christos       perror_with_name (_("POKEMTETAGS not supported"));
     63      1.1  christos       break;
     64      1.1  christos     case EFAULT:
     65      1.1  christos       perror_with_name (_("Couldn't store allocation tags"));
     66      1.1  christos       break;
     67      1.1  christos     case EOPNOTSUPP:
     68      1.1  christos       perror_with_name (_("PROT_MTE not enabled for requested address"));
     69      1.1  christos     default:
     70      1.1  christos       perror_with_name (_("Unknown MTE error"));
     71      1.1  christos       break;
     72      1.1  christos     }
     73      1.1  christos }
     74      1.1  christos 
     75      1.1  christos /* Helper to prepare a vector of tags to be passed on to the kernel.  The
     76      1.1  christos    main purpose of this function is to optimize the number of calls to
     77      1.1  christos    ptrace if we're writing too many tags at once, like a pattern fill
     78      1.1  christos    request.
     79      1.1  christos 
     80      1.1  christos    Return a vector of tags of up to MAX_SIZE size, containing the tags that
     81      1.1  christos    must be passed on to the kernel, extracted from TAGS, starting at POS.
     82      1.1  christos    GRANULES is the number of tag granules to be modified.  */
     83      1.1  christos 
     84      1.1  christos static gdb::byte_vector
     85      1.1  christos prepare_tag_vector (size_t granules, const gdb::byte_vector &tags, size_t pos,
     86      1.1  christos 		    size_t max_size)
     87      1.1  christos {
     88      1.1  christos   gdb::byte_vector t;
     89      1.1  christos 
     90      1.1  christos   if (granules == 0)
     91      1.1  christos     return t;
     92      1.1  christos 
     93      1.1  christos   gdb_assert (tags.size () > 0 && max_size > 0);
     94      1.1  christos 
     95      1.1  christos   if (granules > AARCH64_MTE_TAGS_MAX_SIZE)
     96      1.1  christos     t.resize (AARCH64_MTE_TAGS_MAX_SIZE);
     97      1.1  christos   else
     98      1.1  christos     t.resize (granules);
     99      1.1  christos 
    100      1.1  christos   size_t tag_count = tags.size ();
    101      1.1  christos 
    102      1.1  christos   for (size_t i = 0; i < t.size (); i++)
    103      1.1  christos     t[i] = tags[(pos + i) % tag_count];
    104      1.1  christos 
    105      1.1  christos   return t;
    106      1.1  christos }
    107      1.1  christos 
    108      1.1  christos /* See nat/aarch64-mte-linux-ptrace.h */
    109      1.1  christos 
    110      1.1  christos bool
    111      1.1  christos aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len,
    112      1.1  christos 			   gdb::byte_vector &tags)
    113      1.1  christos {
    114      1.1  christos   size_t ntags = aarch64_mte_get_tag_granules (address, len,
    115      1.1  christos 					       AARCH64_MTE_GRANULE_SIZE);
    116      1.1  christos 
    117      1.1  christos   /* If the memory range contains no tags, nothing left to do.  */
    118      1.1  christos   if (ntags == 0)
    119      1.1  christos     return true;
    120      1.1  christos 
    121      1.1  christos   gdb_byte tagbuf[ntags];
    122      1.1  christos 
    123      1.1  christos   struct iovec iovec;
    124      1.1  christos   iovec.iov_base = tagbuf;
    125      1.1  christos   iovec.iov_len = ntags;
    126      1.1  christos 
    127      1.1  christos   tags.clear ();
    128      1.1  christos   bool done_reading = false;
    129      1.1  christos 
    130      1.1  christos   /* The kernel may return less tags than we requested.  Loop until we've read
    131      1.1  christos      all the requested tags or until we get an error.  */
    132      1.1  christos   while (!done_reading)
    133      1.1  christos     {
    134      1.1  christos       /* Attempt to read ntags allocation tags from the kernel.  */
    135      1.1  christos       if (ptrace (PTRACE_PEEKMTETAGS, tid, address, &iovec) < 0)
    136      1.1  christos 	aarch64_mte_linux_peek_error (errno);
    137      1.1  christos 
    138      1.1  christos       /* Make sure the kernel returned at least one tag.  */
    139      1.1  christos       if (iovec.iov_len <= 0)
    140      1.1  christos 	{
    141      1.1  christos 	  tags.clear ();
    142      1.1  christos 	  return false;
    143      1.1  christos 	}
    144      1.1  christos 
    145      1.1  christos       /* Copy the tags the kernel returned.  */
    146      1.1  christos       for (size_t i = 0; i < iovec.iov_len; i++)
    147      1.1  christos 	tags.push_back (tagbuf[i]);
    148      1.1  christos 
    149      1.1  christos       /* Are we done reading tags?  */
    150      1.1  christos       if (tags.size () == ntags)
    151      1.1  christos 	done_reading = true;
    152      1.1  christos       else
    153      1.1  christos 	{
    154      1.1  christos 	  address += iovec.iov_len * AARCH64_MTE_GRANULE_SIZE;
    155      1.1  christos 	  iovec.iov_len = ntags - iovec.iov_len;
    156      1.1  christos 	}
    157      1.1  christos     }
    158      1.1  christos   return true;
    159      1.1  christos }
    160      1.1  christos 
    161      1.1  christos /* See nat/aarch64-mte-linux-ptrace.h */
    162      1.1  christos 
    163      1.1  christos bool
    164      1.1  christos aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len,
    165      1.1  christos 			   const gdb::byte_vector &tags)
    166      1.1  christos {
    167      1.1  christos   if (tags.size () == 0)
    168      1.1  christos     return true;
    169      1.1  christos 
    170      1.1  christos   /* Get the number of tags we need to write.  */
    171      1.1  christos   size_t ntags = aarch64_mte_get_tag_granules (address, len,
    172      1.1  christos 					       AARCH64_MTE_GRANULE_SIZE);
    173      1.1  christos 
    174      1.1  christos   /* If the memory range contains no tags, nothing left to do.  */
    175      1.1  christos   if (ntags == 0)
    176      1.1  christos     return true;
    177      1.1  christos 
    178      1.1  christos   bool done_writing = false;
    179      1.1  christos   size_t tags_written = 0;
    180      1.1  christos 
    181      1.1  christos   /* Write all the tags, AARCH64_MTE_TAGS_MAX_SIZE blocks at a time.  */
    182      1.1  christos   while (!done_writing)
    183      1.1  christos     {
    184      1.1  christos       gdb::byte_vector t = prepare_tag_vector (ntags - tags_written, tags,
    185      1.1  christos 					       tags_written,
    186      1.1  christos 					       AARCH64_MTE_TAGS_MAX_SIZE);
    187      1.1  christos 
    188      1.1  christos       struct iovec iovec;
    189      1.1  christos       iovec.iov_base = t.data ();
    190      1.1  christos       iovec.iov_len = t.size ();
    191      1.1  christos 
    192      1.1  christos       /* Request the kernel to update the allocation tags.  */
    193      1.1  christos       if (ptrace (PTRACE_POKEMTETAGS, tid, address, &iovec) < 0)
    194      1.1  christos 	aarch64_mte_linux_poke_error (errno);
    195      1.1  christos 
    196      1.1  christos       /* Make sure the kernel wrote at least one tag.  */
    197      1.1  christos       if (iovec.iov_len <= 0)
    198      1.1  christos 	return false;
    199      1.1  christos 
    200      1.1  christos       tags_written += iovec.iov_len;
    201      1.1  christos 
    202      1.1  christos       /* Are we done writing tags?  */
    203      1.1  christos       if (tags_written == ntags)
    204      1.1  christos 	done_writing = true;
    205      1.1  christos       else
    206      1.1  christos 	address += iovec.iov_len * AARCH64_MTE_GRANULE_SIZE;
    207      1.1  christos     }
    208      1.1  christos 
    209      1.1  christos   return true;
    210      1.1  christos }
    211