This is the mail archive of the binutils@sources.redhat.com mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Thumb32 assembler (32/69)


Expressions with optional trailing relocations, such as can appear on
branch instructions under ELF.  Also, convert parse_reloc from a
linear search to a hash lookup.  I discovered much later that this
introduces warnings which break non-ELF targets; it does gets fixed
eventually.

Note that the snapshot on which this patch is based, predates the
introduction of the TLS relocs for ARM; hence their absence from
reloc_names[].  I've merged mainline into my local tree since then,
but I'm not going to show those diffs, as they are boring.

zw

	* config/tc-arm.c (struct arm_it): Add hasreloc field to operands.
	(struct reloc_entry): New global structure definition.
	(arm_reloc_hsh): New global hash table.
	(arm_parse_reloc): Rename parse_reloc; define always; operate on a
	char ** argument instead of input_line_pointer; rewrite internals
	in terms of arm_reloc_hsh.
	(s_arm_elf_cons): Update to match.  Improve error checking.
	(OP_EXPr, OP_RR_EXr): New operand parser codes.
	(parse_operands): Implement them.
	(encode_branch): New function split out of do_branch.
	(do_branch, do_blx): Use parse_operands and encode_branch.
	(do_branch25): Delete.
	(reloc_names): New table.
	(insns): Put the encoding for BLX(2) into the table.
	(md_begin): Initialize arm_reloc_hsh.

===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c	(revision 33)
+++ gas/config/tc-arm.c	(revision 34)
@@ -198,6 +198,7 @@
     int present    : 1;  /* operand present */
     int isreg	   : 1;  /* operand was a register */
     int writeback  : 1;  /* operand has trailing ! */
+    int hasreloc   : 1;  /* operand has relocation suffix */
   } operands[6];
 };
 
@@ -341,6 +342,12 @@
 #define PSR_s   (1 << 18)
 #define PSR_f   (1 << 19)
 
+struct reloc_entry
+{
+  char *name;
+  bfd_reloc_code_real_type reloc;
+};
+
 /* These values are the bit offsets of the register fields, allowing these
    to be used directly in encoding operations.  */
 enum vfp_dp_reg_pos
@@ -596,6 +603,7 @@
 static struct hash_control *arm_shift_hsh;
 static struct hash_control *arm_psr_hsh;
 static struct hash_control *arm_reg_hsh;
+static struct hash_control *arm_reloc_hsh;
 
 /* Stuff needed to resolve the label ambiguity
    As:
@@ -1870,48 +1878,34 @@
     }
 }
 
-#ifdef OBJ_ELF
-static bfd_reloc_code_real_type
-arm_parse_reloc (void)
+/* Parse an explicit relocation suffix on an expression.  This is
+   either nothing, or a word in parentheses.  Note that if !OBJ_ELF,
+   arm_reloc_hsh contains no entries, so this function can only
+   succeed if there is no () after the word.  Returns -1 on error,
+   BFD_RELOC_UNUSED if there wasn't any suffix.  */
+static int
+parse_reloc (char **str)
 {
-  char         id [16];
-  char *       ip;
-  unsigned int i;
-  static struct
-  {
-    char * str;
-    int    len;
-    bfd_reloc_code_real_type reloc;
-  }
-  reloc_map[] =
-  {
-#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
-    MAP ("(got)",    BFD_RELOC_ARM_GOT32),
-    MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF),
-    /* ScottB: Jan 30, 1998 - Added support for parsing "var(PLT)"
-       branch instructions generated by GCC for PLT relocs.  */
-    MAP ("(plt)",    BFD_RELOC_ARM_PLT32),
-    MAP ("(target1)", BFD_RELOC_ARM_TARGET1),
-    MAP ("(sbrel)", BFD_RELOC_ARM_SBREL32),
-    MAP ("(target2)", BFD_RELOC_ARM_TARGET2),
-    { NULL, 0,         BFD_RELOC_UNUSED }
-#undef MAP
-  };
+  struct reloc_entry *r;
+  char *p, *q;
+  
+  if (**str != '(')
+    return BFD_RELOC_UNUSED;
 
