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>
Wed, 24 Mar 2021 17:52:57 +0000 (14:52 -0300)
AArch64 MTE support in the Linux kernel exposes a new register
through ptrace.  This patch adds the required code to support it.

include/ChangeLog:

2021-03-24  Luis Machado  <luis.machado@linaro.org>

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

gdb/ChangeLog:

2021-03-24  Luis Machado  <luis.machado@linaro.org>

* aarch64-linux-nat.c (fetch_mteregs_from_thread): New function.
(store_mteregs_to_thread): New function.
(aarch64_linux_nat_target::fetch_registers): Update to call
fetch_mteregs_from_thread.
(aarch64_linux_nat_target::store_registers): Update to call
store_mteregs_to_thread.
* 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:

2021-03-24  Luis Machado  <luis.machado@linaro.org>

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

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

index 33a58cb06d9e400805b66579afeddaa0bc2b39ea..866623e168a4f41a86b07ef266298e601c132a4e 100644 (file)
@@ -1,3 +1,18 @@
+2021-03-24  Luis Machado  <luis.machado@linaro.org>
+
+       * aarch64-linux-nat.c (fetch_mteregs_from_thread): New function.
+       (store_mteregs_to_thread): New function.
+       (aarch64_linux_nat_target::fetch_registers): Update to call
+       fetch_mteregs_from_thread.
+       (aarch64_linux_nat_target::store_registers): Update to call
+       store_mteregs_to_thread.
+       * 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.
+
 2021-03-24  Luis Machado  <luis.machado@linaro.org>
 
        * aarch64-linux-nat.c
index c56880e33d707a3df847628a91fe9994d73b7b2e..fe3ba44c5534dac421f5f030ab853975eb37d816 100644 (file)
@@ -461,6 +461,58 @@ 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_mteregs_from_thread (struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
+  int regno = tdep->mte_reg_base;
+
+  gdb_assert (regno != -1);
+
+  uint64_t tag_ctl = 0;
+  struct iovec iovec;
+
+  iovec.iov_base = &tag_ctl;
+  iovec.iov_len = sizeof (tag_ctl);
+
+  int tid = get_ptrace_pid (regcache->ptid ());
+  if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_TAGGED_ADDR_CTRL, &iovec) != 0)
+      perror_with_name (_("unable to fetch MTE registers."));
+
+  regcache->raw_supply (regno, &tag_ctl);
+}
+
+/* Store to the current thread the valid MTE register set in the GDB's
+   register array.  */
+
+static void
+store_mteregs_to_thread (struct regcache *regcache)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (regcache->arch ());
+  int regno = tdep->mte_reg_base;
+
+  gdb_assert (regno != -1);
+
+  uint64_t tag_ctl = 0;
+
+  if (REG_VALID != regcache->get_register_status (regno))
+    return;
+
+  regcache->raw_collect (regno, (char *) &tag_ctl);
+
+  struct iovec iovec;
+
+  iovec.iov_base = &tag_ctl;
+  iovec.iov_len = sizeof (tag_ctl);
+
+  int tid = get_ptrace_pid (regcache->ptid ());
+  if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_TAGGED_ADDR_CTRL, &iovec) != 0)
+    perror_with_name (_("unable to store MTE registers."));
+}
+
 /* Implement the "fetch_registers" target_ops method.  */
 
 void
@@ -479,6 +531,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_mteregs_from_thread (regcache);
     }
   else if (regno < AARCH64_V0_REGNUM)
     fetch_gregs_from_thread (regcache);
@@ -493,6 +548,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))
+    fetch_mteregs_from_thread (regcache);
 }
 
 /* Implement the "store_registers" target_ops method.  */
@@ -510,6 +570,9 @@ aarch64_linux_nat_target::store_registers (struct regcache *regcache,
        store_sveregs_to_thread (regcache);
       else
        store_fpregs_to_thread (regcache);
+
+      if (tdep->has_mte ())
+       store_mteregs_to_thread (regcache);
     }
   else if (regno < AARCH64_V0_REGNUM)
     store_gregs_to_thread (regcache);
