]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
gas: avoid (scrubber) diagnostics for stuff past .end
authorJan Beulich <jbeulich@suse.com>
Wed, 11 Sep 2024 11:56:36 +0000 (13:56 +0200)
committerJan Beulich <jbeulich@suse.com>
Wed, 11 Sep 2024 11:56:36 +0000 (13:56 +0200)
What's past an active .end directive (when that has its default purpose)
is supposed to be entirely ignored. That should be true not just for
regular processing, but also for "pre-processing" (aka scrubbing). A
complication is that such a directive may of course occur inside a
(false) conditional or a macro definition. To deal with that make sure
we can continue as usual if called another time.

Note however that .end inside a macro will still have the full macro
body expanded; dealing with that would require further (perhaps
intrusive) adjustments in sb_scrub_and_add_sb() and/or callers thereof.
However, at least some of the warnings issued by do_scrub_chars() are
unlikely to occur when expanding a macro. (If we needed to go that far,
presumably .exitm would also want recognizing.)

gas/app.c
gas/testsuite/gas/all/end-no-dot.l [new file with mode: 0644]
gas/testsuite/gas/all/end-no-dot.s [new file with mode: 0644]
gas/testsuite/gas/all/end.l [new file with mode: 0644]
gas/testsuite/gas/all/end.s [new file with mode: 0644]
gas/testsuite/gas/all/gas.exp

index 743062e8b82a50c277f0fd9bf38f7b54449f36bf..8dc69ff4ce04e61866104f0716a232126318ec4c 100644 (file)
--- a/gas/app.c
+++ b/gas/app.c
@@ -58,6 +58,15 @@ static const char   symver_pseudo[] = ".symver";
 static const char * symver_state;
 #endif
 
+/* The pseudo-op (without leading dot) at which we want to (perhaps just
+   temporarily) stop processing.  See the comments in do_scrub_chars().  */
+static const char   end_pseudo[] = "end ";
+static const char * end_state;
+
+/* Whether, considering the state at start of assembly, NO_PSEUDO_DOT is
+   active.  */
+static bool no_pseudo_dot;
+
 static char last_char;
 
 #define LEX_IS_SYMBOL_COMPONENT                1
@@ -161,6 +170,12 @@ do_scrub_begin (int m68k_mri ATTRIBUTE_UNUSED)
 {
   const char *p;
 
+  /* Latch this once at start.  xtensa uses a hook function, yet context isn't
+     meaningful for scrubbing (or else we'd need to sync scrubber behavior as
+     state changes).  */
+  if (lex['/'] == 0)
+    no_pseudo_dot = NO_PSEUDO_DOT;
+
 #ifdef TC_M68K
   scrub_m68k_mri = m68k_mri;
 
@@ -282,6 +297,7 @@ struct app_save
   int          add_newlines;
   char *       saved_input;
   size_t       saved_input_len;
+  const char * end_state;
 #ifdef TC_M68K
   int          scrub_m68k_mri;
   const char * mri_state;
@@ -312,6 +328,7 @@ app_push (void)
       memcpy (saved->saved_input, saved_input, saved_input_len);
       saved->saved_input_len = saved_input_len;
     }
+  saved->end_state = end_state;
 #ifdef TC_M68K
   saved->scrub_m68k_mri = scrub_m68k_mri;
   saved->mri_state = mri_state;
@@ -352,6 +369,7 @@ app_pop (char *arg)
       saved_input_len = saved->saved_input_len;
       free (saved->saved_input);
     }
+  end_state = saved->end_state;
 #ifdef TC_M68K
   scrub_m68k_mri = saved->scrub_m68k_mri;
   mri_state = saved->mri_state;
@@ -800,6 +818,43 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 
     recycle:
 
