[PATCH 00/23] Memory Tagging Support + AArch64 Linux implementation

classic Classic list List threaded Threaded
42 messages Options
123
Reply | Threaded
Open this post in threaded view
|

[PATCH 00/23] Memory Tagging Support + AArch64 Linux implementation

Sourceware - gdb-patches mailing list
This patch series implements general memory tagging support for GDB, as well
as an implementation for AArch64 Linux.

Memory tagging improves memory safety by tagging various parts of memory and
raising exceptions when the allocation tag (the one associated with a range of
memory addresses) does not match the logical tag contained in a pointer that is
used to access the memory area.

We already have an implementation of such a mechanism for sparc64 (ADI), but
it is target-specific and not exposed to the rest of GDB. This series aims to
make the infrastructure available to other targets that may wish to support
their specific memory tagging approaches. For AArch64 Linux this is called
MTE (Memory Tagging Extensions).

The series is split into a set that deals with generic changes to GDB's
infrastructure (target methods, gdbarch hooks and remote packets), a set that
implements support for AArch64 Linux and one last set that implements new
commands, updates the documentation and adds tests.

The goal is to make it so the architecture independent parts of GDB don't
need to interpret tag formats, given the formats are likely different
for each architecture.  For this reason, GDB will handle tags as a sequence of
bytes and will not assume a particular format.

The architecture-specific code can handle the sequence of bytes appropriately.

Luis Machado (23):
  New target methods for memory tagging support
  New gdbarch memory tagging hooks
  Add GDB-side remote target support for memory tagging
  Unit testing for GDB-side remote memory tagging handling
  GDBserver remote packet support for memory tagging
  Unit tests for gdbserver memory tagging remote packets
  Documentation for memory tagging remote packets
  AArch64: Add MTE CPU feature check support
  AArch64: Add target description/feature for MTE registers
  AArch64: Add MTE register set support for GDB and gdbserver
  AArch64: Add MTE ptrace requests
  AArch64: Implement memory tagging target methods for AArch64
  Refactor parsing of /proc/<pid>/smaps
  AArch64: Implement the memory tagging gdbarch hooks
  AArch64: Add unit testing for logical tag set/get operations
  AArch64: Report tag violation error information
  AArch64: Add gdbserver MTE support
  New mtag commands
  Documentation for the new mtag commands
  Extend "x" and "print" commands to support memory tagging
  Document new "x" and "print" memory tagging extensions
  Add NEWS entry.
  Add memory tagging testcases

 gdb/Makefile.in                        |   3 +
 gdb/NEWS                               |  32 ++
 gdb/aarch64-linux-nat.c                | 121 ++++++-
 gdb/aarch64-linux-tdep.c               | 330 ++++++++++++++++-
 gdb/aarch64-tdep.c                     |  40 ++-
 gdb/aarch64-tdep.h                     |  12 +-
 gdb/arch-utils.c                       |  50 +++
 gdb/arch-utils.h                       |  23 ++
 gdb/arch/aarch64-mte-linux.c           |  70 ++++
 gdb/arch/aarch64-mte-linux.h           |  66 ++++
 gdb/arch/aarch64.c                     |   7 +-
 gdb/arch/aarch64.h                     |   7 +-
 gdb/configure.nat                      |   3 +-
 gdb/configure.tgt                      |   1 +
 gdb/doc/gdb.texinfo                    | 174 ++++++++-
 gdb/features/Makefile                  |   1 +
 gdb/features/aarch64-mte.c             |  14 +
 gdb/features/aarch64-mte.xml           |  11 +
 gdb/gdbarch.c                          | 137 ++++++++
 gdb/gdbarch.h                          |  53 +++
 gdb/gdbarch.sh                         |  36 ++
 gdb/linux-tdep.c                       | 356 ++++++++++++-------
 gdb/linux-tdep.h                       |   4 +
 gdb/nat/aarch64-mte-linux-ptrace.c     | 200 +++++++++++
 gdb/nat/aarch64-mte-linux-ptrace.h     |  50 +++
 gdb/printcmd.c                         | 468 ++++++++++++++++++++++++-
 gdb/remote.c                           | 230 ++++++++++++
 gdb/target-delegates.c                 |  84 +++++
 gdb/target.h                           |  25 ++
 gdb/testsuite/gdb.arch/aarch64-mte.c   | 107 ++++++
 gdb/testsuite/gdb.arch/aarch64-mte.exp | 371 ++++++++++++++++++++
 gdb/testsuite/gdb.base/memtag.c        |  22 ++
 gdb/testsuite/gdb.base/memtag.exp      |  64 ++++
 gdb/testsuite/lib/gdb.exp              |  16 +
 gdb/valprint.h                         |   1 +
 gdbserver/Makefile.in                  |   1 +
 gdbserver/configure.srv                |   2 +
 gdbserver/linux-aarch64-ipa.cc         |   8 +-
 gdbserver/linux-aarch64-low.cc         |  89 ++++-
 gdbserver/linux-aarch64-tdesc.cc       |  10 +-
 gdbserver/linux-aarch64-tdesc.h        |   3 +-
 gdbserver/remote-utils.cc              |  40 +--
 gdbserver/remote-utils.h               |   2 +
 gdbserver/server.cc                    | 214 +++++++++++
 gdbserver/server.h                     |   3 +
 gdbserver/target.cc                    |  20 ++
 gdbserver/target.h                     |  17 +
 gdbsupport/rsp-low.cc                  |   2 +-
 include/elf/common.h                   |   3 +
 49 files changed, 3421 insertions(+), 182 deletions(-)
 create mode 100644 gdb/arch/aarch64-mte-linux.c
 create mode 100644 gdb/arch/aarch64-mte-linux.h
 create mode 100644 gdb/features/aarch64-mte.c
 create mode 100644 gdb/features/aarch64-mte.xml
 create mode 100644 gdb/nat/aarch64-mte-linux-ptrace.c
 create mode 100644 gdb/nat/aarch64-mte-linux-ptrace.h
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte.c
 create mode 100644 gdb/testsuite/gdb.arch/aarch64-mte.exp
 create mode 100644 gdb/testsuite/gdb.base/memtag.c
 create mode 100644 gdb/testsuite/gdb.base/memtag.exp

--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 01/23] New target methods for memory tagging support

Sourceware - gdb-patches mailing list
This patch starts adding some of the generic pieces to accomodate memory
tagging.

We have three new target methods:

- supports_memory_tagging: Checks if the target supports memory tagging. This
  defaults to false for targets that don't support memory tagging.

- fetch_memtags: Fetches the allocation tags associated with a particular
  memory range [address, address + length).

  The default is to return 0 without returning any tags. This should only
  be called if memory tagging is supported.

- store_memtags: Stores a set of allocation tags for a particular memory
  range [address, address + length).

  The default is to return 0. This should only
  be called if memory tagging is supported.

It also adds a control option for enabling/disabling memory tagging
manually: set memory-tagging on/off.

The default is "on", with GDB making its use conditional to the
architecture supporting memory tagging.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * printcmd.c (memtag): New static global.
        (show_memtag): New function.
        (_initialize_printcmd): Add set/show memory-tagging command.
        * remote.c (remote_target) <supports_memory_tagging>: New method
        override.
        <fetch_memtags>: New method override.
        <store_memtags>: New method override.
        (remote_target::supports_memory_tagging): New method.
        (remote_target::fetch_memtags): New method.
        (remote_target::store_memtags): New method.
        * target-delegates.c
        (dummy_target) <supports_memory_tagging>: New method override.
        <fetch_memtags>: New method override.
        <store_memtags>: New method override.
        (debug_target) <supports_memory_tagging>: New method override.
        <fetch_memtags>: New method override.
        <store_memtags>: New method override.
        (target_ops::supports_memory_tagging): New method.
        (target_ops::fetch_memtags): New method.
        (target_ops::store_memtags): New method.
        (dummy_target::supports_memory_tagging): New method.
        (dummy_target::fetch_memtags): New method.
        (dummy_target::store_memtags): New method.
        (debug_target::supports_memory_tagging): New method.
        (debug_target::fetch_memtags): New method.
        (debug_target::store_memtags): New method.
        * target.h (struct target_ops) <supports_memory_tagging>: New virtual
        method.
        <fetch_memtags: New virtual method.
        <store_memtags>: New virtual method.
        (target_supports_memory_tagging): Define.
        (target_fetch_memtags): Define.
        (target_store_memtags): Define.
---
 gdb/printcmd.c         | 28 ++++++++++++++
 gdb/remote.c           | 36 ++++++++++++++++++
 gdb/target-delegates.c | 84 ++++++++++++++++++++++++++++++++++++++++++
 gdb/target.h           | 25 +++++++++++++
 4 files changed, 173 insertions(+)

diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index 309d2cabff..d0261a021a 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -84,6 +84,24 @@ static CORE_ADDR last_examine_address;
 
 static value_ref_ptr last_examine_value;
 
+/* If TRUE (default), and the architecture supports it, GDB will attempt to use
+   the memory tagging infrastructure to validate certain memory accesses.  It
+   will also report memory tag violations alongside a SIGSEGV signal.
+
+   If FALSE, GDB will not use memory tagging in any way, and debugging will work
+   in the standard way.  */
+static bool memtag = true;
+
+static void
+show_memtag (struct ui_file *file, int from_tty,
+     struct cmd_list_element *c,
+     const char *value)
+{
+  fprintf_filtered (file,
+    _("Use of memory tagging infrastructure is \"%s\".\n"),
+    value);
+}
+
 /* Largest offset between a symbolic value and an address, that will be
    printed as `0x1234 <symbol+offset>'.  */
 
@@ -2882,4 +2900,14 @@ Construct a GDB command and then evaluate it.\n\
 Usage: eval \"format string\", ARG1, ARG2, ARG3, ..., ARGN\n\
 Convert the arguments to a string as \"printf\" would, but then\n\
 treat this string as a command line, and evaluate it."));
+
+  add_setshow_boolean_cmd ("memory-tagging", class_support,
+   &memtag, _("\
+Set whether the debugger should use memory tagging infrastructure."), _("\
+Show whether the debugger should use memory tagging infrastructure."), _("\
+If on, gdb will attempt to validate memory tags and will warn the user if\n\
+certain operations have illegal tags."),
+    NULL,
+    show_memtag,
+    &setlist, &showlist);
 }
diff --git a/gdb/remote.c b/gdb/remote.c
index 59075cb09f..3e36c8a233 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -685,6 +685,16 @@ class remote_target : public process_stratum_target
   int remove_exec_catchpoint (int) override;
   enum exec_direction_kind execution_direction () override;
 
+  bool supports_memory_tagging () override;
+
+  /* Read memory tags via the qMemTags packet  */
+  int fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags) override;
+
+  /* Write allocation tags via the QMemTags packet.  */
+  int store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags) override;
+
 public: /* Remote specific methods.  */
 
   void remote_download_command_source (int num, ULONGEST addr,
@@ -14378,6 +14388,32 @@ set_range_stepping (const char *ignore_args, int from_tty,
     }
 }
 
+/* Implement the "supports_memory_tagging" target_ops method.  */
+
+bool
+remote_target::supports_memory_tagging ()
+{
+  return false;
+}
+
+/* Implement the "fetch_memtags" target_ops method.  */
+
+int
+remote_target::fetch_memtags (CORE_ADDR address, size_t len,
+      gdb::byte_vector &tags)
+{
+  return 0;
+}
+
+/* Implement the "store_memtags" target_ops method.  */
+
+int
+remote_target::store_memtags (CORE_ADDR address, size_t len,
+      const gdb::byte_vector &tags)
+{
+  return 0;
+}
+
 void _initialize_remote ();
 void
 _initialize_remote ()
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index c28af09718..2fa945f27a 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -171,6 +171,11 @@ struct dummy_target : public target_ops
   const struct frame_unwind *get_tailcall_unwinder () override;
   void prepare_to_generate_core () override;
   void done_generating_core () override;
+  bool supports_memory_tagging () override;
+  int fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags) override;
+  int store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags) override;
 };
 
 struct debug_target : public target_ops
@@ -340,6 +345,11 @@ struct debug_target : public target_ops
   const struct frame_unwind *get_tailcall_unwinder () override;
   void prepare_to_generate_core () override;
   void done_generating_core () override;
+  bool supports_memory_tagging () override;
+  int fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags) override;
+  int store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags) override;
 };
 
 void
@@ -4363,3 +4373,77 @@ debug_target::done_generating_core ()
   fputs_unfiltered (")\n", gdb_stdlog);
 }
 
+bool
+target_ops::supports_memory_tagging ()
+{
+  return this->beneath ()->supports_memory_tagging ();
+}
+
+int
+target_ops::fetch_memtags (CORE_ADDR address, size_t len,
+   gdb::byte_vector &tags)
+{
+  return this->beneath ()->fetch_memtags (address, len, tags);
+}
+
+int
+target_ops::store_memtags (CORE_ADDR address, size_t len,
+   const gdb::byte_vector &tags)
+{
+  return this->beneath ()->store_memtags (address, len, tags);
+}
+
+bool
+dummy_target::supports_memory_tagging ()
+{
+  return false;
+}
+
+int
+dummy_target::fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags)
+{
+  return 0;
+}
+
+int
+dummy_target::store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags)
+{
+  return 0;
+}
+
+bool
+debug_target::supports_memory_tagging ()
+{
+  bool result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->supports_memory_tagging (...)\n", this->beneath ()->shortname ());
+  result = this->beneath ()->supports_memory_tagging ();
+  fprintf_unfiltered (gdb_stdlog, "<- %s->supports_memory_tagging (", this->beneath ()->shortname ());
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_bool (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
+int
+debug_target::fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags)
+{
+  fprintf_unfiltered (gdb_stdlog, "-> %s->fetch_memtags (...)\n", this->beneath ()->shortname ());
+  int result = this->beneath ()->fetch_memtags (address, len, tags);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->fetch_memtags (", this->beneath ()->shortname ());
+  fputs_unfiltered (")\n", gdb_stdlog);
+  return result;
+}
+
+int
+debug_target::store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags)
+{
+  fprintf_unfiltered (gdb_stdlog, "-> %s->store_memtags (...)\n", this->beneath ()->shortname ());
+  int result = this->beneath ()->store_memtags (address, len, tags);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->store_memtags (", this->beneath ()->shortname ());
+  fputs_unfiltered (")\n", gdb_stdlog);
+  return result;
+}
diff --git a/gdb/target.h b/gdb/target.h
index 4e8d4cccd5..df2c7ca42a 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -1252,6 +1252,22 @@ struct target_ops
     /* Cleanup after generating a core file.  */
     virtual void done_generating_core ()
       TARGET_DEFAULT_IGNORE ();
+
+    /* Returns true if the target supports memory tagging.  */
+    virtual bool supports_memory_tagging ()
+      TARGET_DEFAULT_RETURN (false);
+
+    /* Return the allocated memory tags associated with
+       [ADDRESS, ADDRESS + LEN) in TAGS.  */
+    virtual int fetch_memtags (CORE_ADDR address, size_t len,
+       gdb::byte_vector &tags)
+      TARGET_DEFAULT_IGNORE ();
+
+    /* Write the allocation tags contained in TAGS to the memory range
+       [ADDRESS, ADDRESS + LEN).  */
+    virtual int store_memtags (CORE_ADDR address, size_t len,
+       const gdb::byte_vector &tags)
+      TARGET_DEFAULT_IGNORE ();
   };
 
 /* Deleter for std::unique_ptr.  See comments in
@@ -2308,6 +2324,15 @@ extern gdb::unique_xmalloc_ptr<char> target_fileio_read_stralloc
 #define target_augmented_libraries_svr4_read() \
   (current_top_target ()->augmented_libraries_svr4_read) ()
 
+#define target_supports_memory_tagging() \
+  ((current_top_target ()->supports_memory_tagging) ())
+
+#define target_fetch_memtags(address, len, tags) \
+  (current_top_target ()->fetch_memtags) ((address), (len), (tags))
+
+#define target_store_memtags(address, len, tags) \
+  (current_top_target ()->store_memtags) ((address), (len), (tags))
+
 /* Command logging facility.  */
 
 #define target_log_command(p) \
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 02/23] New gdbarch memory tagging hooks

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
We need some new gdbarch hooks to help us manipulate memory tags without having
to have GDB calls the target methods directly.

This patch adds the following hooks:

gdbarch_memtag_to_string
--
Returns a printable string corresponding to the tag.

gdbarch_tagged_address_p
--
Checks if a particular address is protected with memory tagging.

gdbarch_memtag_mismatch_p
--
Checks if there is a mismatch between the logical tag of a pointer and the
allocation tag.

gdbarch_set_memtags:
--
Sets either the allocation tag or the logical tag for a particular value.

gdbarch_get_memtag:
--
Gets either the allocation tag or the logical tag for a particular value.

gdbarch_granule_size
--
Sets the memory tag granule size, which represents the number of bytes a
particular allocation tag covers. For example, this is 16 bytes for
AArch64's MTE.

I've used struct value as opposed to straight CORE_ADDR so other architectures
can use the infrastructure without having to rely on fixed types.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * arch-utils.c (default_memtag_to_string, +default_tagged_address_p)
        (default_memtag_mismatch_p, default_set_memtags)
        (default_get_memtag): New functions.
        * arch-utils.h (default_memtag_to_string, default_tagged_address_p)
        (default_memtag_mismatch_p, default_set_memtags)
        (default_get_memtag): New prototypes.
        * gdbarch.c: Regenerate.
        * gdbarch.h: Regenerate.
        * gdbarch.sh (memtag_to_string, tagged_address_p, memtag_mismatch_p)
        (set_memtags, get_memtag, memtag_granule_size): New gdbarch hooks.
        (enum memtag_type): New enum.
---
 gdb/arch-utils.c |  50 +++++++++++++++++
 gdb/arch-utils.h |  23 ++++++++
 gdb/gdbarch.c    | 137 +++++++++++++++++++++++++++++++++++++++++++++++
 gdb/gdbarch.h    |  53 ++++++++++++++++++
 gdb/gdbarch.sh   |  36 +++++++++++++
 5 files changed, 299 insertions(+)

diff --git a/gdb/arch-utils.c b/gdb/arch-utils.c
index 13ba50abe6..88e228fed0 100644
--- a/gdb/arch-utils.c
+++ b/gdb/arch-utils.c
@@ -78,6 +78,56 @@ legacy_register_sim_regno (struct gdbarch *gdbarch, int regnum)
     return LEGACY_SIM_REGNO_IGNORE;
 }
 
