aboutsummaryrefslogtreecommitdiff
path: root/bfd/bfd.c
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2017-11-05 16:22:55 +1030
committerAlan Modra <amodra@gmail.com>2017-11-05 18:53:10 +1030
commit7167fe4c70ea74f1bb74a6130bb7e6bf5ca354ee (patch)
treed18b949e287e1c81530541f17e9a98ef0131d54c /bfd/bfd.c
parent0724bd460babc0c3c31aa67b5f8385a4cb5279f9 (diff)
downloadgdb-7167fe4c70ea74f1bb74a6130bb7e6bf5ca354ee.zip
gdb-7167fe4c70ea74f1bb74a6130bb7e6bf5ca354ee.tar.gz
gdb-7167fe4c70ea74f1bb74a6130bb7e6bf5ca354ee.tar.bz2
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.
Diffstat (limited to 'bfd/bfd.c')
-rw-r--r--bfd/bfd.c336
1 files changed, 305 insertions, 31 deletions
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. */