gnulib: define the path to gnulib's parent dir
[binutils-gdb.git] / gdbserver / linux-aarch64-low.cc
index 56bf027b888b1769fbebb8acefeea31076f7e5f8..daccfef746e3174b4e75eb7d725655e2aa824ed7 100644 (file)
@@ -1,7 +1,7 @@
 /* GNU/Linux/AArch64 specific low level interface, for the remote server for
    GDB.
 
-   Copyright (C) 2009-2020 Free Software Foundation, Inc.
+   Copyright (C) 2009-2021 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of GDB.
 
 #include "gdb_proc_service.h"
 #include "arch/aarch64.h"
+#include "arch/aarch64-mte-linux.h"
 #include "linux-aarch32-tdesc.h"
 #include "linux-aarch64-tdesc.h"
+#include "nat/aarch64-mte-linux-ptrace.h"
 #include "nat/aarch64-sve-linux-ptrace.h"
 #include "tdesc.h"
 
 #include <sys/reg.h>
 #endif
 
+#ifdef HAVE_GETAUXVAL
+#include <sys/auxv.h>
+#endif
+
 /* Linux target op definitions for the AArch64 architecture.  */
 
 class aarch64_target : public linux_process_target
@@ -65,6 +71,30 @@ public:
 
   bool supports_z_point_type (char z_type) override;
 
+  bool supports_tracepoints () override;
+
+  bool supports_fast_tracepoints () override;
+
+  int install_fast_tracepoint_jump_pad
+    (CORE_ADDR tpoint, CORE_ADDR tpaddr, CORE_ADDR collector,
+     CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry,
+     CORE_ADDR *trampoline, ULONGEST *trampoline_size,
+     unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size,
+     CORE_ADDR *adjusted_insn_addr, CORE_ADDR *adjusted_insn_addr_end,
+     char *err) override;
+
+  int get_min_fast_tracepoint_insn_len () override;
+
+  struct emit_ops *emit_ops () override;
+
+  bool supports_memory_tagging () override;
+
+  bool fetch_memtags (CORE_ADDR address, size_t len,
+                     gdb::byte_vector &tags, int type) override;
+
+  bool store_memtags (CORE_ADDR address, size_t len,
+                     const gdb::byte_vector &tags, int type) override;
+
 protected:
 
   void low_arch_setup () override;
@@ -105,6 +135,14 @@ protected:
   void low_new_fork (process_info *parent, process_info *child) override;
 
   void low_prepare_to_resume (lwp_info *lwp) override;
+
+  int low_get_thread_area (int lwpid, CORE_ADDR *addrp) override;
+
+  bool low_supports_range_stepping () override;
+
+  bool low_supports_catch_syscall () override;
+
+  void low_get_syscall_trapinfo (regcache *regcache, int *sysno) override;
 };
 
 /* The singleton target ops object.  */
@@ -236,6 +274,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 ()
 {
@@ -434,6 +495,23 @@ aarch64_target::low_remove_point (raw_bkpt_type type, CORE_ADDR addr,
   return ret;
 }
 
+/* Return the address only having significant bits.  This is used to ignore
+   the top byte (TBI).  */
+
+static CORE_ADDR
+address_significant (CORE_ADDR addr)
+{
+  /* Clear insignificant bits of a target address and sign extend resulting
+     address.  */
+  int addr_bit = 56;
+
+  CORE_ADDR sign = (CORE_ADDR) 1 << (addr_bit - 1);
+  addr &= ((CORE_ADDR) 1 << addr_bit) - 1;
+  addr = (addr ^ sign) - sign;
+
+  return addr;
+}
+
 /* Implementation of linux target ops method "low_stopped_data_address".  */
 
 CORE_ADDR
@@ -454,6 +532,12 @@ aarch64_target::low_stopped_data_address ()
       || (siginfo.si_code & 0xffff) != 0x0004 /* TRAP_HWBKPT */)
     return (CORE_ADDR) 0;
 
+  /* Make sure to ignore the top byte, otherwise we may not recognize a
+     hardware watchpoint hit.  The stopped data addresses coming from the
+     kernel can potentially be tagged addresses.  */
+  const CORE_ADDR addr_trap
+    = address_significant ((CORE_ADDR) siginfo.si_addr);
+
   /* Check if the address matches any watched address.  */
   state = aarch64_get_debug_reg_state (pid_of (current_thread));
   for (i = aarch64_num_wp_regs - 1; i >= 0; --i)
