Fix printing of non-address types when memory tagging is enabled
authorLuis Machado <luis.machado@linaro.org>
Tue, 20 Jul 2021 10:29:28 +0000 (07:29 -0300)
committerLuis Machado <luis.machado@linaro.org>
Tue, 20 Jul 2021 10:29:28 +0000 (07:29 -0300)
When the architecture supports memory tagging, we handle
pointer/reference types in a special way, so we can validate tags and
show mismatches.

Unfortunately, the currently implementation errors out when the user
prints non-address values: composite types, floats, references, member
functions and other things.

Vector registers:

 (gdb) p $v0
 Value can't be converted to integer.

Non-existent internal variables:

 (gdb) p $foo
 Value can't be converted to integer.

The same happens for complex types and printing struct/union types.

There are a few problems here.

The first one is that after print_command_1 evaluates the expression
to print, the tag validation code call value_as_address
unconditionally, without making sure we have have a suitable type
where it makes to sense to call it.  That results in value_as_address
(if it isn't given a pointer-like type) trying to treat the value as
an integer and convert it to an address, which #1 - doesn't make sense
(i.e., no sense in validating tags after "print 1"), and throws for
non-integer-convertible types.  We fix this by making sure we have a
pointer or reference type first, and only if so then proceed to check
if the address-like value has tags.

The second is that we're calling value_as_address even if we have an
optimized out or unavailable value, which throws, because the value's
contents aren't fully accessible/readable.  This error currently
escapes out and aborts the print.  This case is fixed by checking for
optimized out / unavailable explicitly.

Third, the tag checking process does not gracefully handle exceptions.
If any exception is thrown from the tag validation code, we abort the
print.  E.g., the target may fail to access tags via a running thread.
Or the needed /proc files aren't available.  Or some other untold
reason.  This is a bit too rigid.  This commit changes print_command_1
to catch errors, print them, and still continue with the normal
expression printing path instead of erroring out and printing nothing
useful.

With this patch, printing works correctly again:

 (gdb) p $v0
 $1 = {d = {f = {2.0546950501119882e-81, 2.0546950501119882e-81}, u = {33999881233896036313399988123389603631}, s = {
       33999881233896036313399988123389603631}}, s = {f = {1.59329203e-10, 1.59329203e-10, 1.59329203e-10, 1.59329203e-10}, u = {
       791621423791621423791621423791621423}, s = {791621423791621423791621423791621423}}, h = {bf = {1.592e-10,
       1.592e-10, 1.592e-10, 1.592e-10, 1.592e-10, 1.592e-10, 1.592e-10, 1.592e-10}, f = {0.11224, 0.11224, 0.11224, 0.11224, 0.11224,
       0.11224, 0.11224, 0.11224}, u = {12079, 12079, 12079, 12079, 12079, 12079, 12079, 12079}, s = {12079, 12079, 12079, 12079,
       12079, 12079, 12079, 12079}}, b = {u = {47 <repeats 16 times>}, s = {47 <repeats 16 times>}}, q = {u = {
       62718710765820030520700417840365121327}, s = {62718710765820030520700417840365121327}}}
 (gdb) p $foo
 $2 = void
 (gdb) p 2 + 2i
 $3 = 2 + 2i

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=28110

gdb/gdbarch.h
gdb/gdbarch.sh
gdb/printcmd.c

index ece765b826fadbc0d39c915dada1b605c75ae6a0..7db3e36d76aabfd6b47c67890b0c47e0c46b8c4f 100644 (file)
@@ -730,7 +730,8 @@ typedef std::string (gdbarch_memtag_to_string_ftype) (struct gdbarch *gdbarch, s
 extern std::string gdbarch_memtag_to_string (struct gdbarch *gdbarch, struct value *tag);
 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. */
+/* Return true if ADDRESS contains a tag and false otherwise.  ADDRESS
+   must be either a pointer or a reference type. */
 
 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);
index d9332c2103e29fc6fec60de398b5f802b3457982..9bc9de91c30bdfb30804a50eb84fdee4ff0a6740 100755 (executable)
@@ -608,7 +608,8 @@ v;int;significant_addr_bit;;;;;;0
 # Return a string representation of the memory tag TAG.
 m;std::string;memtag_to_string;struct value *tag;tag;;default_memtag_to_string;;0
 
-# Return true if ADDRESS contains a tag and false otherwise.
+# Return true if ADDRESS contains a tag and false otherwise.  ADDRESS
+# must be either a pointer or a reference type.
 m;bool;tagged_address_p;struct value *address;address;;default_tagged_address_p;;0
 
 # Return true if the tag from ADDRESS matches the memory tag for that
index 3cd42f817f5164a13d2b2cbe794d4744f81b14ac..416b87c69c6f575a7f1a8398e22db2a7c305772f 100644 (file)
@@ -1266,19 +1266,26 @@ print_value (value *val, const value_print_options &opts)
 static bool
 should_validate_memtags (struct value *value)
 {
-  if (target_supports_memory_tagging ()
-      && gdbarch_tagged_address_p (target_gdbarch (), value))
-    {
-      gdb_assert (value != nullptr && value_type (value) != nullptr);
+  gdb_assert (value != nullptr && value_type (value) != nullptr);
 
-      enum type_code code = value_type (value)->code ();
+  if (!target_supports_memory_tagging ())
+    return false;
 
-      return (code == TYPE_CODE_PTR
-             || code == TYPE_CODE_REF
-             || code == TYPE_CODE_METHODPTR
-             || code == TYPE_CODE_MEMBERPTR);
-    }
-  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;
+
+  /* We do.  Check whether it includes any tags.  */
+  return gdbarch_tagged_address_p (target_gdbarch (), value);
 }
 
 /* Helper for parsing arguments for print_command_1.  */
@@ -1321,26 +1328,42 @@ print_command_1 (const char *args, int voidprint)
                    value_type (val)->code () != TYPE_CODE_VOID))
     {
       /* If memory tagging validation is on, check if the tag is valid.  */
-      if (print_opts.memory_tag_violations && should_validate_memtags (val)
-         && !gdbarch_memtag_matches_p (target_gdbarch (), val))
+      if (print_opts.memory_tag_violations)
        {
-         /* 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 ());
+         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);
     }
 }