]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
x86: undo '{' being a symbol-start character
authorJan Beulich <jbeulich@suse.com>
Fri, 19 Jul 2024 09:44:07 +0000 (11:44 +0200)
committerJan Beulich <jbeulich@suse.com>
Fri, 19 Jul 2024 09:44:07 +0000 (11:44 +0200)
Having it that way has undue side effects, in permitting not only
pseudo-prefixes to be parsed correctly, but also permitting odd symbol
names which ought to be possible only when quoted.  Borrow what other
architectures do: Put in place an "unrecognized line" hook to parse off
any pseudo prefixes, while using the "start of line" hook to reject ones
not actually followed by an insn. For that parsing re-use parse_insn()
in yet a slightly different mode (dealing with only pseudo-prefixes).

With that, pp may no longer be cleared from init_globals(), but instead
needs clearing after a line was fully processed. Since md_assemble() has
pretty many return paths, convert that into a local helper, with a
trivial wrapper around it.

Similarly pp may no longer be updated (by check_register()) when
processing anything other than insn operands. To be able to (easily)
recognize the case, clear current_templates.start when done with an insn
(or with .insn).

gas/config/tc-i386.c
gas/config/tc-i386.h

index 67d8f85bbbbee0ac57fc1871a0685182ca81669e..bbc8618117e0bd6efc7135704babe84ab44704a8 100644 (file)
@@ -138,6 +138,13 @@ typedef struct
 }
 arch_entry;
 
+/* Modes for parse_insn() to operate in.  */
+enum parse_mode {
+  parse_all,
+  parse_prefix,
+  parse_pseudo_prefix,
+};
+
 static void update_code_flag (int, int);
 static void s_insn (int);
 static void s_noopt (int);
@@ -163,7 +170,7 @@ static int i386_intel_operand (char *, int);
 static int i386_intel_simplify (expressionS *);
 static int i386_intel_parse_name (const char *, expressionS *);
 static const reg_entry *parse_register (const char *, char **);
-static const char *parse_insn (const char *, char *, bool);
+static const char *parse_insn (const char *, char *, enum parse_mode);
 static char *parse_operands (char *, const char *);
 static void swap_operands (void);
 static void swap_2_operands (unsigned int, unsigned int);
