/* tc-arm.c -- Assemble for the ARM
- Copyright (C) 1994, 95, 96, 97, 98, 1999, 2000
+ Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
Free Software Foundation, Inc.
Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
Modified by David Taylor (dtaylor@armltd.co.uk)
{"ed", 0x01800000},
{"fd", 0x00800000},
{"ea", 0x01000000},
- {"fa", 0x08000000},
+ {"fa", 0x00000000},
{"ib", 0x01800000},
{"ia", 0x00800000},
{"db", 0x01000000},
- {"da", 0x08000000},
+ {"da", 0x00000000},
{NULL, 0}
};
static CONST struct asm_flg stm_flags[] =
{
- {"ed", 0x08000000},
+ {"ed", 0x00000000},
{"fd", 0x01000000},
{"ea", 0x00800000},
{"fa", 0x01800000},
{"ib", 0x01800000},
{"ia", 0x00800000},
{"db", 0x01000000},
- {"da", 0x08000000},
+ {"da", 0x00000000},
{NULL, 0}
};
== inst.reloc.exp.X_add_number)
&& literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned)
break;
+
+ if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op
+ && inst.reloc.exp.X_op == O_symbol
+ && (literals[lit_count].exp.X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (literals[lit_count].exp.X_add_symbol
+ == inst.reloc.exp.X_add_symbol)
+ && (literals[lit_count].exp.X_op_symbol
+ == inst.reloc.exp.X_op_symbol))
+ break;
+
lit_count++;
}
if (lit_count == next_literal_pool_place) /* New entry. */
{
- if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
+ if (next_literal_pool_place >= MAX_LITERAL_POOL_SIZE)
{
inst.error = _("Literal Pool Overflow");
return FAIL;
return;
}
+#if 0 /* The first edition of the ARM architecture manual stated that
+ writing anything other than the flags with an immediate operation
+ had UNPREDICTABLE effects. This constraint was removed in the
+ second edition of the specification. */
if ((cpu_variant & ARM_EXT_V5) != ARM_EXT_V5
&& inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
{
inst.error = _("immediate value cannot be used to set this field");
return;
}
+#endif
flags |= INST_IMMEDIATE;
accum0_required_here (str)
char ** str;
{
- static char buff [128]; /* Note the address is taken. Hence, static. */
+ static char buff [128]; /* Note the address is taken. Hence, static. */
char * p = * str;
char c;
int result = 0; /* The accum number. */
skip_whitespace (p);
-
+
*str = p; /* Advance caller's string pointer too. */
c = *p++;
while (isalnum (c))
c = *p++;
*--p = 0; /* Aap nul into input buffer at non-alnum. */
-
+
if (! ( streq (*str, "acc0") || streq (*str, "ACC0")))
{
sprintf (buff, _("acc0 expected, not '%.100s'"), *str);
inst.error = buff;
result = FAIL;
}
-
+
*p = c; /* Unzap. */
*str = p; /* Caller's string pointer to after match. */
return result;
/* Expects **str -> after a comma. May be leading blanks.
Advances *str, recognizing a load mode, and setting inst.instruction.
- Returns rn, or else FAIL (in which case may set inst.error
+ Returns rn, or else FAIL (in which case may set inst.error
and not advance str)
-
+
Note: doesn't know Rd, so no err checks that require such knowledge. */
static int
int pre_inc = 0;
skip_whitespace (str);
-
+
if (* str == '[')
{
str++;
-
+
skip_whitespace (str);
if ((rn = reg_required_here (& str, 16)) == FAIL)
if (* str == ']')
{
str ++;
-
+
if (skip_past_comma (& str) == SUCCESS)
{
/* [Rn],... (post inc) */
}
pre_inc = 1;
-
+
if (ldst_extend (& str, 1) == FAIL)
return FAIL;
inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */
inst.reloc.pc_rel = 1;
inst.instruction |= (REG_PC << 16);
-
+
rn = REG_PC;
pre_inc = 1;
}
inst.instruction |= (pre_inc ? PRE_INDEX : 0);
* string = str;
-
+
return rn;
}
|| (rn = reg_required_here (& str, 12)) == FAIL)
inst.error = BAD_ARGS;
- else if (rd == REG_PC || rm == REG_PC || rs == REG_PC || rn == REG_PC)
+ else if (rd == REG_PC || rm == REG_PC || rs == REG_PC || rn == REG_PC)
inst.error = BAD_PC;
else if (flags)
if (rdlo == rdhi)
as_tsktsk (_("rdhi and rdlo must be different"));
-
+
if (flags)
inst.error = BAD_FLAGS;
else
/* ARM V5E (el Segundo)
MCRRcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
- MRRCcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+ MRRCcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
These are equivalent to the XScale instructions MAR and MRA,
- respectively, when coproc == 0, opcode == 0, and CRm == 0.
+ respectively, when coproc == 0, opcode == 0, and CRm == 0.
Result unpredicatable if Rd or Rn is R15. */
/* Unpredictable result if rd or rn is R15. */
if (rd == REG_PC || rn == REG_PC)
- as_tsktsk
+ as_tsktsk
(_("Warning: Instruction unpredictable when using r15"));
if (skip_past_comma (& str) == FAIL
end_of_line (str);
}
-
/* ARM V5 count-leading-zeroes instruction (argument parse)
CLZ{<cond>} <Rd>, <Rm>
Condition defaults to COND_ALWAYS.
if (flags)
inst.error = BAD_FLAGS;
-
+
end_of_line (str);
}
expressionS expr;
unsigned long number;
- skip_whitespace (str);
+ skip_whitespace (str);
/* Allow optional leading '#'. */
if (is_immediate_prefix (*str))
inst.error = _("bad or missing expression");
return;
}
-
+
number = expr.X_add_number;
-
+
/* Check it fits an 8 bit unsigned. */
if (number != (number & 0xff))
{
{
if (my_get_expression (& inst.reloc.exp, & str))
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_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);
}
BLX{<condition>} <Rm> ie BLX(2)
Unfortunately, there are two different opcodes for this mnemonic.
So, the insns[].value is not used, and the code here zaps values
- into inst.instruction.
+ into inst.instruction.
Also, the <target_addr> can be 25 bits, hence has its own reloc. */
static void
as_bad (BAD_FLAGS);
return;
}
-
+
skip_whitespace (mystr);
rm = reg_required_here (& mystr, 0);
-
+
/* The above may set inst.error. Ignore his opinion. */
inst.error = 0;
-
+
if (rm != FAIL)
{
/* Arg is a register.
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, flags);
inst.instruction = 0x4780;
/* Note that this call is to the ARM register recognizer. BLX(2)
- uses the ARM register space, not the Thumb one, so a call to
+ uses the ARM register space, not the Thumb one, so a call to
thumb_reg() would be wrong. */
rm = reg_required_here (& mystr, 3);
inst.error = 0;
-
+
if (rm != FAIL)
{
/* It's BLX(2). The .instruction was zapped with rm & is final. */
if (my_get_expression (& inst.reloc.exp, & mystr))
return;
-
+
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BLX;
inst.reloc.pc_rel = 1;
}
-
+
end_of_line (mystr);
}
unsigned long number;
skip_whitespace (str);
-
+
/* Allow optional leading '#'. */
if (is_immediate_prefix (* str))
str++;
memset (& expr, '\0', sizeof (expr));
-
+
if (my_get_expression (& expr, & str) || (expr.X_op != O_constant))
{
inst.error = _("bad or missing expression");
return;
}
-
+
number = expr.X_add_number;
-
+
/* Check it fits a 16 bit unsigned. */
if (number != (number & 0xffff))
{
inst.error = _("immediate value out of range");
return;
}
-
+
/* Top 12 of 16 bits to bits 19:8. */
inst.instruction |= (number & 0xfff0) << 4;
-
+
/* Bottom 4 of 16 bits to bits 3:0. */
inst.instruction |= number & 0xf;
}
/* Xscale multiply-accumulate (argument parse)
- MIAcc acc0,Rm,Rs
+ MIAcc acc0,Rm,Rs
MIAPHcc acc0,Rm,Rs
MIAxycc acc0,Rm,Rs. */
if (flags)
as_bad (BAD_FLAGS);
-
+
else if (accum0_required_here (& str) == FAIL)
inst.error = ERR_NO_ACCUM;
-
+
else if (skip_past_comma (& str) == FAIL
|| (rm = reg_required_here (& str, 0)) == FAIL)
inst.error = BAD_ARGS;
-
+
else if (skip_past_comma (& str) == FAIL
|| (rs = reg_required_here (& str, 12)) == FAIL)
inst.error = BAD_ARGS;
-
- /* inst.instruction has now been zapped with both rm and rs. */
+
+ /* inst.instruction has now been zapped with both rm and rs. */
else if (rm == REG_PC || rs == REG_PC)
inst.error = BAD_PC; /* Undefined result if rm or rs is R15. */
-
+
else
end_of_line (str);
}
if (flags)
as_bad (BAD_FLAGS);
-
+
else if (accum0_required_here (& str) == FAIL)
inst.error = ERR_NO_ACCUM;
-
+
else if (skip_past_comma (& str) == FAIL
|| (rdlo = reg_required_here (& str, 12)) == FAIL)
inst.error = BAD_ARGS;
-
+
else if (skip_past_comma (& str) == FAIL
|| (rdhi = reg_required_here (& str, 16)) == FAIL)
inst.error = BAD_ARGS;
-
+
/* inst.instruction has now been zapped with both rdlo and rdhi. */
else if (rdlo == REG_PC || rdhi == REG_PC)
inst.error = BAD_PC; /* Undefined result if rdlo or rdhi is R15. */
-
+
else
end_of_line (str);
}
end_of_line (str);
}
-/* Xscale: Preload-Cache
+/* Xscale: Preload-Cache
PLD <addr_mode>
-
+
Syntactically, like LDR with B=1, W=0, L=1. */
static void
return;
skip_whitespace (str);
-
+
if (* str == ']')
{
/* [Rn], ... ? */
inst.error = _("pre-indexed expression expected");
return;
}
-
+
if (ldst_extend (& str, 0) == FAIL)
return;
++ str;
skip_whitespace (str);
-
+
if (* str == '!') /* [Rn]! */
{
inst.error = _("writeback used in preload instruction");
++ str;
}
-
+
inst.instruction |= PRE_INDEX;
}
return;
}
-
+
if ((cpu_variant & ARM_EXT_XSCALE) != ARM_EXT_XSCALE)
{
static char buff[128];
while (isspace (*str))
--str;
str -= 4;
-
+
/* Deny all knowledge. */
sprintf (buff, _("bad instruction '%.100s'"), str);
inst.error = buff;
return;
}
-
+
skip_whitespace (str);
-
+
if ((rd = reg_required_here (& str, 12)) == FAIL)
{
inst.error = BAD_ARGS;
inst.error = BAD_ARGS;
return;
}
-
+
/* inst.instruction has now been zapped with Rd and the addressing mode. */
if (rd & 1) /* Unpredictable result if Rd is odd. */
{
- inst.error = _("Destination register must be even");
+ inst.error = _("Destination register must be even");
return;
}
((inst.instruction & WRITE_BACK)
|| (!(inst.instruction & PRE_INDEX))))
as_warn (_("pre/post-indexing used when modified address register is destination"));
-
+
end_of_line (str);
}
if (ldst_extend (&str, halfword) == FAIL)
return;
if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
+ {
+ if (flags & TRANS_BIT)
+ as_warn (_("Rn and Rd must be different in %s"),
+ ((inst.instruction & LOAD_BIT)
+ ? "LDRT" : "STRT"));
+ else
+ as_warn (_("%s register same as write-back base"),
+ ((inst.instruction & LOAD_BIT)
+ ? _("destination") : _("source")));
+ }
}
else
{
}
flags |= INDEX_UP;
- if (! (flags & TRANS_BIT))
- pre_inc = 1;
+ if (flags & TRANS_BIT)
+ {
+ if (conflict_reg)
+ as_warn (_("Rn and Rd must be different in %s"),
+ ((inst.instruction & LOAD_BIT)
+ ? "LDRT" : "STRT"));
+ }
+ else
+ pre_inc = 1;
}
}
else
if (Rs != Rd)
{
- inst.error = _("dest and source1 one must be the same register");
+ inst.error = _("dest and source1 must be the same register");
return;
}
Rs = Rn;
if (atpcs)
{
asection * sec;
-
+
sec = bfd_make_section (stdoutput, ".arm.atpcs");
if (sec != NULL)
}
/* Catch special cases. */
- if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT))
+ if (cpu_variant & ARM_EXT_XSCALE)
+ mach = bfd_mach_arm_XScale;
+ else if (cpu_variant & ARM_EXT_V5E)
+ mach = bfd_mach_arm_5TE;
+ else if (cpu_variant & ARM_EXT_V5)
+ {
+ if (cpu_variant & ARM_EXT_THUMB)
+ mach = bfd_mach_arm_5T;
+ else
+ mach = bfd_mach_arm_5;
+ }
+ else if (cpu_variant & ARM_EXT_HALFWORD)
{
- if (cpu_variant & ARM_EXT_XSCALE)
- mach = bfd_mach_arm_XScale;
- else if (cpu_variant & ARM_EXT_V5E)
- mach = bfd_mach_arm_5TE;
- else if (cpu_variant & ARM_EXT_V5)
- {
- if (cpu_variant & ARM_EXT_THUMB)
- mach = bfd_mach_arm_5T;
- else
- mach = bfd_mach_arm_5;
- }
- else if (cpu_variant & ARM_EXT_HALFWORD)
- {
- if (cpu_variant & ARM_EXT_THUMB)
- mach = bfd_mach_arm_4T;
- else
- mach = bfd_mach_arm_4;
- }
- else if (cpu_variant & ARM_EXT_LONGMUL)
- mach = bfd_mach_arm_3M;
+ if (cpu_variant & ARM_EXT_THUMB)
+ mach = bfd_mach_arm_4T;
+ else
+ mach = bfd_mach_arm_4;
}
+ else if (cpu_variant & ARM_EXT_LONGMUL)
+ mach = bfd_mach_arm_3M;
bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
}
newval = (newval & 0xf800) | ((value & 0x7fffff) >> 12);
newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
+ if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+ /* Remove bit zero of the adjusted offset. Bit zero can only be
+ set if the upper insn is at a half-word boundary, since the
+ destination address, an ARM instruction, must always be on a
+ word boundary. The semantics of the BLX (1) instruction, however,
+ are that bit zero in the offset must always be zero, and the
+ corresponding bit one in the target address will be set from bit
+ one of the source address. */
+ newval2 &= ~1;
md_number_to_chars (buf, newval, THUMB_SIZE);
md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
}
ARM_SET_INTERWORK (sym, support_interwork);
#endif
- if (label_is_thumb_function_name)
+ /* Note - do not allow local symbols (.Lxxx) to be labeled
+ as Thumb functions. This is because these labels, whilst
+ they exist inside Thumb code, are not the entry points for
+ possible ARM->Thumb calls. Also, these labels can be used
+ as part of a computed goto or switch statement. eg gcc
+ can generate code that looks like this:
+
+ ldr r2, [pc, .Laaa]
+ lsl r3, r3, #2
+ ldr r2, [r3, r2]
+ mov pc, r2
+
+ .Lbbb: .word .Lxxx
+ .Lccc: .word .Lyyy
+ ..etc...
+ .Laaa: .word Lbbb
+
+ The first instruction loads the address of the jump table.
+ The second instruction converts a table index into a byte offset.
+ The third instruction gets the jump address out of the table.
+ The fourth instruction performs the jump.
+
+ If the address stored at .Laaa is that of a symbol which has the
+ Thumb_Func bit set, then the linker will arrange for this address
+ to have the bottom bit set, which in turn would mean that the
+ address computation performed by the third instruction would end
+ up with the bottom bit set. Since the ARM is capable of unaligned
+ word loads, the instruction would then load the incorrect address
+ out of the jump table, and chaos would ensue. */
+ if (label_is_thumb_function_name
+ && (S_GET_NAME (sym)[0] != '.' || S_GET_NAME (sym)[1] != 'L')
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
{
/* When the address of a Thumb function is taken the bottom
bit of that address should be set. This will allow
return false;
}
+#ifdef OBJ_COFF
+/* This is a little hack to help the gas/arm/adrl.s test. It prevents
+ local labels from being added to the output symbol table when they
+ are used with the ADRL pseudo op. The ADRL relocation should always
+ be resolved before the binbary is emitted, so it is safe to say that
+ it is adjustable. */
+
+boolean
+arm_fix_adjustable (fixP)
+ fixS * fixP;
+{
+ if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+ return 1;
+ return 0;
+}
+#endif
#ifdef OBJ_ELF
/* Relocations against Thumb function names must be left unadjusted,
so that the linker can use this information to correctly set the
}
#endif /* OBJ_ELF */
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+arm_handle_align (fragP)
+ fragS *fragP;
+{
+ static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
+ static char const thumb_noop[2] = { 0xc0, 0x46 };
+ static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
+ static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
+
+ int bytes, fix, noop_size;
+ char * p;
+ const char * noop;
+
+ if (fragP->fr_type != rs_align_code)
+ return;
+
+ bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ p = fragP->fr_literal + fragP->fr_fix;
+ fix = 0;
+
+ if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
+ bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+
+ if (fragP->tc_frag_data)
+ {
+ if (target_big_endian)
+ noop = thumb_bigend_noop;
+ else
+ noop = thumb_noop;
+ noop_size = sizeof (thumb_noop);
+ }
+ else
+ {
+ if (target_big_endian)
+ noop = arm_bigend_noop;
+ else
+ noop = arm_noop;
+ noop_size = sizeof (arm_noop);
+ }
+
+ if (bytes & (noop_size - 1))
+ {
+ fix = bytes & (noop_size - 1);
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ while (bytes >= noop_size)
+ {
+ memcpy (p, noop, noop_size);
+ p += noop_size;
+ bytes -= noop_size;
+ fix += noop_size;
+ }
+
+ fragP->fr_fix += fix;
+ fragP->fr_var = noop_size;
+}
+
+/* Called from md_do_align. Used to create an alignment
+ frag in a code section. */
+
+void
+arm_frag_align_code (n, max)
+ int n;
+ int max;
+{
+ char * p;
+
+ /* We assume that there will never be a requirment
+ to support alignments greater than 32 bytes. */
+ if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+ as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+
+ p = frag_var (rs_align_code,
+ MAX_MEM_FOR_RS_ALIGN_CODE,
+ 1,
+ (relax_substateT) max,
+ (symbolS *) NULL,
+ (offsetT) n,
+ (char *) NULL);
+ *p = 0;
+
+}
+
+/* Perform target specific initialisation of a frag. */
+
+void
+arm_init_frag (fragP)
+ fragS *fragP;
+{
+ /* Record whether this frag is in an ARM or a THUMB area. */
+ fragP->tc_frag_data = thumb_mode;
+}