ubsan: next_char_of_string signed integer overflow
[binutils-gdb.git] / gas / read.c
index 9ab88f8962a3a2a8c8bc06d8cac3a2707758acfb..6c8b7c3efdb23320e3e1b4338488b34d1ab8cf73 100644 (file)
@@ -1,5 +1,5 @@
 /* read.c - read a source file -
-   Copyright (C) 1986-2018 Free Software Foundation, Inc.
+   Copyright (C) 1986-2021 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
@@ -40,6 +40,8 @@
 #include "dw2gencfi.h"
 #include "wchar.h"
 
+#include <limits.h>
+
 #ifndef TC_START_LABEL
 #define TC_START_LABEL(STR, NUL_CHAR, NEXT_CHAR) (NEXT_CHAR == ':')
 #endif
@@ -62,7 +64,7 @@
 #endif
 
 char *input_line_pointer;      /*->next char of source file to parse.  */
-bfd_boolean input_from_string = FALSE;
+bool input_from_string = false;
 
 #if BITS_PER_CHAR != 8
 /*  The following table is indexed by[(char)] and will break if
@@ -293,7 +295,53 @@ address_bytes (void)
 
 /* Set up pseudo-op tables.  */
 
-static struct hash_control *po_hash;
+struct po_entry
+{
+  const char *poc_name;
+
+  const pseudo_typeS *pop;
+};
+
+typedef struct po_entry po_entry_t;
+
+/* Hash function for a po_entry.  */
+
+static hashval_t
+hash_po_entry (const void *e)
+{
+  const po_entry_t *entry = (const po_entry_t *) e;
+  return htab_hash_string (entry->poc_name);
+}
+
+/* Equality function for a po_entry.  */
+
+static int
+eq_po_entry (const void *a, const void *b)
+{
+  const po_entry_t *ea = (const po_entry_t *) a;
+  const po_entry_t *eb = (const po_entry_t *) b;
+
+  return strcmp (ea->poc_name, eb->poc_name) == 0;
+}
+
+static po_entry_t *
+po_entry_alloc (const char *poc_name, const pseudo_typeS *pop)
+{
+  po_entry_t *entry = XNEW (po_entry_t);
+  entry->poc_name = poc_name;
+  entry->pop = pop;
+  return entry;
+}
+
+static const pseudo_typeS *
+po_entry_find (htab_t table, const char *poc_name)
+{
+  po_entry_t needle = { poc_name, NULL };
+  po_entry_t *entry = htab_find (table, &needle);
+  return entry != NULL ? entry->pop : NULL;
+}
+
+static struct htab *po_hash;
 
 static const pseudo_typeS potable[] = {
   {"abort", s_abort, 0},
@@ -316,9 +364,7 @@ static const pseudo_typeS potable[] = {
   {"common.s", s_mri_common, 1},
   {"data", s_data, 0},
   {"dc", cons, 2},
-#ifdef TC_ADDRESS_BYTES
   {"dc.a", cons, 0},
-#endif
   {"dc.b", cons, 1},
   {"dc.d", float_cons, 'd'},
   {"dc.l", cons, 4},
@@ -336,10 +382,10 @@ static const pseudo_typeS potable[] = {
   {"ds.b", s_space, 1},
   {"ds.d", s_space, 8},
   {"ds.l", s_space, 4},
-  {"ds.p", s_space, 12},
+  {"ds.p", s_space, 'p'},
   {"ds.s", s_space, 4},
   {"ds.w", s_space, 2},
-  {"ds.x", s_space, 12},
+  {"ds.x", s_space, 'x'},
   {"debug", s_ignore, 0},
 #ifdef S_SET_DESC
   {"desc", s_desc, 0},
@@ -417,6 +463,8 @@ static const pseudo_typeS potable[] = {
   {"noformat", s_ignore, 0},
   {"nolist", listing_list, 0}, /* Turn listing off.  */
   {"nopage", listing_nopage, 0},
+  {"nop", s_nop, 0},
+  {"nops", s_nops, 0},
   {"octa", cons, 16},
   {"offset", s_struct, 0},
   {"org", s_org, 0},
@@ -442,7 +490,6 @@ static const pseudo_typeS potable[] = {
 /* size  */
   {"space", s_space, 0},
   {"skip", s_space, 0},
-  {"nop", s_nop, 0},
   {"sleb128", s_leb128, 1},
   {"spc", s_ignore, 0},
   {"stabd", s_stab, 'd'},
@@ -481,6 +528,9 @@ static const pseudo_typeS potable[] = {
   {"weakref", s_weakref, 0},
   {"word", cons, 2},
   {"zero", s_space, 0},
+  {"2byte", cons, 2},
+  {"4byte", cons, 4},
+  {"8byte", cons, 8},
   {NULL, NULL, 0}                      /* End sentinel.  */
 };
 
@@ -512,14 +562,17 @@ static const char *pop_table_name;
 void
 pop_insert (const pseudo_typeS *table)
 {
-  const char *errtxt;
   const pseudo_typeS *pop;
   for (pop = table; pop->poc_name; pop++)
     {
-      errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop);
-      if (errtxt && (!pop_override_ok || strcmp (errtxt, "exists")))
-       as_fatal (_("error constructing %s pseudo-op table: %s"), pop_table_name,
-                 errtxt);
+      po_entry_t *elt = po_entry_alloc (pop->poc_name, pop);
+      if (htab_insert (po_hash, elt, 0) != NULL)
+       {
+         free (elt);
+         if (!pop_override_ok)
+           as_fatal (_("error constructing %s pseudo-op table"),
+                     pop_table_name);
+       }
     }
 }
 
@@ -538,7 +591,8 @@ pop_insert (const pseudo_typeS *table)
 static void
 pobegin (void)
 {
-  po_hash = hash_new ();
+  po_hash = htab_create_alloc (16, hash_po_entry, eq_po_entry, NULL,
+                              xcalloc, xfree);
 
   /* Do the target-specific pseudo ops.  */
   pop_table_name = "md";
@@ -739,10 +793,10 @@ assemble_one (char *line)
 
 #endif  /* HANDLE_BUNDLE */
 
-static bfd_boolean
+static bool
 in_bss (void)
 {
-  flagword flags = bfd_get_section_flags (stdoutput, now_seg);
+  flagword flags = bfd_section_flags (now_seg);
 
   return (flags & SEC_ALLOC) && !(flags & (SEC_LOAD | SEC_HAS_CONTENTS));
 }
@@ -816,8 +870,8 @@ read_a_source_file (const char *name)
   char nul_char;
   char next_char;
   char *s;             /* String of symbol, '\0' appended.  */
-  int temp;
-  pseudo_typeS *pop;
+  long temp;
+  const pseudo_typeS *pop;
 
 #ifdef WARN_COMMENTS
   found_comment = 0;
@@ -845,7 +899,7 @@ read_a_source_file (const char *name)
 #endif
       while (input_line_pointer < buffer_limit)
        {
-         bfd_boolean was_new_line;
+         bool was_new_line;
          /* We have more of this buffer to parse.  */
 
          /* We now have input_line_pointer->1st char of next line.
@@ -953,8 +1007,7 @@ read_a_source_file (const char *name)
                      else
                        line_label = symbol_create (line_start,
                                                    absolute_section,
-                                                   (valueT) 0,
-                                                   &zero_address_frag);
+                                                   &zero_address_frag, 0);
 
                      next_char = restore_line_pointer (nul_char);
                      if (next_char == ':')
@@ -1052,7 +1105,8 @@ read_a_source_file (const char *name)
                  {
                    char *s2 = s;
 
-                   strncpy (original_case_string, s2, sizeof (original_case_string));
+                   strncpy (original_case_string, s2,
+                            sizeof (original_case_string) - 1);
                    original_case_string[sizeof (original_case_string) - 1] = 0;
 
                    while (*s2)
@@ -1066,7 +1120,7 @@ read_a_source_file (const char *name)
                    {
                      /* The MRI assembler uses pseudo-ops without
                         a period.  */
-                     pop = (pseudo_typeS *) hash_find (po_hash, s);
+                     pop = po_entry_find (po_hash, s);
                      if (pop != NULL && pop->poc_handler == NULL)
                        pop = NULL;
                    }
@@ -1081,7 +1135,7 @@ read_a_source_file (const char *name)
                         already know that the pseudo-op begins with a '.'.  */
 
                      if (pop == NULL)
-                       pop = (pseudo_typeS *) hash_find (po_hash, s + 1);
+                       pop = po_entry_find (po_hash, s + 1);
                      if (pop && !pop->poc_handler)
                        pop = NULL;
 
@@ -1211,10 +1265,24 @@ read_a_source_file (const char *name)
              /* Read the whole number.  */
              while (ISDIGIT (*input_line_pointer))
                {
-                 temp = (temp * 10) + *input_line_pointer - '0';
+                 const long digit = *input_line_pointer - '0';
+                 if (temp > (LONG_MAX - digit) / 10)
+                   {
+                     as_bad (_("local label too large near %s"), backup);
+                     temp = -1;
+                     break;
+                   }
+                 temp = temp * 10 + digit;
                  ++input_line_pointer;
                }
 
+             /* Overflow: stop processing the label.  */
+             if (temp == -1)
+               {
+                 ignore_rest_of_line ();
+                 continue;
+               }
+
              if (LOCAL_LABELS_DOLLAR
                  && *input_line_pointer == '$'
                  && *(input_line_pointer + 1) == ':')
@@ -1223,7 +1291,7 @@ read_a_source_file (const char *name)
 
                  if (dollar_label_defined (temp))
                    {
-                     as_fatal (_("label \"%d$\" redefined"), temp);
+                     as_fatal (_("label \"%ld$\" redefined"), temp);
                    }
 
                  define_dollar_label (temp);
@@ -1252,7 +1320,7 @@ read_a_source_file (const char *name)
              char *tmp_buf = 0;
 
              s = input_line_pointer;
-             if (strncmp (s, "APP\n", 4))
+             if (!startswith (s, "APP\n"))
                {
                  /* We ignore it.  */
                  ignore_rest_of_line ();
@@ -1331,8 +1399,7 @@ read_a_source_file (const char *name)
                  new_length += 100;
                }
 
-             if (tmp_buf)
-               free (tmp_buf);
+             free (tmp_buf);
 
              /* We've "scrubbed" input to the preferred format.  In the
                 process we may have consumed the whole of the remaining
@@ -1482,7 +1549,7 @@ static void
 s_align (signed int arg, int bytes_p)
 {
   unsigned int align_limit = TC_ALIGN_LIMIT;
-  unsigned int align;
+  addressT align;
   char *stop = NULL;
   char stopc = 0;
   offsetT fill = 0;
@@ -1529,7 +1596,7 @@ s_align (signed int arg, int bytes_p)
   if (align > align_limit)
     {
       align = align_limit;
-      as_warn (_("alignment too large: %u assumed"), align);
+      as_warn (_("alignment too large: %u assumed"), align_limit);
     }
 
   if (*input_line_pointer != ',')
@@ -1800,8 +1867,7 @@ s_comm_internal (int param,
  out:
   if (flag_mri)
     mri_comment_end (stop, stopc);
-  if (name != NULL)
-    free (name);
+  free (name);
   return symbolP;
 }
 
@@ -1861,8 +1927,7 @@ s_mri_common (int small ATTRIBUTE_UNUSED)
 
   sym = symbol_find_or_make (name);
   c = restore_line_pointer (c);
-  if (alc != NULL)
-    free (alc);
+  free (alc);
 
   if (*input_line_pointer != ',')
     align = 0;
@@ -2419,7 +2484,7 @@ s_linkonce (int ignore ATTRIBUTE_UNUSED)
     if ((bfd_applicable_section_flags (stdoutput) & SEC_LINK_ONCE) == 0)
       as_warn (_(".linkonce is not supported for this object file format"));
 
-    flags = bfd_get_section_flags (stdoutput, now_seg);
+    flags = bfd_section_flags (now_seg);
     flags |= SEC_LINK_ONCE;
     switch (type)
       {
@@ -2438,7 +2503,7 @@ s_linkonce (int ignore ATTRIBUTE_UNUSED)
        flags |= SEC_LINK_DUPLICATES_SAME_CONTENTS;
        break;
       }
-    if (!bfd_set_section_flags (stdoutput, now_seg, flags))
+    if (!bfd_set_section_flags (now_seg, flags))
       as_bad (_("bfd_set_section_flags: %s"),
              bfd_errmsg (bfd_get_error ()));
   }
@@ -2464,7 +2529,7 @@ bss_alloc (symbolS *symbolP, addressT size, unsigned int align)
        {
          bss_seg = subseg_new (".sbss", 1);
          seg_info (bss_seg)->bss = 1;
-         if (!bfd_set_section_flags (stdoutput, bss_seg, SEC_ALLOC))
+         if (!bfd_set_section_flags (bss_seg, SEC_ALLOC | SEC_SMALL_DATA))
            as_warn (_("error setting flags for \".sbss\": %s"),
                     bfd_errmsg (bfd_get_error ()));
        }
@@ -2483,7 +2548,7 @@ bss_alloc (symbolS *symbolP, addressT size, unsigned int align)
     symbol_get_frag (symbolP)->fr_symbol = NULL;
 
   symbol_set_frag (symbolP, frag_now);
-  pfrag = frag_var (rs_org, 1, 1, 0, symbolP, size, NULL);
+  pfrag = frag_var (rs_org, 1, 1, 0, symbolP, size * OCTETS_PER_BYTE, NULL);
   *pfrag = 0;
 
 #ifdef S_SET_SIZE
@@ -2725,10 +2790,10 @@ s_macro (int ignore ATTRIBUTE_UNUSED)
        }
 
       if (((NO_PSEUDO_DOT || flag_m68k_mri)
-          && hash_find (po_hash, name) != NULL)
+          && po_entry_find (po_hash, name) != NULL)
          || (!flag_m68k_mri
              && *name == '.'
-             && hash_find (po_hash, name + 1) != NULL))
+             && po_entry_find (po_hash, name + 1) != NULL))
        as_warn_where (file,
                 line,
                 _("attempt to redefine pseudo-op `%s' ignored"),
@@ -2957,9 +3022,9 @@ s_mri_sect (char *type ATTRIBUTE_UNUSED)
          flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_READONLY | SEC_ROM;
        if (flags != SEC_NO_FLAGS)
          {
-           if (!bfd_set_section_flags (stdoutput, seg, flags))
+           if (!bfd_set_section_flags (seg, flags))
              as_warn (_("error setting flags for \"%s\": %s"),
-                      bfd_section_name (stdoutput, seg),
+                      bfd_section_name (seg),
                       bfd_errmsg (bfd_get_error ()));
          }
       }
@@ -2972,81 +3037,10 @@ s_mri_sect (char *type ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 
 #else /* ! TC_M68K */
-#ifdef TC_I960
-
-  char *name;
-  char c;
-  segT seg;
-
-  SKIP_WHITESPACE ();
-
-  c = get_symbol_name (& name);
-
-  name = xstrdup (name);
-
-  c = restore_line_pointer (c);
-
-  seg = subseg_new (name, 0);
-
-  if (c != ',')
-    *type = 'C';
-  else
-    {
-      char *sectype;
-
-      ++input_line_pointer;
-      SKIP_WHITESPACE ();
-      c = get_symbol_name (& sectype);
-      if (*sectype == '\0')
-       *type = 'C';
-      else if (strcasecmp (sectype, "text") == 0)
-       *type = 'C';
-      else if (strcasecmp (sectype, "data") == 0)
-       *type = 'D';
-      else if (strcasecmp (sectype, "romdata") == 0)
-       *type = 'R';
-      else
-       as_warn (_("unrecognized section type `%s'"), sectype);
-      (void) restore_line_pointer (c);
-    }
-
-  if (*input_line_pointer == ',')
-    {
-      char *seccmd;
-
-      ++input_line_pointer;
-      SKIP_WHITESPACE ();
-      c = get_symbol_name (& seccmd);
-      if (strcasecmp (seccmd, "absolute") == 0)
-       {
-         as_bad (_("absolute sections are not supported"));
-         *input_line_pointer = c;
-         ignore_rest_of_line ();
-         return;
-       }
-      else if (strcasecmp (seccmd, "align") == 0)
-       {
-         unsigned int align;
-
-         (void) restore_line_pointer (c);
-         align = get_absolute_expression ();
-         record_alignment (seg, align);
-       }
-      else
-       {
-         as_warn (_("unrecognized section command `%s'"), seccmd);
-         (void) restore_line_pointer (c);
-       }
-    }
-
-  demand_empty_rest_of_line ();
-
-#else /* ! TC_I960 */
   /* The MRI assembler seems to use different forms of .sect for
      different targets.  */
   as_bad ("MRI mode not supported for this target");
   ignore_rest_of_line ();
-#endif /* ! TC_I960 */
 #endif /* ! TC_M68K */
 }
 
@@ -3188,7 +3182,8 @@ do_repeat_with_expander (size_t count,
          sub = strstr (processed.ptr, expander);
          len = sprintf (sub, "%lu", (unsigned long) count);
          gas_assert (len < 8);
-         strcpy (sub + len, sub + 8);
+         memmove (sub + len, sub + 8,
+                  processed.ptr + processed.len - (sub + 8));
          processed.len -= (8 - len);
          sb_add_sb (& many, & processed);
          sb_kill (& processed);
@@ -3332,6 +3327,29 @@ s_space (int mult)
   md_flush_pending_output ();
 #endif
 
+  switch (mult)
+    {
+    case 'x':
+#ifdef X_PRECISION
+# ifndef P_PRECISION
+#  define P_PRECISION     X_PRECISION
+#  define P_PRECISION_PAD X_PRECISION_PAD
+# endif
+      mult = (X_PRECISION + X_PRECISION_PAD) * sizeof (LITTLENUM_TYPE);
+      if (!mult)
+#endif
+       mult = 12;
+      break;
+
+    case 'p':
+#ifdef P_PRECISION
+      mult = (P_PRECISION + P_PRECISION_PAD) * sizeof (LITTLENUM_TYPE);
+      if (!mult)
+#endif
+       mult = 12;
+      break;
+    }
+
 #ifdef md_cons_align
   md_cons_align (1);
 #endif
@@ -3511,6 +3529,61 @@ s_space (int mult)
 
 void
 s_nop (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  fragS *start;
+  addressT start_off;
+  offsetT frag_off;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  SKIP_WHITESPACE ();
+  expression (&exp);
+  demand_empty_rest_of_line ();
+
+  start = frag_now;
+  start_off = frag_now_fix ();
+  do
+    {
+#ifdef md_emit_single_noop
+      md_emit_single_noop;
+#else
+      char *nop;
+
+#ifndef md_single_noop_insn
+#define md_single_noop_insn "nop"
+#endif
+      /* md_assemble might modify its argument, so
+        we must pass it a string that is writable.  */
+      if (asprintf (&nop, "%s", md_single_noop_insn) < 0)
+       as_fatal ("%s", xstrerror (errno));
+
+      /* Some targets assume that they can update input_line_pointer
+        inside md_assemble, and, worse, that they can leave it
+        assigned to the string pointer that was provided as an
+        argument.  So preserve ilp here.  */
+      char *saved_ilp = input_line_pointer;
+      md_assemble (nop);
+      input_line_pointer = saved_ilp;
+      free (nop);
+#endif
+#ifdef md_flush_pending_output
+      md_flush_pending_output ();
+#endif
+    } while (exp.X_op == O_constant
+            && exp.X_add_number > 0
+            && frag_offset_ignore_align_p (start, frag_now, &frag_off)
+            && frag_off + frag_now_fix () < start_off + exp.X_add_number);
+}
+
+void
+s_nops (int ignore ATTRIBUTE_UNUSED)
 {
   expressionS exp;
   expressionS val;
@@ -3523,8 +3596,12 @@ s_nop (int ignore ATTRIBUTE_UNUSED)
   md_cons_align (1);
 #endif
 
+  SKIP_WHITESPACE ();
   expression (&exp);
+  /* Note - this expression is tested for an absolute value in
+     write.c:relax_segment().  */
 
+  SKIP_WHITESPACE ();
   if (*input_line_pointer == ',')
     {
       ++input_line_pointer;
@@ -3536,29 +3613,137 @@ s_nop (int ignore ATTRIBUTE_UNUSED)
       val.X_add_number = 0;
     }
 
-  if (val.X_op == O_constant)
+  if (val.X_op != O_constant)
+    {
+      as_bad (_("unsupported variable nop control in .nops directive"));
+      val.X_op = O_constant;
+      val.X_add_number = 0;
+    }
+  else if (val.X_add_number < 0)
+    {
+      as_warn (_("negative nop control byte, ignored"));
+      val.X_add_number = 0;
+    }
+
+  demand_empty_rest_of_line ();
+
+  if (need_pass_2)
+    /* Ignore this directive if we are going to perform a second pass.  */
+    return;
+
+  /* Store the no-op instruction control byte in the first byte of frag.  */
+  char *p;
+  symbolS *sym = make_expr_symbol (&exp);
+  p = frag_var (rs_space_nop, 1, 1, (relax_substateT) 0,
+               sym, (offsetT) 0, (char *) 0);
+  *p = val.X_add_number;
+}
+
+/* Obtain the size of a floating point number, given a type.  */
+
+static int
+float_length (int float_type, int *pad_p)
+{
+  int length, pad = 0;
+
+  switch (float_type)
+    {
+    case 'b':
+    case 'B':
+    case 'h':
+    case 'H':
+      length = 2;
+      break;
+
+    case 'f':
+    case 'F':
+    case 's':
+    case 'S':
+      length = 4;
+      break;
+
+    case 'd':
+    case 'D':
+    case 'r':
+    case 'R':
+      length = 8;
+      break;
+
+    case 'x':
+    case 'X':
+#ifdef X_PRECISION
+      length = X_PRECISION * sizeof (LITTLENUM_TYPE);
+      pad = X_PRECISION_PAD * sizeof (LITTLENUM_TYPE);
+      if (!length)
+#endif
+       length = 12;
+      break;
+
+    case 'p':
+    case 'P':
+#ifdef P_PRECISION
+      length = P_PRECISION * sizeof (LITTLENUM_TYPE);
+      pad = P_PRECISION_PAD * sizeof (LITTLENUM_TYPE);
+      if (!length)
+#endif
+       length = 12;
+      break;
+
+    default:
+      as_bad (_("unknown floating type '%c'"), float_type);
+      length = -1;
+      break;
+    }
+
+  if (pad_p)
+    *pad_p = pad;
+
+  return length;
+}
+
+static int
+parse_one_float (int float_type, char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT])
+{
+  int length;
+
+  SKIP_WHITESPACE ();
+
+  /* Skip any 0{letter} that may be present.  Don't even check if the
+     letter is legal.  Someone may invent a "z" format and this routine
+     has no use for such information. Lusers beware: you get
+     diagnostics if your input is ill-conditioned.  */
+  if (input_line_pointer[0] == '0'
+      && ISALPHA (input_line_pointer[1]))
+    input_line_pointer += 2;
+
+  /* Accept :xxxx, where the x's are hex digits, for a floating point
+     with the exact digits specified.  */
+  if (input_line_pointer[0] == ':')
     {
-      if (val.X_add_number < 0)
+      ++input_line_pointer;
+      length = hex_float (float_type, temp);
+      if (length < 0)
        {
-         as_warn (_("negative nop control byte, ignored"));
-         val.X_add_number = 0;
+         ignore_rest_of_line ();
+         return length;
        }
+    }
+  else
+    {
+      const char *err;
 
-      if (!need_pass_2)
+      err = md_atof (float_type, temp, &length);
+      know (length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
+      know (err != NULL || length > 0);
+      if (err)
        {
-         /* Store the no-op instruction control byte in the first byte
-            of frag.  */
-         char *p;
-         symbolS *sym = make_expr_symbol (&exp);
-         p = frag_var (rs_space_nop, 1, 1, (relax_substateT) 0,
-                       sym, (offsetT) 0, (char *) 0);
-         *p = val.X_add_number;
+         as_bad (_("bad floating literal: %s"), err);
+         ignore_rest_of_line ();
+         return -1;
        }
     }
-  else
-    as_bad (_("unsupported variable nop control in .nop directive"));
 
-  demand_empty_rest_of_line ();
+  return length;
 }
 
 /* This is like s_space, but the value is a floating point number with
@@ -3586,51 +3771,24 @@ s_float_space (int float_type)
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
-      as_bad (_("missing value"));
-      ignore_rest_of_line ();
-      if (flag_mri)
-       mri_comment_end (stop, stopc);
-      return;
-    }
-
-  ++input_line_pointer;
-
-  SKIP_WHITESPACE ();
-
-  /* Skip any 0{letter} that may be present.  Don't even check if the
-   * letter is legal.  */
-  if (input_line_pointer[0] == '0'
-      && ISALPHA (input_line_pointer[1]))
-    input_line_pointer += 2;
+      int pad;
 
-  /* Accept :xxxx, where the x's are hex digits, for a floating point
-     with the exact digits specified.  */
-  if (input_line_pointer[0] == ':')
-    {
-      flen = hex_float (float_type, temp);
-      if (flen < 0)
-       {
-         ignore_rest_of_line ();
-         if (flag_mri)
-           mri_comment_end (stop, stopc);
-         return;
-       }
+      flen = float_length (float_type, &pad);
+      if (flen >= 0)
+       memset (temp, 0, flen += pad);
     }
   else
     {
-      const char *err;
+      ++input_line_pointer;
 
-      err = md_atof (float_type, temp, &flen);
-      know (flen <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
-      know (err != NULL || flen > 0);
-      if (err)
-       {
-         as_bad (_("bad floating literal: %s"), err);
-         ignore_rest_of_line ();
-         if (flag_mri)
-           mri_comment_end (stop, stopc);
-         return;
-       }
+      flen = parse_one_float (float_type, temp);
+    }
+
+  if (flen < 0)
+    {
+      if (flag_mri)
+       mri_comment_end (stop, stopc);
+      return;
     }
 
   while (--count >= 0)
@@ -3830,7 +3988,8 @@ ignore_rest_of_line (void)
   input_line_pointer++;
 
   /* Return pointing just after end-of-line.  */
-  know (is_end_of_line[(unsigned char) input_line_pointer[-1]]);
+  if (input_line_pointer <= buffer_limit)
+    know (is_end_of_line[(unsigned char) input_line_pointer[-1]]);
 }
 
 /* Sets frag for given symbol to zero_address_frag, except when the
@@ -3978,7 +4137,6 @@ pseudo_set (symbolS *symbolP)
 /* Some targets need to parse the expression in various fancy ways.
    You can define TC_PARSE_CONS_EXPRESSION to do whatever you like
    (for example, the HPPA does this).  Otherwise, you can define
-   BITFIELD_CONS_EXPRESSIONS to permit bitfields to be specified, or
    REPEAT_CONS_EXPRESSIONS to permit repeat counts.  If none of these
    are defined, which is the normal case, then only simple expressions
    are permitted.  */
@@ -3989,12 +4147,6 @@ parse_mri_cons (expressionS *exp, unsigned int nbytes);
 #endif
 
 #ifndef TC_PARSE_CONS_EXPRESSION
-#ifdef BITFIELD_CONS_EXPRESSIONS
-#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
-  (parse_bitfield_cons (EXP, NBYTES), TC_PARSE_CONS_RETURN_NONE)
-static void
-parse_bitfield_cons (expressionS *exp, unsigned int nbytes);
-#endif
 #ifdef REPEAT_CONS_EXPRESSIONS
 #define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) \
   (parse_repeat_cons (EXP, NBYTES), TC_PARSE_CONS_RETURN_NONE)
@@ -4044,10 +4196,8 @@ cons_worker (int nbytes, /* 1=.byte, 2=.word, 4=.long.  */
       return;
     }
 
-#ifdef TC_ADDRESS_BYTES
   if (nbytes == 0)
     nbytes = TC_ADDRESS_BYTES ();
-#endif
 
 #ifdef md_cons_align
   md_cons_align (nbytes);
@@ -4159,6 +4309,9 @@ s_reloc (int ignore ATTRIBUTE_UNUSED)
       goto err_out;
     case O_constant:
       exp.X_add_symbol = section_symbol (now_seg);
+      /* Mark the section symbol used in relocation so that it will be
+        included in the symbol table.  */
+      symbol_mark_used_in_reloc (exp.X_add_symbol);
       exp.X_op = O_symbol;
       /* Fallthru */
     case O_symbol:
@@ -4455,24 +4608,19 @@ emit_expr_with_reloc (expressionS *exp,
       valueT get;
       valueT use;
       valueT mask;
-      valueT hibit;
       valueT unmask;
 
       /* JF << of >= number of bits in the object is undefined.  In
         particular SPARC (Sun 4) has problems.  */
       if (nbytes >= sizeof (valueT))
        {
+         know (nbytes == sizeof (valueT));
          mask = 0;
-         if (nbytes > sizeof (valueT))
-           hibit = 0;
-         else
-           hibit = (valueT) 1 << (nbytes * BITS_PER_CHAR - 1);
        }
       else
        {
          /* Don't store these bits.  */
          mask = ~(valueT) 0 << (BITS_PER_CHAR * nbytes);
-         hibit = (valueT) 1 << (nbytes * BITS_PER_CHAR - 1);
        }
 
       unmask = ~mask;          /* Do store these bits.  */
@@ -4484,23 +4632,16 @@ emit_expr_with_reloc (expressionS *exp,
 
       get = exp->X_add_number;
       use = get & unmask;
-      if ((get & mask) != 0
-         && ((get & mask) != mask
-             || (get & hibit) == 0))
+      if ((get & mask) != 0 && (-get & mask) != 0)
        {
+         char get_buf[128];
+         char use_buf[128];
+
+         /* These buffers help to ease the translation of the warning message.  */
+         sprintf_vma (get_buf, get);
+         sprintf_vma (use_buf, use);
          /* Leading bits contain both 0s & 1s.  */
-#if defined (BFD64) && BFD_HOST_64BIT_LONG_LONG
-#ifndef __MSVCRT__
-         as_warn (_("value 0x%llx truncated to 0x%llx"),
-                  (unsigned long long) get, (unsigned long long) use);
-#else
-         as_warn (_("value 0x%I64x truncated to 0x%I64x"),
-                  (unsigned long long) get, (unsigned long long) use);
-#endif
-#else
-         as_warn (_("value 0x%lx truncated to 0x%lx"),
-                  (unsigned long) get, (unsigned long) use);
-#endif
+         as_warn (_("value 0x%s truncated to 0x%s"), get_buf, use_buf);
        }
       /* Put bytes in right order.  */
       md_number_to_chars (p, use, (int) nbytes);
@@ -4658,136 +4799,6 @@ emit_expr_fix (expressionS *exp, unsigned int nbytes, fragS *frag, char *p,
 #endif
 }
 \f
-#ifdef BITFIELD_CONS_EXPRESSIONS
-
-/* i960 assemblers, (eg, asm960), allow bitfields after ".byte" as
-   w:x,y:z, where w and y are bitwidths and x and y are values.  They
-   then pack them all together. We do a little better in that we allow
-   them in words, longs, etc. and we'll pack them in target byte order
-   for you.
-
-   The rules are: pack least significant bit first, if a field doesn't
-   entirely fit, put it in the next unit.  Overflowing the bitfield is
-   explicitly *not* even a warning.  The bitwidth should be considered
-   a "mask".
-
-   To use this function the tc-XXX.h file should define
-   BITFIELD_CONS_EXPRESSIONS.  */
-
-static void
-parse_bitfield_cons (expressionS *exp, unsigned int nbytes)
-{
-  unsigned int bits_available = BITS_PER_CHAR * nbytes;
-  char *hold = input_line_pointer;
-
-  (void) expression (exp);
-
-  if (*input_line_pointer == ':')
-    {
-      /* Bitfields.  */
-      long value = 0;
-
-      for (;;)
-       {
-         unsigned long width;
-
-         if (*input_line_pointer != ':')
-           {
-             input_line_pointer = hold;
-             break;
-           }                   /* Next piece is not a bitfield.  */
-
-         /* In the general case, we can't allow
-            full expressions with symbol
-            differences and such.  The relocation
-            entries for symbols not defined in this
-            assembly would require arbitrary field
-            widths, positions, and masks which most
-            of our current object formats don't
-            support.
-
-            In the specific case where a symbol
-            *is* defined in this assembly, we
-            *could* build fixups and track it, but
-            this could lead to confusion for the
-            backends.  I'm lazy. I'll take any
-            SEG_ABSOLUTE. I think that means that
-            you can use a previous .set or
-            .equ type symbol.  xoxorich.  */
-
-         if (exp->X_op == O_absent)
-           {
-             as_warn (_("using a bit field width of zero"));
-             exp->X_add_number = 0;
-             exp->X_op = O_constant;
-           }                   /* Implied zero width bitfield.  */
-
-         if (exp->X_op != O_constant)
-           {
-             *input_line_pointer = '\0';
-             as_bad (_("field width \"%s\" too complex for a bitfield"), hold);
-             *input_line_pointer = ':';
-             demand_empty_rest_of_line ();
-             return;
-           }                   /* Too complex.  */
-
-         if ((width = exp->X_add_number) > (BITS_PER_CHAR * nbytes))
-           {
-             as_warn (ngettext ("field width %lu too big to fit in %d byte:"
-                                " truncated to %d bits",
-                                "field width %lu too big to fit in %d bytes:"
-                                " truncated to %d bits",
-                                nbytes),
-                      width, nbytes, (BITS_PER_CHAR * nbytes));
-             width = BITS_PER_CHAR * nbytes;
-           }                   /* Too big.  */
-
-         if (width > bits_available)
-           {
-             /* FIXME-SOMEDAY: backing up and reparsing is wasteful.  */
-             input_line_pointer = hold;
-             exp->X_add_number = value;
-             break;
-           }                   /* Won't fit.  */
-
-         /* Skip ':'.  */
-         hold = ++input_line_pointer;
-
-         (void) expression (exp);
-         if (exp->X_op != O_constant)
-           {
-             char cache = *input_line_pointer;
-
-             *input_line_pointer = '\0';
-             as_bad (_("field value \"%s\" too complex for a bitfield"), hold);
-             *input_line_pointer = cache;
-             demand_empty_rest_of_line ();
-             return;
-           }                   /* Too complex.  */
-
-         value |= ((~(-(1 << width)) & exp->X_add_number)
-                   << ((BITS_PER_CHAR * nbytes) - bits_available));
-
-         if ((bits_available -= width) == 0
-             || is_it_end_of_statement ()
-             || *input_line_pointer != ',')
-           {
-             break;
-           }                   /* All the bitfields we're gonna get.  */
-
-         hold = ++input_line_pointer;
-         (void) expression (exp);
-       }
-
-      exp->X_add_number = value;
-      exp->X_op = O_constant;
-      exp->X_unsigned = 1;
-      exp->X_extrabit = 0;
-    }
-}
-
-#endif /* BITFIELD_CONS_EXPRESSIONS */
-\f
 /* Handle an MRI style string expression.  */
 
 #ifdef TC_M68K
@@ -4901,39 +4912,11 @@ parse_repeat_cons (expressionS *exp, unsigned int nbytes)
 static int
 hex_float (int float_type, char *bytes)
 {
-  int length;
+  int pad, length = float_length (float_type, &pad);
   int i;
 
-  switch (float_type)
-    {
-    case 'f':
-    case 'F':
-    case 's':
-    case 'S':
-      length = 4;
-      break;
-
-    case 'd':
-    case 'D':
-    case 'r':
-    case 'R':
-      length = 8;
-      break;
-
-    case 'x':
-    case 'X':
-      length = 12;
-      break;
-
-    case 'p':
-    case 'P':
-      length = 12;
-      break;
-
-    default:
-      as_bad (_("unknown floating type type '%c'"), float_type);
-      return -1;
-    }
+  if (length < 0)
+    return length;
 
   /* It would be nice if we could go through expression to parse the
      hex constant, but if we get a bignum it's a pain to sort it into
@@ -4980,7 +4963,9 @@ hex_float (int float_type, char *bytes)
        memset (bytes, 0, length - i);
     }
 
-  return length;
+  memset (bytes + length, 0, pad);
+
+  return length + pad;
 }
 
 /*                     float_cons()
@@ -5006,7 +4991,6 @@ float_cons (/* Clobbers input_line-pointer, checks end-of-line.  */
 {
   char *p;
   int length;                  /* Number of chars in an object.  */
-  const char *err;             /* Error from scanning floating literal.  */
   char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT];
 
   if (is_it_end_of_statement ())
@@ -5040,41 +5024,9 @@ float_cons (/* Clobbers input_line-pointer, checks end-of-line.  */
 
   do
     {
-      /* input_line_pointer->1st char of a flonum (we hope!).  */
-      SKIP_WHITESPACE ();
-
-      /* Skip any 0{letter} that may be present. Don't even check if the
-        letter is legal. Someone may invent a "z" format and this routine
-        has no use for such information. Lusers beware: you get
-        diagnostics if your input is ill-conditioned.  */
-      if (input_line_pointer[0] == '0'
-         && ISALPHA (input_line_pointer[1]))
-       input_line_pointer += 2;
-
-      /* Accept :xxxx, where the x's are hex digits, for a floating
-        point with the exact digits specified.  */
-      if (input_line_pointer[0] == ':')
-       {
-         ++input_line_pointer;
-         length = hex_float (float_type, temp);
-         if (length < 0)
-           {
-             ignore_rest_of_line ();
-             return;
-           }
-       }
-      else
-       {
-         err = md_atof (float_type, temp, &length);
-         know (length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT);
-         know (err != NULL || length > 0);
-         if (err)
-           {
-             as_bad (_("bad floating literal: %s"), err);
-             ignore_rest_of_line ();
-             return;
-           }
-       }
+      length = parse_one_float (float_type, temp);
+      if (length < 0)
+       return;
 
       if (!need_pass_2)
        {
@@ -5346,7 +5298,7 @@ output_big_leb128 (char *p, LITTLENUM_TYPE *bignum, unsigned int size, int sign)
 /* Generate the appropriate fragments for a given expression to emit a
    leb128 value.  SIGN is 1 for sleb, 0 for uleb.  */
 
-static void
+void
 emit_leb128_expr (expressionS *exp, int sign)
 {
   operatorT op = exp->X_op;
@@ -5459,7 +5411,7 @@ s_leb128 (int sign)
 
   do
     {
-      deferred_expression (&exp);
+      expression (&exp);
       emit_leb128_expr (&exp, sign);
     }
   while (*input_line_pointer++ == ',');
@@ -5568,11 +5520,14 @@ stringer (int bits_appendzero)
          while (is_a_char (c = next_char_of_string ()))
            stringer_append_char (c, bitsize);
 
+         /* Treat "a" "b" as "ab".  Even if we are appending zeros.  */
+         SKIP_ALL_WHITESPACE ();
+         if (*input_line_pointer == '"')
+           break;
+
          if (append_zero)
            stringer_append_char (0, bitsize);
 
-         know (input_line_pointer[-1] == '\"');
-
 #if !defined(NO_LISTING) && defined (OBJ_ELF)
          /* In ELF, when gcc is emitting DWARF 1 debugging output, it
             will emit .string with a filename in the .debug section
@@ -5597,8 +5552,11 @@ stringer (int bits_appendzero)
          c = get_single_number ();
          stringer_append_char (c, bitsize);
          if (*input_line_pointer != '>')
-           as_bad (_("expected <nn>"));
-
+           {
+             as_bad (_("expected <nn>"));
+             ignore_rest_of_line ();
+             return;
+           }
          input_line_pointer++;
          break;
        case ',':
@@ -5640,8 +5598,9 @@ next_char_of_string (void)
       bump_line_counters ();
       break;
 
-#ifndef NO_STRING_ESCAPES
     case '\\':
+      if (!TC_STRING_ESCAPES)
+       break;
       switch (c = *input_line_pointer++ & CHAR_MASK)
        {
        case 'b':
@@ -5683,7 +5642,7 @@ next_char_of_string (void)
        case '8':
        case '9':
          {
-           long number;
+           unsigned number;
            int i;
 
            for (i = 0, number = 0;
@@ -5701,7 +5660,7 @@ next_char_of_string (void)
        case 'x':
        case 'X':
          {
-           long number;
+           unsigned number;
 
            number = 0;
            c = *input_line_pointer++;
@@ -5743,7 +5702,6 @@ next_char_of_string (void)
          break;
        }
       break;
-#endif /* ! defined (NO_STRING_ESCAPES) */
 
     default:
       break;
@@ -5813,12 +5771,12 @@ demand_copy_C_string (int *len_pointer)
 
       for (len = *len_pointer; len > 0; len--)
        {
-         if (*s == 0)
+         if (s[len - 1] == 0)
            {
              s = 0;
-             len = 1;
              *len_pointer = 0;
              as_bad (_("this string may not contain \'\\0\'"));
+             break;
            }
        }
     }
@@ -5979,7 +5937,16 @@ s_incbin (int x ATTRIBUTE_UNUSED)
   if (binfile)
     {
       long   file_len;
+      struct stat filestat;
 
+      if (fstat (fileno (binfile), &filestat) != 0
+         || ! S_ISREG (filestat.st_mode)
+         || S_ISDIR (filestat.st_mode))
+       {
+         as_bad (_("unable to include `%s'"), path);
+         goto done;
+       }
+      
       register_dependency (path);
 
       /* Compute the length of the file.  */
@@ -6015,11 +5982,10 @@ s_incbin (int x ATTRIBUTE_UNUSED)
        as_warn (_("truncated file `%s', %ld of %ld bytes read"),
                 path, bytes, count);
     }
-done:
+ done:
   if (binfile != NULL)
     fclose (binfile);
-  if (path)
-    free (path);
+  free (path);
 }
 
 /* .include -- include a file at this point.  */
@@ -6079,7 +6045,7 @@ s_include (int arg ATTRIBUTE_UNUSED)
 
   free (path);
   path = filename;
-gotit:
+ gotit:
   /* malloc Storage leak when file is found on path.  FIXME-SOMEDAY.  */
   register_dependency (path);
   input_scrub_insert_file (path);
@@ -6320,7 +6286,7 @@ s_ignore (int arg ATTRIBUTE_UNUSED)
 void
 read_print_statistics (FILE *file)
 {
-  hash_print_statistics (file, "pseudo-op table", po_hash);
+  htab_print_statistics (file, "pseudo-op table", po_hash);
 }
 
 /* Inserts the given line into the input stream.
@@ -6419,10 +6385,7 @@ static char *saved_limit;
    overruns should not occur.  Saves the current input line pointer so that
    it can be restored by calling restore_ilp().
 
-   Does not support recursion.
-
-   FIXME: This function is currently only used by stabs.c but that
-   should be extended to other files in the gas source directory.  */
+   Does not support recursion.  */
 
 void
 temp_ilp (char *buf)
@@ -6439,7 +6402,7 @@ temp_ilp (char *buf)
 
   input_line_pointer = buf;
   buffer_limit = buf + strlen (buf);
-  input_from_string = TRUE;
+  input_from_string = true;
 }
 
 /* Restore a saved input line pointer.  */
@@ -6451,7 +6414,7 @@ restore_ilp (void)
 
   input_line_pointer = saved_ilp;
   buffer_limit = saved_limit;
-  input_from_string = FALSE;
+  input_from_string = false;
 
   saved_ilp = NULL;
 }