]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Enable capability writes to memory
authorLuis Machado <luis.machado@linaro.org>
Tue, 12 Jan 2021 18:53:00 +0000 (15:53 -0300)
committerJohn Baldwin <jhb@FreeBSD.org>
Thu, 1 Sep 2022 22:57:21 +0000 (15:57 -0700)
Enable writing/forging capabilities to memory via the PTRACE_POKECAP ptrace
request.  This patch enables both GDB and gdbserver for Morello.

For remote writes, we use the qXfer:capa:write packet.

gdb/ChangeLog:

2021-03-17  Luis Machado  <luis.machado@arm.com>

* aarch64-linux-nat.c (aarch64_linux_nat_target)
<write_capability>: New member function override.
(aarch64_linux_nat_target::write_capability): New member
function.
(maint_print_cap_from_addr_cmd): Adjust printing format.
(maint_set_capability_in_memory_cmd): New maintenance function.
(add_show_debug_regs_command): Register new maintenance command.
* nat/aarch64-cap-linux.c (aarch64_linux_write_capability): New
function.
* nat/aarch64-cap-linux.h (aarch64_linux_write_capability): New
prototype.
* remote.c (remote_target)
<write_capability>: New member function override.
Adjust enum documentation for PACKET_qXfer_capability.
(remote_target::write_capability): New member function.
(_initialize_remote): Adjust documentation for
PACKET_qXfer_capability.
* target-debug.h
(target_debug_print_gdb_array_view_const_gdb_byte): New macro.
* target-delegates.c: Regenerate.
* target.c (target_write_capability): New function.
* target.h (struct target_ops) <write_capability>: New virtual member
function.
(target_write_capability): New prototype.

gdbserver/ChangeLog:

2021-03-17  Luis Machado  <luis.machado@arm.com>

* linux-aarch64-low.cc (aarch64_target::qxfer_capability): Handle
capability writes.
* server.cc (handle_qxfer_capability): Likewise.

gdb/aarch64-linux-nat.c
gdb/nat/aarch64-cap-linux.c
gdb/nat/aarch64-cap-linux.h
gdb/remote.c
gdb/target-debug.h
gdb/target-delegates.c
gdb/target.c
gdb/target.h
gdbserver/linux-aarch64-low.cc
gdbserver/server.cc

index 0d9e16b23493f5369425a95702425bb3b4783e5e..189f31702518c31fc41bebd878ed3a169421af7d 100644 (file)
@@ -111,6 +111,8 @@ public:
                      const gdb::byte_vector &tags, int type) override;
 
   gdb::byte_vector read_capability (CORE_ADDR addr) override;
+  bool write_capability (CORE_ADDR addr,
+                        gdb::array_view<const gdb_byte> buffer) override;
 };
 
 static aarch64_linux_nat_target the_aarch64_linux_nat_target;
@@ -991,6 +993,25 @@ aarch64_linux_nat_target::read_capability (CORE_ADDR addr)
   return cap_vec;
 }
 
+/* Implement the "write_capability" target_ops method.  */
+
+bool
+aarch64_linux_nat_target::write_capability (CORE_ADDR addr,
+                                           gdb::array_view<const gdb_byte> buffer)
+{
+  int tid = get_ptrace_pid (inferior_ptid);
+
+  struct user_cap cap;
+
+  memcpy (&cap.tag, buffer.data (), 1);
+  memcpy (&cap.val, buffer.data () + 1, 16);
+
+  if (!aarch64_linux_write_capability (tid, addr, cap))
+    perror_with_name (_("Unable to write capability from address."));
+
+  return true;
+}
+
 /* Implement the maintenance print capability tag command.  */
 
 static void
@@ -1001,11 +1022,47 @@ maint_print_cap_from_addr_cmd (const char *args, int from_tty)
   cap = target_read_capability (addr);
 
   for (auto it : cap)
