This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Thumb32 assembler (32/69)
- From: Zack Weinberg <zack at codesourcery dot com>
- To: binutils <binutils at sourceware dot org>
- Date: Tue, 26 Apr 2005 02:54:54 -0700
- Subject: 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 ();