+
+/* See arch-utils.h */
+
+std::string
+default_memtag_to_string (struct gdbarch *gdbarch, struct value *address,
+  enum memtag_type tag_type)
+{
+  /* By default, assume the address is untagged.  */
+  return "";
+}
+
+/* See arch-utils.h */
+
+bool
+default_tagged_address_p (struct gdbarch *gdbarch, struct value *address)
+{
+  /* By default, assume the address is untagged.  */
+  return false;
+}
+
+/* See arch-utils.h */
+
+bool
+default_memtag_mismatch_p (struct gdbarch *gdbarch, struct value *address)
+{
+  /* By default, assume there is no mismatch.  */
+  return false;
+}
+
+/* See arch-utils.h */
+
+int
+default_set_memtags (struct gdbarch *gdbarch, struct value *address,
+     size_t length, const gdb::byte_vector &tags,
+     enum memtag_type tag_type)
+{
+  /* By default, return 0;  */
+  return 0;
+}
+
+/* See arch-utils.h */
+
+struct value *
+default_get_memtag (struct gdbarch *gdbarch, struct value *address,
+    enum memtag_type tag_type)
+{
+  /* By default, return no tag.  */
+  return NULL;
+}
+
 CORE_ADDR
 generic_skip_trampoline_code (struct frame_info *frame, CORE_ADDR pc)
 {
diff --git a/gdb/arch-utils.h b/gdb/arch-utils.h
index 43d64b1f4f..75ddf676b2 100644
--- a/gdb/arch-utils.h
+++ b/gdb/arch-utils.h
@@ -134,6 +134,29 @@ extern const struct floatformat **
   default_floatformat_for_type (struct gdbarch *gdbarch,
  const char *name, int len);
 
+/* Default implementation of gdbarch_tagged_address_p.  */
+extern std::string default_memtag_to_string (struct gdbarch *gdbarch,
+     struct value *address,
+     enum memtag_type tag_type);
+
+/* Default implementation of gdbarch_tagged_address_p.  */
+bool default_tagged_address_p (struct gdbarch *gdbarch, struct value *address);
+
+/* Default implementation of gdbarch_memtag_mismatch_p.  */
+extern bool default_memtag_mismatch_p (struct gdbarch *gdbarch,
+       struct value *address);
+
+/* Default implementation of gdbarch_set_memtags.  */
+int default_set_memtags (struct gdbarch *gdbarch,
+ struct value *address, size_t length,
+ const gdb::byte_vector &tags,
+ enum memtag_type tag_type);
+
+/* Default implementation of gdbarch_get_memtag.  */
+struct value *default_get_memtag (struct gdbarch *gdbarch,
+  struct value *address,
+  enum memtag_type tag_type);
+
 extern CORE_ADDR generic_skip_trampoline_code (struct frame_info *frame,
        CORE_ADDR pc);
 
diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c
index 21ee840e88..34f9e51aae 100644
--- a/gdb/gdbarch.c
+++ b/gdb/gdbarch.c
@@ -251,6 +251,12 @@ struct gdbarch
   gdbarch_convert_from_func_ptr_addr_ftype *convert_from_func_ptr_addr;
   gdbarch_addr_bits_remove_ftype *addr_bits_remove;
   int significant_addr_bit;
+  gdbarch_memtag_to_string_ftype *memtag_to_string;
+  gdbarch_tagged_address_p_ftype *tagged_address_p;
+  gdbarch_memtag_mismatch_p_ftype *memtag_mismatch_p;
+  gdbarch_set_memtags_ftype *set_memtags;
+  gdbarch_get_memtag_ftype *get_memtag;
+  CORE_ADDR memtag_granule_size;
   gdbarch_software_single_step_ftype *software_single_step;
   gdbarch_single_step_through_delay_ftype *single_step_through_delay;
   gdbarch_print_insn_ftype *print_insn;
@@ -426,6 +432,11 @@ gdbarch_alloc (const struct gdbarch_info *info,
   gdbarch->stabs_argument_has_addr = default_stabs_argument_has_addr;
   gdbarch->convert_from_func_ptr_addr = convert_from_func_ptr_addr_identity;
   gdbarch->addr_bits_remove = core_addr_identity;
+  gdbarch->memtag_to_string = default_memtag_to_string;
+  gdbarch->tagged_address_p = default_tagged_address_p;
+  gdbarch->memtag_mismatch_p = default_memtag_mismatch_p;
+  gdbarch->set_memtags = default_set_memtags;
+  gdbarch->get_memtag = default_get_memtag;
   gdbarch->print_insn = default_print_insn;
   gdbarch->skip_trampoline_code = generic_skip_trampoline_code;
   gdbarch->skip_solib_resolver = generic_skip_solib_resolver;
@@ -615,6 +626,12 @@ verify_gdbarch (struct gdbarch *gdbarch)
   /* Skip verify of convert_from_func_ptr_addr, invalid_p == 0 */
   /* Skip verify of addr_bits_remove, invalid_p == 0 */
   /* Skip verify of significant_addr_bit, invalid_p == 0 */
+  /* Skip verify of memtag_to_string, invalid_p == 0 */
+  /* Skip verify of tagged_address_p, invalid_p == 0 */
+  /* Skip verify of memtag_mismatch_p, invalid_p == 0 */
+  /* Skip verify of set_memtags, invalid_p == 0 */
+  /* Skip verify of get_memtag, invalid_p == 0 */
+  /* Skip verify of memtag_granule_size, invalid_p == 0 */
   /* Skip verify of software_single_step, has predicate.  */
   /* Skip verify of single_step_through_delay, has predicate.  */
   /* Skip verify of print_insn, invalid_p == 0 */
@@ -1053,6 +1070,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   fprintf_unfiltered (file,
                       "gdbarch_dump: get_longjmp_target = <%s>\n",
                       host_address_to_string (gdbarch->get_longjmp_target));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: get_memtag = <%s>\n",
+                      host_address_to_string (gdbarch->get_memtag));
   fprintf_unfiltered (file,
                       "gdbarch_dump: get_pc_address_flags = <%s>\n",
                       host_address_to_string (gdbarch->get_pc_address_flags));
@@ -1188,6 +1208,15 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   fprintf_unfiltered (file,
                       "gdbarch_dump: memory_remove_breakpoint = <%s>\n",
                       host_address_to_string (gdbarch->memory_remove_breakpoint));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: memtag_granule_size = %s\n",
+                      core_addr_to_string_nz (gdbarch->memtag_granule_size));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: memtag_mismatch_p = <%s>\n",
+                      host_address_to_string (gdbarch->memtag_mismatch_p));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: memtag_to_string = <%s>\n",
+                      host_address_to_string (gdbarch->memtag_to_string));
   fprintf_unfiltered (file,
                       "gdbarch_dump: num_pseudo_regs = %s\n",
                       plongest (gdbarch->num_pseudo_regs));
@@ -1332,6 +1361,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   fprintf_unfiltered (file,
                       "gdbarch_dump: sdb_reg_to_regnum = <%s>\n",
                       host_address_to_string (gdbarch->sdb_reg_to_regnum));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: set_memtags = <%s>\n",
+                      host_address_to_string (gdbarch->set_memtags));
   fprintf_unfiltered (file,
                       "gdbarch_dump: short_bit = %s\n",
                       plongest (gdbarch->short_bit));
@@ -1440,6 +1472,9 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file)
   fprintf_unfiltered (file,
                       "gdbarch_dump: syscalls_info = %s\n",
                       host_address_to_string (gdbarch->syscalls_info));
+  fprintf_unfiltered (file,
+                      "gdbarch_dump: tagged_address_p = <%s>\n",
+                      host_address_to_string (gdbarch->tagged_address_p));
   fprintf_unfiltered (file,
                       "gdbarch_dump: target_desc = %s\n",
                       host_address_to_string (gdbarch->target_desc));
@@ -3212,6 +3247,108 @@ set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch,
   gdbarch->significant_addr_bit = significant_addr_bit;
 }
 
+std::string
+gdbarch_memtag_to_string (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->memtag_to_string != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_memtag_to_string called\n");
+  return gdbarch->memtag_to_string (gdbarch, address, tag_type);
+}
+
+void
+set_gdbarch_memtag_to_string (struct gdbarch *gdbarch,
+                              gdbarch_memtag_to_string_ftype memtag_to_string)
+{
+  gdbarch->memtag_to_string = memtag_to_string;
+}
+
+bool
+gdbarch_tagged_address_p (struct gdbarch *gdbarch, struct value *address)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->tagged_address_p != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_tagged_address_p called\n");
+  return gdbarch->tagged_address_p (gdbarch, address);
+}
+
+void
+set_gdbarch_tagged_address_p (struct gdbarch *gdbarch,
+                              gdbarch_tagged_address_p_ftype tagged_address_p)
+{
+  gdbarch->tagged_address_p = tagged_address_p;
+}
+
+bool
+gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch, struct value *address)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->memtag_mismatch_p != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_memtag_mismatch_p called\n");
+  return gdbarch->memtag_mismatch_p (gdbarch, address);
+}
+
+void
+set_gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch,
+                               gdbarch_memtag_mismatch_p_ftype memtag_mismatch_p)
+{
+  gdbarch->memtag_mismatch_p = memtag_mismatch_p;
+}
+
+int
+gdbarch_set_memtags (struct gdbarch *gdbarch, struct value *address, size_t length, const gdb::byte_vector &tags, enum memtag_type tag_type)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->set_memtags != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_set_memtags called\n");
+  return gdbarch->set_memtags (gdbarch, address, length, tags, tag_type);
+}
+
+void
+set_gdbarch_set_memtags (struct gdbarch *gdbarch,
+                         gdbarch_set_memtags_ftype set_memtags)
+{
+  gdbarch->set_memtags = set_memtags;
+}
+
+struct value *
+gdbarch_get_memtag (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type)
+{
+  gdb_assert (gdbarch != NULL);
+  gdb_assert (gdbarch->get_memtag != NULL);
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_get_memtag called\n");
+  return gdbarch->get_memtag (gdbarch, address, tag_type);
+}
+
+void
+set_gdbarch_get_memtag (struct gdbarch *gdbarch,
+                        gdbarch_get_memtag_ftype get_memtag)
+{
+  gdbarch->get_memtag = get_memtag;
+}
+
+CORE_ADDR
+gdbarch_memtag_granule_size (struct gdbarch *gdbarch)
+{
+  gdb_assert (gdbarch != NULL);
+  /* Skip verify of memtag_granule_size, invalid_p == 0 */
+  if (gdbarch_debug >= 2)
+    fprintf_unfiltered (gdb_stdlog, "gdbarch_memtag_granule_size called\n");
+  return gdbarch->memtag_granule_size;
+}
+
+void
+set_gdbarch_memtag_granule_size (struct gdbarch *gdbarch,
+                                 CORE_ADDR memtag_granule_size)
+{
+  gdbarch->memtag_granule_size = memtag_granule_size;
+}
+
 int
 gdbarch_software_single_step_p (struct gdbarch *gdbarch)
 {
diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h
index 0940156aeb..8d6e8c9524 100644
--- a/gdb/gdbarch.h
+++ b/gdb/gdbarch.h
@@ -115,6 +115,18 @@ enum function_call_return_method
   return_method_struct,
 };
 
+enum memtag_type
+{
+  /* Logical tag, the tag that is stored in unused bits of a pointer to a
+     virtual address.  */
+  tag_logical = 0,
+
+  /* Allocation tag, the tag that is associated with every granule of memory in
+     the physical address space.  Allocation tags are used to validate memory
+     accesses via pointers containing logical tags.  */
+  tag_allocation,
+};
+
 
 
 /* The following are pre-initialized by GDBARCH.  */
@@ -705,6 +717,47 @@ extern void set_gdbarch_addr_bits_remove (struct gdbarch *gdbarch, gdbarch_addr_
 extern int gdbarch_significant_addr_bit (struct gdbarch *gdbarch);
 extern void set_gdbarch_significant_addr_bit (struct gdbarch *gdbarch, int significant_addr_bit);
 
+/* Return a string representation of the memory tag TYPE of ADDRESS.
+   If no tag is associated with such an address, return the empty string. */
+
+typedef std::string (gdbarch_memtag_to_string_ftype) (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
+extern std::string gdbarch_memtag_to_string (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
+extern void set_gdbarch_memtag_to_string (struct gdbarch *gdbarch, gdbarch_memtag_to_string_ftype *memtag_to_string);
+
+/* Return true if ADDRESS contains a tag and false otherwise. */
+
+typedef bool (gdbarch_tagged_address_p_ftype) (struct gdbarch *gdbarch, struct value *address);
+extern bool gdbarch_tagged_address_p (struct gdbarch *gdbarch, struct value *address);
+extern void set_gdbarch_tagged_address_p (struct gdbarch *gdbarch, gdbarch_tagged_address_p_ftype *tagged_address_p);
+
+/* Return true if the tag from ADDRESS does not match the memory tag for that
+   particular address.  Return false otherwise. */
+
+typedef bool (gdbarch_memtag_mismatch_p_ftype) (struct gdbarch *gdbarch, struct value *address);
+extern bool gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch, struct value *address);
+extern void set_gdbarch_memtag_mismatch_p (struct gdbarch *gdbarch, gdbarch_memtag_mismatch_p_ftype *memtag_mismatch_p);
+
+/* Set the tags for the address range [ADDRESS, ADDRESS + LENGTH) to TAGS
+   Return 0 if successful and non-zero otherwise. */
+
+typedef int (gdbarch_set_memtags_ftype) (struct gdbarch *gdbarch, struct value *address, size_t length, const gdb::byte_vector &tags, enum memtag_type tag_type);
+extern int gdbarch_set_memtags (struct gdbarch *gdbarch, struct value *address, size_t length, const gdb::byte_vector &tags, enum memtag_type tag_type);
+extern void set_gdbarch_set_memtags (struct gdbarch *gdbarch, gdbarch_set_memtags_ftype *set_memtags);
+
+/* Return the tag portion of ADDRESS, assuming ADDRESS is tagged. */
+
+typedef struct value * (gdbarch_get_memtag_ftype) (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
+extern struct value * gdbarch_get_memtag (struct gdbarch *gdbarch, struct value *address, enum memtag_type tag_type);
+extern void set_gdbarch_get_memtag (struct gdbarch *gdbarch, gdbarch_get_memtag_ftype *get_memtag);
+
+/* memtag_granule_size is the size of the allocation tag granule, for
+   architectures that support memory tagging.
+   This is 0 for architectures that do not support memory tagging.
+   For a non-zero value, this represents the number of bytes of memory per tag. */
+
+extern CORE_ADDR gdbarch_memtag_granule_size (struct gdbarch *gdbarch);
+extern void set_gdbarch_memtag_granule_size (struct gdbarch *gdbarch, CORE_ADDR memtag_granule_size);
+
 /* FIXME/cagney/2001-01-18: This should be split in two.  A target method that
    indicates if the target needs software single step.  An ISA method to
    implement it.
diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh
index 41e7b8d5cc..1df82c45ec 100755
--- a/gdb/gdbarch.sh
+++ b/gdb/gdbarch.sh
@@ -604,6 +604,30 @@ m;CORE_ADDR;addr_bits_remove;CORE_ADDR addr;addr;;core_addr_identity;;0
 # additional data associated with the address.
 v;int;significant_addr_bit;;;;;;0
 
+# Return a string representation of the memory tag TYPE of ADDRESS.
+# If no tag is associated with such an address, return the empty string.
++m;std::string;memtag_to_string;struct value *address, enum memtag_type tag_type;address, tag_type;;default_memtag_to_string;;0
+
+# Return true if ADDRESS contains a tag and false otherwise.
++m;bool;tagged_address_p;struct value *address;address;;default_tagged_address_p;;0
+
+# Return true if the tag from ADDRESS does not match the memory tag for that
+# particular address.  Return false otherwise.
++m;bool;memtag_mismatch_p;struct value *address;address;;default_memtag_mismatch_p;;0
+
+# Set the tags for the address range [ADDRESS, ADDRESS + LENGTH) to TAGS
+# Return 0 if successful and non-zero otherwise.
++m;int;set_memtags;struct value *address, size_t length, const gdb::byte_vector \&tags, enum memtag_type tag_type;address, length, tags, tag_type;;default_set_memtags;;0
+
+# Return the tag portion of ADDRESS, assuming ADDRESS is tagged.
++m;struct value *;get_memtag;struct value *address, enum memtag_type tag_type;address, tag_type;;default_get_memtag;;0
+
+# memtag_granule_size is the size of the allocation tag granule, for
+# architectures that support memory tagging.
+# This is 0 for architectures that do not support memory tagging.
+# For a non-zero value, this represents the number of bytes of memory per tag.
+v;CORE_ADDR;memtag_granule_size;;;;;;0
+
 # FIXME/cagney/2001-01-18: This should be split in two.  A target method that
 # indicates if the target needs software single step.  An ISA method to
 # implement it.
@@ -1351,6 +1375,18 @@ enum function_call_return_method
   return_method_struct,
 };
 
+enum memtag_type
+{
+  /* Logical tag, the tag that is stored in unused bits of a pointer to a
+     virtual address.  */
+  tag_logical = 0,
+
+  /* Allocation tag, the tag that is associated with every granule of memory in
+     the physical address space.  Allocation tags are used to validate memory
+     accesses via pointers containing logical tags.  */
+  tag_allocation,
+};
+
 EOF
 
 # function typedef's
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 03/23] Add GDB-side remote target support for memory tagging

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
This patch adds memory tagging support to GDB's remote side, with
packet string checks, new packet support and an implementation of
the two new tags methods fetch_atags and store_atags.

GDBserver needs to know how to read/write allocation tags, since that is
done via ptrace.  It doesn't need to know about logical tags.

The new packets are:

qMemTags:<address>:<length>
--

Reads tags from the address range [<address>, <address + length>)

QMemTags:<address>:<length>:<uninterpreted tag bytes>
--
Writes the tags represented by the uninterpreted bytes to the address range
[<address>, <address + length>).

The interpretation of what to do with the tag bytes is up to the arch-specific
code.

Note that these new packets consider the case of packet size overflow as an
error, given the common use case is to read/write only a few memory tags at
a time.  Having to use a couple new packets for multi-part transfers wouldn't
make sense for the little use it would have.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * remote.c (PACKET_memory_tagging_feature): New enum.
        (remote_memory_tagging_p): New function.
        (remote_protocol_features): New "memory-tagging" entry.
        (remote_target::remote_query_supported): Handle memory tagging
        support.
        (remote_target::supports_memory_tagging): Implement.
        (create_fmemtags_request, parse_fmemtags_reply)
        (create_smemtags_request): New functions.
        (remote_target::fetch_memtags): Implement.
        (remote_target::store_memtags): Implement.
        (_initialize_remote): Add new "memory-tagging-feature"
        config command.
---
 gdb/remote.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 106 insertions(+), 1 deletion(-)

diff --git a/gdb/remote.c b/gdb/remote.c
index 3e36c8a233..7a3f272995 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2103,6 +2103,10 @@ enum {
   /* Support TARGET_WAITKIND_NO_RESUMED.  */
   PACKET_no_resumed,
 
+  /* Support for memory tagging, allocation tag fetch/store
+     packets and the tag violation stop replies.  */
+  PACKET_memory_tagging_feature,
+
   PACKET_MAX
 };
 
@@ -2244,6 +2248,14 @@ remote_exec_event_p (struct remote_state *rs)
   return packet_support (PACKET_exec_event_feature) == PACKET_ENABLE;
 }
 
+/* Returns true if memory tagging is supported.  */
+
+static bool
+remote_memory_tagging_p (void)
+{
+  return packet_support (PACKET_memory_tagging_feature) == PACKET_ENABLE;
+}
+
 /* Insert fork catchpoint target routine.  If fork events are enabled
    then return success, nothing more to do.  */
 
@@ -5245,6 +5257,8 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "vContSupported", PACKET_DISABLE, remote_supported_packet, PACKET_vContSupported },
   { "QThreadEvents", PACKET_DISABLE, remote_supported_packet, PACKET_QThreadEvents },
   { "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed },
+  { "memory-tagging", PACKET_DISABLE, remote_supported_packet,
+    PACKET_memory_tagging_feature },
 };
 
 static char *remote_support_xml;
@@ -5339,6 +5353,10 @@ remote_target::remote_query_supported ()
       if (packet_set_cmd_state (PACKET_no_resumed) != AUTO_BOOLEAN_FALSE)
  remote_query_supported_append (&q, "no-resumed+");
 
