Automatic date update in version.in
[deliverable/binutils-gdb.git] / gdb / printcmd.c
index 850890644aafd5bfe54410449aec197d6ff68257..416b87c69c6f575a7f1a8398e22db2a7c305772f 100644 (file)
 #include "gdbsupport/byte-vector.h"
 #include "gdbsupport/gdb_optional.h"
 #include "safe-ctype.h"
+#include "gdbsupport/rsp-low.h"
+
+/* Chain containing all defined memory-tag subcommands.  */
+
+static struct cmd_list_element *memory_tag_list;
 
 /* Last specified output format.  */
 
@@ -67,6 +72,10 @@ static char last_size = 'w';
 
 static int last_count;
 
+/* Last specified tag-printing option.  */
+
+static bool last_print_tags = false;
+
 /* Default address to examine next, and associated architecture.  */
 
 static struct gdbarch *next_gdbarch;
@@ -188,6 +197,7 @@ decode_format (const char **string_ptr, int oformat, int osize)
   val.size = '?';
   val.count = 1;
   val.raw = 0;
+  val.print_tags = false;
 
   if (*p == '-')
     {
@@ -210,6 +220,11 @@ decode_format (const char **string_ptr, int oformat, int osize)
          val.raw = 1;
          p++;
        }
+      else if (*p == 'm')
+       {
+         val.print_tags = true;
+         p++;
+       }
       else if (*p >= 'a' && *p <= 'z')
        val.format = *p++;
       else
@@ -493,7 +508,7 @@ print_scalar_formatted (const gdb_byte *valaddr, struct type *type,
        opts.format = 0;
        if (type->is_unsigned ())
          type = builtin_type (gdbarch)->builtin_true_unsigned_char;
-       else
+       else
          type = builtin_type (gdbarch)->builtin_true_char;
 
        value_print (value_from_longest (type, *val_long), stream, &opts);
@@ -1095,12 +1110,50 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
       need_to_update_next_address = 1;
     }
 
+  /* Whether we need to print the memory tag information for the current
+     address range.  */
+  bool print_range_tag = true;
+  uint32_t gsize = gdbarch_memtag_granule_size (gdbarch);
+
   /* Print as many objects as specified in COUNT, at most maxelts per line,
      with the address of the next one at the start of each line.  */
 
   while (count > 0)
     {
       QUIT;
+
+      CORE_ADDR tag_laddr = 0, tag_haddr = 0;
+
+      /* Print the memory tag information if requested.  */
+      if (fmt.print_tags && print_range_tag
+         && target_supports_memory_tagging ())
+       {
+         tag_laddr = align_down (next_address, gsize);
+         tag_haddr = align_down (next_address + gsize, gsize);
+
+         struct value *v_addr
+           = value_from_ulongest (builtin_type (gdbarch)->builtin_data_ptr,
+                                  tag_laddr);
+
+         if (gdbarch_tagged_address_p (target_gdbarch (), v_addr))
+           {
+             /* Fetch the allocation tag.  */
+             struct value *tag
+               = gdbarch_get_memtag (gdbarch, v_addr, memtag_type::allocation);
+             std::string atag
+               = gdbarch_memtag_to_string (gdbarch, tag);
+
+             if (!atag.empty ())
+               {
+                 printf_filtered (_("<Allocation Tag %s for range [%s,%s)>\n"),
+                                  atag.c_str (),
+                                  paddress (gdbarch, tag_laddr),
+                                  paddress (gdbarch, tag_haddr));
+               }
+           }
+         print_range_tag = false;
+       }
+
       if (format == 'i')
        fputs_filtered (pc_prefix (next_address), gdb_stdout);
       print_address (next_gdbarch, next_address, gdb_stdout);
@@ -1131,6 +1184,11 @@ do_examine (struct format_data fmt, struct gdbarch *gdbarch, CORE_ADDR addr)
          /* Display any branch delay slots following the final insn.  */
          if (format == 'i' && count == 1)
            count += branch_delay_insns;
+
+         /* Update the tag range based on the current address being
+            processed.  */
+         if (tag_haddr <= next_address)
+             print_range_tag = true;
        }
       printf_filtered ("\n");
     }
@@ -1203,21 +1261,46 @@ print_value (value *val, const value_print_options &opts)
   annotate_value_history_end ();
 }
 