@@ -461,7 +545,6 @@ aarch64_target::low_stopped_data_address ()
       const unsigned int offset
        = aarch64_watchpoint_offset (state->dr_ctrl_wp[i]);
       const unsigned int len = aarch64_watchpoint_length (state->dr_ctrl_wp[i]);
-      const CORE_ADDR addr_trap = (CORE_ADDR) siginfo.si_addr;
       const CORE_ADDR addr_watch = state->dr_addr_wp[i] + offset;
       const CORE_ADDR addr_watch_aligned = align_down (state->dr_addr_wp[i], 8);
       const CORE_ADDR addr_orig = state->dr_addr_orig_wp[i];
@@ -617,9 +700,13 @@ aarch64_target::low_arch_setup ()
     {
       uint64_t vq = aarch64_sve_get_vq (tid);
       unsigned long hwcap = linux_get_hwcap (8);
+      unsigned long hwcap2 = linux_get_hwcap2 (8);
       bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
+      /* MTE is AArch64-only.  */
+      bool mte_p = hwcap2 & HWCAP2_MTE;
 
-      current_process ()->tdesc = aarch64_linux_read_description (vq, pauth_p);
+      current_process ()->tdesc
+       = aarch64_linux_read_description (vq, pauth_p, mte_p);
     }
   else
     current_process ()->tdesc = aarch32_linux_read_description ();
@@ -655,6 +742,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
 };
 
@@ -684,6 +774,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
 };
 
@@ -715,13 +808,13 @@ aarch64_target::get_regs_info ()
   return &regs_info_aarch64;
 }
 
-/* Implementation of linux_target_ops method "supports_tracepoints".  */
+/* Implementation of target ops method "supports_tracepoints".  */
 
-static int
-aarch64_supports_tracepoints (void)
+bool
+aarch64_target::supports_tracepoints ()
 {
   if (current_thread == NULL)
-    return 1;
+    return true;
   else
     {
       /* We don't support tracepoints on aarch32 now.  */
@@ -729,10 +822,10 @@ aarch64_supports_tracepoints (void)
     }
 }
 
-/* Implementation of linux_target_ops method "get_thread_area".  */
+/* Implementation of linux target ops method "low_get_thread_area".  */
 
-static int
-aarch64_get_thread_area (int lwpid, CORE_ADDR *addrp)
+int
+aarch64_target::low_get_thread_area (int lwpid, CORE_ADDR *addrp)
 {
   struct iovec iovec;
   uint64_t reg;
@@ -748,10 +841,16 @@ aarch64_get_thread_area (int lwpid, CORE_ADDR *addrp)
   return 0;
 }
 
-/* Implementation of linux_target_ops method "get_syscall_trapinfo".  */
+bool
+aarch64_target::low_supports_catch_syscall ()
+{
+  return true;
+}
 
-static void
-aarch64_get_syscall_trapinfo (struct regcache *regcache, int *sysno)
+/* Implementation of linux target ops method "low_get_syscall_trapinfo".  */
+
+void
+aarch64_target::low_get_syscall_trapinfo (regcache *regcache, int *sysno)
 {
   int use_64bit = register_size (regcache->tdesc, 0) == 8;
 
@@ -1958,23 +2057,23 @@ static const struct aarch64_insn_visitor visitor =
   aarch64_ftrace_insn_reloc_others,
 };
 
-/* Implementation of linux_target_ops method
+bool
+aarch64_target::supports_fast_tracepoints ()
+{
+  return true;
+}
+
+/* Implementation of target ops method
    "install_fast_tracepoint_jump_pad".  */
 
