libtool.m4: fix the NM="/nm/over/here -B/option/with/path" case
[binutils-gdb.git] / gas / expr.c
index 777504f1f8004821f6f19f584b603ea66850b655..2341343bf007d4687971cd1d5ceab70eb2448937 100644 (file)
@@ -1,5 +1,5 @@
 /* expr.c -operands, expressions-
-   Copyright (C) 1987-2016 Free Software Foundation, Inc.
+   Copyright (C) 1987-2022 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
 #include "as.h"
 #include "safe-ctype.h"
 
-#ifdef HAVE_LIMITS_H
 #include <limits.h>
-#endif
 #ifndef CHAR_BIT
 #define CHAR_BIT 8
 #endif
 
-static void floating_constant (expressionS * expressionP);
-static valueT generic_bignum_to_int32 (void);
-#ifdef BFD64
-static valueT generic_bignum_to_int64 (void);
-#endif
-static void integer_constant (int radix, expressionS * expressionP);
-static void mri_char_constant (expressionS *);
-static void clean_up_expression (expressionS * expressionP);
-static segT operand (expressionS *, enum expr_mode);
-static operatorT operatorf (int *);
+bool literal_prefix_dollar_hex = false;
 
-extern const char EXP_CHARS[], FLT_CHARS[];
+static void clean_up_expression (expressionS * expressionP);
 
 /* We keep a mapping of expression symbols to file positions, so that
    we can provide better error messages.  */
@@ -102,13 +91,13 @@ make_expr_symbol (expressionS *expressionP)
                            : expressionP->X_op == O_register
                              ? reg_section
                              : expr_section),
-                          0, &zero_address_frag);
+                          &zero_address_frag, 0);
   symbol_set_value_expression (symbolP, expressionP);
 
   if (expressionP->X_op == O_constant)
     resolve_symbol_value (symbolP);
 
-  n = (struct expr_symbol_line *) xmalloc (sizeof *n);
+  n = XNEW (struct expr_symbol_line);
   n->sym = symbolP;
   n->file = as_where (&n->line);
   n->next = expr_symbol_lines;
@@ -138,6 +127,52 @@ expr_symbol_where (symbolS *sym, const char **pfile, unsigned int *pline)
 
   return 0;
 }