+      if (packet_set_cmd_state (PACKET_memory_tagging_feature)
+  != AUTO_BOOLEAN_FALSE)
+ remote_query_supported_append (&q, "memory-tagging+");
+
       /* Keep this one last to work around a gdbserver <= 7.10 bug in
  the qSupported:xmlRegisters=i386 handling.  */
       if (remote_support_xml != NULL
@@ -14393,7 +14411,63 @@ set_range_stepping (const char *ignore_args, int from_tty,
 bool
 remote_target::supports_memory_tagging ()
 {
-  return false;
+  return remote_memory_tagging_p ();
+}
+
+/* Create the qMemTags packet given ADDRESS and LEN.
+
+   Return 0 if successful, non-zero otherwise.  */
+
+static void
+create_fmemtags_request (gdb::char_vector &packet, CORE_ADDR address,
+ size_t len)
+{
+  int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
+
+  std::string request = string_printf ("qMemTags:%s,%s",
+       phex_nz (address, addr_size),
+       phex_nz (len, sizeof (len)));
+
+  strcpy (packet.data (), request.c_str ());
+}
+
+/* Parse the qMemTags packet reply into TAGS.
+
+   Return 0 if successful, non-zero otherwise.  */
+
+static int
+parse_fmemtags_reply (gdb::char_vector &reply, gdb::byte_vector &tags)
+{
+  if (reply.empty () || reply[0] == 'E' || reply[0] != 'm')
+    return 1;
+
+  /* Copy the tag data.  */
+  tags = hex2bin (reply.data () + 1);
+
+  return 0;
+}
+
+/* Create the QMemTags packet given ADDRESS, LEN and TAGS.
+
+   Return 0 if successful, non-zero otherwise.  */
+
+static void
+create_smemtags_request (gdb::char_vector &packet, CORE_ADDR address,
+ size_t len, const gdb::byte_vector &tags)
+{
+  int addr_size = gdbarch_addr_bit (target_gdbarch ()) / 8;
+
+  /* Put together the main packet, address and length.  */
+  std::string request = string_printf ("QMemTags:%s,%s:",
+       phex_nz (address, addr_size),
+       phex_nz (len, sizeof (len)));
+  request += bin2hex (tags.data (), tags.size ());
+
+  /* Check if we have exceeded the maximum packet size.  */
+  if (packet.size () < request.length ())
+    error (_("Contents too big for packet QMemTags."));
+
+  strcpy (packet.data (), request.c_str ());
 }
 
 /* Implement the "fetch_memtags" target_ops method.  */
@@ -14402,6 +14476,19 @@ int
 remote_target::fetch_memtags (CORE_ADDR address, size_t len,
       gdb::byte_vector &tags)
 {
+  /* Make sure the qMemTags packet is supported.  */
+  if (!remote_memory_tagging_p ())
+    gdb_assert_not_reached ("remote fetch_memtags called with packet disabled");
+
+  struct remote_state *rs = get_remote_state ();
+
+  create_fmemtags_request (rs->buf, address, len);
+
+  putpkt (rs->buf);
+  getpkt (&rs->buf, 0);
+
+  parse_fmemtags_reply (rs->buf, tags);
+
   return 0;
 }
 
@@ -14411,6 +14498,21 @@ int
 remote_target::store_memtags (CORE_ADDR address, size_t len,
       const gdb::byte_vector &tags)
 {
+  /* Make sure the QMemTags packet is supported.  */
+  if (!remote_memory_tagging_p ())
+    gdb_assert_not_reached ("remote store_memtags called with packet disabled");
+
+  struct remote_state *rs = get_remote_state ();
+
+  create_smemtags_request (rs->buf, address, len, tags);
+
+  putpkt (rs->buf);
+  getpkt (&rs->buf, 0);
+
+  /* Verify if the request was successful.  */
+  if (packet_check_result (rs->buf.data ()) != PACKET_OK)
+    return 1;
+
   return 0;
 }
 
@@ -14813,6 +14915,9 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_no_resumed],
  "N stop reply", "no-resumed-stop-reply", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_memory_tagging_feature],
+ "memory-tagging-feature", "memory-tagging-feature", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 04/23] Unit testing for GDB-side remote memory tagging handling

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Include some unit testing for the functions handling the new qMemTags and
QMemTags packets.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * remote: Include gdbsupport/selftest.h.
        (test_memory_tagging_functions): New function.
        (_initialize_remote): Register test_memory_tagging_functions.
---
 gdb/remote.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 89 insertions(+)

diff --git a/gdb/remote.c b/gdb/remote.c
index 7a3f272995..55f3ae0c9d 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -78,6 +78,7 @@
 #include <algorithm>
 #include <unordered_map>
 #include "async-event.h"
+#include "gdbsupport/selftest.h"
 
 /* The remote target.  */
 
@@ -14516,6 +14517,89 @@ remote_target::store_memtags (CORE_ADDR address, size_t len,
   return 0;
 }
 
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+static void
+test_memory_tagging_functions (void)
+{
+  remote_target remote;
+
+  struct packet_config *config
+    = &remote_protocol_packets[PACKET_memory_tagging_feature];
+
+  /* Test memory tagging packet support.  */
+  config->support = PACKET_SUPPORT_UNKNOWN;
+  SELF_CHECK (remote.supports_memory_tagging () == false);
+  config->support = PACKET_DISABLE;
+  SELF_CHECK (remote.supports_memory_tagging () == false);
+  config->support = PACKET_ENABLE;
+  SELF_CHECK (remote.supports_memory_tagging () == true);
+
+  /* Setup testing.  */
+  gdb::char_vector packet;
+  gdb::byte_vector tags, bv;
+  std::string expected, reply;
+  packet.resize (32000);
+
+  /* Test creating a qMemTags request.  */
+
+  expected = "qMemTags:0,0";
+  create_fmemtags_request (packet, 0x0, 0x0);
+  SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+  expected = "qMemTags:deadbeef,10";
+  create_fmemtags_request (packet, 0xdeadbeef, 16);
+  SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+  /* Test parsing a qMemTags reply.  */
+
+  /* Error reply, tags vector unmodified.  */
+  reply = "E00";
+  strcpy (packet.data (), reply.c_str ());
+  tags.resize (0);
+  SELF_CHECK (parse_fmemtags_reply (packet, tags) != 0);
+  SELF_CHECK (tags.size () == 0);
+
+  /* Valid reply, tags vector updated.  */
+  tags.resize (0);
+  bv.resize (0);
+
+  for (int i = 0; i < 5; i++)
+    bv.push_back (i);
+
+  reply = "m" + bin2hex (bv.data (), bv.size ());
+  strcpy (packet.data (), reply.c_str ());
+
+  SELF_CHECK (parse_fmemtags_reply (packet, tags) == 0);
+  SELF_CHECK (tags.size () == 5);
+
+  for (int i = 0; i < 5; i++)
+    SELF_CHECK (tags[i] == i);
+
+  /* Test creating a QMemTags request.  */
+
+  /* Empty tag data.  */
+  tags.resize (0);
+  expected = "QMemTags:0,0:";
+  create_smemtags_request (packet, 0x0, 0x0, tags);
+  SELF_CHECK (memcmp (packet.data (), expected.c_str (),
+      expected.length ()) == 0);
+
+  /* Non-empty tag data.  */
+  tags.resize (0);
+  for (int i = 0; i < 5; i++)
+    tags.push_back (i);
+  expected = "QMemTags:deadbeef,ff:0001020304";
+  create_smemtags_request (packet, 0xdeadbeef, 255, tags);
+  SELF_CHECK (memcmp (packet.data (), expected.c_str (),
+      expected.length ()) == 0);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
 void _initialize_remote ();
 void
 _initialize_remote ()
@@ -15029,4 +15113,9 @@ Specify \"unlimited\" to display all the characters."),
 
   /* Eventually initialize fileio.  See fileio.c */
   initialize_remote_fileio (&remote_set_cmdlist, &remote_show_cmdlist);
+
+#if GDB_SELF_TEST
+  selftests::register_test ("remote_memory_tagging",
+    selftests::test_memory_tagging_functions);
+#endif
 }
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 05/23] GDBserver remote packet support for memory tagging

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
This patch adds the generic remote bits to gdbserver so it can check for memory
tagging support and handle fetch tags and store tags requests.

gdbserver/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * remote-utils.cc (decode_m_packet_params): Renamed from ...
        (decode_m_packet): ... this, which now calls decode_m_packet_params.
        (decode_M_packet): Use decode_m_packet_params.
        * remote-utils.h (decode_m_packet_params): New prototype.
        * server.cc (create_fmemtags_reply, parse_smemtags_request): New
        functions.
        (handle_general_set): Handle the QMemTags packet.
        (parse_fmemtags_request): New function.
        (handle_query): Handle the qMemTags packet and advertise memory
        tagging support.
        (captured_main): Initialize memory tagging flag.
        * server.h (struct client_state): Initialize memory tagging flag.
        * target.cc (process_stratum_target::supports_memory_tagging)
        (process_stratum_target::fetch_memtags)
        (process_stratum_target::store_memtags): New methods.
        * target.h: Include gdbsupport/byte-vector.h.
        (class process_stratum_target) <supports_memory_tagging>
        <fetch_memtags, store_memtags>: New class virtual methods.
        (target_supports_memory_tagging): Define.
---
 gdbserver/remote-utils.cc |  40 ++++++------
 gdbserver/remote-utils.h  |   2 +
 gdbserver/server.cc       | 126 ++++++++++++++++++++++++++++++++++++++
 gdbserver/server.h        |   3 +
 gdbserver/target.cc       |  20 ++++++
 gdbserver/target.h        |  17 +++++
 6 files changed, 186 insertions(+), 22 deletions(-)

diff --git a/gdbserver/remote-utils.cc b/gdbserver/remote-utils.cc
index c26668dc0f..5cf9872510 100644
--- a/gdbserver/remote-utils.cc
+++ b/gdbserver/remote-utils.cc
@@ -1302,10 +1302,14 @@ prepare_resume_reply (char *buf, ptid_t ptid,
     }
 }
 
-void
-decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
+/* Decode ADDR and LEN from a parameter of the form "addr,len<x>", with <x>
+   being an end marker character.  */
+
+char *
+decode_m_packet_params (char *from, CORE_ADDR *mem_addr_ptr,
+ unsigned int *len_ptr, const char end_marker)
 {
-  int i = 0, j = 0;
+  int i = 0;
   char ch;
   *mem_addr_ptr = *len_ptr = 0;
 
@@ -1315,39 +1319,31 @@ decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
       *mem_addr_ptr |= fromhex (ch) & 0x0f;
     }
 
-  for (j = 0; j < 4; j++)
+  while ((ch = from[i++]) != end_marker)
     {
-      if ((ch = from[i++]) == 0)
- break;
       *len_ptr = *len_ptr << 4;
       *len_ptr |= fromhex (ch) & 0x0f;
     }
+
+  return from + i;
+}
+
+void
+decode_m_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr)
+{
+  decode_m_packet_params (from, mem_addr_ptr, len_ptr, '\0');
 }
 
 void
 decode_M_packet (char *from, CORE_ADDR *mem_addr_ptr, unsigned int *len_ptr,
  unsigned char **to_p)
 {
-  int i = 0;
-  char ch;
-  *mem_addr_ptr = *len_ptr = 0;
-
-  while ((ch = from[i++]) != ',')
-    {
-      *mem_addr_ptr = *mem_addr_ptr << 4;
-      *mem_addr_ptr |= fromhex (ch) & 0x0f;
-    }
-
-  while ((ch = from[i++]) != ':')
-    {
-      *len_ptr = *len_ptr << 4;
-      *len_ptr |= fromhex (ch) & 0x0f;
-    }
+  from = decode_m_packet_params (from, mem_addr_ptr, len_ptr, ':');
 
   if (*to_p == NULL)
     *to_p = (unsigned char *) xmalloc (*len_ptr);
 
-  hex2bin (&from[i++], *to_p, *len_ptr);
+  hex2bin (from, *to_p, *len_ptr);
 }
 
 int
diff --git a/gdbserver/remote-utils.h b/gdbserver/remote-utils.h
index 1b31456798..1c82f40ad6 100644
--- a/gdbserver/remote-utils.h
+++ b/gdbserver/remote-utils.h
@@ -45,6 +45,8 @@ void prepare_resume_reply (char *buf, ptid_t ptid,
 
 const char *decode_address_to_semicolon (CORE_ADDR *addrp, const char *start);
 void decode_address (CORE_ADDR *addrp, const char *start, int len);
+char *decode_m_packet_params (char *from, CORE_ADDR *mem_addr_ptr,
+      unsigned int *len_ptr, const char end_marker);
 void decode_m_packet (char *from, CORE_ADDR * mem_addr_ptr,
       unsigned int *len_ptr);
 void decode_M_packet (char *from, CORE_ADDR * mem_addr_ptr,
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index ab5363eb03..2fb2b399b2 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -545,12 +545,59 @@ handle_btrace_conf_general_set (char *own_buf)
   return 1;
 }
 
+/* Create the qMemTags packet reply given TAGS.  */
+
+static int
+create_fmemtags_reply (char *reply, const gdb::byte_vector &tags)
+{
+  /* It is an error to pass a zero-sized tag vector.  */
+  if (tags.size () == 0)
+    return 1;
+
+  std::string packet ("m");
+
+  /* Write the tag data.  */
+  packet += bin2hex (tags.data (), tags.size ());
+
+  /* Check if the reply is too big for the packet to handle.  */
+  if (PBUFSIZ < packet.size ())
+    return 1;
+
+  strcpy (reply, packet.c_str ());
+  return 0;
+}
+
+/* Parse the QMemTags request into ADDR, LEN and TAGS.
+
+   Return 0 if successful, non-zero otherwise.  */
+
+static int
+parse_smemtags_request (char *request, CORE_ADDR *addr, size_t *len,
+ gdb::byte_vector &tags)
+{
+  if (!startswith (request, "QMemTags:"))
+    return 1;
+
+  char *p = request + strlen ("QMemTags:");
+
+  /* Read address and length.  */
+  unsigned int length = 0;
+  p = decode_m_packet_params (p, addr, &length, ':');
+  *len = length;
+
+  /* Read the tag data.  */
+  tags = hex2bin (p);
+
+  return 0;
+}
+
 /* Handle all of the extended 'Q' packets.  */
 
 static void
 handle_general_set (char *own_buf)
 {
   client_state &cs = get_client_state ();
+
   if (startswith (own_buf, "QPassSignals:"))
     {
       int numsigs = (int) GDB_SIGNAL_LAST, i;
@@ -899,6 +946,30 @@ handle_general_set (char *own_buf)
       return;
     }
 
+
+  /* Handle store memory tags packets.  */
+  if (startswith (own_buf, "QMemTags:")
+      && target_supports_memory_tagging ())
+    {
+      gdb::byte_vector tags;
+      CORE_ADDR addr = 0;
+      size_t len = 0;
+
+      require_running_or_return (own_buf);
+
+      int ret = parse_smemtags_request (own_buf, &addr, &len, tags);
+
+      if (ret == 0)
+ ret = the_target->store_memtags (addr, len, tags);
+
+      if (ret)
+ write_enn (own_buf);
+      else
+ write_ok (own_buf);
+
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -2119,6 +2190,26 @@ crc32 (CORE_ADDR base, int len, unsigned int crc)
   return (unsigned long long) crc;
 }
 
+/* Parse the qMemTags packet request into ADDR and LEN.
+
+   Return 0 if successful, non-zero otherwise.  */
+
+static int
+parse_fmemtags_request (char *request, CORE_ADDR *addr, size_t *len)
+{
+  if (!startswith (request, "qMemTags:"))
+    return 1;
+
+  char *p = request + strlen ("qMemTags:");
+
+  /* Read address and length.  */
+  unsigned int length = 0;
+  decode_m_packet (p, addr, &length);
+  *len = length;
+
+  return 0;
+}
+
 /* Add supported btrace packets to BUF.  */
 
 static void
@@ -2337,6 +2428,12 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
      events.  */
   report_no_resumed = true;
  }
+      else if (strcmp (p, "memory-tagging+") == 0)
+ {
+  /* GDB supports memory tagging features.  */
+  if (target_supports_memory_tagging ())
+    cs.memory_tagging_feature = true;
+ }
       else
  {
   /* Move the unknown features all together.  */
@@ -2454,6 +2551,9 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
 
       strcat (own_buf, ";no-resumed+");
 
+      if (target_supports_memory_tagging ())
+ strcat (own_buf, ";memory-tagging+");
+
       /* Reinitialize components as needed for the new connection.  */
       hostio_handle_new_gdb_connection ();
       target_handle_new_gdb_connection ();
@@ -2646,6 +2746,31 @@ handle_query (char *own_buf, int packet_len, int *new_packet_len_p)
   if (target_supports_tracepoints () && handle_tracepoint_query (own_buf))
     return;
 
+  /* Handle fetch memory tags packets.  */
+  if (startswith (own_buf, "qMemTags:")
+      && target_supports_memory_tagging ())
+    {
+      gdb::byte_vector tags;
+      CORE_ADDR addr = 0;
+      size_t len = 0;
+
+      require_running_or_return (own_buf);
+
+      int ret = parse_fmemtags_request (own_buf, &addr, &len);
+
+      if (ret == 0)
+ ret = the_target->fetch_memtags (addr, len, tags);
+
+      if (ret == 0)
+ ret = create_fmemtags_reply (own_buf, tags);
+
+      if (ret)
+ write_enn (own_buf);
+
+      *new_packet_len_p = strlen (own_buf);
+      return;
+    }
+
   /* Otherwise we didn't know what packet it was.  Say we didn't
      understand it.  */
   own_buf[0] = 0;
@@ -3852,6 +3977,7 @@ captured_main (int argc, char *argv[])
       cs.swbreak_feature = 0;
       cs.hwbreak_feature = 0;
       cs.vCont_supported = 0;
+      cs.memory_tagging_feature = false;
 
       remote_open (port);
 
diff --git a/gdbserver/server.h b/gdbserver/server.h
index 22228050a8..3d4a086e18 100644
--- a/gdbserver/server.h
+++ b/gdbserver/server.h
@@ -190,6 +190,9 @@ struct client_state
 
   int current_traceframe = -1;
 
+  /* If true, memory tagging features are supported.  */
+  bool memory_tagging_feature = false;
+
 };
 
 client_state &get_client_state ();
diff --git a/gdbserver/target.cc b/gdbserver/target.cc
index 87f62a0b55..8beeeaee9e 100644
--- a/gdbserver/target.cc
+++ b/gdbserver/target.cc
@@ -463,6 +463,26 @@ process_stratum_target::supports_read_offsets ()
   return false;
 }
 
+bool
+process_stratum_target::supports_memory_tagging ()
+{
+  return false;
+}
+
+int
+process_stratum_target::fetch_memtags (CORE_ADDR address, size_t len,
+       gdb::byte_vector &tags)
+{
+  return 0;
+}
+
+int
+process_stratum_target::store_memtags (CORE_ADDR address, size_t len,
+       const gdb::byte_vector &tags)
+{
+  return 0;
+}
+
 int
 process_stratum_target::read_offsets (CORE_ADDR *text, CORE_ADDR *data)
 {
diff --git a/gdbserver/target.h b/gdbserver/target.h
index 13f069f772..f85c876fa3 100644
--- a/gdbserver/target.h
+++ b/gdbserver/target.h
@@ -30,6 +30,7 @@
 #include "gdbsupport/array-view.h"
 #include "gdbsupport/btrace-common.h"
 #include <vector>
+#include "gdbsupport/byte-vector.h"
 
 struct emit_ops;
 struct buffer;
@@ -499,6 +500,19 @@ class process_stratum_target
 
   /* Return tdesc index for IPA.  */
   virtual int get_ipa_tdesc_idx ();
+
+  /* Returns true if the target supports memory tagging facilities.  */
+  virtual bool supports_memory_tagging ();
+
+  /* Return the allocated memory tags associated with
+     [ADDRESS, ADDRESS + LEN) in TAGS.  */
+  virtual int fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags);
+
+  /* Write the allocation tags contained in TAGS to the memory range
+     [ADDRESS, ADDRESS + LEN).  */
+  virtual int store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags);
 };
 
 extern process_stratum_target *the_target;
@@ -525,6 +539,9 @@ int kill_inferior (process_info *proc);
 #define target_supports_exec_events() \
   the_target->supports_exec_events ()
 
+#define target_supports_memory_tagging() \
+  the_target->supports_memory_tagging ()
+
 #define target_handle_new_gdb_connection() \
   the_target->handle_new_gdb_connection ()
 
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 06/23] Unit tests for gdbserver memory tagging remote packets

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Add some unit testing to exercise the functions handling the qMemTags and
QMemTags packets as well as feature support.

gdbserver/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * server.cc (test_memory_tagging_functions): New function.
        (captured_main): Register test_memory_tagging_functions.
---
 gdbserver/server.cc | 88 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 2fb2b399b2..e0a7b55abb 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -3680,6 +3680,91 @@ detach_or_kill_for_exit_cleanup ()
     }
 }
 
