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.
This commit is contained in:
Alan Modra
2017-11-05 16:22:55 +10:30
parent 0724bd460b
commit 7167fe4c70
2 changed files with 317 additions and 31 deletions

View 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> 2017-11-04 Alan Modra <amodra@gmail.com>
* elf32-ppc.c (got_entries_needed, got_relocs_needed): New functions. * elf32-ppc.c (got_entries_needed, got_relocs_needed): New functions.

336
bfd/bfd.c
View File

@ -626,25 +626,47 @@ CODE_FRAGMENT
static const char *_bfd_error_program_name; static const char *_bfd_error_program_name;
/* This macro and _bfd_doprnt (originally _doprint) taken from /* Support for positional parameters. */
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 union _bfd_doprnt_args
and bfd_size_type args, which vary in size depending on BFD {
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. */ configuration. */
#define PRINT_TYPE(TYPE) \ #define PRINT_TYPE(TYPE, FIELD) \
do \ do \
{ \ { \
TYPE value = va_arg (ap, TYPE); \ TYPE value = (TYPE) args[arg_no].FIELD; \
result = fprintf (stream, specifier, value); \ result = fprintf (stream, specifier, value); \
} while (0) } while (0)
static int 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; const char *ptr = format;
char specifier[128]; char specifier[128];
int total_printed = 0; int total_printed = 0;
unsigned int arg_count = 0;
while (*ptr != '\0') while (*ptr != '\0')
{ {
@ -660,39 +682,75 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
result = fprintf (stream, "%s", ptr); result = fprintf (stream, "%s", ptr);
ptr += result; ptr += result;
} }
else if (ptr[1] == '%')
{
fputc ('%', stream);
result = 1;
ptr += 2;
}
else else
{ {
/* We have a format specifier! */ /* We have a format specifier! */
char *sptr = specifier; char *sptr = specifier;
int wide_width = 0, short_width = 0; int wide_width = 0, short_width = 0;
unsigned int arg_no;
/* Copy the % and move forward. */ /* Copy the % and move forward. */
*sptr++ = *ptr++; *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. */ /* Move past flags. */
while (strchr ("-+ #0", *ptr)) while (strchr ("-+ #0", *ptr))
*sptr++ = *ptr++; *sptr++ = *ptr++;
if (*ptr == '*') if (*ptr == '*')
{ {
int value = abs (va_arg (ap, int)); int value;
sptr += sprintf (sptr, "%d", value); unsigned int arg_index;
ptr++; 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 else
/* Handle explicit numeric value. */ /* Handle explicit numeric value. */
while (ISDIGIT (*ptr)) while (ISDIGIT (*ptr))
*sptr++ = *ptr++; *sptr++ = *ptr++;
/* Precision. */
if (*ptr == '.') if (*ptr == '.')
{ {
/* Copy and go past the period. */ /* Copy and go past the period. */
*sptr++ = *ptr++; *sptr++ = *ptr++;
if (*ptr == '*') if (*ptr == '*')
{ {
int value = abs (va_arg (ap, int)); int value;
sptr += sprintf (sptr, "%d", value); unsigned int arg_index;
ptr++; 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 else
/* Handle explicit numeric value. */ /* 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. */ /* Copy the type specifier, and NULL terminate. */
*sptr++ = *ptr++; *sptr++ = *ptr++;
*sptr = '\0'; *sptr = '\0';
if ((int) arg_no < 0)
arg_no = arg_count;
switch (ptr[-1]) 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 as an int and trust the C library printf to cast it
to the right width. */ to the right width. */
if (short_width) if (short_width)
PRINT_TYPE (int); PRINT_TYPE (int, i);
else else
{ {
/* L modifier for bfd_vma or bfd_size_type may be /* L modifier for bfd_vma or bfd_size_type may be
either long long or long. */ either long long or long. */
if (sptr[-2] == 'L') if (ptr[-2] == 'L')
{ {
sptr[-2] = 'l'; sptr[-2] = 'l';
if (BFD_ARCH_SIZE < 64 || BFD_HOST_64BIT_LONG) 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) switch (wide_width)
{ {
case 0: case 0:
PRINT_TYPE (int); PRINT_TYPE (int, i);
break; break;
case 1: case 1:
PRINT_TYPE (long); PRINT_TYPE (long, l);
break; break;
case 2: case 2:
default: default:
@ -772,10 +832,10 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
*sptr = '\0'; *sptr = '\0';
#endif #endif
#if defined (__GNUC__) || defined (HAVE_LONG_LONG) #if defined (__GNUC__) || defined (HAVE_LONG_LONG)
PRINT_TYPE (long long); PRINT_TYPE (long long, ll);
#else #else
/* Fake it and hope for the best. */ /* Fake it and hope for the best. */
PRINT_TYPE (long); PRINT_TYPE (long, l);
#endif #endif
break; break;
} }
@ -789,35 +849,32 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
case 'G': case 'G':
{ {
if (wide_width == 0) if (wide_width == 0)
PRINT_TYPE (double); PRINT_TYPE (double, d);
else else
{ {
#if defined (__GNUC__) || defined (HAVE_LONG_DOUBLE) #if defined (__GNUC__) || defined (HAVE_LONG_DOUBLE)
PRINT_TYPE (long double); PRINT_TYPE (long double, ld);
#else #else
/* Fake it and hope for the best. */ /* Fake it and hope for the best. */
PRINT_TYPE (double); PRINT_TYPE (double, d);
#endif #endif
} }
} }
break; break;
case 's': case 's':
PRINT_TYPE (char *); PRINT_TYPE (char *, p);
break; break;
case 'p': case 'p':
PRINT_TYPE (void *); PRINT_TYPE (void *, p);
break;
case '%':
fputc ('%', stream);
result = 1;
break; break;
case 'A': case 'A':
{ {
asection *sec = va_arg (ap, asection *); asection *sec;
bfd *abfd; bfd *abfd;
const char *group = NULL; const char *group = NULL;
struct coff_comdat_info *ci; struct coff_comdat_info *ci;
sec = (asection *) args[arg_no].p;
if (sec == NULL) if (sec == NULL)
/* Invoking %A with a null section pointer is an /* Invoking %A with a null section pointer is an
internal error. */ internal error. */
@ -841,8 +898,9 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
break; break;
case 'B': case 'B':
{ {
bfd *abfd = va_arg (ap, bfd *); bfd *abfd;
abfd = (bfd *) args[arg_no].p;
if (abfd == NULL) if (abfd == NULL)
/* Invoking %B with a null bfd pointer is an /* Invoking %B with a null bfd pointer is an
internal error. */ internal error. */
@ -858,6 +916,7 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
default: default:
abort(); abort();
} }
arg_count++;
} }
if (result == -1) if (result == -1)
return -1; return -1;
@ -867,15 +926,230 @@ _bfd_doprnt (FILE *stream, const char *format, va_list ap)
return total_printed; 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. /* This is the default routine to handle BFD error messages.
Like fprintf (stderr, ...), but also handles some extra format specifiers. Like fprintf (stderr, ...), but also handles some extra format specifiers.
%A section name from section. For group components, print group name too. %A section name from section. For group components, prints group name too.
%B file name from bfd. For archive components, prints archive too. */ %B file name from bfd. For archive components, prints archive too.
Beware: Only supports a maximum of 9 format arguments. */
static void static void
error_handler_internal (const char *fmt, va_list ap) 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. */ /* PR 4992: Don't interrupt output being sent to stdout. */
fflush (stdout); fflush (stdout);
@ -884,7 +1158,7 @@ error_handler_internal (const char *fmt, va_list ap)
else else
fprintf (stderr, "BFD: "); 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 /* On AIX, putc is implemented as a macro that triggers a -Wunused-value
warning, so use the fputc function to avoid it. */ warning, so use the fputc function to avoid it. */