+
+/* Look up a previously used .startof. / .sizeof. symbol, or make a fresh
+   one.  */
+
+static symbolS *
+symbol_lookup_or_make (const char *name, bool start)
+{
+  static symbolS **seen[2];
+  static unsigned int nr_seen[2];
+  char *buf = concat (start ? ".startof." : ".sizeof.", name, NULL);
+  symbolS *symbolP;
+  unsigned int i;
+
+  for (i = 0; i < nr_seen[start]; ++i)
+    {
+    symbolP = seen[start][i];
+
+    if (! symbolP)
+      break;
+
+    name = S_GET_NAME (symbolP);
+    if ((symbols_case_sensitive
+        ? strcasecmp (buf, name)
+        : strcmp (buf, name)) == 0)
+      {
+       free (buf);
+       return symbolP;
+      }
+    }
+
+  symbolP = symbol_make (buf);
+  free (buf);
+
+  if (i >= nr_seen[start])
+    {
+      unsigned int nr = (i + 1) * 2;
+
+      seen[start] = XRESIZEVEC (symbolS *, seen[start], nr);
+      nr_seen[start] = nr;
+      memset (&seen[start][i + 1], 0, (nr - i - 1) * sizeof(seen[0][0]));
+    }
+
+  seen[start][i] = symbolP;
+
+  return symbolP;
+}
 \f
 /* Utilities for building expressions.
    Since complex expressions are recorded as symbols for use in other
@@ -220,31 +255,25 @@ floating_constant (expressionS *expressionP)
   expressionP->X_add_number = -1;
 }
 
-static valueT
+uint32_t
 generic_bignum_to_int32 (void)
 {
-  valueT number =
-          ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS)
-          | (generic_bignum[0] & LITTLENUM_MASK);
-  number &= 0xffffffff;
-  return number;
+  return ((((uint32_t) generic_bignum[1] & LITTLENUM_MASK)
+          << LITTLENUM_NUMBER_OF_BITS)
+         | ((uint32_t) generic_bignum[0] & LITTLENUM_MASK));
 }
 
-#ifdef BFD64
-static valueT
+uint64_t
 generic_bignum_to_int64 (void)
 {
-  valueT number =
-    ((((((((valueT) generic_bignum[3] & LITTLENUM_MASK)
-         << LITTLENUM_NUMBER_OF_BITS)
-        | ((valueT) generic_bignum[2] & LITTLENUM_MASK))
-       << LITTLENUM_NUMBER_OF_BITS)
-       | ((valueT) generic_bignum[1] & LITTLENUM_MASK))
-      << LITTLENUM_NUMBER_OF_BITS)
-     | ((valueT) generic_bignum[0] & LITTLENUM_MASK));
-  return number;
+  return ((((((((uint64_t) generic_bignum[3] & LITTLENUM_MASK)
+              << LITTLENUM_NUMBER_OF_BITS)
+             | ((uint64_t) generic_bignum[2] & LITTLENUM_MASK))
+            << LITTLENUM_NUMBER_OF_BITS)
+           | ((uint64_t) generic_bignum[1] & LITTLENUM_MASK))
+          << LITTLENUM_NUMBER_OF_BITS)
+         | ((uint64_t) generic_bignum[0] & LITTLENUM_MASK));
 }
-#endif
 
 static void
 integer_constant (int radix, expressionS *expressionP)
@@ -510,6 +539,21 @@ integer_constant (int radix, expressionS *expressionP)
       && input_line_pointer - 1 == suffix)
     c = *input_line_pointer++;
 
+#ifndef tc_allow_U_suffix
+#define tc_allow_U_suffix 1
+#endif
+  /* PR 19910: Look for, and ignore, a U suffix to the number.  */
+  if (tc_allow_U_suffix && (c == 'U' || c == 'u'))
+    c = * input_line_pointer++;
+
+#ifndef tc_allow_L_suffix
+#define tc_allow_L_suffix 1
+#endif
+  /* PR 20732: Look for, and ignore, a L or LL suffix to the number.  */
+  if (tc_allow_L_suffix)
+    while (c == 'L' || c == 'l')
+      c = * input_line_pointer++;
+
   if (small)
     {
       /* Here with number, in correct radix. c is the next char.
@@ -523,7 +567,7 @@ integer_constant (int radix, expressionS *expressionP)
          /* Backward ref to local label.
             Because it is backward, expect it to be defined.  */
          /* Construct a local label.  */
-         name = fb_label_name ((int) number, 0);
+         name = fb_label_name (number, 0);
 
          /* Seen before, or symbol is defined: OK.  */
          symbolP = symbol_find (name);
@@ -557,7 +601,7 @@ integer_constant (int radix, expressionS *expressionP)
             Construct a local label name, then an undefined symbol.
             Don't create a xseg frag for it: caller may do that.
             Just return it as never seen before.  */
-         name = fb_label_name ((int) number, 1);
+         name = fb_label_name (number, 1);
          symbolP = symbol_find_or_make (name);
          /* We have no need to check symbol properties.  */
 #ifndef many_segments
@@ -576,15 +620,15 @@ integer_constant (int radix, expressionS *expressionP)
             then this is a fresh instantiation of that number, so create
             it.  */
 
-         if (dollar_label_defined ((long) number))
+         if (dollar_label_defined (number))
            {
-             name = dollar_label_name ((long) number, 0);
+             name = dollar_label_name (number, 0);
              symbolP = symbol_find (name);
              know (symbolP != NULL);
            }
          else
            {
-             name = dollar_label_name ((long) number, 1);
+             name = dollar_label_name (number, 1);
              symbolP = symbol_find_or_make (name);
            }
 
@@ -765,15 +809,6 @@ operand (expressionS *expressionP, enum expr_mode mode)
                        expressionP);
       break;
 
-#ifdef LITERAL_PREFIXDOLLAR_HEX
-    case '$':
-      /* $L is the start of a local label, not a hex constant.  */
-      if (* input_line_pointer == 'L')
-      goto isname;
-      integer_constant (16, expressionP);
-      break;
-#endif
-
 #ifdef LITERAL_PREFIXPERCENT_BIN
     case '%':
       integer_constant (2, expressionP);
