static arm_feature_set selected_cpu = ARM_ARCH_NONE;
/* Must be long enough to hold any of the names in arm_cpus. */
static char selected_cpu_name[16];
+
+/* Return if no cpu was selected on command-line. */
+static bfd_boolean
+no_cpu_selected (void)
+{
+ return selected_cpu.core == arm_arch_none.core
+ && selected_cpu.coproc == arm_arch_none.coproc;
+}
+
#ifdef OBJ_ELF
# ifdef EABI_DEFAULT
static int meabi_flags = EABI_DEFAULT;
_("cannot use register index with PC-relative addressing")
#define BAD_PC_WRITEBACK \
_("cannot use writeback with PC-relative addressing")
+#define BAD_RANGE _("branch out of range")
static struct hash_control * arm_ops_hsh;
static struct hash_control * arm_cond_hsh;
symbolS * symbol;
segT section;
subsegT sub_section;
+#ifdef OBJ_ELF
+ struct dwarf2_line_info locs [MAX_LITERAL_POOL_SIZE];
+#endif
struct literal_pool * next;
} literal_pool;
if (!reg)
as_bad (_("unknown register alias '%s'"), name);
else if (reg->builtin)
- as_warn (_("ignoring attempt to undefine built-in register '%s'"),
+ as_warn (_("ignoring attempt to use .unreq on fixed register name: '%s'"),
name);
else
{
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
- else if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
+
+ if (state == MAP_ARM || state == MAP_THUMB)
+ /* PR gas/12931
+ All ARM instructions require 4-byte alignment.
+ (Almost) all Thumb instructions require 2-byte alignment.
+
+ When emitting instructions into any section, mark the section
+ appropriately.
+
+ Some Thumb instructions are alignment-sensitive modulo 4 bytes,
+ but themselves require 2-byte alignment; this applies to some
+ PC- relative forms. However, these cases will invovle implicit
+ literal pool generation or an explicit .align >=2, both of
+ which will cause the section to me marked with sufficient
+ alignment. Thus, we don't handle those cases here. */
+ record_alignment (now_seg, state == MAP_ARM ? 2 : 1);
+
+ if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
/* This case will be evaluated later in the next else. */
return;
else if (TRANSITION (MAP_UNDEFINED, MAP_ARM)
}
pool->literals[entry] = inst.reloc.exp;
+#ifdef OBJ_ELF
+ /* PR ld/12974: Record the location of the first source line to reference
+ this entry in the literal pool. If it turns out during linking that the
+ symbol does not exist we will be able to give an accurate line number for
+ the (first use of the) missing reference. */
+ if (debug_type == DEBUG_DWARF2)
+ dwarf2_where (pool->locs + entry);
+#endif
pool->next_free_entry += 1;
}
#endif
for (entry = 0; entry < pool->next_free_entry; entry ++)
- /* First output the expression in the instruction to the pool. */
- emit_expr (&(pool->literals[entry]), 4); /* .word */
+ {
+#ifdef OBJ_ELF
+ if (debug_type == DEBUG_DWARF2)
+ dwarf2_gen_line_info (frag_now_fix (), pool->locs + entry);
+#endif
+ /* First output the expression in the instruction to the pool. */
+ emit_expr (&(pool->literals[entry]), 4); /* .word */
+ }
/* Mark the pool as empty. */
pool->next_free_entry = 0;
/* If we're on a 64-bit host, then a 64-bit number can be returned using
O_constant. We have to be careful not to break compilation for
32-bit X_add_number, though. */
- if ((exp.X_add_number & ~0xffffffffl) != 0)
+ if ((exp.X_add_number & ~(offsetT)(0xffffffffU)) != 0)
{
/* X >> 32 is illegal if sizeof (exp.X_add_number) == 4. */
inst.operands[i].reg = ((exp.X_add_number >> 16) >> 16) & 0xffffffff;
}
}
else
- if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
- return PARSE_OPERAND_FAIL;
+ {
+ char *q = p;
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return PARSE_OPERAND_FAIL;
+ /* If the offset is 0, find out if it's a +0 or -0. */
+ if (inst.reloc.exp.X_op == O_constant
+ && inst.reloc.exp.X_add_number == 0)
+ {
+ skip_whitespace (q);
+ if (*q == '#')
+ {
+ q++;
+ skip_whitespace (q);
+ }
+ if (*q == '-')
+ inst.operands[i].negative = 1;
+ }
+ }
}
}
else if (skip_past_char (&p, ':') == SUCCESS)
}
else
{
+ char *q = p;
if (inst.operands[i].negative)
{
inst.operands[i].negative = 0;
}
if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
return PARSE_OPERAND_FAIL;
+ /* If the offset is 0, find out if it's a +0 or -0. */
+ if (inst.reloc.exp.X_op == O_constant
+ && inst.reloc.exp.X_add_number == 0)
+ {
+ skip_whitespace (q);
+ if (*q == '#')
+ {
+ q++;
+ skip_whitespace (q);
+ }
+ if (*q == '-')
+ inst.operands[i].negative = 1;
+ }
}
}
}
/* Parse a PSR flag operand. The value returned is FAIL on syntax error,
or a bitmask suitable to be or-ed into the ARM msr instruction. */
static int
-parse_psr (char **str)
+parse_psr (char **str, bfd_boolean lhs)
{
char *p;
unsigned long psr_field;
const struct asm_psr *psr;
char *start;
+ bfd_boolean is_apsr = FALSE;
+ bfd_boolean m_profile = ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m);
+
+ /* PR gas/12698: If the user has specified -march=all then m_profile will
+ be TRUE, but we want to ignore it in this case as we are building for any
+ CPU type, including non-m variants. */
+ if (selected_cpu.core == arm_arch_any.core)
+ m_profile = FALSE;
/* CPSR's and SPSR's can now be lowercase. This is just a convenience
feature for ease of use and backwards compatibility. */
p = *str;
if (strncasecmp (p, "SPSR", 4) == 0)
- psr_field = SPSR_BIT;
- else if (strncasecmp (p, "CPSR", 4) == 0
- || (strncasecmp (p, "APSR", 4) == 0
- && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m)))
- psr_field = 0;
- else
+ {
+ if (m_profile)
+ goto unsupported_psr;
+
+ psr_field = SPSR_BIT;
+ }
+ else if (strncasecmp (p, "CPSR", 4) == 0)
+ {
+ if (m_profile)
+ goto unsupported_psr;
+
+ psr_field = 0;
+ }
+ else if (strncasecmp (p, "APSR", 4) == 0)
+ {
+ /* APSR[_<bits>] can be used as a synonym for CPSR[_<flags>] on ARMv7-A
+ and ARMv7-R architecture CPUs. */
+ is_apsr = TRUE;
+ psr_field = 0;
+ }
+ else if (m_profile)
{
start = p;
do
p++;
while (ISALNUM (*p) || *p == '_');
+ if (strncasecmp (start, "iapsr", 5) == 0
+ || strncasecmp (start, "eapsr", 5) == 0
+ || strncasecmp (start, "xpsr", 4) == 0
+ || strncasecmp (start, "psr", 3) == 0)
+ p = start + strcspn (start, "rR") + 1;
+
psr = (const struct asm_psr *) hash_find_n (arm_v7m_psr_hsh, start,
p - start);
+
if (!psr)
return FAIL;
+ /* If APSR is being written, a bitfield may be specified. Note that
+ APSR itself is handled above. */
+ if (psr->field <= 3)
+ {
+ psr_field = psr->field;
+ is_apsr = TRUE;
+ goto check_suffix;
+ }
+
*str = p;
- return psr->field;
+ /* M-profile MSR instructions have the mask field set to "10", except
+ *PSR variants which modify APSR, which may use a different mask (and
+ have been handled already). Do that by setting the PSR_f field
+ here. */
+ return psr->field | (lhs ? PSR_f : 0);
}
+ else
+ goto unsupported_psr;
p += 4;
+check_suffix:
if (*p == '_')
{
/* A suffix follows. */
p++;
while (ISALNUM (*p) || *p == '_');
- psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start,
- p - start);
- if (!psr)
- goto error;
+ if (is_apsr)
+ {
+ /* APSR uses a notation for bits, rather than fields. */
+ unsigned int nzcvq_bits = 0;
+ unsigned int g_bit = 0;
+ char *bit;
+
+ for (bit = start; bit != p; bit++)
+ {
+ switch (TOLOWER (*bit))
+ {
+ case 'n':
+ nzcvq_bits |= (nzcvq_bits & 0x01) ? 0x20 : 0x01;
+ break;
+
+ case 'z':
+ nzcvq_bits |= (nzcvq_bits & 0x02) ? 0x20 : 0x02;
+ break;
+
+ case 'c':
+ nzcvq_bits |= (nzcvq_bits & 0x04) ? 0x20 : 0x04;
+ break;
+
+ case 'v':
+ nzcvq_bits |= (nzcvq_bits & 0x08) ? 0x20 : 0x08;
+ break;
+
+ case 'q':
+ nzcvq_bits |= (nzcvq_bits & 0x10) ? 0x20 : 0x10;
+ break;
+
+ case 'g':
+ g_bit |= (g_bit & 0x1) ? 0x2 : 0x1;
+ break;
+
+ default:
+ inst.error = _("unexpected bit specified after APSR");
+ return FAIL;
+ }
+ }
+
+ if (nzcvq_bits == 0x1f)
+ psr_field |= PSR_f;
+
+ if (g_bit == 0x1)
+ {
+ if (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp))
+ {
+ inst.error = _("selected processor does not "
+ "support DSP extension");
+ return FAIL;
+ }
- psr_field |= psr->field;
+ psr_field |= PSR_s;
+ }
+
+ if ((nzcvq_bits & 0x20) != 0
+ || (nzcvq_bits != 0x1f && nzcvq_bits != 0)
+ || (g_bit & 0x2) != 0)
+ {
+ inst.error = _("bad bitmask specified after APSR");
+ return FAIL;
+ }
+ }
+ else
+ {
+ psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start,
+ p - start);
+ if (!psr)
+ goto error;
+
+ psr_field |= psr->field;
+ }
}
else
{
if (ISALNUM (*p))
goto error; /* Garbage after "[CS]PSR". */
- psr_field |= (PSR_c | PSR_f);
+ /* Unadorned APSR is equivalent to APSR_nzcvq/CPSR_f (for writes). This
+ is deprecated, but allow it anyway. */
+ if (is_apsr && lhs)
+ {
+ psr_field |= PSR_f;
+ as_tsktsk (_("writing to APSR without specifying a bitmask is "
+ "deprecated"));
+ }
+ else if (!m_profile)
+ /* These bits are never right for M-profile devices: don't set them
+ (only code paths which read/write APSR reach here). */
+ psr_field |= (PSR_c | PSR_f);
}
*str = p;
return psr_field;
+ unsupported_psr:
+ inst.error = _("selected processor does not support requested special "
+ "purpose register");
+ return FAIL;
+
error:
inst.error = _("flag for {c}psr instruction expected");
return FAIL;
OP_CPSF, /* CPS flags */
OP_ENDI, /* Endianness specifier */
- OP_PSR, /* CPSR/SPSR mask for msr */
+ OP_wPSR, /* CPSR/SPSR/APSR mask for msr (writing). */
+ OP_rPSR, /* CPSR/SPSR/APSR mask for msr (reading). */
OP_COND, /* conditional code */
OP_TB, /* Table branch. */
- OP_RVC_PSR, /* CPSR/SPSR mask for msr, or VFP control register. */
OP_APSR_RR, /* ARM register or "APSR_nzcv". */
OP_RRnpc_I0, /* ARM register or literal 0 */
OP_oI7b, /* immediate, prefix optional, 0 .. 7 */
OP_oI31b, /* 0 .. 31 */
OP_oI32b, /* 1 .. 32 */
+ OP_oI32z, /* 0 .. 32 */
OP_oIffffb, /* 0 .. 65535 */
OP_oI255c, /* curly-brace enclosed, 0 .. 255 */
case OP_oI31b:
case OP_I31b: po_imm_or_fail ( 0, 31, TRUE); break;
case OP_oI32b: po_imm_or_fail ( 1, 32, TRUE); break;
+ case OP_oI32z: po_imm_or_fail ( 0, 32, TRUE); break;
case OP_oIffffb: po_imm_or_fail ( 0, 0xffff, TRUE); break;
/* Immediate variants */
case OP_CPSF: val = parse_cps_flags (&str); break;
case OP_ENDI: val = parse_endian_specifier (&str); break;
case OP_oROR: val = parse_ror (&str); break;
- case OP_PSR: val = parse_psr (&str); break;
case OP_COND: val = parse_cond (&str); break;
case OP_oBARRIER_I15:
po_barrier_or_imm (str); break;
goto failure;
break;
- case OP_RVC_PSR:
- po_reg_or_goto (REG_TYPE_VFC, try_banked_reg);
- inst.operands[i].isvec = 1; /* Mark VFP control reg as vector. */
- break;
- try_banked_reg:
+ case OP_wPSR:
+ case OP_rPSR:
po_reg_or_goto (REG_TYPE_RNB, try_psr);
if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_virt))
{
goto failure;
}
break;
- try_psr:
- val = parse_psr (&str);
- break;
+ try_psr:
+ val = parse_psr (&str, op_parse_code == OP_wPSR);
+ break;
case OP_APSR_RR:
po_reg_or_goto (REG_TYPE_RN, try_apsr);
case OP_CPSF:
case OP_ENDI:
case OP_oROR:
- case OP_PSR:
- case OP_RVC_PSR:
+ case OP_wPSR:
+ case OP_rPSR:
case OP_COND:
case OP_oBARRIER_I15:
case OP_REGLST:
}
if (inst.reloc.type == BFD_RELOC_UNUSED)
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ {
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ }
}
}
BAD_PC_WRITEBACK);
inst.instruction |= HWOFFSET_IMM;
if (inst.reloc.type == BFD_RELOC_UNUSED)
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ {
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ }
}
}
inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
}
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+
return SUCCESS;
}
do_ldrd (void)
{
constraint (inst.operands[0].reg % 2 != 0,
- _("first destination register must be even"));
+ _("first transfer register must be even"));
constraint (inst.operands[1].present
&& inst.operands[1].reg != inst.operands[0].reg + 1,
- _("can only load two consecutive registers"));
+ _("can only transfer two consecutive registers"));
constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
constraint (!inst.operands[2].isreg, _("'[' expected"));
if (!inst.operands[1].present)
inst.operands[1].reg = inst.operands[0].reg + 1;
- if (inst.instruction & LOAD_BIT)
- {
- /* encode_arm_addr_mode_3 will diagnose overlap between the base
- register and the first register written; we have to diagnose
- overlap between the base and the second register written here. */
+ /* encode_arm_addr_mode_3 will diagnose overlap between the base
+ register and the first register written; we have to diagnose
+ overlap between the base and the second register written here. */
- if (inst.operands[2].reg == inst.operands[1].reg
- && (inst.operands[2].writeback || inst.operands[2].postind))
- as_warn (_("base register written back, and overlaps "
- "second destination register"));
+ if (inst.operands[2].reg == inst.operands[1].reg
+ && (inst.operands[2].writeback || inst.operands[2].postind))
+ as_warn (_("base register written back, and overlaps "
+ "second transfer register"));
+ if (!(inst.instruction & V4_STR_BIT))
+ {
/* For an index-register load, the index register must not overlap the
- destination (even if not write-back). */
- else if (inst.operands[2].immisreg
- && ((unsigned) inst.operands[2].imm == inst.operands[0].reg
- || (unsigned) inst.operands[2].imm == inst.operands[1].reg))
- as_warn (_("index register overlaps destination register"));
+ destination (even if not write-back). */
+ if (inst.operands[2].immisreg
+ && ((unsigned) inst.operands[2].imm == inst.operands[0].reg
+ || (unsigned) inst.operands[2].imm == inst.operands[1].reg))
+ as_warn (_("index register overlaps transfer register"));
}
-
inst.instruction |= inst.operands[0].reg << 12;
encode_arm_addr_mode_3 (2, /*is_t=*/FALSE);
}
/* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */
constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
!= (PSR_c|PSR_f),
- _("'CPSR' or 'SPSR' expected"));
+ _("'APSR', 'CPSR' or 'SPSR' expected"));
br = (15<<16) | (inst.operands[1].imm & SPSR_BIT);
}
{
inst.instruction |= inst.operands[2].reg << 8;
inst.instruction |= SHIFT_BY_REG;
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
inst.reloc.type = BFD_RELOC_UNUSED;
}
+static void
+do_t_strexbh (void)
+{
+ constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+ || inst.operands[2].postind || inst.operands[2].writeback
+ || inst.operands[2].immisreg || inst.operands[2].shifted
+ || inst.operands[2].negative,
+ BAD_ADDR_MODE);
+
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rm_rd_rn ();
+}
+
static void
do_strexd (void)
{
static void
vfp_conv (int srcsize)
{
- unsigned immbits = srcsize - inst.operands[1].imm;
+ int immbits = srcsize - inst.operands[1].imm;
+
+ if (srcsize == 16 && !(immbits >= 0 && immbits <= srcsize))
+ {
+ /* If srcsize is 16, inst.operands[1].imm must be in the range 0-16.
+ i.e. immbits must be in range 0 - 16. */
+ inst.error = _("immediate value out of range, expected range [0, 16]");
+ return;
+ }
+ else if (srcsize == 32 && !(immbits >= 0 && immbits < srcsize))
+ {
+ /* If srcsize is 32, inst.operands[1].imm must be in the range 1-32.
+ i.e. immbits must be in range 0 - 31. */
+ inst.error = _("immediate value out of range, expected range [1, 32]");
+ return;
+ }
+
inst.instruction |= (immbits & 1) << 5;
inst.instruction |= (immbits >> 1);
}
}
else
{
+ unsigned int value = inst.reloc.exp.X_add_number;
+ unsigned int shift = inst.operands[2].shift_kind;
+
Rn = inst.operands[2].reg;
/* See if we can do this with a 16-bit instruction. */
if (!inst.operands[2].shifted && inst.size_req != 4)
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction |= Rd << 8;
inst.instruction |= Rs << 16;
+ constraint (Rd == REG_SP && Rs == REG_SP && value > 3,
+ _("shift value over 3 not allowed in thumb mode"));
+ constraint (Rd == REG_SP && Rs == REG_SP && shift != SHIFT_LSL,
+ _("only LSL shift allowed in thumb mode"));
encode_thumb32_shifted_operand (2);
}
}
if (unified_syntax
&& (inst.size_req == 4
- || (inst.size_req != 2 && inst.operands[0].hasreloc)))
+ || (inst.size_req != 2
+ && (inst.operands[0].hasreloc
+ || inst.reloc.exp.X_op == O_constant))))
{
inst.instruction = THUMB_OP32(opcode);
if (cond == COND_ALWAYS)
}
/* Definitely a 32-bit variant. */
+ /* Warning for Erratum 752419. */
+ if (opcode == T_MNEM_ldr
+ && inst.operands[0].reg == REG_SP
+ && inst.operands[1].writeback == 1
+ && !inst.operands[1].immisreg)
+ {
+ if (no_cpu_selected ()
+ || (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7)
+ && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a)
+ && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7r)))
+ as_warn (_("This instruction may be unpredictable "
+ "if executed on M-profile cores "
+ "with interrupts enabled."));
+ }
+
/* Do some validations regarding addressing modes. */
if (inst.operands[1].immisreg && opcode != T_MNEM_ldr
&& opcode != T_MNEM_str)
{
int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
- if (flags == 0)
- {
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m),
- _("selected processor does not support "
- "requested special purpose register"));
- }
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
+ constraint (flags != 0, _("selected processor does not support "
+ "requested special purpose register"));
else
- {
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1),
- _("selected processor does not support "
- "requested special purpose register"));
- /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */
- constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f),
- _("'CPSR' or 'SPSR' expected"));
- }
+ /* mrs only accepts APSR/CPSR/SPSR/CPSR_all/SPSR_all (for non-M profile
+ devices). */
+ constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f),
+ _("'APSR', 'CPSR' or 'SPSR' expected"));
inst.instruction |= (flags & SPSR_BIT) >> 2;
inst.instruction |= inst.operands[1].imm & 0xff;
else
flags = inst.operands[0].imm;
- if (flags & ~0xff)
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
{
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1),
- _("selected processor does not support "
- "requested special purpose register"));
+ int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
+
+ constraint ((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && (bits & ~(PSR_s | PSR_f)) != 0)
+ || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && bits != PSR_f),
+ _("selected processor does not support requested special "
+ "purpose register"));
}
else
- {
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m),
- _("selected processor does not support "
- "requested special purpose register"));
- flags |= PSR_f;
- }
+ constraint ((flags & 0xff) != 0, _("selected processor does not support "
+ "requested special purpose register"));
Rn = inst.operands[1].reg;
reject_bad_reg (Rn);
inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].reg << 16;
inst.instruction |= inst.operands[2].reg;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
{
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[2].reg << 3;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
{
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[2].reg << 3;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
{
to ARM_EXT_V6M. */
if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6m))
{
- if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_os))
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_os)
+ /* This only applies to the v6m howver, not later architectures. */
+ && ! ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7))
as_bad (_("SVC is not permitted on this architecture"));
ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_os);
}
switch (exp->X_op)
{
case O_constant:
+ if (pc_rel)
+ {
+ /* Create an absolute valued symbol, so we have something to
+ refer to in the object file. Unfortunately for us, gas's
+ generic expression parsing will already have folded out
+ any use of .set foo/.type foo %function that may have
+ been used to set type information of the target location,
+ that's being specified symbolically. We have to presume
+ the user knows what they are doing. */
+ char name[16 + 8];
+ symbolS *symbol;
+
+ sprintf (name, "*ABS*0x%lx", (unsigned long)exp->X_add_number);
+
+ symbol = symbol_find_or_make (name);
+ S_SET_SEGMENT (symbol, absolute_section);
+ symbol_set_frag (symbol, &zero_address_frag);
+ S_SET_VALUE (symbol, exp->X_add_number);
+ exp->X_op = O_symbol;
+ exp->X_add_symbol = symbol;
+ exp->X_add_number = 0;
+ }
+ /* FALLTHROUGH */
case O_symbol:
case O_add:
case O_subtract:
{"c", PSR_c},
{"x", PSR_x},
{"s", PSR_s},
- {"g", PSR_s},
/* Combinations of flags. */
{"fs", PSR_f | PSR_s},
{"csxf", PSR_c | PSR_s | PSR_x | PSR_f},
{"cxfs", PSR_c | PSR_x | PSR_f | PSR_s},
{"cxsf", PSR_c | PSR_x | PSR_s | PSR_f},
-
- /* APSR flags */
- {"nzcvq", PSR_f},
- {"nzcvqg", PSR_s | PSR_f}
};
/* Table of V7M psr names. */
{"primask", 16}, {"PRIMASK", 16},
{"basepri", 17}, {"BASEPRI", 17},
{"basepri_max", 18}, {"BASEPRI_MAX", 18},
+ {"basepri_max", 18}, {"BASEPRI_MASK", 18}, /* Typo, preserved for backwards compatibility. */
{"faultmask", 19}, {"FAULTMASK", 19},
{"control", 20}, {"CONTROL", 20}
};
#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_msr
- TCE("mrs", 1000000, f3e08000, 2, (APSR_RR, RVC_PSR), mrs, t_mrs),
- TCE("msr", 120f000, f3808000, 2, (RVC_PSR, RR_EXi), msr, t_msr),
+ TCE("mrs", 1000000, f3e08000, 2, (RRnpc, rPSR), mrs, t_mrs),
+ TCE("msr", 120f000, f3808000, 2, (wPSR, RR_EXi), msr, t_msr),
#undef ARM_VARIANT
#define ARM_VARIANT & arm_ext_v3m /* ARM 7M long multiplies. */
TCE("ldrexh", 1f00f9f, e8d00f5f, 2, (RRnpc_npcsp, RRnpcb),
rd_rn, rd_rn),
TCE("strexb", 1c00f90, e8c00f40, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
- strex, rm_rd_rn),
+ strex, t_strexbh),
TCE("strexh", 1e00f90, e8c00f50, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
- strex, rm_rd_rn),
+ strex, t_strexbh),
TUF("clrex", 57ff01f, f3bf8f2f, 0, (), noargs, noargs),
#undef ARM_VARIANT
NCE(vldr, d100b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
NCE(vstr, d000b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
- nCEF(vcvt, _vcvt, 3, (RNSDQ, RNSDQ, oI32b), neon_cvt),
+ nCEF(vcvt, _vcvt, 3, (RNSDQ, RNSDQ, oI32z), neon_cvt),
nCEF(vcvtr, _vcvt, 2, (RNSDQ, RNSDQ), neon_cvtr),
nCEF(vcvtb, _vcvt, 2, (RVS, RVS), neon_cvtb),
nCEF(vcvtt, _vcvt, 2, (RVS, RVS), neon_cvtt),
if (S_IS_DEFINED (fragp->fr_symbol)
&& ARM_IS_FUNC (fragp->fr_symbol))
return 4;
+
+ /* PR 12532. Global symbols with default visibility might
+ be preempted, so do not relax relocations to them. */
+ if ((ELF_ST_VISIBILITY (S_GET_OTHER (fragp->fr_symbol)) == STV_DEFAULT)
+ && (! S_IS_LOCAL (fragp->fr_symbol)))
+ return 4;
#endif
val = relaxed_symbol_addr (fragp, stretch);
value = 0;
case BFD_RELOC_ARM_LITERAL:
- sign = value >= 0;
+ sign = value > 0;
if (value < 0)
value = - value;
}
newval = md_chars_to_number (buf, INSN_SIZE);
- newval &= 0xff7ff000;
- newval |= value | (sign ? INDEX_UP : 0);
+ if (value == 0)
+ newval &= 0xfffff000;
+ else
+ {
+ newval &= 0xff7ff000;
+ newval |= value | (sign ? INDEX_UP : 0);
+ }
md_number_to_chars (buf, newval, INSN_SIZE);
break;
case BFD_RELOC_ARM_OFFSET_IMM8:
case BFD_RELOC_ARM_HWLITERAL:
- sign = value >= 0;
+ sign = value > 0;
if (value < 0)
value = - value;
}
newval = md_chars_to_number (buf, INSN_SIZE);
- newval &= 0xff7ff0f0;
- newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0);
+ if (value == 0)
+ newval &= 0xfffff0f0;
+ else
+ {
+ newval &= 0xff7ff0f0;
+ newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0);
+ }
md_number_to_chars (buf, newval, INSN_SIZE);
break;
_("misaligned branch destination"));
if ((value & (offsetT)0xfe000000) != (offsetT)0
&& (value & (offsetT)0xfe000000) != (offsetT)0xfe000000)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
{
else
{
if (value & ~0x7e)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
{
case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */
if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
{
case BFD_RELOC_THUMB_PCREL_BRANCH12: /* Unconditional branch. */
if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
{
/* Force a relocation for a branch 20 bits wide. */
fixP->fx_done = 0;
}
- if ((value & ~0x1fffff) && ((value & ~0x1fffff) != ~0x1fffff))
+ if ((value & ~0x1fffff) && ((value & ~0x0fffff) != ~0x0fffff))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("conditional branch out of range"));
break;
case BFD_RELOC_THUMB_PCREL_BLX:
-
/* If there is a blx from a thumb state function to
another thumb function flip this to a bl and warn
about it. */
goto thumb_bl_common;
case BFD_RELOC_THUMB_PCREL_BRANCH23:
-
/* A bl from Thumb state ISA to an internal ARM state function
is converted to a blx. */
if (fixP->fx_addsy
1 of the base address. */
value = (value + 1) & ~ 1;
-
if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
- {
- if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)))
- {
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
- }
- else if ((value & ~0x1ffffff)
- && ((value & ~0x1ffffff) != ~0x1ffffff))
- {
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Thumb2 branch out of range"));
- }
- }
+ {
+ if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+ else if ((value & ~0x1ffffff)
+ && ((value & ~0x1ffffff) != ~0x1ffffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Thumb2 branch out of range"));
+ }
if (fixP->fx_done || !seg->use_rela_p)
encode_thumb2_b_bl_offset (buf, value);
break;
case BFD_RELOC_THUMB_PCREL_BRANCH25:
- if ((value & ~0x1ffffff) && ((value & ~0x1ffffff) != ~0x1ffffff))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ if ((value & ~0x0ffffff) && ((value & ~0x0ffffff) != ~0x0ffffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
encode_thumb2_b_bl_offset (buf, value);
as_bad_where (fixP->fx_file, fixP->fx_line,
_("co-processor offset out of range"));
cp_off_common:
- sign = value >= 0;
+ sign = value > 0;
if (value < 0)
value = -value;
if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
newval = md_chars_to_number (buf, INSN_SIZE);
else
newval = get_thumb32_insn (buf);
- newval &= 0xff7fff00;
- newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+ if (value == 0)
+ newval &= 0xffffff00;
+ else
+ {
+ newval &= 0xff7fff00;
+ newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+ }
if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
|| fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2)
md_number_to_chars (buf, newval, INSN_SIZE);
}
#endif
- /* Resolve these relocations even if the symbol is extern or weak. */
+ /* Resolve these relocations even if the symbol is extern or weak.
+ Technically this is probably wrong due to symbol preemption.
+ In practice these relocations do not have enough range to be useful
+ at dynamic link time, and some code (e.g. in the Linux kernel)
+ expects these references to be resolved. */
if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM8
|| fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2
+ || fixp->fx_r_type == BFD_RELOC_ARM_THUMB_OFFSET
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12
- || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12)
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_OFFSET_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM_S2)
return 0;
/* Always leave these relocations for the linker. */
{"cortex-r4", ARM_ARCH_V7R, FPU_NONE, "Cortex-R4"},
{"cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16,
"Cortex-R4F"},
+ {"cortex-r5", ARM_ARCH_V7R_IDIV,
+ FPU_NONE, "Cortex-R5"},
{"cortex-m4", ARM_ARCH_V7EM, FPU_NONE, "Cortex-M4"},
{"cortex-m3", ARM_ARCH_V7M, FPU_NONE, "Cortex-M3"},
{"cortex-m1", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M1"},
static const struct arm_option_extension_value_table arm_extensions[] =
{
{"idiv", ARM_FEATURE (ARM_EXT_ADIV | ARM_EXT_DIV, 0),
- ARM_FEATURE (ARM_EXT_V7A, 0)},
+ ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)},
{"iwmmxt", ARM_FEATURE (0, ARM_CEXT_IWMMXT), ARM_ANY},
{"iwmmxt2", ARM_FEATURE (0, ARM_CEXT_IWMMXT2), ARM_ANY},
{"maverick", ARM_FEATURE (0, ARM_CEXT_MAVERICK), ARM_ANY},