/* tc-arm.c -- Assemble for the ARM
- Copyright (C) 1994, 95, 96, 97, 98, 1999, 2000
+ Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
Free Software Foundation, Inc.
Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
Modified by David Taylor (dtaylor@armltd.co.uk)
#ifdef OBJ_ELF
#include "elf/arm.h"
+#include "dwarf2dbg.h"
#endif
/* Types of processor to assemble for. */
#define ARM_CPU_MASK 0x0000000f
/* The following bitmasks control CPU extensions (ARM7 onwards): */
-#define ARM_LONGMUL 0x00000010 /* Allow long multiplies. */
-#define ARM_HALFWORD 0x00000020 /* Allow half word loads. */
-#define ARM_THUMB 0x00000040 /* Allow BX instruction. */
+#define ARM_EXT_LONGMUL 0x00000010 /* Allow long multiplies. */
+#define ARM_EXT_HALFWORD 0x00000020 /* Allow half word loads. */
+#define ARM_EXT_THUMB 0x00000040 /* Allow BX instruction. */
#define ARM_EXT_V5 0x00000080 /* Allow CLZ, etc. */
+#define ARM_EXT_V5E 0x00000100 /* "El Segundo". */
+#define ARM_EXT_XSCALE 0x00000200 /* Allow MIA etc. */
/* Architectures are the sum of the base and extensions. */
-#define ARM_ARCH_V4 (ARM_7 | ARM_LONGMUL | ARM_HALFWORD)
-#define ARM_ARCH_V4T (ARM_ARCH_V4 | ARM_THUMB)
+#define ARM_ARCH_V3M ARM_EXT_LONGMUL
+#define ARM_ARCH_V4 (ARM_ARCH_V3M | ARM_EXT_HALFWORD)
+#define ARM_ARCH_V4T (ARM_ARCH_V4 | ARM_EXT_THUMB)
#define ARM_ARCH_V5 (ARM_ARCH_V4 | ARM_EXT_V5)
-#define ARM_ARCH_V5T (ARM_ARCH_V5 | ARM_THUMB)
+#define ARM_ARCH_V5T (ARM_ARCH_V5 | ARM_EXT_THUMB)
+#define ARM_ARCH_V5TE (ARM_ARCH_V5T | ARM_EXT_V5E)
+#define ARM_ARCH_XSCALE (ARM_ARCH_V5TE | ARM_EXT_XSCALE)
/* Some useful combinations: */
#define ARM_ANY 0x00ffffff
#define FPU_MEMMULTI 0x7f000000 /* Not fpu_core. */
#ifndef CPU_DEFAULT
+#if defined __XSCALE__
+#define CPU_DEFAULT (ARM_9 | ARM_ARCH_XSCALE)
+#else
#if defined __thumb__
-#define CPU_DEFAULT (ARM_ARCH_V4 | ARM_THUMB)
+#define CPU_DEFAULT (ARM_7 | ARM_ARCH_V4T)
#else
-#define CPU_DEFAULT ARM_ALL
+#define CPU_DEFAULT ARM_ALL
+#endif
#endif
#endif
#if defined OBJ_COFF || defined OBJ_ELF
/* Flags stored in private area of BFD structure. */
static boolean uses_apcs_26 = false;
+static boolean atpcs = false;
static boolean support_interwork = false;
static boolean uses_apcs_float = false;
static boolean pic_code = false;
struct arm_it inst;
-struct asm_shift
+enum asm_shift_index
{
- CONST char * template;
- unsigned long value;
+ SHIFT_LSL = 0,
+ SHIFT_LSR,
+ SHIFT_ASR,
+ SHIFT_ROR,
+ SHIFT_RRX
+};
+
+struct asm_shift_properties
+{
+ enum asm_shift_index index;
+ unsigned long bit_field;
+ unsigned int allows_0 : 1;
+ unsigned int allows_32 : 1;
+};
+
+static const struct asm_shift_properties shift_properties [] =
+{
+ { SHIFT_LSL, 0, 1, 0},
+ { SHIFT_LSR, 0x20, 0, 1},
+ { SHIFT_ASR, 0x40, 0, 1},
+ { SHIFT_ROR, 0x60, 0, 0},
+ { SHIFT_RRX, 0x60, 0, 0}
+};
+
+struct asm_shift_name
+{
+ const char * name;
+ const struct asm_shift_properties * properties;
};
-static CONST struct asm_shift shift[] =
-{
- {"asl", 0},
- {"lsl", 0},
- {"lsr", 0x00000020},
- {"asr", 0x00000040},
- {"ror", 0x00000060},
- {"rrx", 0x00000060},
- {"ASL", 0},
- {"LSL", 0},
- {"LSR", 0x00000020},
- {"ASR", 0x00000040},
- {"ROR", 0x00000060},
- {"RRX", 0x00000060}
+static const struct asm_shift_name shift_names [] =
+{
+ { "asl", shift_properties + SHIFT_LSL },
+ { "lsl", shift_properties + SHIFT_LSL },
+ { "lsr", shift_properties + SHIFT_LSR },
+ { "asr", shift_properties + SHIFT_ASR },
+ { "ror", shift_properties + SHIFT_ROR },
+ { "rrx", shift_properties + SHIFT_RRX },
+ { "ASL", shift_properties + SHIFT_LSL },
+ { "LSL", shift_properties + SHIFT_LSL },
+ { "LSR", shift_properties + SHIFT_LSR },
+ { "ASR", shift_properties + SHIFT_ASR },
+ { "ROR", shift_properties + SHIFT_ROR },
+ { "RRX", shift_properties + SHIFT_RRX }
};
#define NO_SHIFT_RESTRICT 1
#define CP_T_UD 0x00800000
#define CP_T_WB 0x00200000
-#define CONDS_BIT (0x00100000)
-#define LOAD_BIT (0x00100000)
-#define TRANS_BIT (0x00200000)
+#define CONDS_BIT 0x00100000
+#define LOAD_BIT 0x00100000
+#define TRANS_BIT 0x00200000
+
+#define DOUBLE_LOAD_FLAG 0x00000001
struct asm_cond
{
static CONST struct asm_flg ldr_flags[] =
{
+ {"d", DOUBLE_LOAD_FLAG},
{"b", 0x00400000},
{"t", TRANS_BIT},
{"bt", 0x00400000 | TRANS_BIT},
static CONST struct asm_flg str_flags[] =
{
+ {"d", DOUBLE_LOAD_FLAG},
{"b", 0x00400000},
{"t", TRANS_BIT},
{"bt", 0x00400000 | TRANS_BIT},
{"ed", 0x01800000},
{"fd", 0x00800000},
{"ea", 0x01000000},
- {"fa", 0x08000000},
+ {"fa", 0x00000000},
{"ib", 0x01800000},
{"ia", 0x00800000},
{"db", 0x01000000},
- {"da", 0x08000000},
+ {"da", 0x00000000},
{NULL, 0}
};
static CONST struct asm_flg stm_flags[] =
{
- {"ed", 0x08000000},
+ {"ed", 0x00000000},
{"fd", 0x01000000},
{"ea", 0x00800000},
{"fa", 0x01800000},
{"ib", 0x01800000},
{"ia", 0x00800000},
{"db", 0x01000000},
- {"da", 0x08000000},
+ {"da", 0x00000000},
{NULL, 0}
};
{"SPSR_csxf", false, PSR_c | PSR_s | PSR_x | PSR_f},
{"SPSR_cxfs", false, PSR_c | PSR_x | PSR_f | PSR_s},
{"SPSR_cxsf", false, PSR_c | PSR_x | PSR_s | PSR_f},
- /* For backwards compatability with older toolchain we also
- support lower case versions of some of these flags. */
- {"cpsr", true, PSR_c | PSR_f},
- {"cpsr_all", true, PSR_c | PSR_f},
- {"spsr", false, PSR_c | PSR_f},
- {"spsr_all", false, PSR_c | PSR_f},
- {"cpsr_flg", true, PSR_f},
- {"cpsr_f", true, PSR_f},
- {"spsr_flg", false, PSR_f},
- {"spsr_f", false, PSR_f},
- {"cpsr_c", true, PSR_c},
- {"cpsr_ctl", true, PSR_c},
- {"spsr_c", false, PSR_c},
- {"spsr_ctl", false, PSR_c}
};
/* Functions called by parser. */
/* ARM THUMB. */
static void do_bx PARAMS ((char *, unsigned long));
+/* ARM_EXT_XScale. */
+static void do_mia PARAMS ((char *, unsigned long));
+static void do_mar PARAMS ((char *, unsigned long));
+static void do_mra PARAMS ((char *, unsigned long));
+static void do_pld PARAMS ((char *, unsigned long));
+static void do_ldrd PARAMS ((char *, unsigned long));
+
+/* ARM_EXT_V5. */
+static void do_blx PARAMS ((char *, unsigned long));
+static void do_bkpt PARAMS ((char *, unsigned long));
+static void do_clz PARAMS ((char *, unsigned long));
+static void do_lstc2 PARAMS ((char *, unsigned long));
+static void do_cdp2 PARAMS ((char *, unsigned long));
+static void do_co_reg2 PARAMS ((char *, unsigned long));
+
+static void do_t_blx PARAMS ((char *));
+static void do_t_bkpt PARAMS ((char *));
+
+/* ARM_EXT_V5E. */
+static void do_smla PARAMS ((char *, unsigned long));
+static void do_smlal PARAMS ((char *, unsigned long));
+static void do_smul PARAMS ((char *, unsigned long));
+static void do_qadd PARAMS ((char *, unsigned long));
+static void do_co_reg2c PARAMS ((char *, unsigned long));
+
/* Coprocessor Instructions. */
static void do_cdp PARAMS ((char *, unsigned long));
static void do_lstc PARAMS ((char *, unsigned long));
take 2: */
#define INSN_SIZE 4
-/* LONGEST_INST is the longest basic instruction name without conditions or
- flags. ARM7M has 4 of length 5. */
-
-#define LONGEST_INST 5
+/* LONGEST_INST is the longest basic instruction name without
+ conditions or flags. ARM7M has 4 of length 5. El Segundo
+ has one basic instruction name of length 7 (SMLALxy). */
+#define LONGEST_INST 7
struct asm_opcode
{
static CONST struct asm_opcode insns[] =
{
+/* Intel XScale extensions to ARM V5 ISA. */
+ {"mia", 0x0e200010, NULL, NULL, ARM_EXT_XSCALE, do_mia},
+ {"miaph", 0x0e280010, NULL, NULL, ARM_EXT_XSCALE, do_mia},
+ {"miabb", 0x0e2c0010, NULL, NULL, ARM_EXT_XSCALE, do_mia},
+ {"miabt", 0x0e2d0010, NULL, NULL, ARM_EXT_XSCALE, do_mia},
+ {"miatb", 0x0e2e0010, NULL, NULL, ARM_EXT_XSCALE, do_mia},
+ {"miatt", 0x0e2f0010, NULL, NULL, ARM_EXT_XSCALE, do_mia},
+ {"mar", 0x0c400000, NULL, NULL, ARM_EXT_XSCALE, do_mar},
+ {"mra", 0x0c500000, NULL, NULL, ARM_EXT_XSCALE, do_mra},
+ {"pld", 0xf450f000, "", NULL, ARM_EXT_XSCALE, do_pld},
+ {"ldr", 0x000000d0, NULL, ldr_flags, ARM_ANY, do_ldrd},
+ {"str", 0x000000f0, NULL, str_flags, ARM_ANY, do_ldrd},
+
/* ARM Instructions. */
{"and", 0x00000000, NULL, s_flag, ARM_ANY, do_arit},
{"eor", 0x00200000, NULL, s_flag, ARM_ANY, do_arit},
handled by the PSR_xxx defines above. */
/* ARM 7M long multiplies - need signed/unsigned flags! */
- {"smull", 0x00c00090, NULL, s_flag, ARM_LONGMUL, do_mull},
- {"umull", 0x00800090, NULL, s_flag, ARM_LONGMUL, do_mull},
- {"smlal", 0x00e00090, NULL, s_flag, ARM_LONGMUL, do_mull},
- {"umlal", 0x00a00090, NULL, s_flag, ARM_LONGMUL, do_mull},
+ {"smull", 0x00c00090, NULL, s_flag, ARM_EXT_LONGMUL, do_mull},
+ {"umull", 0x00800090, NULL, s_flag, ARM_EXT_LONGMUL, do_mull},
+ {"smlal", 0x00e00090, NULL, s_flag, ARM_EXT_LONGMUL, do_mull},
+ {"umlal", 0x00a00090, NULL, s_flag, ARM_EXT_LONGMUL, do_mull},
/* ARM THUMB interworking. */
- {"bx", 0x012fff10, NULL, NULL, ARM_THUMB, do_bx},
+ {"bx", 0x012fff10, NULL, NULL, ARM_EXT_THUMB, do_bx},
/* Floating point instructions. */
{"wfs", 0x0e200110, NULL, NULL, FPU_ALL, do_fp_ctrl},
{"stc", 0x0c000000, NULL, cplong_flag, ARM_2UP, do_lstc},
{"mcr", 0x0e000010, NULL, NULL, ARM_2UP, do_co_reg},
{"mrc", 0x0e100010, NULL, NULL, ARM_2UP, do_co_reg},
+
+/* ARM ISA extension 5. */
+/* Note: blx is actually 2 opcodes, so the .value is set dynamically.
+ And it's sometimes conditional and sometimes not. */
+ {"blx", 0, NULL, NULL, ARM_EXT_V5, do_blx},
+ {"clz", 0x016f0f10, NULL, NULL, ARM_EXT_V5, do_clz},
+ {"bkpt", 0xe1200070, "", NULL, ARM_EXT_V5, do_bkpt},
+ {"ldc2", 0xfc100000, "", cplong_flag, ARM_EXT_V5, do_lstc2},
+ {"stc2", 0xfc000000, "", cplong_flag, ARM_EXT_V5, do_lstc2},
+ {"cdp2", 0xfe000000, "", NULL, ARM_EXT_V5, do_cdp2},
+ {"mcr2", 0xfe000010, "", NULL, ARM_EXT_V5, do_co_reg2},
+ {"mrc2", 0xfe100010, "", NULL, ARM_EXT_V5, do_co_reg2},
+
+/* ARM ISA extension 5E, El Segundo. */
+ {"smlabb", 0x01000080, NULL, NULL, ARM_EXT_V5E, do_smla},
+ {"smlatb", 0x010000a0, NULL, NULL, ARM_EXT_V5E, do_smla},
+ {"smlabt", 0x010000c0, NULL, NULL, ARM_EXT_V5E, do_smla},
+ {"smlatt", 0x010000e0, NULL, NULL, ARM_EXT_V5E, do_smla},
+
+ {"smlawb", 0x01200080, NULL, NULL, ARM_EXT_V5E, do_smla},
+ {"smlawt", 0x012000c0, NULL, NULL, ARM_EXT_V5E, do_smla},
+
+ {"smlalbb",0x01400080, NULL, NULL, ARM_EXT_V5E, do_smlal},
+ {"smlaltb",0x014000a0, NULL, NULL, ARM_EXT_V5E, do_smlal},
+ {"smlalbt",0x014000c0, NULL, NULL, ARM_EXT_V5E, do_smlal},
+ {"smlaltt",0x014000e0, NULL, NULL, ARM_EXT_V5E, do_smlal},
+
+ {"smulbb", 0x01600080, NULL, NULL, ARM_EXT_V5E, do_smul},
+ {"smultb", 0x016000a0, NULL, NULL, ARM_EXT_V5E, do_smul},
+ {"smulbt", 0x016000c0, NULL, NULL, ARM_EXT_V5E, do_smul},
+ {"smultt", 0x016000e0, NULL, NULL, ARM_EXT_V5E, do_smul},
+
+ {"smulwb", 0x012000a0, NULL, NULL, ARM_EXT_V5E, do_smul},
+ {"smulwt", 0x012000e0, NULL, NULL, ARM_EXT_V5E, do_smul},
+
+ {"qadd", 0x01000050, NULL, NULL, ARM_EXT_V5E, do_qadd},
+ {"qdadd", 0x01400050, NULL, NULL, ARM_EXT_V5E, do_qadd},
+ {"qsub", 0x01200050, NULL, NULL, ARM_EXT_V5E, do_qadd},
+ {"qdsub", 0x01600050, NULL, NULL, ARM_EXT_V5E, do_qadd},
+
+ {"mcrr", 0x0c400000, NULL, NULL, ARM_EXT_V5E, do_co_reg2c},
+ {"mrrc", 0x0c500000, NULL, NULL, ARM_EXT_V5E, do_co_reg2c},
};
/* Defines for various bits that we will want to toggle. */
int size;
- /* Which CPU variants this exists for. */
+ /* Which CPU variants this exists for. */
unsigned long variants;
/* Function to call to parse args. */
static CONST struct thumb_opcode tinsns[] =
{
- {"adc", 0x4140, 2, ARM_THUMB, do_t_arit},
- {"add", 0x0000, 2, ARM_THUMB, do_t_add},
- {"and", 0x4000, 2, ARM_THUMB, do_t_arit},
- {"asr", 0x0000, 2, ARM_THUMB, do_t_asr},
- {"b", T_OPCODE_BRANCH, 2, ARM_THUMB, do_t_branch12},
- {"beq", 0xd0fe, 2, ARM_THUMB, do_t_branch9},
- {"bne", 0xd1fe, 2, ARM_THUMB, do_t_branch9},
- {"bcs", 0xd2fe, 2, ARM_THUMB, do_t_branch9},
- {"bhs", 0xd2fe, 2, ARM_THUMB, do_t_branch9},
- {"bcc", 0xd3fe, 2, ARM_THUMB, do_t_branch9},
- {"bul", 0xd3fe, 2, ARM_THUMB, do_t_branch9},
- {"blo", 0xd3fe, 2, ARM_THUMB, do_t_branch9},
- {"bmi", 0xd4fe, 2, ARM_THUMB, do_t_branch9},
- {"bpl", 0xd5fe, 2, ARM_THUMB, do_t_branch9},
- {"bvs", 0xd6fe, 2, ARM_THUMB, do_t_branch9},
- {"bvc", 0xd7fe, 2, ARM_THUMB, do_t_branch9},
- {"bhi", 0xd8fe, 2, ARM_THUMB, do_t_branch9},
- {"bls", 0xd9fe, 2, ARM_THUMB, do_t_branch9},
- {"bge", 0xdafe, 2, ARM_THUMB, do_t_branch9},
- {"blt", 0xdbfe, 2, ARM_THUMB, do_t_branch9},
- {"bgt", 0xdcfe, 2, ARM_THUMB, do_t_branch9},
- {"ble", 0xddfe, 2, ARM_THUMB, do_t_branch9},
- {"bal", 0xdefe, 2, ARM_THUMB, do_t_branch9},
- {"bic", 0x4380, 2, ARM_THUMB, do_t_arit},
- {"bl", 0xf7fffffe, 4, ARM_THUMB, do_t_branch23},
- {"bx", 0x4700, 2, ARM_THUMB, do_t_bx},
- {"cmn", T_OPCODE_CMN, 2, ARM_THUMB, do_t_arit},
- {"cmp", 0x0000, 2, ARM_THUMB, do_t_compare},
- {"eor", 0x4040, 2, ARM_THUMB, do_t_arit},
- {"ldmia", 0xc800, 2, ARM_THUMB, do_t_ldmstm},
- {"ldr", 0x0000, 2, ARM_THUMB, do_t_ldr},
- {"ldrb", 0x0000, 2, ARM_THUMB, do_t_ldrb},
- {"ldrh", 0x0000, 2, ARM_THUMB, do_t_ldrh},
- {"ldrsb", 0x5600, 2, ARM_THUMB, do_t_lds},
- {"ldrsh", 0x5e00, 2, ARM_THUMB, do_t_lds},
- {"ldsb", 0x5600, 2, ARM_THUMB, do_t_lds},
- {"ldsh", 0x5e00, 2, ARM_THUMB, do_t_lds},
- {"lsl", 0x0000, 2, ARM_THUMB, do_t_lsl},
- {"lsr", 0x0000, 2, ARM_THUMB, do_t_lsr},
- {"mov", 0x0000, 2, ARM_THUMB, do_t_mov},
- {"mul", T_OPCODE_MUL, 2, ARM_THUMB, do_t_arit},
- {"mvn", T_OPCODE_MVN, 2, ARM_THUMB, do_t_arit},
- {"neg", T_OPCODE_NEG, 2, ARM_THUMB, do_t_arit},
- {"orr", 0x4300, 2, ARM_THUMB, do_t_arit},
- {"pop", 0xbc00, 2, ARM_THUMB, do_t_push_pop},
- {"push", 0xb400, 2, ARM_THUMB, do_t_push_pop},
- {"ror", 0x41c0, 2, ARM_THUMB, do_t_arit},
- {"sbc", 0x4180, 2, ARM_THUMB, do_t_arit},
- {"stmia", 0xc000, 2, ARM_THUMB, do_t_ldmstm},
- {"str", 0x0000, 2, ARM_THUMB, do_t_str},
- {"strb", 0x0000, 2, ARM_THUMB, do_t_strb},
- {"strh", 0x0000, 2, ARM_THUMB, do_t_strh},
- {"swi", 0xdf00, 2, ARM_THUMB, do_t_swi},
- {"sub", 0x0000, 2, ARM_THUMB, do_t_sub},
- {"tst", T_OPCODE_TST, 2, ARM_THUMB, do_t_arit},
+ {"adc", 0x4140, 2, ARM_EXT_THUMB, do_t_arit},
+ {"add", 0x0000, 2, ARM_EXT_THUMB, do_t_add},
+ {"and", 0x4000, 2, ARM_EXT_THUMB, do_t_arit},
+ {"asr", 0x0000, 2, ARM_EXT_THUMB, do_t_asr},
+ {"b", T_OPCODE_BRANCH, 2, ARM_EXT_THUMB, do_t_branch12},
+ {"beq", 0xd0fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bne", 0xd1fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bcs", 0xd2fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bhs", 0xd2fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bcc", 0xd3fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bul", 0xd3fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"blo", 0xd3fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bmi", 0xd4fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bpl", 0xd5fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bvs", 0xd6fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bvc", 0xd7fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bhi", 0xd8fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bls", 0xd9fe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bge", 0xdafe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"blt", 0xdbfe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bgt", 0xdcfe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"ble", 0xddfe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bal", 0xdefe, 2, ARM_EXT_THUMB, do_t_branch9},
+ {"bic", 0x4380, 2, ARM_EXT_THUMB, do_t_arit},
+ {"bl", 0xf7fffffe, 4, ARM_EXT_THUMB, do_t_branch23},
+ {"blx", 0, 0, ARM_EXT_V5, do_t_blx},
+ {"bkpt", 0xbe00, 2, ARM_EXT_V5, do_t_bkpt},
+ {"bx", 0x4700, 2, ARM_EXT_THUMB, do_t_bx},
+ {"cmn", T_OPCODE_CMN, 2, ARM_EXT_THUMB, do_t_arit},
+ {"cmp", 0x0000, 2, ARM_EXT_THUMB, do_t_compare},
+ {"eor", 0x4040, 2, ARM_EXT_THUMB, do_t_arit},
+ {"ldmia", 0xc800, 2, ARM_EXT_THUMB, do_t_ldmstm},
+ {"ldr", 0x0000, 2, ARM_EXT_THUMB, do_t_ldr},
+ {"ldrb", 0x0000, 2, ARM_EXT_THUMB, do_t_ldrb},
+ {"ldrh", 0x0000, 2, ARM_EXT_THUMB, do_t_ldrh},
+ {"ldrsb", 0x5600, 2, ARM_EXT_THUMB, do_t_lds},
+ {"ldrsh", 0x5e00, 2, ARM_EXT_THUMB, do_t_lds},
+ {"ldsb", 0x5600, 2, ARM_EXT_THUMB, do_t_lds},
+ {"ldsh", 0x5e00, 2, ARM_EXT_THUMB, do_t_lds},
+ {"lsl", 0x0000, 2, ARM_EXT_THUMB, do_t_lsl},
+ {"lsr", 0x0000, 2, ARM_EXT_THUMB, do_t_lsr},
+ {"mov", 0x0000, 2, ARM_EXT_THUMB, do_t_mov},
+ {"mul", T_OPCODE_MUL, 2, ARM_EXT_THUMB, do_t_arit},
+ {"mvn", T_OPCODE_MVN, 2, ARM_EXT_THUMB, do_t_arit},
+ {"neg", T_OPCODE_NEG, 2, ARM_EXT_THUMB, do_t_arit},
+ {"orr", 0x4300, 2, ARM_EXT_THUMB, do_t_arit},
+ {"pop", 0xbc00, 2, ARM_EXT_THUMB, do_t_push_pop},
+ {"push", 0xb400, 2, ARM_EXT_THUMB, do_t_push_pop},
+ {"ror", 0x41c0, 2, ARM_EXT_THUMB, do_t_arit},
+ {"sbc", 0x4180, 2, ARM_EXT_THUMB, do_t_arit},
+ {"stmia", 0xc000, 2, ARM_EXT_THUMB, do_t_ldmstm},
+ {"str", 0x0000, 2, ARM_EXT_THUMB, do_t_str},
+ {"strb", 0x0000, 2, ARM_EXT_THUMB, do_t_strb},
+ {"strh", 0x0000, 2, ARM_EXT_THUMB, do_t_strh},
+ {"swi", 0xdf00, 2, ARM_EXT_THUMB, do_t_swi},
+ {"sub", 0x0000, 2, ARM_EXT_THUMB, do_t_sub},
+ {"tst", T_OPCODE_TST, 2, ARM_EXT_THUMB, do_t_arit},
/* Pseudo ops: */
- {"adr", 0x0000, 2, ARM_THUMB, do_t_adr},
- {"nop", 0x46C0, 2, ARM_THUMB, do_t_nop}, /* mov r8,r8 */
+ {"adr", 0x0000, 2, ARM_EXT_THUMB, do_t_adr},
+ {"nop", 0x46C0, 2, ARM_EXT_THUMB, do_t_nop}, /* mov r8,r8 */
};
struct reg_entry
#define BAD_PC _("r15 not allowed here")
#define BAD_FLAGS _("Instruction should not have flags")
#define BAD_COND _("Instruction is not conditional")
+#define ERR_NO_ACCUM _("acc0 expected")
static struct hash_control * arm_ops_hsh = NULL;
static struct hash_control * arm_tops_hsh = NULL;
{ "sect.s", arm_s_section, 0 },
{ "word", s_arm_elf_cons, 4 },
{ "long", s_arm_elf_cons, 4 },
+ { "file", dwarf2_directive_file, 0 },
+ { "loc", dwarf2_directive_loc, 0 },
#else
{ "word", cons, 4},
#endif
== inst.reloc.exp.X_add_number)
&& literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned)
break;
+
+ if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op
+ && inst.reloc.exp.X_op == O_symbol
+ && (literals[lit_count].exp.X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (literals[lit_count].exp.X_add_symbol
+ == inst.reloc.exp.X_add_symbol)
+ && (literals[lit_count].exp.X_op_symbol
+ == inst.reloc.exp.X_op_symbol))
+ break;
+
lit_count++;
}
if (lit_count == next_literal_pool_place) /* New entry. */
{
- if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
+ if (next_literal_pool_place >= MAX_LITERAL_POOL_SIZE)
{
inst.error = _("Literal Pool Overflow");
return FAIL;
static void
symbol_locate (symbolP, name, segment, valu, frag)
- symbolS * symbolP;
+ symbolS * symbolP;
CONST char * name; /* It is copied, the caller can modify. */
segT segment; /* Segment identifier (SEG_<something>). */
valueT valu; /* Symbol value. */
{
extern struct list_info_struct * listing_tail;
fragS * dummy_frag = (fragS *) xmalloc (sizeof (fragS));
-
+
memset (dummy_frag, 0, sizeof (fragS));
dummy_frag->fr_type = rs_fill;
dummy_frag->line = listing_tail;
case 16:
if (! thumb_mode)
{
- if (! (cpu_variant & ARM_THUMB))
+ if (! (cpu_variant & ARM_EXT_THUMB))
as_bad (_("selected processor does not support THUMB opcodes"));
thumb_mode = 1;
case 32:
if (thumb_mode)
{
- if ((cpu_variant & ARM_ANY) == ARM_THUMB)
+ if ((cpu_variant & ARM_ANY) == ARM_EXT_THUMB)
as_bad (_("selected processor does not support ARM opcodes"));
thumb_mode = 0;
/* Terminate the word. */
*--p = 0;
+ /* CPSR's and SPSR's can now be lowercase. This is just a convenience
+ feature for ease of use and backwards compatibility. */
+ if (!strncmp (start, "cpsr", 4))
+ strncpy (start, "CPSR", 4);
+ else if (!strncmp (start, "spsr", 4))
+ strncpy (start, "SPSR", 4);
+
/* Now locate the word in the psr hash table. */
psr = (CONST struct asm_psr *) hash_find (arm_psr_hsh, start);
return;
}
- if (inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
+#if 0 /* The first edition of the ARM architecture manual stated that
+ writing anything other than the flags with an immediate operation
+ had UNPREDICTABLE effects. This constraint was removed in the
+ second edition of the specification. */
+ if ((cpu_variant & ARM_EXT_V5) != ARM_EXT_V5
+ && inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
{
- inst.error = _("can only set flag field with immediate value");
+ inst.error = _("immediate value cannot be used to set this field");
return;
}
+#endif
flags |= INST_IMMEDIATE;
return;
}
- if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC)
+ if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_mul (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm;
+
+ /* Only one format "rd, rm, rs". */
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (&str, 16)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ if (rm == rd)
+ as_tsktsk (_("rd and rm should be different in mul"));
+
+ if (skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 8)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+static void
+do_mla (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm;
+
+ /* Only one format "rd, rm, rs, rn". */
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (&str, 16)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rd == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ if (skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 0)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ if (rm == rd)
+ as_tsktsk (_("rd and rm should be different in mla"));
+
+ if (skip_past_comma (&str) == FAIL
+ || (rd = reg_required_here (&str, 8)) == FAIL
+ || skip_past_comma (&str) == FAIL
+ || (rm = reg_required_here (&str, 12)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rd == REG_PC || rm == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ inst.instruction |= flags;
+ end_of_line (str);
+ return;
+}
+
+/* Expects *str -> the characters "acc0", possibly with leading blanks.
+ Advances *str to the next non-alphanumeric.
+ Returns 0, or else FAIL (in which case sets inst.error).
+
+ (In a future XScale, there may be accumulators other than zero.
+ At that time this routine and its callers can be upgraded to suit.) */
+
+static int
+accum0_required_here (str)
+ char ** str;
+{
+ static char buff [128]; /* Note the address is taken. Hence, static. */
+ char * p = * str;
+ char c;
+ int result = 0; /* The accum number. */
+
+ skip_whitespace (p);
+
+ *str = p; /* Advance caller's string pointer too. */
+ c = *p++;
+ while (isalnum (c))
+ c = *p++;
+
+ *--p = 0; /* Aap nul into input buffer at non-alnum. */
+
+ if (! ( streq (*str, "acc0") || streq (*str, "ACC0")))
+ {
+ sprintf (buff, _("acc0 expected, not '%.100s'"), *str);
+ inst.error = buff;
+ result = FAIL;
+ }
+
+ *p = c; /* Unzap. */
+ *str = p; /* Caller's string pointer to after match. */
+ return result;
+}
+
+/* Expects **str -> after a comma. May be leading blanks.
+ Advances *str, recognizing a load mode, and setting inst.instruction.
+ Returns rn, or else FAIL (in which case may set inst.error
+ and not advance str)
+
+ Note: doesn't know Rd, so no err checks that require such knowledge. */
+
+static int
+ld_mode_required_here (string)
+ char ** string;
+{
+ char * str = * string;
+ int rn;
+ int pre_inc = 0;
+
+ skip_whitespace (str);
+
+ if (* str == '[')
+ {
+ str++;
+
+ skip_whitespace (str);
+
+ if ((rn = reg_required_here (& str, 16)) == FAIL)
+ return FAIL;
+
+ skip_whitespace (str);
+
+ if (* str == ']')
+ {
+ str ++;
+
+ if (skip_past_comma (& str) == SUCCESS)
+ {
+ /* [Rn],... (post inc) */
+ if (ldst_extend (& str, 1) == FAIL)
+ return FAIL;
+ }
+ else /* [Rn] */
+ {
+ skip_whitespace (str);
+
+ if (* str == '!')
+ {
+ str ++;
+ inst.instruction |= WRITE_BACK;
+ }
+
+ inst.instruction |= INDEX_UP | HWOFFSET_IMM;
+ pre_inc = 1;
+ }
+ }
+ else /* [Rn,...] */
+ {
+ if (skip_past_comma (& str) == FAIL)
+ {
+ inst.error = _("pre-indexed expression expected");
+ return FAIL;
+ }
+
+ pre_inc = 1;
+
+ if (ldst_extend (& str, 1) == FAIL)
+ return FAIL;
+
+ skip_whitespace (str);
+
+ if (* str ++ != ']')
+ {
+ inst.error = _("missing ]");
+ return FAIL;
+ }
+
+ skip_whitespace (str);
+
+ if (* str == '!')
+ {
+ str ++;
+ inst.instruction |= WRITE_BACK;
+ }
+ }
+ }
+ else if (* str == '=') /* ldr's "r,=label" syntax */
+ /* We should never reach here, because <text> = <expression> is
+ caught gas/read.c read_a_source_file() as a .set operation. */
+ return FAIL;
+ else /* PC +- 8 bit immediate offset. */
+ {
+ if (my_get_expression (& inst.reloc.exp, & str))
+ return FAIL;
+
+ inst.instruction |= HWOFFSET_IMM; /* The I bit. */
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */
+ inst.reloc.pc_rel = 1;
+ inst.instruction |= (REG_PC << 16);
+
+ rn = REG_PC;
+ pre_inc = 1;
+ }
+
+ inst.instruction |= (pre_inc ? PRE_INDEX : 0);
+ * string = str;
+
+ return rn;
+}
+
+/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
+ SMLAxy{cond} Rd,Rm,Rs,Rn
+ SMLAWy{cond} Rd,Rm,Rs,Rn
+ Error if any register is R15. */
+
+static void
+do_smla (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm, rs, rn;
+
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (& str, 16)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rm = reg_required_here (& str, 0)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rs = reg_required_here (& str, 8)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rn = reg_required_here (& str, 12)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (rd == REG_PC || rm == REG_PC || rs == REG_PC || rn == REG_PC)
+ inst.error = BAD_PC;
+
+ else if (flags)
+ inst.error = BAD_FLAGS;
+
+ else
+ end_of_line (str);
+}
+
+/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
+ SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
+ Error if any register is R15.
+ Warning if Rdlo == Rdhi. */
+
+static void
+do_smlal (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rdlo, rdhi, rm, rs;
+
+ skip_whitespace (str);
+
+ if ((rdlo = reg_required_here (& str, 12)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rdhi = reg_required_here (& str, 16)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rm = reg_required_here (& str, 0)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rs = reg_required_here (& str, 8)) == FAIL)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (rdlo == REG_PC || rdhi == REG_PC || rm == REG_PC || rs == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ if (rdlo == rdhi)
+ as_tsktsk (_("rdhi and rdlo must be different"));
+
+ if (flags)
+ inst.error = BAD_FLAGS;
+ else
+ end_of_line (str);
+}
+
+/* ARM V5E (El Segundo) signed-multiply (argument parse)
+ SMULxy{cond} Rd,Rm,Rs
+ Error if any register is R15. */
+
+static void
+do_smul (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm, rs;
+
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (& str, 16)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rm = reg_required_here (& str, 0)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rs = reg_required_here (& str, 8)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (rd == REG_PC || rm == REG_PC || rs == REG_PC)
+ inst.error = BAD_PC;
+
+ else if (flags)
+ inst.error = BAD_FLAGS;
+
+ else
+ end_of_line (str);
+}
+
+/* ARM V5E (El Segundo) saturating-add/subtract (argument parse)
+ Q[D]{ADD,SUB}{cond} Rd,Rm,Rn
+ Error if any register is R15. */
+
+static void
+do_qadd (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm, rn;
+
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (& str, 12)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rm = reg_required_here (& str, 0)) == FAIL
+ || skip_past_comma (& str) == FAIL
+ || (rn = reg_required_here (& str, 16)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (rd == REG_PC || rm == REG_PC || rn == REG_PC)
+ inst.error = BAD_PC;
+
+ else if (flags)
+ inst.error = BAD_FLAGS;
+
+ else
+ end_of_line (str);
+}
+
+/* ARM V5E (el Segundo)
+ MCRRcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+ MRRCcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+
+ These are equivalent to the XScale instructions MAR and MRA,
+ respectively, when coproc == 0, opcode == 0, and CRm == 0.
+
+ Result unpredicatable if Rd or Rn is R15. */
+
+static void
+do_co_reg2c (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rn;
+
+ skip_whitespace (str);
+
+ if (co_proc_number (& str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_opc_expr (& str, 4, 4) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || (rd = reg_required_here (& str, 12)) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || (rn = reg_required_here (& str, 16)) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ /* Unpredictable result if rd or rn is R15. */
+ if (rd == REG_PC || rn == REG_PC)
+ as_tsktsk
+ (_("Warning: Instruction unpredictable when using r15"));
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 0) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (flags)
+ inst.error = BAD_COND;
+
+ end_of_line (str);
+}
+
+/* ARM V5 count-leading-zeroes instruction (argument parse)
+ CLZ{<cond>} <Rd>, <Rm>
+ Condition defaults to COND_ALWAYS.
+ Error if Rd or Rm are R15. */
+
+static void
+do_clz (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rd, rm;
+
+ if (flags)
+ {
+ as_bad (BAD_FLAGS);
+ return;
+ }
+
+ skip_whitespace (str);
+
+ if (((rd = reg_required_here (& str, 12)) == FAIL)
+ || (skip_past_comma (& str) == FAIL)
+ || ((rm = reg_required_here (& str, 0)) == FAIL))
+ inst.error = BAD_ARGS;
+
+ else if (rd == REG_PC || rm == REG_PC )
+ inst.error = BAD_PC;
+
+ else
+ end_of_line (str);
+}
+
+/* ARM V5 (argument parse)
+ LDC2{L} <coproc>, <CRd>, <addressing mode>
+ STC2{L} <coproc>, <CRd>, <addressing mode>
+ Instruction is not conditional, and has 0xf in the codition field.
+ Otherwise, it's the same as LDC/STC. */
+
+static void
+do_lstc2 (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ if (flags)
+ inst.error = BAD_COND;
+
+ skip_whitespace (str);
+
+ if (co_proc_number (& str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ }
+ else if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ }
+ else if (skip_past_comma (& str) == FAIL
+ || cp_address_required_here (& str) == FAIL)
+ {
+ if (! inst.error)
+ inst.error = BAD_ARGS;
+ }
+ else
+ end_of_line (str);
+}
+
+/* ARM V5 (argument parse)
+ CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>, <opcode_2>
+ Instruction is not conditional, and has 0xf in the condition field.
+ Otherwise, it's the same as CDP. */
+
+static void
+do_cdp2 (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ skip_whitespace (str);
+
+ if (co_proc_number (& str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_opc_expr (& str, 20,4) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 16) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 0) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == SUCCESS)
+ {
+ if (cp_opc_expr (& str, 5, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+ }
+
+ if (flags)
+ inst.error = BAD_FLAGS;
+
+ end_of_line (str);
+}
+
+/* ARM V5 (argument parse)
+ MCR2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
+ MRC2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
+ Instruction is not conditional, and has 0xf in the condition field.
+ Otherwise, it's the same as MCR/MRC. */
+
+static void
+do_co_reg2 (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ skip_whitespace (str);
+
+ if (co_proc_number (& str) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_opc_expr (& str, 21, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || reg_required_here (& str, 12) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 16) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == FAIL
+ || cp_reg_required_here (& str, 0) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+
+ if (skip_past_comma (& str) == SUCCESS)
+ {
+ if (cp_opc_expr (& str, 5, 3) == FAIL)
+ {
+ if (!inst.error)
+ inst.error = BAD_ARGS;
+ return;
+ }
+ }
+
+ if (flags)
+ inst.error = BAD_COND;
+
+ end_of_line (str);
+}
+
+/* THUMB V5 breakpoint instruction (argument parse)
+ BKPT <immed_8>. */
+
+static void
+do_t_bkpt (str)
+ char * str;
+{
+ expressionS expr;
+ unsigned long number;
+
+ skip_whitespace (str);
+
+ /* Allow optional leading '#'. */
+ if (is_immediate_prefix (*str))
+ str ++;
+
+ memset (& expr, '\0', sizeof (expr));
+ if (my_get_expression (& expr, & str) || (expr.X_op != O_constant))
+ {
+ inst.error = _("bad or missing expression");
+ return;
+ }
+
+ number = expr.X_add_number;
+
+ /* Check it fits an 8 bit unsigned. */
+ if (number != (number & 0xff))
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+
+ inst.instruction |= number;
+
+ end_of_line (str);
+}
+
+/* ARM V5 branch-link-exchange (argument parse) for BLX(1) only.
+ Expects inst.instruction is set for BLX(1).
+ Note: this is cloned from do_branch, and the reloc changed to be a
+ new one that can cope with setting one extra bit (the H bit). */
+
+static void
+do_branch25 (str, flags)
+ char * str;
+ unsigned long flags ATTRIBUTE_UNUSED;
+{
+ if (my_get_expression (& inst.reloc.exp, & str))
+ return;
+
+#ifdef OBJ_ELF
+ {
+ char * save_in;
+
+ /* ScottB: February 5, 1998 */
+ /* Check to see of PLT32 reloc required for the instruction. */
+
+ /* arm_parse_reloc() works on input_line_pointer.
+ We actually want to parse the operands to the branch instruction
+ passed in 'str'. Save the input pointer and restore it later. */
+ save_in = input_line_pointer;
+ input_line_pointer = str;
+
+ if (inst.reloc.exp.X_op == O_symbol
+ && *str == '('
+ && arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
+ {
+ inst.reloc.type = BFD_RELOC_ARM_PLT32;
+ inst.reloc.pc_rel = 0;
+ /* Modify str to point to after parsed operands, otherwise
+ end_of_line() will complain about the (PLT) left in str. */
+ str = input_line_pointer;
+ }
+ else
+ {
+ inst.reloc.type = BFD_RELOC_ARM_PCREL_BLX;
+ inst.reloc.pc_rel = 1;
+ }
+
+ input_line_pointer = save_in;
+ }
+#else
+ inst.reloc.type = BFD_RELOC_ARM_PCREL_BLX;
+ inst.reloc.pc_rel = 1;
+#endif /* OBJ_ELF */
+
+ end_of_line (str);
+}
+
+/* ARM V5 branch-link-exchange instruction (argument parse)
+ BLX <target_addr> ie BLX(1)
+ BLX{<condition>} <Rm> ie BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the insns[].value is not used, and the code here zaps values
+ into inst.instruction.
+ Also, the <target_addr> can be 25 bits, hence has its own reloc. */
+
+static void
+do_blx (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ char * mystr = str;
+ int rm;
+
+ if (flags)
+ {
+ as_bad (BAD_FLAGS);
+ return;
+ }
+
+ skip_whitespace (mystr);
+ rm = reg_required_here (& mystr, 0);
+
+ /* The above may set inst.error. Ignore his opinion. */
+ inst.error = 0;
+
+ if (rm != FAIL)
+ {
+ /* Arg is a register.
+ Use the condition code our caller put in inst.instruction.
+ Pass ourselves off as a BX with a funny opcode. */
+ inst.instruction |= 0x012fff30;
+ do_bx (str, flags);
+ }
+ else
+ {
+ /* This must be is BLX <target address>, no condition allowed. */
+ if (inst.instruction != COND_ALWAYS)
+ {
+ inst.error = BAD_COND;
+ return;
+ }
+
+ inst.instruction = 0xfafffffe;
+
+ /* Process like a B/BL, but with a different reloc.
+ Note that B/BL expecte fffffe, not 0, offset in the opcode table. */
+ do_branch25 (str, flags);
+ }
+}
+
+/* ARM V5 Thumb BLX (argument parse)
+ BLX <target_addr> which is BLX(1)
+ BLX <Rm> which is BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the tinsns[].value is not used, and the code here zaps values
+ into inst.instruction. */
+
+static void
+do_t_blx (str)
+ char * str;
+{
+ char * mystr = str;
+ int rm;
+
+ skip_whitespace (mystr);
+ inst.instruction = 0x4780;
+
+ /* Note that this call is to the ARM register recognizer. BLX(2)
+ uses the ARM register space, not the Thumb one, so a call to
+ thumb_reg() would be wrong. */
+ rm = reg_required_here (& mystr, 3);
+ inst.error = 0;
+
+ if (rm != FAIL)
+ {
+ /* It's BLX(2). The .instruction was zapped with rm & is final. */
+ inst.size = 2;
+ }
+ else
+ {
+ /* No ARM register. This must be BLX(1). Change the .instruction. */
+ inst.instruction = 0xf7ffeffe;
+ inst.size = 4;
+
+ if (my_get_expression (& inst.reloc.exp, & mystr))
+ return;
+
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BLX;
+ inst.reloc.pc_rel = 1;
+ }
+
+ end_of_line (mystr);
+}
+
+/* ARM V5 breakpoint instruction (argument parse)
+ BKPT <16 bit unsigned immediate>
+ Instruction is not conditional.
+ The bit pattern given in insns[] has the COND_ALWAYS condition,
+ and it is an error if the caller tried to override that.
+ Note "flags" is nonzero if a flag was supplied (which is an error). */
+
+static void
+do_bkpt (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ expressionS expr;
+ unsigned long number;
+
+ skip_whitespace (str);
+
+ /* Allow optional leading '#'. */
+ if (is_immediate_prefix (* str))
+ str++;
+
+ memset (& expr, '\0', sizeof (expr));
+
+ if (my_get_expression (& expr, & str) || (expr.X_op != O_constant))
+ {
+ inst.error = _("bad or missing expression");
+ return;
+ }
+
+ number = expr.X_add_number;
+
+ /* Check it fits a 16 bit unsigned. */
+ if (number != (number & 0xffff))
+ {
+ inst.error = _("immediate value out of range");
+ return;
+ }
+
+ /* Top 12 of 16 bits to bits 19:8. */
+ inst.instruction |= (number & 0xfff0) << 4;
+
+ /* Bottom 4 of 16 bits to bits 3:0. */
+ inst.instruction |= number & 0xf;
+
+ end_of_line (str);
+
+ if (flags)
+ inst.error = BAD_FLAGS;
+}
+
+/* Xscale multiply-accumulate (argument parse)
+ MIAcc acc0,Rm,Rs
+ MIAPHcc acc0,Rm,Rs
+ MIAxycc acc0,Rm,Rs. */
+
+static void
+do_mia (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rs;
+ int rm;
+
+ if (flags)
+ as_bad (BAD_FLAGS);
+
+ else if (accum0_required_here (& str) == FAIL)
+ inst.error = ERR_NO_ACCUM;
+
+ else if (skip_past_comma (& str) == FAIL
+ || (rm = reg_required_here (& str, 0)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (skip_past_comma (& str) == FAIL
+ || (rs = reg_required_here (& str, 12)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ /* inst.instruction has now been zapped with both rm and rs. */
+ else if (rm == REG_PC || rs == REG_PC)
+ inst.error = BAD_PC; /* Undefined result if rm or rs is R15. */
+
+ else
+ end_of_line (str);
+}
+
+/* Xscale move-accumulator-register (argument parse)
+
+ MARcc acc0,RdLo,RdHi. */
+
+static void
+do_mar (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rdlo, rdhi;
+
+ if (flags)
+ as_bad (BAD_FLAGS);
+
+ else if (accum0_required_here (& str) == FAIL)
+ inst.error = ERR_NO_ACCUM;
+
+ else if (skip_past_comma (& str) == FAIL
+ || (rdlo = reg_required_here (& str, 12)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (skip_past_comma (& str) == FAIL
+ || (rdhi = reg_required_here (& str, 16)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ /* inst.instruction has now been zapped with both rdlo and rdhi. */
+ else if (rdlo == REG_PC || rdhi == REG_PC)
+ inst.error = BAD_PC; /* Undefined result if rdlo or rdhi is R15. */
+
+ else
+ end_of_line (str);
+}
+
+/* Xscale move-register-accumulator (argument parse)
+
+ MRAcc RdLo,RdHi,acc0. */
+
+static void
+do_mra (str, flags)
+ char * str;
+ unsigned long flags;
+{
+ int rdlo;
+ int rdhi;
+
+ if (flags)
{
- inst.error = BAD_PC;
+ as_bad (BAD_FLAGS);
return;
}
- inst.instruction |= flags;
- end_of_line (str);
- return;
+ skip_whitespace (str);
+
+ if ((rdlo = reg_required_here (& str, 12)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (skip_past_comma (& str) == FAIL
+ || (rdhi = reg_required_here (& str, 16)) == FAIL)
+ inst.error = BAD_ARGS;
+
+ else if (skip_past_comma (& str) == FAIL
+ || accum0_required_here (& str) == FAIL)
+ inst.error = ERR_NO_ACCUM;
+
+ /* inst.instruction has now been zapped with both rdlo and rdhi. */
+ else if (rdlo == rdhi)
+ inst.error = BAD_ARGS; /* Undefined result if 2 writes to same reg. */
+
+ else if (rdlo == REG_PC || rdhi == REG_PC)
+ inst.error = BAD_PC; /* Undefined result if rdlo or rdhi is R15. */
+ else
+ end_of_line (str);
}
+/* Xscale: Preload-Cache
+
+ PLD <addr_mode>
+
+ Syntactically, like LDR with B=1, W=0, L=1. */
+
static void
-do_mul (str, flags)
+do_pld (str, flags)
char * str;
unsigned long flags;
{
- int rd, rm;
-
- /* Only one format "rd, rm, rs". */
- skip_whitespace (str);
+ int rd;
- if ((rd = reg_required_here (&str, 16)) == FAIL)
+ if (flags)
{
- inst.error = BAD_ARGS;
+ as_bad (BAD_FLAGS);
return;
}
- if (rd == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ skip_whitespace (str);
- if (skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
+ if (* str != '[')
{
- inst.error = BAD_ARGS;
+ inst.error = _("'[' expected after PLD mnemonic");
return;
}
- if (rm == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ ++ str;
+ skip_whitespace (str);
- if (rm == rd)
- as_tsktsk (_("rd and rm should be different in mul"));
+ if ((rd = reg_required_here (& str, 16)) == FAIL)
+ return;
- if (skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 8)) == FAIL)
+ skip_whitespace (str);
+
+ if (* str == ']')
{
- inst.error = BAD_ARGS;
- return;
- }
+ /* [Rn], ... ? */
+ ++ str;
+ skip_whitespace (str);
- if (rm == REG_PC)
+ if (skip_past_comma (& str) == SUCCESS)
+ {
+ if (ldst_extend (& str, 0) == FAIL)
+ return;
+ }
+ else if (* str == '!') /* [Rn]! */
+ {
+ inst.error = _("writeback used in preload instruction");
+ ++ str;
+ }
+ else /* [Rn] */
+ inst.instruction |= INDEX_UP | PRE_INDEX;
+ }
+ else /* [Rn, ...] */
{
- inst.error = BAD_PC;
- return;
+ if (skip_past_comma (& str) == FAIL)
+ {
+ inst.error = _("pre-indexed expression expected");
+ return;
+ }
+
+ if (ldst_extend (& str, 0) == FAIL)
+ return;
+
+ skip_whitespace (str);
+
+ if (* str != ']')
+ {
+ inst.error = _("missing ]");
+ return;
+ }
+
+ ++ str;
+ skip_whitespace (str);
+
+ if (* str == '!') /* [Rn]! */
+ {
+ inst.error = _("writeback used in preload instruction");
+ ++ str;
+ }
+
+ inst.instruction |= PRE_INDEX;
}
- inst.instruction |= flags;
end_of_line (str);
- return;
}
+/* Xscale load-consecutive (argument parse)
+ Mode is like LDRH.
+
+ LDRccD R, mode
+ STRccD R, mode. */
+
static void
-do_mla (str, flags)
+do_ldrd (str, flags)
char * str;
unsigned long flags;
{
- int rd, rm;
-
- /* Only one format "rd, rm, rs, rn". */
- skip_whitespace (str);
+ int rd;
+ int rn;
- if ((rd = reg_required_here (&str, 16)) == FAIL)
+ if (flags != DOUBLE_LOAD_FLAG)
{
- inst.error = BAD_ARGS;
+ /* Change instruction pattern to normal ldr/str. */
+ if (inst.instruction & 0x20)
+ inst.instruction = (inst.instruction & COND_MASK) | 0x04000000; /* str */
+ else
+ inst.instruction = (inst.instruction & COND_MASK) | 0x04100000; /* ldr */
+
+ /* Perform a normal load/store instruction parse. */
+ do_ldst (str, flags);
+
return;
}
- if (rd == REG_PC)
+ if ((cpu_variant & ARM_EXT_XSCALE) != ARM_EXT_XSCALE)
{
- inst.error = BAD_PC;
+ static char buff[128];
+
+ --str;
+ while (isspace (*str))
+ --str;
+ str -= 4;
+
+ /* Deny all knowledge. */
+ sprintf (buff, _("bad instruction '%.100s'"), str);
+ inst.error = buff;
return;
}
- if (skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
+ skip_whitespace (str);
+
+ if ((rd = reg_required_here (& str, 12)) == FAIL)
{
inst.error = BAD_ARGS;
return;
}
- if (rm == REG_PC)
+ if (skip_past_comma (& str) == FAIL
+ || (rn = ld_mode_required_here (& str)) == FAIL)
{
- inst.error = BAD_PC;
+ if (!inst.error)
+ inst.error = BAD_ARGS;
return;
}
- if (rm == rd)
- as_tsktsk (_("rd and rm should be different in mla"));
-
- if (skip_past_comma (&str) == FAIL
- || (rd = reg_required_here (&str, 8)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 12)) == FAIL)
+ /* inst.instruction has now been zapped with Rd and the addressing mode. */
+ if (rd & 1) /* Unpredictable result if Rd is odd. */
{
- inst.error = BAD_ARGS;
+ inst.error = _("Destination register must be even");
return;
}
- if (rd == REG_PC || rm == REG_PC)
+ if (rd == REG_LR || rd == 12)
{
- inst.error = BAD_PC;
+ inst.error = _("r12 or r14 not allowed here");
return;
}
- inst.instruction |= flags;
+ if (((rd == rn) || (rd + 1 == rn))
+ &&
+ ((inst.instruction & WRITE_BACK)
+ || (!(inst.instruction & PRE_INDEX))))
+ as_warn (_("pre/post-indexing used when modified address register is destination"));
+
end_of_line (str);
- return;
}
/* Returns the index into fp_values of a floating point number,
char ** str;
int unrestrict;
{
- struct asm_shift * shft;
+ const struct asm_shift_name * shift;
char * p;
char c;
c = * p;
* p = '\0';
- shft = (struct asm_shift *) hash_find (arm_shift_hsh, * str);
+ shift = (const struct asm_shift_name *) hash_find (arm_shift_hsh, * str);
* p = c;
- if (shft)
+
+ if (shift == NULL)
{
- if ( ! strncmp (* str, "rrx", 3)
- || ! strncmp (* str, "RRX", 3))
- {
- * str = p;
- inst.instruction |= shft->value;
- return SUCCESS;
- }
+ inst.error = _("Shift expression expected");
+ return FAIL;
+ }
- skip_whitespace (p);
+ assert (shift->properties->index == shift_properties[shift->properties->index].index);
- if (unrestrict && reg_required_here (& p, 8) != FAIL)
- {
- inst.instruction |= shft->value | SHIFT_BY_REG;
- * str = p;
- return SUCCESS;
- }
- else if (is_immediate_prefix (* p))
- {
- inst.error = NULL;
- p ++;
+ if (shift->properties->index == SHIFT_RRX)
+ {
+ * str = p;
+ inst.instruction |= shift->properties->bit_field;
+ return SUCCESS;
+ }
- if (my_get_expression (& inst.reloc.exp, & p))
- return FAIL;
+ skip_whitespace (p);
- /* Validate some simple #expressions. */
- if (inst.reloc.exp.X_op == O_constant)
- {
- unsigned num = inst.reloc.exp.X_add_number;
+ if (unrestrict && reg_required_here (& p, 8) != FAIL)
+ {
+ inst.instruction |= shift->properties->bit_field | SHIFT_BY_REG;
+ * str = p;
+ return SUCCESS;
+ }
+ else if (! is_immediate_prefix (* p))
+ {
+ inst.error = (unrestrict
+ ? _("shift requires register or #expression")
+ : _("shift requires #expression"));
+ * str = p;
+ return FAIL;
+ }
- /* Reject operations greater than 32, or lsl #32. */
- if (num > 32 || (num == 32 && shft->value == 0))
- {
- inst.error = _("Invalid immediate shift");
- return FAIL;
- }
+ inst.error = NULL;
+ p ++;
- /* Shifts of zero should be converted to lsl
- (which is zero). */
- if (num == 0)
- {
- * str = p;
- return SUCCESS;
- }
+ if (my_get_expression (& inst.reloc.exp, & p))
+ return FAIL;
- /* Shifts of 32 are encoded as 0, for those shifts that
- support it. */
- if (num == 32)
- num = 0;
+ /* Validate some simple #expressions. */
+ if (inst.reloc.exp.X_op == O_constant)
+ {
+ unsigned num = inst.reloc.exp.X_add_number;
- inst.instruction |= (num << 7) | shft->value;
- * str = p;
- return SUCCESS;
+ /* Reject operations greater than 32. */
+ if (num > 32
+ /* Reject a shift of 0 unless the mode allows it. */
+ || (num == 0 && shift->properties->allows_0 == 0)
+ /* Reject a shift of 32 unless the mode allows it. */
+ || (num == 32 && shift->properties->allows_32 == 0)
+ )
+ {
+ /* As a special case we allow a shift of zero for
+ modes that do not support it to be recoded as an
+ logical shift left of zero (ie nothing). We warn
+ about this though. */
+ if (num == 0)
+ {
+ as_warn (_("Shift of 0 ignored."));
+ shift = & shift_names[0];
+ assert (shift->properties->index == SHIFT_LSL);
+ }
+ else
+ {
+ inst.error = _("Invalid immediate shift");
+ return FAIL;
}
+ }
- inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
- inst.reloc.pc_rel = 0;
- inst.instruction |= shft->value;
+ /* Shifts of 32 are encoded as 0, for those shifts that
+ support it. */
+ if (num == 32)
+ num = 0;
- * str = p;
- return SUCCESS;
- }
- else
- {
- inst.error = (unrestrict
- ? _("shift requires register or #expression")
- : _("shift requires #expression"));
- * str = p;
- return FAIL;
- }
+ inst.instruction |= (num << 7) | shift->properties->bit_field;
+ }
+ else
+ {
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+ inst.reloc.pc_rel = 0;
+ inst.instruction |= shift->properties->bit_field;
}
- inst.error = _("Shift expression expected");
- return FAIL;
+ * str = p;
+ return SUCCESS;
}
/* Do those data_ops which can take a negative immediate constant
static void
do_ldst (str, flags)
- char *str;
+ char * str;
unsigned long flags;
{
int halfword = 0;
{
/* This is actually a load/store of a halfword, or a
signed-extension load. */
- if ((cpu_variant & ARM_HALFWORD) == 0)
+ if ((cpu_variant & ARM_EXT_HALFWORD) == 0)
{
inst.error
= _("Processor does not support halfwords or signed bytes");
if (ldst_extend (&str, halfword) == FAIL)
return;
if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
+ {
+ if (flags & TRANS_BIT)
+ as_warn (_("Rn and Rd must be different in %s"),
+ ((inst.instruction & LOAD_BIT)
+ ? "LDRT" : "STRT"));
+ else
+ as_warn (_("%s register same as write-back base"),
+ ((inst.instruction & LOAD_BIT)
+ ? _("destination") : _("source")));
+ }
}
else
{
}
flags |= INDEX_UP;
- if (! (flags & TRANS_BIT))
- pre_inc = 1;
+ if (flags & TRANS_BIT)
+ {
+ if (conflict_reg)
+ as_warn (_("Rn and Rd must be different in %s"),
+ ((inst.instruction & LOAD_BIT)
+ ? "LDRT" : "STRT"));
+ }
+ else
+ pre_inc = 1;
}
}
else
if (Rs != Rd)
{
- inst.error = _("dest and source1 one must be the same register");
+ inst.error = _("dest and source1 must be the same register");
return;
}
Rs = Rn;
hash_insert (arm_tops_hsh, tinsns[i].template, (PTR) (tinsns + i));
for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i));
- for (i = 0; i < sizeof (shift) / sizeof (struct asm_shift); i++)
- hash_insert (arm_shift_hsh, shift[i].template, (PTR) (shift + i));
+ for (i = 0; i < sizeof (shift_names) / sizeof (struct asm_shift_name); i++)
+ hash_insert (arm_shift_hsh, shift_names[i].name, (PTR) (shift_names + i));
for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
if ((cpu_variant & FPU_ALL) == FPU_NONE) flags |= F_SOFT_FLOAT;
bfd_set_private_flags (stdoutput, flags);
+
+ /* We have run out flags in the COFF header to encode the
+ status of ATPCS support, so instead we create a dummy,
+ empty, debug section called .arm.atpcs. */
+ if (atpcs)
+ {
+ asection * sec;
+
+ sec = bfd_make_section (stdoutput, ".arm.atpcs");
+
+ if (sec != NULL)
+ {
+ bfd_set_section_flags
+ (stdoutput, sec, SEC_READONLY | SEC_DEBUGGING /* | SEC_HAS_CONTENTS */);
+ bfd_set_section_size (stdoutput, sec, 0);
+ bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
+ }
+ }
}
#endif
}
/* Catch special cases. */
- if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT))
+ if (cpu_variant & ARM_EXT_XSCALE)
+ mach = bfd_mach_arm_XScale;
+ else if (cpu_variant & ARM_EXT_V5E)
+ mach = bfd_mach_arm_5TE;
+ else if (cpu_variant & ARM_EXT_V5)
{
- if (cpu_variant & (ARM_EXT_V5 & ARM_THUMB))
+ if (cpu_variant & ARM_EXT_THUMB)
mach = bfd_mach_arm_5T;
- else if (cpu_variant & ARM_EXT_V5)
+ else
mach = bfd_mach_arm_5;
- else if (cpu_variant & ARM_THUMB)
+ }
+ else if (cpu_variant & ARM_EXT_HALFWORD)
+ {
+ if (cpu_variant & ARM_EXT_THUMB)
mach = bfd_mach_arm_4T;
- else if ((cpu_variant & ARM_ARCH_V4) == ARM_ARCH_V4)
+ else
mach = bfd_mach_arm_4;
- else if (cpu_variant & ARM_LONGMUL)
- mach = bfd_mach_arm_3M;
}
+ else if (cpu_variant & ARM_EXT_LONGMUL)
+ mach = bfd_mach_arm_3M;
bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
}
if (newimm != (unsigned int) FAIL)
newinsn = temp;
/* Still No ? Try using a negated value. */
- else if (validate_immediate_twopart (- value, & highpart) != (unsigned int) FAIL)
+ else if ((newimm = validate_immediate_twopart (- value, & highpart)) != (unsigned int) FAIL)
temp = newinsn = (temp & OPCODE_MASK) | OPCODE_SUB << DATA_OP_SHIFT;
/* Otherwise - give up. */
else
{
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Unable to compute ADRL instructions for PC offset of 0x%x"),
+ _("Unable to compute ADRL instructions for PC offset of 0x%lx"),
value);
break;
}
is the PC) with the destination register. We have
already added in the PC in the first instruction and we
do not want to do it again. */
- newinsn &= ~0xf0000;
+ newinsn &= ~ 0xf0000;
newinsn |= ((newinsn & 0x0f000) << 4);
}
sign = value >= 0;
if (value < 0)
- value = -value;
+ value = - value;
if (validate_offset_imm (value, 0) == FAIL)
{
sign = value >= 0;
if (value < 0)
- value = -value;
+ value = - value;
if (validate_offset_imm (value, 1) == FAIL)
{
sign = value >= 0;
if (value < 0)
- value = -value;
+ value = - value;
if (validate_offset_imm (value, 0) == FAIL)
{
newval = (newval & 0xf800) | ((value & 0x7fffff) >> 12);
newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
+ if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+ /* Remove bit zero of the adjusted offset. Bit zero can only be
+ set if the upper insn is at a half-word boundary, since the
+ destination address, an ARM instruction, must always be on a
+ word boundary. The semantics of the BLX (1) instruction, however,
+ are that bit zero in the offset must always be zero, and the
+ corresponding bit one in the target address will be set from bit
+ one of the source address. */
+ newval2 &= ~1;
md_number_to_chars (buf, newval, THUMB_SIZE);
md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
}
if ((value + 2) & ~0x3fe)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Invalid offset, value too big (0x%08X)"), value);
+ _("Invalid offset, value too big (0x%08lX)"), value);
/* Round up, since pc will be rounded down. */
newval |= (value + 2) >> 2;
case 9: /* SP load/store. */
if (value & ~0x3fc)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Invalid offset, value too big (0x%08X)"), value);
+ _("Invalid offset, value too big (0x%08lX)"), value);
newval |= value >> 2;
break;
case 6: /* Word load/store. */
if (value & ~0x7c)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Invalid offset, value too big (0x%08X)"), value);
+ _("Invalid offset, value too big (0x%08lX)"), value);
newval |= value << 4; /* 6 - 2. */
break;
case 7: /* Byte load/store. */
if (value & ~0x1f)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Invalid offset, value too big (0x%08X)"), value);
+ _("Invalid offset, value too big (0x%08lX)"), value);
newval |= value << 6;
break;
case 8: /* Halfword load/store. */
if (value & ~0x3e)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Invalid offset, value too big (0x%08X)"), value);
+ _("Invalid offset, value too big (0x%08lX)"), value);
newval |= value << 5; /* 6 - 1. */
break;
case BFD_RELOC_ARM_ADRL_IMMEDIATE:
as_bad_where (fixp->fx_file, fixp->fx_line,
- _("ADRL used for a symbol not defined in the same file"),
- fixp->fx_r_type);
+ _("ADRL used for a symbol not defined in the same file"));
return NULL;
case BFD_RELOC_ARM_OFFSET_IMM:
default: type = _("<unknown>"); break;
}
as_bad_where (fixp->fx_file, fixp->fx_line,
- _("Can not represent %s relocation in this object file format (%d)"),
- type, fixp->fx_pcrel);
+ _("Cannot represent %s relocation in this object file format"),
+ type);
return NULL;
}
}
inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
inst.reloc.type);
- return;
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst.size);
+#endif
}
void
if (*q && !strncmp (q, ".req ", 4))
{
int reg;
- char * copy_of_str = str;
+ char * copy_of_str;
char * r;
+#ifdef IGNORE_OPCODE_CASE
+ str = original_case_string;
+#endif
+ copy_of_str = str;
+
q += 4;
skip_whitespace (q);
-m[arm]8[10] Arm 8 processors
-m[arm]9[20][tdmi] Arm 9 processors
-mstrongarm[110[0]] StrongARM processors
- -m[arm]v[2345[t]] Arm architectures
+ -mxscale XScale processors
+ -m[arm]v[2345[t[e]]] Arm architectures
-mall All (except the ARM1)
FP variants:
-mfpa10, -mfpa11 FPA10 and 11 co-processor instructions
-mapcs-float Pass floats in float regs
-mapcs-reentrant Position independent code
-mthumb-interwork Code supports Arm/Thumb interworking
+ -matpcs ARM/Thumb Procedure Call Standard
-moabi Old ELF ABI */
CONST char * md_shortopts = "m:k";
/* Limit assembler to generating only Thumb instructions: */
if (streq (str, "thumb"))
{
- cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB;
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_EXT_THUMB;
cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_NONE;
thumb_mode = 1;
}
else if (streq (str, "thumb-interwork"))
{
- if ((cpu_variant & ARM_THUMB) == 0)
+ if ((cpu_variant & ARM_EXT_THUMB) == 0)
cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4T;
#if defined OBJ_COFF || defined OBJ_ELF
support_interwork = true;
as_bad (_("Unrecognised APCS switch -m%s"), arg);
return 0;
}
+
+ if (! strcmp (str, "atpcs"))
+ {
+ atpcs = true;
+ return 1;
+ }
#endif
/* Strip off optional "arm". */
if (! strncmp (str, "arm", 3))
switch (*str)
{
case 't':
- cpu_variant |= (ARM_THUMB | ARM_ARCH_V4);
+ cpu_variant |= ARM_ARCH_V4T;
break;
case 'm':
- cpu_variant |= ARM_LONGMUL;
+ cpu_variant |= ARM_EXT_LONGMUL;
break;
case 'f': /* fe => fp enabled cpu. */
case '8':
if (streq (str, "8") || streq (str, "810"))
cpu_variant = (cpu_variant & ~ARM_ANY)
- | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL;
+ | ARM_8 | ARM_ARCH_V4;
else
goto bad;
break;
case '9':
if (streq (str, "9"))
cpu_variant = (cpu_variant & ~ARM_ANY)
- | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
+ | ARM_9 | ARM_ARCH_V4T;
else if (streq (str, "920"))
cpu_variant = (cpu_variant & ~ARM_ANY)
- | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL;
+ | ARM_9 | ARM_ARCH_V4;
else if (streq (str, "920t"))
cpu_variant = (cpu_variant & ~ARM_ANY)
- | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
+ | ARM_9 | ARM_ARCH_V4T;
else if (streq (str, "9tdmi"))
cpu_variant = (cpu_variant & ~ARM_ANY)
- | ARM_9 | ARM_ARCH_V4 | ARM_LONGMUL | ARM_THUMB;
+ | ARM_9 | ARM_ARCH_V4T;
else
goto bad;
break;
|| streq (str, "strongarm110")
|| streq (str, "strongarm1100"))
cpu_variant = (cpu_variant & ~ARM_ANY)
- | ARM_8 | ARM_ARCH_V4 | ARM_LONGMUL;
+ | ARM_8 | ARM_ARCH_V4;
else
goto bad;
break;
+ case 'x':
+ if (streq (str, "xscale"))
+ cpu_variant = ARM_9 | ARM_ARCH_XSCALE;
+ else
+ goto bad;
+ break;
+
case 'v':
/* Select variant based on architecture rather than
processor. */
switch (*++str)
{
- case 'm': cpu_variant |= ARM_LONGMUL; break;
+ case 'm': cpu_variant |= ARM_EXT_LONGMUL; break;
case 0: break;
default:
as_bad (_("Invalid architecture variant -m%s"), arg);
break;
case '4':
- cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4;
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7 | ARM_ARCH_V4;
switch (*++str)
{
- case 't': cpu_variant |= ARM_THUMB; break;
+ case 't': cpu_variant |= ARM_EXT_THUMB; break;
case 0: break;
default:
as_bad (_("Invalid architecture variant -m%s"), arg);
break;
case '5':
- cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V5;
+ cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_9 | ARM_ARCH_V5;
switch (*++str)
{
- case 't': cpu_variant |= ARM_THUMB; break;
+ case 't': cpu_variant |= ARM_EXT_THUMB; break;
+ case 'e': cpu_variant |= ARM_EXT_V5E; break;
case 0: break;
default:
as_bad (_("Invalid architecture variant -m%s"), arg);
#if defined OBJ_COFF || defined OBJ_ELF
fprintf (fp, _("\
-mapcs-32, -mapcs-26 specify which ARM Procedure Calling Standard to use\n\
+ -matpcs use ARM/Thumb Procedure Calling Standard\n\
-mapcs-float floating point args are passed in FP regs\n\
-mapcs-reentrant the code is position independent/reentrant\n"));
#endif
ARM_SET_INTERWORK (sym, support_interwork);
#endif
- if (label_is_thumb_function_name)
+ /* Note - do not allow local symbols (.Lxxx) to be labeled
+ as Thumb functions. This is because these labels, whilst
+ they exist inside Thumb code, are not the entry points for
+ possible ARM->Thumb calls. Also, these labels can be used
+ as part of a computed goto or switch statement. eg gcc
+ can generate code that looks like this:
+
+ ldr r2, [pc, .Laaa]
+ lsl r3, r3, #2
+ ldr r2, [r3, r2]
+ mov pc, r2
+
+ .Lbbb: .word .Lxxx
+ .Lccc: .word .Lyyy
+ ..etc...
+ .Laaa: .word Lbbb
+
+ The first instruction loads the address of the jump table.
+ The second instruction converts a table index into a byte offset.
+ The third instruction gets the jump address out of the table.
+ The fourth instruction performs the jump.
+
+ If the address stored at .Laaa is that of a symbol which has the
+ Thumb_Func bit set, then the linker will arrange for this address
+ to have the bottom bit set, which in turn would mean that the
+ address computation performed by the third instruction would end
+ up with the bottom bit set. Since the ARM is capable of unaligned
+ word loads, the instruction would then load the incorrect address
+ out of the jump table, and chaos would ensue. */
+ if (label_is_thumb_function_name
+ && (S_GET_NAME (sym)[0] != '.' || S_GET_NAME (sym)[1] != 'L')
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
{
/* When the address of a Thumb function is taken the bottom
bit of that address should be set. This will allow
}
#endif
#ifdef OBJ_ELF
- symbolS *sym;
- char bind;
+ symbolS * sym;
+ char bind;
for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
{
if (ARM_IS_THUMB (sym))
{
- elf_symbol_type *elf_sym;
+ elf_symbol_type * elf_sym;
elf_sym = elf_symbol (symbol_get_bfdsym (sym));
bind = ELF_ST_BIND (elf_sym);
char *
arm_canonicalize_symbol_name (name)
- char *name;
+ char * name;
{
int len;
boolean
arm_validate_fix (fixP)
- fixS *fixP;
+ fixS * fixP;
{
/* If the destination of the branch is a defined symbol which does not have
the THUMB_FUNC attribute, then we must be calling a function which has
return false;
}
+#ifdef OBJ_COFF
+/* This is a little hack to help the gas/arm/adrl.s test. It prevents
+ local labels from being added to the output symbol table when they
+ are used with the ADRL pseudo op. The ADRL relocation should always
+ be resolved before the binbary is emitted, so it is safe to say that
+ it is adjustable. */
+
+boolean
+arm_fix_adjustable (fixP)
+ fixS * fixP;
+{
+ if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+ return 1;
+ return 0;
+}
+#endif
#ifdef OBJ_ELF
/* Relocations against Thumb function names must be left unadjusted,
so that the linker can use this information to correctly set the
boolean
arm_fix_adjustable (fixP)
- fixS *fixP;
+ fixS * fixP;
{
if (fixP->fx_addsy == NULL)
return 1;
}
reloc_map[] =
{
-#define MAP(str,reloc) { str, sizeof (str)-1, reloc }
+#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
MAP ("(got)", BFD_RELOC_ARM_GOT32),
MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF),
/* ScottB: Jan 30, 1998 - Added support for parsing "var(PLT)"
expression (& exp);
if (exp.X_op == O_symbol
- && *input_line_pointer == '('
+ && * input_line_pointer == '('
&& (reloc = arm_parse_reloc ()) != BFD_RELOC_UNUSED)
{
reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
}
#endif /* OBJ_ELF */
+
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
+
+void
+arm_handle_align (fragP)
+ fragS *fragP;
+{
+ static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
+ static char const thumb_noop[2] = { 0xc0, 0x46 };
+ static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
+ static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
+
+ int bytes, fix, noop_size;
+ char * p;
+ const char * noop;
+
+ if (fragP->fr_type != rs_align_code)
+ return;
+
+ bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ p = fragP->fr_literal + fragP->fr_fix;
+ fix = 0;
+
+ if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
+ bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+
+ if (fragP->tc_frag_data)
+ {
+ if (target_big_endian)
+ noop = thumb_bigend_noop;
+ else
+ noop = thumb_noop;
+ noop_size = sizeof (thumb_noop);
+ }
+ else
+ {
+ if (target_big_endian)
+ noop = arm_bigend_noop;
+ else
+ noop = arm_noop;
+ noop_size = sizeof (arm_noop);
+ }
+
+ if (bytes & (noop_size - 1))
+ {
+ fix = bytes & (noop_size - 1);
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
+ }
+
+ while (bytes >= noop_size)
+ {
+ memcpy (p, noop, noop_size);
+ p += noop_size;
+ bytes -= noop_size;
+ fix += noop_size;
+ }
+
+ fragP->fr_fix += fix;
+ fragP->fr_var = noop_size;
+}
+
+/* Called from md_do_align. Used to create an alignment
+ frag in a code section. */
+
+void
+arm_frag_align_code (n, max)
+ int n;
+ int max;
+{
+ char * p;
+
+ /* We assume that there will never be a requirment
+ to support alignments greater than 32 bytes. */
+ if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+ as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+
+ p = frag_var (rs_align_code,
+ MAX_MEM_FOR_RS_ALIGN_CODE,
+ 1,
+ (relax_substateT) max,
+ (symbolS *) NULL,
+ (offsetT) n,
+ (char *) NULL);
+ *p = 0;
+
+}
+
+/* Perform target specific initialisation of a frag. */
+
+void
+arm_init_frag (fragP)
+ fragS *fragP;
+{
+ /* Record whether this frag is in an ARM or a THUMB area. */
+ fragP->tc_frag_data = thumb_mode;
+}