+      /* We need to watch out for .end directives: We should in particular not
+        issue diagnostics for anything after an active one.  */
+      if (end_state == NULL)
+       {
+         if ((state == 0 || state == 1)
+             && (ch == '.'
+                 || (no_pseudo_dot && ch == end_pseudo[0])))
+           end_state = end_pseudo + (ch != '.');
+       }
+      else if (ch != '\0'
+              && (*end_state == ch
+                  /* Avoid triggering on directives like .endif or .endr.  */
+                  || (*end_state == ' ' && !IS_SYMBOL_COMPONENT (ch))))
+       {
+         if (IS_NEWLINE (ch) || IS_LINE_SEPARATOR (ch))
+           goto end_end;
+         ++end_state;
+       }
+      else if (*end_state != '\0')
+       /* We did not get the expected character, or we didn't
+          get a valid terminating character after seeing the
+          entire pseudo-op, so we must go back to the beginning.  */
+       end_state = NULL;
+      else if (IS_NEWLINE (ch) || IS_LINE_SEPARATOR (ch))
+       {
+       end_end:
+         /* We've read the entire pseudo-op.  If this is the end of the line,
+            bail out now by (ab)using the output-full path.  This allows the
+            caller to process input up to here and terminate processing if this
+            directive is actually active (not on the false branch of a
+            conditional and not in a macro definition).  */
+         end_state = NULL;
+         state = 0;
+         PUT (ch);
+         goto tofull;
+       }
+
 #if defined TC_ARM && defined OBJ_ELF
       /* We need to watch out for .symver directives.  See the comment later
         in this function.  */
@@ -1440,7 +1495,7 @@ do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen,
 #if defined TC_ARM && defined OBJ_ELF
              && symver_state == NULL
 #endif
-             )
+             && end_state == NULL)
            {
              char *s;
              ptrdiff_t len;
diff --git a/gas/testsuite/gas/all/end-no-dot.l b/gas/testsuite/gas/all/end-no-dot.l
new file mode 100644 (file)
index 0000000..fa47ae3
--- /dev/null
@@ -0,0 +1,3 @@
+# No diagnostics should appear for anything past "end".
+>3<
+>4<
diff --git a/gas/testsuite/gas/all/end-no-dot.s b/gas/testsuite/gas/all/end-no-dot.s
new file mode 100644 (file)
index 0000000..bee10c8
--- /dev/null
@@ -0,0 +1,11 @@
+       if 0
+       end a b c
+       endif
+
+       irpc n,34
+       print ">\n<"
+       endr
+
+       end q r, s
+       "\z"
+       äöü'\
\ No newline at end of file
diff --git a/gas/testsuite/gas/all/end.l b/gas/testsuite/gas/all/end.l
new file mode 100644 (file)
index 0000000..e18518d
--- /dev/null
@@ -0,0 +1,3 @@
+# No diagnostics should appear for anything past .end.
+>1<
+>2<
diff --git a/gas/testsuite/gas/all/end.s b/gas/testsuite/gas/all/end.s
new file mode 100644 (file)
index 0000000..3090792
--- /dev/null
@@ -0,0 +1,11 @@
+       .if 0
+       .end a b c
+       .endif
+
+       .irpc n,12
+       .print ">\n<"
+       .endr
+
+       .end q r, s
+       "\z"
+       äöü'\
\ No newline at end of file
index 45d037cc190421fa767866275dcaea21ab98a423..5fff61f86edb5d7eda4125f24ee865eb63f65fd1 100644 (file)
@@ -464,6 +464,30 @@ switch -glob $target_triplet {
        run_dump_test weakref1w
     }
 }
+
+# .end works differently on some targets. Also make sure to test the dot-less
+# form on targets setting NO_PSEUDO_DOT (and not overriding the directive).
+switch -glob $target_triplet {
+    alpha*-*-* { }
+    hppa*-*-* { }
+    iq2000-*-* { }
+    microblaze-*-* { }
+    mips*-*-* { }
+    score*-*-* { }
+    xtensa*-*-* { }
+    m68hc1*-*-* -
+    s12z-*-*    -
+    spu-*-*     -
+    xgate-*-*   -
+    z80-*-* {
+       run_list_test "end"
+       run_list_test "end-no-dot"
+    }
+    default {
+       run_list_test "end"
+    }
+}
+
 gas_test_error "weakref2.s" "" "e: would close weakref loop: e => a => b => c => d => e"
 gas_test_error "weakref3.s" "" "a: would close weakref loop: a => b => c => d => e => a"
 gas_test_error "weakref4.s" "" "is already defined"