-  for (i = 0, ip = input_line_pointer;
-       i < sizeof (id) && (ISALNUM (*ip) || ISPUNCT (*ip));
-       i++, ip++)
-    id[i] = TOLOWER (*ip);
+  p = *str + 1;
+  q = p;
 
-  for (i = 0; reloc_map[i].str; i++)
-    if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0)
-      break;
+  while (*q && *q != ')' && *q != ',')
+    q++;
+  if (*q != ')')
+    return -1;
 
-  input_line_pointer += reloc_map[i].len;
+  if ((r = hash_find_n (arm_reloc_hsh, p, q - p)) == NULL)
+    return -1;
 
-  return reloc_map[i].reloc;
+  *str = q + 1;
+  return r->reloc;
 }
-#endif
 
 static int
 ldst_extend (char ** str)
@@ -3449,31 +3443,41 @@
   mapping_state (MAP_DATA);
   do
     {
-      bfd_reloc_code_real_type reloc;
+      int reloc;
 
       expression (& exp);
 
-      if (exp.X_op == O_symbol
-	  && * input_line_pointer == '('
-	  && (reloc = arm_parse_reloc ()) != BFD_RELOC_UNUSED)
+      if (exp.X_op != O_symbol)
+	emit_expr (&exp, (unsigned int) nbytes);
+      else
 	{
-	  reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
-	  int size = bfd_get_reloc_size (howto);
-
-	  if (size > nbytes)
-	    as_bad ("%s relocations do not fit in %d bytes",
-		    howto->name, nbytes);
+	  reloc = parse_reloc (&input_line_pointer);
+	  if (reloc == -1)
+	    {
+	      as_bad (_("unrecognized relocation suffix"));
+	      ignore_rest_of_line ();
+	      return;
+	    }
+	  else if (reloc == BFD_RELOC_UNUSED)
+	    emit_expr (&exp, (unsigned int) nbytes);
 	  else
 	    {
-	      char *p = frag_more ((int) nbytes);
-	      int offset = nbytes - size;
+	      reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
+	      int size = bfd_get_reloc_size (howto);
 
-	      fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
-			   &exp, 0, reloc);
+	      if (size > nbytes)
+		as_bad ("%s relocations do not fit in %d bytes",
+			howto->name, nbytes);
+	      else
+		{
+		  char *p = frag_more ((int) nbytes);
+		  int offset = nbytes - size;
+
+		  fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
+			       size, &exp, 0, reloc);
+		}
 	    }
 	}
-      else
-	emit_expr (&exp, (unsigned int) nbytes);
     }
   while (*input_line_pointer++ == ',');
 
@@ -4314,21 +4318,22 @@
 
 #define OP_EXP	   024	/* arbitrary expression */
 #define OP_iEXP	   025	/* same, with optional immediate prefix */
+#define OP_EXPr	   026	/* same, with optional relocation suffix */
 
-#define OP_I0	   026	/* immediate value 0 */
-#define OP_I4      027  /*                 1 .. 4 */
-#define OP_I7	   030  /*                 0 .. 7 */
-#define OP_I15	   031  /*                 0 .. 15 */
-#define OP_I16	   032  /*                 1 .. 16 */
-#define OP_I31	   033	/*                 0 .. 31 */
-#define OP_I32	   034	/*                 1 .. 32 */
-#define OP_Is63	   035  /*               -64 .. 63 */
-#define OP_I255	   036	/*                 0 .. 255 */
-#define OP_Iffff   037  /*                 0 .. 65535 */
+#define OP_I0	   027	/* immediate value 0 */
+#define OP_I4      030  /*                 1 .. 4 */
+#define OP_I7	   031  /*                 0 .. 7 */
+#define OP_I15	   032  /*                 0 .. 15 */
+#define OP_I16	   033  /*                 1 .. 16 */
+#define OP_I31	   034	/*                 0 .. 31 */
+#define OP_I32	   035	/*                 1 .. 32 */
+#define OP_Is63	   036  /*               -64 .. 63 */
+#define OP_I255	   037	/*                 0 .. 255 */
+#define OP_Iffff   040 /*                 0 .. 65535 */
 
