]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/aarch64-linux-tdep.c
Support linkmap offsets for the AAPCS64-CAP ABI
[thirdparty/binutils-gdb.git] / gdb / aarch64-linux-tdep.c
index 39e607658f45494382598d220ab315b8b74c191e..d04fe8961edb12cec7960078086e22cd0bd2b876 100644 (file)
@@ -1,6 +1,6 @@
 /* Target-dependent code for GNU/Linux AArch64.
 
-   Copyright (C) 2009-2019 Free Software Foundation, Inc.
+   Copyright (C) 2009-2020 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of GDB.
 
 #include "defs.h"
 
+#include "gdbcmd.h"
 #include "gdbarch.h"
-#include "arch-utils.h"
 #include "glibc-tdep.h"
 #include "linux-tdep.h"
 #include "aarch64-tdep.h"
 #include "aarch64-linux-tdep.h"
+#include "arch/aarch64-cap-linux.h"
 #include "osabi.h"
 #include "solib-svr4.h"
 #include "symtab.h"
 #include "tramp-frame.h"
 #include "trad-frame.h"
+#include "target/target.h"
+#include "target.h"
 
-#include "inferior.h"
 #include "regcache.h"
 #include "regset.h"
 
-#include "cli/cli-utils.h"
 #include "stap-probe.h"
 #include "parser-defs.h"
 #include "user-regs.h"
 
 #include "record-full.h"
 #include "linux-record.h"
-#include "auxv.h"
+
+#include "value.h"
+
+/* For aarch64_debug.  */
+#include "arch/aarch64-insn.h"
+
 #include "elf/common.h"
 
+#include "gdbsupport/capability.h"
+
 /* Signal frame handling.
 
       +------------+  ^
 #define AARCH64_EXTRA_MAGIC                    0x45585401
 #define AARCH64_FPSIMD_MAGIC                   0x46508001
 #define AARCH64_SVE_MAGIC                      0x53564501
+#define AARCH64_MORELLO_MAGIC                  0x4d524c01
 
 /* Defines for the extra_context that follows an AARCH64_EXTRA_MAGIC.  */
 #define AARCH64_EXTRA_DATAP_OFFSET             8
 #define AARCH64_SVE_CONTEXT_SIZE(vq) \
   (AARCH64_SVE_CONTEXT_FFR_OFFSET (vq) + (vq * 2))
 
+/* Defines for the Morello sigcontext data, which is define in the kernel like
+   so:
+
+   struct morello_context
+   {
+     struct _aarch64_ctx head;
+     __u64 __pad;
+     __kernel_uintcap_t cregs[31];
+     __kernel_uintcap_t csp;
+     __kernel_uintcap_t rcsp;
+     __kernel_uintcap_t pcc;
+   };
+
+*/
+
+#define AARCH64_MORELLO_SIGCONTEXT_SIZE              (8 + 8 + 34 * 16)
+#define AARCH64_MORELLO_SIGCONTEXT_C0_OFFSET  16
 
 /* Read an aarch64_ctx, returning the magic value, and setting *SIZE to the
    size, or return 0 on error.  */
@@ -206,6 +232,11 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
   bool extra_found = false;
   int num_regs = gdbarch_num_regs (gdbarch);
 
+  if (aarch64_debug)
+    {
+      debug_printf ("\naarch64: Entering aarch64_linux_sigframe_init\n");
+    }
+
   /* Read in the integer registers.  */
 
   for (int i = 0; i < 31; i++)
@@ -262,6 +293,50 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
            break;
          }
 