+#if GDB_SELF_TEST
+
+namespace selftests {
+
+static void
+test_memory_tagging_functions (void)
+{
+  /* Setup testing.  */
+  gdb::char_vector packet;
+  gdb::byte_vector tags, bv;
+  std::string expected;
+  packet.resize (32000);
+  CORE_ADDR addr;
+  size_t len;
+
+  /* Test parsing a qMemTags request.  */
+
+  /* Invalid request, addr and len unchanged.  */
+  addr = 0xff;
+  len = 255;
+  strcpy (packet.data (), "qMemTags_wrong:0,0");
+  SELF_CHECK (parse_fmemtags_request (packet.data (), &addr, &len) != 0);
+  SELF_CHECK (addr == 0xff && len == 255);
+
+  /* Valid request, addr and len updated.  */
+  addr = 0xff;
+  len = 255;
+  strcpy (packet.data (), "qMemTags:0,0");
+  SELF_CHECK (parse_fmemtags_request (packet.data (), &addr, &len) == 0);
+  SELF_CHECK (addr == 0 && len == 0);
+
+  /* Valid request, addr and len updated.  */
+  addr = 0;
+  len = 0;
+  strcpy (packet.data (), "qMemTags:deadbeef,ff");
+  SELF_CHECK (parse_fmemtags_request (packet.data (), &addr, &len) == 0);
+  SELF_CHECK (addr == 0xdeadbeef && len == 255);
+
+  /* Test creating a qMemTags reply.  */
+
+  /* Non-empty tag data.  */
+  bv.resize (0);
+
+  for (int i = 0; i < 5; i++)
+    bv.push_back (i);
+
+  expected = "m0001020304";
+  SELF_CHECK (create_fmemtags_reply (packet.data (), bv) == 0);
+  SELF_CHECK (strcmp (packet.data (), expected.c_str ()) == 0);
+
+  /* Empty tag data (error).  */
+  bv.clear ();
+  SELF_CHECK (create_fmemtags_reply (packet.data (), bv) != 0);
+
+  /* Test parsing a QMemTags request.  */
+
+  /* Invalid request and non-empty tag data: addr, len and tags unchanged.  */
+  addr = 0xff;
+  len = 255;
+  tags.resize (5);
+  strcpy (packet.data (), "QMemTags_wrong:0,0:");
+  SELF_CHECK (parse_smemtags_request (packet.data (), &addr, &len, tags) != 0);
+  SELF_CHECK (addr == 0xff && len == 255 && tags.size () == 5);
+
+  /* Valid request and empty tag data: addr, len and tags updated.  */
+  addr = 0xff;
+  len = 255;
+  tags.resize (5);
+  strcpy (packet.data (), "QMemTags:0,0:");
+  SELF_CHECK (parse_smemtags_request (packet.data (), &addr, &len, tags) == 0);
+  SELF_CHECK (addr == 0 && len == 0 && tags.size () == 0);
+
+  /* Valid request and non-empty tag data: addr, len and tags updated.  */
+  addr = 0;
+  len = 0;
+  tags.resize (0);
+  strcpy (packet.data (),
+  "QMemTags:deadbeef,ff:0001020304");
+  SELF_CHECK (parse_smemtags_request (packet.data (), &addr, &len, tags) == 0);
+  SELF_CHECK (addr == 0xdeadbeef && len == 255 && tags.size () == 5);
+}
+
+} // namespace selftests
+#endif /* GDB_SELF_TEST */
+
 /* Main function.  This is called by the real "main" function,
    wrapped in a TRY_CATCH that handles any uncaught exceptions.  */
 
@@ -3697,6 +3782,9 @@ captured_main (int argc, char *argv[])
   bool selftest = false;
 #if GDB_SELF_TEST
   const char *selftest_filter = NULL;
+
+  selftests::register_test ("remote_memory_tagging",
+    selftests::test_memory_tagging_functions);
 #endif
 
   current_directory = getcwd (NULL, 0);
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 07/23] Documentation for memory tagging remote packets

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Document the remote packet changes to support memory tagging.

gdb/doc/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * gdb.texinfo (General Query Packets): Document qMemTags and
        QMemTags.
        Document the "memory-tagging" feature.
---
 gdb/doc/gdb.texinfo | 84 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 84 insertions(+)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index a002084d5b..bc610e44cd 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -40654,6 +40654,77 @@ is a sequence of thread IDs, @var{threadid} (eight hex
 digits), from the target.  See @code{remote.c:parse_threadlist_response()}.
 @end table
 
+@item qMemTags:@var{start address}:@var{length}
+@cindex fetch memory tags
+@cindex @samp{qMemTags} packet
+Fetch memory tags in the address range @r{[}@var{start address},
+@var{start address} + @var{length}@r{)}.  The target is responsible for
+calculating how many tags will be returned, as this is architecture-specific.
+
+@var{start address} is the starting address of the memory range.
+
+@var{length} is the length, in bytes, of the memory range.
+
+Reply:
+@table @samp
+@item @var{mXX}@dots{}
+Hex encoded sequence of uninterpreted bytes representing the tags found in
+the request memory range.
+
+@item E @var{nn}
+An error occured.  This means that fetching of memory tags failed for some
+reason.
+
+@item @w{}
+An empty reply indicates that @samp{qMemTags} is not supported by the stub,
+although this should not happen given @value{GDBN} will only send this packet
+if the stub has advertised support for memory tagging via @samp{qSupported}.
+@end table
+
+@item QMemTags:@var{start address}:@var{length}:@var{tag bytes}
+@cindex store memory tags
+@cindex @samp{QMemTags} packet
+Store memory tags to the address range @r{[}@var{start address},
+@var{start address} + @var{length}@r{)}.  The target is responsible for
+interpreting the tag bytes and modifying the memory tag granules
+accordingly, given this is architecture-specific.
+
+The interpretation of how many tags should be written to how many memory tag
+granules is also architecture-specific.  The behavior is
+implementation-specific, but the following is suggested.
+
+If the number of memory tags, @var{N}, is greater than or equal to the number
+of memory tag granules, @var{G}, only @var{G} tags will be stored.
+
+If @var{N} is less than @var{G}, the behavior is that of a fill operation,
+and the tag bytes will be used as a pattern that will get repeated until
+@var{G} tags are stored.
+
+@var{start address} is the starting address of the memory range.  The address
+does not have any restriction on alignment or size.
+
+@var{length} is the length, in bytes, of the memory range.
+
+@var{tag bytes} is a sequence of hex encoded uninterpreted bytes which will be
+interpreted by the target.  Each pair of hex digits is interpreted as a
+single byte.
+
+Reply:
+@table @samp
+@item OK
+The request was successful and the memory tag granules were modified
+accordingly.
+
+@item E @var{nn}
+An error occured.  This means that modifying the memory tag granules failed
+for some reason.
+
+@item @w{}
+An empty reply indicates that @samp{QMemTags} is not supported by the stub,
+although this should not happen given @value{GDBN} will only send this packet
+if the stub has advertised support for memory tagging via @samp{qSupported}.
+@end table
+
 @item qOffsets
 @cindex section offsets, remote request
 @cindex @samp{qOffsets} packet
@@ -41321,6 +41392,11 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab No
 
+@item @samp{memory-tagging}
+@tab No
+@tab @samp{-}
+@tab No
+
 @end multitable
 
 These are the currently defined stub features, in more detail:
@@ -41535,6 +41611,14 @@ The remote stub understands the @samp{QThreadEvents} packet.
 @item no-resumed
 The remote stub reports the @samp{N} stop reply.
 
+@item memory-tagging
+The remote stub supports and implements the required memory tagging
+functionality and understands the @samp{qMemTags} and @samp{QMemTags} packets.
+
+For AArch64 GNU/Linux systems, this feature also requires access to the smaps
+file in the proc filesystem so memory mapping page flags can be inspected.  This
+is done via the @samp{vFile} requests.
+
 @end table
 
 @item qSymbol::
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 08/23] AArch64: Add MTE CPU feature check support

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
This patch is a preparation for the next patches implementing MTE. It just adds
a HWCAP2 constant for MTE, creates a new generic arch/aarch64-mte-linux.h file
and includes that file in the source files that will use it.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * Makefile.in (HFILES_NO_SRCDIR): Add arch/aarch64-mte-linux.h.
        * aarch64-linux-nat.c: Include arch/aarch64-mte-linux.h.
        * aarch64-linux-tdep.c: Likewise
        * arch/aarch64-mte-linux.h: New file.

gdbserver/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * linux-aarch64-low.cc: Include arch/aarch64-mte-linux.h.
---
 gdb/Makefile.in                |  1 +
 gdb/aarch64-linux-nat.c        |  2 ++
 gdb/aarch64-linux-tdep.c       |  2 ++
 gdb/arch/aarch64-mte-linux.h   | 28 ++++++++++++++++++++++++++++
 gdbserver/linux-aarch64-low.cc |  1 +
 5 files changed, 34 insertions(+)
 create mode 100644 gdb/arch/aarch64-mte-linux.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 9d48445739..dcd444136a 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1446,6 +1446,7 @@ HFILES_NO_SRCDIR = \
  arch/aarch32.h \
  arch/aarch64.h \
  arch/aarch64-insn.h \
+ arch/aarch64-mte-linux.h \
  arch/arc.h \
  arch/arm.h \
  arch/i386.h \
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 77d5863a56..260f47558b 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -50,6 +50,8 @@
 #include "gdb_proc_service.h"
 #include "arch-utils.h"
 
+#include "arch/aarch64-mte-linux.h"
+
 #ifndef TRAP_HWBKPT
 #define TRAP_HWBKPT 0x0004
 #endif
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 34ba0d87ba..bfb4fc51e1 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -44,6 +44,8 @@
 #include "record-full.h"
 #include "linux-record.h"
 
+#include "arch/aarch64-mte-linux.h"
+
 /* Signal frame handling.
 
       +------------+  ^
diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h
new file mode 100644
index 0000000000..c6a91c2db4
--- /dev/null
+++ b/gdb/arch/aarch64-mte-linux.h
@@ -0,0 +1,28 @@
+/* Common Linux target-dependent definitions for AArch64 MTE
+
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef ARCH_AARCH64_LINUX_H
+#define ARCH_AARCH64_LINUX_H
+
+/* Feature check for Memory Tagging Extension.  */
+#ifndef HWCAP2_MTE
+#define HWCAP2_MTE  (1 << 18)
+#endif
+
+#endif /* ARCH_AARCH64_LINUX_H */
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 08208ae4f4..7512aac54b 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -40,6 +40,7 @@
 
 #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-sve-linux-ptrace.h"
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 09/23] AArch64: Add target description/feature for MTE registers

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
This patch adds a target description and feature "mte" for aarch64.

It includes one new register, tag_ctl, that can be used to configure the
tag generation rules and sync/async modes.  It is 64-bit in size.

The patch also adjusts the code that creates the target descriptions at
runtime based on CPU feature checks.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * aarch64-linux-nat.c
        (aarch64_linux_nat_target::read_description): Take MTE flag into
        account.
        Slight refactor to hwcap flag checking.
        * aarch64-linux-tdep.c
        (aarch64_linux_core_read_description): Likewise.
        * aarch64-tdep.c (tdesc_aarch64_list): Add one more dimension for
        MTE.
        (aarch64_read_description): Add mte_p parameter and update to use it.
        Update the documentation.
        (aarch64_gdbarch_init): Update call to aarch64_read_description.
        * aarch64-tdep.h (aarch64_read_description): Add mte_p parameter.
        * arch/aarch64.c: Include ../features/aarch64-mte.c.
        (aarch64_create_target_description): Add mte_p parameter and update
        the code to use it.
        * arch/aarch64.h (aarch64_create_target_description): Add mte_p
        parameter.
        * features/Makefile (FEATURE_XMLFILES): Add aarch64-mte.xml.
        * features/aarch64-mte.c: New file, generated.
        * features/aarch64-mte.xml: New file.

gdbserver/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * linux-aarch64-ipa.cc (get_ipa_tdesc): Update call to
        aarch64_linux_read_description.
        (initialize_low_tracepoint): Likewise.
        * linux-aarch64-low.cc (aarch64_target::low_arch_setup): Take MTE flag
        into account.
        * linux-aarch64-tdesc.cc (tdesc_aarch64_list): Add one more dimension
        for MTE.
        (aarch64_linux_read_description): Add mte_p parameter and update to
        use it.
        * linux-aarch64-tdesc.h (aarch64_linux_read_description): Add mte_p
        parameter.
---
 gdb/aarch64-linux-nat.c          |  7 +++++--
 gdb/aarch64-linux-tdep.c         |  5 ++++-
 gdb/aarch64-tdep.c               | 16 +++++++++-------
 gdb/aarch64-tdep.h               |  3 ++-
 gdb/arch/aarch64.c               |  7 ++++++-
 gdb/arch/aarch64.h               |  7 +++++--
 gdb/features/Makefile            |  1 +
 gdb/features/aarch64-mte.c       | 14 ++++++++++++++
 gdb/features/aarch64-mte.xml     | 11 +++++++++++
 gdbserver/linux-aarch64-ipa.cc   |  8 ++++----
 gdbserver/linux-aarch64-low.cc   |  6 +++++-
 gdbserver/linux-aarch64-tdesc.cc | 10 +++++-----
 gdbserver/linux-aarch64-tdesc.h  |  3 ++-
 13 files changed, 73 insertions(+), 25 deletions(-)
 create mode 100644 gdb/features/aarch64-mte.c
 create mode 100644 gdb/features/aarch64-mte.xml

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 260f47558b..1392ec440c 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -653,9 +653,12 @@ aarch64_linux_nat_target::read_description ()
     return aarch32_read_description ();
 
   CORE_ADDR hwcap = linux_get_hwcap (this);
+  CORE_ADDR hwcap2 = linux_get_hwcap2 (this);
 
-  return aarch64_read_description (aarch64_sve_get_vq (tid),
-   hwcap & AARCH64_HWCAP_PACA);
+  bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
+  bool mte_p = hwcap2 & HWCAP2_MTE;
+
+  return aarch64_read_description (aarch64_sve_get_vq (tid), pauth_p, mte_p);
 }
 
 /* Convert a native/host siginfo object, into/from the siginfo in the
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index bfb4fc51e1..53f9d9f6d2 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -654,9 +654,12 @@ aarch64_linux_core_read_description (struct gdbarch *gdbarch,
      struct target_ops *target, bfd *abfd)
 {
   CORE_ADDR hwcap = linux_get_hwcap (target);
+  CORE_ADDR hwcap2 = linux_get_hwcap2 (target);
 
+  bool pauth_p = hwcap & AARCH64_HWCAP_PACA;
+  bool mte_p = hwcap2 & HWCAP2_MTE;
   return aarch64_read_description (aarch64_linux_core_read_vq (gdbarch, abfd),
-   hwcap & AARCH64_HWCAP_PACA);
+   pauth_p, mte_p);
 }
 
 /* Implementation of `gdbarch_stap_is_single_operand', as defined in
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 5e7d0d0b86..536f6f3dc9 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -62,7 +62,7 @@
 #define HA_MAX_NUM_FLDS 4
 
 /* All possible aarch64 target descriptors.  */
-struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/];
+struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/][2 /* mte */];
 
 /* The standard register names, and all the valid aliases for them.  */
 static const struct
@@ -3139,21 +3139,23 @@ aarch64_displaced_step_hw_singlestep (struct gdbarch *gdbarch,
 
 /* Get the correct target description for the given VQ value.
    If VQ is zero then it is assumed SVE is not supported.
-   (It is not possible to set VQ to zero on an SVE system).  */
+   (It is not possible to set VQ to zero on an SVE system).
+
+   MTE_P indicates the presence of the Memory Tagging Extension feature. */
 
 const target_desc *
-aarch64_read_description (uint64_t vq, bool pauth_p)
+aarch64_read_description (uint64_t vq, bool pauth_p, bool mte_p)
 {
   if (vq > AARCH64_MAX_SVE_VQ)
     error (_("VQ is %" PRIu64 ", maximum supported value is %d"), vq,
    AARCH64_MAX_SVE_VQ);
 
-  struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p];
+  struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p][mte_p];
 
   if (tdesc == NULL)
     {
-      tdesc = aarch64_create_target_description (vq, pauth_p);
-      tdesc_aarch64_list[vq][pauth_p] = tdesc;
+      tdesc = aarch64_create_target_description (vq, pauth_p, mte_p);
+      tdesc_aarch64_list[vq][pauth_p][mte_p] = tdesc;
     }
 
   return tdesc;
@@ -3253,7 +3255,7 @@ aarch64_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
      value.  */
   const struct target_desc *tdesc = info.target_desc;
   if (!tdesc_has_registers (tdesc) || vq != aarch64_get_tdesc_vq (tdesc))
-    tdesc = aarch64_read_description (vq, false);
+    tdesc = aarch64_read_description (vq, false, false);
   gdb_assert (tdesc);
 
   feature_core = tdesc_find_feature (tdesc,"org.gnu.gdb.aarch64.core");
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index f6ebabeaeb..4f8b19ad2f 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -102,7 +102,8 @@ struct gdbarch_tdep
   }
 };
 
-const target_desc *aarch64_read_description (uint64_t vq, bool pauth_p);
+const target_desc *aarch64_read_description (uint64_t vq, bool pauth_p,
+     bool mte_p);
 
 extern int aarch64_process_record (struct gdbarch *gdbarch,
                                struct regcache *regcache, CORE_ADDR addr);
diff --git a/gdb/arch/aarch64.c b/gdb/arch/aarch64.c
index f611543498..9104caf767 100644
--- a/gdb/arch/aarch64.c
+++ b/gdb/arch/aarch64.c
@@ -23,11 +23,12 @@
 #include "../features/aarch64-fpu.c"
 #include "../features/aarch64-sve.c"
 #include "../features/aarch64-pauth.c"
+#include "../features/aarch64-mte.c"
 
 /* See arch/aarch64.h.  */
 
 target_desc *
-aarch64_create_target_description (uint64_t vq, bool pauth_p)
+aarch64_create_target_description (uint64_t vq, bool pauth_p, bool mte_p)
 {
   target_desc *tdesc = allocate_target_description ();
 
@@ -47,5 +48,9 @@ aarch64_create_target_description (uint64_t vq, bool pauth_p)
   if (pauth_p)
     regnum = create_feature_aarch64_pauth (tdesc, regnum);
 
+  /* Memory tagging extension registers.  */
+  if (mte_p)
+    regnum = create_feature_aarch64_mte (tdesc, regnum);
+
   return tdesc;
 }
diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h
index 857bb22b03..6b1882afcc 100644
--- a/gdb/arch/aarch64.h
+++ b/gdb/arch/aarch64.h
@@ -25,9 +25,12 @@
 /* Create the aarch64 target description.  A non zero VQ value indicates both
    the presence of SVE and the Vector Quotient - the number of 128bit chunks in
    an SVE Z register.  HAS_PAUTH_P indicates the presence of the PAUTH
-   feature.  */
+   feature.
 
