X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-arm.c;h=545b7ec7a39c9e8bdc0f92a4e98af251090dab6d;hb=6530b175a1051db81806d031b8ab2937744ff57b;hp=ce32aef54e077047427cadeb5b45127e22885569;hpb=95b75c0115a8dfef2afcefd3db9f191383b7376f;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index ce32aef54e..545b7ec7a3 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, 2010 + 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) Modified by David Taylor (dtaylor@armltd.co.uk) @@ -32,7 +32,7 @@ #include "safe-ctype.h" #include "subsegs.h" #include "obstack.h" - +#include "libiberty.h" #include "opcode/arm.h" #ifdef OBJ_ELF @@ -98,7 +98,7 @@ enum arm_float_abi /* Types of processor to assemble for. */ #ifndef CPU_DEFAULT /* The code that was here used to select a default CPU depending on compiler - pre-defines which were only present when doing native builds, thus + pre-defines which were only present when doing native builds, thus changing gas' default behaviour depending upon the build host. If you have a target that requires a default CPU option then the you @@ -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; @@ -336,6 +351,9 @@ enum it_instruction_type IT_INSN /* The IT insn has been parsed. */ }; +/* The maximum number of operands we need. */ +#define ARM_IT_MAX_OPERANDS 6 + struct arm_it { const char * error; @@ -387,7 +405,7 @@ struct arm_it unsigned negative : 1; /* Index register was negated. */ unsigned shifted : 1; /* Shift applied to operation. */ unsigned shift_kind : 3; /* Shift operation (enum shift_kind). */ - } operands[6]; + } operands[ARM_IT_MAX_OPERANDS]; }; static struct arm_it inst; @@ -502,6 +520,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 +530,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; @@ -544,6 +563,7 @@ const char * const reg_expected_msgs[] = }; /* Some well known registers that we refer to directly elsewhere. */ +#define REG_R12 12 #define REG_SP 13 #define REG_LR 14 #define REG_PC 15 @@ -602,6 +622,14 @@ struct asm_opcode #define T2_OPCODE_MASK 0xfe1fffff #define T2_DATA_OP_SHIFT 21 +#define A_COND_MASK 0xf0000000 +#define A_PUSH_POP_OP_MASK 0x0fff0000 + +/* Opcodes for pushing/poping registers to/from the stack. */ +#define A1_OPCODE_PUSH 0x092d0000 +#define A2_OPCODE_PUSH 0x052d0004 +#define A2_OPCODE_POP 0x049d0004 + /* Codes to distinguish the arithmetic instructions. */ #define OPCODE_AND 0 #define OPCODE_EOR 1 @@ -708,6 +736,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 +771,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; @@ -1465,6 +1497,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) { @@ -2030,6 +2066,7 @@ parse_neon_el_struct_list (char **str, unsigned *pbase, arm_reloc_hsh contains no entries, so this function can only succeed if there is no () after the word. Returns -1 on error, BFD_RELOC_UNUSED if there wasn't any suffix. */ + static int parse_reloc (char **str) { @@ -2058,7 +2095,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 +2244,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; @@ -2392,7 +2429,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 { @@ -2569,7 +2606,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) @@ -3036,6 +3090,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; } @@ -3133,8 +3195,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; @@ -3480,6 +3548,7 @@ s_arm_unwind_fnend (int ignored ATTRIBUTE_UNUSED) record_alignment (now_seg, 2); ptr = frag_more (8); + memset (ptr, 0, 8); where = frag_now_fix () - 8; /* Self relative offset of the function start. */ @@ -4264,12 +4333,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 @@ -4323,6 +4417,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 }, @@ -4343,6 +4438,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}, @@ -4412,7 +4508,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; @@ -4802,10 +4898,9 @@ parse_shifter_operand (char **str, int i) return FAIL; } - /* Convert to decoded value. md_apply_fix will put it back. */ - inst.reloc.exp.X_add_number - = (((inst.reloc.exp.X_add_number << (32 - value)) - | (inst.reloc.exp.X_add_number >> value)) & 0xffffffff); + /* Encode as specified. */ + inst.operands[i].imm = inst.reloc.exp.X_add_number | value << 7; + return SUCCESS; } inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE; @@ -4977,7 +5072,7 @@ parse_shifter_operand_group_reloc (char **str, int i) /* 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) @@ -5090,7 +5185,7 @@ parse_address_main (char **str, int i, int group_relocations, 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; } @@ -5153,8 +5248,24 @@ 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) @@ -5162,7 +5273,7 @@ parse_address_main (char **str, int i, int group_relocations, /* 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; } @@ -5228,6 +5339,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; @@ -5235,6 +5347,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; + } } } } @@ -5309,37 +5434,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. */ @@ -5350,23 +5523,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; - psr_field |= psr->field; + 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_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; @@ -5688,7 +5944,7 @@ parse_neon_mov (char **str, int *which_operand) inst.operands[i].reg = val; inst.operands[i].isreg = 1; - inst.operands[i++].present = 1; + inst.operands[i].present = 1; } } else if (parse_qfloat_immediate (&ptr, &inst.operands[i].imm) == SUCCESS) @@ -5778,7 +6034,7 @@ parse_neon_mov (char **str, int *which_operand) inst.operands[i].isvec = 1; inst.operands[i].issingle = 1; inst.operands[i].vectype = optype; - inst.operands[i++].present = 1; + inst.operands[i].present = 1; } } else @@ -5815,7 +6071,7 @@ 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, + 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 */ @@ -5892,11 +6148,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 */ @@ -5910,6 +6166,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 */ @@ -6239,6 +6496,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 */ @@ -6362,7 +6620,6 @@ 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_I15: po_barrier_or_imm (str); break; @@ -6371,13 +6628,19 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) 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); @@ -6525,8 +6788,8 @@ 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 + if (inst.operands[i].isreg + && inst.operands[i].reg == REG_PC && (inst.operands[i].writeback || thumb)) inst.error = BAD_PC; break; @@ -6534,8 +6797,8 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb) case OP_CPSF: case OP_ENDI: case OP_oROR: - case OP_PSR: - case OP_RVC_PSR: + case OP_wPSR: + case OP_rPSR: case OP_COND: case OP_oBARRIER_I15: case OP_REGLST: @@ -6768,7 +7031,11 @@ encode_arm_shifter_operand (int i) encode_arm_shift (i); } else - inst.instruction |= INST_IMMEDIATE; + { + inst.instruction |= INST_IMMEDIATE; + if (inst.reloc.type != BFD_RELOC_ARM_IMMEDIATE) + inst.instruction |= inst.operands[i].imm; + } } /* Subroutine of encode_arm_addr_mode_2 and encode_arm_addr_mode_3. */ @@ -6861,7 +7128,12 @@ encode_arm_addr_mode_2 (int i, bfd_boolean is_t) } 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; + } } } @@ -6897,7 +7169,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; + } } } @@ -6959,6 +7237,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; } @@ -7261,14 +7543,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; } @@ -7484,6 +7767,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) { @@ -7501,11 +7803,21 @@ do_it (void) } } +/* If there is only one register in the register list, + then return its register number. Otherwise return -1. */ +static int +only_one_reg_in_list (int range) +{ + int i = ffs (range) - 1; + return (i > 15 || range != (1 << i)) ? -1 : i; +} + static void -do_ldmstm (void) +encode_ldmstm(int from_push_pop_mnem) { int base_reg = inst.operands[0].reg; int range = inst.operands[1].imm; + int one_reg; inst.instruction |= base_reg << 16; inst.instruction |= range; @@ -7538,6 +7850,23 @@ do_ldmstm (void) as_warn (_("if writeback register is in list, it must be the lowest reg in the list")); } } + + /* If PUSH/POP has only one register, then use the A2 encoding. */ + one_reg = only_one_reg_in_list (range); + if (from_push_pop_mnem && one_reg >= 0) + { + int is_push = (inst.instruction & A_PUSH_POP_OP_MASK) == A1_OPCODE_PUSH; + + inst.instruction &= A_COND_MASK; + inst.instruction |= is_push ? A2_OPCODE_PUSH : A2_OPCODE_POP; + inst.instruction |= one_reg << 12; + } +} + +static void +do_ldmstm (void) +{ + encode_ldmstm (/*from_push_pop_mnem=*/FALSE); } /* ARMv5TE load-consecutive (argument parse) @@ -7550,35 +7879,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); } @@ -7631,6 +7959,18 @@ do_ldrexd (void) inst.instruction |= inst.operands[2].reg << 16; } +/* In both ARM and thumb state 'ldr pc, #imm' with an immediate + which is not a multiple of four is UNPREDICTABLE. */ +static void +check_ldr_r15_aligned (void) +{ + constraint (!(inst.operands[1].immisreg) + && (inst.operands[0].reg == REG_PC + && inst.operands[1].reg == REG_PC + && (inst.reloc.exp.X_add_number & 0x3)), + _("ldr to register 15 must be 4-byte alligned")); +} + static void do_ldst (void) { @@ -7639,6 +7979,7 @@ do_ldst (void) if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/FALSE)) return; encode_arm_addr_mode_2 (1, /*is_t=*/FALSE); + check_ldr_r15_aligned (); } static void @@ -7782,7 +8123,7 @@ static void do_vmrs (void) { unsigned Rt = inst.operands[0].reg; - + if (thumb_mode && inst.operands[0].reg == REG_SP) { inst.error = BAD_SP; @@ -7796,8 +8137,18 @@ do_vmrs (void) return; } - if (inst.operands[1].reg != 1) - first_error (_("operand 1 must be FPSCR")); + switch (inst.operands[1].reg) + { + case 0: /* FPSID */ + case 1: /* FPSCR */ + case 6: /* MVFR1 */ + case 7: /* MVFR0 */ + case 8: /* FPEXC */ + inst.instruction |= (inst.operands[1].reg << 16); + break; + default: + first_error (_("operand 1 must be a VFP extension System Register")); + } inst.instruction |= (Rt << 12); } @@ -7806,7 +8157,7 @@ static void do_vmsr (void) { unsigned Rt = inst.operands[1].reg; - + if (thumb_mode) reject_bad_reg (Rt); else if (Rt == REG_PC) @@ -7815,8 +8166,16 @@ do_vmsr (void) return; } - if (inst.operands[0].reg != 1) - first_error (_("operand 0 must be FPSCR")); + switch (inst.operands[0].reg) + { + case 0: /* FPSID */ + case 1: /* FPSCR */ + case 8: /* FPEXC */ + inst.instruction |= (inst.operands[0].reg << 16); + break; + default: + first_error (_("operand 0 must be FPSID or FPSCR pr FPEXC")); + } inst.instruction |= (Rt << 12); } @@ -7824,16 +8183,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: @@ -7951,8 +8324,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. */ @@ -7994,7 +8368,7 @@ do_push_pop (void) inst.operands[0].isreg = 1; inst.operands[0].writeback = 1; inst.operands[0].reg = REG_SP; - do_ldmstm (); + encode_ldmstm (/*from_push_pop_mnem=*/TRUE); } /* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the @@ -8081,6 +8455,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; @@ -8093,6 +8470,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) { @@ -8191,6 +8575,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) { @@ -8464,7 +8863,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); } @@ -9166,6 +9581,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) @@ -9216,6 +9634,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); } } @@ -9596,8 +10018,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); } } @@ -9606,6 +10027,7 @@ do_t_branch (void) { int opcode; int cond; + int reloc; cond = inst.cond; set_it_insn_type (IF_INSIDE_IT_LAST_INSN); @@ -9624,33 +10046,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; } @@ -9672,8 +10098,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 @@ -9941,30 +10374,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 UNKNOWN"), - 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; + } } } @@ -10121,14 +10592,33 @@ 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) + if (inst.operands[1].immisreg) reject_bad_reg (inst.operands[1].imm); + constraint (inst.operands[1].writeback == 1 + && inst.operands[0].reg == inst.operands[1].reg, + BAD_OVERLAP); + inst.instruction = THUMB_OP32 (opcode); inst.instruction |= inst.operands[0].reg << 12; encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE); + check_ldr_r15_aligned (); return; } @@ -10219,7 +10709,16 @@ do_t_ldstd (void) inst.operands[1].reg = inst.operands[0].reg + 1; constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here")); + constraint (inst.operands[0].reg == REG_R12, + _("r12 not allowed here")); } + + if (inst.operands[2].writeback + && (inst.operands[0].reg == inst.operands[2].reg + || inst.operands[1].reg == inst.operands[2].reg)) + as_warn (_("base register written back, and overlaps " + "one of transfer registers")); + inst.instruction |= inst.operands[0].reg << 12; inst.instruction |= inst.operands[1].reg << 8; encode_thumb32_addr_mode (2, /*is_t=*/FALSE, /*is_d=*/TRUE); @@ -10481,6 +10980,17 @@ do_t_mov_cmp (void) switch (inst.instruction) { case T_MNEM_mov: + /* In v4t or v5t a move of two lowregs produces unpredictable + results. Don't allow this. */ + if (low_regs) + { + constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6), + "MOV Rd, Rs with two low registers is not " + "permitted on this architecture"); + ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, + arm_ext_v6); + } + inst.instruction = T_OPCODE_MOV_HR; inst.instruction |= (Rn & 0x8) << 4; inst.instruction |= (Rn & 0x7); @@ -10668,34 +11178,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 @@ -10709,26 +11226,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; } @@ -11188,6 +11712,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 { @@ -11216,6 +11744,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 { @@ -11255,6 +11787,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 { @@ -11313,6 +11849,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; @@ -11321,6 +11859,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) { @@ -11473,6 +12021,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; } @@ -11923,7 +12482,9 @@ neon_select_shape (enum neon_shape shape, ...) if (!matches) break; } - if (matches) + if (matches && (j >= ARM_IT_MAX_OPERANDS || !inst.operands[j].present)) + /* We've matched all the entries in the shape table, and we don't + have any left over operands which have not been matched. */ break; } @@ -14746,6 +15307,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) @@ -15104,6 +15677,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: @@ -16108,6 +16704,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[] = { @@ -16138,6 +16741,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), @@ -16214,6 +16845,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}, @@ -16293,6 +16925,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} }; @@ -16323,7 +16956,13 @@ static struct reloc_entry reloc_names[] = { "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}, - { "got_prel", BFD_RELOC_ARM_GOT_PREL}, { "GOT_PREL", BFD_RELOC_ARM_GOT_PREL} + { "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 @@ -16704,7 +17343,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. */ @@ -16717,8 +17356,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. */ @@ -16993,18 +17632,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), @@ -17063,12 +17714,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 @@ -17089,6 +17742,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). */ @@ -17540,8 +18200,8 @@ static const struct asm_opcode insns[] = cCE("fmrs", e100a10, 2, (RR, RVS), vfp_reg_from_sp), cCE("fmsr", e000a10, 2, (RVS, RR), vfp_sp_from_reg), cCE("fmstat", ef1fa10, 0, (), noargs), - cCE("vmrs", ef10a10, 2, (APSR_RR, RVC), vmrs), - cCE("vmsr", ee10a10, 2, (RVC, RR), vmsr), + cCE("vmrs", ef00a10, 2, (APSR_RR, RVC), vmrs), + cCE("vmsr", ee00a10, 2, (RVC, RR), vmsr), cCE("fsitos", eb80ac0, 2, (RVS, RVS), vfp_sp_monadic), cCE("fuitos", eb80a40, 2, (RVS, RVS), vfp_sp_monadic), cCE("ftosis", ebd0a40, 2, (RVS, RVS), vfp_sp_monadic), @@ -17693,7 +18353,7 @@ static const struct asm_opcode insns[] = 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), @@ -18391,6 +19051,26 @@ md_chars_to_number (char * buf, int n) /* MD interface: Sections. */ +/* Calculate the maximum variable size (i.e., excluding fr_fix) + that an rs_machine_dependent frag may reach. */ + +unsigned int +arm_frag_max_var (fragS *fragp) +{ + /* We only use rs_machine_dependent for variable-size Thumb instructions, + which are either THUMB_SIZE (2) or INSN_SIZE (4). + + Note that we generate relaxable instructions even for cases that don't + really need it, like an immediate that's a trivial constant. So we're + overestimating the instruction size for some of those cases. Rather + than putting more intelligence here, it would probably be better to + avoid generating a relaxation frag in the first place when it can be + determined up front that a short instruction will suffice. */ + + gas_assert (fragp->fr_type == rs_machine_dependent); + return INSN_SIZE; +} + /* Estimate the size of a frag before relaxing. Assume everything fits in 2 bytes. */ @@ -18708,6 +19388,12 @@ relax_branch (fragS *fragp, asection *sec, int bits, long stretch) 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); @@ -18956,7 +19642,7 @@ arm_frag_align_code (int n, int max) { char err_msg[128]; - sprintf (err_msg, + sprintf (err_msg, _("alignments greater than %d bytes not supported in .text sections."), MAX_MEM_FOR_RS_ALIGN_CODE + 1); as_fatal ("%s", err_msg); @@ -19314,8 +20000,12 @@ create_unwind_entry (int have_data) size = unwind.opcode_count - 2; } else - /* An extra byte is required for the opcode count. */ - size = unwind.opcode_count + 1; + { + gas_assert (unwind.personality_index == -1); + + /* An extra byte is required for the opcode count. */ + size = unwind.opcode_count + 1; + } size = (size + 3) >> 2; if (size > 0xff) @@ -19327,6 +20017,8 @@ create_unwind_entry (int have_data) /* Allocate the table entry. */ ptr = frag_more ((size << 2) + 4); + /* PR 13449: Zero the table entries in case some of them are not used. */ + memset (ptr, 0, (size << 2) + 4); where = frag_now_fix () - ((size << 2) + 4); switch (unwind.personality_index) @@ -19341,7 +20033,7 @@ create_unwind_entry (int have_data) ptr += 4; /* Set the first byte to the number of additional words. */ - data = size - 1; + data = size > 0 ? size - 1 : 0; n = 3; break; @@ -19491,7 +20183,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; @@ -19502,7 +20194,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; @@ -19513,7 +20205,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; @@ -19522,7 +20214,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; @@ -19855,7 +20547,7 @@ encode_thumb2_b_bl_offset (char * buf, offsetT value) I1 = (value >> 23) & 0x01; I2 = (value >> 22) & 0x01; hi = (value >> 12) & 0x3ff; - lo = (value >> 1) & 0x7ff; + lo = (value >> 1) & 0x7ff; newval = md_chars_to_number (buf, THUMB_SIZE); newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE); newval |= (S << 10) | hi; @@ -19972,7 +20664,7 @@ md_apply_fix (fixS * fixP, break; } } - + newimm = encode_arm_immediate (value); temp = md_chars_to_number (buf, INSN_SIZE); @@ -20022,7 +20714,7 @@ md_apply_fix (fixS * fixP, value = 0; case BFD_RELOC_ARM_LITERAL: - sign = value >= 0; + sign = value > 0; if (value < 0) value = - value; @@ -20040,14 +20732,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; @@ -20064,8 +20761,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; @@ -20252,17 +20954,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) @@ -20290,6 +20995,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) { @@ -20324,7 +21038,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 @@ -20344,7 +21058,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)) { @@ -20367,7 +21081,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)) { @@ -20397,8 +21111,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) { @@ -20434,8 +21147,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) { @@ -20448,8 +21160,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) { @@ -20461,8 +21172,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) { @@ -20475,15 +21185,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")); @@ -20508,14 +21217,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)) { @@ -20534,13 +21241,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)) { @@ -20566,21 +21271,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); @@ -20588,9 +21287,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); @@ -20608,6 +21306,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: @@ -20675,7 +21381,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 @@ -20683,8 +21389,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); @@ -21209,6 +21920,10 @@ 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: @@ -21254,6 +21969,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: @@ -21448,14 +22164,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. */ @@ -21515,6 +22242,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; @@ -21722,8 +22454,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); @@ -21733,6 +22465,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 } @@ -21793,8 +22527,16 @@ md_begin (void) hash_insert (arm_barrier_opt_hsh, barrier_opt_names[i].template_name, (void *) (barrier_opt_names + i)); #ifdef OBJ_ELF - for (i = 0; i < sizeof (reloc_names) / sizeof (struct reloc_entry); i++) - hash_insert (arm_reloc_hsh, reloc_names[i].name, (void *) (reloc_names + i)); + for (i = 0; i < ARRAY_SIZE (reloc_names); i++) + { + struct reloc_entry * entry = reloc_names + i; + + if (arm_is_eabi() && entry->reloc == BFD_RELOC_ARM_PLT32) + /* This makes encode_branch() use the EABI versions of this relocation. */ + entry->reloc = BFD_RELOC_UNUSED; + + hash_insert (arm_reloc_hsh, entry->name, (void *) entry); + } #endif set_constant_flonums (); @@ -22227,6 +22969,7 @@ const struct arm_legacy_option_table arm_legacy_opts[] = struct arm_cpu_option_table { char *name; + size_t name_len; const arm_feature_set value; /* For some CPUs we assume an FPU unless the user explicitly sets -mfpu=... */ @@ -22238,186 +22981,238 @@ struct arm_cpu_option_table /* This list should, at a minimum, contain all the cpu names recognized by GCC. */ +#define ARM_CPU_OPT(N, V, DF, CN) { N, sizeof (N) - 1, V, DF, CN } static const struct arm_cpu_option_table arm_cpus[] = { - {"all", ARM_ANY, FPU_ARCH_FPA, NULL}, - {"arm1", ARM_ARCH_V1, FPU_ARCH_FPA, NULL}, - {"arm2", ARM_ARCH_V2, FPU_ARCH_FPA, NULL}, - {"arm250", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL}, - {"arm3", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL}, - {"arm6", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm60", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm600", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm610", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm620", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7m", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL}, - {"arm7d", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7dm", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL}, - {"arm7di", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7dmi", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL}, - {"arm70", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm700", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm700i", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm710", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm710t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm720", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm720t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm740t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm710c", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7100", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7500", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7500fe", ARM_ARCH_V3, FPU_ARCH_FPA, NULL}, - {"arm7t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm7tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm7tdmi-s", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm8", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"arm810", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"strongarm", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"strongarm1", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"strongarm110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"strongarm1100", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"strongarm1110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"arm9", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm920", ARM_ARCH_V4T, FPU_ARCH_FPA, "ARM920T"}, - {"arm920t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm922t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL}, - {"fa526", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, - {"fa626", ARM_ARCH_V4, FPU_ARCH_FPA, NULL}, + ARM_CPU_OPT ("all", ARM_ANY, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm1", ARM_ARCH_V1, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm2", ARM_ARCH_V2, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm250", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm3", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm6", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm60", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm600", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm610", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm620", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7m", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7d", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7dm", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7di", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7dmi", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm70", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm700", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm700i", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm710", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm710t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm720", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm720t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm740t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm710c", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7100", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7500", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7500fe", ARM_ARCH_V3, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm7tdmi-s", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm8", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm810", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("strongarm", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("strongarm1", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("strongarm110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("strongarm1100", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("strongarm1110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm9", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm920", ARM_ARCH_V4T, FPU_ARCH_FPA, "ARM920T"), + ARM_CPU_OPT ("arm920t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm922t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("fa526", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), + ARM_CPU_OPT ("fa626", ARM_ARCH_V4, FPU_ARCH_FPA, NULL), /* For V5 or later processors we default to using VFP; but the user should really set the FPU type explicitly. */ - {"arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL}, - {"arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, - {"arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"}, - {"arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"}, - {"arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL}, - {"arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL}, - {"arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM946E-S"}, - {"arm946e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, - {"arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL}, - {"arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM966E-S"}, - {"arm966e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, - {"arm968e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, - {"arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL}, - {"arm10tdmi", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL}, - {"arm10e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, - {"arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM1020E"}, - {"arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL}, - {"arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL}, - {"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}, - {"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}, - {"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 + ARM_CPU_OPT ("arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"), + ARM_CPU_OPT ("arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"), + ARM_CPU_OPT ("arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM946E-S"), + ARM_CPU_OPT ("arm946e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM966E-S"), + ARM_CPU_OPT ("arm966e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm968e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL), + ARM_CPU_OPT ("arm10tdmi", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL), + ARM_CPU_OPT ("arm10e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM1020E"), + ARM_CPU_OPT ("arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL), + ARM_CPU_OPT ("arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm1022e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, + "ARM1026EJ-S"), + ARM_CPU_OPT ("arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("fa606te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("fa616te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("fa626te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("fmp626", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("fa726te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm1136js", ARM_ARCH_V6, FPU_NONE, "ARM1136J-S"), + ARM_CPU_OPT ("arm1136j-s", ARM_ARCH_V6, FPU_NONE, NULL), + ARM_CPU_OPT ("arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2, + "ARM1136JF-S"), + ARM_CPU_OPT ("arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, "MPCore"), + ARM_CPU_OPT ("mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, "MPCore"), + ARM_CPU_OPT ("arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE, NULL), + ARM_CPU_OPT ("arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL), + ARM_CPU_OPT ("arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("cortex-a5", ARM_ARCH_V7A_MP_SEC, + FPU_NONE, "Cortex-A5"), + ARM_CPU_OPT ("cortex-a7", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT, + FPU_ARCH_NEON_VFP_V4, + "Cortex-A7"), + ARM_CPU_OPT ("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"), + ARM_CPU_OPT ("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-m4", ARM_ARCH_V7EM, FPU_NONE, 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"), + ARM_CPU_OPT ("cortex-a15", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT, + FPU_ARCH_NEON_VFP_V4, + "Cortex-A15"), + ARM_CPU_OPT ("cortex-r4", ARM_ARCH_V7R, FPU_NONE, "Cortex-R4"), + ARM_CPU_OPT ("cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16, + "Cortex-R4F"), + ARM_CPU_OPT ("cortex-r5", ARM_ARCH_V7R_IDIV, + FPU_NONE, "Cortex-R5"), + ARM_CPU_OPT ("cortex-m4", ARM_ARCH_V7EM, FPU_NONE, "Cortex-M4"), + ARM_CPU_OPT ("cortex-m3", ARM_ARCH_V7M, FPU_NONE, "Cortex-M3"), + ARM_CPU_OPT ("cortex-m1", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M1"), + ARM_CPU_OPT ("cortex-m0", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0"), + ARM_CPU_OPT ("cortex-m0plus", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0+"), /* ??? XSCALE is really an architecture. */ - {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL}, + ARM_CPU_OPT ("xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL), /* ??? iwmmxt is not a processor. */ - {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL}, - {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL}, - {"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL}, + ARM_CPU_OPT ("iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL), + ARM_CPU_OPT ("i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL), /* Maverick */ - {"ep9312", ARM_FEATURE (ARM_AEXT_V4T, ARM_CEXT_MAVERICK), FPU_ARCH_MAVERICK, "ARM920T"}, - {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE, NULL} + ARM_CPU_OPT ("ep9312", ARM_FEATURE (ARM_AEXT_V4T, ARM_CEXT_MAVERICK), + FPU_ARCH_MAVERICK, + "ARM920T"), + { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE, NULL } }; +#undef ARM_CPU_OPT struct arm_arch_option_table { char *name; + size_t name_len; const arm_feature_set value; const arm_feature_set default_fpu; }; /* This list should, at a minimum, contain all the architecture names recognized by GCC. */ +#define ARM_ARCH_OPT(N, V, DF) { N, sizeof (N) - 1, V, DF } static const struct arm_arch_option_table arm_archs[] = { - {"all", ARM_ANY, FPU_ARCH_FPA}, - {"armv1", ARM_ARCH_V1, FPU_ARCH_FPA}, - {"armv2", ARM_ARCH_V2, FPU_ARCH_FPA}, - {"armv2a", ARM_ARCH_V2S, FPU_ARCH_FPA}, - {"armv2s", ARM_ARCH_V2S, FPU_ARCH_FPA}, - {"armv3", ARM_ARCH_V3, FPU_ARCH_FPA}, - {"armv3m", ARM_ARCH_V3M, FPU_ARCH_FPA}, - {"armv4", ARM_ARCH_V4, FPU_ARCH_FPA}, - {"armv4xm", ARM_ARCH_V4xM, FPU_ARCH_FPA}, - {"armv4t", ARM_ARCH_V4T, FPU_ARCH_FPA}, - {"armv4txm", ARM_ARCH_V4TxM, FPU_ARCH_FPA}, - {"armv5", ARM_ARCH_V5, FPU_ARCH_VFP}, - {"armv5t", ARM_ARCH_V5T, FPU_ARCH_VFP}, - {"armv5txm", ARM_ARCH_V5TxM, FPU_ARCH_VFP}, - {"armv5te", ARM_ARCH_V5TE, FPU_ARCH_VFP}, - {"armv5texp", ARM_ARCH_V5TExP, FPU_ARCH_VFP}, - {"armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP}, - {"armv6", ARM_ARCH_V6, FPU_ARCH_VFP}, - {"armv6j", ARM_ARCH_V6, FPU_ARCH_VFP}, - {"armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP}, - {"armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP}, - {"armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP}, - {"armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP}, - {"armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP}, - {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP}, - {"armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP}, - {"armv6-m", ARM_ARCH_V6M, FPU_ARCH_VFP}, - {"armv7", ARM_ARCH_V7, FPU_ARCH_VFP}, + ARM_ARCH_OPT ("all", ARM_ANY, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv1", ARM_ARCH_V1, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv2", ARM_ARCH_V2, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv2a", ARM_ARCH_V2S, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv2s", ARM_ARCH_V2S, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv3", ARM_ARCH_V3, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv3m", ARM_ARCH_V3M, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv4", ARM_ARCH_V4, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv4xm", ARM_ARCH_V4xM, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv4t", ARM_ARCH_V4T, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv4txm", ARM_ARCH_V4TxM, FPU_ARCH_FPA), + ARM_ARCH_OPT ("armv5", ARM_ARCH_V5, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv5t", ARM_ARCH_V5T, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv5txm", ARM_ARCH_V5TxM, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv5te", ARM_ARCH_V5TE, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv5texp", ARM_ARCH_V5TExP, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6", ARM_ARCH_V6, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6j", ARM_ARCH_V6, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6-m", ARM_ARCH_V6M, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv6s-m", ARM_ARCH_V6SM, FPU_ARCH_VFP), + ARM_ARCH_OPT ("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. */ - {"armv7a", ARM_ARCH_V7A, FPU_ARCH_VFP}, - {"armv7r", ARM_ARCH_V7R, FPU_ARCH_VFP}, - {"armv7m", ARM_ARCH_V7M, FPU_ARCH_VFP}, - {"armv7-a", ARM_ARCH_V7A, FPU_ARCH_VFP}, - {"armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP}, - {"armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP}, - {"armv7e-m", ARM_ARCH_V7EM, FPU_ARCH_VFP}, - {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP}, - {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP}, - {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP}, - {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE} + ARM_ARCH_OPT ("armv7a", ARM_ARCH_V7A, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv7r", ARM_ARCH_V7R, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv7m", ARM_ARCH_V7M, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv7-a", ARM_ARCH_V7A, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP), + ARM_ARCH_OPT ("armv7e-m", ARM_ARCH_V7EM, FPU_ARCH_VFP), + ARM_ARCH_OPT ("xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP), + ARM_ARCH_OPT ("iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP), + ARM_ARCH_OPT ("iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP), + { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE } }; +#undef ARM_ARCH_OPT -/* 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; + size_t name_len; const arm_feature_set value; + const arm_feature_set allowed_archs; +}; + +/* The following table must be in alphabetical order with a NULL last entry. + */ +#define ARM_EXT_OPT(N, V, AA) { N, sizeof (N) - 1, V, AA } +static const struct arm_option_extension_value_table arm_extensions[] = +{ + ARM_EXT_OPT ("idiv", ARM_FEATURE (ARM_EXT_ADIV | ARM_EXT_DIV, 0), + ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)), + ARM_EXT_OPT ("iwmmxt",ARM_FEATURE (0, ARM_CEXT_IWMMXT), ARM_ANY), + ARM_EXT_OPT ("iwmmxt2", + ARM_FEATURE (0, ARM_CEXT_IWMMXT2), ARM_ANY), + ARM_EXT_OPT ("maverick", + ARM_FEATURE (0, ARM_CEXT_MAVERICK), ARM_ANY), + ARM_EXT_OPT ("mp", ARM_FEATURE (ARM_EXT_MP, 0), + ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)), + ARM_EXT_OPT ("os", ARM_FEATURE (ARM_EXT_OS, 0), + ARM_FEATURE (ARM_EXT_V6M, 0)), + ARM_EXT_OPT ("sec", ARM_FEATURE (ARM_EXT_SEC, 0), + ARM_FEATURE (ARM_EXT_V6K | ARM_EXT_V7A, 0)), + ARM_EXT_OPT ("virt", ARM_FEATURE (ARM_EXT_VIRT | ARM_EXT_ADIV + | ARM_EXT_DIV, 0), + ARM_FEATURE (ARM_EXT_V7A, 0)), + ARM_EXT_OPT ("xscale",ARM_FEATURE (0, ARM_CEXT_XSCALE), ARM_ANY), + { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE } }; +#undef ARM_EXT_OPT -static const struct arm_option_cpu_value_table arm_extensions[] = +/* 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}, @@ -22490,20 +23285,28 @@ struct arm_long_option_table }; static bfd_boolean -arm_parse_extension (char * str, const arm_feature_set **opt_p) +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; + char *ext; + size_t len; if (*str != '+') { @@ -22515,28 +23318,88 @@ arm_parse_extension (char * str, const arm_feature_set **opt_p) ext = strchr (str, '+'); if (ext != NULL) - optlen = ext - str; + len = ext - str; else - optlen = strlen (str); + len = strlen (str); + + if (len >= 2 && strncmp (str, "no", 2) == 0) + { + if (adding_value != 0) + { + adding_value = 0; + opt = arm_extensions; + } + + len -= 2; + str += 2; + } + else if (len > 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) + if (len == 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 (opt->name_len == len && strncmp (opt->name, str, len) == 0) { - 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 (opt->name_len == len && strncmp (opt->name, str, len) == 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; }; @@ -22545,25 +23408,25 @@ arm_parse_extension (char * str, const arm_feature_set **opt_p) } static bfd_boolean -arm_parse_cpu (char * str) +arm_parse_cpu (char *str) { - const struct arm_cpu_option_table * opt; - char * ext = strchr (str, '+'); - int optlen; + const struct arm_cpu_option_table *opt; + char *ext = strchr (str, '+'); + size_t len; if (ext != NULL) - optlen = ext - str; + len = ext - str; else - optlen = strlen (str); + len = strlen (str); - if (optlen == 0) + if (len == 0) { as_bad (_("missing cpu name `%s'"), str); return FALSE; } for (opt = arm_cpus; opt->name != NULL; opt++) - if (strncmp (opt->name, str, optlen) == 0) + if (opt->name_len == len && strncmp (opt->name, str, len) == 0) { mcpu_cpu_opt = &opt->value; mcpu_fpu_opt = &opt->default_fpu; @@ -22571,9 +23434,9 @@ arm_parse_cpu (char * str) strcpy (selected_cpu_name, opt->canonical_name); else { - int i; + size_t i; - for (i = 0; i < optlen; i++) + for (i = 0; i < len; i++) selected_cpu_name[i] = TOUPPER (opt->name[i]); selected_cpu_name[i] = 0; } @@ -22589,25 +23452,25 @@ arm_parse_cpu (char * str) } static bfd_boolean -arm_parse_arch (char * str) +arm_parse_arch (char *str) { const struct arm_arch_option_table *opt; char *ext = strchr (str, '+'); - int optlen; + size_t len; if (ext != NULL) - optlen = ext - str; + len = ext - str; else - optlen = strlen (str); + len = strlen (str); - if (optlen == 0) + if (len == 0) { as_bad (_("missing architecture name `%s'"), str); return FALSE; } for (opt = arm_archs; opt->name != NULL; opt++) - if (streq (opt->name, str)) + if (opt->name_len == len && strncmp (opt->name, str, len) == 0) { march_cpu_opt = &opt->value; march_fpu_opt = &opt->default_fpu; @@ -22626,7 +23489,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)) @@ -22854,9 +23717,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}, @@ -22888,6 +23752,8 @@ static void aeabi_set_public_attributes (void) { int arch; + char profile; + int virt_sec = 0; arm_feature_set flags; arm_feature_set tmp; const cpu_arch_ver_table *p; @@ -22897,13 +23763,26 @@ aeabi_set_public_attributes (void) ARM_MERGE_FEATURE_SETS (flags, arm_arch_used, thumb_arch_used); ARM_MERGE_FEATURE_SETS (flags, flags, *mfpu_opt); ARM_MERGE_FEATURE_SETS (flags, flags, selected_cpu); - /*Allow the user to override the reported architecture. */ + + if (ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any)) + ARM_MERGE_FEATURE_SETS (flags, flags, arm_ext_v1); + + if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_any)) + ARM_MERGE_FEATURE_SETS (flags, flags, arm_ext_v4t); + + /* Allow the user to override the reported architecture. */ if (object_arch) { ARM_CLEAR_FEATURE (flags, flags, arm_arch_any); 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++) @@ -22951,11 +23830,16 @@ aeabi_set_public_attributes (void) /* Tag_CPU_arch_profile. */ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a)) - aeabi_set_attribute_int (Tag_CPU_arch_profile, 'A'); + profile = 'A'; else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7r)) - aeabi_set_attribute_int (Tag_CPU_arch_profile, 'R'); + profile = 'R'; else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_m)) - aeabi_set_attribute_int (Tag_CPU_arch_profile, 'M'); + profile = 'M'; + else + profile = '\0'; + + if (profile != '\0') + aeabi_set_attribute_int (Tag_CPU_arch_profile, profile); /* Tag_ARM_ISA_use. */ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v1) @@ -22999,19 +23883,36 @@ aeabi_set_public_attributes (void) aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_fma) ? 2 : 1)); - + /* 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_div)) - aeabi_set_attribute_int (Tag_DIV_use, 0); - /* Fill this in when gas supports v7a sdiv/udiv. - else if (... v7a with div extension used ...) - aeabi_set_attribute_int (Tag_DIV_use, 2); */ - else - aeabi_set_attribute_int (Tag_DIV_use, 1); + /* Tag_DIV_use. + + We set Tag_DIV_use to two when integer divide instructions have been used + in ARM state, or when Thumb integer divide instructions have been used, + but we have no architecture profile set, nor have we any ARM instructions. + + For new architectures we will have to check these tests. */ + gas_assert (arch <= TAG_CPU_ARCH_V7E_M); + if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv) + || (profile == '\0' + && ARM_CPU_HAS_FEATURE (flags, arm_ext_div) + && !ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any))) + aeabi_set_attribute_int (Tag_DIV_use, 2); + + /* 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. */ @@ -23054,6 +23955,7 @@ s_arm_cpu (int ignored ATTRIBUTE_UNUSED) int i; for (i = 0; opt->name[i]; i++) selected_cpu_name[i] = TOUPPER (opt->name[i]); + selected_cpu_name[i] = 0; } ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt); @@ -23131,12 +24033,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; @@ -23253,7 +24207,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) {