-#define OP_bI7	   040  /* immediate, prefix optional, 0 .. 7 */
-#define OP_bI15    041  /*                             0 .. 15 */
-#define OP_bI31    042  /*                             0 .. 31 */
+#define OP_bI7	   041  /* immediate, prefix optional, 0 .. 7 */
+#define OP_bI15    042  /*                             0 .. 15 */
+#define OP_bI31    043  /*                             0 .. 31 */
 
 #define OP_I31w	   050  /* 0 .. 31, optional trailing ! */
 #define OP_RRw	   051  /* ARM register, not the PC, optional trailing ! */
@@ -4344,6 +4349,7 @@
 #define OP_RR_EX   100	/* ARM register or expression */
 #define OP_RL_iEX  101	/* Thumb low register or expression with imm prefix */
 #define OP_RRnpc_I0 102	/* ARM register or literal 0 */
+#define OP_RR_EXr  103  /* ARM register or expression with opt. reloc suff. */
 
 /* Optional operands.  All have the high bit set.  */
 #define OP_obI7    200  /* optional, prefix optional, immediate 0 .. 7 */
@@ -4545,6 +4551,26 @@
 	    return FAIL;
 	  break;
 
+	case OP_(EXPr):
+	EXPr:
+	  if (my_get_expression (&inst.reloc.exp, &str))
+	    return FAIL;
+	  if (inst.reloc.exp.X_op == O_symbol)
+	    {
+	      int reloc = parse_reloc (&str);
+	      if (reloc == -1)
+		{
+		  inst.error = _("unrecognized relocation suffix");
+		  return FAIL;
+		}
+	      else if (reloc != BFD_RELOC_UNUSED)
+		{
+		  inst.operands[i].imm = reloc;
+		  inst.operands[i].hasreloc = 1;
+		}
+	    }
+	  break;
+
 	  /* Register or expression */
 	case OP_(RR_EX):
 	  {
@@ -4559,6 +4585,19 @@
 	  }
 	  break;
 
+	case OP_(RR_EXr):
+	  {
+	    int reg_ = arm_reg_parse (&str, REG_TYPE_RN);
+	    if (reg_ != FAIL)
+	      {
+		inst.operands[i].reg = reg_;
+		inst.operands[i].isreg = 1;
+	      }
+	    else
+	      goto EXPr;
+	  }
+	  break;
+
 	case OP_(oRL_iEX):
 	case OP_(RL_iEX):
 	  if (is_immediate_prefix (*str))
@@ -4828,94 +4867,32 @@
 }
 
 static void