-    fprintf_unfiltered (gdb_stdlog, "%x ", it);
+    fprintf_unfiltered (gdb_stdlog, "%02x ", it);
 
   fputs_unfiltered ("\n", gdb_stdlog);
 }
 
+/* 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."));
+}
+
 void _initialize_aarch64_linux_nat ();
 void
 _initialize_aarch64_linux_nat ()
@@ -1016,6 +1073,10 @@ _initialize_aarch64_linux_nat ()
 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);
+
   /* Register the target.  */
   linux_target = &the_aarch64_linux_nat_target;
   add_inf_child_target (&the_aarch64_linux_nat_target);
index cd204d11e479651d3597536b2c7f80296a12dd2b..1b427803c1636deb34803310bf96eef8210bd7aa 100644 (file)
@@ -35,3 +35,16 @@ aarch64_linux_read_capability (int tid, CORE_ADDR address,
 
   return true;
 }
+
+/* See aarch64-cap-linux.h */
+
+bool
+aarch64_linux_write_capability (int tid, CORE_ADDR address,
+                               const user_cap &cap)
+{
+  /* Fetch the tag from ptrace.  */
+  if (ptrace (PTRACE_POKECAP, tid, address, &cap) < 0)
+    return false;
+
+  return true;
+}
index b5394704e4ff0bdb3bb9ea7ad1db606498d7fa1c..3dc582a4c94d53fd049fde664ad508420e9ae865 100644 (file)
@@ -65,4 +65,11 @@ struct user_cap {
 
 extern bool aarch64_linux_read_capability (int tid, CORE_ADDR address,
                                           user_cap &cap);
+
+/* For thread TID, write capability CAP to the memory address ADDRESS.
+
+   Return true if successful and false otherwise.  */
+
+extern bool aarch64_linux_write_capability (int tid, CORE_ADDR address,
+                                           const user_cap &cap);
 #endif /* NAT_AARCH64_CAP_LINUX_H */
index 68511621772a6c20e39a1ed1d639d78c8493c54a..b941fbfdca47c90042553a81c3ea0485b00d9ae5 100644 (file)
@@ -690,6 +690,8 @@ public:
                      const gdb::byte_vector &tags, int type) override;
 
   gdb::byte_vector read_capability (CORE_ADDR addr) override;
+  bool write_capability (CORE_ADDR addr,
+                        gdb::array_view<const gdb_byte> buffer) override;
 
 public: /* Remote specific methods.  */
 
@@ -2215,7 +2217,7 @@ enum {
      packets and the tag violation stop replies.  */
   PACKET_memory_tagging_feature,
 
-  /* Support for the qXfer:capa:read packet.  */
+  /* Support for the qXfer:capa:read and qXfer:capa:write packets.  */
   PACKET_qXfer_capability,
 
   PACKET_MAX
@@ -11277,6 +11279,10 @@ remote_target::xfer_partial (enum target_object object,
                                  readbuf, offset, len, xfered_len,
                                  &remote_protocol_packets
                                  [PACKET_qXfer_capability]);
+      else if (writebuf)
+       return remote_write_qxfer ("capa", annex, writebuf, offset, len,
+                                  xfered_len, &remote_protocol_packets
+                                  [PACKET_qXfer_capability]);
       else
        return TARGET_XFER_E_IO;
     }
@@ -14945,6 +14951,28 @@ remote_target::read_capability (CORE_ADDR addr)
   return cap_vec;
 }
 
+/* Implementation of the write_capability method.  */
+
+bool
+remote_target::write_capability (CORE_ADDR addr,
+                                gdb::array_view<const gdb_byte> buffer)
+{
+  gdb_assert (!buffer.empty ());
+  std::string addr_str = string_printf ("%s", phex_nz (addr, 0));
+  ULONGEST xfered_len;
+  enum target_xfer_status status;
+
+  status = target_xfer_partial (current_inferior ()->top_target (),
+                               TARGET_OBJECT_CAPABILITY, addr_str.c_str (),
+                               nullptr, buffer.data (), 0, buffer.size (),
+                               &xfered_len);
+
+  if (status != TARGET_XFER_OK)
+    perror_with_name (_("Unable to write capability to address."));
+
+  return true;
+}
+
 void _initialize_remote ();
 void
 _initialize_remote ()