-/* Implementation of the "print" and "call" commands.  */
+/* Returns true if memory tags should be validated.  False otherwise.  */
 
-static void
-print_command_1 (const char *args, bool voidprint)
+static bool
+should_validate_memtags (struct value *value)
 {
-  struct value *val;
-  value_print_options print_opts;
+  gdb_assert (value != nullptr && value_type (value) != nullptr);
+
+  if (!target_supports_memory_tagging ())
+    return false;
+
+  enum type_code code = value_type (value)->code ();
+
+  /* Skip non-address values.  */
+  if (code != TYPE_CODE_PTR
+      && !TYPE_IS_REFERENCE (value_type (value)))
+    return false;
+
+  /* OK, we have an address value.  Check we have a complete value we
+     can extract.  */
+  if (value_optimized_out (value)
+      || !value_entirely_available (value))
+    return false;
 
-  get_user_print_options (&print_opts);
+  /* We do.  Check whether it includes any tags.  */
+  return gdbarch_tagged_address_p (target_gdbarch (), value);
+}
+
+/* Helper for parsing arguments for print_command_1.  */
+
+static struct value *
+process_print_command_args (const char *args, value_print_options *print_opts,
+                           bool voidprint)
+{
+  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;
 
@@ -1226,14 +1309,63 @@ print_command_1 (const char *args, bool voidprint)
       /* VOIDPRINT is true to indicate that we do want to print a void
         value, so invert it for parse_expression.  */
       expression_up expr = parse_expression (exp, nullptr, !voidprint);
-      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, voidprint);
 
   if (voidprint || (val && value_type (val) &&
                    value_type (val)->code () != TYPE_CODE_VOID))