+       case AARCH64_MORELLO_MAGIC:
+         {
+           if (aarch64_debug)
+             debug_printf ("aarch64: Found Morello section at %s.\n",
+                           paddress (gdbarch, section));
+
+           /* Handle Morello sigcontext.  */
+           if (!tdep->has_capability ())
+             break;
+
+           int regno = tdep->cap_reg_base;
+           CORE_ADDR offset = section + AARCH64_MORELLO_SIGCONTEXT_C0_OFFSET;
+           int reg_size = C_REGISTER_SIZE;
+
+           if (aarch64_debug)
+             {
+               debug_printf ("aarch64: Reading C registers from sigreturn "
+                             "frame.\n");
+             }
+
+           for (int i = 0; i < AARCH64_C_REGS_NUM; i++)
+             {
+               trad_frame_set_reg_addr (this_cache, regno + i,
+                                        offset + i * reg_size);
+             }
+
+           if (aarch64_debug)
+             {
+               debug_printf ("aarch64: Reading PCC, CSP and RCSP registers "
+                             "from sigreturn frame at %s.\n",
+                             paddress (gdbarch, offset + 31 * reg_size));
+             }
+
+           trad_frame_set_reg_addr (this_cache, tdep->cap_reg_csp,
+                                    offset + 31 * reg_size);
+           trad_frame_set_reg_addr (this_cache, tdep->cap_reg_rcsp,
+                                    offset + 32 * reg_size);
+           trad_frame_set_reg_addr (this_cache, tdep->cap_reg_pcc,
+                                    offset + 33 * reg_size);
+
+           section += size;
+           break;
+         }
+
        case AARCH64_EXTRA_MAGIC:
          {
            /* Extra is always the last valid section in reserved and points to
@@ -360,6 +435,9 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
     }
 
   trad_frame_set_id (this_cache, frame_id_build (sp, func));
+
+  if (aarch64_debug)
+    debug_printf ("aarch64: Exitting aarch64_linux_sigframe_init\n");
 }
 
 static const struct tramp_frame aarch64_linux_rt_sigframe =
@@ -398,6 +476,24 @@ static const struct regcache_map_entry aarch64_linux_fpregmap[] =
     { 0 }
   };
 
+/* Since the C register numbers are determined dynamically, we leave
+   placeholders so we can update the numbers later.  */
+static struct regcache_map_entry aarch64_linux_cregmap[] =
+  {
+    { 31, -1, 16 }, /* c0 ... c30 */
+    { 1, -1, 16 }, /* pcc */
+    { 1, -1, 16 }, /* csp */
+    { 1, -1, 16 }, /* ddc */
+    { 1, -1, 16 }, /* ctpidr */
+    { 1, -1, 16 }, /* rcsp */
+    { 1, -1, 16 }, /* rddc */
+    { 1, -1, 16 }, /* rctpidr */
+    { 1, -1, 16 }, /* cid */
+    { 1, -1, 8 },  /* tag_map */
+    { 1, -1, 8 },  /* cctlr */
+    { 0 }
+  };
+
 /* Register set definitions.  */
 
 const struct regset aarch64_linux_gregset =
@@ -412,6 +508,13 @@ const struct regset aarch64_linux_fpregset =
     regcache_supply_regset, regcache_collect_regset
   };
 
+/* The capability register set.  */
+const struct regset aarch64_linux_cregset =
+  {
+    aarch64_linux_cregmap,
+    regcache_supply_regset, regcache_collect_regset
+  };
+
 /* The fields in an SVE header at the start of a SVE regset.  */
 
 #define SVE_HEADER_SIZE_LENGTH         4
@@ -452,7 +555,7 @@ aarch64_linux_core_read_vq (struct gdbarch *gdbarch, bfd *abfd)
       return 0;
     }
 
-  size_t size = bfd_section_size (abfd, sve_section);
+  size_t size = bfd_section_size (sve_section);
 
   /* Check extended state size.  */
   if (size < SVE_HEADER_SIZE)
@@ -586,7 +689,7 @@ aarch64_linux_collect_sve_regset (const struct regset *regset,
                            size - SVE_HEADER_SIZE);
 }
 
-/* Implement the "regset_from_core_section" gdbarch method.  */
+/* Implement the "iterate_over_regset_sections" gdbarch method.  */
 
 static void
 aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
@@ -627,6 +730,34 @@ aarch64_linux_iterate_over_regset_sections (struct gdbarch *gdbarch,
   else
     cb (".reg2", AARCH64_LINUX_SIZEOF_FPREGSET, AARCH64_LINUX_SIZEOF_FPREGSET,
        &aarch64_linux_fpregset, NULL, cb_data);
+
+
+  if (tdep->has_pauth ())
+    {
+      /* Create this on the fly in order to handle the variable location.  */
+      const struct regcache_map_entry pauth_regmap[] =
+       {
+         { 2, AARCH64_PAUTH_DMASK_REGNUM (tdep->pauth_reg_base), 8},
+         { 0 }
+       };
+
+      const struct regset aarch64_linux_pauth_regset =
+       {
+         pauth_regmap, regcache_supply_regset, regcache_collect_regset
+       };
+
+      cb (".reg-aarch-pauth", AARCH64_LINUX_SIZEOF_PAUTH,
+         AARCH64_LINUX_SIZEOF_PAUTH, &aarch64_linux_pauth_regset,
+         "pauth registers", cb_data);
+    }
+
+  /* Morello capability registers.  */
+  if (tdep->has_capability ())
+    {
+      cb (".reg-aarch-morello", AARCH64_LINUX_CREGS_SIZE,
+         AARCH64_LINUX_CREGS_SIZE, &aarch64_linux_cregset,
+         NULL, cb_data);
+    }
 }
 
 /* Implement the "core_read_description" gdbarch method.  */
