]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
AArch64: Add MTE register set support for GDB and gdbserver
authorLuis Machado <luis.machado@linaro.org>
Mon, 15 Jun 2020 16:59:40 +0000 (13:59 -0300)
committerLuis Machado <luis.machado@linaro.org>
Thu, 25 Jun 2020 16:23:38 +0000 (13:23 -0300)
AArch64 MTE support in the Linux kernel exposes a couple new read-only registers
through ptrace.  This patch adds the required code to support them.

include/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

* elf/common.h (NT_ARM_MTE): Define.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

* aarch64-linux-nat.c (fetch_mte_from_thread): New function.
(aarch64_linux_nat_target::fetch_registers): Update to call
fetch_mte_from_thread.
* aarch64-linux-tdep.c
(aarch64_linux_iterate_over_regset_sections): Handle MTE register
section.
* aarch64-tdep.c (aarch64_mte_register_names): New struct.
(aarch64_cannot_store_register): Handle MTE registers.
(aarch64_gdbarch_init): Initialize and setup MTE registers.
* aarch64-tdep.h (gdbarch_tdep) <mte_reg_base>: New field.
<has_mte>: New method.
* arch/aarch64-linux.h (AARCH64_LINUX_SIZEOF_MTE): Define.

gdbserver/ChangeLog:

YYYY-MM-DD  Luis Machado  <luis.machado@linaro.org>

* linux-aarch64-low.cc (aarch64_store_mteregset): New function.
(aarch64_regsets): Add MTE register set entry.
(aarch64_sve_regsets): Add MTE register set entry.

gdb/aarch64-linux-nat.c
gdb/aarch64-linux-tdep.c
gdb/aarch64-tdep.c
gdb/aarch64-tdep.h
gdb/arch/aarch64-mte-linux.h
gdbserver/linux-aarch64-low.cc
include/elf/common.h

index 1392ec440c979d33769b151e48a54b8867e6cba8..63e139568fdfdb3dd39c333a9f2cc61ac80737bf 100644 (file)
@@ -461,6 +461,29 @@ fetch_pauth_masks_from_thread (struct regcache *regcache)
                        &pauth_regset[1]);
 }
 
+/* Fill GDB's register array with the MTE register values from
+   the current thread.  */
+
+static void
+fetch_mte_from_thread (struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
+  int ret;
+  struct iovec iovec;
+  uint64_t mte_regset[2] = {0, 0};
+  int tid = regcache->ptid ().lwp ();
+
+  iovec.iov_base = &mte_regset;
+  iovec.iov_len = sizeof (mte_regset);
+
+  ret = ptrace (PTRACE_GETREGSET, tid, NT_ARM_MTE, &iovec);
+  if (ret != 0)
+    perror_with_name (_("unable to fetch MTE registers."));
+
+  regcache->raw_supply (tdep->mte_reg_base, &mte_regset[0]);
+  regcache->raw_supply (tdep->mte_reg_base + 1, &mte_regset[1]);
+}
+
 /* Implement the "fetch_registers" target_ops method.  */
 
 void
@@ -479,6 +502,9 @@ aarch64_linux_nat_target::fetch_registers (struct regcache *regcache,
 
       if (tdep->has_pauth ())
        fetch_pauth_masks_from_thread (regcache);
+
+      if (tdep->has_mte ())
+       fetch_mte_from_thread (regcache);
     }
   else if (regno < AARCH64_V0_REGNUM)
     fetch_gregs_from_thread (regcache);
@@ -493,6 +519,11 @@ aarch64_linux_nat_target::fetch_registers (struct regcache *regcache,
          || regno == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base))
        fetch_pauth_masks_from_thread (regcache);
     }
+
+  /* Fetch individual MTE registers.  */
+  if (tdep->has_mte ()
+      && (regno == tdep->mte_reg_base || regno == (tdep->mte_reg_base + 1)))
+    fetch_mte_from_thread (regcache);
 }
 
 /* Implement the "store_registers" target_ops method.  */
