This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Re: [js@convergence.de: binutils-2.11.92.0.12.3 gas MIPS bug: nested parens in %hi]
- From: Thiemo Seufer <ica2_ts at csv dot ica dot uni-stuttgart dot de>
- To: Johannes Stezenbach <js at convergence dot de>
- Cc: binutils at sources dot redhat dot com, "H . J . Lu" <hjl at lucon dot org>
- Date: Sat, 12 Jan 2002 15:23:23 +0100
- Subject: Re: [js@convergence.de: binutils-2.11.92.0.12.3 gas MIPS bug: nested parens in %hi]
- References: <20020108141009.A29041@lucon.org> <20020108151315.A30013@lucon.org>
> > ----- Forwarded message from Johannes Stezenbach <js@convergence.de> -----
[snip]
> > the assembler of binutils-2.11.92.0.12.3 for MIPS has a bug which
> > shows up when parsing percent-ops like %hi and %lo with
> > nested parentheses, e.g.
> >
> > lui t0, %hi((0xa0000000 + 0x10001000))
> >
> > produces parsing errors because of unmatched parens
> > (sorry, I don't have the exact error message right now).
I overlooked this possibility when I changed/reimplemented this
functions to support NewABI's nested percent op's like
lui $1, %hi(%neg(%pc_rel(symbol)))
[snip]
> > I attempted to fix this problem (patch attached), but unfortunately
> > my understanding of gas' expression parser is very limited.
> > My patch is thus more a workaround-hack than a fix.
> >
> > I tried hard to figure out how my_getSmallExpression() in
> > gas/config/tc-mips.c works for nested precent-ops, but
> > either I failed or the thing is totally broken.
It is broken in several ways as I found out now.
> > I dont't know all the details of the MIPS assembler syntax,
> > but wouldn't it be the right way to use expr() (in gas/expr.c) for
> > parsing, and provide support for percent-ops via the
> > md_operand() hook? This should work since is_name_beginner('%')
> > returns FALSE and LITERAL_PREFIXPERCENT_BIN is #undef'd.
AFAICS this does not work for nested percent op's, so I expanded
your patch to fix some more flaws and did also some code cleanup.
It still looks ugly.
Thiemo
2002-01-12 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
Johannes Stezenbach <js@convergence.de>
/gas/ChangeLog
* config/tc-mips.c (percent_op): Ensure longer percent_op's are
matched before the shorter ones.
(my_getSmallParser): Fix handling of nested parentheses in
percent_op's. Code cleanup.
(my_getPercentOp): New function, code from my_getSmallParser.
(my_getSmallExpression): Fix handling of closing parentheses.
Code cleanup. Better comments.
diff -BurpNX /bigdisk/src/binutils-exclude src-orig/gas/config/tc-mips.c src/gas/config/tc-mips.c
--- src-orig/gas/config/tc-mips.c Thu Jan 10 21:14:28 2002
+++ src/gas/config/tc-mips.c Sat Jan 12 12:42:30 2002
@@ -702,6 +702,7 @@ static void mips16_ip PARAMS ((char *str
static void mips16_immed PARAMS ((char *, unsigned int, int, offsetT, boolean,
boolean, boolean, unsigned long *,
boolean *, unsigned short *));
+static int my_getPercentOp PARAMS ((char **, unsigned int *, int *));
static int my_getSmallParser PARAMS ((char **, unsigned int *, int *));
static int my_getSmallExpression PARAMS ((expressionS *, char *));
static void my_getExpression PARAMS ((expressionS *, char *));
@@ -9314,26 +9380,24 @@ static struct percent_op_match
const enum small_ex_type type;
} percent_op[] =
{
-#ifdef OBJ_ELF
- {"%half", S_EX_HALF},
-#endif
- {"%hi", S_EX_HI},
{"%lo", S_EX_LO},
#ifdef OBJ_ELF
- {"%gp_rel", S_EX_GP_REL},
- {"%got", S_EX_GOT},
+ {"%call_hi", S_EX_CALL_HI},
+ {"%call_lo", S_EX_CALL_LO},
{"%call16", S_EX_CALL16},
{"%got_disp", S_EX_GOT_DISP},
{"%got_page", S_EX_GOT_PAGE},
{"%got_ofst", S_EX_GOT_OFST},
{"%got_hi", S_EX_GOT_HI},
{"%got_lo", S_EX_GOT_LO},
- {"%neg", S_EX_NEG},
- {"%higher", S_EX_HIGHER},
+ {"%got", S_EX_GOT},
+ {"%gp_rel", S_EX_GP_REL},
+ {"%half", S_EX_HALF},
{"%highest", S_EX_HIGHEST},
- {"%call_hi", S_EX_CALL_HI},
- {"%call_lo", S_EX_CALL_LO}
+ {"%higher", S_EX_HIGHER},
+ {"%neg", S_EX_NEG},
#endif
+ {"%hi", S_EX_HI}
};
/* Parse small expression input. STR gets adjusted to eat up whitespace.
@@ -9347,10 +9411,9 @@ my_getSmallParser (str, len, nestlevel)
unsigned int *len;
int *nestlevel;
{
- int type = S_EX_NONE;
-
*len = 0;
*str += strspn (*str, " \t");
+ /* Check for expression in parentheses. */
if (**str == '(')
{
char *b = *str + 1 + strspn (*str + 1, " \t");
@@ -9377,50 +9440,68 @@ my_getSmallParser (str, len, nestlevel)
}
}
}
+ /* Check for percent_op (in parentheses). */
else if (b[0] == '%')
{
*str = b;
- goto percent_op;
+ return my_getPercentOp (str, len, nestlevel);
}
- /* Some other expression in the braces. */
- *len = strcspn (*str, ")") + 1;
+ /* Some other expression in the parentheses, which can contain
+ parentheses itself. Attempt to find the matching one. */
+ {
+ int pcnt = 1;
+ char *s;
+
+ *len = 1;
+ for (s = *str + 1; *s && pcnt; s++, (*len)++)
+ {
+ if (*s == '(')
+ pcnt++;
+ else if (*s == ')')
+ pcnt--;
+ }
+ }
}
- /* Check for percent_op. */
+ /* Check for percent_op (outside of parentheses). */
else if (*str[0] == '%')
- {
- char *tmp;
- unsigned int i;
+ return my_getPercentOp (str, len, nestlevel);
-percent_op:
- tmp = *str + 1;
- i = 0;
+ /* Any other expression. */
+ return S_EX_NONE;
+}
- while (ISALPHA (*tmp) || *tmp == '_')
- {
- *tmp = TOLOWER (*tmp);
- tmp++;
- }
- while (i < (sizeof (percent_op) / sizeof (struct percent_op_match)))
+static int
+my_getPercentOp (str, len, nestlevel)
+ char **str;
+ unsigned int *len;
+ int *nestlevel;
+{
+ char *tmp = *str + 1;
+ unsigned int i = 0;
+
+ while (ISALPHA (*tmp) || *tmp == '_')
+ {
+ *tmp = TOLOWER (*tmp);
+ tmp++;
+ }
+ while (i < (sizeof (percent_op) / sizeof (struct percent_op_match)))
+ {
+ if (strncmp (*str, percent_op[i].str, strlen (percent_op[i].str)))
+ i++;
+ else
{
- if (strncmp (*str, percent_op[i].str, strlen (percent_op[i].str)))
- i++;
- else
- {
- type = percent_op[i].type;
+ int type = percent_op[i].type;
- /* Only %hi and %lo are allowed for OldABI. */
- if (! HAVE_NEWABI && type != S_EX_HI && type != S_EX_LO)
- return S_EX_NONE;
-
- *len = strlen (percent_op[i].str);
- (*nestlevel)++;
- return type;
- }
+ /* Only %hi and %lo are allowed for OldABI. */
+ if (! HAVE_NEWABI && type != S_EX_HI && type != S_EX_LO)
+ return S_EX_NONE;
+
+ *len = strlen (percent_op[i].str);
+ (*nestlevel)++;
+ return type;
}
}
-
- /* Any other expression. */
return S_EX_NONE;
}
@@ -9432,46 +9513,59 @@ my_getSmallExpression (ep, str)
static char *oldstr = NULL;
int c = S_EX_NONE;
int oldc;
- int nest_level = 0;
+ int nestlevel = -1;
unsigned int len;
- /* Don't update oldstr if the last call had nested percent_op's. */
+ /* Don't update oldstr if the last call had nested percent_op's. We need
+ it to parse the outer ones later. */
if (! oldstr)
oldstr = str;
do
{
oldc = c;
- c = my_getSmallParser (&str, &len, &nest_level);
+ c = my_getSmallParser (&str, &len, &nestlevel);
if (c != S_EX_NONE && c != S_EX_REGISTER)
str += len;
}
while (c != S_EX_NONE && c != S_EX_REGISTER);
- /* A percent_op was encountered. */
- if (nest_level)
+ if (nestlevel >= 0)
{
- /* Don't try to get an expression if it is already blanked out. */
+ /* A percent_op was encountered. Don't try to get an expression if
+ it is already blanked out. */
if (*(str + strspn (str + 1, " )")) != ')')
{
char save;
+ /* Let my_getExpression() stop at the closing parenthesis. */
save = *(str + len);
*(str + len) = '\0';
my_getExpression (ep, str);
*(str + len) = save;
}
- if (nest_level > 1)
+ if (nestlevel > 0)
{
- /* blank out including the % sign. */
- char *p = strrchr (oldstr, '%');
- memset (p, ' ', str - p + len);
+ /* Blank out including the % sign and the proper matching
+ parenthesis. */
+ int pcnt = 1;
+ char *s = strrchr (oldstr, '%');
+ char *end;
+
+ for (end = strchr (s, '(') + 1; *end && pcnt; end++)
+ {
+ if (*end == '(')
+ pcnt++;
+ else if (*end == ')')
+ pcnt--;
+ }
+
+ memset (s, ' ', end - s);
str = oldstr;
}
else
- {
- expr_end = strchr (str, ')') + 1;
- }
+ expr_end = str + len;
+
c = oldc;
}
else if (c == S_EX_NONE)
@@ -9491,7 +9585,8 @@ my_getSmallExpression (ep, str)
as_fatal(_("internal error"));
}
- if (nest_level <= 1)
+ if (nestlevel <= 0)
+ /* All percent_op's have been handled. */
oldstr = NULL;
return c;