@@ -635,12 +766,13 @@ static const struct target_desc *
 aarch64_linux_core_read_description (struct gdbarch *gdbarch,
                                     struct target_ops *target, bfd *abfd)
 {
-  CORE_ADDR aarch64_hwcap = 0;
+  CORE_ADDR hwcap = linux_get_hwcap (target);
+  CORE_ADDR hwcap2 = linux_get_hwcap2 (target);
+  bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
+  bool capability_p = hwcap2 & HWCAP2_MORELLO;
 
-  if (target_auxv_search (target, AT_HWCAP, &aarch64_hwcap) != 1)
-    return NULL;
-
-  return aarch64_read_description (aarch64_linux_core_read_vq (gdbarch, abfd));
+  return aarch64_read_description (aarch64_linux_core_read_vq (gdbarch, abfd),
+                                  pauth_p, capability_p);
 }
 
 /* Implementation of `gdbarch_stap_is_single_operand', as defined in
@@ -1329,7 +1461,7 @@ aarch64_linux_get_syscall_number (struct gdbarch *gdbarch, thread_info *thread)
      This function will only ever get called when stopped at the entry or exit
      of a syscall, so by checking for 0 in x0 (arg0/retval), x1 (arg1), x8
      (syscall), x29 (FP) and x30 (LR) we can infer:
-     1) Either inferior is at exit from sucessful execve.
+     1) Either inferior is at exit from successful execve.
      2) Or inferior is at entry to a call to io_setup with invalid arguments and
        a corrupted FP and LR.
      It should be safe enough to assume case 1.  */
