PR22397, BFD internal error when message locale isn't C
authorAlan Modra <amodra@gmail.com>
Sun, 5 Nov 2017 05:52:55 +0000 (16:22 +1030)
committerAlan Modra <amodra@gmail.com>
Sun, 5 Nov 2017 08:23:10 +0000 (18:53 +1030)
This adds positional parameter support to the bfd error handler,
something that was lost 2017-04-13 when _doprnt was added with commit
c08bb8dd.  The number of format args is now limited to 9, which is
sufficient for current _bfd_error_handler messages.  If someone
exceeds 9 args they get the joy of modifying this code to support more
args (shouldn't be too difficult).

PR 22397
* bfd.c (union _bfd_doprnt_args): New.
(PRINT_TYPE): Add FIELD arg.  Take value from args.
(_bfd_doprnt): Replace ap parameter with args.  Adjust all
PRINT_TYPE invocations and reading of format args to suit.
Move "%%" handling out of switch handling args.  Support
positional parameters.
(_bfd_doprnt_scan): New function.
(error_handler_internal): Call _bfd_doprnt_scan and read args.

bfd/ChangeLog
bfd/bfd.c

index 9a98d2a..80d14ae 100644 (file)
@@ -1,3 +1,15 @@
+2017-11-05  Alan Modra  <amodra@gmail.com>
+
+       PR 22397
+       * bfd.c (union _bfd_doprnt_args): New.
+       (PRINT_TYPE): Add FIELD arg.  Take value from args.
+       (_bfd_doprnt): Replace ap parameter with args.  Adjust all
+       PRINT_TYPE invocations and reading of format args to suit.
+       Move "%%" handling out of switch handling args.  Support
+       positional parameters.
+       (_bfd_doprnt_scan): New function.
+       (error_handler_internal): Call _bfd_doprnt_scan and read args.
+
 2017-11-04  Alan Modra  <amodra@gmail.com>
 
        * elf32-ppc.c (got_entries_needed, got_relocs_needed): New functions.
index 7d6185f..006fb2b 100644 (file)
--- a/bfd/bfd.c
+++ b/bfd/bfd.c
@@ -626,25 +626,47 @@ CODE_FRAGMENT
 
 static const char *_bfd_error_program_name;
 
-/* This macro and _bfd_doprnt (originally _doprint) taken from
-   libiberty _doprnt.c, tidied a little and extended to handle '%A'
-   and '%B'.  'L' as a modifer for integer formats is used for bfd_vma
-   and bfd_size_type args, which vary in size depending on BFD
+/* Support for positional parameters.  */
+
+union _bfd_doprnt_args
+{
+  int i;
+  long l;
+  long long ll;
+  double d;
+  long double ld;
+  void *p;
+  enum
+  {
+    Int,
+    Long,
+    LongLong,
+    Double,
+    LongDouble,
+    Ptr
+  } type;
+};
+
+/* This macro and _bfd_doprnt taken from libiberty _doprnt.c, tidied a
+   little and extended to handle '%A', '%B' and positional parameters.
+   'L' as a modifer for integer formats is used for bfd_vma and
+   bfd_size_type args, which vary in size depending on BFD
    configuration.  */
 
-#define PRINT_TYPE(TYPE) \
+#define PRINT_TYPE(TYPE, FIELD) \
   do                                                           \
     {                                                          \
-      TYPE value = va_arg (ap, TYPE);                          \
+      TYPE value = (TYPE) args[arg_no].FIELD;                  \
       result = fprintf (stream, specifier, value);             \
     } while (0)
 
 static int
-_bfd_doprnt (FILE *stream, const char *format, va_list ap)
+_bfd_doprnt (FILE *stream, const char *format, union _bfd_doprnt_args *args)
 {
   const char *ptr = format;
   char specifier[128];
   int total_printed = 0;
+  unsigned int arg_count = 0;
 
   while (*ptr != '\0')
     {
@@ -660,39 +682,75 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
            result = fprintf (stream, "%s", ptr);
          ptr += result;
        }
+      else if (ptr[1] == '%')
+       {
+         fputc ('%', stream);
+         result = 1;
+         ptr += 2;
+       }
       else
        {
          /* We have a format specifier!  */
          char *sptr = specifier;
          int wide_width = 0, short_width = 0;
+         unsigned int arg_no;
 
          /* Copy the % and move forward.  */
          *sptr++ = *ptr++;
 
+         /* Check for a positional parameter.  */
+         arg_no = -1u;
+         if (*ptr != '0' && ISDIGIT (*ptr) && ptr[1] == '$')
+           {
+             arg_no = *ptr - '1';
+             ptr += 2;
+           }
+
          /* Move past flags.  */
          while (strchr ("-+ #0", *ptr))
            *sptr++ = *ptr++;
 
          if (*ptr == '*')
            {
-             int value = abs (va_arg (ap, int));
-             sptr += sprintf (sptr, "%d", value);
+             int value;
+             unsigned int arg_index;
+
              ptr++;
+             arg_index = arg_count;
+             if (*ptr != '0' && ISDIGIT (*ptr) && ptr[1] == '$')
+               {
+                 arg_index = *ptr - '1';
+                 ptr += 2;
+               }
+             value = abs (args[arg_index].i);
+             arg_count++;
+             sptr += sprintf (sptr, "%d", value);
            }
          else
            /* Handle explicit numeric value.  */
            while (ISDIGIT (*ptr))
              *sptr++ = *ptr++;
 
+         /* Precision.  */
          if (*ptr == '.')
            {
              /* Copy and go past the period.  */
              *sptr++ = *ptr++;
              if (*ptr == '*')
                {
-                 int value = abs (va_arg (ap, int));
-                 sptr += sprintf (sptr, "%d", value);
+                 int value;
+                 unsigned int arg_index;
+
                  ptr++;
+                 arg_index = arg_count;
+                 if (*ptr != '0' && ISDIGIT (*ptr) && ptr[1] == '$')
+                   {
+                     arg_index = *ptr - '1';
+                     ptr += 2;
+                   }
+                 value = abs (args[arg_index].i);
+                 arg_count++;
+                 sptr += sprintf (sptr, "%d", value);
                }
              else
                /* Handle explicit numeric value.  */
@@ -721,6 +779,8 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
          /* Copy the type specifier, and NULL terminate.  */
          *sptr++ = *ptr++;
          *sptr = '\0';
+         if ((int) arg_no < 0)
+           arg_no = arg_count;
 
          switch (ptr[-1])
            {
@@ -736,12 +796,12 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
                   as an int and trust the C library printf to cast it
                   to the right width.  */
                if (short_width)
-                 PRINT_TYPE (int);
+                 PRINT_TYPE (int, i);
                else
                  {
                    /* L modifier for bfd_vma or bfd_size_type may be
                       either long long or long.  */
-                   if (sptr[-2] == 'L')
+                   if (ptr[-2] == 'L')
                      {
                        sptr[-2] = 'l';
                        if (BFD_ARCH_SIZE < 64 || BFD_HOST_64BIT_LONG)
@@ -757,10 +817,10 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
                    switch (wide_width)
                      {
                      case 0:
-                       PRINT_TYPE (int);
+                       PRINT_TYPE (int, i);
                        break;
                      case 1:
-                       PRINT_TYPE (long);
+                       PRINT_TYPE (long, l);
                        break;
                      case 2:
                      default:
@@ -772,10 +832,10 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
                        *sptr = '\0';
 #endif
 #if defined (__GNUC__) || defined (HAVE_LONG_LONG)
-                       PRINT_TYPE (long long);
+                       PRINT_TYPE (long long, ll);
 #else
                        /* Fake it and hope for the best.  */
-                       PRINT_TYPE (long);
+                       PRINT_TYPE (long, l);
 #endif
                        break;
                      }
@@ -789,35 +849,32 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
            case 'G':
              {
                if (wide_width == 0)
-                 PRINT_TYPE (double);
+                 PRINT_TYPE (double, d);
                else
                  {
 #if defined (__GNUC__) || defined (HAVE_LONG_DOUBLE)
-                   PRINT_TYPE (long double);
+                   PRINT_TYPE (long double, ld);
 #else
                    /* Fake it and hope for the best.  */
-                   PRINT_TYPE (double);
+                   PRINT_TYPE (double, d);
 #endif
                  }
              }
              break;
            case 's':
-             PRINT_TYPE (char *);
+             PRINT_TYPE (char *, p);
              break;
            case 'p':
-             PRINT_TYPE (void *);
-             break;
-           case '%':
-             fputc ('%', stream);
-             result = 1;
+             PRINT_TYPE (void *, p);
              break;
            case 'A':
              {
-               asection *sec = va_arg (ap, asection *);
+               asection *sec;
                bfd *abfd;
                const char *group = NULL;
                struct coff_comdat_info *ci;
 
+               sec = (asection *) args[arg_no].p;
                if (sec == NULL)
                  /* Invoking %A with a null section pointer is an
                     internal error.  */
@@ -841,8 +898,9 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
              break;
            case 'B':
              {
-               bfd *abfd = va_arg (ap, bfd *);
+               bfd *abfd;
 
+               abfd = (bfd *) args[arg_no].p;
                if (abfd == NULL)
                  /* Invoking %B with a null bfd pointer is an
                     internal error.  */
@@ -858,6 +916,7 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
            default:
              abort();
            }
+         arg_count++;
        }
       if (result == -1)
        return -1;
@@ -867,15 +926,230 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
   return total_printed;
 }
 
+/* First pass over FORMAT to gather ARGS.  Returns number of args.  */
+
+static unsigned int
+_bfd_doprnt_scan (const char *format, union _bfd_doprnt_args *args)
+{
+  const char *ptr = format;
+  unsigned int arg_count = 0;
+
+  while (*ptr != '\0')
+    {
+      if (*ptr != '%')
+       {
+         ptr = strchr (ptr, '%');
+         if (ptr == NULL)
+           break;
+       }
+      else if (ptr[1] == '%')
+       ptr += 2;
+      else
+       {
+         int wide_width = 0, short_width = 0;
+         unsigned int arg_no;
+
+         ptr++;
+
+         /* Check for a positional parameter.  */
+         arg_no = -1u;
+         if (*ptr != '0' && ISDIGIT (*ptr) && ptr[1] == '$')
+           {
+             arg_no = *ptr - '1';
+             ptr += 2;
+           }
+
+         /* Move past flags.  */
+         while (strchr ("-+ #0", *ptr))
+           ptr++;
+
+         if (*ptr == '*')
+           {
+             unsigned int arg_index;
+
+             ptr++;
+             arg_index = arg_count;
+             if (*ptr != '0' && ISDIGIT (*ptr) && ptr[1] == '$')
+               {
+                 arg_index = *ptr - '1';
+                 ptr += 2;
+               }
+             args[arg_index].type = Int;
+             arg_count++;
+             if (arg_count > 9)
+               abort ();
+           }
+         else
+           /* Handle explicit numeric value.  */
+           while (ISDIGIT (*ptr))
+             ptr++;
+
+         /* Precision.  */
+         if (*ptr == '.')
+           {
+             ptr++;
+             if (*ptr == '*')
+               {
+                 unsigned int arg_index;
+
+                 ptr++;
+                 arg_index = arg_count;
+                 if (*ptr != '0' && ISDIGIT (*ptr) && ptr[1] == '$')
+                   {
+                     arg_index = *ptr - '1';
+                     ptr += 2;
+                   }
+                 args[arg_index].type = Int;
+                 arg_count++;
+                 if (arg_count > 9)
+                   abort ();
+               }
+             else
+               /* Handle explicit numeric value.  */
+               while (ISDIGIT (*ptr))
+                 ptr++;
+           }
+         while (strchr ("hlL", *ptr))
+           {
+             switch (*ptr)
+               {
+               case 'h':
+                 short_width = 1;
+                 break;
+               case 'l':
+                 wide_width++;
+                 break;
+               case 'L':
+                 wide_width = 2;
+                 break;
+               default:
+                 abort();
+               }
+             ptr++;
+           }
+
+         ptr++;
+         if ((int) arg_no < 0)
+           arg_no = arg_count;
+
+         switch (ptr[-1])
+           {
+           case 'd':
+           case 'i':
+           case 'o':
+           case 'u':
+           case 'x':
+           case 'X':
+           case 'c':
+             {
+               if (short_width)
+                 args[arg_no].type = Int;
+               else
+                 {
+                   if (ptr[-2] == 'L')
+                     {
+                       if (BFD_ARCH_SIZE < 64 || BFD_HOST_64BIT_LONG)
+                         wide_width = 1;
+                     }
+
+                   switch (wide_width)
+                     {
+                     case 0:
+                       args[arg_no].type = Int;
+                       break;
+                     case 1:
+                       args[arg_no].type = Long;
+                       break;
+                     case 2:
+                     default:
+#if defined (__GNUC__) || defined (HAVE_LONG_LONG)
+                       args[arg_no].type = LongLong;
+#else
+                       args[arg_no].type = Long;
+#endif
+                       break;
+                     }
+                 }
+             }
+             break;
+           case 'f':
+           case 'e':
+           case 'E':
+           case 'g':
+           case 'G':
+             {
+               if (wide_width == 0)
+                 args[arg_no].type = Double;
+               else
+                 {
+#if defined (__GNUC__) || defined (HAVE_LONG_DOUBLE)
+                   args[arg_no].type = LongDouble;
+#else
+                   args[arg_no].type = Double;
+#endif
+                 }
+             }
+             break;
+           case 's':
+           case 'p':
+           case 'A':
+           case 'B':
+             args[arg_no].type = Ptr;
+             break;
+           default:
+             abort();
+           }
+         arg_count++;
+         if (arg_count > 9)
+           abort ();
+       }
+    }
+
+  return arg_count;
+}
+
 /* This is the default routine to handle BFD error messages.
    Like fprintf (stderr, ...), but also handles some extra format specifiers.
 
-   %A section name from section.  For group components, print group name too.
-   %B file name from bfd.  For archive components, prints archive too.  */
+   %A section name from section.  For group components, prints group name too.
+   %B file name from bfd.  For archive components, prints archive too.
+
+   Beware: Only supports a maximum of 9 format arguments.  */
 
 static void
 error_handler_internal (const char *fmt, va_list ap)
 {
+  int i, arg_count;
+  union _bfd_doprnt_args args[9];
+
+  arg_count = _bfd_doprnt_scan (fmt, args);
+  for (i = 0; i < arg_count; i++)
+    {
+      switch (args[i].type)
+       {
+       case Int:
+         args[i].i = va_arg (ap, int);
+         break;
+       case Long:
+         args[i].l = va_arg (ap, long);
+         break;
+       case LongLong:
+         args[i].ll = va_arg (ap, long long);
+         break;
+       case Double:
+         args[i].d = va_arg (ap, double);
+         break;
+       case LongDouble:
+         args[i].ld = va_arg (ap, long double);
+         break;
+       case Ptr:
+         args[i].p = va_arg (ap, void *);
+         break;
+       default:
+         abort ();
+       }
+    }
+
   /* PR 4992: Don't interrupt output being sent to stdout.  */
   fflush (stdout);
 
@@ -884,7 +1158,7 @@ error_handler_internal (const char *fmt, va_list ap)
   else
     fprintf (stderr, "BFD: ");
 
-  _bfd_doprnt (stderr, fmt, ap);
+  _bfd_doprnt (stderr, fmt, args);
 
   /* On AIX, putc is implemented as a macro that triggers a -Wunused-value
      warning, so use the fputc function to avoid it.  */