-static int
-aarch64_install_fast_tracepoint_jump_pad (CORE_ADDR tpoint,
-                                         CORE_ADDR tpaddr,
-                                         CORE_ADDR collector,
-                                         CORE_ADDR lockaddr,
-                                         ULONGEST orig_size,
-                                         CORE_ADDR *jump_entry,
-                                         CORE_ADDR *trampoline,
-                                         ULONGEST *trampoline_size,
-                                         unsigned char *jjump_pad_insn,
-                                         ULONGEST *jjump_pad_insn_size,
-                                         CORE_ADDR *adjusted_insn_addr,
-                                         CORE_ADDR *adjusted_insn_addr_end,
-                                         char *err)
+int
+aarch64_target::install_fast_tracepoint_jump_pad
+  (CORE_ADDR tpoint, CORE_ADDR tpaddr, CORE_ADDR collector,
+   CORE_ADDR lockaddr, ULONGEST orig_size, CORE_ADDR *jump_entry,
+   CORE_ADDR *trampoline, ULONGEST *trampoline_size,
+   unsigned char *jjump_pad_insn, ULONGEST *jjump_pad_insn_size,
+   CORE_ADDR *adjusted_insn_addr, CORE_ADDR *adjusted_insn_addr_end,
+   char *err)
 {
   uint32_t buf[256];
   uint32_t *p = buf;
@@ -3075,29 +3174,29 @@ static struct emit_ops aarch64_emit_ops_impl =
   aarch64_emit_ge_got,
 };
 
-/* Implementation of linux_target_ops method "emit_ops".  */
+/* Implementation of target ops method "emit_ops".  */
 
-static struct emit_ops *
-aarch64_emit_ops (void)
+emit_ops *
+aarch64_target::emit_ops ()
 {
   return &aarch64_emit_ops_impl;
 }
 
-/* Implementation of linux_target_ops method
+/* Implementation of target ops method
    "get_min_fast_tracepoint_insn_len".  */
 
-static int
-aarch64_get_min_fast_tracepoint_insn_len (void)
+int
+aarch64_target::get_min_fast_tracepoint_insn_len ()
 {
   return 4;
 }
 
-/* Implementation of linux_target_ops method "supports_range_stepping".  */
+/* Implementation of linux target ops method "low_supports_range_stepping".  */
 
-static int
-aarch64_supports_range_stepping (void)
+bool
+aarch64_target::low_supports_range_stepping ()
 {
-  return 1;
+  return true;
 }
 
 /* Implementation of target ops method "sw_breakpoint_from_kind".  */
@@ -3137,25 +3236,53 @@ aarch64_target::breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
     return arm_breakpoint_kind_from_current_state (pcptr);
 }
 
-/* Support for hardware single step.  */
+/* Returns true if memory tagging is supported.  */
+bool
+aarch64_target::supports_memory_tagging ()
+{
+  if (current_thread == NULL)
+    {
+      /* We don't have any processes running, so don't attempt to
+        use linux_get_hwcap2 as it will try to fetch the current
+        thread id.  Instead, just fetch the auxv from the self
+        PID.  */
+#ifdef HAVE_GETAUXVAL
+      return (getauxval (AT_HWCAP2) & HWCAP2_MTE) != 0;
+#else
+      return true;
+#endif
+    }
+
+  return (linux_get_hwcap2 (8) & HWCAP2_MTE) != 0;
+}
 
-static int
-aarch64_supports_hardware_single_step (void)
+bool
+aarch64_target::fetch_memtags (CORE_ADDR address, size_t len,
+                              gdb::byte_vector &tags, int type)
 {
-  return 1;
+  /* Allocation tags are per-process, so any tid is fine.  */
+  int tid = lwpid_of (current_thread);
+
+  /* Allocation tag?  */
+  if (type == static_cast <int> (aarch64_memtag_type::mte_allocation))
+    return aarch64_mte_fetch_memtags (tid, address, len, tags);
+
+  return false;
 }
 
-struct linux_target_ops the_low_target =
+bool
+aarch64_target::store_memtags (CORE_ADDR address, size_t len,
+                              const gdb::byte_vector &tags, int type)
 {
-  aarch64_supports_tracepoints,
-  aarch64_get_thread_area,
-  aarch64_install_fast_tracepoint_jump_pad,
-  aarch64_emit_ops,
-  aarch64_get_min_fast_tracepoint_insn_len,
-  aarch64_supports_range_stepping,
-  aarch64_supports_hardware_single_step,
-  aarch64_get_syscall_trapinfo,
-};
+  /* Allocation tags are per-process, so any tid is fine.  */
+  int tid = lwpid_of (current_thread);
+
+  /* Allocation tag?  */
+  if (type == static_cast <int> (aarch64_memtag_type::mte_allocation))
+    return aarch64_mte_store_memtags (tid, address, len, tags);
+
+  return false;
+}
 
 /* The linux target ops object.  */