@@ -1411,11 +1543,299 @@ aarch64_linux_syscall_record (struct regcache *regcache,
 
 /* Implement the "gcc_target_options" gdbarch method.  */
 
-static char *
+static std::string
 aarch64_linux_gcc_target_options (struct gdbarch *gdbarch)
 {
   /* GCC doesn't know "-m64".  */
-  return NULL;
+  return {};
+}
+
+/* AArch64 Linux implementation of the report_signal_info gdbarch
+   hook.  Displays information about possible memory tag violations or
+   capability violations.  */
+
+static void
+aarch64_linux_report_signal_info (struct gdbarch *gdbarch,
+                                 struct ui_out *uiout,
+                                 enum gdb_signal siggnal)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  if (!tdep->has_capability () || siggnal != GDB_SIGNAL_SEGV)
+    return;
+
+  CORE_ADDR fault_addr = 0;
+  long si_code = 0;
+
+  try
+    {
+      /* Sigcode tells us if the segfault is actually a capability
+        violation.  */
+      si_code = parse_and_eval_long ("$_siginfo.si_code\n");
+
+      fault_addr
+       = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr");
+    }
+  catch (const gdb_exception &exception)
+    {
+      return;
+    }
+
+  /* If this is not a capability violation, just return.  */
+  if (si_code != SEGV_CAPTAGERR && si_code != SEGV_CAPSEALEDERR
+      && si_code != SEGV_CAPBOUNDSERR && si_code != SEGV_CAPPERMERR
+      && si_code != SEGV_CAPSTORETAGERR)
+    return;
+
+  uiout->text ("\n");
+
+  std::string str_si_code;
+
+  switch (si_code)
+    {
+      case SEGV_CAPTAGERR:
+       str_si_code = "tag";
+       break;
+      case SEGV_CAPSEALEDERR:
+       str_si_code = "sealed";
+       break;
+      case SEGV_CAPBOUNDSERR:
+       str_si_code = "bounds";
+       break;
+      case SEGV_CAPPERMERR:
+       str_si_code = "permission";
+       break;
+      case SEGV_CAPSTORETAGERR:
+       str_si_code = "access";
+       break;
+      default:
+       str_si_code = "unknown";
+       break;
+    }
+
+  std::string str_meaning = "Capability " + str_si_code + " fault";
+  uiout->field_string ("sigcode-meaning", str_meaning);
+
+  /* FIXME-Morello: Show more information about the faults.  */
+  uiout->text (_(" while accessing address "));
+  uiout->field_core_addr ("fault-addr", gdbarch, fault_addr);
+}
+
+/* AArch64 Linux implementation of the get_cap_tag_from_address gdbarch
+   hook.  Returns the tag from the capability located at ADDR.  */
+
+static bool
+aarch64_linux_get_cap_tag_from_address (struct gdbarch *gdbarch, CORE_ADDR addr)
+{
+  gdb::byte_vector cap;
+
+  cap = target_read_capability (addr);
+
+  if (cap.size () == 0)
+    return false;
+
+  return cap[0] != 0;
+}
+
+/* Return the number of Morello memory tag granules contained in the memory
+   range [addr, addr + len).  */
+
+static size_t
+morello_get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size)
+{
+  /* An empty range has 0 tag granules.  */
+  if (len == 0)
+    return 0;
+
+  /* Start address */
+  CORE_ADDR s_addr = align_down (addr, granule_size);
+  /* End address */
+  CORE_ADDR e_addr = align_down (addr + len - 1, granule_size);
+
+  /* We always have at least 1 granule because len is non-zero at this
+     point.  */
+  return 1 + (e_addr - s_addr) / granule_size;
+}
+
+/* Maximum number of tags to request.  */
+#define MAX_TAGS_TO_TRANSFER 1024
+
+/* AArch64 Linux implementation of the aarch64_create_memtag_notes_from_range
+   gdbarch hook.  Create core file notes for memory tags.  */
+
+static std::vector<gdb::byte_vector>
+aarch64_linux_create_memtag_notes_from_range (struct gdbarch *gdbarch,
+                                             CORE_ADDR start_address,
+                                             CORE_ADDR end_address)
+{
+  /* We only handle CHERI capability tags for now.  */
+
+  /* Figure out how many tags we need to store in this memory range.  */
+  int granules = morello_get_tag_granules (start_address,
+                                          end_address - start_address,
+                                          MORELLO_TAG_GRANULE_SIZE);
+
+  /* A vector to store multiple notes.  */
+  std::vector<gdb::byte_vector> notes;
+
+  /* Add the CHERI note.  */
+  notes.resize (1);
+  /* Resize to the number of bytes the tag granules will take.  */
+  notes[0].resize (sizeof (struct tag_dump_header) + granules);
+
+  /* Retrieve the tags and store them in the vector.  */
+  gdb::byte_vector tags;
+  CORE_ADDR address = start_address;
+  while (granules > 0)
+    {
+      /* Transfer tags in chunks.  */
+      gdb::byte_vector tags_read;
+      size_t xfer_len
+       = (granules >= MAX_TAGS_TO_TRANSFER)? MAX_TAGS_TO_TRANSFER : granules;
+
+      /* First clear the vector of tags.  */
+      tags_read.resize (0);
+
+      /* Copy tags in chunks.  */
+      while (tags_read.size () < xfer_len)
+       {
+         CORE_ADDR addr
+           = address + tags_read.size () * MORELLO_TAG_GRANULE_SIZE;
+
+         /* Always align the address to 16 bytes so we can read the
+            capability properly.  When we have a request to read only the
+            capability tags, then we won't need to do this.  */
+         CORE_ADDR aligned_addr = align_down (addr, MORELLO_TAG_GRANULE_SIZE);
+         bool tag
+           = aarch64_linux_get_cap_tag_from_address (gdbarch, aligned_addr);
+         gdb_byte tag_byte = (tag == false)? 0 : 1;
+         tags_read.push_back (tag_byte);
+       }
+
+      /* This process may take a while.  Make sure it is interruptible.  */
+      QUIT;
+
+      /* Transfer over the tags that have been read.  */
+      tags.insert (tags.end (), tags_read.begin (), tags_read.end ());
+
+      /* Adjust the remaining granules and starting address.  */
+      granules -= tags_read.size ();
+      address += tags_read.size () * MORELLO_TAG_GRANULE_SIZE;
+    }
+
+  /* Create the header.  Please note we don't yet compress the tag data.
+     We may do so in the future to save space, since a capability tag is only
+     1 bit in size.  */
+  struct tag_dump_header header;
+  header.format = ELF_CORE_TAG_CHERI;
+  header.start_vma = start_address;
+  header.end_vma = end_address;
+  header.u.cheri.granule_byte_size = MORELLO_TAG_GRANULE_SIZE;
+  header.u.cheri.tag_bit_size = MORELLO_TAG_BIT_SIZE;
+  header.u.cheri.__unused = 0;
+
+  /* Copy the tags to the note.  */
+  memcpy (notes[0].data (), &header, sizeof (header));
+  memcpy (notes[0].data () + sizeof (header), tags.data (), tags.size ());
+
+  return notes;
+}
+
+/* AArch64 Linux implementation of the aarch64_decode_memtag_note gdbarch
+   hook.  Decode a memory tag note and return the tag that it contains for
+   a particular address.  */
+
+static gdb_byte
+aarch64_linux_decode_memtag_note (struct gdbarch *gdbarch,
+                                 gdb::array_view <const gdb_byte> note,
+                                 CORE_ADDR address)
+{
+  /* Read the header.  */
+  struct tag_dump_header header;
+  memcpy (&header, note.data (), sizeof (header));
+
+  /* Align the address to 16 bytes.  We assume the start_vma is already
+     aligned to the proper boundary, otherwise we would have tags dumped
+     into two different memory mappings.  */
+  address = align_down (address, MORELLO_TAG_GRANULE_SIZE);
+  CORE_ADDR offset = address - header.start_vma;
+  gdb_byte tag;
+
+  /* Read the tag.  */
+  memcpy (&tag, note.data () + sizeof (header)
+         + (offset / header.u.cheri.granule_byte_size), 1);
+
+  return tag;
+}
+
+/* Implement the maintenance print capability tag command.  */
+
+static void
+maint_print_cap_from_addr_cmd (const char *args, int from_tty)
+{
+  gdb::byte_vector cap;
+  CORE_ADDR addr = parse_and_eval_address (args);
+  cap = target_read_capability (addr);
+
+  if (cap.empty ())
+    {
+      fprintf_unfiltered (gdb_stdlog,
+                         "Could not read capability from address %s.\n",
+                         phex_nz (addr, 8));
+      return;
+    }
+
+  for (auto it : cap)
+    fprintf_unfiltered (gdb_stdlog, "%02x ", it);
+  fputs_unfiltered ("\n", gdb_stdlog);
+
+  bool tag = (cap[0] == 1);
+  uint128_t cap_128bits;
+  memcpy (&cap_128bits, &cap[1], 16);
+
+  capability capability (cap_128bits, tag);
+
+  fprintf_unfiltered (gdb_stdlog, "verbose: %s\n",
+                     capability.to_str (false).c_str ());
+  fputs_unfiltered ("\n", gdb_stdlog);
+  fprintf_unfiltered (gdb_stdlog, "compact: %s\n",
+                     capability.to_str (true).c_str ());
+}
+
+/* Implement the maintenance set capability in memory command.  */
+
+static void
+maint_set_capability_in_memory_cmd (const char *args, int from_tty)
+{
+  std::string addr_str, tag_str, upper_str, lower_str;
+  const char *args_ptr = args;
+
+  addr_str = extract_string_maybe_quoted (&args_ptr);
+  tag_str = extract_string_maybe_quoted (&args_ptr);
+  upper_str = extract_string_maybe_quoted (&args_ptr);
+  lower_str = extract_string_maybe_quoted (&args_ptr);
+
+  CORE_ADDR addr = parse_and_eval_address (addr_str.c_str ());
+  CORE_ADDR tag_part = parse_and_eval_address (tag_str.c_str ());
+  CORE_ADDR half_a = parse_and_eval_address (upper_str.c_str ());
+  CORE_ADDR half_b = parse_and_eval_address (lower_str.c_str ());
+
+  unsigned __int128 a, b;
+
+  a = half_a;
+  b = half_b;
+
+  a = (a << 64) | b;
+  bool tag = (tag_part != 0)? true : false;
+
+  gdb::byte_vector cap;
+
+  cap.resize (17);
+  memcpy (cap.data (), &tag, 1);
+  memcpy (cap.data () + 1, &a, 16);
+
+  if (!target_write_capability (addr, {cap.data (), cap.size ()}))
+    perror_with_name (_("Failed to set capability in memory."));
 }
 
 static void