-target_desc *aarch64_create_target_description (uint64_t vq, bool has_pauth_p);
+   MTE_P indicates the presence of the Memory Tagging Extension feature.  */
+
+target_desc *aarch64_create_target_description (uint64_t vq, bool has_pauth_p,
+ bool mte_p);
 
 /* Register numbers of various important registers.
    Note that on SVE, the Z registers reuse the V register numbers and the V
diff --git a/gdb/features/Makefile b/gdb/features/Makefile
index d0af9a47b4..a746cccca9 100644
--- a/gdb/features/Makefile
+++ b/gdb/features/Makefile
@@ -201,6 +201,7 @@ $(outdir)/%.dat: %.xml number-regs.xsl sort-regs.xsl gdbserver-regs.xsl
 FEATURE_XMLFILES = aarch64-core.xml \
  aarch64-fpu.xml \
  aarch64-pauth.xml \
+ aarch64-mte.xml \
  arc/core-v2.xml \
  arc/aux-v2.xml \
  arc/core-arcompact.xml \
diff --git a/gdb/features/aarch64-mte.c b/gdb/features/aarch64-mte.c
new file mode 100644
index 0000000000..883b19cd15
--- /dev/null
+++ b/gdb/features/aarch64-mte.c
@@ -0,0 +1,14 @@
+/* THIS FILE IS GENERATED.  -*- buffer-read-only: t -*- vi:set ro:
+  Original: aarch64-mte.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_aarch64_mte (struct target_desc *result, long regnum)
+{
+  struct tdesc_feature *feature;
+
+  feature = tdesc_create_feature (result, "org.gnu.gdb.aarch64.mte");
+  tdesc_create_reg (feature, "tag_ctl", regnum++, 0, "system", 64, "uint64");
+  return regnum;
+}
diff --git a/gdb/features/aarch64-mte.xml b/gdb/features/aarch64-mte.xml
new file mode 100644
index 0000000000..85455b13b2
--- /dev/null
+++ b/gdb/features/aarch64-mte.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<!-- Copyright (C) 2020 Free Software Foundation, Inc.
+
+     Copying and distribution of this file, with or without modification,
+     are permitted in any medium without royalty provided the copyright
+     notice and this notice are preserved.  -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.aarch64.mte">
+  <reg name="tag_ctl" bitsize="64" type="uint64" group="system" save-restore="no"/>
+</feature>
diff --git a/gdbserver/linux-aarch64-ipa.cc b/gdbserver/linux-aarch64-ipa.cc
index 694dfd77df..1a47bf95d7 100644
--- a/gdbserver/linux-aarch64-ipa.cc
+++ b/gdbserver/linux-aarch64-ipa.cc
@@ -147,12 +147,12 @@ get_raw_reg (const unsigned char *raw_regs, int regnum)
 
 /* Return target_desc to use for IPA, given the tdesc index passed by
    gdbserver.  Index is ignored, since we have only one tdesc
-   at the moment.  SVE and pauth not yet supported.  */
+   at the moment.  SVE, pauth and MTE not yet supported.  */
 
 const struct target_desc *
 get_ipa_tdesc (int idx)
 {
-  return aarch64_linux_read_description (0, false);
+  return aarch64_linux_read_description (0, false, false);
 }
 
 /* Allocate buffer for the jump pads.  The branch instruction has a reach
@@ -204,6 +204,6 @@ alloc_jump_pad_buffer (size_t size)
 void
 initialize_low_tracepoint (void)
 {
-  /* SVE and pauth not yet supported.  */
-  aarch64_linux_read_description (0, false);
+  /* SVE, pauth and MTE not yet supported.  */
+  aarch64_linux_read_description (0, false, false);
 }
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 7512aac54b..60d60a4d5c 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -642,9 +642,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 ();
diff --git a/gdbserver/linux-aarch64-tdesc.cc b/gdbserver/linux-aarch64-tdesc.cc
index 897fbb43bd..3c5233d1fb 100644
--- a/gdbserver/linux-aarch64-tdesc.cc
+++ b/gdbserver/linux-aarch64-tdesc.cc
@@ -27,22 +27,22 @@
 #include <inttypes.h>
 
 /* All possible aarch64 target descriptors.  */
-struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/];
+struct target_desc *tdesc_aarch64_list[AARCH64_MAX_SVE_VQ + 1][2/*pauth*/][2 /* mte */];
 
 /* Create the aarch64 target description.  */
 
 const target_desc *
-aarch64_linux_read_description (uint64_t vq, bool pauth_p)
+aarch64_linux_read_description (uint64_t vq, bool pauth_p, bool mte_p)
 {
   if (vq > AARCH64_MAX_SVE_VQ)
     error (_("VQ is %" PRIu64 ", maximum supported value is %d"), vq,
    AARCH64_MAX_SVE_VQ);
 
-  struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p];
+  struct target_desc *tdesc = tdesc_aarch64_list[vq][pauth_p][mte_p];
 
   if (tdesc == NULL)
     {
-      tdesc = aarch64_create_target_description (vq, pauth_p);
+      tdesc = aarch64_create_target_description (vq, pauth_p, mte_p);
 
       static const char *expedite_regs_aarch64[] = { "x29", "sp", "pc", NULL };
       static const char *expedite_regs_aarch64_sve[] = { "x29", "sp", "pc",
@@ -53,7 +53,7 @@ aarch64_linux_read_description (uint64_t vq, bool pauth_p)
       else
  init_target_desc (tdesc, expedite_regs_aarch64_sve);
 
-      tdesc_aarch64_list[vq][pauth_p] = tdesc;
+      tdesc_aarch64_list[vq][pauth_p][mte_p] = tdesc;
     }
 
   return tdesc;
diff --git a/gdbserver/linux-aarch64-tdesc.h b/gdbserver/linux-aarch64-tdesc.h
index 0165e633d4..09d42970a9 100644
--- a/gdbserver/linux-aarch64-tdesc.h
+++ b/gdbserver/linux-aarch64-tdesc.h
@@ -20,6 +20,7 @@
 #ifndef GDBSERVER_LINUX_AARCH64_TDESC_H
 #define GDBSERVER_LINUX_AARCH64_TDESC_H
 
-const target_desc * aarch64_linux_read_description (uint64_t vq, bool pauth_p);
+const target_desc * aarch64_linux_read_description (uint64_t vq, bool pauth_p,
+    bool mte_p);
 
 #endif /* GDBSERVER_LINUX_AARCH64_TDESC_H */
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 10/23] AArch64: Add MTE register set support for GDB and gdbserver

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
AArch64 MTE support in the Linux kernel exposes a new register
through ptrace.  This patch adds the required code to support it.

include/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

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

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * 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:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * 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/aarch64-linux-nat.c        | 70 ++++++++++++++++++++++++++++++++++
 gdb/aarch64-tdep.c             | 24 ++++++++++++
 gdb/aarch64-tdep.h             |  9 +++++
 gdb/arch/aarch64-mte-linux.h   |  3 ++
 gdbserver/linux-aarch64-low.cc | 29 ++++++++++++++
 include/elf/common.h           |  3 ++
 6 files changed, 138 insertions(+)

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 1392ec440c..dea34da669 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -461,6 +461,60 @@ 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 = regcache->ptid ().lwp ();
+  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 = regcache->ptid ().lwp ();
+  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 +533,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 +550,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 +572,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 +582,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
diff --git a/gdb/aarch64-tdep.c b/gdb/aarch64-tdep.c
index 536f6f3dc9..ed6c3d6af0 100644
--- a/gdb/aarch64-tdep.c
+++ b/gdb/aarch64-tdep.c
@@ -176,6 +176,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
 {
@@ -3225,6 +3231,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
@@ -3262,6 +3269,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;
@@ -3332,6 +3341,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,
+    first_mte_regnum + i,
+    aarch64_mte_register_names[i]);
+
+      num_regs += i;
+    }
+
   if (!valid_p)
     {
       tdesc_data_cleanup (tdesc_data);
@@ -3352,6 +3375,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);
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index 4f8b19ad2f..eb01e31df1 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -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,
diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h
index c6a91c2db4..4124e80543 100644
--- a/gdb/arch/aarch64-mte-linux.h
+++ b/gdb/arch/aarch64-mte-linux.h
@@ -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 */
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 60d60a4d5c..22485d9466 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -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 ()
 {
@@ -684,6 +707,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
 };
 
@@ -713,6 +739,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
 };
 
diff --git a/include/elf/common.h b/include/elf/common.h
index 2138868c9b..5950a63214 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -653,6 +653,9 @@
  /*   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_SIGINFO 0x53494749 /* Fields of siginfo_t.  */
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 11/23] AArch64: Add MTE ptrace requests

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
This patch adds the required ptrace request definitions into a new include
file that will be used by the next patches.

They are PTRACE_PEEKMTETAGS and PTRACE_POKEMTETAGS.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * Makefile.in (HFILES_NO_SRCDIR): Add nat/aarch64-mte-linux-ptrace.h.
        * nat/aarch64-mte-linux-ptrace.h: New file.
---
 gdb/Makefile.in                    |  1 +
 gdb/nat/aarch64-mte-linux-ptrace.h | 33 ++++++++++++++++++++++++++++++
 2 files changed, 34 insertions(+)
 create mode 100644 gdb/nat/aarch64-mte-linux-ptrace.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index dcd444136a..ac4b12c928 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1483,6 +1483,7 @@ HFILES_NO_SRCDIR = \
  nat/aarch64-linux.h \
  nat/aarch64-linux-hw-point.h \
  nat/aarch64-sve-linux-ptrace.h \
+ nat/aarch64-mte-linux-ptrace.h \
  nat/amd64-linux-siginfo.h \
  nat/gdb_ptrace.h \
  nat/gdb_thread_db.h \
diff --git a/gdb/nat/aarch64-mte-linux-ptrace.h b/gdb/nat/aarch64-mte-linux-ptrace.h
new file mode 100644
index 0000000000..099b6440ca
--- /dev/null
+++ b/gdb/nat/aarch64-mte-linux-ptrace.h
@@ -0,0 +1,33 @@
+/* Common native Linux definitions for AArch64 MTE.
+
+   Copyright (C) 2018-2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_AARCH64_MTE_LINUX_PTRACE_H
+#define NAT_AARCH64_MTE_LINUX_PTRACE_H
+
+/* MTE allocation tag access */
+
+#ifndef PTRACE_PEEKMTETAGS
+#define PTRACE_PEEKMTETAGS  33
+#endif
+
+#ifndef PTRACE_POKEMTETAGS
+#define PTRACE_POKEMTETAGS  34
+#endif
+
+#endif /* NAT_AARCH64_MTE_LINUX_PTRACE_H */
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 12/23] AArch64: Implement memory tagging target methods for AArch64

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
The patch implements the memory tagging target hooks for AArch64, so we
can handle MTE.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * Makefile.in (ALL_64_TARGET_OBS): Add arch/aarch64-mte-linux.o.
        (HFILES_NO_SRCDIR): Add arch/aarch64-mte-linux.h and
        nat/aarch64-mte-linux-ptrace.h.
        * aarch64-linux-nat.c: Include nat/aarch64-mte-linux-ptrace.h.
        (aarch64_linux_nat_target) <supports_memory_tagging>: New method
        override.
        <fetch_memtags>: New method override.
        <store_memtags>: New method override.
        (aarch64_linux_nat_target::supports_memory_tagging): New method.
        (aarch64_linux_nat_target::fetch_memtags): New method.
        (aarch64_linux_nat_target::store_memtags): New method.
        * arch/aarch64-mte-linux.c: New file.
        * arch/aarch64-mte-linux.h: Include gdbsupport/common-defs.h.
        (MTE_GRANULE_SIZE): Define.
        (get_tag_granules): New prototype.
        * configure.nat (NATDEPFILES): Add nat/aarch64-mte-linux-ptrace.o.
        * configure.tgt (aarch64*-*-linux*): Add arch/aarch64-mte-linux.o.
        * nat/aarch64-mte-linux-ptrace.c: New file.
        * nat/aarch64-mte-linux-ptrace.h: New file.
---
 gdb/Makefile.in                    |   1 +
 gdb/aarch64-linux-nat.c            |  42 ++++++
 gdb/arch/aarch64-mte-linux.c       |  34 +++++
 gdb/arch/aarch64-mte-linux.h       |  10 ++
 gdb/configure.nat                  |   3 +-
 gdb/configure.tgt                  |   1 +
 gdb/nat/aarch64-mte-linux-ptrace.c | 200 +++++++++++++++++++++++++++++
 gdb/nat/aarch64-mte-linux-ptrace.h |  17 +++
 8 files changed, 307 insertions(+), 1 deletion(-)
 create mode 100644 gdb/arch/aarch64-mte-linux.c
 create mode 100644 gdb/nat/aarch64-mte-linux-ptrace.c

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index ac4b12c928..c15e2ab555 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -682,6 +682,7 @@ ALL_64_TARGET_OBS = \
  amd64-windows-tdep.o \
  arch/aarch64.o \
  arch/aarch64-insn.o \
+ arch/aarch64-mte-linux.o \
  arch/amd64.o \
  ia64-linux-tdep.o \
  ia64-tdep.o \
diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index dea34da669..40b79a8e98 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -52,6 +52,8 @@
 
 #include "arch/aarch64-mte-linux.h"
 
+#include "nat/aarch64-mte-linux-ptrace.h"
+
 #ifndef TRAP_HWBKPT
 #define TRAP_HWBKPT 0x0004
 #endif
@@ -102,6 +104,16 @@ class aarch64_linux_nat_target final : public linux_nat_target
     override;
 
   struct gdbarch *thread_architecture (ptid_t) override;
+
+  bool supports_memory_tagging () override;
+
+  /* Read memory allocation tags from memory via PTRACE.  */
+  int fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags) override;
+
+  /* Write allocation tags to memory via PTRACE.  */
+  int store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags) override;
 };
 
 static aarch64_linux_nat_target the_aarch64_linux_nat_target;
@@ -1050,6 +1062,36 @@ aarch64_linux_nat_target::thread_architecture (ptid_t ptid)
   return gdbarch_find_by_info (info);
 }
 
+/* Implement the "supports_memory_tagging" target_ops method.  */
+
+bool
+aarch64_linux_nat_target::supports_memory_tagging ()
+{
+  return (linux_get_hwcap2 (this) & HWCAP2_MTE) != 0;
+}
+
+/* Implement the "fetch_memtags" target_ops method.  */
+
+int
+aarch64_linux_nat_target::fetch_memtags (CORE_ADDR address, size_t len,
+ gdb::byte_vector &tags)
+{
+  int tid = inferior_ptid.lwp ();
+
+  return aarch64_mte_fetch_memtags (tid, address, len, tags);
+}
+
+/* Implement the "store_memtags" target_ops method.  */
+
+int
+aarch64_linux_nat_target::store_memtags (CORE_ADDR address, size_t len,
+ const gdb::byte_vector &tags)
+{
+  int tid = inferior_ptid.lwp ();
+
+  return aarch64_mte_store_memtags (tid, address, len, tags);
+}
+
 /* Define AArch64 maintenance commands.  */
 
 static void
diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-mte-linux.c
new file mode 100644
index 0000000000..ee162aef19
--- /dev/null
+++ b/gdb/arch/aarch64-mte-linux.c
@@ -0,0 +1,34 @@
+/* Common Linux target-dependent functionality for AArch64 MTE
+
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "arch/aarch64-mte-linux.h"
+
+/* See arch/aarch64-mte-linux.h */
+
+size_t
+get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size)
+{
+  /* Start address */
+  CORE_ADDR s_addr = align_down (addr, granule_size);
+  /* End address */
+  CORE_ADDR e_addr = align_down (addr + len, granule_size);
+
+  /* We always have at least 1 granule.  */
+  return 1 + (e_addr - s_addr) / granule_size;
+}
diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h
index 4124e80543..e555f0af19 100644
--- a/gdb/arch/aarch64-mte-linux.h
+++ b/gdb/arch/aarch64-mte-linux.h
@@ -20,6 +20,8 @@
 #ifndef ARCH_AARCH64_LINUX_H
 #define ARCH_AARCH64_LINUX_H
 
+#include "gdbsupport/common-defs.h"
+
 /* Feature check for Memory Tagging Extension.  */
 #ifndef HWCAP2_MTE
 #define HWCAP2_MTE  (1 << 18)
@@ -28,4 +30,12 @@
 /* The MTE regset consists of a single 64-bit register.  */
 #define AARCH64_LINUX_SIZEOF_MTE 8
 
+/* We have one tag per 16 bytes of memory.  */
+#define MTE_GRANULE_SIZE 16
+
+/* Return the number of tag granules in the memory range
+   [ADDR, ADDR + LEN) given GRANULE_SIZE.  */
+extern size_t get_tag_granules (CORE_ADDR addr, size_t len,
+ size_t granule_size);
+
 #endif /* ARCH_AARCH64_LINUX_H */
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 6ea2583495..1a91ea438e 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -236,7 +236,8 @@ case ${gdb_host} in
  NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
  aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
  nat/aarch64-linux.o \
- nat/aarch64-sve-linux-ptrace.o"
+ nat/aarch64-sve-linux-ptrace.o \
+ nat/aarch64-mte-linux-ptrace.o"
  ;;
     arm)
  # Host: ARM based machine running GNU/Linux
diff --git a/gdb/configure.tgt b/gdb/configure.tgt
index d66f01bb9f..208266118f 100644
--- a/gdb/configure.tgt
+++ b/gdb/configure.tgt
@@ -124,6 +124,7 @@ aarch64*-*-freebsd*)
 aarch64*-*-linux*)
  # Target: AArch64 linux
  gdb_target_obs="aarch64-linux-tdep.o arch/aarch64.o\
+ arch/aarch64-mte-linux.o \
  arch/arm.o arch/arm-linux.o arch/arm-get-next-pcs.o \
  arm-tdep.o arm-linux-tdep.o \
  glibc-tdep.o linux-tdep.o solib-svr4.o \
diff --git a/gdb/nat/aarch64-mte-linux-ptrace.c b/gdb/nat/aarch64-mte-linux-ptrace.c
new file mode 100644
index 0000000000..f92028176d
--- /dev/null
+++ b/gdb/nat/aarch64-mte-linux-ptrace.c
@@ -0,0 +1,200 @@
+/* Common Linux native ptrace code for AArch64 MTE.
+
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/byte-vector.h"
+
+#include "arch/aarch64.h"
+#include "arch/aarch64-mte-linux.h"
+#include "nat/aarch64-linux.h"
+#include "nat/aarch64-mte-linux-ptrace.h"
+
+#include "linux-ptrace.h"
+#include <sys/uio.h>
+
+/* Helper function to display various possible errors when reading
+   MTE tags.  */
+
+static void
+aarch64_mte_linux_peek_error (int error)
+{
+  switch (error)
+    {
+    case EIO:
+      perror_with_name (_("PEEKMTETAGS not supported"));
+      break;
+    case EFAULT:
+      perror_with_name (_("Couldn't fetch allocation tags"));
+      break;
+    case EOPNOTSUPP:
+      perror_with_name (_("PROT_ME not enabled for requested address"));
+    default:
+      perror_with_name (_("Unknown MTE error"));
+      break;
+    }
+}
+
+/* Helper function to display various possible errors when writing
+   MTE tags.  */
+
+static void
+aarch64_mte_linux_poke_error (int error)
+{
+  switch (error)
+    {
+    case EIO:
+      perror_with_name (_("POKEMTETAGS not supported"));
+      break;
+    case EFAULT:
+      perror_with_name (_("Couldn't store allocation tags"));
+      break;
+    case EOPNOTSUPP:
+      perror_with_name (_("PROT_ME not enabled for requested address"));
+    default:
+      perror_with_name (_("Unknown MTE error"));
+      break;
+    }
+}
+
+/* Helper to prepare a vector of tags to be passed on to the kernel.  The
+   main purpose of this function is to optimize the number of calls to
+   ptrace if we're writing too many tags at once, like a pattern fill
+   request.
+
+   Return a vector of tags of up to MAX_SIZE size, containing the tags that
+   must be passed on to the kernel, extracted from TAGS, starting at POS.
+   GRANULES is the number of tag granules to be modified.  */
+
+static gdb::byte_vector
+prepare_tag_vector (size_t granules, const gdb::byte_vector &tags, size_t pos,
+    size_t max_size)
+{
+  gdb::byte_vector t;
+
+  if (granules == 0)
+    {
+      t.clear ();
+      return t;
+    }
+
+  gdb_assert (tags.size () > 0 && max_size > 0);
+
+  if (granules > TAGS_MAX_SIZE)
+    t.resize (TAGS_MAX_SIZE);
+  else
+    t.resize (granules);
+
+  size_t tag_count = tags.size ();
+
+  for (size_t i = 0; i < t.size (); i++)
+    t[i] = tags[(pos + i) % tag_count];
+
+  return t;
+}
+
+/* See nat/aarch64-mte-linux-ptrace.h */
+
+int
+aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len,
+   gdb::byte_vector &tags)
+{
+  size_t ntags = get_tag_granules (address, len, MTE_GRANULE_SIZE);
+  gdb_byte tagbuf[ntags];
+
+  struct iovec iovec;
+  iovec.iov_base = tagbuf;
+  iovec.iov_len = ntags;
+
+  tags.clear ();
+  bool done_reading = false;
+
+  /* The kernel may return less tags than we requested.  Loop until we've read
+     all the requested tags or until we get an error.  */
+  while (!done_reading)
+    {
+      /* Attempt to read ntags allocation tags from the kernel.  */
+      if (ptrace (PTRACE_PEEKMTETAGS, tid, address, &iovec) < 0)
+ aarch64_mte_linux_peek_error (errno);
+
+      /* Make sure the kernel returned at least one tag.  */
+      if (iovec.iov_len <= 0)
+ {
+  tags.clear ();
+  return 1;
+ }
+
+      /* Copy the tags the kernel returned.  */
+      for (size_t i = 0; i < iovec.iov_len; i++)
+ tags.push_back (tagbuf[i]);
+
+      /* Are we done reading tags?  */
+      if (tags.size () == ntags)
+ done_reading = true;
+      else
+ {
+  address += iovec.iov_len * MTE_GRANULE_SIZE;
+  iovec.iov_len = ntags - iovec.iov_len;
+ }
+    }
+  return 0;
+}
+
+/* See nat/aarch64-mte-linux-ptrace.h */
+
+int
+aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len,
+   const gdb::byte_vector &tags)
+{
+  if (tags.size () == 0)
+    return 0;
+
+  /* Get the number of tags we need to write.  */
+  size_t ntags = get_tag_granules (address, len, MTE_GRANULE_SIZE);
+  bool done_writing = false;
+  size_t tags_written = 0;
+
+  /* Write all the tags, TAGS_MAX_SIZE blocks at a time.  */
+  while (!done_writing)
+    {
+      gdb::byte_vector t = prepare_tag_vector (ntags - tags_written, tags,
+       tags_written, TAGS_MAX_SIZE);
+
+      struct iovec iovec;
+      iovec.iov_base = t.data ();
+      iovec.iov_len = t.size ();
+
+      /* Request the kernel to update the allocation tags.  */
+      if (ptrace (PTRACE_POKEMTETAGS, tid, address, &iovec) < 0)
+ aarch64_mte_linux_poke_error (errno);
+
+      /* Make sure the kernel wrote at least one tag.  */
+      if (iovec.iov_len <= 0)
+ return 1;
+
+      tags_written += iovec.iov_len;
+
+      /* Are we done writing tags?  */
+      if (tags_written == ntags)
+ done_writing = true;
+      else
+ address += iovec.iov_len * MTE_GRANULE_SIZE;
+    }
+
+  return 0;
+}
diff --git a/gdb/nat/aarch64-mte-linux-ptrace.h b/gdb/nat/aarch64-mte-linux-ptrace.h
index 099b6440ca..7ba6f014f6 100644
--- a/gdb/nat/aarch64-mte-linux-ptrace.h
+++ b/gdb/nat/aarch64-mte-linux-ptrace.h
@@ -30,4 +30,21 @@
 #define PTRACE_POKEMTETAGS  34
 #endif
 
