This is the mail archive of the binutils-cvs@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[binutils-gdb] PR22397, BFD internal error when message locale isn't C


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=7167fe4c70ea74f1bb74a6130bb7e6bf5ca354ee

commit 7167fe4c70ea74f1bb74a6130bb7e6bf5ca354ee
Author: Alan Modra <amodra@gmail.com>
Date:   Sun Nov 5 16:22:55 2017 +1030

    PR22397, BFD internal error when message locale isn't C
    
    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.

Diff:
---
 bfd/ChangeLog |  12 +++
 bfd/bfd.c     | 336 ++++++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 317 insertions(+), 31 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 9a98d2a..80d14ae 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -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.
diff --git a/bfd/bfd.c b/bfd/bfd.c
index 7d6185f..006fb2b 100644
--- 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.  */


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]