@@ -944,15 +979,21 @@ operand (expressionS *expressionP, enum expr_mode mode)
       if (md_need_index_operator())
        goto de_fault;
 # endif
-      /* FALLTHROUGH */
 #endif
+      /* Fall through.  */
     case '(':
       /* Didn't begin with digit & not a name.  */
       segment = expr (0, expressionP, mode);
       /* expression () will pass trailing whitespace.  */
       if ((c == '(' && *input_line_pointer != ')')
          || (c == '[' && *input_line_pointer != ']'))
-       as_bad (_("missing '%c'"), c == '(' ? ')' : ']');
+       {
+         if (* input_line_pointer)
+           as_bad (_("found '%c', expected: '%c'"),
+                   * input_line_pointer, c == '(' ? ')' : ']');
+         else
+           as_bad (_("missing '%c'"), c == '(' ? ')' : ']');
+       }           
       else
        input_line_pointer++;
       SKIP_WHITESPACE ();
@@ -969,8 +1010,8 @@ operand (expressionS *expressionP, enum expr_mode mode)
       if (! flag_m68k_mri || *input_line_pointer != '\'')
        goto de_fault;
       ++input_line_pointer;
-      /* Fall through.  */
 #endif
+      /* Fall through.  */
     case '\'':
       if (! flag_m68k_mri)
        {
@@ -991,12 +1032,13 @@ operand (expressionS *expressionP, enum expr_mode mode)
       /* Double quote is the bitwise not operator in MRI mode.  */
       if (! flag_m68k_mri)
        goto de_fault;
-      /* Fall through.  */
 #endif
+      /* Fall through.  */
     case '~':
       /* '~' is permitted to start a label on the Delta.  */
       if (is_name_beginner (c))
        goto isname;
+      /* Fall through.  */
     case '!':
     case '-':
     case '+':
@@ -1020,9 +1062,16 @@ operand (expressionS *expressionP, enum expr_mode mode)
                  expressionP->X_extrabit ^= 1;
              }
            else if (c == '~' || c == '"')
-             expressionP->X_add_number = ~ expressionP->X_add_number;
+             {
+               expressionP->X_add_number = ~ expressionP->X_add_number;
+               expressionP->X_extrabit ^= 1;
+             }
            else if (c == '!')
-             expressionP->X_add_number = ! expressionP->X_add_number;
+             {
+               expressionP->X_add_number = ! expressionP->X_add_number;
+               expressionP->X_unsigned = 1;
+               expressionP->X_extrabit = 0;
+             }
          }
        else if (expressionP->X_op == O_big
                 && expressionP->X_add_number <= 0
@@ -1094,7 +1143,21 @@ operand (expressionS *expressionP, enum expr_mode mode)
       }
       break;
 
-#if defined (DOLLAR_DOT) || defined (TC_M68K)
+#if !defined (DOLLAR_DOT) && !defined (TC_M68K)
+    case '$':
+      if (literal_prefix_dollar_hex)
+       {
+         /* $L is the start of a local label, not a hex constant.  */
+         if (* input_line_pointer == 'L')
+               goto isname;
+         integer_constant (16, expressionP);
+       }
+      else
+       {
+         goto isname;
+       }
+      break;
+#else
     case '$':
       /* '$' is the program counter when in MRI mode, or when
         DOLLAR_DOT is defined.  */
@@ -1134,26 +1197,28 @@ operand (expressionS *expressionP, enum expr_mode mode)
                   || input_line_pointer[1] == 'T');
          input_line_pointer += start ? 8 : 7;
          SKIP_WHITESPACE ();
