X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fexpr.c;h=2341343bf007d4687971cd1d5ceab70eb2448937;hb=caf606c90d55305967b9253447dda93d2f1835ab;hp=620fdce31d76c0ed9334f5c4608d9747fa1eca2a;hpb=3df4e177c9bdd2dd095a3c4e437ffba47cf32b37;p=binutils-gdb.git diff --git a/gas/expr.c b/gas/expr.c index 620fdce31d7..2341343bf00 100644 --- a/gas/expr.c +++ b/gas/expr.c @@ -1,7 +1,5 @@ /* expr.c -operands, expressions- - Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, - 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 - Free Software Foundation, Inc. + Copyright (C) 1987-2022 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. @@ -29,27 +27,15 @@ #include "as.h" #include "safe-ctype.h" -#include "obstack.h" -#ifdef HAVE_LIMITS_H #include -#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. */ @@ -57,7 +43,7 @@ extern const char EXP_CHARS[], FLT_CHARS[]; struct expr_symbol_line { struct expr_symbol_line *next; symbolS *sym; - char *file; + const char *file; unsigned int line; }; @@ -90,6 +76,7 @@ make_expr_symbol (expressionS *expressionP) zero.X_op = O_constant; zero.X_add_number = 0; zero.X_unsigned = 0; + zero.X_extrabit = 0; clean_up_expression (&zero); expressionP = &zero; } @@ -104,15 +91,15 @@ 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; - as_where (&n->file, &n->line); + n->file = as_where (&n->line); n->next = expr_symbol_lines; expr_symbol_lines = n; @@ -124,9 +111,9 @@ make_expr_symbol (expressionS *expressionP) the symbol. */ int -expr_symbol_where (symbolS *sym, char **pfile, unsigned int *pline) +expr_symbol_where (symbolS *sym, const char **pfile, unsigned int *pline) { - register struct expr_symbol_line *l; + struct expr_symbol_line *l; for (l = expr_symbol_lines; l != NULL; l = l->next) { @@ -140,6 +127,52 @@ expr_symbol_where (symbolS *sym, 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; +} /* Utilities for building expressions. Since complex expressions are recorded as symbols for use in other @@ -161,6 +194,7 @@ expr_build_uconstant (offsetT value) e.X_op = O_constant; e.X_add_number = value; e.X_unsigned = 1; + e.X_extrabit = 0; return make_expr_symbol (&e); } @@ -172,7 +206,7 @@ expr_build_dot (void) expressionS e; current_location (&e); - return make_expr_symbol (&e); + return symbol_clone_if_forward_ref (make_expr_symbol (&e)); } /* Build any floating-point literal here. @@ -221,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) @@ -286,6 +314,12 @@ integer_constant (int radix, expressionS *expressionP) #define valuesize 32 #endif + if (is_end_of_line[(unsigned char) *input_line_pointer]) + { + expressionP->X_op = O_absent; + return; + } + if ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) && radix == 0) { int flt = 0; @@ -505,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. @@ -518,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); @@ -552,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 @@ -571,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); } @@ -705,7 +754,7 @@ current_location (expressionS *expressionp) else { expressionp->X_op = O_symbol; - expressionp->X_add_symbol = symbol_temp_new_now (); + expressionp->X_add_symbol = &dot_symbol; expressionp->X_add_number = 0; } } @@ -732,6 +781,7 @@ operand (expressionS *expressionP, enum expr_mode mode) something like ``.quad 0x80000000'' is not sign extended even though it appears negative if valueT is 32 bits. */ expressionP->X_unsigned = 1; + expressionP->X_extrabit = 0; /* Digits, assume it is a bignum. */ @@ -759,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); @@ -832,32 +873,28 @@ operand (expressionS *expressionP, enum expr_mode mode) break; case 'b': - if (LOCAL_LABELS_FB && ! (flag_m68k_mri || NUMBERS_WITH_SUFFIX)) + if (LOCAL_LABELS_FB && !flag_m68k_mri + && input_line_pointer[1] != '0' + && input_line_pointer[1] != '1') { - /* This code used to check for '+' and '-' here, and, in - some conditions, fall through to call - integer_constant. However, that didn't make sense, - as integer_constant only accepts digits. */ - /* Some of our code elsewhere does permit digits greater - than the expected base; for consistency, do the same - here. */ - if (input_line_pointer[1] < '0' - || input_line_pointer[1] > '9') - { - /* Parse this as a back reference to label 0. */ - input_line_pointer--; - integer_constant (10, expressionP); - break; - } - /* Otherwise, parse this as a binary number. */ + /* Parse this as a back reference to label 0. */ + input_line_pointer--; + integer_constant (10, expressionP); + break; } + /* Otherwise, parse this as a binary number. */ /* Fall through. */ case 'B': - input_line_pointer++; + if (input_line_pointer[1] == '0' + || input_line_pointer[1] == '1') + { + input_line_pointer++; + integer_constant (2, expressionP); + break; + } if (flag_m68k_mri || NUMBERS_WITH_SUFFIX) - goto default_case; - integer_constant (2, expressionP); - break; + input_line_pointer++; + goto default_case; case '0': case '1': @@ -875,48 +912,35 @@ operand (expressionS *expressionP, enum expr_mode mode) case 'f': if (LOCAL_LABELS_FB) { + int is_label = 1; + /* If it says "0f" and it could possibly be a floating point number, make it one. Otherwise, make it a local label, and try to deal with parsing the rest later. */ - if (!input_line_pointer[1] - || (is_end_of_line[0xff & input_line_pointer[1]]) - || strchr (FLT_CHARS, 'f') == NULL) - goto is_0f_label; - { - char *cp = input_line_pointer + 1; - int r = atof_generic (&cp, ".", EXP_CHARS, - &generic_floating_point_number); - switch (r) - { - case 0: - case ERROR_EXPONENT_OVERFLOW: - if (*cp == 'f' || *cp == 'b') - /* Looks like a difference expression. */ - goto is_0f_label; - else if (cp == input_line_pointer + 1) - /* No characters has been accepted -- looks like - end of operand. */ - goto is_0f_label; - else - goto is_0f_float; - default: - as_fatal (_("expr.c(operand): bad atof_generic return val %d"), - r); - } - } - - /* Okay, now we've sorted it out. We resume at one of these - two labels, depending on what we've decided we're probably - looking at. */ - is_0f_label: - input_line_pointer--; - integer_constant (10, expressionP); - break; - - is_0f_float: - /* Fall through. */ - ; + if (!is_end_of_line[(unsigned char) input_line_pointer[1]] + && strchr (FLT_CHARS, 'f') != NULL) + { + char *cp = input_line_pointer + 1; + + atof_generic (&cp, ".", EXP_CHARS, + &generic_floating_point_number); + + /* Was nothing parsed, or does it look like an + expression? */ + is_label = (cp == input_line_pointer + 1 + || (cp == input_line_pointer + 2 + && (cp[-1] == '-' || cp[-1] == '+')) + || *cp == 'f' + || *cp == 'b'); + } + if (is_label) + { + input_line_pointer--; + integer_constant (10, expressionP); + break; + } } + /* Fall through. */ case 'd': case 'D': @@ -955,18 +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. */ - if (mode != expr_defer) - segment = expression (expressionP); - else - segment = deferred_expression (expressionP); + 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 (); @@ -983,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) { @@ -1005,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 '+': @@ -1024,16 +1052,26 @@ operand (expressionS *expressionP, enum expr_mode mode) /* input_line_pointer -> char after operand. */ if (c == '-') { - expressionP->X_add_number = - expressionP->X_add_number; + expressionP->X_add_number + = - (addressT) expressionP->X_add_number; /* Notice: '-' may overflow: no warning is given. This is compatible with other people's assemblers. Sigh. */ expressionP->X_unsigned = 0; + if (expressionP->X_add_number) + 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 @@ -1081,6 +1119,7 @@ operand (expressionS *expressionP, enum expr_mode mode) expressionP->X_add_number = i >= expressionP->X_add_number; expressionP->X_op = O_constant; expressionP->X_unsigned = 1; + expressionP->X_extrabit = 0; } } else if (expressionP->X_op != O_illegal @@ -1104,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. */ @@ -1144,31 +1197,32 @@ 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 (); - name = input_line_pointer; - c = get_symbol_end (); - - 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); + c = get_symbol_name (& name); + 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; - SKIP_WHITESPACE (); + SKIP_WHITESPACE_AFTER_NAME (); if (*input_line_pointer != ')') as_bad (_("syntax error in .startof. or .sizeof.")); else @@ -1224,13 +1278,13 @@ operand (expressionS *expressionP, enum expr_mode mode) #if defined(md_need_index_operator) || defined(TC_M68K) de_fault: #endif - if (is_name_beginner (c)) /* Here if did not begin with a digit. */ + if (is_name_beginner (c) || c == '"') /* Here if did not begin with a digit. */ { /* Identifier begins here. This is kludged for speed, so code is repeated. */ isname: - name = --input_line_pointer; - c = get_symbol_end (); + -- input_line_pointer; + c = get_symbol_name (&name); #ifdef md_operator { @@ -1239,15 +1293,15 @@ operand (expressionS *expressionP, enum expr_mode mode) switch (op) { case O_uminus: - *input_line_pointer = c; + restore_line_pointer (c); c = '-'; goto unary; case O_bit_not: - *input_line_pointer = c; + restore_line_pointer (c); c = '~'; goto unary; case O_logical_not: - *input_line_pointer = c; + restore_line_pointer (c); c = '!'; goto unary; case O_illegal: @@ -1256,9 +1310,10 @@ operand (expressionS *expressionP, enum expr_mode mode) default: break; } + if (op != O_absent && op != O_illegal) { - *input_line_pointer = c; + restore_line_pointer (c); expr (9, expressionP, mode); expressionP->X_add_symbol = make_expr_symbol (expressionP); expressionP->X_op_symbol = NULL; @@ -1276,46 +1331,7 @@ operand (expressionS *expressionP, enum expr_mode mode) entering it in the symbol table. */ if (md_parse_name (name, expressionP, mode, &c)) { - *input_line_pointer = c; - break; - } -#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 (); - - name = input_line_pointer; - c = get_symbol_end (); - - 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 (); - + restore_line_pointer (c); break; } #endif @@ -1325,7 +1341,9 @@ operand (expressionS *expressionP, enum expr_mode mode) /* If we have an absolute symbol or a reg, then we know its value now. */ segment = S_GET_SEGMENT (symbolP); - if (mode != expr_defer && segment == absolute_section) + if (mode != expr_defer + && segment == absolute_section + && !S_FORCE_RELOC (symbolP, 0)) { expressionP->X_op = O_constant; expressionP->X_add_number = S_GET_VALUE (symbolP); @@ -1341,7 +1359,8 @@ operand (expressionS *expressionP, enum expr_mode mode) expressionP->X_add_symbol = symbolP; expressionP->X_add_number = 0; } - *input_line_pointer = c; + + restore_line_pointer (c); } else { @@ -1366,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. */ @@ -1597,8 +1616,8 @@ operatorf (int *num_chars) #ifdef md_operator if (is_name_beginner (c)) { - char *name = input_line_pointer; - char ec = get_symbol_end (); + char *name; + char ec = get_symbol_name (& name); ret = md_operator (name, 2, &ec); switch (ret) @@ -1718,6 +1737,42 @@ operatorf (int *num_chars) /* NOTREACHED */ } +/* Implement "word-size + 1 bit" addition for + {resultP->X_extrabit:resultP->X_add_number} + {rhs_highbit:amount}. This + is used so that the full range of unsigned word values and the full range of + signed word values can be represented in an O_constant expression, which is + useful e.g. for .sleb128 directives. */ + +void +add_to_result (expressionS *resultP, offsetT amount, int rhs_highbit) +{ + valueT ures = resultP->X_add_number; + valueT uamount = amount; + + resultP->X_add_number += uamount; + + resultP->X_extrabit ^= rhs_highbit; + + if (ures + uamount < ures) + resultP->X_extrabit ^= 1; +} + +/* Similarly, for subtraction. */ + +void +subtract_from_result (expressionS *resultP, offsetT amount, int rhs_highbit) +{ + valueT ures = resultP->X_add_number; + valueT uamount = amount; + + resultP->X_add_number -= uamount; + + resultP->X_extrabit ^= rhs_highbit; + + if (ures < uamount) + resultP->X_extrabit ^= 1; +} + /* Parse an expression. */ segT @@ -1736,7 +1791,10 @@ expr (int rankarg, /* Larger # is higher rank. */ /* Save the value of dot for the fixup code. */ if (rank == 0) - dot_value = frag_now_fix (); + { + dot_value = frag_now_fix (); + dot_frag = frag_now; + } retval = operand (resultP, mode); @@ -1747,7 +1805,7 @@ expr (int rankarg, /* Larger # is higher rank. */ while (op_left != O_illegal && op_rank[(int) op_left] > rank) { segT rightseg; - bfd_vma frag_off; + offsetT frag_off; input_line_pointer += op_chars; /* -> after operator. */ @@ -1814,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)) @@ -1830,7 +1895,7 @@ expr (int rankarg, /* Larger # is higher rank. */ && (md_register_arithmetic || resultP->X_op != O_register)) { /* X + constant. */ - resultP->X_add_number += right.X_add_number; + add_to_result (resultP, right.X_add_number, right.X_extrabit); } /* This case comes up in PIC code. */ else if (op_left == O_subtract @@ -1840,16 +1905,19 @@ expr (int rankarg, /* Larger # is higher rank. */ #ifdef md_allow_local_subtract && md_allow_local_subtract (resultP, & right, rightseg) #endif - && (SEG_NORMAL (rightseg) + && ((SEG_NORMAL (rightseg) + && !S_FORCE_RELOC (resultP->X_add_symbol, 0) + && !S_FORCE_RELOC (right.X_add_symbol, 0)) || right.X_add_symbol == resultP->X_add_symbol) && frag_offset_fixed_p (symbol_get_frag (resultP->X_add_symbol), symbol_get_frag (right.X_add_symbol), &frag_off)) { - resultP->X_add_number -= right.X_add_number; - resultP->X_add_number -= frag_off / OCTETS_PER_BYTE; - resultP->X_add_number += (S_GET_VALUE (resultP->X_add_symbol) - - S_GET_VALUE (right.X_add_symbol)); + offsetT symval_diff = S_GET_VALUE (resultP->X_add_symbol) + - S_GET_VALUE (right.X_add_symbol); + subtract_from_result (resultP, right.X_add_number, right.X_extrabit); + subtract_from_result (resultP, frag_off / OCTETS_PER_BYTE, 0); + add_to_result (resultP, symval_diff, symval_diff < 0); resultP->X_op = O_constant; resultP->X_add_symbol = 0; } @@ -1857,7 +1925,7 @@ expr (int rankarg, /* Larger # is higher rank. */ && (md_register_arithmetic || resultP->X_op != O_register)) { /* X - constant. */ - resultP->X_add_number -= right.X_add_number; + subtract_from_result (resultP, right.X_add_number, right.X_extrabit); } else if (op_left == O_add && resultP->X_op == O_constant && (md_register_arithmetic || right.X_op != O_register)) @@ -1866,7 +1934,7 @@ expr (int rankarg, /* Larger # is higher rank. */ resultP->X_op = right.X_op; resultP->X_add_symbol = right.X_add_symbol; resultP->X_op_symbol = right.X_op_symbol; - resultP->X_add_number += right.X_add_number; + add_to_result (resultP, right.X_add_number, right.X_extrabit); retval = rightseg; } else if (resultP->X_op == O_constant && right.X_op == O_constant) @@ -1889,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; @@ -1906,7 +1988,9 @@ expr (int rankarg, /* Larger # is higher rank. */ /* Constant + constant (O_add) is handled by the previous if statement for constant + X, so is omitted here. */ - case O_subtract: resultP->X_add_number -= v; break; + case O_subtract: + subtract_from_result (resultP, v, 0); + break; case O_eq: resultP->X_add_number = resultP->X_add_number == v ? ~ (offsetT) 0 : 0; @@ -1950,11 +2034,15 @@ expr (int rankarg, /* Larger # is higher rank. */ resultP->X_op = op_left; resultP->X_op_symbol = right.X_add_symbol; if (op_left == O_add) - resultP->X_add_number += right.X_add_number; + add_to_result (resultP, right.X_add_number, right.X_extrabit); else if (op_left == O_subtract) { - resultP->X_add_number -= right.X_add_number; - if (retval == rightseg && SEG_NORMAL (retval)) + subtract_from_result (resultP, right.X_add_number, + right.X_extrabit); + if (retval == rightseg + && SEG_NORMAL (retval) + && !S_FORCE_RELOC (resultP->X_add_symbol, 0) + && !S_FORCE_RELOC (right.X_add_symbol, 0)) { retval = absolute_section; rightseg = absolute_section; @@ -1970,6 +2058,7 @@ expr (int rankarg, /* Larger # is higher rank. */ resultP->X_op = op_left; resultP->X_add_number = 0; resultP->X_unsigned = 1; + resultP->X_extrabit = 0; } if (retval != rightseg) @@ -2026,7 +2115,7 @@ resolve_expression (expressionS *expressionP) valueT left, right; segT seg_left, seg_right; fragS *frag_left, *frag_right; - bfd_vma frag_off; + offsetT frag_off; switch (op) { @@ -2138,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))) { @@ -2277,31 +2369,97 @@ resolve_expression (expressionS *expressionP) expr.c is just a branch office read.c anyway, and putting it here lessens the crowd at read.c. - Assume input_line_pointer is at start of symbol name. + Assume input_line_pointer is at start of symbol name, or the + start of a double quote enclosed symbol name. Advance input_line_pointer past symbol name. - Turn that character into a '\0', returning its former value. + Turn that character into a '\0', returning its former value, + which may be the closing double quote. This allows a string compare (RMS wants symbol names to be strings) - of the symbol name. + of the symbol name. There will always be a char following symbol name, because all good lines end in end-of-line. */ char -get_symbol_end (void) +get_symbol_name (char ** ilp_return) { char c; - /* We accept \001 in a name in case this is being called with a + * ilp_return = input_line_pointer; + /* 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 == '"') + { + char *dst = input_line_pointer; + + * ilp_return = input_line_pointer; + for (;;) + { + c = *input_line_pointer++; + + if (c == 0) + { + as_warn (_("missing closing '\"'")); + break; + } + + if (c == '"') + { + char *ilp_save = input_line_pointer; + + 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); + return c; +} + +/* Replace the NUL character pointed to by input_line_pointer + with C. If C is \" then advance past it. Return the character + now pointed to by input_line_pointer. */ + +char +restore_line_pointer (char c) +{ + * input_line_pointer = c; + if (c == '"') + c = * ++ input_line_pointer; + return c; } unsigned int