-    print_value (val, print_opts);
+    {
+      /* If memory tagging validation is on, check if the tag is valid.  */
+      if (print_opts.memory_tag_violations)
+       {
+         try
+           {
+             if (should_validate_memtags (val)
+                 && !gdbarch_memtag_matches_p (target_gdbarch (), val))
+               {
+                 /* Fetch the logical tag.  */
+                 struct value *tag
+                   = gdbarch_get_memtag (target_gdbarch (), val,
+                                         memtag_type::logical);
+                 std::string ltag
+                   = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+                 /* Fetch the allocation tag.  */
+                 tag = gdbarch_get_memtag (target_gdbarch (), val,
+                                           memtag_type::allocation);
+                 std::string atag
+                   = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+                 printf_filtered (_("Logical tag (%s) does not match the "
+                                    "allocation tag (%s).\n"),
+                                  ltag.c_str (), atag.c_str ());
+               }
+           }
+         catch (gdb_exception_error &ex)
+           {
+             if (ex.error == TARGET_CLOSE_ERROR)
+               throw;
+
+             fprintf_filtered (gdb_stderr,
+                               _("Could not validate memory tag: %s\n"),
+                               ex.message->c_str ());
+           }
+       }
+
+      print_value (val, print_opts);
+    }
 }
 
 /* Called from command completion function to skip over /FMT
@@ -1416,8 +1548,7 @@ info_symbol_command (const char *arg, int from_tty)
 
        sect_addr = overlay_mapped_address (addr, osect);
 
-       if (obj_section_addr (osect) <= sect_addr
-           && sect_addr < obj_section_endaddr (osect)
+       if (osect->addr () <= sect_addr && sect_addr < osect->endaddr ()
            && (msymbol
                = lookup_minimal_symbol_by_pc_section (sect_addr,
                                                       osect).minsym))
@@ -1728,6 +1859,7 @@ x_command (const char *exp, int from_tty)
   struct value *val;
 
   fmt.format = last_format ? last_format : 'x';
+  fmt.print_tags = last_print_tags;
   fmt.size = last_size;
   fmt.count = 1;
   fmt.raw = 0;
@@ -1784,6 +1916,9 @@ x_command (const char *exp, int from_tty)
     last_size = fmt.size;
   last_format = fmt.format;
 
+  /* Remember tag-printing setting.  */
+  last_print_tags = fmt.print_tags;
+
   /* Set a couple of internal variables if appropriate.  */
   if (last_examine_value != nullptr)
     {
@@ -2274,7 +2409,7 @@ print_variable_and_value (const char *name, struct symbol *var,
       val = read_var_value (var, NULL, frame);
       get_user_print_options (&opts);
       opts.deref_ref = 1;
-      common_val_print (val, stream, indent, &opts, current_language);
+      common_val_print_checked (val, stream, indent, &opts, current_language);
 
       /* common_val_print invalidates FRAME when a pretty printer calls inferior
         function.  */
@@ -2778,6 +2913,293 @@ eval_command (const char *arg, int from_tty)
   execute_command (expanded.c_str (), from_tty);
 }
 
+/* Convenience function for error checking in memory-tag 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 memory-tag commands.  */
+
+static void
+show_memory_tagging_unsupported (void)
+{
+  error (_("Memory tagging not supported or disabled by the current"
+          " architecture."));
+}
+
+/* Implement the "memory-tag" prefix command.  */
+
+static void
+memory_tag_command (const char *arg, int from_tty)
+{
+  help_list (memory_tag_list, "memory-tag ", all_commands, gdb_stdout);
+}
+
+/* Helper for print-logical-tag and print-allocation-tag.  */
+
+static void
+memory_tag_print_tag_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, true);
+
+  /* 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 == memtag_type::allocation
+      && !gdbarch_tagged_address_p (target_gdbarch (), val))
+    show_addr_not_tagged (value_as_address (val));
+
+  struct value *tag_value
+    = gdbarch_get_memtag (target_gdbarch (), val, tag_type);
+  std::string tag = gdbarch_memtag_to_string (target_gdbarch (), tag_value);
+
+  if (tag.empty ())
+    printf_filtered (_("%s tag unavailable.\n"),
+                    tag_type
+                      == memtag_type::logical? "Logical" : "Allocation");
+
+  struct value *v_tag = process_print_command_args (tag.c_str (),
+                                                   &print_opts,
+                                                   true);
+  print_opts.output_format = 'x';
+  print_value (v_tag, print_opts);
+}
+
+/* Implement the "memory-tag print-logical-tag" command.  */
+
+static void
+memory_tag_print_logical_tag_command (const char *args, int from_tty)
+{
+  if (!target_supports_memory_tagging ())
+    show_memory_tagging_unsupported ();
+
+  memory_tag_print_tag_command (args, memtag_type::logical);
+}
+
+/* Implement the "memory-tag print-allocation-tag" command.  */
+
+static void
+memory_tag_print_allocation_tag_command (const char *args, int from_tty)
+{
+  if (!target_supports_memory_tagging ())
+    show_memory_tagging_unsupported ();
+
+  memory_tag_print_tag_command (args, memtag_type::allocation);
+}
+
+/* Parse ARGS and extract ADDR and TAG.
+   ARGS should have format <expression> <tag bytes>.  */
+
+static void
+parse_with_logical_tag_input (const char *args, struct value **val,
+                             gdb::byte_vector &tags,
+                             value_print_options *print_opts)
+{
+  /* Fetch the address.  */
+  std::string address_string = extract_string_maybe_quoted (&args);
+
+  /* Parse the address into a value.  */
+  *val = process_print_command_args (address_string.c_str (), print_opts,
+                                    true);
+
+  /* Fetch the tag bytes.  */
+  std::string tag_string = extract_string_maybe_quoted (&args);
+
+  /* Validate the input.  */
+  if (address_string.empty () || tag_string.empty ())
+    error (_("Missing arguments."));
+
+  if (tag_string.length () != 2)
+    error (_("Error parsing tags argument. The tag should be 2 digits."));
+
+  tags = hex2bin (tag_string.c_str ());
+}
+
+/* Implement the "memory-tag with-logical-tag" command.  */
+
+static void
+memory_tag_with_logical_tag_command (const char *args, int from_tty)
+{
+  if (!target_supports_memory_tagging ())
+    show_memory_tagging_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_with_logical_tag_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 type 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);
+
+  /* Length doesn't matter for a logical tag.  Pass 0.  */
+  if (!gdbarch_set_memtags (target_gdbarch (), val, 0, tags,
+                           memtag_type::logical))
+    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_set_allocation_tag_input (const char *args, struct value **val,
+                               size_t *length, gdb::byte_vector &tags)
+{
+  /* Fetch the address.  */
+  std::string address_string = extract_string_maybe_quoted (&args);
+
+  /* Parse the address into a value.  */
+  value_print_options print_opts;
+  *val = process_print_command_args (address_string.c_str (), &print_opts,
+                                    true);
+
+  /* Fetch the length.  */
+  std::string length_string = extract_string_maybe_quoted (&args);
+
+  /* Fetch the tag bytes.  */
+  std::string tags_string = extract_string_maybe_quoted (&args);
+
+  /* Validate the input.  */
+  if (address_string.empty () || length_string.empty () || tags_string.empty ())
+    error (_("Missing arguments."));
+
+  errno = 0;
+  const char *trailer = nullptr;
+  LONGEST parsed_length = strtoulst (length_string.c_str (), &trailer, 10);
+
+  if (errno != 0 || (trailer != nullptr && trailer[0] != '\0'))
+    error (_("Error parsing length argument."));
+
+  if (parsed_length <= 0)
+    error (_("Invalid zero or negative length."));
+
+  *length = parsed_length;
+
+  if (tags_string.length () % 2)
+    error (_("Error parsing tags argument. Tags should be 2 digits per byte."));
+
+  tags = hex2bin (tags_string.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 "memory-tag set-allocation-tag" command.
+   ARGS should be in the format <address> <length> <tags>.  */
+
+static void
+memory_tag_set_allocation_tag_command (const char *args, int from_tty)
+{
+  if (!target_supports_memory_tagging ())
+    show_memory_tagging_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_set_allocation_tag_input (args, &val, &length, tags);
+
+  if (!gdbarch_set_memtags (target_gdbarch (), val, length, tags,
+                           memtag_type::allocation))
+    printf_filtered (_("Could not update the allocation tag(s).\n"));
+  else
+    printf_filtered (_("Allocation tag(s) updated successfully.\n"));
+}
+
+/* Implement the "memory-tag check" command.  */
+
+static void
+memory_tag_check_command (const char *args, int from_tty)
+{
+  if (!target_supports_memory_tagging ())
+    show_memory_tagging_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, true);
+
+  /* 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);
+
+  /* Check if the tag is valid.  */
+  if (!gdbarch_memtag_matches_p (target_gdbarch (), val))
+    {
+      struct value *tag
+       = gdbarch_get_memtag (target_gdbarch (), val, memtag_type::logical);
+      std::string ltag
+       = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+      tag = gdbarch_get_memtag (target_gdbarch (), val,
+                               memtag_type::allocation);
+      std::string atag
+       = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+      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
+    {
+      struct value *tag
+       = gdbarch_get_memtag (target_gdbarch (), val, memtag_type::logical);
+      std::string ltag
+       = gdbarch_memtag_to_string (target_gdbarch (), tag);
+
+      printf_filtered (_("Memory tags for address %s match (%s).\n"),
+                      paddress (target_gdbarch (), addr), ltag.c_str ());
+    }
+}
+
 void _initialize_printcmd ();
 void
 _initialize_printcmd ()
@@ -2786,7 +3208,8 @@ _initialize_printcmd ()
 
   current_display_number = -1;
 
-  gdb::observers::free_objfile.attach (clear_dangling_display_expressions);
+  gdb::observers::free_objfile.attach (clear_dangling_display_expressions,
+                                      "printcmd");
 
   add_info ("address", info_address_command,
            _("Describe where symbol SYM is stored.\n\
@@ -2880,7 +3303,7 @@ Use \"set variable\" for variables with names identical to set subcommands.\n\
 \n\
 With a subcommand, this command modifies parts of the gdb environment.\n\
 You can see these environment settings with the \"show\" command."),
-                 &setlist, "set ", 1, &cmdlist);
+                 &setlist, 1, &cmdlist);
   if (dbx_commands)
     add_com ("assign", class_vars, set_command, _("\
 Evaluate expression EXP and assign result to variable VAR.\n\
@@ -2903,7 +3326,8 @@ current working language.  The result is printed and saved in the value\n\
 history, if it is not void."));
   set_cmd_completer_handle_brkchars (c, print_command_completer);
 
-  add_cmd ("variable", class_vars, set_command, _("\
+  cmd_list_element *set_variable_cmd
+    = add_cmd ("variable", class_vars, set_command, _("\
 Evaluate expression EXP and assign result to variable VAR.\n\
 Usage: set variable VAR = EXP\n\
 This uses assignment syntax appropriate for the current language\n\
@@ -2912,8 +3336,8 @@ VAR may be a debugger \"convenience\" variable (names starting\n\
 with $), a register (a few standard names starting with $), or an actual\n\
 variable in the program being debugged.  EXP is any valid expression.\n\
 This may usually be abbreviated to simply \"set\"."),
-          &setlist);
-  add_alias_cmd ("var", "variable", class_vars, 0, &setlist);
+              &setlist);
+  add_alias_cmd ("var", set_variable_cmd, class_vars, 0, &setlist);
 
   const auto print_opts = make_value_print_options_def_group (nullptr);
 
@@ -2950,10 +3374,11 @@ EXP may be preceded with /FMT, where FMT is a format letter\n\
 but no count or size letter (see \"x\" command)."),
                                              print_opts);
 
-  c = add_com ("print", class_vars, print_command, print_help.c_str ());
-  set_cmd_completer_handle_brkchars (c, print_command_completer);
-  add_com_alias ("p", "print", class_vars, 1);
-  add_com_alias ("inspect", "print", class_vars, 1);
+  cmd_list_element *print_cmd
+    = add_com ("print", class_vars, print_command, print_help.c_str ());
+  set_cmd_completer_handle_brkchars (print_cmd, print_command_completer);
+  add_com_alias ("p", print_cmd, class_vars, 1);
+  add_com_alias ("inspect", print_cmd, class_vars, 1);
 
   add_setshow_uinteger_cmd ("max-symbolic-offset", no_class,
                            &max_symbolic_offset, _("\
@@ -2980,4 +3405,61 @@ 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."));
+
+  /* Memory tagging commands.  */
+  add_prefix_cmd ("memory-tag", class_vars, memory_tag_command, _("\
+Generic command for printing and manipulating memory tag properties."),
+                 &memory_tag_list, 0, &cmdlist);
+  add_cmd ("print-logical-tag", class_vars,
+          memory_tag_print_logical_tag_command,
+          ("Print the logical tag from POINTER.\n\
+Usage: memory-tag print-logical-tag <POINTER>.\n\
+<POINTER> is an expression that evaluates to a pointer.\n\
+Print the logical tag contained in POINTER.  The tag interpretation is\n\
+architecture-specific."),
+          &memory_tag_list);
+  add_cmd ("print-allocation-tag", class_vars,
+          memory_tag_print_allocation_tag_command,
+          _("Print the allocation tag for ADDRESS.\n\
+Usage: memory-tag print-allocation-tag <ADDRESS>.\n\
+<ADDRESS> is an expression that evaluates to a memory address.\n\
+Print the allocation tag associated with the memory address ADDRESS.\n\
+The tag interpretation is architecture-specific."),
+          &memory_tag_list);
+  add_cmd ("with-logical-tag", class_vars, memory_tag_with_logical_tag_command,
+          _("Print a POINTER with a specific logical TAG.\n\
+Usage: memory-tag with-logical-tag <POINTER> <TAG>\n\
+<POINTER> is an expression that evaluates to a pointer.\n\
+<TAG> is a sequence of hex bytes that is interpreted by the architecture\n\
+as a single memory tag."),
+          &memory_tag_list);
+  add_cmd ("set-allocation-tag", class_vars,
+          memory_tag_set_allocation_tag_command,
+          _("Set the allocation tag(s) for a memory range.\n\
+Usage: memory-tag set-allocation-tag <ADDRESS> <LENGTH> <TAG_BYTES>\n\
+<ADDRESS> is an expression that evaluates to a memory address\n\
+<LENGTH> is the number of bytes that is added to <ADDRESS> to calculate\n\
+the memory range.\n\
+<TAG_BYTES> is a sequence of hex bytes that is interpreted by the\n\
+architecture as one or more memory tags.\n\
+Sets the tags of the memory range [ADDRESS, ADDRESS + LENGTH)\n\
+to 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 are updated.\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 gets repeated until the number of tag granules in the memory range\n\
+[ADDRESS, ADDRESS + LENGTH) is updated."),
+          &memory_tag_list);
+  add_cmd ("check", class_vars, memory_tag_check_command,
+          _("Validate a pointer's logical tag against the allocation tag.\n\
+Usage: memory-tag check <POINTER>\n\
+<POINTER> is an expression that evaluates to a pointer\n\
+Fetch the logical and allocation tags for POINTER and compare them\n\
+for equality.  If the tags do not match, print additional information about\n\
+the tag mismatch."),
+          &memory_tag_list);
 }
This page took 0.02961 seconds and 4 git commands to generate.