+/* Maximum number of tags to pass at once to the kernel.  */
+#define TAGS_MAX_SIZE 4096
+
+/* Read the allocation tags from memory range [ADDRESS, ADDRESS + LEN)
+   into TAGS.
+
+   Return 0 if successful and non-zero otherwise.  */
+extern int aarch64_mte_fetch_memtags (int tid, CORE_ADDR address, size_t len,
+      gdb::byte_vector &tags);
+
+/* Write the TAGS allocation tags to the memory range
+   [ADDRESS, ADDRESS + LEN).
+
+   Return 0 if successful and non-zero otherwise.  */
+extern int aarch64_mte_store_memtags (int tid, CORE_ADDR address, size_t len,
+      const gdb::byte_vector &tags);
+
 #endif /* NAT_AARCH64_MTE_LINUX_PTRACE_H */
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 13/23] Refactor parsing of /proc/<pid>/smaps

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
The Linux kernel exposes the information about MTE-protected pages via the
proc filesystem, more specifically through the smaps file.

What we're looking for is a mapping with the 'mt' flag, which tells us that
mapping was created with a PROT_MTE flag and, thus, is capable of using memory
tagging.

We already parse that file for other purposes (core file
generation/filtering), so this patch refactors the code to make the parsing
of the smaps file reusable for memory tagging.

The function linux_address_in_memtag_page uses the refactored code to allow
querying for memory tag support in a particular address, and it gets used in the
next patch.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * linux-tdep.c (struct smaps_vmflags) <memory_tagging>: New flag
        bit.
        (struct smaps_data): New struct.
        (decode_vmflags): Handle the 'mt' flag.
        (parse_smaps_data): New function, refactored from
        linux_find_memory_regions_full.
        (linux_address_in_memtag_page): New function.
        (linux_find_memory_regions_full): Refactor into parse_smaps_data.
        * linux-tdep.h (linux_address_in_memtag_page): New prototype.
---
 gdb/linux-tdep.c | 356 +++++++++++++++++++++++++++++++----------------
 gdb/linux-tdep.h |   4 +
 2 files changed, 240 insertions(+), 120 deletions(-)

diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c
index fd4337f100..daab1761a9 100644
--- a/gdb/linux-tdep.c
+++ b/gdb/linux-tdep.c
@@ -86,8 +86,33 @@ struct smaps_vmflags
     /* Is this a MAP_SHARED mapping (VM_SHARED, "sh").  */
 
     unsigned int shared_mapping : 1;
+
+    /* Memory map has memory tagging enabled.  */
+
+    unsigned int memory_tagging : 1;
   };
 
+/* Data structure that holds the information contained in the
+   /proc/<pid>/smaps file.  */
+
+struct smaps_data
+{
+  ULONGEST start_address;
+  ULONGEST end_address;
+  std::string filename;
+  struct smaps_vmflags vmflags;
+  bool read;
+  bool write;
+  bool exec;
+  bool priv;
+  bool has_anonymous;
+  bool mapping_anon_p;
+  bool mapping_file_p;
+
+  ULONGEST inode;
+  ULONGEST offset;
+};
+
 /* Whether to take the /proc/PID/coredump_filter into account when
    generating a corefile.  */
 
@@ -472,6 +497,8 @@ decode_vmflags (char *p, struct smaps_vmflags *v)
  v->exclude_coredump = 1;
       else if (strcmp (s, "sh") == 0)
  v->shared_mapping = 1;
+      else if (strcmp (s, "mt") == 0)
+ v->memory_tagging = 1;
     }
 }
 
@@ -1172,6 +1199,185 @@ typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size,
     const char *filename,
     void *data);
 
+/* Helper function to parse the contents of /proc/<pid>/smaps into a data
+   structure, for easy access.
+
+   DATA is the contents of the smaps file.  The parsed contents are stored
+   into the SMAPS vector.  */
+
+static int
+parse_smaps_data (const char *data,
+  std::vector<struct smaps_data> &smaps,
+  const char *mapsfilename)
+{
+  char *line, *t;
+
+  gdb_assert (data != nullptr);
+
+  smaps.clear ();
+
+  line = strtok_r ((char *) data, "\n", &t);
+
+  while (line != NULL)
+    {
+      ULONGEST addr, endaddr, offset, inode;
+      const char *permissions, *device, *filename;
+      struct smaps_vmflags v;
+      size_t permissions_len, device_len;
+      int read, write, exec, priv;
+      int has_anonymous = 0;
+      int mapping_anon_p;
+      int mapping_file_p;
+
+      memset (&v, 0, sizeof (v));
+      read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
+    &offset, &device, &device_len, &inode, &filename);
+      mapping_anon_p = mapping_is_anonymous_p (filename);
+      /* If the mapping is not anonymous, then we can consider it
+ to be file-backed.  These two states (anonymous or
+ file-backed) seem to be exclusive, but they can actually
+ coexist.  For example, if a file-backed mapping has
+ "Anonymous:" pages (see more below), then the Linux
+ kernel will dump this mapping when the user specified
+ that she only wants anonymous mappings in the corefile
+ (*even* when she explicitly disabled the dumping of
+ file-backed mappings).  */
+      mapping_file_p = !mapping_anon_p;
+
+      /* Decode permissions.  */
+      read = (memchr (permissions, 'r', permissions_len) != 0);
+      write = (memchr (permissions, 'w', permissions_len) != 0);
+      exec = (memchr (permissions, 'x', permissions_len) != 0);
+      /* 'private' here actually means VM_MAYSHARE, and not
+ VM_SHARED.  In order to know if a mapping is really
+ private or not, we must check the flag "sh" in the
+ VmFlags field.  This is done by decode_vmflags.  However,
+ if we are using a Linux kernel released before the commit
+ 834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
+ not have the VmFlags there.  In this case, there is
+ really no way to know if we are dealing with VM_SHARED,
+ so we just assume that VM_MAYSHARE is enough.  */
+      priv = memchr (permissions, 'p', permissions_len) != 0;
+
+      /* Try to detect if region should be dumped by parsing smaps
+ counters.  */
+      for (line = strtok_r (NULL, "\n", &t);
+   line != NULL && line[0] >= 'A' && line[0] <= 'Z';
+   line = strtok_r (NULL, "\n", &t))
+ {
+  char keyword[64 + 1];
+
+  if (sscanf (line, "%64s", keyword) != 1)
+    {
+      warning (_("Error parsing {s,}maps file '%s'"), mapsfilename);
+      break;
+    }
+
+  if (strcmp (keyword, "Anonymous:") == 0)
+    {
+      /* Older Linux kernels did not support the
+ "Anonymous:" counter.  Check it here.  */
+      has_anonymous = 1;
+    }
+  else if (strcmp (keyword, "VmFlags:") == 0)
+    decode_vmflags (line, &v);
+
+  if (strcmp (keyword, "AnonHugePages:") == 0
+      || strcmp (keyword, "Anonymous:") == 0)
+    {
+      unsigned long number;
+
+      if (sscanf (line, "%*s%lu", &number) != 1)
+ {
+  warning (_("Error parsing {s,}maps file '%s' number"),
+   mapsfilename);
+  break;
+ }
+      if (number > 0)
+ {
+  /* Even if we are dealing with a file-backed
+     mapping, if it contains anonymous pages we
+     consider it to be *also* an anonymous
+     mapping, because this is what the Linux
+     kernel does:
+
+     // Dump segments that have been written to.
+     if (vma->anon_vma && FILTER(ANON_PRIVATE))
+       goto whole;
+
+    Note that if the mapping is already marked as
+    file-backed (i.e., mapping_file_p is
+    non-zero), then this is a special case, and
+    this mapping will be dumped either when the
+    user wants to dump file-backed *or* anonymous
+    mappings.  */
+  mapping_anon_p = 1;
+ }
+    }
+ }
+      /* Save the smaps entry to the vector.  */
+ struct smaps_data map;
+ std::string fname (filename);
+
+ map.start_address = addr;
+ map.end_address = endaddr;
+ map.filename = fname;
+ map.vmflags = v;
+ map.read = read? true : false;
+ map.write = write? true : false;
+ map.exec = exec? true : false;
+ map.priv = priv? true : false;
+ map.has_anonymous = has_anonymous;
+ map.mapping_anon_p = mapping_anon_p? true : false;
+ map.mapping_file_p = mapping_file_p? true : false;
+ map.offset = offset;
+ map.inode = inode;
+
+ smaps.emplace_back (map);
+    }
+
+  return 0;
+}
+
+/* See linux-tdep.h.  */
+
+bool
+linux_address_in_memtag_page (CORE_ADDR address)
+{
+  if (current_inferior ()->fake_pid_p)
+    return false;
+
+  pid_t pid = current_inferior ()->pid;
+
+  std::string smaps_file = string_printf ("/proc/%d/smaps", pid);
+
+  gdb::unique_xmalloc_ptr<char> data
+    = target_fileio_read_stralloc (NULL, smaps_file.c_str ());
+
+  if (data == nullptr)
+    return false;
+
+  std::vector<struct smaps_data> smaps;
+
+  /* Parse the contents of smaps into a vector.  */
+  parse_smaps_data (data.get (), smaps, smaps_file.c_str ());
+
+  if (!smaps.empty ())
+    {
+      for (struct smaps_data map : smaps)
+ {
+  /* Is the address within [start_address, end_address) in a page
+     mapped with memory tagging?  */
+  if (address >= map.start_address
+      && address < map.end_address
+      && map.vmflags.memory_tagging)
+    return true;
+ }
+    }
+
+  return false;
+}
+
 /* List memory regions in the inferior for a corefile.  */
 
 static int
@@ -1179,8 +1385,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
  linux_find_memory_region_ftype *func,
  void *obfd)
 {
-  char mapsfilename[100];
-  char coredumpfilter_name[100];
+  std::string coredumpfilter_name;
   pid_t pid;
   /* Default dump behavior of coredump_filter (0x33), according to
      Documentation/filesystems/proc.txt from the Linux kernel
@@ -1198,10 +1403,9 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
 
   if (use_coredump_filter)
     {
-      xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name),
- "/proc/%d/coredump_filter", pid);
+      coredumpfilter_name = string_printf ("/proc/%d/coredump_filter", pid);
       gdb::unique_xmalloc_ptr<char> coredumpfilterdata
- = target_fileio_read_stralloc (NULL, coredumpfilter_name);
+ = target_fileio_read_stralloc (NULL, coredumpfilter_name.c_str ());
       if (coredumpfilterdata != NULL)
  {
   unsigned int flags;
@@ -1211,124 +1415,37 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
  }
     }
 
-  xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid);
+  std::string mapsfilename = string_printf ("/proc/%d/smaps", pid);
   gdb::unique_xmalloc_ptr<char> data
-    = target_fileio_read_stralloc (NULL, mapsfilename);
+    = target_fileio_read_stralloc (NULL, mapsfilename.c_str ());
   if (data == NULL)
     {
       /* Older Linux kernels did not support /proc/PID/smaps.  */
-      xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid);
-      data = target_fileio_read_stralloc (NULL, mapsfilename);
+      mapsfilename = string_printf ("/proc/%d/maps", pid);
+      data = target_fileio_read_stralloc (NULL, mapsfilename.c_str ());
     }
 