index 53f9d9f6d21638ba948d17295e7d21e66ebd344a..fa76d87eaab4a4b040d85cb4e23e4012a646cfa8 100644 (file)
@@ -645,6 +645,27 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
          AARCH64_LINUX_SIZEOF_PAUTH, &aarch64_linux_pauth_regset,
          "pauth registers", cb_data);
     }
+
+  /* Handle MTE register set.  */
+  if (tdep->has_mte ())
+    {
+      /* FIXME: This is still a WIP, awaiting further details from the Linuxkernel.  */
+      /* Create this on the fly in order to handle the variable location.  */
+      const struct regcache_map_entry mte_regmap[] =
+       {
+         { 2, tdep->mte_reg_base, 8},
+         { 0 }
+       };
+
+      const struct regset aarch64_linux_mte_regset =
+       {
+         mte_regmap, regcache_supply_regset, regcache_collect_regset
+       };
+
+      cb (".reg-aarch-mte", AARCH64_LINUX_SIZEOF_MTE,
+         AARCH64_LINUX_SIZEOF_MTE, &aarch64_linux_mte_regset,
+         "MTE registers", cb_data);
+    }
 }
 
 /* Implement the "core_read_description" gdbarch method.  */
index 536f6f3dc9c57e9e539f39bf20c455c68c8f3c15..992507edd631c2d423e1cc5429de053e8d020971 100644 (file)
@@ -176,6 +176,14 @@ static const char *const aarch64_pauth_register_names[] =
   "pauth_cmask"
 };
 
+static const char *const aarch64_mte_register_names[] =
+{
+  /* System Control Top Level Register.  */
+  "sctlr",
+  /* Tag Control Register.  */
+  "gcr"
+};
+
 /* AArch64 prologue cache structure.  */
 struct aarch64_prologue_cache
 {
@@ -3202,12 +3210,19 @@ aarch64_cannot_store_register (struct gdbarch *gdbarch, int regnum)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  if (!tdep->has_pauth ())
-    return 0;
+  /* Is this a PAC register?  */
+  if (tdep->has_pauth ()
+      && (regnum == AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base)
+         || regnum == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base)))
+    return 1;
+
+  /* Is this a MTE register?  */
+  if (tdep->has_mte ()
+      && (regnum == tdep->mte_reg_base || regnum == (tdep->mte_reg_base + 1)))
+    return 1;
 
-  /* Pointer authentication registers are read-only.  */
-  return (regnum == AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base)
-         || regnum == AARCH64_PAUTH_CMASK_REGNUM (tdep->pauth_reg_base));
+  /* No restrictions on register store.  */
+  return 0;
 }
 
 /* Initialize the current architecture based on INFO.  If possible,
@@ -3225,6 +3240,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   bool valid_p = true;
   int i, num_regs = 0, num_pseudo_regs = 0;
   int first_pauth_regnum = -1, pauth_ra_state_offset = -1;
+  int first_mte_regnum = -1;
 
   /* Use the vector length passed via the target info.  Here -1 is used for no
      SVE, and 0 is unset.  If unset then use the vector length from the existing
@@ -3262,6 +3278,8 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   feature_fpu = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.fpu");
   feature_sve = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.sve");
   feature_pauth = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.pauth");
+  const struct tdesc_feature *feature_mte
+    = tdesc_find_feature (tdesc, "org.gnu.gdb.aarch64.mte");
 
   if (feature_core == nullptr)
     return nullptr;
@@ -3332,6 +3350,20 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
       num_pseudo_regs += 1;    /* Count RA_STATE pseudo register.  */
     }
 
