gdb: convert callback_handler_installed from int to bool
[binutils-gdb.git] / gdb / aarch64-linux-tdep.c
index d573f06436a6c53f7f9d5b2208dcfde1b418b47b..cb132d5a540bef68357cf7a4a2c42905abaa8ba0 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-dependent code for GNU/Linux AArch64.
 
-   Copyright (C) 2009-2021 Free Software Foundation, Inc.
+   Copyright (C) 2009-2022 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of GDB.
@@ -51,6 +51,8 @@
 #include "arch-utils.h"
 #include "value.h"
 
+#include "gdbsupport/selftest.h"
+
 /* Signal frame handling.
 
       +------------+  ^
@@ -284,7 +286,7 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
 {
   struct gdbarch *gdbarch = get_frame_arch (this_frame);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
   CORE_ADDR sp = get_frame_register_unsigned (this_frame, AARCH64_SP_REGNUM);
   CORE_ADDR sigcontext_addr = (sp + AARCH64_RT_SIGFRAME_UCONTEXT_OFFSET
                               + AARCH64_UCONTEXT_SIGCONTEXT_OFFSET );
@@ -638,7 +640,8 @@ aarch64_linux_collect_sve_regset (const struct regset *regset,
   gdb_byte *header = (gdb_byte *) buf;
   struct gdbarch *gdbarch = regcache->arch ();
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-  uint64_t vq = gdbarch_tdep (gdbarch)->vq;
+  aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+  uint64_t vq = tdep->vq;
 
   gdb_assert (buf != NULL);
   gdb_assert (size > SVE_HEADER_SIZE);
@@ -673,7 +676,7 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
                                            void *cb_data,
                                            const struct regcache *regcache)
 {
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
 
   cb (".reg", AARCH64_LINUX_SIZEOF_GREGSET, AARCH64_LINUX_SIZEOF_GREGSET,
       &aarch64_linux_gregset, NULL, cb_data);
@@ -685,7 +688,7 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
        {
          { 32, AARCH64_SVE_Z0_REGNUM, (int) (tdep->vq * 16) },
          { 16, AARCH64_SVE_P0_REGNUM, (int) (tdep->vq * 16 / 8) },
-         { 1, AARCH64_SVE_FFR_REGNUM, 4 },
+         { 1, AARCH64_SVE_FFR_REGNUM, (int) (tdep->vq * 16 / 8) },
          { 1, AARCH64_FPSR_REGNUM, 4 },
          { 1, AARCH64_FPCR_REGNUM, 4 },
          { 0 }
@@ -726,6 +729,26 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
          AARCH64_LINUX_SIZEOF_PAUTH, &aarch64_linux_pauth_regset,
          "pauth registers", cb_data);
     }
+
+  /* Handle MTE registers.  */
+  if (tdep->has_mte ())
+    {
+      /* Create this on the fly in order to handle the variable location.  */
+      const struct regcache_map_entry mte_regmap[] =
+       {
+         { 1, 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_REGSET,
+         AARCH64_LINUX_SIZEOF_MTE_REGSET, &aarch64_linux_mte_regset,
+         "MTE registers", cb_data);
+    }
 }
 
 /* Implement the "core_read_description" gdbarch method.  */