-  if (data != NULL)
-    {
-      char *line, *t;
-
-      line = strtok_r (data.get (), "\n", &t);
-      while (line != NULL)
- {
-  ULONGEST addr, endaddr, offset, inode;
-  const char *permissions, *device, *filename;
-  struct smaps_vmflags v;
-  size_t permissions_len, device_len;
-  int read, write, exec, priv;
-  int has_anonymous = 0;
-  int should_dump_p = 0;
-  int mapping_anon_p;
-  int mapping_file_p;
-
-  memset (&v, 0, sizeof (v));
-  read_mapping (line, &addr, &endaddr, &permissions, &permissions_len,
- &offset, &device, &device_len, &inode, &filename);
-  mapping_anon_p = mapping_is_anonymous_p (filename);
-  /* If the mapping is not anonymous, then we can consider it
-     to be file-backed.  These two states (anonymous or
-     file-backed) seem to be exclusive, but they can actually
-     coexist.  For example, if a file-backed mapping has
-     "Anonymous:" pages (see more below), then the Linux
-     kernel will dump this mapping when the user specified
-     that she only wants anonymous mappings in the corefile
-     (*even* when she explicitly disabled the dumping of
-     file-backed mappings).  */
-  mapping_file_p = !mapping_anon_p;
-
-  /* Decode permissions.  */
-  read = (memchr (permissions, 'r', permissions_len) != 0);
-  write = (memchr (permissions, 'w', permissions_len) != 0);
-  exec = (memchr (permissions, 'x', permissions_len) != 0);
-  /* 'private' here actually means VM_MAYSHARE, and not
-     VM_SHARED.  In order to know if a mapping is really
-     private or not, we must check the flag "sh" in the
-     VmFlags field.  This is done by decode_vmflags.  However,
-     if we are using a Linux kernel released before the commit
-     834f82e2aa9a8ede94b17b656329f850c1471514 (3.10), we will
-     not have the VmFlags there.  In this case, there is
-     really no way to know if we are dealing with VM_SHARED,
-     so we just assume that VM_MAYSHARE is enough.  */
-  priv = memchr (permissions, 'p', permissions_len) != 0;
-
-  /* Try to detect if region should be dumped by parsing smaps
-     counters.  */
-  for (line = strtok_r (NULL, "\n", &t);
-       line != NULL && line[0] >= 'A' && line[0] <= 'Z';
-       line = strtok_r (NULL, "\n", &t))
-    {
-      char keyword[64 + 1];
+  if (data == nullptr)
+    return 1;
 
-      if (sscanf (line, "%64s", keyword) != 1)
- {
-  warning (_("Error parsing {s,}maps file '%s'"), mapsfilename);
-  break;
- }
+  std::vector<struct smaps_data> smaps;
 
-      if (strcmp (keyword, "Anonymous:") == 0)
- {
-  /* Older Linux kernels did not support the
-     "Anonymous:" counter.  Check it here.  */
-  has_anonymous = 1;
- }
-      else if (strcmp (keyword, "VmFlags:") == 0)
- decode_vmflags (line, &v);
+  /* Parse the contents of smaps into a vector.  */
+  parse_smaps_data (data.get (), smaps, mapsfilename.c_str ());
 
-      if (strcmp (keyword, "AnonHugePages:") == 0
-  || strcmp (keyword, "Anonymous:") == 0)
- {
-  unsigned long number;
-
-  if (sscanf (line, "%*s%lu", &number) != 1)
-    {
-      warning (_("Error parsing {s,}maps file '%s' number"),
-       mapsfilename);
-      break;
-    }
-  if (number > 0)
-    {
-      /* Even if we are dealing with a file-backed
- mapping, if it contains anonymous pages we
- consider it to be *also* an anonymous
- mapping, because this is what the Linux
- kernel does:
-
- // Dump segments that have been written to.
- if (vma->anon_vma && FILTER(ANON_PRIVATE))
- goto whole;
-
- Note that if the mapping is already marked as
- file-backed (i.e., mapping_file_p is
- non-zero), then this is a special case, and
- this mapping will be dumped either when the
- user wants to dump file-backed *or* anonymous
- mappings.  */
-      mapping_anon_p = 1;
-    }
- }
-    }
+  if (!smaps.empty ())
+    {
+      for (struct smaps_data map : smaps)
+ {
+  int should_dump_p = 0;
 
-  if (has_anonymous)
-    should_dump_p = dump_mapping_p (filterflags, &v, priv,
-    mapping_anon_p, mapping_file_p,
-    filename, addr, offset);
+  if (map.has_anonymous)
+    should_dump_p = dump_mapping_p (filterflags, &map.vmflags, map.priv,
+    map.mapping_anon_p,
+    map.mapping_file_p,
+    map.filename.c_str (),
+    map.start_address,
+    map.offset);
   else
     {
       /* Older Linux kernels did not support the "Anonymous:" counter.
@@ -1338,16 +1455,15 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch,
 
   /* Invoke the callback function to create the corefile segment.  */
   if (should_dump_p)
-    func (addr, endaddr - addr, offset, inode,
-  read, write, exec, 1, /* MODIFIED is true because we
-   want to dump the mapping.  */
-  filename, obfd);
+    func (map.start_address, map.end_address - map.start_address,
+  map.offset, map.inode, map.read, map.write, map.exec,
+  1, /* MODIFIED is true because we want to dump
+ the mapping.  */
+  map.filename.c_str (), obfd);
  }
-
-      return 0;
     }
 
-  return 1;
+  return 0;
 }
 
 /* A structure for passing information through
diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h
index 91c28738f5..32c9c03835 100644
--- a/gdb/linux-tdep.h
+++ b/gdb/linux-tdep.h
@@ -41,6 +41,10 @@ DEF_ENUM_FLAGS_TYPE (enum linux_siginfo_extra_field_values,
 struct type *linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch,
  linux_siginfo_extra_fields);
 
+ /* Return true if ADDRESS is within the boundaries of a page mapped with
+   memory tagging protection.  */
+bool linux_address_in_memtag_page (CORE_ADDR address);
+
 typedef char *(*linux_collect_thread_registers_ftype) (const struct regcache *,
        ptid_t,
        bfd *, char *, int *,
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 14/23] AArch64: Implement the memory tagging gdbarch hooks

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
This patch implements the memory tagging gdbarch hooks for AArch64, for
the MTE feature.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * aarch64-linux-tdep.c: Include target.h, arch-utils.h, value.h.
        (aarch64_linux_get_atag, aarch64_linux_tagged_address_p)
        (aarch64_linux_memtag_mismatch_p, aarch64_linux_set_memtags)
        (aarch64_linux_get_memtag, aarch64_linux_memtag_to_string): New
        functions.
        (aarch64_linux_init_abi): Initialize MTE-related gdbarch hooks.
        * arch/aarch64-mte-linux.c (make_ltag_bits, make_ltag)
        (aarch64_linux_set_ltag, aarch64_linux_get_ltag): New functions.
        * arch/aarch64-mte-linux.h (MTE_LOGICAL_TAG_START_BIT)
        (MTE_LOGICAL_MAX_VALUE): Define.
        (make_ltag_bits, make_ltag, aarch64_linux_set_ltag)
        (aarch64_linux_get_ltag): New prototype.
---
 gdb/aarch64-linux-tdep.c     | 212 +++++++++++++++++++++++++++++++++++
 gdb/arch/aarch64-mte-linux.c |  36 ++++++
 gdb/arch/aarch64-mte-linux.h |  19 ++++
 3 files changed, 267 insertions(+)

diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 53f9d9f6d2..abcd325951 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -30,6 +30,7 @@
 #include "symtab.h"
 #include "tramp-frame.h"
 #include "trad-frame.h"
+#include "target.h"
 #include "target/target.h"
 
 #include "regcache.h"
@@ -46,6 +47,9 @@
 
 #include "arch/aarch64-mte-linux.h"
 
+#include "arch-utils.h"
+#include "value.h"
+
 /* Signal frame handling.
 
       +------------+  ^
@@ -1437,6 +1441,189 @@ aarch64_linux_gcc_target_options (struct gdbarch *gdbarch)
   return {};
 }
 
+/* Helper to get the allocation tag from a 64-bit ADDRESS.
+
+   Return 0 for success and non-zero otherwise.  */
+
+static int
+aarch64_linux_get_atag (CORE_ADDR address, CORE_ADDR *tag)
+{
+  gdb::byte_vector tags;
+
+  /* Attempt to fetch the allocation tag.  */
+  if (target_fetch_memtags (address, 0, tags) != 0)
+    return 1;
+
+  /* Only one tag should've been returned.  Make sure we got exactly that.  */
+  gdb_assert (tags.size () == 1);
+
+  /* Although our tags are 4 bits in size, they are stored in a
+     byte.  */
+  *tag = tags[0];
+
+  return 0;
+}
+
+/* Implement the tagged_address_p gdbarch method.  */
+
+static bool
+aarch64_linux_tagged_address_p (struct gdbarch *gdbarch, struct value *address)
+{
+  gdb_assert (address != nullptr);
+
+  CORE_ADDR addr = value_as_address (address);
+
+  /* Remove the top byte for the memory range check.  */
+  addr = address_significant (gdbarch, addr);
+
+  /* Check if the page that contains ADDRESS is mapped with PROT_MTE.  */
+  if (!linux_address_in_memtag_page (addr))
+    return false;
+
+  /* We have a valid tag in the top byte of the 64-bit address.  */
+  return true;
+}
+
+/* Implement the memtag_mismatch_p gdbarch method.  */
+
+static bool
+aarch64_linux_memtag_mismatch_p (struct gdbarch *gdbarch,
+ struct value *address)
+{
+  gdb_assert (address != nullptr);
+
+  /* Make sure we are dealing with a tagged address to begin with.  */
+  if (!aarch64_linux_tagged_address_p (gdbarch, address))
+    return false;
+
+  CORE_ADDR addr = value_as_address (address);
+
+  /* Fetch the allocation tag for ADDRESS.  */
+  CORE_ADDR atag = 0;
+
+  if (aarch64_linux_get_atag (addr, &atag) != 0)
+    return false;
+
+  /* Fetch the logical tag for ADDRESS.  */
+  gdb_byte ltag = aarch64_linux_get_ltag (addr);
+
+  /* Are the tags the same?  */
+  if (ltag == atag)
+    return false;
+
+  return true;
+}
+
+/* Implement the set_memtags gdbarch method.  */
+
+static int
+aarch64_linux_set_memtags (struct gdbarch *gdbarch, struct value *address,
+   size_t length, const gdb::byte_vector &tags,
+   enum memtag_type tag_type)
+{
+  if (tags.empty ())
+    return 0;
+
+  gdb_assert (address != nullptr);
+
+  CORE_ADDR addr = value_as_address (address);
+
+  /* Set the logical tag or the allocation tag.  */
+  if (tag_type == tag_logical)
+    {
+      /* When setting logical tags, we don't care about the length, since
+ we are only setting a single logical tag.  */
+      addr = aarch64_linux_set_ltag (addr, tags[0]);
+
+      /* Update the value's content with the tag.  */
+      enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+      gdb_byte *srcbuf = value_contents_raw (address);
+      store_unsigned_integer (srcbuf, sizeof (addr), byte_order, addr);
+    }
+  else
+    {
+      /* Make sure we are dealing with a tagged address to begin with.  */
+      if (!aarch64_linux_tagged_address_p (gdbarch, address))
+ return 1;
+
+      /* With G being the number of tag granules and N the number of tags
+ passed in, we can have the following cases:
+
+ 1 - G == N: Store all the N tags to memory.
+
+ 2 - G < N : Warn about having more tags than granules, but write G
+     tags.
+
+ 3 - G > N : This is a "fill tags" operation.  We should use the tags
+     as a pattern to fill the granules repeatedly until we have
+     written G tags to memory.
+      */
+
+      size_t g = get_tag_granules (addr, length, MTE_GRANULE_SIZE);
+      size_t n = tags.size ();
+
+      if (g < n)
+ {
+  warning (_("Got more tags than memory granules.  Tags will be "
+     "truncated."));
+ }
+      else if (g > n)
+ warning (_("Using tag pattern to fill memory range."));
+
+      if (target_store_memtags (addr, length, tags) != 0)
+ return 1;
+    }
+  return 0;
+}
+
+/* Implement the get_memtag gdbarch method.  */
+
+static struct value *
+aarch64_linux_get_memtag (struct gdbarch *gdbarch, struct value *address,
+  enum memtag_type tag_type)
+{
+  gdb_assert (address != nullptr);
+
+  CORE_ADDR addr = value_as_address (address);
+  CORE_ADDR tag = 0;
+
+  /* Get the logical tag or the allocation tag.  */
+  if (tag_type == tag_logical)
+    tag = aarch64_linux_get_ltag (addr);
+  else
+    {
+      /* Make sure we are dealing with a tagged address to begin with.  */
+      if (!aarch64_linux_tagged_address_p (gdbarch, address))
+ return nullptr;
+
+      if (aarch64_linux_get_atag (addr, &tag) != 0)
+ return nullptr;
+    }
+
+  /* Convert the tag to a value.  */
+  return value_from_ulongest (builtin_type (gdbarch)->builtin_unsigned_int,
+      tag);
+}
+
+/* Implement the memtag_to_string gdbarch method.  */
+
+static std::string
+aarch64_linux_memtag_to_string (struct gdbarch *gdbarch,
+ struct value *address,
+ enum memtag_type tag_type)
+{
+  gdb_assert (address != nullptr);
+
+  struct value *v_tag = aarch64_linux_get_memtag (gdbarch, address, tag_type);
+
+  if (v_tag == nullptr && tag_allocation)
+    error (_("Error getting tag from target"));
+
+  CORE_ADDR tag = value_as_address (v_tag);
+
+  return string_printf ("0x%s", phex_nz (tag, sizeof (tag)));
+}
+
 static void
 aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -1494,6 +1681,31 @@ aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
      data associated with the address.  */
   set_gdbarch_significant_addr_bit (gdbarch, 56);
 
+  /* MTE-specific settings and hooks.  */
+  if (tdep->has_mte ())
+    {
+      /* Register a hook for checking if an address is tagged or not.  */
+      set_gdbarch_tagged_address_p (gdbarch, aarch64_linux_tagged_address_p);
+
+      /* Register a hook for checking if there is a memory tag mismatch.  */
+      set_gdbarch_memtag_mismatch_p (gdbarch,
+     aarch64_linux_memtag_mismatch_p);
+
+      /* Register a hook for setting the logical/allocation tags for
+ a range of addresses.  */
+      set_gdbarch_set_memtags (gdbarch, aarch64_linux_set_memtags);
+
+      /* Register a hook for extracting the logical/allocation tag from an
+ address.  */
+      set_gdbarch_get_memtag (gdbarch, aarch64_linux_get_memtag);
+
+      /* Set the allocation tag granule size to 16 bytes.  */
+      set_gdbarch_memtag_granule_size (gdbarch, MTE_GRANULE_SIZE);
+
+      /* Register a hook for converting a memory tag to a string.  */
+      set_gdbarch_memtag_to_string (gdbarch, aarch64_linux_memtag_to_string);
+    }
+
   /* Initialize the aarch64_linux_record_tdep.  */
   /* These values are the size of the type that will be used in a system
      call.  They are obtained from Linux Kernel source.  */
diff --git a/gdb/arch/aarch64-mte-linux.c b/gdb/arch/aarch64-mte-linux.c
index ee162aef19..5c02e5ef5b 100644
--- a/gdb/arch/aarch64-mte-linux.c
+++ b/gdb/arch/aarch64-mte-linux.c
@@ -32,3 +32,39 @@ get_tag_granules (CORE_ADDR addr, size_t len, size_t granule_size)
   /* We always have at least 1 granule.  */
   return 1 + (e_addr - s_addr) / granule_size;
 }
+
+/* See arch/aarch64-mte-linux.h */
+
+CORE_ADDR
+make_ltag_bits (CORE_ADDR value)
+{
+  return value & MTE_LOGICAL_MAX_VALUE;
+}
+
+/* See arch/aarch64-mte-linux.h */
+
+CORE_ADDR
+make_ltag (CORE_ADDR value)
+{
+  return make_ltag_bits (value) << MTE_LOGICAL_TAG_START_BIT;
+}
+
+/* See arch/aarch64-mte-linux.h */
+
+CORE_ADDR
+aarch64_linux_set_ltag (CORE_ADDR address, CORE_ADDR tag)
+{
+  /* Remove the existing tag.  */
+  address &= ~make_ltag (MTE_LOGICAL_MAX_VALUE);
+
+  /* Return the new tagged address.  */
+  return address | make_ltag (tag);
+}
+
+/* See arch/aarch64-mte-linux.h */
+
+CORE_ADDR
+aarch64_linux_get_ltag (CORE_ADDR address)
+{
+  return make_ltag_bits (address >> MTE_LOGICAL_TAG_START_BIT);
+}
diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h
index e555f0af19..5c5783f28b 100644
--- a/gdb/arch/aarch64-mte-linux.h
+++ b/gdb/arch/aarch64-mte-linux.h
@@ -32,10 +32,29 @@
 
 /* We have one tag per 16 bytes of memory.  */
 #define MTE_GRANULE_SIZE 16
+#define MTE_LOGICAL_TAG_START_BIT   56
+#define MTE_LOGICAL_MAX_VALUE    0xf
 
 /* Return the number of tag granules in the memory range
    [ADDR, ADDR + LEN) given GRANULE_SIZE.  */
 extern size_t get_tag_granules (CORE_ADDR addr, size_t len,
  size_t granule_size);
 
+/* Return the 4-bit tag made from VALUE.  */
+extern CORE_ADDR make_ltag_bits (CORE_ADDR value);
+
+/* Return the 4-bit tag that can be OR-ed to an address.  */
+extern CORE_ADDR make_ltag (CORE_ADDR value);
+
+/* Helper to set the logical TAG for a 64-bit ADDRESS.
+
+   It is always possible to set the logical tag.  */
+extern CORE_ADDR aarch64_linux_set_ltag (CORE_ADDR address,
+ CORE_ADDR tag);
+
+/* Helper to get the logical tag from a 64-bit ADDRESS.
+
+   It is always possible to get the logical tag.  */
+extern CORE_ADDR aarch64_linux_get_ltag (CORE_ADDR address);
+
 #endif /* ARCH_AARCH64_LINUX_H */
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 15/23] AArch64: Add unit testing for logical tag set/get operations

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Add some unit testing to exercise setting/getting logical tags in the
AArch64 implementation.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * aarch64-linux-tdep.c: Include gdbsupport/selftest.h.
        (aarch64_linux_ltag_tests): New function.
        (_initialize_aarch64_linux_tdep): Register aarch64_linux_ltag_tests.
---
 gdb/aarch64-linux-tdep.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index abcd325951..5cb66fbb14 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -50,6 +50,8 @@
 #include "arch-utils.h"
 #include "value.h"
 
+#include "gdbsupport/selftest.h"
+
 /* Signal frame handling.
 
       +------------+  ^
@@ -1882,10 +1884,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_linux_get_ltag (addr) == (i & 0xf));
+
+      addr = aarch64_linux_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
 }
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 16/23] AArch64: Report tag violation error information

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Whenever a memory tag violation occurs, we get a SIGSEGV. Additional
information can be obtained through the siginfo data structure.

For AArch64 the Linux kernel may expose the fault address and tag
information, if we have a synchronous event. Otherwise there is
no fault address available.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * aarch64-linux-tdep.c
        (aarch64_linux_handle_segmentation_fault): New function.
        (aarch64_linux_init_abi): Register
        aarch64_linux_handle_segmentation_fault as segmentation fault hook.
        * arch/aarch64-linux.h (SEGV_MTEAERR): Define.
        (SEGV_MTESERR): Define.
---
 gdb/aarch64-linux-tdep.c     | 58 ++++++++++++++++++++++++++++++++++++
 gdb/arch/aarch64-mte-linux.h |  6 ++++
 2 files changed, 64 insertions(+)

diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index 5cb66fbb14..e89e3a5467 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -1626,6 +1626,61 @@ aarch64_linux_memtag_to_string (struct gdbarch *gdbarch,
   return string_printf ("0x%s", phex_nz (tag, sizeof (tag)));
 }
 
+/* AArch64 Linux implementation of the handle_segmentation_fault gdbarch
+   hook.  Displays information about possible memory tag violations.  */
+
+static void
+aarch64_linux_handle_segmentation_fault (struct gdbarch *gdbarch,
+ struct ui_out *uiout)
+{
+  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\n");
+
+      fault_addr
+ = parse_and_eval_long ("$_siginfo._sifields._sigfault.si_addr");
+    }
+  catch (const gdb_exception &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");
+
+      CORE_ADDR atag;
+      if (aarch64_linux_get_atag (fault_addr, &atag) != 0)
+ uiout->text (_("Allocation tag unavailable"));
+      else
+ {
+  uiout->text (_("Allocation tag "));
+  uiout->field_core_addr ("allocation-tag", gdbarch, atag);
+ }
+    }
+  else
+    {
+      uiout->text ("\n");
+      uiout->text (_("Fault address unavailable"));
+    }
+}
+
 static void
 aarch64_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 {
@@ -1706,6 +1761,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_handle_segmentation_fault (gdbarch,
+      aarch64_linux_handle_segmentation_fault);
     }
 
   /* Initialize the aarch64_linux_record_tdep.  */
diff --git a/gdb/arch/aarch64-mte-linux.h b/gdb/arch/aarch64-mte-linux.h
index 5c5783f28b..a5a980ed49 100644
--- a/gdb/arch/aarch64-mte-linux.h
+++ b/gdb/arch/aarch64-mte-linux.h
@@ -35,6 +35,12 @@
 #define MTE_LOGICAL_TAG_START_BIT   56
 #define MTE_LOGICAL_MAX_VALUE    0xf
 
+/* Memory tagging definitions.  */
+#ifndef SEGV_MTEAERR
+# define SEGV_MTEAERR 8
+# define SEGV_MTESERR 9
+#endif
+
 /* Return the number of tag granules in the memory range
    [ADDR, ADDR + LEN) given GRANULE_SIZE.  */
 extern size_t get_tag_granules (CORE_ADDR addr, size_t len,
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 17/23] AArch64: Add gdbserver MTE support

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Adds the AArch64-specific memory tagging support (MTE) by implementing the
required hooks and checks for GDBserver.

gdbserver/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * Makefile.in (SFILES): Add /../gdb/nat/aarch64-mte-linux-ptrace.c.
        * configure.srv (aarch64*-*-linux*): Add arch/aarch64-mte-linux.o and
        nat/aarch64-mte-linux-ptrace.o.
        * linux-aarch64-low.cc: Include nat/aarch64-mte-linux-ptrace.h.
        (class aarch64_target) <supports_memory_tagging>
        <fetch_memtags, store_memtags>: New method overrides.
        (aarch64_target::supports_memory_tagging)
        (aarch64_target::fetch_memtags)
        (aarch64_target::store_memtags): New methods.
---
 gdbserver/Makefile.in          |  1 +
 gdbserver/configure.srv        |  2 ++
 gdbserver/linux-aarch64-low.cc | 53 ++++++++++++++++++++++++++++++++++
 3 files changed, 56 insertions(+)

diff --git a/gdbserver/Makefile.in b/gdbserver/Makefile.in
index 9d7687be53..8e54e1fd09 100644
--- a/gdbserver/Makefile.in
+++ b/gdbserver/Makefile.in
@@ -212,6 +212,7 @@ SFILES = \
  $(srcdir)/../gdb/arch/ppc-linux-common.c \
  $(srcdir)/../gdb/arch/riscv.c \
  $(srcdir)/../gdb/nat/aarch64-sve-linux-ptrace.c \
+ $(srcdir)/../gdb/nat/aarch64-mte-linux-ptrace.c \
  $(srcdir)/../gdb/nat/linux-btrace.c \
  $(srcdir)/../gdb/nat/linux-namespaces.c \
  $(srcdir)/../gdb/nat/linux-osdata.c \
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 5e33bd9c54..e8d599e866 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -52,8 +52,10 @@ case "${gdbserver_host}" in
  srv_tgtobj="$srv_tgtobj nat/aarch64-linux.o"
  srv_tgtobj="$srv_tgtobj arch/aarch64-insn.o"
  srv_tgtobj="$srv_tgtobj arch/aarch64.o"
+ srv_tgtobj="$srv_tgtobj arch/aarch64-mte-linux.o"
  srv_tgtobj="$srv_tgtobj linux-aarch64-tdesc.o"
  srv_tgtobj="$srv_tgtobj nat/aarch64-sve-linux-ptrace.o"
+ srv_tgtobj="$srv_tgtobj nat/aarch64-mte-linux-ptrace.o"
  srv_tgtobj="${srv_tgtobj} $srv_linux_obj"
  srv_linux_regsets=yes
  srv_linux_thread_db=yes
diff --git a/gdbserver/linux-aarch64-low.cc b/gdbserver/linux-aarch64-low.cc
index 22485d9466..f8d2511fff 100644
--- a/gdbserver/linux-aarch64-low.cc
+++ b/gdbserver/linux-aarch64-low.cc
@@ -44,12 +44,17 @@
 #include "linux-aarch32-tdesc.h"
 #include "linux-aarch64-tdesc.h"
 #include "nat/aarch64-sve-linux-ptrace.h"
+#include "nat/aarch64-mte-linux-ptrace.h"
 #include "tdesc.h"
 
 #ifdef HAVE_SYS_REG_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
@@ -82,6 +87,14 @@ public:
 
   struct emit_ops *emit_ops () override;
 
+  bool supports_memory_tagging () override;
+
+  int fetch_memtags (CORE_ADDR address, size_t len,
+     gdb::byte_vector &tags) override;
+
+  int store_memtags (CORE_ADDR address, size_t len,
+     const gdb::byte_vector &tags) override;
+
 protected:
 
   void low_arch_setup () override;
@@ -3201,6 +3214,46 @@ aarch64_target::breakpoint_kind_from_current_state (CORE_ADDR *pcptr)
     return arm_breakpoint_kind_from_current_state (pcptr);
 }
 
+/* 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;
+}
+
+int
+aarch64_target::fetch_memtags (CORE_ADDR address, size_t len,
+       gdb::byte_vector &tags)
+{
+  /* Allocation tags are per-process, so any tid is fine.  */
+  int tid = lwpid_of (current_thread);
+
+  return aarch64_mte_fetch_memtags (tid, address, len, tags);
+}
+
+int
+aarch64_target::store_memtags (CORE_ADDR address, size_t len,
+       const gdb::byte_vector &tags)
+{
+  /* Allocation tags are per-process, so any tid is fine.  */
+  int tid = lwpid_of (current_thread);
+
+  return aarch64_mte_store_memtags (tid, address, len, tags);
+}
+
 /* The linux target ops object.  */
 
 linux_process_target *the_linux_target = &the_aarch64_target;
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 18/23] New mtag commands

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Add new commands under the "mtag" prefix to allow users to inspect, modify and
check memory tags in different ways.

