Handle %I64d in format_pieces
authorTom Tromey <tromey@adacore.com>
Thu, 14 Nov 2019 22:00:19 +0000 (15:00 -0700)
committerTom Tromey <tromey@adacore.com>
Thu, 21 Nov 2019 21:39:40 +0000 (14:39 -0700)
We found a bug internally where gdb would crash while disassembling a
certain instruction.  This was tracked down to the handling of %I64d
in format_pieces.

format_pieces will convert %ll to %I64d on mingw -- so format_pieces
should also handle parsing this format.  In this patch, I've made the
parsing unconditional, since I think it is harmless to accept extra
formats.  I've also taken the opportunity to convert the length
modifier test to a "switch".

Tested internally using our failing test case.

gdb/ChangeLog
2019-11-21  Tom Tromey  <tromey@adacore.com>

* gdbsupport/format.c (format_pieces): Parse %I64d.
* unittests/format_pieces-selftests.c (test_windows_formats): New
function.
(run_tests): Call it.

Change-Id: If335c7c2fc8d01e629cd55182394a483334d79c7

gdb/ChangeLog
gdb/gdbsupport/format.c
gdb/unittests/format_pieces-selftests.c

index 02490485838a19c90859e4dbcbaf9b96385edede..0c81de4a6bc3c6b5f4d23bc403f05638ebaa2ead 100644 (file)
@@ -1,3 +1,10 @@
+2019-11-21  Tom Tromey  <tromey@adacore.com>
+
+       * gdbsupport/format.c (format_pieces): Parse %I64d.
+       * unittests/format_pieces-selftests.c (test_windows_formats): New
+       function.
+       (run_tests): Call it.
+
 2019-11-21  Peeter Joot  <peeter.joot@lzlabs.com>
 
        Byte reverse display of variables with DW_END_big, DW_END_little
index 2e2d90a92467ecda49cff7dd92e0526fa1dc9764..67daa6d6df3332c6abd8eddf2f34551494ef4c3b 100644 (file)
@@ -126,6 +126,7 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
        int seen_size_t = 0;
        int bad = 0;
        int n_int_args = 0;
+       bool seen_i64 = false;
 
        /* Skip over "%%", it will become part of a literal piece.  */
        if (*f == '%')
@@ -195,13 +196,13 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
          }
 
        /* The next part of a format specifier is a length modifier.  */
-       if (*f == 'h')
+       switch (*f)
          {
+         case 'h':
            seen_h = 1;
            f++;
-         }
-       else if (*f == 'l')
-         {
+           break;
+         case 'l':
            f++;
            lcount++;
            if (*f == 'l')
@@ -209,21 +210,18 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
                f++;
                lcount++;
              }
-         }
-       else if (*f == 'L')
-         {
+           break;
+         case 'L':
            seen_big_l = 1;
            f++;
-         }
-       /* Decimal32 modifier.  */
-       else if (*f == 'H')
-         {
+           break;
+         case 'H':
+           /* Decimal32 modifier.  */
            seen_big_h = 1;
            f++;
-         }
-       /* Decimal64 and Decimal128 modifiers.  */
-       else if (*f == 'D')
-         {
+           break;
+         case 'D':
+           /* Decimal64 and Decimal128 modifiers.  */
            f++;
 
            /* Check for a Decimal128.  */
@@ -234,13 +232,24 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
              }
            else
              seen_big_d = 1;
-         }
-       /* For size_t or ssize_t.  */
-       else if (*f == 'z')
-         {
+           break;
+         case 'z':
+           /* For size_t or ssize_t.  */
            seen_size_t = 1;
            f++;
-         }
+           break;
+         case 'I':
+           /* Support the Windows '%I64' extension, because an
+              earlier call to format_pieces might have converted %lld
+              to %I64d.  */
+           if (f[1] == '6' && f[2] == '4')
+             {
+               f += 3;
+               lcount = 2;
+               seen_i64 = true;
+             }
+           break;
+       }
 
        switch (*f)
          {
@@ -353,7 +362,7 @@ format_pieces::format_pieces (const char **arg, bool gdb_extensions)
 
        sub_start = current_substring;
 
-       if (lcount > 1 && USE_PRINTF_I64)
+       if (lcount > 1 && !seen_i64 && USE_PRINTF_I64)
          {
            /* Windows' printf does support long long, but not the usual way.
               Convert %lld to %I64d.  */
index 3971201478091d9c7e30a043a1f1637d66e75ea9..d7e97d4069f178afe2316056b46e45b01b393127 100644 (file)
@@ -119,6 +119,16 @@ test_format_int_sizes ()
     });
 }
 
+static void
+test_windows_formats ()
+{
+  check ("rc%I64d",
+    {
+     format_piece ("rc", literal_piece, 0),
+     format_piece ("%I64d", long_long_arg, 0),
+    });
+}
+
 static void
 run_tests ()
 {
@@ -126,6 +136,7 @@ run_tests ()
   test_format_specifier ();
   test_gdb_formats ();
   test_format_int_sizes ();
+  test_windows_formats ();
 }
 
 } /* namespace format_pieces */