@@ -1639,20 +2059,76 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   set_xml_syscall_file_name (gdbarch, "syscalls/aarch64-linux.xml");
   set_gdbarch_get_syscall_number (gdbarch, aarch64_linux_get_syscall_number);
 
-  /* Displaced stepping.  */
-  set_gdbarch_max_insn_length (gdbarch, 4 * DISPLACED_MODIFIED_INSNS);
-  set_gdbarch_displaced_step_copy_insn (gdbarch,
-                                       aarch64_displaced_step_copy_insn);
-  set_gdbarch_displaced_step_fixup (gdbarch, aarch64_displaced_step_fixup);
-  set_gdbarch_displaced_step_location (gdbarch, linux_displaced_step_location);
   set_gdbarch_displaced_step_hw_singlestep (gdbarch,
                                            aarch64_displaced_step_hw_singlestep);
 
   set_gdbarch_gcc_target_options (gdbarch, aarch64_linux_gcc_target_options);
+
+  if (tdep->has_capability ())
+    {
+      /* Register CHERI-specific linkmap offsets for the AAPCS64_CAP ABI.  */
+      if (tdep->abi == AARCH64_ABI_AAPCS64_CAP)
+       set_solib_svr4_fetch_link_map_offsets (gdbarch,
+                                       svr4_lp64_cheri_fetch_link_map_offsets);
+
+      /* Initialize the register numbers for the core file register set.
+        Please note the PCC/CSP position in GDB's target description is
+        the inverse of the position in the Linux Kernel's user_morello_state
+        data structure.  This can cause some confusion.  */
+      aarch64_linux_cregmap[0].regno = tdep->cap_reg_base;
+      aarch64_linux_cregmap[1].regno = tdep->cap_reg_pcc;
+      aarch64_linux_cregmap[2].regno = tdep->cap_reg_csp;
+
+      /* Set the rest of the registers.  */
+      int next_regnum = tdep->cap_reg_base + 33;
+      for (int i = 3; i <= 10; i++)
+       {
+         aarch64_linux_cregmap[i].regno = next_regnum;
+         next_regnum++;
+       }
+
+      set_gdbarch_report_signal_info (gdbarch,
+                                     aarch64_linux_report_signal_info);
+      set_gdbarch_get_cap_tag_from_address (gdbarch,
+                                           aarch64_linux_get_cap_tag_from_address);
+
+      /* Core file helpers.  */
+
+      /* Core file helper to create memory tag notes for a particular range of
+        addresses.  */
+      set_gdbarch_create_memtag_notes_from_range (gdbarch,
+                                 aarch64_linux_create_memtag_notes_from_range);
+
+      /* Core file helper to decode a memory tag note.  */
+      set_gdbarch_decode_memtag_note (gdbarch,
+                                     aarch64_linux_decode_memtag_note);
+
+      add_cmd ("cap_from_addr", class_maintenance, maint_print_cap_from_addr_cmd,
+              _("Print the capability from addr."), &maintenanceprintlist);
+
+      add_cmd ("cap_in_memory", class_maintenance,
+              maint_set_capability_in_memory_cmd,
+              _("Print the capability from addr."), &maintenancelist);
+    }
+  else
+    {
+      /* Displaced stepping.  */
+      /* Note: Morello does not support displaced stepping yet because
+        adjustments to GPR's may not be correct.  This is because GDB can't
+        make adjustments to the upper 65 bits of the C registers.  */
+      set_gdbarch_max_insn_length (gdbarch,
+                                  4 * AARCH64_DISPLACED_MODIFIED_INSNS);
+      set_gdbarch_displaced_step_copy_insn (gdbarch,
+                                           aarch64_displaced_step_copy_insn);
+      set_gdbarch_displaced_step_fixup (gdbarch, aarch64_displaced_step_fixup);
+      set_gdbarch_displaced_step_location (gdbarch,
+                                          linux_displaced_step_location);
+    }
 }
 
+void _initialize_aarch64_linux_tdep ();
 void
-_initialize_aarch64_linux_tdep (void)
+_initialize_aarch64_linux_tdep ()
 {
   gdbarch_register_osabi (bfd_arch_aarch64, 0, GDB_OSABI_LINUX,
                          aarch64_linux_init_abi);