+  /* Add the MTE registers.  */
+  if (feature_mte != NULL)
+    {
+      first_mte_regnum = num_regs;
+      /* Validate the descriptor provides the mandatory MTE registers and
+        allocate their numbers.  */
+      for (i = 0; i < ARRAY_SIZE (aarch64_mte_register_names); i++)
+       valid_p &= tdesc_numbered_register (feature_mte, tdesc_data,
+                                           first_mte_regnum + i,
+                                           aarch64_mte_register_names[i]);
+
+      num_regs += i;
+    }
+
   if (!valid_p)
     {
       tdesc_data_cleanup (tdesc_data);
@@ -3352,6 +3384,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   tdep->pauth_reg_base = first_pauth_regnum;
   tdep->pauth_ra_state_regnum = (feature_pauth == NULL) ? -1
                                : pauth_ra_state_offset + num_regs;
+  tdep->mte_reg_base = first_mte_regnum;
 
   set_gdbarch_push_dummy_call (gdbarch, aarch64_push_dummy_call);
   set_gdbarch_frame_align (gdbarch, aarch64_frame_align);
index 4f8b19ad2f6fa280343ef0ccfc83902e42b3a2af..eb01e31df13fc8d80f279e0246989ac55771c690 100644 (file)
@@ -100,6 +100,15 @@ struct gdbarch_tdep
   {
     return pauth_reg_base != -1;
   }
+
+  /* First MTE register.  This is -1 if no MTE registers are available.  */
+  int mte_reg_base;
+
+  /* Returns true if the target supports MTE.  */
+  bool has_mte () const
+  {
+    return mte_reg_base != -1;
+  }
 };
 
 const target_desc *aarch64_read_description (uint64_t vq, bool pauth_p,
index c6a91c2db4e2b7b9f26c82eb3e8d5f4e2550aed0..423d4dea96e8f10354e17a2a4ce8f08467453ff6 100644 (file)
@@ -25,4 +25,7 @@
 #define HWCAP2_MTE  (1 << 18)
 #endif
 
+/* The MTE regset consists of 2 registers of 64-bit size.  */
+#define AARCH64_LINUX_SIZEOF_MTE (2 * 64)
+
 #endif /* ARCH_AARCH64_LINUX_H */
index 60d60a4d5c8100640cf24af272060765a3d2ea80..b2a8640af53dda75d8c62d8313b6f043ca54feb1 100644 (file)
@@ -261,6 +261,23 @@ aarch64_store_pauthregset (struct regcache *regcache, const void *buf)
                   &pauth_regset[1]);
 }
 
+/* Store the MTE registers to regcache.  */
+
+static void
+aarch64_store_mteregset (struct regcache *regcache, const void *buf)
+{
+  uint64_t *mte_regset = (uint64_t *) buf;
+  int mte_base = find_regno (regcache->tdesc, "sctlr");
+
+  if (mte_base == 0)
+    return;
+
+  /* SCTLR register */
+  supply_register (regcache, mte_base, &mte_regset[0]);
+  /* GCR register */
+  supply_register (regcache, mte_base + 1, &mte_regset[1]);
+}
+
 bool
 aarch64_target::low_supports_breakpoints ()
 {
@@ -684,6 +701,8 @@ static struct regset_info aarch64_regsets[] =
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
     AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
     NULL, aarch64_store_pauthregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_MTE,
+    AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, NULL, aarch64_store_mteregset },
   NULL_REGSET
 };
 
@@ -713,6 +732,8 @@ static struct regset_info aarch64_sve_regsets[] =
   { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_PAC_MASK,
     AARCH64_PAUTH_REGS_SIZE, OPTIONAL_REGS,
     NULL, aarch64_store_pauthregset },
+  { PTRACE_GETREGSET, PTRACE_SETREGSET, NT_ARM_MTE,
+    AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, NULL, aarch64_store_mteregset },
   NULL_REGSET
 };
 
index 4d94c4fd5b34c3d59409950ae6141aa308695bf9..33c773e85c523c3fa8777339b330d83c79e71cb9 100644 (file)
                                        /*   note name must be "LINUX".  */
 #define NT_ARM_PAC_MASK        0x406           /* AArch pointer authentication code masks */
                                        /*   note name must be "LINUX".  */
+#define NT_ARM_MTE     0x407           /* AArch MTE registers.  */
+                                       /*   note name must be "LINUX".  */
 #define NT_ARC_V2      0x600           /* ARC HS accumulator/extra registers.  */
                                        /*   note name must be "LINUX".  */
 #define NT_SIGINFO     0x53494749      /* Fields of siginfo_t.  */