-do_branch (char * str)
+encode_branch (int default_reloc)
 {
-  expression_or_fail (&inst.reloc.exp, &str);
-
-#ifdef OBJ_ELF
-  {
-    char * save_in;
-
-    /* ScottB: February 5, 1998 - Check to see of PLT32 reloc
-       required for the instruction.  */
-
-    /* arm_parse_reloc () works on input_line_pointer.
-       We actually want to parse the operands to the branch instruction
-       passed in 'str'.  Save the input pointer and restore it later.  */
-    save_in = input_line_pointer;
-    input_line_pointer = str;
-    if (inst.reloc.exp.X_op == O_symbol
-	&& *str == '('
-	&& arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
-      {
-	inst.reloc.type   = BFD_RELOC_ARM_PLT32;
-	inst.reloc.pc_rel = 0;
-	/* Modify str to point to after parsed operands, otherwise
-	   end_of_line() will complain about the (PLT) left in str.  */
-	str = input_line_pointer;
-      }
-    else
-      {
-	inst.reloc.type   = BFD_RELOC_ARM_PCREL_BRANCH;
-	inst.reloc.pc_rel = 1;
-      }
-    input_line_pointer = save_in;
-  }
-#else
-  inst.reloc.type   = BFD_RELOC_ARM_PCREL_BRANCH;
-  inst.reloc.pc_rel = 1;
-#endif /* OBJ_ELF  */
-
-  end_of_line (str);
+  if (inst.operands[0].hasreloc)
+    {
+      if (inst.operands[0].imm != BFD_RELOC_ARM_PLT32)
+	{
+	  inst.error = _("the only suffix valid here is '(plt)'");
+	  return;
+	}
+      inst.reloc.type   = BFD_RELOC_ARM_PLT32;
+      inst.reloc.pc_rel = 0;
+    }
+  else
+    {
+      inst.reloc.type = default_reloc;
+      inst.reloc.pc_rel = 1;
+    }
 }
 
-/* ARM V5 branch-link-exchange (argument parse) for BLX(1) only.
-   Expects inst.instruction is set for BLX(1).
-   Note: this is cloned from do_branch, and the reloc changed to be a
-	new one that can cope with setting one extra bit (the H bit).  */
-
 static void
-do_branch25 (char * str)
+do_branch (char * str)
 {
-  expression_or_fail (&inst.reloc.exp, &str);
+  if (parse_operands (str, OPERANDS1(EXPr)))
+    return;
 
-#ifdef OBJ_ELF
-  {
-    char * save_in;
-
-    /* ScottB: February 5, 1998 */
-    /* Check to see of PLT32 reloc required for the instruction.  */
-
-    /* arm_parse_reloc() works on input_line_pointer.
-       We actually want to parse the operands to the branch instruction
-       passed in 'str'.  Save the input pointer and restore it later.  */
-    save_in = input_line_pointer;
-    input_line_pointer = str;
-
-    if (inst.reloc.exp.X_op == O_symbol
-	&& *str == '('
-	&& arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
-      {
-	inst.reloc.type   = BFD_RELOC_ARM_PLT32;
-	inst.reloc.pc_rel = 0;
-	/* Modify str to point to after parsed operands, otherwise
-	   end_of_line() will complain about the (PLT) left in str.  */
-	str = input_line_pointer;
-      }
-    else
-      {
-	inst.reloc.type   = BFD_RELOC_ARM_PCREL_BLX;
-	inst.reloc.pc_rel = 1;
-      }
-
-    input_line_pointer = save_in;
-  }
-#else
-  inst.reloc.type   = BFD_RELOC_ARM_PCREL_BLX;
-  inst.reloc.pc_rel = 1;
-#endif /* OBJ_ELF */
-
-  end_of_line (str);
+  encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
 }
 
 /* ARM V5 branch-link-exchange instruction (argument parse)
@@ -4929,35 +4906,29 @@
 static void
 do_blx (char * str)
 {
-  int rm;
+  if (parse_operands (str, OPERANDS1(RR_EXr)))
+    return;
 
-  rm = reg_required_here (&str, 0, REG_TYPE_RN);
-
-  if (rm != FAIL)
+  if (inst.operands[0].isreg)
     {
-      /* Arg is a register.  Use the condition code our caller put in
-	 inst.instruction, and zap the opcode.  */
-      inst.instruction |= 0x012fff30;
-
-      /* It is not illegal to do "blx pc", just useless.  */
-      if (rm == REG_PC)
+      /* Arg is a register; the opcode provided by insns[] is correct.
+         It is not illegal to do "blx pc", just useless.  */
+      if (inst.operands[0].reg == REG_PC)
 	as_tsktsk (_("use of r15 in blx in ARM mode is not really useful"));
-      end_of_line (str);
+
+      inst.instruction |= inst.operands[0].reg;
     }
   else
     {
-      /* This must be BLX <target address>, no condition allowed.  */
-      if (inst.instruction != COND_ALWAYS)
+      /* Arg is an address; this instruction cannot be executed
+	 conditionally, and the opcode must be adjusted.  */
+      if ((inst.instruction & COND_MASK) != COND_ALWAYS)
 	{
 	  inst.error = BAD_COND;
 	  return;
 	}
-
       inst.instruction = 0xfafffffe;
-
-      /* Process like a B/BL, but with a different reloc.
-	 Note that B/BL expecte fffffe, not 0, offset in the opcode table.  */
-      do_branch25 (str);
+      encode_branch (BFD_RELOC_ARM_PCREL_BLX);
     }
 }
 