+
+         /* Cover for the as_bad () invocations below.  */
+         expressionP->X_op = O_absent;
+
          if (*input_line_pointer != '(')
            as_bad (_("syntax error in .startof. or .sizeof."));
          else
            {
-             char *buf;
-
              ++input_line_pointer;
              SKIP_WHITESPACE ();
              c = get_symbol_name (& name);
-
-             buf = (char *) xmalloc (strlen (name) + 10);
-             if (start)
-               sprintf (buf, ".startof.%s", name);
-             else
-               sprintf (buf, ".sizeof.%s", name);
-             symbolP = symbol_make (buf);
-             free (buf);
+             if (! *name)
+               {
+                 as_bad (_("expected symbol name"));
+                 (void) restore_line_pointer (c);
+                 if (c == ')')
+                   ++input_line_pointer;
+                 break;
+               }
 
              expressionP->X_op = O_symbol;
-             expressionP->X_add_symbol = symbolP;
+             expressionP->X_add_symbol = symbol_lookup_or_make (name, start);
              expressionP->X_add_number = 0;
 
              *input_line_pointer = c;
@@ -1271,43 +1336,6 @@ operand (expressionS *expressionP, enum expr_mode mode)
            }
 #endif
 
-#ifdef TC_I960
-         /* The MRI i960 assembler permits
-                lda sizeof code,g13
-            FIXME: This should use md_parse_name.  */
-         if (flag_mri
-             && (strcasecmp (name, "sizeof") == 0
-                 || strcasecmp (name, "startof") == 0))
-           {
-             int start;
-             char *buf;
-
-             start = (name[1] == 't'
-                      || name[1] == 'T');
-
-             *input_line_pointer = c;
-             SKIP_WHITESPACE_AFTER_NAME ();
-
-             c = get_symbol_name (& name);
-
-             buf = (char *) xmalloc (strlen (name) + 10);
-             if (start)
-               sprintf (buf, ".startof.%s", name);
-             else
-               sprintf (buf, ".sizeof.%s", name);
-             symbolP = symbol_make (buf);
-             free (buf);
-
-             expressionP->X_op = O_symbol;
-             expressionP->X_add_symbol = symbolP;
-             expressionP->X_add_number = 0;
-
-             *input_line_pointer = c;
-             SKIP_WHITESPACE_AFTER_NAME ();
-             break;
-           }
-#endif
-
          symbolP = symbol_find_or_make (name);
 
          /* If we have an absolute symbol or a reg, then we know its
@@ -1357,7 +1385,7 @@ operand (expressionS *expressionP, enum expr_mode mode)
   /* It is more 'efficient' to clean up the expressionS when they are
      created.  Doing it here saves lines of code.  */
   clean_up_expression (expressionP);
-  SKIP_WHITESPACE ();          /* -> 1st char after operand.  */
+  SKIP_ALL_WHITESPACE ();              /* -> 1st char after operand.  */
   know (*input_line_pointer != ' ');
 
   /* The PA port needs this information.  */
@@ -1721,7 +1749,7 @@ add_to_result (expressionS *resultP, offsetT amount, int rhs_highbit)
   valueT ures = resultP->X_add_number;
   valueT uamount = amount;
 
-  resultP->X_add_number += amount;
+  resultP->X_add_number += uamount;
 
   resultP->X_extrabit ^= rhs_highbit;
 
@@ -1737,7 +1765,7 @@ subtract_from_result (expressionS *resultP, offsetT amount, int rhs_highbit)
   valueT ures = resultP->X_add_number;
   valueT uamount = amount;
 
-  resultP->X_add_number -= amount;
+  resultP->X_add_number -= uamount;
 
   resultP->X_extrabit ^= rhs_highbit;
 
@@ -1844,6 +1872,13 @@ expr (int rankarg,               /* Larger # is higher rank.  */
          right.X_op_symbol = NULL;
        }
 
+      if (mode == expr_defer
+         && ((resultP->X_add_symbol != NULL
+              && S_IS_FORWARD_REF (resultP->X_add_symbol))
+             || (right.X_add_symbol != NULL
+                 && S_IS_FORWARD_REF (right.X_add_symbol))))
+       goto general;
+
       /* Optimize common cases.  */
 #ifdef md_optimize_expr
       if (md_optimize_expr (resultP, op_left, &right))