@@ -15342,7 +15370,7 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
                         "memory-tagging-feature", "memory-tagging-feature", 0);
 
   add_packet_config_cmd (&remote_protocol_packets[PACKET_qXfer_capability],
-                        "qXfer:capa:read", "read-capability", 0);
+                        "qXfer:capa:read", "read-write-capability", 0);
 
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
index e7a509858a715296be5c53de80e267f66c2ce079..86c856d4f947299937aac8df073c605c3d836e6c 100644 (file)
   target_debug_do_print (host_address_to_string (X.get ()))
 #define target_debug_print_gdb_array_view_const_int(X) \
   target_debug_do_print (host_address_to_string (X.data ()))
+#define target_debug_print_gdb_array_view_const_gdb_byte(X)    \
+  target_debug_do_print (host_address_to_string (X.data ()))
 #define target_debug_print_inferior_p(inf) \
   target_debug_do_print (host_address_to_string (inf))
 #define target_debug_print_record_print_flags(X) \
index c2e4d6e266651a74ef6349b575aaf9b12616fdcf..693c9f13e6b9f206fab59314334940d9faba730f 100644 (file)
@@ -197,6 +197,7 @@ struct dummy_target : public target_ops
   bool fetch_memtags (CORE_ADDR arg0, size_t arg1, gdb::byte_vector &arg2, int arg3) override;
   bool store_memtags (CORE_ADDR arg0, size_t arg1, const gdb::byte_vector &arg2, int arg3) override;
   gdb::byte_vector read_capability (CORE_ADDR arg0) override;
+  bool write_capability (CORE_ADDR arg0, gdb::array_view<const gdb_byte> arg1) override;
 };
 
 struct debug_target : public target_ops
@@ -372,6 +373,7 @@ struct debug_target : public target_ops
   bool fetch_memtags (CORE_ADDR arg0, size_t arg1, gdb::byte_vector &arg2, int arg3) override;
   bool store_memtags (CORE_ADDR arg0, size_t arg1, const gdb::byte_vector &arg2, int arg3) override;
   gdb::byte_vector read_capability (CORE_ADDR arg0) override;
+  bool write_capability (CORE_ADDR arg0, gdb::array_view<const gdb_byte> arg1) override;
 };
 
 void
@@ -4564,3 +4566,31 @@ debug_target::read_capability (CORE_ADDR arg0)
   return result;
 }
 