@@ -1466,9 +1489,10 @@ aarch64_linux_syscall_record (struct regcache *regcache,
 
   if (syscall_gdb < 0)
     {
-      printf_unfiltered (_("Process record and replay target doesn't "
-                          "support syscall number %s\n"),
-                        plongest (svc_number));
+      fprintf_unfiltered (gdb_stderr,
+                         _("Process record and replay target doesn't "
+                           "support syscall number %s\n"),
+                         plongest (svc_number));
       return -1;
     }
 
@@ -1565,7 +1589,8 @@ aarch64_linux_memtag_matches_p (struct gdbarch *gdbarch,
   CORE_ADDR addr = value_as_address (address);
 
   /* Fetch the allocation tag for ADDRESS.  */
-  gdb::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr);
+  gdb::optional<CORE_ADDR> atag
+    = aarch64_mte_get_atag (address_significant (gdbarch, addr));
 
   if (!atag.has_value ())
     return true;
@@ -1598,11 +1623,14 @@ aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address,
 
       /* Update the value's content with the tag.  */
       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
-      gdb_byte *srcbuf = value_contents_raw (address);
+      gdb_byte *srcbuf = value_contents_raw (address).data ();
       store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr);
     }
   else
     {
+      /* Remove the top byte.  */
+      addr = address_significant (gdbarch, addr);
+
       /* Make sure we are dealing with a tagged address to begin with.  */
       if (!aarch64_linux_tagged_address_p (gdbarch, address))
        return false;
@@ -1657,6 +1685,8 @@ aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address,
       if (!aarch64_linux_tagged_address_p (gdbarch, address))
        return nullptr;
 
+      /* Remove the top byte.  */
+      addr = address_significant (gdbarch, addr);
       gdb::optional<CORE_ADDR> atag = aarch64_mte_get_atag (addr);
 
       if (!atag.has_value ())
@@ -1683,6 +1713,74 @@ aarch64_linux_memtag_to_string (struct gdbarch *gdbarch, struct value *tag_value
   return string_printf ("0x%s", phex_nz (tag, sizeof (tag)));
 }
 
+/* AArch64 Linux implementation of the report_signal_info gdbarch
+   hook.  Displays information about possible memory tag violations.  */
+
+static void
+aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
+                                 struct ui_out *uiout,
+                                 enum gdb_signal siggnal)
+{
+  aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
+
+  if (!tdep->has_mte () || siggnal != GDB_SIGNAL_SEGV)
+    return;
+
+  CORE_ADDR fault_addr = 0;
+  long si_code = 0;
+
+  try
+    {
+      /* Sigcode tells us if the segfault is actually a memory tag
+        violation.  */
+      si_code = parse_and_eval_long ("$_siginfo.si_code");
+
+      fault_addr
+       = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr");
+    }
+  catch (const gdb_exception_error &exception)
+    {
+      exception_print (gdb_stderr, exception);
+      return;
+    }
+
+  /* If this is not a memory tag violation, just return.  */
+  if (si_code != SEGV_MTEAERR && si_code != SEGV_MTESERR)
+    return;
+
+  uiout->text ("\n");
+
+  uiout->field_string ("sigcode-meaning", _("Memory tag violation"));
+
+  /* For synchronous faults, show additional information.  */
+  if (si_code == SEGV_MTESERR)
+    {
+      uiout->text (_(" while accessing address "));
+      uiout->field_core_addr ("fault-addr", gdbarch, fault_addr);
+      uiout->text ("\n");
+
+      gdb::optional<CORE_ADDR> atag
+       = aarch64_mte_get_atag (address_significant (gdbarch, fault_addr));
+      gdb_byte ltag = aarch64_mte_get_ltag (fault_addr);
+
+      if (!atag.has_value ())
+       uiout->text (_("Allocation tag unavailable"));
+      else
+       {
+         uiout->text (_("Allocation tag "));
+         uiout->field_string ("allocation-tag", hex_string (*atag));
+         uiout->text ("\n");
+         uiout->text (_("Logical tag "));
+         uiout->field_string ("logical-tag", hex_string (ltag));
+       }
+    }
+  else
+    {
+      uiout->text ("\n");
+      uiout->text (_("Fault address unavailable"));
+    }
+}
+
 static void
 aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -1692,14 +1790,14 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
                                                                    NULL };
   static const char *const stap_register_indirection_suffixes[] = { "]",
                                                                    NULL };
-  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  aarch64_gdbarch_tdep *tdep = (aarch64_gdbarch_tdep *) gdbarch_tdep (gdbarch);
 
   tdep->lowest_pc = 0x8000;
 
   linux_init_abi (info, gdbarch, 1);
 
   set_solib_svr4_fetch_link_map_offsets (gdbarch,
-                                        svr4_lp64_fetch_link_map_offsets);
+                                        linux_lp64_fetch_link_map_offsets);
 
   /* Enable TLS support.  */
   set_gdbarch_fetch_tls_load_module_address (gdbarch,
@@ -1763,6 +1861,9 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
       /* Register a hook for converting a memory tag to a string.  */
       set_gdbarch_memtag_to_string (gdbarch, aarch64_linux_memtag_to_string);
+
+      set_gdbarch_report_signal_info (gdbarch,
+                                     aarch64_linux_report_signal_info);
     }
 
   /* Initialize the aarch64_linux_record_tdep.  */
@@ -1940,10 +2041,39 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_gdbarch_gcc_target_options (gdbarch, aarch64_linux_gcc_target_options);
 }
 
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+/* Verify functions to read and write logical tags.  */
+
+static void
+aarch64_linux_ltag_tests (void)
+{
+  /* We have 4 bits of tags, but we test writing all the bits of the top
+     byte of address.  */
+  for (int i = 0; i < 1 << 8; i++)
+    {
+      CORE_ADDR addr = ((CORE_ADDR) i << 56) | 0xdeadbeef;
+      SELF_CHECK (aarch64_mte_get_ltag (addr) == (i & 0xf));
+
+      addr = aarch64_mte_set_ltag (0xdeadbeef, i);
+      SELF_CHECK (addr = ((CORE_ADDR) (i & 0xf) << 56) | 0xdeadbeef);
+    }
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
 void _initialize_aarch64_linux_tdep ();
 void
 _initialize_aarch64_linux_tdep ()
 {
   gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX,
                          aarch64_linux_init_abi);
+
+#if GDB_SELF_TEST
+  selftests::register_test ("aarch64-linux-tagged-address",
+                           selftests::aarch64_linux_ltag_tests);
+#endif
 }