@@ -1922,15 +1957,29 @@ expr (int rankarg,              /* Larger # is higher rank.  */
          switch (op_left)
            {
            default:                    goto general;
-           case O_multiply:            resultP->X_add_number *= v; break;
+           case O_multiply:
+             /* Do the multiply as unsigned to silence ubsan.  The
+                result is of course the same when we throw away high
+                bits of the result.  */
+             resultP->X_add_number *= (valueT) v;
+             break;
            case O_divide:              resultP->X_add_number /= v; break;
            case O_modulus:             resultP->X_add_number %= v; break;
-           case O_left_shift:          resultP->X_add_number <<= v; break;
+           case O_left_shift:
+             /* We always use unsigned shifts.  According to the ISO
+                C standard, left shift of a signed type having a
+                negative value is undefined behaviour, and right
+                shift of a signed type having negative value is
+                implementation defined.  Left shift of a signed type
+                when the result overflows is also undefined
+                behaviour.  So don't trigger ubsan warnings or rely
+                on characteristics of the compiler.  */
+             resultP->X_add_number
+               = (valueT) resultP->X_add_number << (valueT) v;
+             break;
            case O_right_shift:
-             /* We always use unsigned shifts, to avoid relying on
-                characteristics of the compiler used to compile gas.  */
-             resultP->X_add_number =
-               (offsetT) ((valueT) resultP->X_add_number >> (valueT) v);
+             resultP->X_add_number
+               = (valueT) resultP->X_add_number >> (valueT) v;
              break;
            case O_bit_inclusive_or:    resultP->X_add_number |= v; break;
            case O_bit_or_not:          resultP->X_add_number |= ~v; break;
@@ -2178,7 +2227,10 @@ resolve_expression (expressionS *expressionP)
                || op == O_lt || op == O_le || op == O_ge || op == O_gt)
               && seg_left == seg_right
               && (finalize_syms
-                  || frag_offset_fixed_p (frag_left, frag_right, &frag_off))
+                  || frag_offset_fixed_p (frag_left, frag_right, &frag_off)
+                  || (op == O_gt
+                      && frag_gtoffset_p (left, frag_left,
+                                          right, frag_right, &frag_off)))
               && (seg_left != reg_section || left == right)
               && (seg_left != undefined_section || add_symbol == op_symbol)))
        {
@@ -2333,30 +2385,65 @@ get_symbol_name (char ** ilp_return)
   char c;
 
   * ilp_return = input_line_pointer;
-  /* We accept \001 in a name in case this is being called with a
+  /* We accept FAKE_LABEL_CHAR in a name in case this is being called with a
      constructed string.  */
-  if (is_name_beginner (c = *input_line_pointer++) || c == '\001')
+  if (is_name_beginner (c = *input_line_pointer++)
+      || (input_from_string && c == FAKE_LABEL_CHAR))
     {
       while (is_part_of_name (c = *input_line_pointer++)
-            || c == '\001')
+            || (input_from_string && c == FAKE_LABEL_CHAR))
        ;
       if (is_name_ender (c))
        c = *input_line_pointer++;
     }
   else if (c == '"')
     {
-      bfd_boolean backslash_seen;
+      char *dst = input_line_pointer;
 
       * ilp_return = input_line_pointer;
-      do
+      for (;;)
        {
-         backslash_seen = c == '\\';
-         c = * input_line_pointer ++;
-       }
-      while (c != 0 && (c != '"' || backslash_seen));
+         c = *input_line_pointer++;
+
+         if (c == 0)
+           {
+             as_warn (_("missing closing '\"'"));
+             break;
+           }
+
+         if (c == '"')
+           {
+             char *ilp_save = input_line_pointer;
 
-      if (c == 0)
-       as_warn (_("missing closing '\"'"));
+             SKIP_WHITESPACE ();
+             if (*input_line_pointer == '"')
+               {
+                 ++input_line_pointer;
+                 continue;
+               }
+             input_line_pointer = ilp_save;
+             break;
+           }
+
+         if (c == '\\')
+           switch (*input_line_pointer)
+             {
+             case '"':
+             case '\\':
+               c = *input_line_pointer++;
+               break;
+
+             default:
+               if (c != 0)
+                 as_warn (_("'\\%c' in quoted symbol name; "
+                            "behavior may change in the future"),
+                          *input_line_pointer);
+               break;
+             }
+
+         *dst++ = c;
+       }
+      *dst = 0;
     }
   *--input_line_pointer = 0;
   return c;