ld script expression parsing
authorAlan Modra <amodra@gmail.com>
Sun, 31 Jan 2021 22:45:41 +0000 (09:15 +1030)
committerAlan Modra <amodra@gmail.com>
Mon, 1 Feb 2021 14:57:12 +0000 (01:27 +1030)
Parsing symbol or file/section names in ld linker scripts is a little
complicated.  Inside SECTIONS, a name might be the start of an
expression or an output section.  Is ".foo=x-y" a fancy section name
or is it the expression ".foo = x - y"?  It isn't possible for a
single lookahead parser to decide, so the answer in this case is
that it's a section name.  This is the reason why everyone writes
linker script assignment expressions with lots of white-space.

However, there are many places where the parser knows for sure that an
expression is expected.  Those could be written without whitespace
given the first change to ldlex.l below.  Unfortunately, that runs
into a lookahead problem.  Optional expressions at the end of an
output section statement require the parser to look ahead one token in
expression context.  For this example from standard scripts
  .interp             : { *(.interp) }
  .note.gnu.build-id  : { *(.note.gnu.build-id) }
at the end of the .interp closing brace, the parser is looking for
a possible memspec, phdr, fill or even an optional comma.  The next
token is a NAME, but in expression context that NAME now doesn't
include '-' as a valid char.  So the lookahead NAME is
".note.gnu.build" with an unexpected "-id" syntax error before the
colon.  The rest of the patch involving ldlex_backup arranges to
discard that NAME token so that it will be rescanned in the proper
script context.

* ldgram.y (section): Call ldlex_backup.  Remove empty action.
* ldlex.h (ldlex_backup): Declare.
* ldlex.l (<EXPRESSION>NAME): Don't use NOCFILENAMECHAR set of
chars, use SYMBOLNAMECHAR.
(ldlex_backup): New function.

ld/ChangeLog
ld/ldgram.y
ld/ldlex.h
ld/ldlex.l

index 5b6ead74330068d765fe64ef60845f6f6d697860..c5f73f8646b801c9d0c458970cd916d09b5f4e3b 100644 (file)
@@ -1,3 +1,11 @@
+2021-02-01  Alan Modra  <amodra@gmail.com>
+
+       * ldgram.y (section): Call ldlex_backup.  Remove empty action.
+       * ldlex.h (ldlex_backup): Declare.
+       * ldlex.l (<EXPRESSION>NAME): Don't use NOCFILENAMECHAR set of
+       chars, use SYMBOLNAMECHAR.
+       (ldlex_backup): New function.
+
 2021-02-01  Alan Modra  <amodra@gmail.com>
 
        * ldgram.y: Whitespace fixes.
index b0a4619bb3ab0ccdfdcac1c070fb5b034db43c4c..08dc110f3da9993c10165e65b183a9e15cd1901b 100644 (file)
@@ -1071,11 +1071,15 @@ section:        NAME            { ldlex_expression(); }
                '}' { ldlex_popstate (); ldlex_expression (); }
                memspec_opt memspec_at_opt phdr_opt fill_opt
                {
+                 if (yychar == NAME)
+                   {
+                     yyclearin;
+                     ldlex_backup ();
+                   }
                  ldlex_popstate ();
                  lang_leave_output_section_statement ($18, $15, $17, $16);
                }
                opt_comma
-               {}
        |       OVERLAY
                        { ldlex_expression (); }
                opt_exp_without_type opt_nocrossrefs opt_at opt_subalign
@@ -1089,6 +1093,11 @@ section: NAME            { ldlex_expression(); }
                        { ldlex_popstate (); ldlex_expression (); }
                memspec_opt memspec_at_opt phdr_opt fill_opt
                        {
+                         if (yychar == NAME)
+                           {
+                             yyclearin;
+                             ldlex_backup ();
+                           }
                          ldlex_popstate ();
                          lang_leave_overlay ($5, (int) $4,
                                              $16, $13, $15, $14);
index b010102832150c416aa6a2e02691483bb9980173..d9b36ea270207b4fc46dd45725f175d8cbab4bd3 100644 (file)
@@ -191,6 +191,7 @@ extern void ldlex_defsym (void);
 extern void ldlex_expression (void);
 extern void ldlex_both (void);
 extern void ldlex_popstate (void);
+extern void ldlex_backup (void);
 extern const char* ldlex_filename (void);
 
 /* In lexsup.c.  */
index 237892c0ec3a181e773e5197443aa25be4e05fd2..7652e8d2a29a432faaa4c6ffe2a5872c24a2c28a 100644 (file)
@@ -385,7 +385,7 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
                                  yylval.name = xstrdup (yytext + 2);
                                  return LNAME;
                                }
-<EXPRESSION>{SYMBOLNAMECHAR1}{NOCFILENAMECHAR}* {
+<EXPRESSION>{SYMBOLNAMECHAR1}{SYMBOLNAMECHAR}* {
                                  yylval.name = xstrdup (yytext);
                                  return NAME;
                                }
@@ -636,6 +636,16 @@ ldlex_popstate (void)
   yy_start = *(--state_stack_p);
 }
 
+/* In cases where the parser needs to look ahead and the context
+   changes from expression to script or vice-versa, throw away a
+   NAME.  What constitutes a NAME depends on context.  */
+
+void
+ldlex_backup (void)
+{
+  yyless (0);
+}
+
 /* Return the current file name, or the previous file if no file is
    current.  */