+bool
+target_ops::write_capability (CORE_ADDR arg0, gdb::array_view<const gdb_byte> arg1)
+{
+  return this->beneath ()->write_capability (arg0, arg1);
+}
+
+bool
+dummy_target::write_capability (CORE_ADDR arg0, gdb::array_view<const gdb_byte> arg1)
+{
+  tcomplain ();
+}
+
+bool
+debug_target::write_capability (CORE_ADDR arg0, gdb::array_view<const gdb_byte> arg1)
+{
+  bool result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->write_capability (...)\n", this->beneath ()->shortname ());
+  result = this->beneath ()->write_capability (arg0, arg1);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->write_capability (", this->beneath ()->shortname ());
+  target_debug_print_CORE_ADDR (arg0);
+  fputs_unfiltered (", ", gdb_stdlog);
+  target_debug_print_gdb_array_view_const_gdb_byte (arg1);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_bool (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
index 529d28af7caaa62e70fd4d1bec8106d96235873b..b929e44372218da5d88df2bc05b793b0eafe1010 100644 (file)
@@ -4299,6 +4299,15 @@ target_read_capability (CORE_ADDR addr)
   return current_inferior ()->top_target ()->read_capability (addr);
 }
 
+/* See target.h.  */
+
+bool
+target_write_capability (CORE_ADDR addr,
+                        gdb::array_view<const gdb_byte> buffer)
+{
+  return current_inferior ()->top_target ()->write_capability (addr, buffer);
+}
+
 \f
 
 static char targ_desc[] =
index eb99cff174b28ea5843af83a15e8956317fd701b..7bfe6b2f1f088320b7ffdc8ee1674784957ce90a 100644 (file)
@@ -1326,6 +1326,11 @@ struct target_ops
     /* Read a capability from ADDR.  */
     virtual gdb::byte_vector read_capability (CORE_ADDR addr)
       TARGET_DEFAULT_NORETURN (tcomplain ());
+
+    /* Write CAPABILITY to ADDR.  */
+    virtual bool write_capability (CORE_ADDR addr,
+                                  gdb::array_view<const gdb_byte> buffer)
+      TARGET_DEFAULT_NORETURN (tcomplain ());
   };
 
 /* Deleter for std::unique_ptr.  See comments in
@@ -2589,4 +2594,8 @@ extern void target_done_generating_core (void);
 /* See read_capability.  */
 extern gdb::byte_vector target_read_capability (CORE_ADDR addr);
 
+/* See write_capability.  */
+extern bool target_write_capability (CORE_ADDR addr,
+                                    gdb::array_view<const gdb_byte> buffer);
+
 #endif /* !defined (TARGET_H) */
index c9c05c065500c31da9051417fbbc4da47259b0b1..9c90f61a6d6945d992450ffba935b183e0db493d 100644 (file)
@@ -3430,14 +3430,31 @@ aarch64_target::qxfer_capability (const CORE_ADDR address,
 
   struct user_cap cap;
 
-  if (!aarch64_linux_read_capability (tid, address, cap))
+  if (readbuf != nullptr)
     {
-      warning (_("Unable to read capability from address."));
-      return 0;
+      if (!aarch64_linux_read_capability (tid, address, cap))
+       {
+         warning (_("Unable to read capability from address."));
+         return 0;
+       }
+
+      /* Copy data to readbuf.  */
+      memcpy (readbuf, &cap.tag, 1);
+      memcpy (readbuf + 1, &cap.val, 16);
     }
+  else
+    {
+      /* Copy data from writebuf.  */
+      memcpy (&cap.tag, writebuf, 1);
+      memcpy (&cap.val, writebuf + 1, 16);
+      memset (&cap.__reserved, 0, 15);
 
-  memcpy (readbuf, &cap.tag, 1);
-  memcpy (readbuf + 1, &cap.val, 16);
+      if (!aarch64_linux_write_capability (tid, address, cap))
+       {
+         warning (_("Unable to write capability to address."));
+         return 0;
+       }
+    }
 
   return sizeof (cap.val) + 1;
 }
index 99dff6a383c29f1ad431aa3876b72450060c5b54..6153ff54fdad871b75af67c0e67a61d6425512e4 100644 (file)
@@ -2016,14 +2016,27 @@ handle_qxfer_capability (const char *annex, gdb_byte *readbuf,
                         const gdb_byte *writebuf, ULONGEST offset,
                         LONGEST len)
 {
-   if (!the_target->supports_qxfer_capability () || writebuf != NULL)
+  if (!the_target->supports_qxfer_capability ())
     return -2;
 
+  gdb_assert (readbuf != nullptr || writebuf != nullptr);
+
   CORE_ADDR addr;
   unpack_varlen_hex (annex, &addr);
 
-  /* Read a capability and its tag.  */
-  return the_target->qxfer_capability (addr, readbuf, NULL, offset, len);
+  if (readbuf != nullptr)
+    {
+      /* Read a capability and its tag.  */
+      return the_target->qxfer_capability (addr, readbuf, nullptr, offset, len);
+    }
+  else
+    {
+      /* Write a capability to memory.  */
+      return the_target->qxfer_capability (addr, nullptr, writebuf, offset,
+                                          len);
+    }
+
+  return -2;
 }
 
 static const struct qxfer qxfer_packets[] =