@@ -8790,6 +8761,17 @@
   {"cxsf", PSR_c | PSR_x | PSR_s | PSR_f},
 };
 
+static struct reloc_entry reloc_names[] =
+{
+#ifdef OBJ_ELF
+  { "got",     BFD_RELOC_ARM_GOT32   },  { "GOT",     BFD_RELOC_ARM_GOT32   },
+  { "gotoff",  BFD_RELOC_ARM_GOTOFF  },  { "GOTOFF",  BFD_RELOC_ARM_GOTOFF  },
+  { "plt",     BFD_RELOC_ARM_PLT32   },  { "PLT",     BFD_RELOC_ARM_PLT32   },
+  { "target1", BFD_RELOC_ARM_TARGET1 },  { "TARGET1", BFD_RELOC_ARM_TARGET1 },
+  { "target2", BFD_RELOC_ARM_TARGET2 },  { "TARGET2", BFD_RELOC_ARM_TARGET2 },
+  { "sbrel",   BFD_RELOC_ARM_SBREL32 },  { "SBREL",   BFD_RELOC_ARM_SBREL32 },
+#endif
+};
 
 static const struct asm_opcode insns[] =
 {
@@ -8923,9 +8905,9 @@
   {"bx",         0xe12fff10, 2,  ARM_EXT_V4T | ARM_EXT_V5, do_bx},
 
   /*  ARM Architecture 5T.  */
-  /* Note: blx has 2 variants, so the .value is set dynamically.
-     Only one of the variants has conditional execution.  */
-  {"blx",        0xe0000000, 3,  ARM_EXT_V5,       do_blx},
+  /* Note: blx has 2 variants; the .value coded here is for
+     BLX(2).  Only this variant has conditional execution.  */
+  {"blx",        0xe12fff30, 3,  ARM_EXT_V5,       do_blx},
   {"clz",        0xe16f0f10, 3,  ARM_EXT_V5,       do_clz},
   {"bkpt",       0xe1200070, 0,  ARM_EXT_V5,       do_bkpt},
   {"ldc2",       0xfc100000, 0,  ARM_EXT_V5,       do_lstc},
@@ -11870,7 +11852,8 @@
       || (arm_cond_hsh = hash_new ()) == NULL
       || (arm_shift_hsh = hash_new ()) == NULL
       || (arm_psr_hsh = hash_new ()) == NULL
-      || (arm_reg_hsh = hash_new ()) == NULL)
+      || (arm_reg_hsh = hash_new ()) == NULL
+      || (arm_reloc_hsh = hash_new ()) == NULL)
     as_fatal (_("virtual memory exhausted"));
 
   build_arm_ops_hsh ();
@@ -11884,6 +11867,8 @@
     hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
   for (i = 0; i < sizeof (reg_names) / sizeof (struct reg_entry); i++)
     hash_insert (arm_reg_hsh, reg_names[i].name, (PTR) (reg_names + i));
+  for (i = 0; i < sizeof (reloc_names) / sizeof (struct reloc_entry); i++)
+    hash_insert (arm_reloc_hsh, reloc_names[i].name, (PTR) (reloc_names + i));
 
   set_constant_flonums ();
 

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]