The available subcommands are the following:

- mtag showltag <expression>: Shows the logical tag for a particular address.

- mtag setltag <expression> <tag>: Prints the address tagged with the logical
  tag <tag>.

- mtag showatag <expression>: Shows the allocation tag for a particular address.

- mtag setatag <expression> <length> <tags>: Sets one or more allocation tags to
  the specified tags.

- mtag check <expression>: Check if the logical tag in <address> matches its
  allocation tag.

These commands make use of the memory tagging gdbarch methods, and are still
available, but disabled, when memory tagging is not supported by the
architecture.

I've pondered about a way to make these commands invisible when memory tagging
is not available, but given the check is at runtime (and support may come and go
based on a process' configuration), that is a bit too late in the process to
either not include the commands or get rid of them.

Ideas are welcome.

gdb/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * printcmd.c: Include gdbsupport/rsp-low.h.
        (mtaglist): New static global.
        (process_print_command_args): Factored out of
        print_command_1.
        (print_command_1): Use process_print_command_args.
        (show_addr_not_tagged, show_memtag_unsupported, mtag_command)
        (mtag_showtag_command, mtag_showltag_command, mtag_showatag_command)
        (parse_setltag_input, mtag_setltag_command, parse_setatag_input)
        (mtag_setatag_command, mtag_check_command): New functions.
        (_initialize_printcmd): Add "mtag" prefix and subcommands.

gdbsupport/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * rsp-low.cc (fromhex): Change error message text to not be
        RSP-specific.
---
 gdb/printcmd.c        | 359 ++++++++++++++++++++++++++++++++++++++++--
 gdbsupport/rsp-low.cc |   2 +-
 2 files changed, 348 insertions(+), 13 deletions(-)

diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index d0261a021a..ef1c16031b 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -53,6 +53,11 @@
 #include "source.h"
 #include "gdbsupport/byte-vector.h"
 #include "gdbsupport/gdb_optional.h"
+#include "gdbsupport/rsp-low.h"
+
+/* Chain containing all defined mtag subcommands.  */
+
+struct cmd_list_element *mtaglist;
 
 /* Last specified output format.  */
 
@@ -1208,31 +1213,38 @@ print_value (value *val, const value_print_options &opts)
   annotate_value_history_end ();
 }
 
-/* Implementation of the "print" and "call" commands.  */
+/* Helper for parsing arguments for print_command_1.  */
 
-static void
-print_command_1 (const char *args, int voidprint)
+static struct value *
+process_print_command_args (const char *args, value_print_options *print_opts)
 {
-  struct value *val;
-  value_print_options print_opts;
-
-  get_user_print_options (&print_opts);
+  get_user_print_options (print_opts);
   /* Override global settings with explicit options, if any.  */
-  auto group = make_value_print_options_def_group (&print_opts);
+  auto group = make_value_print_options_def_group (print_opts);
   gdb::option::process_options
     (&args, gdb::option::PROCESS_OPTIONS_REQUIRE_DELIMITER, group);
 
-  print_command_parse_format (&args, "print", &print_opts);
+  print_command_parse_format (&args, "print", print_opts);
 
   const char *exp = args;
 
   if (exp != nullptr && *exp)
     {
       expression_up expr = parse_expression (exp);
-      val = evaluate_expression (expr.get ());
+      return evaluate_expression (expr.get ());
     }
-  else
-    val = access_value_history (0);
+
+  return access_value_history (0);
+}
+
+/* Implementation of the "print" and "call" commands.  */
+
+static void
+print_command_1 (const char *args, int voidprint)
+{
+  value_print_options print_opts;
+
+  struct value *val = process_print_command_args (args, &print_opts);
 
   if (voidprint || (val && value_type (val) &&
     value_type (val)->code () != TYPE_CODE_VOID))
@@ -2700,6 +2712,273 @@ eval_command (const char *arg, int from_tty)
   execute_command (expanded.c_str (), from_tty);
 }
 
+/* Convenience function for error checking in mtag commands.  */
+
+static void
+show_addr_not_tagged (CORE_ADDR address)
+{
+  error (_("Address %s not in a region mapped with a memory tagging flag."),
+ paddress (target_gdbarch (), address));
+}
+
+/* Convenience function for error checking in mtag commands.  */
+
+static void
+show_memtag_unsupported (void)
+{
+  error (_("Memory tagging not supported or disabled by the current"
+   " architecture."));
+}
+
+/* Implement the "mtag" prefix command.  */
+
+static void
+mtag_command (const char *arg, int from_tty)
+{
+  help_list (mtaglist, "mtag ", all_commands, gdb_stdout);
+}
+
+/* Helper for showltag and showatag.  */
+
+static void
+mtag_showtag_command (const char *args, enum memtag_type tag_type)
+{
+  if (args == nullptr)
+    error_no_arg (_("address or pointer"));
+
+  /* Parse args into a value.  If the value is a pointer or an address,
+     then fetch the logical or allocation tag.  */
+  value_print_options print_opts;
+
+  struct value *val = process_print_command_args (args, &print_opts);
+
+  /* If the address is not in a region memory mapped with a memory tagging
+     flag, it is no use trying to access/manipulate its allocation tag.
+
+     It is OK to manipulate the logical tag though.  */
+  if (tag_type == tag_allocation
+      && !gdbarch_tagged_address_p (target_gdbarch (), val))
+    show_addr_not_tagged (value_as_address (val));
+
+  std::string tag = gdbarch_memtag_to_string (target_gdbarch (),
+      val, tag_type);
+  if (tag.empty ())
+    printf_filtered (_("%s tag unavailable.\n"),
+     tag_type == tag_logical? "Logical" : "Allocation");
+
+  struct value *v_tag = process_print_command_args (tag.c_str (),
+    &print_opts);
+  print_opts.output_format = 'x';
+  print_value (v_tag, print_opts);
+}
+
+/* Implement the "mtag showltag" command.  */
+
+static void
+mtag_showltag_command (const char *args, int from_tty)
+{
+  if (!memtag || !target_supports_memory_tagging ())
+    show_memtag_unsupported ();
+
+  mtag_showtag_command (args, tag_logical);
+}
+
+/* Implement the "mtag showatag" command.  */
+
+static void
+mtag_showatag_command (const char *args, int from_tty)
+{
+  if (!memtag || !target_supports_memory_tagging ())
+    show_memtag_unsupported ();
+
+  mtag_showtag_command (args, tag_allocation);
+}
+
+/* Parse ARGS and extract ADDR and TAG.
+   ARGS should have format <expression> <tag bytes>.  */
+
+static void
+parse_setltag_input (const char *args, struct value **val,
+     gdb::byte_vector &tags, value_print_options *print_opts)
+{
+  /* Given <expression> can be reasonably complex, we parse things backwards
+     so we can isolate the <tag bytes> portion.  */
+
+  /* Fetch the address.  */
+  std::string s_address = extract_string_maybe_quoted (&args);
+
+  /* Parse the address into a value.  */
+  *val = process_print_command_args (s_address.c_str (), print_opts);
+
+  /* Fetch the tag bytes.  */
+  std::string s_tag = extract_string_maybe_quoted (&args);
+
+  /* Validate the input.  */
+  if (s_address.empty () || s_tag.empty ())
+    error (_("Missing arguments."));
+
+  if (s_tag.length () % 2)
+    error (_("Error parsing tags argument. The tag should be 2 digits."));
+
+  tags = hex2bin (s_tag.c_str ());
+}
+
+/* Implement the "mtag setltag" command.  */
+
+static void
+mtag_setltag_command (const char *args, int from_tty)
+{
+  if (!memtag || !target_supports_memory_tagging ())
+    show_memtag_unsupported ();
+
+  if (args == nullptr)
+    error_no_arg (_("<address> <tag>"));
+
+  gdb::byte_vector tags;
+  struct value *val;
+  value_print_options print_opts;
+
+  /* Parse the input.  */
+  parse_setltag_input (args, &val, tags, &print_opts);
+
+  /* Setting the logical tag is just a local operation that does not touch
+     any memory from the target.  Given an input value, we modify the value
+     to include the appropriate tag.
+
+     For this reason we need to cast the argument value to a
+     (void *) pointer.  This is so we have the right the for the gdbarch
+     hook to manipulate the value and insert the tag.
+
+     Otherwise, this would fail if, for example, GDB parsed the argument value
+     into an int-sized value and the pointer value has a type of greater
+     length.  */
+
+  /* Cast to (void *).  */
+  val = value_cast (builtin_type (target_gdbarch ())->builtin_data_ptr,
+    val);
+
+  if (gdbarch_set_memtags (target_gdbarch (), val, 0, tags,
+   tag_logical) != 0)
+    printf_filtered (_("Could not update the logical tag data.\n"));
+  else
+    {
+      /* Always print it in hex format.  */
+      print_opts.output_format = 'x';
+      print_value (val, print_opts);
+    }
+}
+
+/* Parse ARGS and extract ADDR, LENGTH and TAGS.  */
+
+static void
+parse_setatag_input (const char *args, struct value **val, size_t *length,
+     gdb::byte_vector &tags)
+{
+  /* Fetch the address.  */
+  std::string s_address = extract_string_maybe_quoted (&args);
+
+  /* Parse the address into a value.  */
+  value_print_options print_opts;
+  *val = process_print_command_args (s_address.c_str (), &print_opts);
+
+  /* Fetch the length.  */
+  std::string s_length = extract_string_maybe_quoted (&args);
+
+  /* Fetch the tag bytes.  */
+  std::string s_tags = extract_string_maybe_quoted (&args);
+
+  /* Validate the input.  */
+  if (s_address.empty () || s_length.empty () || s_tags.empty ())
+    error (_("Missing arguments."));
+
+  errno = 0;
+  *length = strtoulst (s_length.c_str (), NULL, 10);
+  if (errno != 0)
+    error (_("Error parsing length argument."));
+
+  if (s_tags.length () % 2)
+    error (_("Error parsing tags argument. Tags should be 2 digits per byte."));
+
+  tags = hex2bin (s_tags.c_str ());
+
+  /* If the address is not in a region memory mapped with a memory tagging
+     flag, it is no use trying to access/manipulate its allocation tag.  */
+  if (!gdbarch_tagged_address_p (target_gdbarch (), *val))
+    show_addr_not_tagged (value_as_address (*val));
+}
+
+/* Implement the "mtag setatag" command.
+   ARGS should be in the format <address> <length> <tags>.  */
+
+static void
+mtag_setatag_command (const char *args, int from_tty)
+{
+  if (!memtag || !target_supports_memory_tagging ())
+    show_memtag_unsupported ();
+
+  if (args == nullptr)
+    error_no_arg (_("<starting address> <length> <tag bytes>"));
+
+  gdb::byte_vector tags;
+  size_t length = 0;
+  struct value *val;
+
+  /* Parse the input.  */
+  parse_setatag_input (args, &val, &length, tags);
+
+  if (gdbarch_set_memtags (target_gdbarch (), val, length, tags,
+   tag_allocation) != 0)
+    printf_filtered (_("Could not update the allocation tag(s).\n"));
+  else
+    printf_filtered (_("Allocation tag(s) updated successfully.\n"));
+}
+
+/* Implement the "mtag check" command.  */
+
+static void
+mtag_check_command (const char *args, int from_tty)
+{
+  if (!memtag || !target_supports_memory_tagging ())
+    show_memtag_unsupported ();
+
+  if (args == nullptr)
+    error (_("Argument required (address or pointer)"));
+
+  /* Parse the expression into a value.  If the value is an address or
+     pointer, then check its logical tag against the allocation tag.  */
+  value_print_options print_opts;
+
+  struct value *val = process_print_command_args (args, &print_opts);
+
+  /* If the address is not in a region memory mapped with a memory tagging
+     flag, it is no use trying to access/manipulate its allocation tag.  */
+  if (!gdbarch_tagged_address_p (target_gdbarch (), val))
+    show_addr_not_tagged (value_as_address (val));
+
+  CORE_ADDR addr = value_as_address (val);
+
+  /* If memory tagging validation is on, check if the tag is valid.  */
+  if (gdbarch_memtag_mismatch_p (target_gdbarch (), val))
+    {
+      std::string ltag = gdbarch_memtag_to_string (target_gdbarch (),
+   val, tag_logical);
+      std::string atag = gdbarch_memtag_to_string (target_gdbarch (),
+   val, tag_allocation);
+
+      printf_filtered (_("Logical tag (%s) does not match"
+ " the allocation tag (%s) for address %s.\n"),
+       ltag.c_str (), atag.c_str (),
+       paddress (target_gdbarch (), addr));
+    }
+  else
+    {
+      std::string ltag = gdbarch_memtag_to_string (target_gdbarch (),
+   val, tag_logical);
+      printf_filtered (_("Memory tags for address %s match (%s).\n"),
+       paddress (target_gdbarch (), addr), ltag.c_str ());
+    }
+}
+
 void _initialize_printcmd ();
 void
 _initialize_printcmd ()
@@ -2910,4 +3189,60 @@ certain operations have illegal tags."),
     NULL,
     show_memtag,
     &setlist, &showlist);
+
+  /* Memory tagging commands.  */
+  add_prefix_cmd ("mtag", class_vars, mtag_command, _("\
+Generic command for showing and manipulating memory tag properties."),
+  &mtaglist, "mtag ", 0, &cmdlist);
+  add_cmd ("showltag", class_vars, mtag_showltag_command,
+   ("Show the logical tag for an address.\n\
+Usage: mtag showltag <address>.\n\
+<address> is an expression that evaluates to a pointer or memory address.\n\
+GDB will show the logical tag associated with <address>.  The tag\n\
+interpretation is architecture-specific."),
+   &mtaglist);
+  add_cmd ("showatag", class_vars, mtag_showatag_command,
+   _("Show the allocation tag for an address.\n\
+Usage: mtag showatag <address>.\n\
+<address> is an expression that evaluates to a pointer or memory address.\n\
+GDB will show the allocation tag associated with <address>.  The tag\n\
+interpretation is architecture-specific."),
+   &mtaglist);
+  add_cmd ("setltag", class_vars, mtag_setltag_command,
+   _("Set the logical tag for an address.\n\
+Usage: mtag setltag <address> <tag>\n\
+<address> is an expression that evaluates to a pointer or memory address.\n\
+<tag> is a sequence of hex bytes that will be interpreted by the\n\
+architecture as a single memory tag.\n\
+GDB will set the logical tag for <address> to <tag>, and will show the\n\
+resulting address with the updated tag."),
+   &mtaglist);
+  add_cmd ("setatag", class_vars, mtag_setatag_command,
+   _("Set the allocation tag for an address.\n\
+Usage: mtag setatag <address> <length> <tag_bytes>\n\
+<address> is an expression that evaluates to a pointer or memory address\n\
+<length> is the number of bytes that will get added to <address> to calculate\n\
+the memory range.\n\
+<tag bytes> is a sequence of hex bytes that will be interpreted by the\n\
+architecture as one or more memory tags.\n\
+Sets the tags of the memory range [<address>, <address> + <length>)\n\
+to the specified tag bytes.\n\
+\n\
+If the number of tags is greater than or equal to the number of tag granules\n\
+in the [<address>, <address> + <length) range, only the tags up to the\n\
+number of tag granules will be stored.\n\
+\n\
+If the number of tags is less than the number of tag granules, then the\n\
+command is a fill operation.  The tag bytes are interpreted as a pattern\n\
+that will get repeated until the number of tag granules in the memory range\n\
+[<address>, <address> + <length>] is stored to."),
+   &mtaglist);
+  add_cmd ("check", class_vars, mtag_check_command,
+   _("Validate the logical tag against the allocation tag.\n\
+Usage: mtag check <address>\n\
+<address> is an expression that evaluates to a pointer or memory address\n\
+GDB will fetch the logical and allocation tags for <address> and will\n\
+compare them for equality.  If the tags do not match, GDB will show\n\
+additional information about the mismatch."),
+   &mtaglist);
 }
diff --git a/gdbsupport/rsp-low.cc b/gdbsupport/rsp-low.cc
index 9bb16605dd..90b51dbb18 100644
--- a/gdbsupport/rsp-low.cc
+++ b/gdbsupport/rsp-low.cc
@@ -32,7 +32,7 @@ fromhex (int a)
   else if (a >= 'A' && a <= 'F')
     return a - 'A' + 10;
   else
-    error (_("Reply contains invalid hex digit %d"), a);
+    error (_("Invalid hex digit %d"), a);
 }
 
 /* See rsp-low.h.  */
--
2.17.1

Reply | Threaded
Open this post in threaded view
|

[PATCH 19/23] Documentation for the new mtag commands

Sourceware - gdb-patches mailing list
In reply to this post by Sourceware - gdb-patches mailing list
Document the new "mtag" command prefix and all of its subcommands.

gdb/doc/ChangeLog:

YYYY-MM-DD  Luis Machado  <[hidden email]>

        * gdb.textinfo (Memory Tagging): New subsection.
        (AArch64 Memory Tagging Extension): New subsection.
---
 gdb/doc/gdb.texinfo | 60 +++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index bc610e44cd..4156d1d70a 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -10806,6 +10806,49 @@ target supports computing the CRC checksum of a block of memory
 (@pxref{qCRC packet}).
 @end table
 
+@subsection Memory Tagging
+
+Memory tagging is a memory protection technology that validates accesses
+through pointers via a tag.  Both the pointer tag and the memory tag in the
+physical address space must match for the memory access to be validated.
+
+There are two types of tags: logical and allocation.  The logical tag is
+stored in the pointers themselves.  The allocation tag is the tag associated
+with the physical address space, against which the logical tags from pointers
+are validated.
+
+If the underlying architecture supports memory tagging, like AArch64,
+@value{GDBN} can make use of it to validate addresses and pointers against
+memory allocation tags.
+
+A command prefix of @code{mtag} gives access to the various memory tagging
+commands.
+
+The @code{mtag} commands are the following:
+
+@table @code
+@kindex mtag showltag
+@item mtag showltag @var{address_expression}
+Show the logical tag contained in the pointer resulting from evaluating the
+argument expression.
+@kindex mtag setltag
+@item mtag setltag @var{address_expression} @var{tag_bytes}
+Print the resulting pointer from evaluating the argument expression with a
+logical tag of @var{tag_bytes}.
+@kindex mtag showatag
+@item mtag showatag @var{address_expression}
+Show the allocation tag from the memory address pointed to by the evaluation
+of the argument expression.
+@kindex mtag setatag
+@item mtag setatag @var{starting_address} @var{length} @var{tag_bytes}
+Set the allocation tag for memory range @r{[}@var{starting_address},
+@var{starting_address} + @var{length}@r{)} to @var{tag_bytes}.
+@kindex mtag check
+@item mtag check @var{address_expression}
+Given the pointer resulting from evaluating the argument expression,  check that
+the logical tag and the allocation tags match.
+@end table
+
 @node Auto Display
 @section Automatic Display
 @cindex automatic display
@@ -24846,6 +24889,23 @@ When GDB prints a backtrace, any addresses that required unmasking will be
 postfixed with the marker [PAC].  When using the MI, this is printed as part
 of the @code{addr_flags} field.
 
+@subsubsection AArch64 Memory Tagging Extension.
+@cindex AArch64 Memory Tagging Extension.
+
+When @value{GDBN} is debugging the AArch64 architecture, the program is
+using the v8.5-A feature Memory Tagging Extension (MTE) and there is support
+in the kernel for MTE, @value{GDBN} will make memory tagging functionality
+available for inspection and editing of logical and allocation tags.
+
+To aid debugging, @value{GDBN} will output additional information when SIGSEGV
+signals are generated as a result of memory tag failures.
+
+A new register, @code{tag_ctl}, is made available through the
+@code{org.gnu.gdb.aarch64.mte} feature.  This register exposes some
+options that can be controlled at runtime and emulates the @code{prctl}
+option @code{PR_SET_TAGGED_ADDR_CTRL}.  For further information, see the
+documentation in the Linux kernel.
+
 @node i386
 @subsection x86 Architecture-specific Issues
 
--
2.17.1

123