X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-arm.c;h=77606a3bb6282bdf9f8140076919bd3a1b9ddb11;hb=877807f8c481a4c5780b3d7bba7963c068910434;hp=b3096c5d73f0d872d32d69e73deec65606d4140a;hpb=8a59fff3dd9f8003c95d89ee2a71d632b130cf8b;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index b3096c5d73..77606a3bb6 100644 --- a/gas/config/tc-arm.c +++ b/gas/config/tc-arm.c @@ -1,6 +1,6 @@ /* tc-arm.c -- Assemble for the ARM Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005, 2006, 2007, 2008, 2009 + 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) Modified by David Taylor (dtaylor@armltd.co.uk) @@ -184,8 +184,8 @@ static const arm_feature_set arm_ext_v5exp = ARM_FEATURE (ARM_EXT_V5ExP, 0); static const arm_feature_set arm_ext_v5j = ARM_FEATURE (ARM_EXT_V5J, 0); static const arm_feature_set arm_ext_v6 = ARM_FEATURE (ARM_EXT_V6, 0); static const arm_feature_set arm_ext_v6k = ARM_FEATURE (ARM_EXT_V6K, 0); -static const arm_feature_set arm_ext_v6z = ARM_FEATURE (ARM_EXT_V6Z, 0); static const arm_feature_set arm_ext_v6t2 = ARM_FEATURE (ARM_EXT_V6T2, 0); +static const arm_feature_set arm_ext_v6m = ARM_FEATURE (ARM_EXT_V6M, 0); static const arm_feature_set arm_ext_v6_notm = ARM_FEATURE (ARM_EXT_V6_NOTM, 0); static const arm_feature_set arm_ext_v6_dsp = ARM_FEATURE (ARM_EXT_V6_DSP, 0); static const arm_feature_set arm_ext_barrier = ARM_FEATURE (ARM_EXT_BARRIER, 0); @@ -196,12 +196,18 @@ static const arm_feature_set arm_ext_v7a = ARM_FEATURE (ARM_EXT_V7A, 0); static const arm_feature_set arm_ext_v7r = ARM_FEATURE (ARM_EXT_V7R, 0); static const arm_feature_set arm_ext_v7m = ARM_FEATURE (ARM_EXT_V7M, 0); static const arm_feature_set arm_ext_m = - ARM_FEATURE (ARM_EXT_V6M | ARM_EXT_V7M, 0); + ARM_FEATURE (ARM_EXT_V6M | ARM_EXT_OS | ARM_EXT_V7M, 0); +static const arm_feature_set arm_ext_mp = ARM_FEATURE (ARM_EXT_MP, 0); +static const arm_feature_set arm_ext_sec = ARM_FEATURE (ARM_EXT_SEC, 0); +static const arm_feature_set arm_ext_os = ARM_FEATURE (ARM_EXT_OS, 0); +static const arm_feature_set arm_ext_adiv = ARM_FEATURE (ARM_EXT_ADIV, 0); +static const arm_feature_set arm_ext_virt = ARM_FEATURE (ARM_EXT_VIRT, 0); static const arm_feature_set arm_arch_any = ARM_ANY; static const arm_feature_set arm_arch_full = ARM_FEATURE (-1, -1); static const arm_feature_set arm_arch_t2 = ARM_ARCH_THUMB2; static const arm_feature_set arm_arch_none = ARM_ARCH_NONE; +static const arm_feature_set arm_arch_v6m_only = ARM_ARCH_V6M_ONLY; static const arm_feature_set arm_cext_iwmmxt2 = ARM_FEATURE (0, ARM_CEXT_IWMMXT2); @@ -233,6 +239,15 @@ static int mfloat_abi_opt = -1; 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; @@ -502,6 +517,7 @@ enum arm_reg_type REG_TYPE_MMXWC, REG_TYPE_MMXWCG, REG_TYPE_XSCALE, + REG_TYPE_RNB }; /* Structure for a hash table entry for a register. @@ -511,7 +527,7 @@ enum arm_reg_type struct reg_entry { const char * name; - unsigned char number; + unsigned int number; unsigned char type; unsigned char builtin; struct neon_typed_alias * neon; @@ -708,6 +724,7 @@ struct asm_opcode _("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; @@ -742,6 +759,9 @@ typedef struct literal_pool 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; @@ -942,6 +962,8 @@ my_get_expression (expressionS * ep, char ** str, int prefix_mode) input_line_pointer = save_in; return 1; } +#else + (void) seg; #endif /* Get rid of any bignums now, so that we don't generate an error for which @@ -1463,6 +1485,10 @@ arm_typed_reg_parse (char **ccp, enum arm_reg_type type, if (reg == FAIL) return FAIL; + /* Do not allow regname(... to parse as a register. */ + if (*str == '(') + return FAIL; + /* Do not allow a scalar (reg+index) to parse as a register. */ if ((atype.defined & NTA_HASINDEX) != 0) { @@ -1881,7 +1907,6 @@ parse_neon_el_struct_list (char **str, unsigned *pbase, int lane = -1; int leading_brace = 0; enum arm_reg_type rtype = REG_TYPE_NDQ; - int addregs = 1; const char *const incr_error = _("register stride must be 1 or 2"); const char *const type_error = _("mismatched element/structure types in list"); struct neon_typed_alias firsttype; @@ -1906,7 +1931,6 @@ parse_neon_el_struct_list (char **str, unsigned *pbase, if (rtype == REG_TYPE_NQ) { reg_incr = 1; - addregs = 2; } firsttype = atype; } @@ -2058,7 +2082,7 @@ parse_reloc (char **str) /* Directives: register aliases. */ static struct reg_entry * -insert_reg_alias (char *str, int number, int type) +insert_reg_alias (char *str, unsigned number, int type) { struct reg_entry *new_reg; const char *name; @@ -2207,7 +2231,7 @@ create_neon_reg_alias (char *newname, char *p) struct reg_entry mybasereg; struct neon_type ntype; struct neon_typed_alias typeinfo; - char *namebuf, *nameend; + char *namebuf, *nameend ATTRIBUTE_UNUSED; int namelen; typeinfo.defined = 0; @@ -2303,7 +2327,16 @@ create_neon_reg_alias (char *newname, char *p) } } + /* If TC_CASE_SENSITIVE is defined, then newname already points to + the desired alias name, and p points to its end. If not, then + the desired alias name is in the global original_case_string. */ +#ifdef TC_CASE_SENSITIVE namelen = nameend - newname; +#else + newname = original_case_string; + namelen = strlen (newname); +#endif + namebuf = (char *) alloca (namelen + 1); strncpy (namebuf, newname, namelen); namebuf[namelen] = '\0'; @@ -2383,7 +2416,7 @@ s_unreq (int a ATTRIBUTE_UNUSED) 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 { @@ -2493,14 +2526,27 @@ make_mapping_symbol (enum mstate state, valueT value, fragS *frag) /* Save the mapping symbols for future reference. Also check that we do not place two mapping symbols at the same offset within a frag. We'll handle overlap between frags in - check_mapping_symbols. */ + check_mapping_symbols. + + If .fill or other data filling directive generates zero sized data, + the mapping symbol for the following code will have the same value + as the one generated for the data filling directive. In this case, + we replace the old symbol with the new one at the same address. */ if (value == 0) { - know (frag->tc_frag_data.first_map == NULL); + if (frag->tc_frag_data.first_map != NULL) + { + know (S_GET_VALUE (frag->tc_frag_data.first_map) == 0); + symbol_remove (frag->tc_frag_data.first_map, &symbol_rootP, &symbol_lastP); + } frag->tc_frag_data.first_map = symbolP; } if (frag->tc_frag_data.last_map != NULL) - know (S_GET_VALUE (frag->tc_frag_data.last_map) < S_GET_VALUE (symbolP)); + { + know (S_GET_VALUE (frag->tc_frag_data.last_map) <= S_GET_VALUE (symbolP)); + if (S_GET_VALUE (frag->tc_frag_data.last_map) == S_GET_VALUE (symbolP)) + symbol_remove (frag->tc_frag_data.last_map, &symbol_rootP, &symbol_lastP); + } frag->tc_frag_data.last_map = symbolP; } @@ -2547,7 +2593,24 @@ mapping_state (enum mstate state) /* 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) @@ -3014,6 +3077,14 @@ add_to_lit_pool (void) } 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; } @@ -3111,8 +3182,14 @@ s_ltorg (int ignored ATTRIBUTE_UNUSED) #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; @@ -4242,12 +4319,37 @@ s_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED) if (tag < NUM_KNOWN_OBJ_ATTRIBUTES) attributes_set_explicitly[tag] = 1; } + +/* Emit a tls fix for the symbol. */ + +static void +s_arm_tls_descseq (int ignored ATTRIBUTE_UNUSED) +{ + char *p; + expressionS exp; +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_cons_align + md_cons_align (4); +#endif + + /* Since we're just labelling the code, there's no need to define a + mapping symbol. */ + expression (&exp); + p = obstack_next_free (&frchain_now->frch_obstack); + fix_new_arm (frag_now, p - frag_now->fr_literal, 4, &exp, 0, + thumb_mode ? BFD_RELOC_ARM_THM_TLS_DESCSEQ + : BFD_RELOC_ARM_TLS_DESCSEQ); +} #endif /* OBJ_ELF */ static void s_arm_arch (int); static void s_arm_object_arch (int); static void s_arm_cpu (int); static void s_arm_fpu (int); +static void s_arm_arch_extension (int); #ifdef TE_PE @@ -4301,6 +4403,7 @@ const pseudo_typeS md_pseudo_table[] = { "arch", s_arm_arch, 0 }, { "object_arch", s_arm_object_arch, 0 }, { "fpu", s_arm_fpu, 0 }, + { "arch_extension", s_arm_arch_extension, 0 }, #ifdef OBJ_ELF { "word", s_arm_elf_cons, 4 }, { "long", s_arm_elf_cons, 4 }, @@ -4321,6 +4424,7 @@ const pseudo_typeS md_pseudo_table[] = { "setfp", s_arm_unwind_setfp, 0 }, { "unwind_raw", s_arm_unwind_raw, 0 }, { "eabi_attribute", s_arm_eabi_attribute, 0 }, + { "tlsdescseq", s_arm_tls_descseq, 0 }, #else { "word", cons, 4}, @@ -4390,7 +4494,7 @@ parse_big_immediate (char **str, int i) /* 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; @@ -4398,14 +4502,32 @@ parse_big_immediate (char **str, int i) } } else if (exp.X_op == O_big - && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 32 - && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number <= 64) + && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 32) { unsigned parts = 32 / LITTLENUM_NUMBER_OF_BITS, j, idx = 0; + /* Bignums have their least significant bits in generic_bignum[0]. Make sure we put 32 bits in imm and 32 bits in reg, in a (hopefully) portable way. */ gas_assert (parts != 0); + + /* Make sure that the number is not too big. + PR 11972: Bignums can now be sign-extended to the + size of a .octa so check that the out of range bits + are all zero or all one. */ + if (LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 64) + { + LITTLENUM_TYPE m = -1; + + if (generic_bignum[parts * 2] != 0 + && generic_bignum[parts * 2] != m) + return FAIL; + + for (j = parts * 2 + 1; j < (unsigned) exp.X_add_number; j++) + if (generic_bignum[j] != generic_bignum[j-1]) + return FAIL; + } + inst.operands[i].imm = 0; for (j = 0; j < parts; j++, idx++) inst.operands[i].imm |= generic_bignum[idx] @@ -4935,6 +5057,33 @@ parse_shifter_operand_group_reloc (char **str, int i) /* Never reached. */ } +/* Parse a Neon alignment expression. Information is written to + inst.operands[i]. We assume the initial ':' has been skipped. + + align .imm = align << 8, .immisalign=1, .preind=0 */ +static parse_operand_result +parse_neon_alignment (char **str, int i) +{ + char *p = *str; + expressionS exp; + + my_get_expression (&exp, &p, GE_NO_PREFIX); + + if (exp.X_op != O_constant) + { + inst.error = _("alignment must be constant"); + return PARSE_OPERAND_FAIL; + } + + inst.operands[i].imm = exp.X_add_number << 8; + inst.operands[i].immisalign = 1; + /* Alignments are not pre-indexes. */ + inst.operands[i].preind = 0; + + *str = p; + return PARSE_OPERAND_SUCCESS; +} + /* Parse all forms of an ARM address expression. Information is written to inst.operands[i] and/or inst.reloc. @@ -5018,22 +5167,15 @@ parse_address_main (char **str, int i, int group_relocations, return PARSE_OPERAND_FAIL; } else if (skip_past_char (&p, ':') == SUCCESS) - { - /* FIXME: '@' should be used here, but it's filtered out by generic - code before we get to see it here. This may be subject to - change. */ - expressionS exp; - my_get_expression (&exp, &p, GE_NO_PREFIX); - if (exp.X_op != O_constant) - { - inst.error = _("alignment must be constant"); - return PARSE_OPERAND_FAIL; - } - inst.operands[i].imm = exp.X_add_number << 8; - inst.operands[i].immisalign = 1; - /* Alignments are not pre-indexes. */ - inst.operands[i].preind = 0; - } + { + /* FIXME: '@' should be used here, but it's filtered out by generic + code before we get to see it here. This may be subject to + change. */ + parse_operand_result result = parse_neon_alignment (&p, i); + + if (result != PARSE_OPERAND_SUCCESS) + return result; + } else { if (inst.operands[i].negative) @@ -5093,10 +5235,35 @@ parse_address_main (char **str, int i, int group_relocations, } } 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) + { + /* FIXME: '@' should be used here, but it's filtered out by generic code + before we get to see it here. This may be subject to change. */ + parse_operand_result result = parse_neon_alignment (&p, i); + + if (result != PARSE_OPERAND_SUCCESS) + return result; + } if (skip_past_char (&p, ']') == FAIL) { @@ -5159,6 +5326,7 @@ parse_address_main (char **str, int i, int group_relocations, } else { + char *q = p; if (inst.operands[i].negative) { inst.operands[i].negative = 0; @@ -5166,6 +5334,19 @@ parse_address_main (char **str, int i, int group_relocations, } 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; + } } } } @@ -5240,37 +5421,85 @@ parse_half (char **str) /* 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; + { + if (m_profile) + goto unsupported_psr; + + psr_field = SPSR_BIT; + } else if (strncasecmp (p, "CPSR", 4) == 0) - psr_field = 0; - else + { + if (m_profile) + goto unsupported_psr; + + psr_field = 0; + } + else if (strncasecmp (p, "APSR", 4) == 0) + { + /* APSR[_] can be used as a synonym for CPSR[_] 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. */ @@ -5281,23 +5510,106 @@ parse_psr (char **str) 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; @@ -5746,6 +6058,8 @@ enum operand_parse_code OP_RRnpc, /* ARM register, not r15 */ OP_RRnpcsp, /* ARM register, neither r15 nor r13 (a.k.a. 'BadReg') */ OP_RRnpcb, /* ARM register, not r15, in square brackets */ + OP_RRnpctw, /* ARM register, not r15 in Thumb-state or with writeback, + optional trailing ! */ OP_RRw, /* ARM register, not r15, optional trailing ! */ OP_RCP, /* Coprocessor number */ OP_RCN, /* Coprocessor register */ @@ -5821,11 +6135,11 @@ enum operand_parse_code 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 */ @@ -5839,6 +6153,7 @@ enum operand_parse_code 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 */ @@ -5854,7 +6169,7 @@ enum operand_parse_code OP_oSHar, /* ASR immediate */ OP_oSHllar, /* LSL or ASR immediate */ OP_oROR, /* ROR 0/8/16/24 */ - OP_oBARRIER, /* Option argument for a barrier instruction. */ + OP_oBARRIER_I15, /* Option argument for a barrier instruction. */ /* Some pre-defined mixed (ARM/THUMB) operands. */ OP_RR_npcsp = MIX_ARM_THUMB_OPERANDS (OP_RR, OP_RRnpcsp), @@ -5964,6 +6279,30 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) } \ while (0) +#define po_barrier_or_imm(str) \ + do \ + { \ + val = parse_barrier (&str); \ + if (val == FAIL) \ + { \ + if (ISALPHA (*str)) \ + goto failure; \ + else \ + goto immediate; \ + } \ + else \ + { \ + if ((inst.instruction & 0xf0) == 0x60 \ + && val != 0xf) \ + { \ + /* ISB can only take SY as an option. */ \ + inst.error = _("invalid barrier type"); \ + goto failure; \ + } \ + } \ + } \ + while (0) + skip_whitespace (str); for (i = 0; upat[i] != OP_stop; i++) @@ -6115,6 +6454,7 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) po_char_or_fail (']'); break; + case OP_RRnpctw: case OP_RRw: case OP_oRRw: po_reg_or_fail (REG_TYPE_RN); @@ -6143,6 +6483,7 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) 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 */ @@ -6266,17 +6607,27 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) 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:val = parse_barrier (&str); break; + case OP_oBARRIER_I15: + po_barrier_or_imm (str); break; + immediate: + if (parse_immediate (&str, &val, 0, 15, TRUE) == FAIL) + goto failure; + break; - case OP_RVC_PSR: - po_reg_or_goto (REG_TYPE_VFC, try_psr); - inst.operands[i].isvec = 1; /* Mark VFP control reg as vector. */ - break; - try_psr: - val = parse_psr (&str); - break; + 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)) + { + inst.error = _("Banked registers are not available with this " + "architecture."); + goto failure; + } + 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); @@ -6423,13 +6774,20 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) } break; + case OP_RRnpctw: + if (inst.operands[i].isreg + && inst.operands[i].reg == REG_PC + && (inst.operands[i].writeback || thumb)) + inst.error = BAD_PC; + break; + 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: + case OP_oBARRIER_I15: case OP_REGLST: case OP_VRSLST: case OP_VRDLST: @@ -6494,6 +6852,7 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) #undef po_reg_or_goto #undef po_imm_or_fail #undef po_scalar_or_fail +#undef po_barrier_or_imm /* Shorthand macro for instruction encoding functions issuing errors. */ #define constraint(expr, err) \ @@ -6737,15 +7096,27 @@ encode_arm_addr_mode_2 (int i, bfd_boolean is_t) if (is_pc && !inst.reloc.pc_rel) { const bfd_boolean is_load = ((inst.instruction & LOAD_BIT) != 0); - /* BAD_PC_ADDRESSING Condition = - is_load => is_t - which becomes !is_load || is_t. */ - constraint ((!is_load || is_t), + + /* If is_t is TRUE, it's called from do_ldstt. ldrt/strt + cannot use PC in addressing. + PC cannot be used in writeback addressing, either. */ + constraint ((is_t || inst.operands[i].writeback), BAD_PC_ADDRESSING); + + /* Use of PC in str is deprecated for ARMv7. */ + if (warn_on_deprecated + && !is_load + && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7)) + as_warn (_("use of PC in this instruction is deprecated")); } 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; + } } } @@ -6781,7 +7152,13 @@ encode_arm_addr_mode_3 (int i, bfd_boolean is_t) 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; + } } } @@ -6843,6 +7220,10 @@ encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override) 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; } @@ -6970,8 +7351,16 @@ do_rd_rm_rn (void) unsigned Rn = inst.operands[2].reg; /* Enforce restrictions on SWP instruction. */ if ((inst.instruction & 0x0fbfffff) == 0x01000090) - constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg, - _("Rn must not overlap other operands")); + { + constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg, + _("Rn must not overlap other operands")); + + /* SWP{b} is deprecated for ARMv6* and ARMv7. */ + if (warn_on_deprecated + && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6)) + as_warn (_("swp{b} use is deprecated for this architecture")); + + } inst.instruction |= inst.operands[0].reg << 12; inst.instruction |= inst.operands[1].reg; inst.instruction |= Rn << 16; @@ -7064,7 +7453,8 @@ do_barrier (void) if (inst.operands[0].present) { constraint ((inst.instruction & 0xf0) != 0x40 - && inst.operands[0].imm != 0xf, + && inst.operands[0].imm > 0xf + && inst.operands[0].imm < 0x0, _("bad barrier type")); inst.instruction |= inst.operands[0].imm; } @@ -7136,14 +7526,15 @@ encode_branch (int default_reloc) { if (inst.operands[0].hasreloc) { - constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32, - _("the only suffix valid here is '(plt)'")); - inst.reloc.type = BFD_RELOC_ARM_PLT32; + constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32 + && inst.operands[0].imm != BFD_RELOC_ARM_TLS_CALL, + _("the only valid suffixes here are '(plt)' and '(tlscall)'")); + inst.reloc.type = inst.operands[0].imm == BFD_RELOC_ARM_PLT32 + ? BFD_RELOC_ARM_PLT32 + : thumb_mode ? BFD_RELOC_ARM_THM_TLS_CALL : BFD_RELOC_ARM_TLS_CALL; } else - { - inst.reloc.type = (bfd_reloc_code_real_type) default_reloc; - } + inst.reloc.type = (bfd_reloc_code_real_type) default_reloc; inst.reloc.pc_rel = 1; } @@ -7359,6 +7750,25 @@ do_dbg (void) inst.instruction |= inst.operands[0].imm; } +static void +do_div (void) +{ + unsigned Rd, Rn, Rm; + + Rd = inst.operands[0].reg; + Rn = (inst.operands[1].present + ? inst.operands[1].reg : Rd); + Rm = inst.operands[2].reg; + + constraint ((Rd == REG_PC), BAD_PC); + constraint ((Rn == REG_PC), BAD_PC); + constraint ((Rm == REG_PC), BAD_PC); + + inst.instruction |= Rd << 16; + inst.instruction |= Rn << 0; + inst.instruction |= Rm << 8; +} + static void do_it (void) { @@ -7425,35 +7835,34 @@ static void 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); } @@ -7699,16 +8108,30 @@ do_vmsr (void) static void do_mrs (void) { + unsigned br; + if (do_vfp_nsyn_mrs () == SUCCESS) return; - /* 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")); constraint (inst.operands[0].reg == REG_PC, BAD_PC); inst.instruction |= inst.operands[0].reg << 12; - inst.instruction |= (inst.operands[1].imm & SPSR_BIT); + + if (inst.operands[1].isreg) + { + br = inst.operands[1].reg; + if (((br & 0x200) == 0) && ((br & 0xf0000) != 0xf000)) + as_bad (_("bad register for mrs")); + } + else + { + /* 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), + _("'APSR', 'CPSR' or 'SPSR' expected")); + br = (15<<16) | (inst.operands[1].imm & SPSR_BIT); + } + + inst.instruction |= br; } /* Two possible forms: @@ -7826,8 +8249,9 @@ do_pkhtb (void) } /* ARMv5TE: Preload-Cache + MP Extensions: Preload for write - PLD + PLD(W) Syntactically, like LDR with B=1, W=0, L=1. */ @@ -7956,6 +8380,9 @@ do_shift (void) { 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; @@ -7968,6 +8395,13 @@ do_smc (void) inst.reloc.pc_rel = 0; } +static void +do_hvc (void) +{ + inst.reloc.type = BFD_RELOC_ARM_HVC; + inst.reloc.pc_rel = 0; +} + static void do_swi (void) { @@ -8066,6 +8500,21 @@ do_strex (void) 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) { @@ -8339,7 +8788,23 @@ do_vfp_dp_const (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); } @@ -9041,6 +9506,9 @@ do_t_add_sub (void) } 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) @@ -9091,6 +9559,10 @@ do_t_add_sub (void) 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); } } @@ -9371,7 +9843,8 @@ do_t_barrier (void) if (inst.operands[0].present) { constraint ((inst.instruction & 0xf0) != 0x40 - && inst.operands[0].imm != 0xf, + && inst.operands[0].imm > 0xf + && inst.operands[0].imm < 0x0, _("bad barrier type")); inst.instruction |= inst.operands[0].imm; } @@ -9470,8 +9943,7 @@ do_t_blx (void) { /* No register. This must be BLX(1). */ inst.instruction = 0xf000e800; - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BLX; - inst.reloc.pc_rel = 1; + encode_branch (BFD_RELOC_THUMB_PCREL_BLX); } } @@ -9480,6 +9952,7 @@ do_t_branch (void) { int opcode; int cond; + int reloc; cond = inst.cond; set_it_insn_type (IF_INSIDE_IT_LAST_INSN); @@ -9498,33 +9971,37 @@ do_t_branch (void) else opcode = inst.instruction; - if (unified_syntax && inst.size_req == 4) + if (unified_syntax + && (inst.size_req == 4 + || (inst.size_req != 2 + && (inst.operands[0].hasreloc + || inst.reloc.exp.X_op == O_constant)))) { inst.instruction = THUMB_OP32(opcode); if (cond == COND_ALWAYS) - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH25; else { gas_assert (cond != 0xF); inst.instruction |= cond << 22; - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH20; } } else { inst.instruction = THUMB_OP16(opcode); if (cond == COND_ALWAYS) - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH12; else { inst.instruction |= cond << 8; - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9; + reloc = BFD_RELOC_THUMB_PCREL_BRANCH9; } /* Allow section relaxation. */ if (unified_syntax && inst.size_req != 2) inst.relax = opcode; } - + inst.reloc.type = reloc; inst.reloc.pc_rel = 1; } @@ -9546,8 +10023,15 @@ static void do_t_branch23 (void) { set_it_insn_type_last (); - inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23; - inst.reloc.pc_rel = 1; + encode_branch (BFD_RELOC_THUMB_PCREL_BRANCH23); + + /* md_apply_fix blows up with 'bl foo(PLT)' where foo is defined in + this file. We used to simply ignore the PLT reloc type here -- + the branch encoding is now needed to deal with TLSCALL relocs. + So if we see a PLT reloc now, put it back to how it used to be to + keep the preexisting behaviour. */ + if (inst.reloc.type == BFD_RELOC_ARM_PLT32) + inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23; #if defined(OBJ_COFF) /* If the destination of the branch is a defined symbol which does not have @@ -9739,6 +10223,12 @@ encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) if (mask & (1 << 13)) inst.error = _("SP not allowed in register list"); + + if ((mask & (1 << base)) != 0 + && writeback) + inst.error = _("having the base register in the register list when " + "using write back is UNPREDICTABLE"); + if (load) { if (mask & (1 << 15)) @@ -9748,19 +10238,11 @@ encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback) else set_it_insn_type_last (); } - - if ((mask & (1 << base)) != 0 - && writeback) - as_warn (_("base register should not be in register list " - "when written back")); } else { if (mask & (1 << 15)) inst.error = _("PC not allowed in register list"); - - if (mask & (1 << base)) - as_warn (_("value stored for r%d is UNPREDICTABLE"), base); } if ((mask & (mask - 1)) == 0) @@ -9817,30 +10299,68 @@ do_t_ldmstm (void) { mask = 1 << inst.operands[0].reg; - if (inst.operands[0].reg <= 7 - && (inst.instruction == T_MNEM_stmia - ? inst.operands[0].writeback - : (inst.operands[0].writeback - == !(inst.operands[1].imm & mask)))) + if (inst.operands[0].reg <= 7) { if (inst.instruction == T_MNEM_stmia - && (inst.operands[1].imm & mask) - && (inst.operands[1].imm & (mask - 1))) - as_warn (_("value stored for r%d is UNPREDICTABLE"), - inst.operands[0].reg); + ? inst.operands[0].writeback + : (inst.operands[0].writeback + == !(inst.operands[1].imm & mask))) + { + if (inst.instruction == T_MNEM_stmia + && (inst.operands[1].imm & mask) + && (inst.operands[1].imm & (mask - 1))) + as_warn (_("value stored for r%d is UNKNOWN"), + inst.operands[0].reg); - inst.instruction = THUMB_OP16 (inst.instruction); - inst.instruction |= inst.operands[0].reg << 8; - inst.instruction |= inst.operands[1].imm; - narrow = TRUE; + inst.instruction = THUMB_OP16 (inst.instruction); + inst.instruction |= inst.operands[0].reg << 8; + inst.instruction |= inst.operands[1].imm; + narrow = TRUE; + } + else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0) + { + /* This means 1 register in reg list one of 3 situations: + 1. Instruction is stmia, but without writeback. + 2. lmdia without writeback, but with Rn not in + reglist. + 3. ldmia with writeback, but with Rn in reglist. + Case 3 is UNPREDICTABLE behaviour, so we handle + case 1 and 2 which can be converted into a 16-bit + str or ldr. The SP cases are handled below. */ + unsigned long opcode; + /* First, record an error for Case 3. */ + if (inst.operands[1].imm & mask + && inst.operands[0].writeback) + inst.error = + _("having the base register in the register list when " + "using write back is UNPREDICTABLE"); + + opcode = (inst.instruction == T_MNEM_stmia ? T_MNEM_str + : T_MNEM_ldr); + inst.instruction = THUMB_OP16 (opcode); + inst.instruction |= inst.operands[0].reg << 3; + inst.instruction |= (ffs (inst.operands[1].imm)-1); + narrow = TRUE; + } } - else if (inst.operands[0] .reg == REG_SP - && inst.operands[0].writeback) + else if (inst.operands[0] .reg == REG_SP) { - inst.instruction = THUMB_OP16 (inst.instruction == T_MNEM_stmia - ? T_MNEM_push : T_MNEM_pop); - inst.instruction |= inst.operands[1].imm; - narrow = TRUE; + if (inst.operands[0].writeback) + { + inst.instruction = + THUMB_OP16 (inst.instruction == T_MNEM_stmia + ? T_MNEM_push : T_MNEM_pop); + inst.instruction |= inst.operands[1].imm; + narrow = TRUE; + } + else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0) + { + inst.instruction = + THUMB_OP16 (inst.instruction == T_MNEM_stmia + ? T_MNEM_str_sp : T_MNEM_ldr_sp); + inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8); + narrow = TRUE; + } } } @@ -9866,7 +10386,7 @@ do_t_ldmstm (void) as_warn (_("this instruction will write back the base register")); if ((inst.operands[1].imm & (1 << inst.operands[0].reg)) && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1))) - as_warn (_("value stored for r%d is UNPREDICTABLE"), + as_warn (_("value stored for r%d is UNKNOWN"), inst.operands[0].reg); } else @@ -9997,6 +10517,21 @@ do_t_ldst (void) } /* 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) @@ -10215,9 +10750,24 @@ do_t_mov_cmp (void) reject_bad_reg (Rn); reject_bad_reg (Rm); } - else if ((Rn == REG_SP || Rn == REG_PC) - && (Rm == REG_SP || Rm == REG_PC)) - reject_bad_reg (Rm); + else if (narrow) + { + /* This is mov.n. */ + if ((Rn == REG_SP || Rn == REG_PC) + && (Rm == REG_SP || Rm == REG_PC)) + { + as_warn (_("Use of r%u as a source register is " + "deprecated when r%u is the destination " + "register."), Rm, Rn); + } + } + else + { + /* This is mov.w. */ + constraint (Rn == REG_PC, BAD_PC); + constraint (Rm == REG_PC, BAD_PC); + constraint (Rn == REG_SP && Rm == REG_SP, BAD_SP); + } } else reject_bad_reg (Rn); @@ -10350,8 +10900,8 @@ do_t_mov_cmp (void) case T_MNEM_movs: /* We know we have low registers at this point. - Generate ADD Rd, Rs, #0. */ - inst.instruction = T_OPCODE_ADD_I3; + Generate LSLS Rd, Rs, #0. */ + inst.instruction = T_OPCODE_LSL_I; inst.instruction |= Rn; inst.instruction |= Rm << 3; break; @@ -10529,34 +11079,41 @@ static void do_t_mrs (void) { unsigned Rd; - int flags; if (do_vfp_nsyn_mrs () == SUCCESS) return; - flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); - if (flags == 0) + Rd = inst.operands[0].reg; + reject_bad_reg (Rd); + inst.instruction |= Rd << 8; + + if (inst.operands[1].isreg) { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m), - _("selected processor does not support " - "requested special purpose register")); + unsigned br = inst.operands[1].reg; + if (((br & 0x200) == 0) && ((br & 0xf000) != 0xf000)) + as_bad (_("bad register for mrs")); + + inst.instruction |= br & (0xf << 16); + inst.instruction |= (br & 0x300) >> 4; + inst.instruction |= (br & SPSR_BIT) >> 2; } 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")); - } + int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT); - Rd = inst.operands[0].reg; - reject_bad_reg (Rd); + if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m)) + constraint (flags != 0, _("selected processor does not support " + "requested special purpose register")); + else + /* 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 |= Rd << 8; - inst.instruction |= (flags & SPSR_BIT) >> 2; - inst.instruction |= inst.operands[1].imm & 0xff; + inst.instruction |= (flags & SPSR_BIT) >> 2; + inst.instruction |= inst.operands[1].imm & 0xff; + inst.instruction |= 0xf0000; + } } static void @@ -10570,26 +11127,33 @@ do_t_msr (void) constraint (!inst.operands[1].isreg, _("Thumb encoding does not support an immediate here")); - flags = inst.operands[0].imm; - if (flags & ~0xff) - { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1), - _("selected processor does not support " - "requested special purpose register")); - } + + if (inst.operands[0].isreg) + flags = (int)(inst.operands[0].reg); else + flags = inst.operands[0].imm; + + if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m)) { - constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_m), - _("selected processor does not support " - "requested special purpose register")); - flags |= PSR_f; + 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 ((flags & 0xff) != 0, _("selected processor does not support " + "requested special purpose register")); Rn = inst.operands[1].reg; reject_bad_reg (Rn); inst.instruction |= (flags & SPSR_BIT) >> 2; - inst.instruction |= (flags & ~SPSR_BIT) >> 8; + inst.instruction |= (flags & 0xf0000) >> 8; + inst.instruction |= (flags & 0x300) >> 4; inst.instruction |= (flags & 0xff); inst.instruction |= Rn << 16; } @@ -11049,6 +11613,10 @@ do_t_shift (void) 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 { @@ -11077,6 +11645,10 @@ do_t_shift (void) 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 { @@ -11116,6 +11688,10 @@ do_t_shift (void) 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 { @@ -11174,6 +11750,8 @@ static void do_t_smc (void) { unsigned int value = inst.reloc.exp.X_add_number; + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a), + _("SMC is not permitted on this architecture")); constraint (inst.reloc.exp.X_op != O_constant, _("expression too complex")); inst.reloc.type = BFD_RELOC_UNUSED; @@ -11182,6 +11760,16 @@ do_t_smc (void) inst.instruction |= (value & 0x000f) << 16; } +static void +do_t_hvc (void) +{ + unsigned int value = inst.reloc.exp.X_add_number; + + inst.reloc.type = BFD_RELOC_UNUSED; + inst.instruction |= (value & 0x0fff); + inst.instruction |= (value & 0xf000) << 4; +} + static void do_t_ssat_usat (int bias) { @@ -11267,8 +11855,7 @@ do_t_strexd (void) constraint (inst.operands[0].reg == inst.operands[1].reg || inst.operands[0].reg == inst.operands[2].reg - || inst.operands[0].reg == inst.operands[3].reg - || inst.operands[1].reg == inst.operands[2].reg, + || inst.operands[0].reg == inst.operands[3].reg, BAD_OVERLAP); inst.instruction |= inst.operands[0].reg; @@ -11335,6 +11922,17 @@ do_t_sxth (void) static void do_t_swi (void) { + /* We have to do the following check manually as ARM_EXT_OS only applies + to ARM_EXT_V6M. */ + if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6m)) + { + 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); + } + inst.reloc.type = BFD_RELOC_ARM_SWI; } @@ -11782,6 +12380,8 @@ neon_select_shape (enum neon_shape shape, ...) case SE_L: break; } + if (!matches) + break; } if (matches) break; @@ -14606,6 +15206,18 @@ do_neon_ldr_str (void) { int is_ldr = (inst.instruction & (1 << 20)) != 0; + /* Use of PC in vstr in ARM mode is deprecated in ARMv7. + And is UNPREDICTABLE in thumb mode. */ + if (!is_ldr + && inst.operands[1].reg == REG_PC + && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7)) + { + if (!thumb_mode && warn_on_deprecated) + as_warn (_("Use of PC here is deprecated")); + else + inst.error = _("Use of PC here is UNPREDICTABLE"); + } + if (inst.operands[0].issingle) { if (is_ldr) @@ -14654,12 +15266,13 @@ do_neon_ld_st_interleave (void) { case 64: alignbits = 1; break; case 128: - if (NEON_REGLIST_LENGTH (inst.operands[0].imm) == 3) + if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2 + && NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4) goto bad_alignment; alignbits = 2; break; case 256: - if (NEON_REGLIST_LENGTH (inst.operands[0].imm) == 3) + if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4) goto bad_alignment; alignbits = 3; break; @@ -14963,6 +15576,29 @@ fix_new_arm (fragS * frag, 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: @@ -15696,7 +16332,7 @@ md_assemble (char *str) || (thumb_mode == 1 && !ARM_CPU_HAS_FEATURE (variant, *opcode->tvariant))) { - as_bad (_("selected processor does not support `%s'"), str); + as_bad (_("selected processor does not support Thumb mode `%s'"), str); return; } if (inst.cond != COND_ALWAYS && !unified_syntax @@ -15721,7 +16357,7 @@ md_assemble (char *str) inst.size_req = 2; else if (inst.size_req == 4) { - as_bad (_("selected processor does not support `%s'"), str); + as_bad (_("selected processor does not support Thumb-2 mode `%s'"), str); return; } } @@ -15787,7 +16423,7 @@ md_assemble (char *str) && !(opcode->avariant && ARM_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant))) { - as_bad (_("selected processor does not support `%s'"), str); + as_bad (_("selected processor does not support ARM mode `%s'"), str); return; } if (inst.size_req) @@ -15967,6 +16603,13 @@ arm_canonicalize_symbol_name (char * name) REGNUM2(p, 4,t), REGNUM2(p, 5,t), REGNUM2(p, 6,t), REGNUM2(p, 7,t), \ REGNUM2(p, 8,t), REGNUM2(p, 9,t), REGNUM2(p,10,t), REGNUM2(p,11,t), \ REGNUM2(p,12,t), REGNUM2(p,13,t), REGNUM2(p,14,t), REGNUM2(p,15,t) +#define SPLRBANK(base,bank,t) \ + REGDEF(lr_##bank, 768|((base+0)<<16), t), \ + REGDEF(sp_##bank, 768|((base+1)<<16), t), \ + REGDEF(spsr_##bank, 768|(base<<16)|SPSR_BIT, t), \ + REGDEF(LR_##bank, 768|((base+0)<<16), t), \ + REGDEF(SP_##bank, 768|((base+1)<<16), t), \ + REGDEF(SPSR_##bank, 768|(base<<16)|SPSR_BIT, t) static const struct reg_entry reg_names[] = { @@ -15997,6 +16640,34 @@ static const struct reg_entry reg_names[] = REGSET(c, CN), REGSET(C, CN), REGSET(cr, CN), REGSET(CR, CN), + /* ARM banked registers. */ + REGDEF(R8_usr,512|(0<<16),RNB), REGDEF(r8_usr,512|(0<<16),RNB), + REGDEF(R9_usr,512|(1<<16),RNB), REGDEF(r9_usr,512|(1<<16),RNB), + REGDEF(R10_usr,512|(2<<16),RNB), REGDEF(r10_usr,512|(2<<16),RNB), + REGDEF(R11_usr,512|(3<<16),RNB), REGDEF(r11_usr,512|(3<<16),RNB), + REGDEF(R12_usr,512|(4<<16),RNB), REGDEF(r12_usr,512|(4<<16),RNB), + REGDEF(SP_usr,512|(5<<16),RNB), REGDEF(sp_usr,512|(5<<16),RNB), + REGDEF(LR_usr,512|(6<<16),RNB), REGDEF(lr_usr,512|(6<<16),RNB), + + REGDEF(R8_fiq,512|(8<<16),RNB), REGDEF(r8_fiq,512|(8<<16),RNB), + REGDEF(R9_fiq,512|(9<<16),RNB), REGDEF(r9_fiq,512|(9<<16),RNB), + REGDEF(R10_fiq,512|(10<<16),RNB), REGDEF(r10_fiq,512|(10<<16),RNB), + REGDEF(R11_fiq,512|(11<<16),RNB), REGDEF(r11_fiq,512|(11<<16),RNB), + REGDEF(R12_fiq,512|(12<<16),RNB), REGDEF(r12_fiq,512|(12<<16),RNB), + REGDEF(SP_fiq,512|(13<<16),RNB), REGDEF(SP_fiq,512|(13<<16),RNB), + REGDEF(LR_fiq,512|(14<<16),RNB), REGDEF(lr_fiq,512|(14<<16),RNB), + REGDEF(SPSR_fiq,512|(14<<16)|SPSR_BIT,RNB), REGDEF(spsr_fiq,512|(14<<16)|SPSR_BIT,RNB), + + SPLRBANK(0,IRQ,RNB), SPLRBANK(0,irq,RNB), + SPLRBANK(2,SVC,RNB), SPLRBANK(2,svc,RNB), + SPLRBANK(4,ABT,RNB), SPLRBANK(4,abt,RNB), + SPLRBANK(6,UND,RNB), SPLRBANK(6,und,RNB), + SPLRBANK(12,MON,RNB), SPLRBANK(12,mon,RNB), + REGDEF(elr_hyp,768|(14<<16),RNB), REGDEF(ELR_hyp,768|(14<<16),RNB), + REGDEF(sp_hyp,768|(15<<16),RNB), REGDEF(SP_hyp,768|(15<<16),RNB), + REGDEF(spsr_hyp,768|(14<<16)|SPSR_BIT,RNB), + REGDEF(SPSR_hyp,768|(14<<16)|SPSR_BIT,RNB), + /* FPA registers. */ REGNUM(f,0,FN), REGNUM(f,1,FN), REGNUM(f,2,FN), REGNUM(f,3,FN), REGNUM(f,4,FN), REGNUM(f,5,FN), REGNUM(f,6,FN), REGNUM(f,7, FN), @@ -16073,6 +16744,7 @@ static const struct asm_psr psrs[] = {"c", PSR_c}, {"x", PSR_x}, {"s", PSR_s}, + /* Combinations of flags. */ {"fs", PSR_f | PSR_s}, {"fx", PSR_f | PSR_x}, @@ -16152,6 +16824,7 @@ static const struct asm_psr v7m_psrs[] = {"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} }; @@ -16181,7 +16854,14 @@ static struct reloc_entry reloc_names[] = { "tlsldm", BFD_RELOC_ARM_TLS_LDM32}, { "TLSLDM", BFD_RELOC_ARM_TLS_LDM32}, { "tlsldo", BFD_RELOC_ARM_TLS_LDO32}, { "TLSLDO", BFD_RELOC_ARM_TLS_LDO32}, { "gottpoff",BFD_RELOC_ARM_TLS_IE32}, { "GOTTPOFF",BFD_RELOC_ARM_TLS_IE32}, - { "tpoff", BFD_RELOC_ARM_TLS_LE32}, { "TPOFF", BFD_RELOC_ARM_TLS_LE32} + { "tpoff", BFD_RELOC_ARM_TLS_LE32}, { "TPOFF", BFD_RELOC_ARM_TLS_LE32}, + { "got_prel", BFD_RELOC_ARM_GOT_PREL}, { "GOT_PREL", BFD_RELOC_ARM_GOT_PREL}, + { "tlsdesc", BFD_RELOC_ARM_TLS_GOTDESC}, + { "TLSDESC", BFD_RELOC_ARM_TLS_GOTDESC}, + { "tlscall", BFD_RELOC_ARM_TLS_CALL}, + { "TLSCALL", BFD_RELOC_ARM_TLS_CALL}, + { "tlsdescseq", BFD_RELOC_ARM_TLS_DESCSEQ}, + { "TLSDESCSEQ", BFD_RELOC_ARM_TLS_DESCSEQ} }; #endif @@ -16207,10 +16887,18 @@ static const struct asm_cond conds[] = static struct asm_barrier_opt barrier_opt_names[] = { - { "sy", 0xf }, - { "un", 0x7 }, - { "st", 0xe }, - { "unst", 0x6 } + { "sy", 0xf }, { "SY", 0xf }, + { "un", 0x7 }, { "UN", 0x7 }, + { "st", 0xe }, { "ST", 0xe }, + { "unst", 0x6 }, { "UNST", 0x6 }, + { "ish", 0xb }, { "ISH", 0xb }, + { "sh", 0xb }, { "SH", 0xb }, + { "ishst", 0xa }, { "ISHST", 0xa }, + { "shst", 0xa }, { "SHST", 0xa }, + { "nsh", 0x7 }, { "NSH", 0x7 }, + { "nshst", 0x6 }, { "NSHST", 0x6 }, + { "osh", 0x3 }, { "OSH", 0x3 }, + { "oshst", 0x2 }, { "OSHST", 0x2 } }; /* Table of ARM-format instructions. */ @@ -16415,9 +17103,6 @@ static struct asm_barrier_opt barrier_opt_names[] = #define do_0 0 -/* Thumb-only, unconditional. */ -#define UT(mnem, op, nops, ops, te) TUE (mnem, 0, op, nops, ops, 0, te) - static const struct asm_opcode insns[] = { #define ARM_VARIANT &arm_ext_v1 /* Core ARM Instructions. */ @@ -16557,7 +17242,7 @@ static const struct asm_opcode insns[] = TCE("stc", c000000, ec000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), TC3("stcl", c400000, ec400000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc), TCE("mcr", e000010, ee000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), - TCE("mrc", e100010, ee100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg), + TCE("mrc", e100010, ee100010, 6, (RCP, I7b, APSR_RR, RCN, RCN, oI7b), co_reg, co_reg), #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v2s /* ARM 3 - swp instructions. */ @@ -16570,8 +17255,8 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_msr - TCE("mrs", 10f0000, f3ef8000, 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. */ @@ -16846,18 +17531,30 @@ static const struct asm_opcode insns[] = 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 -#define ARM_VARIANT & arm_ext_v6z +#define ARM_VARIANT & arm_ext_sec +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_sec TCE("smc", 1600070, f7f08000, 1, (EXPi), smc, t_smc), +#undef ARM_VARIANT +#define ARM_VARIANT & arm_ext_virt +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_virt + + TCE("hvc", 1400070, f7e08000, 1, (EXPi), hvc, t_hvc), + TCE("eret", 160006e, f3de8f00, 0, (), noargs, noargs), + #undef ARM_VARIANT #define ARM_VARIANT & arm_ext_v6t2 +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_v6t2 TCE("bfc", 7c0001f, f36f0000, 3, (RRnpc, I31, I32), bfc, t_bfc), TCE("bfi", 7c00010, f3600000, 4, (RRnpc, RRnpc_I0, I31, I32), bfi, t_bfi), @@ -16874,8 +17571,11 @@ static const struct asm_opcode insns[] = TC3("ldrsbt", 03000d0, f9100e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt), TC3("strht", 02000b0, f8200e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt), - UT("cbnz", b900, 2, (RR, EXP), t_cbz), - UT("cbz", b100, 2, (RR, EXP), t_cbz), + /* Thumb-only instructions. */ +#undef ARM_VARIANT +#define ARM_VARIANT NULL + TUE("cbnz", 0, b900, 2, (RR, EXP), 0, t_cbz), + TUE("cbz", 0, b100, 2, (RR, EXP), 0, t_cbz), /* ARM does not really have an IT instruction, so always allow it. The opcode is copied from Thumb in order to allow warnings in @@ -16913,12 +17613,14 @@ static const struct asm_opcode insns[] = TCE("tbb", 0, e8d0f000, 1, (TB), 0, t_tb), TCE("tbh", 0, e8d0f010, 1, (TB), 0, t_tb), - /* Thumb-2 hardware division instructions (R and M profiles only). */ + /* Hardware division instructions. */ +#undef ARM_VARIANT +#define ARM_VARIANT & arm_ext_adiv #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_div - TCE("sdiv", 0, fb90f0f0, 3, (RR, oRR, RR), 0, t_div), - TCE("udiv", 0, fbb0f0f0, 3, (RR, oRR, RR), 0, t_div), + TCE("sdiv", 710f010, fb90f0f0, 3, (RR, oRR, RR), div, t_div), + TCE("udiv", 730f010, fbb0f0f0, 3, (RR, oRR, RR), div, t_div), /* ARM V6M/V7 instructions. */ #undef ARM_VARIANT @@ -16926,9 +17628,9 @@ static const struct asm_opcode insns[] = #undef THUMB_VARIANT #define THUMB_VARIANT & arm_ext_barrier - TUF("dmb", 57ff050, f3bf8f50, 1, (oBARRIER), barrier, t_barrier), - TUF("dsb", 57ff040, f3bf8f40, 1, (oBARRIER), barrier, t_barrier), - TUF("isb", 57ff060, f3bf8f60, 1, (oBARRIER), barrier, t_barrier), + TUF("dmb", 57ff050, f3bf8f50, 1, (oBARRIER_I15), barrier, t_barrier), + TUF("dsb", 57ff040, f3bf8f40, 1, (oBARRIER_I15), barrier, t_barrier), + TUF("isb", 57ff060, f3bf8f60, 1, (oBARRIER_I15), barrier, t_barrier), /* ARM V7 instructions. */ #undef ARM_VARIANT @@ -16939,6 +17641,13 @@ static const struct asm_opcode insns[] = TUF("pli", 450f000, f910f000, 1, (ADDR), pli, t_pld), TCE("dbg", 320f0f0, f3af80f0, 1, (I15), dbg, t_dbg), +#undef ARM_VARIANT +#define ARM_VARIANT & arm_ext_mp +#undef THUMB_VARIANT +#define THUMB_VARIANT & arm_ext_mp + + TUF("pldw", 410f000, f830f000, 1, (ADDR), pld, t_pld), + #undef ARM_VARIANT #define ARM_VARIANT & fpu_fpa_ext_v1 /* Core FPA instruction set (V1). */ @@ -17404,22 +18113,22 @@ static const struct asm_opcode insns[] = /* Memory operations. */ cCE("flds", d100a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst), cCE("fsts", d000a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst), - cCE("fldmias", c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fldmfds", c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fldmdbs", d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fldmeas", d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fldmiax", c900b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fldmfdx", c900b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fldmdbx", d300b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), - cCE("fldmeax", d300b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), - cCE("fstmias", c800a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fstmeas", c800a00, 2, (RRw, VRSLST), vfp_sp_ldstmia), - cCE("fstmdbs", d200a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fstmfds", d200a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb), - cCE("fstmiax", c800b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fstmeax", c800b00, 2, (RRw, VRDLST), vfp_xp_ldstmia), - cCE("fstmdbx", d200b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), - cCE("fstmfdx", d200b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb), + cCE("fldmias", c900a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fldmfds", c900a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fldmdbs", d300a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fldmeas", d300a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fldmiax", c900b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fldmfdx", c900b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fldmdbx", d300b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), + cCE("fldmeax", d300b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), + cCE("fstmias", c800a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fstmeas", c800a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmia), + cCE("fstmdbs", d200a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fstmfds", d200a00, 2, (RRnpctw, VRSLST), vfp_sp_ldstmdb), + cCE("fstmiax", c800b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fstmeax", c800b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmia), + cCE("fstmdbx", d200b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), + cCE("fstmfdx", d200b00, 2, (RRnpctw, VRDLST), vfp_xp_ldstmdb), /* Monadic operations. */ cCE("fabss", eb00ac0, 2, (RVS, RVS), vfp_sp_monadic), @@ -17447,14 +18156,14 @@ static const struct asm_opcode insns[] = implementations. */ cCE("fldd", d100b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), cCE("fstd", d000b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst), - cCE("fldmiad", c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fldmfdd", c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fldmdbd", d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), - cCE("fldmead", d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), - cCE("fstmiad", c800b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fstmead", c800b00, 2, (RRw, VRDLST), vfp_dp_ldstmia), - cCE("fstmdbd", d200b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), - cCE("fstmfdd", d200b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb), + cCE("fldmiad", c900b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fldmfdd", c900b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fldmdbd", d300b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), + cCE("fldmead", d300b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), + cCE("fstmiad", c800b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fstmead", c800b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmia), + cCE("fstmdbd", d200b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), + cCE("fstmfdd", d200b00, 2, (RRnpctw, VRDLST), vfp_dp_ldstmdb), #undef ARM_VARIANT #define ARM_VARIANT & fpu_vfp_ext_v1 /* VFP V1 (Double precision). */ @@ -17534,16 +18243,16 @@ static const struct asm_opcode insns[] = NCEF(vabs, 1b10300, 2, (RNSDQ, RNSDQ), neon_abs_neg), NCEF(vneg, 1b10380, 2, (RNSDQ, RNSDQ), neon_abs_neg), - NCE(vldm, c900b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vldmia, c900b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vldmdb, d100b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vstm, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vstmia, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm), - NCE(vstmdb, d000b00, 2, (RRw, VRSDLST), neon_ldm_stm), + NCE(vldm, c900b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vldmia, c900b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vldmdb, d100b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vstm, c800b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vstmia, c800b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), + NCE(vstmdb, d000b00, 2, (RRnpctw, VRSDLST), neon_ldm_stm), 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), @@ -18506,7 +19215,8 @@ relax_adr (fragS *fragp, asection *sec, long stretch) /* Assume worst case for symbols not known to be in the same section. */ if (fragp->fr_symbol == NULL || !S_IS_DEFINED (fragp->fr_symbol) - || sec != S_GET_SEGMENT (fragp->fr_symbol)) + || sec != S_GET_SEGMENT (fragp->fr_symbol) + || S_IS_WEAK (fragp->fr_symbol)) return 4; val = relaxed_symbol_addr (fragp, stretch); @@ -18549,13 +19259,20 @@ relax_branch (fragS *fragp, asection *sec, int bits, long stretch) /* Assume worst case for symbols not known to be in the same section. */ if (!S_IS_DEFINED (fragp->fr_symbol) - || sec != S_GET_SEGMENT (fragp->fr_symbol)) + || sec != S_GET_SEGMENT (fragp->fr_symbol) + || S_IS_WEAK (fragp->fr_symbol)) return 4; #ifdef OBJ_ELF 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); @@ -19339,7 +20056,7 @@ md_pcrel_from_section (fixS * fixP, segT seg) case BFD_RELOC_THUMB_PCREL_BRANCH23: if (fixP->fx_addsy && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && (!S_IS_EXTERNAL (fixP->fx_addsy)) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; @@ -19350,7 +20067,7 @@ md_pcrel_from_section (fixS * fixP, segT seg) case BFD_RELOC_THUMB_PCREL_BLX: if (fixP->fx_addsy && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && (!S_IS_EXTERNAL (fixP->fx_addsy)) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && THUMB_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; @@ -19361,7 +20078,7 @@ md_pcrel_from_section (fixS * fixP, segT seg) case BFD_RELOC_ARM_PCREL_BLX: if (fixP->fx_addsy && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && (!S_IS_EXTERNAL (fixP->fx_addsy)) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; @@ -19370,7 +20087,7 @@ md_pcrel_from_section (fixS * fixP, segT seg) case BFD_RELOC_ARM_PCREL_CALL: if (fixP->fx_addsy && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && (!S_IS_EXTERNAL (fixP->fx_addsy)) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && THUMB_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) base = fixP->fx_where + fixP->fx_frag->fr_address; @@ -19760,22 +20477,23 @@ md_apply_fix (fixS * fixP, not have a reloc for it, so tc_gen_reloc will reject it. */ fixP->fx_done = 1; - if (fixP->fx_addsy - && ! S_IS_DEFINED (fixP->fx_addsy)) + if (fixP->fx_addsy) { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("undefined symbol %s used as an immediate value"), - S_GET_NAME (fixP->fx_addsy)); - break; - } + const char *msg = 0; - if (fixP->fx_addsy - && S_GET_SEGMENT (fixP->fx_addsy) != seg) - { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("symbol %s is in a different section"), - S_GET_NAME (fixP->fx_addsy)); - break; + if (! S_IS_DEFINED (fixP->fx_addsy)) + msg = _("undefined symbol %s used as an immediate value"); + else if (S_GET_SEGMENT (fixP->fx_addsy) != seg) + msg = _("symbol %s is in a different section"); + else if (S_IS_WEAK (fixP->fx_addsy)) + msg = _("symbol %s is weak and may be overridden later"); + + if (msg) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + msg, S_GET_NAME (fixP->fx_addsy)); + break; + } } newimm = encode_arm_immediate (value); @@ -19801,24 +20519,25 @@ md_apply_fix (fixS * fixP, unsigned int highpart = 0; unsigned int newinsn = 0xe1a00000; /* nop. */ - if (fixP->fx_addsy - && ! S_IS_DEFINED (fixP->fx_addsy)) + if (fixP->fx_addsy) { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("undefined symbol %s used as an immediate value"), - S_GET_NAME (fixP->fx_addsy)); - break; - } + const char *msg = 0; - if (fixP->fx_addsy - && S_GET_SEGMENT (fixP->fx_addsy) != seg) - { - as_bad_where (fixP->fx_file, fixP->fx_line, - _("symbol %s is in a different section"), - S_GET_NAME (fixP->fx_addsy)); - break; - } + if (! S_IS_DEFINED (fixP->fx_addsy)) + msg = _("undefined symbol %s used as an immediate value"); + else if (S_GET_SEGMENT (fixP->fx_addsy) != seg) + msg = _("symbol %s is in a different section"); + else if (S_IS_WEAK (fixP->fx_addsy)) + msg = _("symbol %s is weak and may be overridden later"); + if (msg) + { + as_bad_where (fixP->fx_file, fixP->fx_line, + msg, S_GET_NAME (fixP->fx_addsy)); + break; + } + } + newimm = encode_arm_immediate (value); temp = md_chars_to_number (buf, INSN_SIZE); @@ -19868,7 +20587,7 @@ md_apply_fix (fixS * fixP, value = 0; case BFD_RELOC_ARM_LITERAL: - sign = value >= 0; + sign = value > 0; if (value < 0) value = - value; @@ -19886,14 +20605,19 @@ md_apply_fix (fixS * fixP, } 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; @@ -19910,8 +20634,13 @@ md_apply_fix (fixS * fixP, } 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; @@ -20098,17 +20827,20 @@ md_apply_fix (fixS * fixP, /* Turn add/sum into addw/subw. */ if (fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM) newval = (newval & 0xfeffffff) | 0x02000000; - - /* 12 bit immediate for addw/subw. */ - if (value < 0) + /* No flat 12-bit imm encoding for addsw/subsw. */ + if ((newval & 0x00100000) == 0) { - value = -value; - newval ^= 0x00a00000; + /* 12 bit immediate for addw/subw. */ + if (value < 0) + { + value = -value; + newval ^= 0x00a00000; + } + if (value > 0xfff) + newimm = (unsigned int) FAIL; + else + newimm = value; } - if (value > 0xfff) - newimm = (unsigned int) FAIL; - else - newimm = value; } if (newimm == (unsigned int)FAIL) @@ -20136,6 +20868,15 @@ md_apply_fix (fixS * fixP, md_number_to_chars (buf, newval, INSN_SIZE); break; + case BFD_RELOC_ARM_HVC: + if (((unsigned long) value) > 0xffff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("invalid hvc expression")); + newval = md_chars_to_number (buf, INSN_SIZE); + newval |= (value & 0xf) | ((value & 0xfff0) << 4); + md_number_to_chars (buf, newval, INSN_SIZE); + break; + case BFD_RELOC_ARM_SWI: if (fixP->tc_fix_data != 0) { @@ -20170,7 +20911,7 @@ md_apply_fix (fixS * fixP, if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) && fixP->fx_addsy - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && THUMB_IS_FUNC (fixP->fx_addsy)) /* Flip the bl to blx. This is a simple flip @@ -20190,7 +20931,7 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_ARM_PCREL_JUMP: if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) && fixP->fx_addsy - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && THUMB_IS_FUNC (fixP->fx_addsy)) { @@ -20213,7 +20954,7 @@ md_apply_fix (fixS * fixP, temp = 1; if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) && fixP->fx_addsy - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && ARM_IS_FUNC (fixP->fx_addsy)) { @@ -20243,8 +20984,7 @@ md_apply_fix (fixS * fixP, _("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) { @@ -20280,8 +21020,7 @@ md_apply_fix (fixS * fixP, 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) { @@ -20294,8 +21033,7 @@ md_apply_fix (fixS * fixP, 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) { @@ -20307,8 +21045,7 @@ md_apply_fix (fixS * fixP, 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) { @@ -20321,15 +21058,14 @@ md_apply_fix (fixS * fixP, case BFD_RELOC_THUMB_PCREL_BRANCH20: if (fixP->fx_addsy && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && !S_IS_EXTERNAL (fixP->fx_addsy) - && S_IS_DEFINED (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) { /* 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")); @@ -20354,14 +21090,12 @@ md_apply_fix (fixS * fixP, 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. */ if (fixP->fx_addsy - && S_IS_DEFINED (fixP->fx_addsy) - && !S_IS_EXTERNAL (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && (S_GET_SEGMENT (fixP->fx_addsy) == seg) && THUMB_IS_FUNC (fixP->fx_addsy)) { @@ -20380,13 +21114,11 @@ md_apply_fix (fixS * fixP, 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 && (S_GET_SEGMENT (fixP->fx_addsy) == seg) - && !S_IS_EXTERNAL (fixP->fx_addsy) - && S_IS_DEFINED (fixP->fx_addsy) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE) && ARM_IS_FUNC (fixP->fx_addsy) && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)) { @@ -20412,21 +21144,15 @@ md_apply_fix (fixS * fixP, 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); @@ -20434,9 +21160,8 @@ md_apply_fix (fixS * fixP, 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); @@ -20454,6 +21179,14 @@ md_apply_fix (fixS * fixP, break; #ifdef OBJ_ELF + case BFD_RELOC_ARM_TLS_CALL: + case BFD_RELOC_ARM_THM_TLS_CALL: + case BFD_RELOC_ARM_TLS_DESCSEQ: + case BFD_RELOC_ARM_THM_TLS_DESCSEQ: + S_SET_THREAD_LOCAL (fixP->fx_addsy); + break; + + case BFD_RELOC_ARM_TLS_GOTDESC: case BFD_RELOC_ARM_TLS_GD32: case BFD_RELOC_ARM_TLS_LE32: case BFD_RELOC_ARM_TLS_IE32: @@ -20467,7 +21200,12 @@ md_apply_fix (fixS * fixP, if (fixP->fx_done || !seg->use_rela_p) md_number_to_chars (buf, 0, 4); break; - + + case BFD_RELOC_ARM_GOT_PREL: + if (fixP->fx_done || !seg->use_rela_p) + md_number_to_chars (buf, value, 4); + break; + case BFD_RELOC_ARM_TARGET2: /* TARGET2 is not partial-inplace, so we need to write the addend here for REL targets, because it won't be written out @@ -20516,7 +21254,7 @@ md_apply_fix (fixS * fixP, 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 @@ -20524,8 +21262,13 @@ md_apply_fix (fixS * fixP, 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); @@ -21050,8 +21793,13 @@ tc_gen_reloc (asection *section, fixS *fixp) return NULL; #ifdef OBJ_ELF + case BFD_RELOC_ARM_TLS_CALL: + case BFD_RELOC_ARM_THM_TLS_CALL: + case BFD_RELOC_ARM_TLS_DESCSEQ: + case BFD_RELOC_ARM_THM_TLS_DESCSEQ: case BFD_RELOC_ARM_GOT32: case BFD_RELOC_ARM_GOTOFF: + case BFD_RELOC_ARM_GOT_PREL: case BFD_RELOC_ARM_PLT32: case BFD_RELOC_ARM_TARGET1: case BFD_RELOC_ARM_ROSEGREL32: @@ -21094,6 +21842,7 @@ tc_gen_reloc (asection *section, fixS *fixp) code = fixp->fx_r_type; break; + case BFD_RELOC_ARM_TLS_GOTDESC: case BFD_RELOC_ARM_TLS_GD32: case BFD_RELOC_ARM_TLS_IE32: case BFD_RELOC_ARM_TLS_LDM32: @@ -21149,6 +21898,7 @@ tc_gen_reloc (asection *section, fixS *fixp) case BFD_RELOC_ARM_SWI: type = "SWI"; break; case BFD_RELOC_ARM_MULTI: type = "MULTI"; break; case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break; + case BFD_RELOC_ARM_T32_OFFSET_IMM: type = "T32_OFFSET_IMM"; break; case BFD_RELOC_ARM_T32_CP_OFF_IMM: type = "T32_CP_OFF_IMM"; break; case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break; case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break; @@ -21287,14 +22037,25 @@ arm_force_relocation (struct fix * fixp) } #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. */ @@ -21354,6 +22115,11 @@ arm_fix_adjustable (fixS * fixP) || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32 || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32 || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32 + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GOTDESC + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_CALL + || fixP->fx_r_type == BFD_RELOC_ARM_THM_TLS_CALL + || fixP->fx_r_type == BFD_RELOC_ARM_TLS_DESCSEQ + || fixP->fx_r_type == BFD_RELOC_ARM_THM_TLS_DESCSEQ || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2) return FALSE; @@ -21561,8 +22327,8 @@ arm_adjust_symtab (void) /* If it's a .thumb_func, declare it as so, otherwise tag label as .code 16. */ if (THUMB_IS_FUNC (sym)) - elf_sym->internal_elf_sym.st_info = - ELF_ST_INFO (bind, STT_ARM_TFUNC); + elf_sym->internal_elf_sym.st_target_internal + = ST_BRANCH_TO_THUMB; else if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4) elf_sym->internal_elf_sym.st_info = ELF_ST_INFO (bind, STT_ARM_16BIT); @@ -21572,6 +22338,8 @@ arm_adjust_symtab (void) /* Remove any overlapping mapping symbols generated by alignment frags. */ bfd_map_over_sections (stdoutput, check_mapping_symbols, (char *) 0); + /* Now do generic ELF adjustments. */ + elf_adjust_symtab (); #endif } @@ -22148,30 +22916,43 @@ static const struct arm_cpu_option_table arm_cpus[] = {"arm1022e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, {"arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM1026EJ-S"}, {"arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL}, - {"fa626te", ARM_ARCH_V5TE, FPU_NONE, NULL}, + {"fa606te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, + {"fa616te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, + {"fa626te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, + {"fmp626", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, {"fa726te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, {"arm1136js", ARM_ARCH_V6, FPU_NONE, "ARM1136J-S"}, {"arm1136j-s", ARM_ARCH_V6, FPU_NONE, NULL}, {"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2, "ARM1136JF-S"}, {"arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2, NULL}, - {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, NULL}, - {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, NULL}, + {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, "MPCore"}, + {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, "MPCore"}, {"arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE, NULL}, {"arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL}, {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL}, {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL}, - {"cortex-a5", ARM_ARCH_V7A, FPU_NONE, NULL}, - {"cortex-a8", ARM_ARCH_V7A, ARM_FEATURE (0, FPU_VFP_V3 + {"cortex-a5", ARM_ARCH_V7A_MP_SEC, + FPU_NONE, "Cortex-A5"}, + {"cortex-a8", ARM_ARCH_V7A_SEC, + ARM_FEATURE (0, FPU_VFP_V3 | FPU_NEON_EXT_V1), - NULL}, - {"cortex-a9", ARM_ARCH_V7A, ARM_FEATURE (0, FPU_VFP_V3 + "Cortex-A8"}, + {"cortex-a9", ARM_ARCH_V7A_MP_SEC, + ARM_FEATURE (0, FPU_VFP_V3 | FPU_NEON_EXT_V1), - NULL}, - {"cortex-r4", ARM_ARCH_V7R, FPU_NONE, NULL}, - {"cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16, NULL}, - {"cortex-m3", ARM_ARCH_V7M, FPU_NONE, NULL}, - {"cortex-m1", ARM_ARCH_V6M, FPU_NONE, NULL}, - {"cortex-m0", ARM_ARCH_V6M, FPU_NONE, NULL}, + "Cortex-A9"}, + {"cortex-a15", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT, + FPU_ARCH_NEON_VFP_V4, + "Cortex-A15"}, + {"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"}, + {"cortex-m0", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0"}, /* ??? XSCALE is really an architecture. */ {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL}, /* ??? iwmmxt is not a processor. */ @@ -22221,6 +23002,7 @@ static const struct arm_arch_option_table arm_archs[] = {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP}, {"armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP}, {"armv6-m", ARM_ARCH_V6M, FPU_ARCH_VFP}, + {"armv6s-m", ARM_ARCH_V6SM, FPU_ARCH_VFP}, {"armv7", ARM_ARCH_V7, FPU_ARCH_VFP}, /* The official spelling of the ARMv7 profile variants is the dashed form. Accept the non-dashed form for compatibility with old toolchains. */ @@ -22237,25 +23019,45 @@ static const struct arm_arch_option_table arm_archs[] = {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE} }; -/* ISA extensions in the co-processor space. */ -struct arm_option_cpu_value_table +/* ISA extensions in the co-processor and main instruction set space. */ +struct arm_option_extension_value_table { char *name; const arm_feature_set value; + const arm_feature_set allowed_archs; }; -static const struct arm_option_cpu_value_table arm_extensions[] = +/* The following table must be in alphabetical order with a NULL last entry. + */ +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 | 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}, + {"mp", ARM_FEATURE (ARM_EXT_MP, 0), + ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)}, + {"os", ARM_FEATURE (ARM_EXT_OS, 0), + ARM_FEATURE (ARM_EXT_V6M, 0)}, + {"sec", ARM_FEATURE (ARM_EXT_SEC, 0), + ARM_FEATURE (ARM_EXT_V6K | ARM_EXT_V7A, 0)}, + {"virt", ARM_FEATURE (ARM_EXT_VIRT | ARM_EXT_ADIV | ARM_EXT_DIV, 0), + ARM_FEATURE (ARM_EXT_V7A, 0)}, + {"xscale", ARM_FEATURE (0, ARM_CEXT_XSCALE), ARM_ANY}, + {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE} +}; + +/* ISA floating-point and Advanced SIMD extensions. */ +struct arm_option_fpu_value_table { - {"maverick", ARM_FEATURE (0, ARM_CEXT_MAVERICK)}, - {"xscale", ARM_FEATURE (0, ARM_CEXT_XSCALE)}, - {"iwmmxt", ARM_FEATURE (0, ARM_CEXT_IWMMXT)}, - {"iwmmxt2", ARM_FEATURE (0, ARM_CEXT_IWMMXT2)}, - {NULL, ARM_ARCH_NONE} + char *name; + const arm_feature_set value; }; /* This list should, at a minimum, contain all the fpu names recognized by GCC. */ -static const struct arm_option_cpu_value_table arm_fpus[] = +static const struct arm_option_fpu_value_table arm_fpus[] = { {"softfpa", FPU_NONE}, {"fpe", FPU_ARCH_FPE}, @@ -22333,15 +23135,23 @@ arm_parse_extension (char * str, const arm_feature_set **opt_p) arm_feature_set *ext_set = (arm_feature_set *) xmalloc (sizeof (arm_feature_set)); + /* We insist on extensions being specified in alphabetical order, and with + extensions being added before being removed. We achieve this by having + the global ARM_EXTENSIONS table in alphabetical order, and using the + ADDING_VALUE variable to indicate whether we are adding an extension (1) + or removing it (0) and only allowing it to change in the order + -1 -> 1 -> 0. */ + const struct arm_option_extension_value_table * opt = NULL; + int adding_value = -1; + /* Copy the feature set, so that we can modify it. */ *ext_set = **opt_p; *opt_p = ext_set; while (str != NULL && *str != 0) { - const struct arm_option_cpu_value_table * opt; char * ext; - int optlen; + size_t optlen; if (*str != '+') { @@ -22357,24 +23167,86 @@ arm_parse_extension (char * str, const arm_feature_set **opt_p) else optlen = strlen (str); + if (optlen >= 2 + && strncmp (str, "no", 2) == 0) + { + if (adding_value != 0) + { + adding_value = 0; + opt = arm_extensions; + } + + optlen -= 2; + str += 2; + } + else if (optlen > 0) + { + if (adding_value == -1) + { + adding_value = 1; + opt = arm_extensions; + } + else if (adding_value != 1) + { + as_bad (_("must specify extensions to add before specifying " + "those to remove")); + return FALSE; + } + } + if (optlen == 0) { as_bad (_("missing architectural extension")); return FALSE; } - for (opt = arm_extensions; opt->name != NULL; opt++) - if (strncmp (opt->name, str, optlen) == 0) + gas_assert (adding_value != -1); + gas_assert (opt != NULL); + + /* Scan over the options table trying to find an exact match. */ + for (; opt->name != NULL; opt++) + if (strncmp (opt->name, str, optlen) == 0 + && strlen (opt->name) == optlen) { - ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value); + /* Check we can apply the extension to this architecture. */ + if (!ARM_CPU_HAS_FEATURE (*ext_set, opt->allowed_archs)) + { + as_bad (_("extension does not apply to the base architecture")); + return FALSE; + } + + /* Add or remove the extension. */ + if (adding_value) + ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value); + else + ARM_CLEAR_FEATURE (*ext_set, *ext_set, opt->value); + break; } if (opt->name == NULL) { - as_bad (_("unknown architectural extension `%s'"), str); + /* Did we fail to find an extension because it wasn't specified in + alphabetical order, or because it does not exist? */ + + for (opt = arm_extensions; opt->name != NULL; opt++) + if (strncmp (opt->name, str, optlen) == 0) + break; + + if (opt->name == NULL) + as_bad (_("unknown architectural extension `%s'"), str); + else + as_bad (_("architectural extensions must be specified in " + "alphabetical order")); + return FALSE; } + else + { + /* We should skip the extension we've just matched the next time + round. */ + opt++; + } str = ext; }; @@ -22445,7 +23317,7 @@ arm_parse_arch (char * str) } for (opt = arm_archs; opt->name != NULL; opt++) - if (streq (opt->name, str)) + if (strncmp (opt->name, str, optlen) == 0) { march_cpu_opt = &opt->value; march_fpu_opt = &opt->default_fpu; @@ -22464,7 +23336,7 @@ arm_parse_arch (char * str) static bfd_boolean arm_parse_fpu (char * str) { - const struct arm_option_cpu_value_table * opt; + const struct arm_option_fpu_value_table * opt; for (opt = arm_fpus; opt->name != NULL; opt++) if (streq (opt->name, str)) @@ -22692,9 +23564,10 @@ static const cpu_arch_ver_table cpu_arch_ver[] = {4, ARM_ARCH_V5TE}, {5, ARM_ARCH_V5TEJ}, {6, ARM_ARCH_V6}, - {7, ARM_ARCH_V6Z}, {9, ARM_ARCH_V6K}, + {7, ARM_ARCH_V6Z}, {11, ARM_ARCH_V6M}, + {12, ARM_ARCH_V6SM}, {8, ARM_ARCH_V6T2}, {10, ARM_ARCH_V7A}, {10, ARM_ARCH_V7R}, @@ -22726,6 +23599,7 @@ static void aeabi_set_public_attributes (void) { int arch; + int virt_sec = 0; arm_feature_set flags; arm_feature_set tmp; const cpu_arch_ver_table *p; @@ -22742,6 +23616,12 @@ aeabi_set_public_attributes (void) ARM_MERGE_FEATURE_SETS (flags, flags, *object_arch); } + /* We need to make sure that the attributes do not identify us as v6S-M + when the only v6S-M feature in use is the Operating System Extensions. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_os)) + if (!ARM_CPU_HAS_FEATURE (flags, arm_arch_v6m_only)) + ARM_CLEAR_FEATURE (flags, flags, arm_ext_os); + tmp = flags; arch = 0; for (p = cpu_arch_ver; p->val; p++) @@ -22821,6 +23701,11 @@ aeabi_set_public_attributes (void) || ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd)) aeabi_set_attribute_int (Tag_VFP_arch, 1); + /* Tag_ABI_HardFP_use. */ + if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd) + && !ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1)) + aeabi_set_attribute_int (Tag_ABI_HardFP_use, 1); + /* Tag_WMMX_arch. */ if (ARM_CPU_HAS_FEATURE (flags, arm_cext_iwmmxt2)) aeabi_set_attribute_int (Tag_WMMX_arch, 2); @@ -22836,6 +23721,26 @@ aeabi_set_public_attributes (void) /* Tag_VFP_HP_extension (formerly Tag_NEON_FP16_arch). */ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_fp16)) aeabi_set_attribute_int (Tag_VFP_HP_extension, 1); + + /* Tag_DIV_use. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv)) + aeabi_set_attribute_int (Tag_DIV_use, 2); + else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_div)) + aeabi_set_attribute_int (Tag_DIV_use, 0); + else + aeabi_set_attribute_int (Tag_DIV_use, 1); + + /* Tag_MP_extension_use. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_mp)) + aeabi_set_attribute_int (Tag_MPextension_use, 1); + + /* Tag Virtualization_use. */ + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_sec)) + virt_sec |= 1; + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_virt)) + virt_sec |= 2; + if (virt_sec != 0) + aeabi_set_attribute_int (Tag_Virtualization_use, virt_sec); } /* Add the default contents for the .ARM.attributes section. */ @@ -22955,12 +23860,64 @@ s_arm_object_arch (int ignored ATTRIBUTE_UNUSED) ignore_rest_of_line (); } +/* Parse a .arch_extension directive. */ + +static void +s_arm_arch_extension (int ignored ATTRIBUTE_UNUSED) +{ + const struct arm_option_extension_value_table *opt; + char saved_char; + char *name; + int adding_value = 1; + + name = input_line_pointer; + while (*input_line_pointer && !ISSPACE (*input_line_pointer)) + input_line_pointer++; + saved_char = *input_line_pointer; + *input_line_pointer = 0; + + if (strlen (name) >= 2 + && strncmp (name, "no", 2) == 0) + { + adding_value = 0; + name += 2; + } + + for (opt = arm_extensions; opt->name != NULL; opt++) + if (streq (opt->name, name)) + { + if (!ARM_CPU_HAS_FEATURE (*mcpu_cpu_opt, opt->allowed_archs)) + { + as_bad (_("architectural extension `%s' is not allowed for the " + "current base architecture"), name); + break; + } + + if (adding_value) + ARM_MERGE_FEATURE_SETS (selected_cpu, selected_cpu, opt->value); + else + ARM_CLEAR_FEATURE (selected_cpu, selected_cpu, opt->value); + + mcpu_cpu_opt = &selected_cpu; + ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt); + *input_line_pointer = saved_char; + demand_empty_rest_of_line (); + return; + } + + if (opt->name == NULL) + as_bad (_("unknown architecture `%s'\n"), name); + + *input_line_pointer = saved_char; + ignore_rest_of_line (); +} + /* Parse a .fpu directive. */ static void s_arm_fpu (int ignored ATTRIBUTE_UNUSED) { - const struct arm_option_cpu_value_table *opt; + const struct arm_option_fpu_value_table *opt; char saved_char; char *name; @@ -23016,6 +23973,7 @@ arm_convert_symbolic_attribute (const char *name) T (Tag_CPU_arch_profile), T (Tag_ARM_ISA_use), T (Tag_THUMB_ISA_use), + T (Tag_FP_arch), T (Tag_VFP_arch), T (Tag_WMMX_arch), T (Tag_Advanced_SIMD_arch), @@ -23030,7 +23988,9 @@ arm_convert_symbolic_attribute (const char *name) T (Tag_ABI_FP_exceptions), T (Tag_ABI_FP_user_exceptions), T (Tag_ABI_FP_number_model), + T (Tag_ABI_align_needed), T (Tag_ABI_align8_needed), + T (Tag_ABI_align_preserved), T (Tag_ABI_align8_preserved), T (Tag_ABI_enum_size), T (Tag_ABI_HardFP_use), @@ -23040,6 +24000,7 @@ arm_convert_symbolic_attribute (const char *name) T (Tag_ABI_FP_optimization_goals), T (Tag_compatibility), T (Tag_CPU_unaligned_access), + T (Tag_FP_HP_extension), T (Tag_VFP_HP_extension), T (Tag_ABI_FP_16bit_format), T (Tag_MPextension_use), @@ -23073,7 +24034,7 @@ arm_apply_sym_value (struct fix * fixP) { if (fixP->fx_addsy && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t) - && !S_IS_EXTERNAL (fixP->fx_addsy)) + && !S_FORCE_RELOC (fixP->fx_addsy, TRUE)) { switch (fixP->fx_r_type) {