@@ -517,6 +580,11 @@ aarch64_linux_nat_target::store_registers (struct regcache *regcache,
     store_sveregs_to_thread (regcache);
   else
     store_fpregs_to_thread (regcache);
+
+  /* Store MTE registers.  */
+  if (tdep->has_mte ()
+      && (regno == tdep->mte_reg_base))
+    store_mteregs_to_thread (regcache);
 }
 
 /* Fill register REGNO (if it is a general-purpose register) in
index 685c50b84dabb27a537d482e3125635789eb8578..44833eb94d3387784942614c1b7bedac28161c52 100644 (file)
@@ -172,6 +172,12 @@ static const char *const aarch64_pauth_register_names[] =
   "pauth_cmask"
 };
 
+static const char *const aarch64_mte_register_names[] =
+{
+  /* Tag Control Register.  */
+  "tag_ctl"
+};
+
 /* AArch64 prologue cache structure.  */
 struct aarch64_prologue_cache
 {
@@ -3346,6 +3352,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
@@ -3383,6 +3390,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;
@@ -3453,6 +3462,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.get (),
+                                           first_mte_regnum + i,
+                                           aarch64_mte_register_names[i]);
+
+      num_regs += i;
+    }
+
   if (!valid_p)
     return nullptr;
 
@@ -3470,6 +3493,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 f6d24292f845f6c5a134fe3aa3c3ed9a2864087d..7bf612b012aff9138fce27ead35312a8477f8a98 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 629ed9cb2759cda9c39f9b22cab32991cb801a77..7c259b8a4a43f4549b649b707f3dc57c84709db4 100644 (file)
@@ -25,4 +25,7 @@
 #define HWCAP2_MTE  (1 << 18)
 #endif
 
+/* The MTE regset consists of a single 64-bit register.  */
+#define AARCH64_LINUX_SIZEOF_MTE 8
+
 #endif /* ARCH_AARCH64_LINUX_H */
index db906600b2874fb64ba8966b6d678932d44c16eb..d4b8d80d393489c31b9d6f0b812f51322dd3b867 100644 (file)
@@ -1,3 +1,10 @@
+2021-03-24  Luis Machado  <luis.machado@linaro.org>
+
+       * linux-aarch64-low.cc (aarch64_fill_mteregset): New function.
+       (aarch64_store_mteregset): New function.
+       (aarch64_regsets): Add MTE register set entry.
+       (aarch64_sve_regsets): Add MTE register set entry.
+
 2021-03-24  Luis Machado  <luis.machado@linaro.org>
 
        * linux-aarch64-ipa.cc (get_ipa_tdesc): Update call to
index 14493c1fbe72d47062461cac0eff3e6a86a24e86..a066d963a5f03e57d2d44cd49fe620fad297dcb0 100644 (file)
@@ -261,6 +261,29 @@ aarch64_store_pauthregset (struct regcache *regcache, const void *buf)
                   &pauth_regset[1]);
 }
 
+/* Fill BUF with the MTE registers from the regcache.  */
+
+static void
+aarch64_fill_mteregset (struct regcache *regcache, void *buf)
+{
+  uint64_t *mte_regset = (uint64_t *) buf;
+  int mte_base = find_regno (regcache->tdesc, "tag_ctl");
+
+  collect_register (regcache, mte_base, mte_regset);
+}
+
+/* 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, "tag_ctl");
+
+  /* Tag Control register */
+  supply_register (regcache, mte_base, mte_regset);
+}
+
 bool
 aarch64_target::low_supports_breakpoints ()
 {
@@ -706,6 +729,9 @@ 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_TAGGED_ADDR_CTRL,
+    AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, aarch64_fill_mteregset,
+    aarch64_store_mteregset },
   NULL_REGSET
 };
 
@@ -735,6 +761,9 @@ 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_TAGGED_ADDR_CTRL,
+    AARCH64_LINUX_SIZEOF_MTE, OPTIONAL_REGS, aarch64_fill_mteregset,
+    aarch64_store_mteregset },
   NULL_REGSET
 };
 
index 241f2be167b821064d9953ac71a2d774f4665a57..f36bb0b80a292a99b36e939d9d16406900733a00 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_TAGGED_ADDR_CTRL        0x409   /* AArch64 tagged address control
+                                          (prctl()) */
+                                       /*   note name must be "LINUX".  */
 #define NT_ARC_V2      0x600           /* ARC HS accumulator/extra registers.  */
                                        /*   note name must be "LINUX".  */
 #define NT_RISCV_CSR    0x900          /* RISC-V Control and Status Registers */