@@ -3317,8 +3324,8 @@ op_lookup (const char *mnemonic)
 void
 md_begin (void)
 {
-  /* Support pseudo prefixes like {disp32}.  */
-  lex_type ['{'] = LEX_BEGIN_NAME;
+  /* Make sure possible padding space is clear.  */
+  memset (&pp, 0, sizeof (pp));
 
   /* Initialize op_hash hash table.  */
   op_hash = str_htab_create ();
@@ -6087,7 +6094,6 @@ static void init_globals (void)
   unsigned int j;
 
   memset (&i, '\0', sizeof (i));
-  memset (&pp, 0, sizeof (pp));
   i.rounding.type = rc_none;
   for (j = 0; j < MAX_OPERANDS; j++)
     i.reloc[j] = NO_RELOC;
@@ -7221,14 +7227,15 @@ x86_ginsn_new (const symbolS *insn_end_sym, enum ginsn_gen_mode gmode)
    machine dependent instruction.  This function is supposed to emit
    the frags/bytes it assembles to.  */
 
-void
-md_assemble (char *line)
+static void
+i386_assemble (char *line)
 {
   unsigned int j;
   char mnemonic[MAX_MNEM_SIZE], mnem_suffix = 0, *copy = NULL;
   char *xstrdup_copy = NULL;
   const char *end, *pass1_mnem = NULL;
   enum i386_error pass1_err = 0;
+  struct pseudo_prefixes orig_pp = pp;
   const insn_template *t;
   struct last_insn *last_insn
     = &seg_info(now_seg)->tc_segment_info_data.last_insn;
@@ -7247,7 +7254,7 @@ md_assemble (char *line)
      We assume that the scrubber has arranged it so that line[0] is the valid
      start of a (possibly prefixed) mnemonic.  */
 
-  end = parse_insn (line, mnemonic, false);
+  end = parse_insn (line, mnemonic, parse_all);
   if (end == NULL)
     {
       if (pass1_mnem != NULL)
@@ -7354,6 +7361,7 @@ md_assemble (char *line)
   no_match:
          pass1_err = i.error;
          pass1_mnem = insn_name (current_templates.start);
+         pp = orig_pp;
          goto retry;
        }
 
@@ -7808,6 +7816,14 @@ md_assemble (char *line)
     last_insn->kind = last_insn_other;
 }
 
+void
+md_assemble (char *line)
+{
+  i386_assemble (line);
+  current_templates.start = NULL;
+  memset (&pp, 0, sizeof (pp));
+}
+
 /* The Q suffix is generally valid only in 64-bit mode, with very few
    exceptions: fild, fistp, fisttp, and cmpxchg8b.  Note that for fild
    and fisttp only one of their two templates is matched below: That's
@@ -7823,7 +7839,7 @@ static INLINE bool q_suffix_allowed(const insn_template *t)
 }
 
 static const char *
-parse_insn (const char *line, char *mnemonic, bool prefix_only)
+parse_insn (const char *line, char *mnemonic, enum parse_mode mode)
 {
   const char *l = line, *token_start = l;
   char *mnem_p;
@@ -7841,6 +7857,8 @@ parse_insn (const char *line, char *mnemonic, bool prefix_only)
          ++mnem_p;
          ++l;
        }
+      else if (mode == parse_pseudo_prefix)
+       break;
       while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0)
        {
          if (*mnem_p == '.')
@@ -7872,7 +7890,7 @@ parse_insn (const char *line, char *mnemonic, bool prefix_only)
               && (intel_syntax
                   || (*l != PREFIX_SEPARATOR && *l != ',')))
        {
-         if (prefix_only)
+         if (mode != parse_all)
            break;
          as_bad (_("invalid character %s in mnemonic"),
                  output_invalid (*l));
@@ -8019,7 +8037,7 @@ parse_insn (const char *line, char *mnemonic, bool prefix_only)
        break;
     }
 
-  if (prefix_only)
+  if (mode != parse_all)
     return token_start;
 
   if (!current_templates.start)
@@ -11894,6 +11912,65 @@ output_interseg_jump (void)
                 i.op[0].imms, 0, reloc (2, 0, 0, i.reloc[0]));
 }
 
+/* Hook used to reject pseudo-prefixes misplaced at the start of a line.  */
+
+void i386_start_line (void)
+{
+  struct pseudo_prefixes last_pp;
+
+  memcpy (&last_pp, &pp, sizeof (pp));
+  memset (&pp, 0, sizeof (pp));
+  if (memcmp (&pp, &last_pp, sizeof (pp)))
+    as_bad_where (frag_now->fr_file, frag_now->fr_line,
+                 _("pseudo prefix without instruction"));
+}
+
+/* Hook used to warn about pseudo-prefixes ahead of a label.  */
+
+bool i386_check_label (void)
+{
+  struct pseudo_prefixes last_pp;
+
+  memcpy (&last_pp, &pp, sizeof (pp));
+  memset (&pp, 0, sizeof (pp));
+  if (memcmp (&pp, &last_pp, sizeof (pp)))
+    as_warn (_("pseudo prefix ahead of label; ignoring"));
+  return true;
+}
+
+/* Hook used to parse pseudo-prefixes off of the start of a line.  */
+
+int
+i386_unrecognized_line (int ch)
+{
+  char mnemonic[MAX_MNEM_SIZE];
+  const char *end;
+
+  if (ch != '{')
+    return 0;
+
+  --input_line_pointer;
+  know (*input_line_pointer == ch);
+
+  end = parse_insn (input_line_pointer, mnemonic, parse_pseudo_prefix);
+  if (end == NULL)
+    {
+      /* Diagnostic was already issued.  */
+      ignore_rest_of_line ();
+      memset (&pp, 0, sizeof (pp));
+      return 1;
+    }
+
+  if (end == input_line_pointer)
+    {
+      ++input_line_pointer;
+      return 0;
+    }
+
+  input_line_pointer += end - input_line_pointer;
+  return 1;
+}
+
 #if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
 void
 x86_cleanup (void)
@@ -13495,13 +13572,14 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
   saved_char = *saved_ilp;
   *saved_ilp = 0;
 
-  end = parse_insn (line, mnemonic, true);
+  end = parse_insn (line, mnemonic, parse_prefix);
   if (end == NULL)
     {
   bad:
       *saved_ilp = saved_char;
       ignore_rest_of_line ();
       i.tm.mnem_off = 0;
+      memset (&pp, 0, sizeof (pp));
       return;
     }
   line += end - line;
@@ -14291,6 +14369,9 @@ s_insn (int dummy ATTRIBUTE_UNUSED)
 
   /* Make sure dot_insn() won't yield "true" anymore.  */
   i.tm.mnem_off = 0;
+
+  current_templates.start = NULL;
+  memset (&pp, 0, sizeof (pp));
 }
 
 #ifdef TE_PE
@@ -16701,7 +16782,8 @@ static bool check_register (const reg_entry *r)
       if (vector_size < VSZ512)
        return false;
 
-      switch (pp.encoding)
+      /* Don't update pp when not dealing with insn operands.  */
+      switch (current_templates.start ? pp.encoding : encoding_evex)
        {
        case encoding_default:
        case encoding_egpr:
@@ -16739,7 +16821,8 @@ static bool check_register (const reg_entry *r)
          || flag_code != CODE_64BIT)
        return false;
 
-      switch (pp.encoding)
+      /* Don't update pp when not dealing with insn operands.  */
+      switch (current_templates.start ? pp.encoding : encoding_evex)
        {
          case encoding_default:
          case encoding_egpr:
@@ -16760,7 +16843,8 @@ static bool check_register (const reg_entry *r)
          || flag_code != CODE_64BIT)
        return false;
 
-      switch (pp.encoding)
+      /* Don't update pp when not dealing with insn operands.  */
+      switch (current_templates.start ? pp.encoding : encoding_egpr)
        {
        case encoding_default:
          pp.encoding = encoding_egpr;
index 8b0fc6398aac80443c3e591efd0a16d277d3c057..cda7166b91f576e868a45331000ddf2ff67bca2b 100644 (file)
@@ -183,6 +183,16 @@ extern int tc_i386_fix_adjustable (struct fix *);
    || (FIX)->fx_r_type == BFD_RELOC_X86_64_REX_GOTPCRELX       \
    || (FIX)->fx_r_type == BFD_RELOC_X86_64_CODE_4_GOTPCRELX)
 
+extern void i386_start_line (void);
+#define md_start_line_hook i386_start_line
+
+extern bool i386_check_label (void);
+#define TC_START_LABEL(STR, NUL_CHAR, NEXT_CHAR) \
+  (NEXT_CHAR == ':' && i386_check_label ())
+
+extern int i386_unrecognized_line (int);
+#define tc_unrecognized_line i386_unrecognized_line
+
 extern int i386_parse_name (char *, expressionS *, char *);
 #define md_parse_name(s, e, m, c) i386_parse_name (s, e, c)