Automate generate on man pages
[deliverable/binutils-gdb.git] / gas / config / tc-arm.c
index ce7a8794b2758288a345b064c0eedacfb1d3bad3..e5f0c136af19898790b2b3bd9c7529282fec3a03 100644 (file)
@@ -1,5 +1,6 @@
 /* tc-arm.c -- Assemble for the ARM
-   Copyright (C) 1994, 95, 96, 97, 1998 Free Software Foundation, Inc.
+   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)
 
@@ -25,7 +26,7 @@
 #define  NO_RELOC 0
 #include "as.h"
 
-/* need TARGET_CPU */
+/* Need TARGET_CPU.  */
 #include "config.h"
 #include "subsegs.h"
 #include "obstack.h"
 
 #ifdef OBJ_ELF
 #include "elf/arm.h"
-#endif
-
-/* ??? This is currently unused.  */
-#ifdef __STDC__
-#define internalError() \
-  as_fatal (_("ARM Internal Error, line %d, %s"), __LINE__, __FILE__)
-#else
-#define internalError() as_fatal (_("ARM Internal Error"))
+#include "dwarf2dbg.h"
 #endif
 
 /* Types of processor to assemble for.  */
 #define ARM_3          0x00000004
 #define ARM_250                ARM_3
 #define ARM_6          0x00000008
-#define ARM_7          ARM_6           /* same core instruction set */
+#define ARM_7          ARM_6           /* Same core instruction set.  */
+#define ARM_8          ARM_6           /* Same core instruction set.  */
+#define ARM_9          ARM_6           /* Same core instruction set.  */
 #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_ARCHv4     (ARM_7 | ARM_LONGMUL | ARM_HALFWORD)
+/* The following bitmasks control CPU extensions (ARM7 onwards):   */
+#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_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_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 ARM_2UP                0x00fffffe
-#define ARM_ALL                ARM_2UP         /* Not arm1 only */
+#define ARM_2UP                (ARM_ANY - ARM_1)
+#define ARM_ALL                ARM_2UP         /* Not arm1 only */
 #define ARM_3UP                0x00fffffc
-#define ARM_6UP                0x00fffff8      /* Includes ARM7 */
+#define ARM_6UP                0x00fffff8      /* Includes ARM7 */
 
 #define FPU_CORE       0x80000000
 #define FPU_FPA10      0x40000000
 #define FPU_FPA11      0x40000000
 #define FPU_NONE       0
 
-/* Some useful combinations  */
-#define FPU_ALL                0xff000000      /* Note this is ~ARM_ANY */
-#define FPU_MEMMULTI   0x7f000000      /* Not fpu_core */
+/* Some useful combinations.  */
+#define FPU_ALL                0xff000000      /* Note this is ~ARM_ANY */
+#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_ARCHv4 | ARM_THUMB)
+#define CPU_DEFAULT    (ARM_7 | ARM_ARCH_V4T)
 #else
-#define CPU_DEFAULT ARM_ALL
+#define CPU_DEFAULT    ARM_ALL
+#endif
 #endif
 #endif
 
 #define FPU_DEFAULT FPU_ALL
 #endif
 
-static unsigned long   cpu_variant = CPU_DEFAULT | FPU_DEFAULT;
+#define streq(a, b)           (strcmp (a, b) == 0)
+#define skip_whitespace(str)  while (*(str) == ' ') ++(str)
+
+static unsigned long cpu_variant = CPU_DEFAULT | FPU_DEFAULT;
+static int target_oabi = 0;
 
 #if defined OBJ_COFF || defined OBJ_ELF
-/* Flags stored in private area of BFD structure */
-static boolean         uses_apcs_26 = false;
-static boolean         support_interwork = false;
-static boolean         uses_apcs_float = false;
-static boolean         pic_code = false;
+/* 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;
 #endif
 
 /* This array holds the chars that always start a comment.  If the
-   pre-processor is disabled, these aren't very useful */
+   pre-processor is disabled, these aren't very useful */
 CONST char comment_chars[] = "@";
 
 /* This array holds the chars that only start a comment at the beginning of
    a line.  If the line seems to have the form '# 123 filename'
-   .line and .file directives will appear in the pre-processed output */
+   .line and .file directives will appear in the pre-processed output */
 /* Note that input_file.c hand checks for '#' at the beginning of the
    first line of the input file.  This is because the compiler outputs
-   #NO_APP at the beginning of its output. */
-/* Also note that comments like this one will always work. */
+   #NO_APP at the beginning of its output.  */
+/* Also note that comments like this one will always work.  */
 CONST char line_comment_chars[] = "#";
 
-CONST char line_separator_chars[] = "";
+CONST char line_separator_chars[] = ";";
 
-/* Chars that can be used to separate mant from exp in floating point nums */
+/* Chars that can be used to separate mant
+   from exp in floating point numbers.  */
 CONST char EXP_CHARS[] = "eE";
 
-/* Chars that mean this number is a floating point constant */
-/* As in 0f12.456 */
-/* or    0d1.2345e12 */
+/* Chars that mean this number is a floating point constant */
+/* As in 0f12.456  */
+/* or    0d1.2345e12  */
 
 CONST char FLT_CHARS[] = "rRsSfFdDxXeEpP";
 
-CONST int md_reloc_size = 8;           /* Size of relocation record */
+/* Prefix characters that indicate the start of an immediate
+   value.  */
+#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
 
-static int thumb_mode = 0;      /* non-zero if assembling thumb instructions */
+#ifdef OBJ_ELF
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_"  */
+symbolS * GOT_symbol;
+#endif
+
+/* Size of relocation record.  */
+CONST int md_reloc_size = 8;
+
+/* 0: assemble for ARM,
+   1: assemble for Thumb,
+   2: assemble for Thumb even though target CPU does not support thumb
+      instructions.  */
+static int thumb_mode = 0;
 
 typedef struct arm_fix
 {
@@ -134,40 +163,66 @@ typedef struct arm_fix
 
 struct arm_it
 {
-  CONST char *error;
+  CONST char *  error;
   unsigned long instruction;
-  int suffix;
-  int size;
+  int           suffix;
+  int           size;
   struct
-    {
-      bfd_reloc_code_real_type type;
-      expressionS exp;
-      int pc_rel;
-    } reloc;
+  {
+    bfd_reloc_code_real_type type;
+    expressionS              exp;
+    int                      pc_rel;
+  } reloc;
 };
 
 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
 };
 
-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}
+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_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
@@ -175,12 +230,12 @@ static CONST struct asm_shift shift[] =
 
 #define NUM_FLOAT_VALS 8
 
-CONST char *fp_const[] = 
+CONST char * fp_const[] =
 {
   "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
 };
 
-/* Number of littlenums required to hold an extended precision number */
+/* Number of littlenums required to hold an extended precision number */
 #define MAX_LITTLENUMS 6
 
 LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
@@ -199,20 +254,22 @@ LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
 #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
 {
-  CONST char *template;
+  CONST char *  template;
   unsigned long value;
 };
 
-/* This is to save a hash look-up in the common case */
+/* This is to save a hash look-up in the common case */
 #define COND_ALWAYS 0xe0000000
 
-static CONST struct asm_cond conds[] = 
+static CONST struct asm_cond conds[] =
 {
   {"eq", 0x00000000},
   {"ne", 0x10000000},
@@ -234,11 +291,11 @@ static CONST struct asm_cond conds[] =
 
 /* Warning: If the top bit of the set_bits is set, then the standard
    instruction bitmask is ignored, and the new bitmask is taken from
-   the set_bits: */
+   the set_bits:  */
 struct asm_flg
 {
-  CONST char *template;                /* Basic flag string */
-  unsigned long set_bits;      /* Bits to set */
+  CONST char *  template;      /* Basic flag string.  */
+  unsigned long set_bits;      /* Bits to set */
 };
 
 static CONST struct asm_flg s_flag[] =
@@ -249,6 +306,7 @@ static CONST struct asm_flg s_flag[] =
 
 static CONST struct asm_flg ldr_flags[] =
 {
+  {"d",  DOUBLE_LOAD_FLAG},
   {"b",  0x00400000},
   {"t",  TRANS_BIT},
   {"bt", 0x00400000 | TRANS_BIT},
@@ -260,6 +318,7 @@ static CONST struct asm_flg ldr_flags[] =
 
 static CONST struct asm_flg str_flags[] =
 {
+  {"d",  DOUBLE_LOAD_FLAG},
   {"b",  0x00400000},
   {"t",  TRANS_BIT},
   {"bt", 0x00400000 | TRANS_BIT},
@@ -285,24 +344,24 @@ static CONST struct asm_flg ldm_flags[] =
   {"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}
 };
 
@@ -363,94 +422,239 @@ static CONST struct asm_flg cplong_flag[] =
 
 struct asm_psr
 {
-  CONST char *template;
-  unsigned long number;
+  CONST char *  template;
+  boolean       cpsr;
+  unsigned long field;
 };
 
-#define PSR_FIELD_MASK  0x000f0000
+/* The bit that distnguishes CPSR and SPSR.  */
+#define SPSR_BIT   (1 << 22)
 
-#define PSR_FLAGS      0x00080000
-#define PSR_CONTROL    0x00010000 /* Undocumented instruction, its use is discouraged by ARM */
-#define PSR_ALL                0x00090000
+/* How many bits to shift the PSR_xxx bits up by.  */
+#define PSR_SHIFT  16
 
-#define CPSR_ALL       0
-#define SPSR_ALL       1
-#define CPSR_FLG       2
-#define SPSR_FLG       3
-#define CPSR_CTL       4
-#define SPSR_CTL       5
+#define PSR_c   (1 << 0)
+#define PSR_x   (1 << 1)
+#define PSR_s   (1 << 2)
+#define PSR_f   (1 << 3)
 
 static CONST struct asm_psr psrs[] =
 {
-  /* Valid <psr>'s */
-  {"cpsr",     CPSR_ALL},
-  {"cpsr_all", CPSR_ALL},
-  {"spsr",     SPSR_ALL},
-  {"spsr_all", SPSR_ALL},
-
-  /* Valid <psrf>'s */
-  {"cpsr_flg", CPSR_FLG},
-  {"spsr_flg", SPSR_FLG},
-  
-  /* Valid <psrc>'s */
-  {"cpsr_c",   CPSR_CTL},
-  {"cpsr_ctl", CPSR_CTL},
-  {"spsr_c",   SPSR_CTL},
-  {"spsr_ctl", SPSR_CTL}
+  {"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},
+  {"CPSR_x",    true,  PSR_x},
+  {"CPSR_s",    true,  PSR_s},
+  {"SPSR_x",    false, PSR_x},
+  {"SPSR_s",    false, PSR_s},
+  /* Combinations of flags.  */
+  {"CPSR_fs",  true, PSR_f | PSR_s},
+  {"CPSR_fx",  true, PSR_f | PSR_x},
+  {"CPSR_fc",  true, PSR_f | PSR_c},
+  {"CPSR_sf",  true, PSR_s | PSR_f},
+  {"CPSR_sx",  true, PSR_s | PSR_x},
+  {"CPSR_sc",  true, PSR_s | PSR_c},
+  {"CPSR_xf",  true, PSR_x | PSR_f},
+  {"CPSR_xs",  true, PSR_x | PSR_s},
+  {"CPSR_xc",  true, PSR_x | PSR_c},
+  {"CPSR_cf",  true, PSR_c | PSR_f},
+  {"CPSR_cs",  true, PSR_c | PSR_s},
+  {"CPSR_cx",  true, PSR_c | PSR_x},
+  {"CPSR_fsx", true, PSR_f | PSR_s | PSR_x},
+  {"CPSR_fsc", true, PSR_f | PSR_s | PSR_c},
+  {"CPSR_fxs", true, PSR_f | PSR_x | PSR_s},
+  {"CPSR_fxc", true, PSR_f | PSR_x | PSR_c},
+  {"CPSR_fcs", true, PSR_f | PSR_c | PSR_s},
+  {"CPSR_fcx", true, PSR_f | PSR_c | PSR_x},
+  {"CPSR_sfx", true, PSR_s | PSR_f | PSR_x},
+  {"CPSR_sfc", true, PSR_s | PSR_f | PSR_c},
+  {"CPSR_sxf", true, PSR_s | PSR_x | PSR_f},
+  {"CPSR_sxc", true, PSR_s | PSR_x | PSR_c},
+  {"CPSR_scf", true, PSR_s | PSR_c | PSR_f},
+  {"CPSR_scx", true, PSR_s | PSR_c | PSR_x},
+  {"CPSR_xfs", true, PSR_x | PSR_f | PSR_s},
+  {"CPSR_xfc", true, PSR_x | PSR_f | PSR_c},
+  {"CPSR_xsf", true, PSR_x | PSR_s | PSR_f},
+  {"CPSR_xsc", true, PSR_x | PSR_s | PSR_c},
+  {"CPSR_xcf", true, PSR_x | PSR_c | PSR_f},
+  {"CPSR_xcs", true, PSR_x | PSR_c | PSR_s},
+  {"CPSR_cfs", true, PSR_c | PSR_f | PSR_s},
+  {"CPSR_cfx", true, PSR_c | PSR_f | PSR_x},
+  {"CPSR_csf", true, PSR_c | PSR_s | PSR_f},
+  {"CPSR_csx", true, PSR_c | PSR_s | PSR_x},
+  {"CPSR_cxf", true, PSR_c | PSR_x | PSR_f},
+  {"CPSR_cxs", true, PSR_c | PSR_x | PSR_s},
+  {"CPSR_fsxc",        true, PSR_f | PSR_s | PSR_x | PSR_c},
+  {"CPSR_fscx",        true, PSR_f | PSR_s | PSR_c | PSR_x},
+  {"CPSR_fxsc",        true, PSR_f | PSR_x | PSR_s | PSR_c},
+  {"CPSR_fxcs",        true, PSR_f | PSR_x | PSR_c | PSR_s},
+  {"CPSR_fcsx",        true, PSR_f | PSR_c | PSR_s | PSR_x},
+  {"CPSR_fcxs",        true, PSR_f | PSR_c | PSR_x | PSR_s},
+  {"CPSR_sfxc",        true, PSR_s | PSR_f | PSR_x | PSR_c},
+  {"CPSR_sfcx",        true, PSR_s | PSR_f | PSR_c | PSR_x},
+  {"CPSR_sxfc",        true, PSR_s | PSR_x | PSR_f | PSR_c},
+  {"CPSR_sxcf",        true, PSR_s | PSR_x | PSR_c | PSR_f},
+  {"CPSR_scfx",        true, PSR_s | PSR_c | PSR_f | PSR_x},
+  {"CPSR_scxf",        true, PSR_s | PSR_c | PSR_x | PSR_f},
+  {"CPSR_xfsc",        true, PSR_x | PSR_f | PSR_s | PSR_c},
+  {"CPSR_xfcs",        true, PSR_x | PSR_f | PSR_c | PSR_s},
+  {"CPSR_xsfc",        true, PSR_x | PSR_s | PSR_f | PSR_c},
+  {"CPSR_xscf",        true, PSR_x | PSR_s | PSR_c | PSR_f},
+  {"CPSR_xcfs",        true, PSR_x | PSR_c | PSR_f | PSR_s},
+  {"CPSR_xcsf",        true, PSR_x | PSR_c | PSR_s | PSR_f},
+  {"CPSR_cfsx",        true, PSR_c | PSR_f | PSR_s | PSR_x},
+  {"CPSR_cfxs",        true, PSR_c | PSR_f | PSR_x | PSR_s},
+  {"CPSR_csfx",        true, PSR_c | PSR_s | PSR_f | PSR_x},
+  {"CPSR_csxf",        true, PSR_c | PSR_s | PSR_x | PSR_f},
+  {"CPSR_cxfs",        true, PSR_c | PSR_x | PSR_f | PSR_s},
+  {"CPSR_cxsf",        true, PSR_c | PSR_x | PSR_s | PSR_f},
+  {"SPSR_fs",  false, PSR_f | PSR_s},
+  {"SPSR_fx",  false, PSR_f | PSR_x},
+  {"SPSR_fc",  false, PSR_f | PSR_c},
+  {"SPSR_sf",  false, PSR_s | PSR_f},
+  {"SPSR_sx",  false, PSR_s | PSR_x},
+  {"SPSR_sc",  false, PSR_s | PSR_c},
+  {"SPSR_xf",  false, PSR_x | PSR_f},
+  {"SPSR_xs",  false, PSR_x | PSR_s},
+  {"SPSR_xc",  false, PSR_x | PSR_c},
+  {"SPSR_cf",  false, PSR_c | PSR_f},
+  {"SPSR_cs",  false, PSR_c | PSR_s},
+  {"SPSR_cx",  false, PSR_c | PSR_x},
+  {"SPSR_fsx", false, PSR_f | PSR_s | PSR_x},
+  {"SPSR_fsc", false, PSR_f | PSR_s | PSR_c},
+  {"SPSR_fxs", false, PSR_f | PSR_x | PSR_s},
+  {"SPSR_fxc", false, PSR_f | PSR_x | PSR_c},
+  {"SPSR_fcs", false, PSR_f | PSR_c | PSR_s},
+  {"SPSR_fcx", false, PSR_f | PSR_c | PSR_x},
+  {"SPSR_sfx", false, PSR_s | PSR_f | PSR_x},
+  {"SPSR_sfc", false, PSR_s | PSR_f | PSR_c},
+  {"SPSR_sxf", false, PSR_s | PSR_x | PSR_f},
+  {"SPSR_sxc", false, PSR_s | PSR_x | PSR_c},
+  {"SPSR_scf", false, PSR_s | PSR_c | PSR_f},
+  {"SPSR_scx", false, PSR_s | PSR_c | PSR_x},
+  {"SPSR_xfs", false, PSR_x | PSR_f | PSR_s},
+  {"SPSR_xfc", false, PSR_x | PSR_f | PSR_c},
+  {"SPSR_xsf", false, PSR_x | PSR_s | PSR_f},
+  {"SPSR_xsc", false, PSR_x | PSR_s | PSR_c},
+  {"SPSR_xcf", false, PSR_x | PSR_c | PSR_f},
+  {"SPSR_xcs", false, PSR_x | PSR_c | PSR_s},
+  {"SPSR_cfs", false, PSR_c | PSR_f | PSR_s},
+  {"SPSR_cfx", false, PSR_c | PSR_f | PSR_x},
+  {"SPSR_csf", false, PSR_c | PSR_s | PSR_f},
+  {"SPSR_csx", false, PSR_c | PSR_s | PSR_x},
+  {"SPSR_cxf", false, PSR_c | PSR_x | PSR_f},
+  {"SPSR_cxs", false, PSR_c | PSR_x | PSR_s},
+  {"SPSR_fsxc",        false, PSR_f | PSR_s | PSR_x | PSR_c},
+  {"SPSR_fscx",        false, PSR_f | PSR_s | PSR_c | PSR_x},
+  {"SPSR_fxsc",        false, PSR_f | PSR_x | PSR_s | PSR_c},
+  {"SPSR_fxcs",        false, PSR_f | PSR_x | PSR_c | PSR_s},
+  {"SPSR_fcsx",        false, PSR_f | PSR_c | PSR_s | PSR_x},
+  {"SPSR_fcxs",        false, PSR_f | PSR_c | PSR_x | PSR_s},
+  {"SPSR_sfxc",        false, PSR_s | PSR_f | PSR_x | PSR_c},
+  {"SPSR_sfcx",        false, PSR_s | PSR_f | PSR_c | PSR_x},
+  {"SPSR_sxfc",        false, PSR_s | PSR_x | PSR_f | PSR_c},
+  {"SPSR_sxcf",        false, PSR_s | PSR_x | PSR_c | PSR_f},
+  {"SPSR_scfx",        false, PSR_s | PSR_c | PSR_f | PSR_x},
+  {"SPSR_scxf",        false, PSR_s | PSR_c | PSR_x | PSR_f},
+  {"SPSR_xfsc",        false, PSR_x | PSR_f | PSR_s | PSR_c},
+  {"SPSR_xfcs",        false, PSR_x | PSR_f | PSR_c | PSR_s},
+  {"SPSR_xsfc",        false, PSR_x | PSR_s | PSR_f | PSR_c},
+  {"SPSR_xscf",        false, PSR_x | PSR_s | PSR_c | PSR_f},
+  {"SPSR_xcfs",        false, PSR_x | PSR_c | PSR_f | PSR_s},
+  {"SPSR_xcsf",        false, PSR_x | PSR_c | PSR_s | PSR_f},
+  {"SPSR_cfsx",        false, PSR_c | PSR_f | PSR_s | PSR_x},
+  {"SPSR_cfxs",        false, PSR_c | PSR_f | PSR_x | PSR_s},
+  {"SPSR_csfx",        false, PSR_c | PSR_s | PSR_f | PSR_x},
+  {"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},
 };
 
-/* Functions called by parser */
-/* ARM instructions */
-static void do_arit            PARAMS ((char *operands, unsigned long flags));
-static void do_cmp             PARAMS ((char *operands, unsigned long flags));
-static void do_mov             PARAMS ((char *operands, unsigned long flags));
-static void do_ldst            PARAMS ((char *operands, unsigned long flags));
-static void do_ldmstm          PARAMS ((char *operands, unsigned long flags));
-static void do_branch          PARAMS ((char *operands, unsigned long flags));
-static void do_swi             PARAMS ((char *operands, unsigned long flags));
-/* Pseudo Op codes */
-static void do_adr             PARAMS ((char *operands, unsigned long flags));
-static void do_nop             PARAMS ((char *operands, unsigned long flags));
-/* ARM 2 */
-static void do_mul             PARAMS ((char *operands, unsigned long flags));
-static void do_mla             PARAMS ((char *operands, unsigned long flags));
-/* ARM 3 */
-static void do_swap            PARAMS ((char *operands, unsigned long flags));
-/* ARM 6 */
-static void do_msr             PARAMS ((char *operands, unsigned long flags));
-static void do_mrs             PARAMS ((char *operands, unsigned long flags));
-/* ARM 7M */
-static void do_mull            PARAMS ((char *operands, unsigned long flags));
-/* ARM THUMB */
-static void do_bx               PARAMS ((char *operands, unsigned long flags));
-
-/* Coprocessor Instructions */
-static void do_cdp             PARAMS ((char *operands, unsigned long flags));
-static void do_lstc            PARAMS ((char *operands, unsigned long flags));
-static void do_co_reg          PARAMS ((char *operands, unsigned long flags));
-static void do_fp_ctrl         PARAMS ((char *operands, unsigned long flags));
-static void do_fp_ldst         PARAMS ((char *operands, unsigned long flags));
-static void do_fp_ldmstm       PARAMS ((char *operands, unsigned long flags));
-static void do_fp_dyadic       PARAMS ((char *operands, unsigned long flags));
-static void do_fp_monadic      PARAMS ((char *operands, unsigned long flags));
-static void do_fp_cmp          PARAMS ((char *operands, unsigned long flags));
-static void do_fp_from_reg     PARAMS ((char *operands, unsigned long flags));
-static void do_fp_to_reg       PARAMS ((char *operands, unsigned long flags));
-
-static void fix_new_arm                PARAMS ((fragS *frag, int where, 
-                                        short int size, expressionS *exp,
-                                        int pc_rel, int reloc));
-static int arm_reg_parse       PARAMS ((char **ccp));
-static int arm_psr_parse       PARAMS ((char **ccp));
-static void symbol_locate      PARAMS ((symbolS *, CONST char *, segT,
-                                        valueT, fragS *));
+/* Functions called by parser.  */
+/* ARM instructions.  */
+static void do_arit            PARAMS ((char *, unsigned long));
+static void do_cmp             PARAMS ((char *, unsigned long));
+static void do_mov             PARAMS ((char *, unsigned long));
+static void do_ldst            PARAMS ((char *, unsigned long));
+static void do_ldmstm          PARAMS ((char *, unsigned long));
+static void do_branch          PARAMS ((char *, unsigned long));
+static void do_swi             PARAMS ((char *, unsigned long));
+/* Pseudo Op codes.  */
+static void do_adr             PARAMS ((char *, unsigned long));
+static void do_adrl            PARAMS ((char *, unsigned long));
+static void do_nop             PARAMS ((char *, unsigned long));
+/* ARM 2.  */
+static void do_mul             PARAMS ((char *, unsigned long));
+static void do_mla             PARAMS ((char *, unsigned long));
+/* ARM 3.  */
+static void do_swap            PARAMS ((char *, unsigned long));
+/* ARM 6.  */
+static void do_msr             PARAMS ((char *, unsigned long));
+static void do_mrs             PARAMS ((char *, unsigned long));
+/* ARM 7M.  */
+static void do_mull            PARAMS ((char *, unsigned long));
+/* 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));
+static void do_co_reg          PARAMS ((char *, unsigned long));
+static void do_fp_ctrl         PARAMS ((char *, unsigned long));
+static void do_fp_ldst         PARAMS ((char *, unsigned long));
+static void do_fp_ldmstm       PARAMS ((char *, unsigned long));
+static void do_fp_dyadic       PARAMS ((char *, unsigned long));
+static void do_fp_monadic      PARAMS ((char *, unsigned long));
+static void do_fp_cmp          PARAMS ((char *, unsigned long));
+static void do_fp_from_reg     PARAMS ((char *, unsigned long));
+static void do_fp_to_reg       PARAMS ((char *, unsigned long));
+
+static void fix_new_arm                PARAMS ((fragS *, int, short, expressionS *, int, int));
+static int arm_reg_parse       PARAMS ((char **));
+static CONST struct asm_psr * arm_psr_parse PARAMS ((char **));
+static void symbol_locate      PARAMS ((symbolS *, CONST char *, segT, valueT, fragS *));
 static int add_to_lit_pool     PARAMS ((void));
-static unsigned validate_immediate     PARAMS ((unsigned));
-static int validate_offset_imm PARAMS ((int, int));
+static unsigned validate_immediate PARAMS ((unsigned));
+static unsigned validate_immediate_twopart PARAMS ((unsigned int, unsigned int *));
+static int validate_offset_imm PARAMS ((unsigned int, int));
 static void opcode_select      PARAMS ((int));
 static void end_of_line                PARAMS ((char *));
 static int reg_required_here   PARAMS ((char **, int));
-static int psr_required_here   PARAMS ((char **, int, int));
+static int psr_required_here   PARAMS ((char **));
 static int co_proc_number      PARAMS ((char **));
 static int cp_opc_expr         PARAMS ((char **, int, int));
 static int cp_reg_required_here        PARAMS ((char **, int));
@@ -460,8 +664,7 @@ static int cp_address_required_here PARAMS ((char **));
 static int my_get_float_expression     PARAMS ((char **));
 static int skip_past_comma     PARAMS ((char **));
 static int walk_no_bignums     PARAMS ((symbolS *));
-static int negate_data_op      PARAMS ((unsigned long *,
-                                        unsigned long));
+static int negate_data_op      PARAMS ((unsigned long *, unsigned long));
 static int data_op2            PARAMS ((char **));
 static int fp_op2              PARAMS ((char **));
 static long reg_list           PARAMS ((char **));
@@ -475,33 +678,58 @@ static void thumb_mov_compare     PARAMS ((char *, int));
 static void set_constant_flonums       PARAMS ((void));
 static valueT md_chars_to_number       PARAMS ((char *, int));
 static void insert_reg_alias   PARAMS ((char *, int));
-static void output_inst                PARAMS ((char *));
+static void output_inst                PARAMS ((void));
+#ifdef OBJ_ELF
+static bfd_reloc_code_real_type        arm_parse_reloc PARAMS ((void));
+#endif
 
 /* ARM instructions take 4bytes in the object file, Thumb instructions
-   take 2: */
+   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 
+struct asm_opcode
 {
-  CONST char *template;                /* Basic string to match */
-  unsigned long value;         /* Basic instruction code */
-  CONST char *comp_suffix;     /* Compulsory suffix that must follow conds */
-  CONST struct asm_flg *flags; /* Bits to toggle if flag 'n' set */
-  unsigned long variants;      /* Which CPU variants this exists for */
-  /* Function to call to parse args */
-  void (*parms) PARAMS ((char *, unsigned long));
+  /* Basic string to match.  */
+  CONST char * template;
+
+  /* Basic instruction code.  */
+  unsigned long value;
+
+  /* Compulsory suffix that must follow conds.  If "", then the
+     instruction is not conditional and must have no suffix.  */
+  CONST char * comp_suffix;
+
+  /* Bits to toggle if flag 'n' set.  */
+  CONST struct asm_flg * flags;
+
+  /* Which CPU variants this exists for.  */
+  unsigned long variants;
+
+  /* Function to call to parse args.  */
+  void (* parms) PARAMS ((char *, unsigned long));
 };
 
-static CONST struct asm_opcode insns[] = 
-{
-/* ARM Instructions */
+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},
   {"sub",   0x00400000, NULL,   s_flag,      ARM_ANY,      do_arit},
@@ -523,34 +751,43 @@ static CONST struct asm_opcode insns[] =
   {"stm",   0x08000000, NULL,   stm_flags,   ARM_ANY,      do_ldmstm},
   {"ldm",   0x08100000, NULL,   ldm_flags,   ARM_ANY,      do_ldmstm},
   {"swi",   0x0f000000, NULL,   NULL,        ARM_ANY,      do_swi},
+#ifdef TE_WINCE
+  {"bl",    0x0b000000, NULL,   NULL,        ARM_ANY,      do_branch},
+  {"b",     0x0a000000, NULL,   NULL,        ARM_ANY,      do_branch},
+#else
   {"bl",    0x0bfffffe, NULL,   NULL,        ARM_ANY,      do_branch},
   {"b",     0x0afffffe, NULL,   NULL,        ARM_ANY,      do_branch},
+#endif
 
-/* Pseudo ops */
+/* Pseudo ops */
   {"adr",   0x028f0000, NULL,   NULL,        ARM_ANY,      do_adr},
+  {"adrl",  0x028f0000, NULL,   NULL,        ARM_ANY,      do_adrl},
   {"nop",   0x01a00000, NULL,   NULL,        ARM_ANY,      do_nop},
 
-/* ARM 2 multiplies */
+/* ARM 2 multiplies */
   {"mul",   0x00000090, NULL,   s_flag,      ARM_2UP,      do_mul},
   {"mla",   0x00200090, NULL,   s_flag,      ARM_2UP,      do_mla},
 
-/* ARM 3 - swp instructions */
+/* ARM 3 - swp instructions */
   {"swp",   0x01000090, NULL,   byte_flag,   ARM_3UP,      do_swap},
 
-/* ARM 6 Coprocessor instructions */
+/* ARM 6 Coprocessor instructions */
   {"mrs",   0x010f0000, NULL,   NULL,        ARM_6UP,      do_mrs},
   {"msr",   0x0120f000, NULL,   NULL,        ARM_6UP,      do_msr},
+/* ScottB: our code uses 0x0128f000 for msr.
+   NickC:  but this is wrong because the bits 16 through 19 are
+           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},
+/* ARM 7M long multiplies - need signed/unsigned flags!  */
+  {"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},
+/* ARM THUMB interworking */
+  {"bx",    0x012fff10, NULL,   NULL,        ARM_EXT_THUMB,    do_bx},
 
-/* Floating point instructions */
+/* Floating point instructions */
   {"wfs",   0x0e200110, NULL,   NULL,        FPU_ALL,      do_fp_ctrl},
   {"rfs",   0x0e300110, NULL,   NULL,        FPU_ALL,      do_fp_ctrl},
   {"wfc",   0x0e400110, NULL,   NULL,        FPU_ALL,      do_fp_ctrl},
@@ -598,16 +835,57 @@ static CONST struct asm_opcode insns[] =
   {"flt",   0x0e000110, "sde",  round_flags, FPU_ALL,      do_fp_from_reg},
   {"fix",   0x0e100110, NULL,   fix_flags,   FPU_ALL,      do_fp_to_reg},
 
-/* Generic copressor instructions */
+/* Generic copressor instructions */
   {"cdp",   0x0e000000, NULL,  NULL,         ARM_2UP,      do_cdp},
   {"ldc",   0x0c100000, NULL,  cplong_flag,  ARM_2UP,      do_lstc},
   {"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},
-};
 
-/* defines for various bits that we will want to toggle */
+/*  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.  */
 #define INST_IMMEDIATE 0x02000000
 #define OFFSET_REG     0x02000000
 #define HWOFFSET_IMM    0x00400000
@@ -615,15 +893,14 @@ static CONST struct asm_opcode insns[] =
 #define PRE_INDEX      0x01000000
 #define INDEX_UP       0x00800000
 #define WRITE_BACK     0x00200000
-#define MULTI_SET_PSR  0x00400000
+#define LDM_TYPE_2_OR_3        0x00400000
 
 #define LITERAL_MASK   0xf000f000
 #define COND_MASK      0xf0000000
 #define OPCODE_MASK    0xfe1fffff
 #define DATA_OP_SHIFT  21
 
-/* Codes to distinguish the arithmetic instructions */
-
+/* Codes to distinguish the arithmetic instructions.  */
 #define OPCODE_AND     0
 #define OPCODE_EOR     1
 #define OPCODE_SUB     2
@@ -641,30 +918,30 @@ static CONST struct asm_opcode insns[] =
 #define OPCODE_BIC     14
 #define OPCODE_MVN     15
 
-static void do_t_nop           PARAMS ((char *operands));
-static void do_t_arit          PARAMS ((char *operands));
-static void do_t_add           PARAMS ((char *operands));
-static void do_t_asr           PARAMS ((char *operands));
-static void do_t_branch9       PARAMS ((char *operands));
-static void do_t_branch12      PARAMS ((char *operands));
-static void do_t_branch23      PARAMS ((char *operands));
-static void do_t_bx            PARAMS ((char *operands));
-static void do_t_compare       PARAMS ((char *operands));
-static void do_t_ldmstm                PARAMS ((char *operands));
-static void do_t_ldr           PARAMS ((char *operands));
-static void do_t_ldrb          PARAMS ((char *operands));
-static void do_t_ldrh          PARAMS ((char *operands));
-static void do_t_lds           PARAMS ((char *operands));
-static void do_t_lsl           PARAMS ((char *operands));
-static void do_t_lsr           PARAMS ((char *operands));
-static void do_t_mov           PARAMS ((char *operands));
-static void do_t_push_pop      PARAMS ((char *operands));
-static void do_t_str           PARAMS ((char *operands));
-static void do_t_strb          PARAMS ((char *operands));
-static void do_t_strh          PARAMS ((char *operands));
-static void do_t_sub           PARAMS ((char *operands));
-static void do_t_swi           PARAMS ((char *operands));
-static void do_t_adr           PARAMS ((char *operands));
+static void do_t_nop           PARAMS ((char *));
+static void do_t_arit          PARAMS ((char *));
+static void do_t_add           PARAMS ((char *));
+static void do_t_asr           PARAMS ((char *));
+static void do_t_branch9       PARAMS ((char *));
+static void do_t_branch12      PARAMS ((char *));
+static void do_t_branch23      PARAMS ((char *));
+static void do_t_bx            PARAMS ((char *));
+static void do_t_compare       PARAMS ((char *));
+static void do_t_ldmstm                PARAMS ((char *));
+static void do_t_ldr           PARAMS ((char *));
+static void do_t_ldrb          PARAMS ((char *));
+static void do_t_ldrh          PARAMS ((char *));
+static void do_t_lds           PARAMS ((char *));
+static void do_t_lsl           PARAMS ((char *));
+static void do_t_lsr           PARAMS ((char *));
+static void do_t_mov           PARAMS ((char *));
+static void do_t_push_pop      PARAMS ((char *));
+static void do_t_str           PARAMS ((char *));
+static void do_t_strb          PARAMS ((char *));
+static void do_t_strh          PARAMS ((char *));
+static void do_t_sub           PARAMS ((char *));
+static void do_t_swi           PARAMS ((char *));
+static void do_t_adr           PARAMS ((char *));
 
 #define T_OPCODE_MUL 0x4340
 #define T_OPCODE_TST 0x4200
@@ -718,9 +995,9 @@ static void do_t_adr                PARAMS ((char *operands));
 
 #define T_OPCODE_BRANCH 0xe7fe
 
-static int thumb_reg           PARAMS ((char **str, int hi_lo));
+static int thumb_reg           PARAMS ((char ** str, int hi_lo));
 
-#define THUMB_SIZE     2       /* Size of thumb instruction */
+#define THUMB_SIZE     2       /* Size of thumb instruction */
 #define THUMB_REG_LO   0x1
 #define THUMB_REG_HI   0x2
 #define THUMB_REG_ANY  0x3
@@ -740,85 +1017,96 @@ static int thumb_reg             PARAMS ((char **str, int hi_lo));
 
 #define THUMB_PP_PC_LR 0x0100
 
-/* These three are used for immediate shifts, do not alter */
+/* These three are used for immediate shifts, do not alter */
 #define THUMB_WORD 2
 #define THUMB_HALFWORD 1
 #define THUMB_BYTE 0
 
-struct thumb_opcode 
+struct thumb_opcode
 {
-  CONST char *template;                /* Basic string to match */
-  unsigned long value;         /* Basic instruction code */
+  /* Basic string to match.  */
+  CONST char * template;
+
+  /* Basic instruction code.  */
+  unsigned long value;
+
   int size;
-  /* Function to call to parse args */
-  void (*parms) PARAMS ((char *));
+
+  /* Which CPU variants this exists for.  */
+  unsigned long variants;
+
+  /* Function to call to parse args.  */
+  void (* parms) PARAMS ((char *));
 };
 
 static CONST struct thumb_opcode tinsns[] =
 {
-  {"adc",      0x4140,         2,      do_t_arit},
-  {"add",      0x0000,         2,      do_t_add},
-  {"and",      0x4000,         2,      do_t_arit},
-  {"asr",      0x0000,         2,      do_t_asr},
-  {"b",                T_OPCODE_BRANCH, 2,     do_t_branch12},
-  {"beq",      0xd0fe,         2,      do_t_branch9},
-  {"bne",      0xd1fe,         2,      do_t_branch9},
-  {"bcs",      0xd2fe,         2,      do_t_branch9},
-  {"bhs",      0xd2fe,         2,      do_t_branch9},
-  {"bcc",      0xd3fe,         2,      do_t_branch9},
-  {"bul",      0xd3fe,         2,      do_t_branch9},
-  {"blo",      0xd3fe,         2,      do_t_branch9},
-  {"bmi",      0xd4fe,         2,      do_t_branch9},
-  {"bpl",      0xd5fe,         2,      do_t_branch9},
-  {"bvs",      0xd6fe,         2,      do_t_branch9},
-  {"bvc",      0xd7fe,         2,      do_t_branch9},
-  {"bhi",      0xd8fe,         2,      do_t_branch9},
-  {"bls",      0xd9fe,         2,      do_t_branch9},
-  {"bge",      0xdafe,         2,      do_t_branch9},
-  {"blt",      0xdbfe,         2,      do_t_branch9},
-  {"bgt",      0xdcfe,         2,      do_t_branch9},
-  {"ble",      0xddfe,         2,      do_t_branch9},
-  {"bic",      0x4380,         2,      do_t_arit},
-  {"bl",       0xf7fffffe,     4,      do_t_branch23},
-  {"bx",       0x4700,         2,      do_t_bx},
-  {"cmn",      T_OPCODE_CMN,   2,      do_t_arit},
-  {"cmp",      0x0000,         2,      do_t_compare},
-  {"eor",      0x4040,         2,      do_t_arit},
-  {"ldmia",    0xc800,         2,      do_t_ldmstm},
-  {"ldr",      0x0000,         2,      do_t_ldr},
-  {"ldrb",     0x0000,         2,      do_t_ldrb},
-  {"ldrh",     0x0000,         2,      do_t_ldrh},
-  {"ldrsb",    0x5600,         2,      do_t_lds},
-  {"ldrsh",    0x5e00,         2,      do_t_lds},
-  {"ldsb",     0x5600,         2,      do_t_lds},
-  {"ldsh",     0x5e00,         2,      do_t_lds},
-  {"lsl",      0x0000,         2,      do_t_lsl},
-  {"lsr",      0x0000,         2,      do_t_lsr},
-  {"mov",      0x0000,         2,      do_t_mov},
-  {"mul",      T_OPCODE_MUL,   2,      do_t_arit},
-  {"mvn",      T_OPCODE_MVN,   2,      do_t_arit},
-  {"neg",      T_OPCODE_NEG,   2,      do_t_arit},
-  {"orr",      0x4300,         2,      do_t_arit},
-  {"pop",      0xbc00,         2,      do_t_push_pop},
-  {"push",     0xb400,         2,      do_t_push_pop},
-  {"ror",      0x41c0,         2,      do_t_arit},
-  {"sbc",      0x4180,         2,      do_t_arit},
-  {"stmia",    0xc000,         2,      do_t_ldmstm},
-  {"str",      0x0000,         2,      do_t_str},
-  {"strb",     0x0000,         2,      do_t_strb},
-  {"strh",     0x0000,         2,      do_t_strh},
-  {"swi",      0xdf00,         2,      do_t_swi},
-  {"sub",      0x0000,         2,      do_t_sub},
-  {"tst",      T_OPCODE_TST,   2,      do_t_arit},
-  /* Pseudo ops: */
-  {"adr",       0x0000,         2,      do_t_adr},
-  {"nop",       0x46C0,         2,      do_t_nop},      /* mov r8,r8 */
+  {"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_EXT_THUMB, do_t_adr},
+  {"nop",       0x46C0,         2,      ARM_EXT_THUMB, do_t_nop},      /* mov r8,r8  */
 };
 
 struct reg_entry
 {
-  CONST char *name;
-  int number;
+  CONST char * name;
+  int          number;
 };
 
 #define int_register(reg) ((reg) >= 0 && (reg) <= 15)
@@ -829,20 +1117,22 @@ struct reg_entry
 #define REG_LR  14
 #define REG_SP  13
 
-/* These are the standard names;  Users can add aliases with .req */
+/* These are the standard names.  Users can add aliases with .req.  */
 static CONST struct reg_entry reg_table[] =
 {
-  /* Processor Register Numbers */
+  /* Processor Register Numbers */
   {"r0", 0},    {"r1", 1},      {"r2", 2},      {"r3", 3},
   {"r4", 4},    {"r5", 5},      {"r6", 6},      {"r7", 7},
   {"r8", 8},    {"r9", 9},      {"r10", 10},    {"r11", 11},
   {"r12", 12},  {"r13", REG_SP},{"r14", REG_LR},{"r15", REG_PC},
-  /* APCS conventions */
+  /* APCS conventions */
   {"a1", 0},   {"a2", 1},    {"a3", 2},     {"a4", 3},
   {"v1", 4},   {"v2", 5},    {"v3", 6},     {"v4", 7},     {"v5", 8},
   {"v6", 9},   {"sb", 9},    {"v7", 10},    {"sl", 10},
   {"fp", 11},  {"ip", 12},   {"sp", REG_SP},{"lr", REG_LR},{"pc", REG_PC},
-  /* FP Registers */
+  /* ATPCS additions to APCS conventions.  */
+  {"wr", 7},    {"v8", 11},
+  /* FP Registers.  */
   {"f0", 16},   {"f1", 17},   {"f2", 18},   {"f3", 19},
   {"f4", 20},   {"f5", 21},   {"f6", 22},   {"f7", 23},
   {"c0", 32},   {"c1", 33},   {"c2", 34},   {"c3", 35},
@@ -853,25 +1143,34 @@ static CONST struct reg_entry reg_table[] =
   {"cr4", 36},  {"cr5", 37},  {"cr6", 38},  {"cr7", 39},
   {"cr8", 40},  {"cr9", 41},  {"cr10", 42}, {"cr11", 43},
   {"cr12", 44}, {"cr13", 45}, {"cr14", 46}, {"cr15", 47},
+  /* ATPCS additions to float register names.  */
+  {"s0",16},   {"s1",17},      {"s2",18},      {"s3",19},
+  {"s4",20},   {"s5",21},      {"s6",22},      {"s7",23},
+  {"d0",16},   {"d1",17},      {"d2",18},      {"d3",19},
+  {"d4",20},   {"d5",21},      {"d6",22},      {"d7",23},
+  /* FIXME: At some point we need to add VFP register names.  */
+  /* Array terminator.  */
   {NULL, 0}
 };
 
-#define bad_args _("Bad arguments to instruction");
-#define bad_pc _("r15 not allowed here");
+#define BAD_ARGS       _("Bad arguments to instruction")
+#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;
-static struct hash_control *arm_cond_hsh = NULL;
-static struct hash_control *arm_shift_hsh = NULL;
-static struct hash_control *arm_reg_hsh = NULL;
-static struct hash_control *arm_psr_hsh = NULL;
+static struct hash_control * arm_ops_hsh   = NULL;
+static struct hash_control * arm_tops_hsh  = NULL;
+static struct hash_control * arm_cond_hsh  = NULL;
+static struct hash_control * arm_shift_hsh = NULL;
+static struct hash_control * arm_reg_hsh   = NULL;
+static struct hash_control * arm_psr_hsh   = NULL;
 
 /* This table describes all the machine specific pseudo-ops the assembler
    has to support.  The fields are:
-   pseudo-op name without dot
-   function to call to execute this pseudo-op
-   Integer arg to pass to the function
-   */
+     pseudo-op name without dot
+     function to call to execute this pseudo-op
+     Integer arg to pass to the function.  */
 
 static void s_req PARAMS ((int));
 static void s_align PARAMS ((int));
@@ -883,27 +1182,50 @@ static void s_thumb PARAMS ((int));
 static void s_code PARAMS ((int));
 static void s_force_thumb PARAMS ((int));
 static void s_thumb_func PARAMS ((int));
+static void s_thumb_set PARAMS ((int));
+static void arm_s_text PARAMS ((int));
+static void arm_s_data PARAMS ((int));
+#ifdef OBJ_ELF
+static void arm_s_section PARAMS ((int));
+static void s_arm_elf_cons PARAMS ((int));
+#endif
 
 static int my_get_expression PARAMS ((expressionS *, char **));
 
 CONST pseudo_typeS md_pseudo_table[] =
 {
-  {"req", s_req, 0},   /* Never called becasue '.req' does not start line */
-  {"bss", s_bss, 0},
-  {"align", s_align, 0},
-  {"arm", s_arm, 0},
-  {"thumb", s_thumb, 0},
-  {"code", s_code, 0},
-  {"force_thumb", s_force_thumb, 0},
-  {"thumb_func", s_thumb_func, 0},
-  {"even", s_even, 0},
-  {"ltorg", s_ltorg, 0},
-  {"pool", s_ltorg, 0},
-  {"word", cons, 4},
-  {"extend", float_cons, 'x'},
-  {"ldouble", float_cons, 'x'},
-  {"packed", float_cons, 'p'},
-  {0, 0, 0}
+  /* Never called becasue '.req' does not start line.  */
+  { "req",         s_req,         0 },
+  { "bss",         s_bss,         0 },
+  { "align",       s_align,       0 },
+  { "arm",         s_arm,         0 },
+  { "thumb",       s_thumb,       0 },
+  { "code",        s_code,        0 },
+  { "force_thumb", s_force_thumb, 0 },
+  { "thumb_func",  s_thumb_func,  0 },
+  { "thumb_set",   s_thumb_set,   0 },
+  { "even",        s_even,        0 },
+  { "ltorg",       s_ltorg,       0 },
+  { "pool",        s_ltorg,       0 },
+  /* Allow for the effect of section changes.  */
+  { "text",        arm_s_text,    0 },
+  { "data",        arm_s_data,    0 },
+#ifdef OBJ_ELF
+  { "section",     arm_s_section, 0 },
+  { "section.s",   arm_s_section, 0 },
+  { "sect",        arm_s_section, 0 },
+  { "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
+  { "extend",      float_cons, 'x' },
+  { "ldouble",     float_cons, 'x' },
+  { "packed",      float_cons, 'p' },
+  { 0, 0, 0 }
 };
 
 /* Stuff needed to resolve the label ambiguity
@@ -919,21 +1241,25 @@ CONST pseudo_typeS md_pseudo_table[] =
 symbolS *  last_label_seen;
 static int label_is_thumb_function_name = false;
 
-/* Literal stuff */
+/* Literal stuff */
 
 #define MAX_LITERAL_POOL_SIZE 1024
 
 typedef struct literalS
 {
-  struct expressionS  exp;
-  struct arm_it      *inst;
+  struct expressionS exp;
+  struct arm_it *    inst;
 } literalT;
 
 literalT literals[MAX_LITERAL_POOL_SIZE];
-int next_literal_pool_place = 0; /* Next free entry in the pool */
-int lit_pool_num = 1; /* Next literal pool number */
-symbolS *current_poolP = NULL;
-symbolS *symbol_make_empty PARAMS ((void)); 
+
+/* Next free entry in the pool.  */
+int next_literal_pool_place = 0;
+
+/* Next literal pool number.  */
+int lit_pool_num = 1;
+
+symbolS * current_poolP = NULL;
 
 static int
 add_to_lit_pool ()
@@ -941,52 +1267,55 @@ add_to_lit_pool ()
   int lit_count = 0;
 
   if (current_poolP == NULL)
-    current_poolP = symbol_make_empty();
+    current_poolP = symbol_create (FAKE_LABEL_NAME, undefined_section,
+                                  (valueT) 0, &zero_address_frag);
 
-  /* Check if this literal value is already in the pool: */
+  /* Check if this literal value is already in the pool:  */
   while (lit_count < next_literal_pool_place)
     {
       if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op
-          && inst.reloc.exp.X_op == O_constant
-          && literals[lit_count].exp.X_add_number == inst.reloc.exp.X_add_number
-          && literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned)
-        break;
+         && inst.reloc.exp.X_op == O_constant
+         && (literals[lit_count].exp.X_add_number
+             == inst.reloc.exp.X_add_number)
+         && literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned)
+       break;
       lit_count++;
     }
 
-  if (lit_count == next_literal_pool_place) /* new entry */
+  if (lit_count == next_literal_pool_place) /* New entry.  */
     {
       if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
-        {
-          inst.error = _("Literal Pool Overflow");
-          return FAIL;
-        }
+       {
+         inst.error = _("Literal Pool Overflow");
+         return FAIL;
+       }
 
       literals[next_literal_pool_place].exp = inst.reloc.exp;
       lit_count = next_literal_pool_place++;
     }
 
   inst.reloc.exp.X_op = O_symbol;
-  inst.reloc.exp.X_add_number = (lit_count)*4-8;
+  inst.reloc.exp.X_add_number = (lit_count) * 4 - 8;
   inst.reloc.exp.X_add_symbol = current_poolP;
 
   return SUCCESS;
 }
+
 /* Can't use symbol_new here, so have to create a symbol and then at
-   a later date assign it a value. Thats what these functions do */
+   a later date assign it a value. Thats what these functions do.  */
+
 static void
 symbol_locate (symbolP, name, segment, valu, frag)
-     symbolS *symbolP; 
-     CONST char *name;         /* It is copied, the caller can modify */
-     segT segment;             /* Segment identifier (SEG_<something>) */
-     valueT valu;              /* Symbol value */
-     fragS *frag;              /* Associated fragment */
+     symbolS *    symbolP;
+     CONST char * name;                /* It is copied, the caller can modify.  */
+     segT         segment;     /* Segment identifier (SEG_<something>).  */
+     valueT       valu;                /* Symbol value.  */
+     fragS *      frag;                /* Associated fragment.  */
 {
   unsigned int name_length;
-  char *preserved_copy_of_name;
+  char * preserved_copy_of_name;
 
-  name_length = strlen (name) + 1;      /* +1 for \0 */
+  name_length = strlen (name) + 1;   /* +1 for \0.  */
   obstack_grow (&notes, name, name_length);
   preserved_copy_of_name = obstack_finish (&notes);
 #ifdef STRIP_UNDERSCORE
@@ -1005,128 +1334,143 @@ symbol_locate (symbolP, name, segment, valu, frag)
   S_SET_VALUE (symbolP, valu);
   symbol_clear_list_pointers(symbolP);
 
-  symbolP->sy_frag = frag;
+  symbol_set_frag (symbolP, frag);
 
-  /*
-   * Link to end of symbol chain.
-   */
+  /* Link to end of symbol chain.  */
   {
     extern int symbol_table_frozen;
     if (symbol_table_frozen)
       abort ();
   }
 
-  symbol_append (symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP);
+  symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
 
   obj_symbol_new_hook (symbolP);
 
 #ifdef tc_symbol_new_hook
   tc_symbol_new_hook (symbolP);
 #endif
+
 #ifdef DEBUG_SYMS
-  verify_symbol_chain(symbol_rootP, symbol_lastP);
-#endif /* DEBUG_SYMS */
+  verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif /* DEBUG_SYMS  */
 }
 
-symbolS *
-symbol_make_empty () 
-{
-  symbolS *symbolP; 
+/* Check that an immediate is valid.
+   If so, convert it to the right format.  */
 
-  symbolP = (symbolS *) obstack_alloc (&notes, sizeof (symbolS));
+static unsigned int
+validate_immediate (val)
+     unsigned int val;
+{
+  unsigned int a;
+  unsigned int i;
 
-  /* symbol must be born in some fixed state.  This seems as good as any. */
-  memset (symbolP, 0, sizeof (symbolS));
+#define rotate_left(v, n) (v << n | v >> (32 - n))
 
-  symbolP->bsym = bfd_make_empty_symbol (stdoutput);
-  assert (symbolP->bsym != 0);
-  symbolP->bsym->udata.p = (PTR) symbolP;
+  for (i = 0; i < 32; i += 2)
+    if ((a = rotate_left (val, i)) <= 0xff)
+      return a | (i << 7); /* 12-bit pack: [shift-cnt,const].  */
 
-  return symbolP;
+  return FAIL;
 }
 
-/* Check that an immediate is valid, and if so, convert it to the right format.  */
+/* Check to see if an immediate can be computed as two seperate immediate
+   values, added together.  We already know that this value cannot be
+   computed by just one ARM instruction.  */
 
 static unsigned int
-validate_immediate (val)
-     unsigned int val;
+validate_immediate_twopart (val, highpart)
+     unsigned int   val;
+     unsigned int * highpart;
 {
-    unsigned int a;
-    unsigned int i;
+  unsigned int a;
+  unsigned int i;
 
-#define rotate_left(v, n) (v << n | v >> (32 - n))
-    
-    for (i = 0; i < 32; i += 2)
-        if ((a = rotate_left (val, i)) <= 0xff)
-            return a | (i << 7); /* 12-bit pack: [shift-cnt,const] */
-    return FAIL;
+  for (i = 0; i < 32; i += 2)
+    if (((a = rotate_left (val, i)) & 0xff) != 0)
+      {
+       if (a & 0xff00)
+         {
+           if (a & ~ 0xffff)
+             continue;
+           * highpart = (a  >> 8) | ((i + 24) << 7);
+         }
+       else if (a & 0xff0000)
+         {
+           if (a & 0xff000000)
+             continue;
+           * highpart = (a >> 16) | ((i + 16) << 7);
+         }
+       else
+         {
+           assert (a & 0xff000000);
+           * highpart = (a >> 24) | ((i + 8) << 7);
+         }
+
+       return (a & 0xff) | (i << 7);
+      }
+
+  return FAIL;
 }
 
 static int
 validate_offset_imm (val, hwse)
-     int val;
+     unsigned int val;
      int hwse;
 {
-  if ((hwse && (val < -255 || val > 255))
-      || (val < -4095 || val > 4095))
-     return FAIL;
+  if ((hwse && val > 255) || val > 4095)
+    return FAIL;
   return val;
 }
 
-    
 static void
 s_req (a)
-     int a;
+     int a ATTRIBUTE_UNUSED;
 {
   as_bad (_("Invalid syntax for .req directive."));
 }
 
 static void
 s_bss (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
   /* We don't support putting frags in the BSS segment, we fake it by
-     marking in_bss, then looking at s_skip for clues?.. */
+     marking in_bss, then looking at s_skip for clues */
   subseg_set (bss_section, 0);
   demand_empty_rest_of_line ();
 }
 
 static void
 s_even (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
-  if (!need_pass_2)            /* Never make frag if expect extra pass. */
+  /* Never make frag if expect extra pass.  */
+  if (!need_pass_2)
     frag_align (1, 0, 0);
+
   record_alignment (now_seg, 1);
+
   demand_empty_rest_of_line ();
 }
 
 static void
-s_ltorg (internal)
-     int internal;
+s_ltorg (ignored)
+     int ignored ATTRIBUTE_UNUSED;
 {
   int lit_count = 0;
   char sym_name[20];
 
   if (current_poolP == NULL)
-    {
-      /* Nothing to do */
-      if (!internal)
-       as_tsktsk (_("Nothing to put in the pool\n"));
-      return;
-    }
+    return;
 
-  /* Align pool as you have word accesses */
-  /* Only make a frag if we have to ... */
+  /* Align pool as you have word accesses.
+     Only make a frag if we have to.  */
   if (!need_pass_2)
     frag_align (2, 0, 0);
 
   record_alignment (now_seg, 2);
 
-  if (internal)
-    as_tsktsk (_("Inserting implicit pool at change of section"));
-
   sprintf (sym_name, "$$lit_\002%x", lit_pool_num++);
 
   symbol_locate (current_poolP, sym_name, now_seg,
@@ -1134,35 +1478,24 @@ s_ltorg (internal)
   symbol_table_insert (current_poolP);
 
   ARM_SET_THUMB (current_poolP, thumb_mode);
+
 #if defined OBJ_COFF || defined OBJ_ELF
   ARM_SET_INTERWORK (current_poolP, support_interwork);
 #endif
-  
+
   while (lit_count < next_literal_pool_place)
-    /* First output the expression in the instruction to the pool */
-    emit_expr (&(literals[lit_count++].exp), 4); /* .word */
+    /* First output the expression in the instruction to the pool */
+    emit_expr (&(literals[lit_count++].exp), 4); /* .word  */
 
   next_literal_pool_place = 0;
   current_poolP = NULL;
 }
 
-#if 0 /* not used */
-static void
-arm_align (power, fill)
-     int power;
-     int fill;
-{
-  /* Only make a frag if we HAVE to ... */
-  if (power && !need_pass_2)
-    frag_align (power, fill, 0);
-
-  record_alignment (now_seg, power);
-}
-#endif
+/* Same as s_align_ptwo but align 0 => align 2.  */
 
 static void
-s_align (unused)       /* Same as s_align_ptwo but align 0 => align 2 */
-     int unused;
+s_align (unused)
+     int unused ATTRIBUTE_UNUSED;
 {
   register int temp;
   register long temp_fill;
@@ -1188,7 +1521,7 @@ s_align (unused)  /* Same as s_align_ptwo but align 0 => align 2 */
   if (!temp)
     temp = 2;
 
-  /* Only make a frag if we HAVE to. . . */
+  /* Only make a frag if we HAVE to.  */
   if (temp && !need_pass_2)
     frag_align (temp, (int) temp_fill, 0);
   demand_empty_rest_of_line ();
@@ -1198,35 +1531,175 @@ s_align (unused)       /* Same as s_align_ptwo but align 0 => align 2 */
 
 static void
 s_force_thumb (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
   /* If we are not already in thumb mode go into it, EVEN if
      the target processor does not support thumb instructions.
      This is used by gcc/config/arm/lib1funcs.asm for example
      to compile interworking support functions even if the
      target processor should not support interworking.  */
-     
   if (! thumb_mode)
     {
-      thumb_mode = 1;
-      
+      thumb_mode = 2;
+
       record_alignment (now_seg, 1);
     }
-  
+
   demand_empty_rest_of_line ();
 }
 
 static void
 s_thumb_func (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
+  if (! thumb_mode)
+    opcode_select (16);
+
   /* The following label is the name/address of the start of a Thumb function.
      We need to know this for the interworking support.  */
-
   label_is_thumb_function_name = true;
-  
-  demand_empty_rest_of_line();
+
+  demand_empty_rest_of_line ();
+}
+
+/* Perform a .set directive, but also mark the alias as
+   being a thumb function.  */
+
+static void
+s_thumb_set (equiv)
+     int equiv;
+{
+  /* XXX the following is a duplicate of the code for s_set() in read.c
+     We cannot just call that code as we need to get at the symbol that
+     is created.  */
+  register char *    name;
+  register char      delim;
+  register char *    end_name;
+  register symbolS * symbolP;
+
+  /* Especial apologies for the random logic:
+     This just grew, and could be parsed much more simply!
+     Dean - in haste.  */
+  name      = input_line_pointer;
+  delim     = get_symbol_end ();
+  end_name  = input_line_pointer;
+  *end_name = delim;
+
+  SKIP_WHITESPACE ();
+
+  if (*input_line_pointer != ',')
+    {
+      *end_name = 0;
+      as_bad (_("Expected comma after name \"%s\""), name);
+      *end_name = delim;
+      ignore_rest_of_line ();
+      return;
+    }
+
+  input_line_pointer++;
+  *end_name = 0;
+
+  if (name[0] == '.' && name[1] == '\0')
+    {
+      /* XXX - this should not happen to .thumb_set.  */
+      abort ();
+    }
+
+  if ((symbolP = symbol_find (name)) == NULL
+      && (symbolP = md_undefined_symbol (name)) == NULL)
+    {
+#ifndef NO_LISTING
+      /* When doing symbol listings, play games with dummy fragments living
+        outside the normal fragment chain to record the file and line info
+         for this symbol.  */
+      if (listing & LISTING_SYMBOLS)
+       {
+         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;
+         symbolP = symbol_new (name, undefined_section, 0, dummy_frag);
+         dummy_frag->fr_symbol = symbolP;
+       }
+      else
+#endif
+       symbolP = symbol_new (name, undefined_section, 0, &zero_address_frag);
+
+#ifdef OBJ_COFF
+      /* "set" symbols are local unless otherwise specified.  */
+      SF_SET_LOCAL (symbolP);
+#endif /* OBJ_COFF  */
+    }                          /* Make a new symbol.  */
+
+  symbol_table_insert (symbolP);
+
+  * end_name = delim;
+
+  if (equiv
+      && S_IS_DEFINED (symbolP)
+      && S_GET_SEGMENT (symbolP) != reg_section)
+    as_bad (_("symbol `%s' already defined"), S_GET_NAME (symbolP));
+
+  pseudo_set (symbolP);
+
+  demand_empty_rest_of_line ();
+
+  /* XXX Now we come to the Thumb specific bit of code.  */
+
+  THUMB_SET_FUNC (symbolP, 1);
+  ARM_SET_THUMB (symbolP, 1);
+#if defined OBJ_ELF || defined OBJ_COFF
+  ARM_SET_INTERWORK (symbolP, support_interwork);
+#endif
+}
+
+/* If we change section we must dump the literal pool first.  */
+
+static void
+arm_s_text (ignore)
+     int ignore;
+{
+  if (now_seg != text_section)
+    s_ltorg (0);
+
+#ifdef OBJ_ELF
+  obj_elf_text (ignore);
+#else
+  s_text (ignore);
+#endif
+}
+
+static void
+arm_s_data (ignore)
+     int ignore;
+{
+  if (flag_readonly_data_in_text)
+    {
+      if (now_seg != text_section)
+       s_ltorg (0);
+    }
+  else if (now_seg != data_section)
+    s_ltorg (0);
+
+#ifdef OBJ_ELF
+  obj_elf_data (ignore);
+#else
+  s_data (ignore);
+#endif
+}
+
+#ifdef OBJ_ELF
+static void
+arm_s_section (ignore)
+     int ignore;
+{
+  s_ltorg (0);
+
+  obj_elf_section (ignore);
 }
+#endif
 
 static void
 opcode_select (width)
@@ -1237,23 +1710,27 @@ opcode_select (width)
     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;
-          /* No need to force the alignment, since we will have been
-             coming from ARM mode, which is word-aligned. */
-          record_alignment (now_seg, 1);
+         /* No need to force the alignment, since we will have been
+             coming from ARM mode, which is word-aligned.  */
+         record_alignment (now_seg, 1);
        }
       break;
 
     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;
-          if (!need_pass_2)
+
+         if (!need_pass_2)
             frag_align (2, 0, 0);
+
           record_alignment (now_seg, 1);
        }
       break;
@@ -1265,7 +1742,7 @@ opcode_select (width)
 
 static void
 s_arm (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
   opcode_select (32);
   demand_empty_rest_of_line ();
@@ -1273,7 +1750,7 @@ s_arm (ignore)
 
 static void
 s_thumb (ignore)
-     int ignore;
+     int ignore ATTRIBUTE_UNUSED;
 {
   opcode_select (16);
   demand_empty_rest_of_line ();
@@ -1281,7 +1758,7 @@ s_thumb (ignore)
 
 static void
 s_code (unused)
-     int unused;
+     int unused ATTRIBUTE_UNUSED;
 {
   register int temp;
 
@@ -1290,7 +1767,7 @@ s_code (unused)
     {
     case 16:
     case 32:
-      opcode_select(temp);
+      opcode_select (temp);
       break;
 
     default:
@@ -1300,22 +1777,21 @@ s_code (unused)
 
 static void
 end_of_line (str)
-     char *str;
+     char * str;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
-  if (*str != '\0')
+  if (* str != '\0')
     inst.error = _("Garbage following instruction");
 }
 
 static int
 skip_past_comma (str)
-     char **str;
+     char ** str;
 {
-  char *p = *str, c;
+  char * p = * str, c;
   int comma = 0;
-    
+
   while ((c = *p) == ' ' || c == ',')
     {
       p++;
@@ -1330,17 +1806,19 @@ skip_past_comma (str)
   return comma ? SUCCESS : FAIL;
 }
 
-/* A standard register must be given at this point.  Shift is the place to
-   put it in the instruction. */
+/* A standard register must be given at this point.
+   SHIFT is the place to put it in inst.instruction.
+   Restores input start point on error.
+   Returns the reg#, or FAIL.  */
 
 static int
 reg_required_here (str, shift)
-     char **str;
-     int shift;
+     char ** str;
+     int     shift;
 {
-  static char buff [128]; /* XXX */
-  int reg;
-  char *start = *str;
+  static char buff [128]; /* XXX  */
+  int         reg;
+  char *      start = * str;
 
   if ((reg = arm_reg_parse (str)) != FAIL && int_register (reg))
     {
@@ -1351,36 +1829,82 @@ reg_required_here (str, shift)
 
   /* Restore the start point, we may have got a reg of the wrong class.  */
   *str = start;
-  
+
   /* In the few cases where we might be able to accept something else
-     this error can be overridden */
+     this error can be overridden */
   sprintf (buff, _("Register expected, not '%.100s'"), start);
   inst.error = buff;
 
   return FAIL;
 }
 
+static CONST struct asm_psr *
+arm_psr_parse (ccp)
+     register char ** ccp;
+{
+  char * start = * ccp;
+  char   c;
+  char * p;
+  CONST struct asm_psr * psr;
+
+  p = start;
+
+  /* Skip to the end of the next word in the input stream.  */
+  do
+    {
+      c = *p++;
+    }
+  while (isalpha (c) || c == '_');
+
+  /* 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);
+
+  /* Restore the input stream.  */
+  *p = c;
+
+  /* If we found a valid match, advance the
+     stream pointer past the end of the word.  */
+  *ccp = p;
+
+  return psr;
+}
+
+/* Parse the input looking for a PSR flag.  */
+
 static int
-psr_required_here (str, cpsr, spsr)
+psr_required_here (str)
      char ** str;
-     int     cpsr;
-     int     spsr;
 {
-  int    psr;
-  char * start = *str;
+  char * start = * str;
+  CONST struct asm_psr * psr;
+
   psr = arm_psr_parse (str);
-  
-  if  (psr == cpsr || psr == spsr)
+
+  if (psr)
     {
-      if (psr == spsr)
-       inst.instruction |= 1 << 22;
-      
+      /* If this is the SPSR that is being modified, set the R bit.  */
+      if (! psr->cpsr)
+       inst.instruction |= SPSR_BIT;
+
+      /* Set the psr flags in the MSR instruction.  */
+      inst.instruction |= psr->field << PSR_SHIFT;
+
       return SUCCESS;
     }
 
-  /* In the few cases where we might be able to accept something else
-     this error can be overridden */
-  inst.error = _("<psr(f)> expected");
+  /* In the few cases where we might be able to accept
+     something else this error can be overridden.  */
+  inst.error = _("flag for {c}psr instruction expected");
 
   /* Restore the start point.  */
   *str = start;
@@ -1389,12 +1913,11 @@ psr_required_here (str, cpsr, spsr)
 
 static int
 co_proc_number (str)
-     char **str;
+     char ** str;
 {
   int processor, pchar;
 
-  while (**str == ' ')
-    (*str)++;
+  skip_whitespace (* str);
 
   /* The data sheet seems to imply that just a number on its own is valid
      here, but the RISC iX assembler seems to accept a prefix 'p'.  We will
@@ -1428,14 +1951,13 @@ co_proc_number (str)
 
 static int
 cp_opc_expr (str, where, length)
-     char **str;
+     char ** str;
      int where;
      int length;
 {
   expressionS expr;
 
-  while (**str == ' ')
-    (*str)++;
+  skip_whitespace (* str);
 
   memset (&expr, '\0', sizeof (expr));
 
@@ -1459,11 +1981,11 @@ cp_opc_expr (str, where, length)
 
 static int
 cp_reg_required_here (str, where)
-     char **str;
-     int where;
+     char ** str;
+     int     where;
 {
-  int reg;
-  char *start = *str;
+  int    reg;
+  char * start = *str;
 
   if ((reg = arm_reg_parse (str)) != FAIL && cp_register (reg))
     {
@@ -1473,21 +1995,21 @@ cp_reg_required_here (str, where)
     }
 
   /* In the few cases where we might be able to accept something else
-     this error can be overridden */
+     this error can be overridden */
   inst.error = _("Co-processor register expected");
 
-  /* Restore the start point */
+  /* Restore the start point */
   *str = start;
   return FAIL;
 }
 
 static int
 fp_reg_required_here (str, where)
-     char **str;
-     int where;
+     char ** str;
+     int     where;
 {
-  int reg;
-  char *start = *str;
+  int    reg;
+  char * start = * str;
 
   if ((reg = arm_reg_parse (str)) != FAIL && fp_register (reg))
     {
@@ -1497,35 +2019,37 @@ fp_reg_required_here (str, where)
     }
 
   /* In the few cases where we might be able to accept something else
-     this error can be overridden */
+     this error can be overridden */
   inst.error = _("Floating point register expected");
 
-  /* Restore the start point */
+  /* Restore the start point */
   *str = start;
   return FAIL;
 }
 
 static int
 cp_address_offset (str)
-     char **str;
+     char ** str;
 {
   int offset;
 
-  while (**str == ' ')
-    (*str)++;
+  skip_whitespace (* str);
 
-  if (**str != '#')
+  if (! is_immediate_prefix (**str))
     {
       inst.error = _("immediate expression expected");
       return FAIL;
     }
 
   (*str)++;
-  if (my_get_expression (&inst.reloc.exp, str))
+
+  if (my_get_expression (& inst.reloc.exp, str))
     return FAIL;
+
   if (inst.reloc.exp.X_op == O_constant)
     {
       offset = inst.reloc.exp.X_add_number;
+
       if (offset & 3)
        {
          inst.error = _("co-processor address must be word aligned");
@@ -1553,40 +2077,40 @@ cp_address_offset (str)
 
 static int
 cp_address_required_here (str)
-     char **str;
+     char ** str;
 {
-  char *p = *str;
-  int pre_inc = 0;
-  int write_back = 0;
+  char * p = * str;
+  int    pre_inc = 0;
+  int    write_back = 0;
 
   if (*p == '[')
     {
       int reg;
 
       p++;
-      while (*p == ' ')
-       p++;
+      skip_whitespace (p);
 
-      if ((reg = reg_required_here (&p, 16)) == FAIL)
+      if ((reg = reg_required_here (& p, 16)) == FAIL)
        return FAIL;
 
-      while (*p == ' ')
-       p++;
+      skip_whitespace (p);
 
       if (*p == ']')
        {
          p++;
-         if (skip_past_comma (&p) == SUCCESS)
+
+         if (skip_past_comma (& p) == SUCCESS)
            {
-             /* [Rn], #expr */
+             /* [Rn], #expr  */
              write_back = WRITE_BACK;
+
              if (reg == REG_PC)
                {
                  inst.error = _("pc may not be used in post-increment");
                  return FAIL;
                }
 
-             if (cp_address_offset (&p) == FAIL)
+             if (cp_address_offset (& p) == FAIL)
                return FAIL;
            }
          else
@@ -1594,20 +2118,20 @@ cp_address_required_here (str)
        }
       else
        {
-         /* '['Rn, #expr']'[!] */
+         /* '['Rn, #expr']'[!]  */
 
-         if (skip_past_comma (&p) == FAIL)
+         if (skip_past_comma (& p) == FAIL)
            {
              inst.error = _("pre-indexed expression expected");
              return FAIL;
            }
 
          pre_inc = PRE_INDEX;
-         if (cp_address_offset (&p) == FAIL)
+
+         if (cp_address_offset (& p) == FAIL)
            return FAIL;
 
-         while (*p == ' ')
-           p++;
+         skip_whitespace (p);
 
          if (*p++ != ']')
            {
@@ -1615,8 +2139,7 @@ cp_address_required_here (str)
              return FAIL;
            }
 
-         while (*p == ' ')
-           p++;
+         skip_whitespace (p);
 
          if (*p == '!')
            {
@@ -1637,7 +2160,7 @@ cp_address_required_here (str)
        return FAIL;
 
       inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
-      inst.reloc.exp.X_add_number -= 8;  /* PC rel adjust */
+      inst.reloc.exp.X_add_number -= 8;  /* PC rel adjust */
       inst.reloc.pc_rel = 1;
       inst.instruction |= (REG_PC << 16);
       pre_inc = PRE_INDEX;
@@ -1650,11 +2173,11 @@ cp_address_required_here (str)
 
 static void
 do_nop (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  /* Do nothing really */
-  inst.instruction |= flags; /* This is pointless */
+  /* Do nothing really */
+  inst.instruction |= flags; /* This is pointless */
   end_of_line (str);
   return;
 }
@@ -1664,173 +2187,182 @@ do_mrs (str, flags)
      char *str;
      unsigned long flags;
 {
-  /* Only one syntax */
-  while (*str == ' ')
-    str++;
+  int skip = 0;
+
+  /* Only one syntax.  */
+  skip_whitespace (str);
 
   if (reg_required_here (&str, 12) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
-  if (skip_past_comma (&str) == FAIL
-      || psr_required_here (& str, CPSR_ALL, SPSR_ALL) == FAIL)
+  if (skip_past_comma (&str) == FAIL)
     {
-      inst.error = _("<psr> expected");
+      inst.error = _("comma expected after register name");
       return;
     }
 
+  skip_whitespace (str);
+
+  if (   strcmp (str, "CPSR") == 0
+      || strcmp (str, "SPSR") == 0
+        /* Lower case versions for backwards compatability.  */
+      || strcmp (str, "cpsr") == 0
+      || strcmp (str, "spsr") == 0)
+    skip = 4;
+
+  /* This is for backwards compatability with older toolchains.  */
+  else if (   strcmp (str, "cpsr_all") == 0
+          || strcmp (str, "spsr_all") == 0)
+    skip = 8;
+  else
+    {
+      inst.error = _("{C|S}PSR expected");
+      return;
+    }
+
+  if (* str == 's' || * str == 'S')
+    inst.instruction |= SPSR_BIT;
+  str += skip;
+
   inst.instruction |= flags;
   end_of_line (str);
-  return;
 }
 
-/* Three possible forms: "<psr>, Rm", "<psrf>, Rm", "<psrf>, #expression" */
+/* Two possible forms:
+      "{C|S}PSR_<field>, Rm",
+      "{C|S}PSR_f, #expression".  */
+
 static void
 do_msr (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  int reg;
+  skip_whitespace (str);
 
-  while (*str == ' ')
-    str ++;
+  if (psr_required_here (& str) == FAIL)
+    return;
 
-  if (psr_required_here (&str, CPSR_ALL, SPSR_ALL) == SUCCESS)
+  if (skip_past_comma (& str) == FAIL)
     {
-      inst.instruction |= PSR_ALL;
+      inst.error = _("comma missing after psr flags");
+      return;
+    }
 
-      /* Sytax should be "<psr>, Rm" */
-      if (skip_past_comma (&str) == FAIL
-         || (reg = reg_required_here (&str, 0)) == FAIL)
-       {
-         inst.error = bad_args;
-         return;
-       }
+  skip_whitespace (str);
+
+  if (reg_required_here (& str, 0) != FAIL)
+    {
+      inst.error = NULL;
+      inst.instruction |= flags;
+      end_of_line (str);
+      return;
     }
-  else
+
+  if (! is_immediate_prefix (* str))
     {
-      if (psr_required_here (& str, CPSR_FLG, SPSR_FLG) == SUCCESS)
-       {
-         inst.instruction |= PSR_FLAGS;
-       }
-      else if (psr_required_here (& str, CPSR_CTL, SPSR_CTL) == SUCCESS)
-       {
-         inst.instruction |= PSR_CONTROL;
-       }
-      else
-       {
-         inst.error = bad_args;
-         return;
-       }
-      
-      if (skip_past_comma (&str) == FAIL)
-       {
-         inst.error = bad_args;
-         return;
-       }
-      
-      /* Syntax could be "<psrf>, rm", "<psrf>, #expression" */
-      
-      if ((reg = reg_required_here (&str, 0)) != FAIL)
-       ;
-      /* Immediate expression */
-      else if (*(str++) == '#')
-       {
-         inst.error = NULL;
-         if (my_get_expression (&inst.reloc.exp, &str))
-           {
-             inst.error = _("Register or shift expression expected");
-             return;
-           }
+      inst.error =
+       _("only a register or immediate value can follow a psr flag");
+      return;
+    }
 
-         if (inst.reloc.exp.X_add_symbol)
-           {
-             inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
-             inst.reloc.pc_rel = 0;
-           }
-         else
-           {
-             unsigned value = validate_immediate (inst.reloc.exp.X_add_number);
-             if (value == FAIL)
-               {
-                 inst.error = _("Invalid constant");
-                 return;
-               }
+  str ++;
+  inst.error = NULL;
 
-             inst.instruction |= value;
-           }
+  if (my_get_expression (& inst.reloc.exp, & str))
+    {
+      inst.error =
+       _("only a register or immediate value can follow a psr flag");
+      return;
+    }
 
-         flags |= INST_IMMEDIATE;
-       }
-      else
+  if ((cpu_variant & ARM_EXT_V5) != ARM_EXT_V5
+      && inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
+    {
+      inst.error = _("immediate value cannot be used to set this field");
+      return;
+    }
+
+  flags |= INST_IMMEDIATE;
+
+  if (inst.reloc.exp.X_add_symbol)
+    {
+      inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+      inst.reloc.pc_rel = 0;
+    }
+  else
+    {
+      unsigned value = validate_immediate (inst.reloc.exp.X_add_number);
+
+      if (value == (unsigned) FAIL)
        {
-         inst.error = _("Error: unrecognised syntax for second argument to msr instruction");
+         inst.error = _("Invalid constant");
          return;
        }
+
+      inst.instruction |= value;
     }
 
-  inst.error = NULL; 
+  inst.error = NULL;
   inst.instruction |= flags;
   end_of_line (str);
-  return;
 }
 
 /* Long Multiply Parser
    UMULL RdLo, RdHi, Rm, Rs
    SMULL RdLo, RdHi, Rm, Rs
    UMLAL RdLo, RdHi, Rm, Rs
-   SMLAL RdLo, RdHi, Rm, Rs
-*/   
+   SMLAL RdLo, RdHi, Rm, Rs.  */
+
 static void
 do_mull (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   int rdlo, rdhi, rm, rs;
 
-  /* only one format "rdlo, rdhi, rm, rs" */
-  while (*str == ' ')
-    str++;
+  /* Only one format "rdlo, rdhi, rm, rs".  */
+  skip_whitespace (str);
 
   if ((rdlo = reg_required_here (&str, 12)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
   if (skip_past_comma (&str) == FAIL
       || (rdhi = reg_required_here (&str, 16)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
   if (skip_past_comma (&str) == FAIL
       || (rm = reg_required_here (&str, 0)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
-  /* rdhi, rdlo and rm must all be different */
+  /* rdhi, rdlo and rm must all be different */
   if (rdlo == rdhi || rdlo == rm || rdhi == rm)
     as_tsktsk (_("rdhi, rdlo and rm must all be different"));
 
   if (skip_past_comma (&str) == FAIL
       || (rs = reg_required_here (&str, 8)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
   if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC)
     {
-      inst.error = bad_pc;
+      inst.error = BAD_PC;
       return;
     }
-   
+
   inst.instruction |= flags;
   end_of_line (str);
   return;
@@ -1838,37 +2370,36 @@ do_mull (str, flags)
 
 static void
 do_mul (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   int rd, rm;
-  
-  /* only one format "rd, rm, rs" */
-  while (*str == ' ')
-    str++;
+
+  /* Only one format "rd, rm, rs".  */
+  skip_whitespace (str);
 
   if ((rd = reg_required_here (&str, 16)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
   if (rd == REG_PC)
     {
-      inst.error = bad_pc;
+      inst.error = BAD_PC;
       return;
     }
 
   if (skip_past_comma (&str) == FAIL
       || (rm = reg_required_here (&str, 0)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
   if (rm == REG_PC)
     {
-      inst.error = bad_pc;
+      inst.error = BAD_PC;
       return;
     }
 
@@ -1878,13 +2409,13 @@ do_mul (str, flags)
   if (skip_past_comma (&str) == FAIL
       || (rm = reg_required_here (&str, 8)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
   if (rm == REG_PC)
     {
-      inst.error = bad_pc;
+      inst.error = BAD_PC;
       return;
     }
 
@@ -1895,78 +2426,1144 @@ do_mul (str, flags)
 
 static void
 do_mla (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   int rd, rm;
 
-  /* only one format "rd, rm, rs, rn" */
-  while (*str == ' ')
-    str++;
+  /* Only one format "rd, rm, rs, rn".  */
+  skip_whitespace (str);
 
   if ((rd = reg_required_here (&str, 16)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
   if (rd == REG_PC)
     {
-      inst.error = bad_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
-      || (rm = reg_required_here (&str, 0)) == FAIL)
+  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)
+    {
+      as_bad (BAD_FLAGS);
+      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_pld (str, flags)
+     char * str;
+     unsigned long flags;
+{
+  int rd;
+
+  if (flags)
+    {
+      as_bad (BAD_FLAGS);
+      return;
+    }
+
+  skip_whitespace (str);
+
+  if (* str != '[')
+    {
+      inst.error = _("'[' expected after PLD mnemonic");
+      return;
+    }
+
+  ++ str;
+  skip_whitespace (str);
+
+  if ((rd = reg_required_here (& str, 16)) == FAIL)
+    return;
+
+  skip_whitespace (str);
+
+  if (* str == ']')
+    {
+      /* [Rn], ... ?  */
+      ++ str;
+      skip_whitespace (str);
+
+      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, ...] */
+    {
+      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;
+    }
+
+  end_of_line (str);
+}
+
+/* Xscale load-consecutive (argument parse)
+   Mode is like LDRH.
+
+     LDRccD R, mode
+     STRccD R, mode.  */
+
+static void
+do_ldrd (str, flags)
+     char * str;
+     unsigned long flags;
+{
+  int rd;
+  int rn;
+
+  if (flags != DOUBLE_LOAD_FLAG)
+    {
+      /* 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 ((cpu_variant & ARM_EXT_XSCALE) != ARM_EXT_XSCALE)
+    {
+      static char buff[128];
+
+      --str;
+      while (isspace (*str))
+       --str;
+      str -= 4;
+
+      /* Deny all knowledge.  */
+      sprintf (buff, _("bad instruction '%.100s'"), str);
+      inst.error = buff;
+      return;
+    }
+
+  skip_whitespace (str);
+
+  if ((rd = reg_required_here (& str, 12)) == FAIL)
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
-  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, or -1 if
-   not in the table.  */
+/* Returns the index into fp_values of a floating point number,
+   or -1 if not in the table.  */
+
 static int
 my_get_float_expression (str)
-     char **str;
+     char ** str;
 {
   LITTLENUM_TYPE words[MAX_LITTLENUMS];
-  char *save_in;
-  expressionS exp;
-  int i, j;
+  char *         save_in;
+  expressionS    exp;
+  int            i;
+  int            j;
 
   memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
-  /* Look for a raw floating point number */
+
+  /* Look for a raw floating point number.  */
   if ((save_in = atof_ieee (*str, 'x', words)) != NULL
-      && (is_end_of_line [(int)(*save_in)] || *save_in == '\0'))
+      && is_end_of_line[(unsigned char) *save_in])
     {
       for (i = 0; i < NUM_FLOAT_VALS; i++)
        {
@@ -1985,7 +3582,7 @@ my_get_float_expression (str)
     }
 
   /* Try and parse a more complex expression, this will probably fail
-     unless the code uses a floating point prefix (eg "0f") */
+     unless the code uses a floating point prefix (eg "0f") */
   save_in = input_line_pointer;
   input_line_pointer = *str;
   if (expression (&exp) == absolute_section
@@ -1994,7 +3591,7 @@ my_get_float_expression (str)
     {
       /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
         Ditto for 15.  */
-      if (gen_to_words (words, 5, (long)15) == 0)
+      if (gen_to_words (words, 5, (long) 15) == 0)
        {
          for (i = 0; i < NUM_FLOAT_VALS; i++)
            {
@@ -2019,19 +3616,20 @@ my_get_float_expression (str)
   return -1;
 }
 
-/* Return true if anything in the expression is a bignum */
+/* Return true if anything in the expression is a bignum.  */
+
 static int
 walk_no_bignums (sp)
-     symbolS *sp;
+     symbolS * sp;
 {
-  if (sp->sy_value.X_op == O_big)
+  if (symbol_get_value_expression (sp)->X_op == O_big)
     return 1;
 
-  if (sp->sy_value.X_add_symbol)
+  if (symbol_get_value_expression (sp)->X_add_symbol)
     {
-      return (walk_no_bignums (sp->sy_value.X_add_symbol)
-             || (sp->sy_value.X_op_symbol
-                 && walk_no_bignums (sp->sy_value.X_op_symbol)));
+      return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
+             || (symbol_get_value_expression (sp)->X_op_symbol
+                 && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
     }
 
   return 0;
@@ -2039,12 +3637,12 @@ walk_no_bignums (sp)
 
 static int
 my_get_expression (ep, str)
-     expressionS *ep;
-     char **str;
+     expressionS * ep;
+     char ** str;
 {
-  char *save_in;
-  segT seg;
-  
+  char * save_in;
+  segT   seg;
+
   save_in = input_line_pointer;
   input_line_pointer = *str;
   seg = expression (ep);
@@ -2083,122 +3681,134 @@ my_get_expression (ep, str)
   return 0;
 }
 
-/* unrestrict should be one if <shift> <register> is permitted for this
-   instruction */
+/* UNRESTRICT should be one if <shift> <register> is permitted for this
+   instruction */
 
 static int
 decode_shift (str, unrestrict)
-     char **str;
-     int unrestrict;
-{
-  struct asm_shift *shft;
-  char *p;
-  char c;
-    
-  while (**str == ' ')
-    (*str)++;
-    
-  for (p = *str; isalpha (*p); p++)
+     char ** str;
+     int     unrestrict;
+{
+  const struct asm_shift_name * shift;
+  char * p;
+  char   c;
+
+  skip_whitespace (* str);
+
+  for (p = * str; isalpha (* p); p ++)
     ;
 
-  if (p == *str)
+  if (p == * str)
     {
       inst.error = _("Shift expression expected");
       return FAIL;
     }
 
-  c = *p;
-  *p = '\0';
-  shft = (struct asm_shift *) hash_find (arm_shift_hsh, *str);
-  *p = c;
-  if (shft)
+  c = * p;
+  * p = '\0';
+  shift = (const struct asm_shift_name *) hash_find (arm_shift_hsh, * str);
+  * p = c;
+
+  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;
+    }
 
-      while (*p == ' ')
-       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 (*p == '#')
-       {
-         inst.error = NULL;
-         p++;
-         if (my_get_expression (&inst.reloc.exp, &p))
-           return FAIL;
+  if (shift->properties->index == SHIFT_RRX)
+    {
+      * str = p;
+      inst.instruction |= shift->properties->bit_field;
+      return SUCCESS;
+    }
 
-         /* Validate some simple #expressions */
-         if (inst.reloc.exp.X_op == O_constant)
-           {
-             unsigned num = inst.reloc.exp.X_add_number;
+  skip_whitespace (p);
 
-             /* Reject operations greater than 32, or lsl #32 */
-             if (num > 32 || (num == 32 && shft->value == 0))
-               {
-                 inst.error = _("Invalid immediate shift");
-                 return FAIL;
-               }
+  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;
+    }
 
-             /* Shifts of zero should be converted to lsl (which is zero)*/
-             if (num == 0)
-               {
-                 *str = p;
-                 return SUCCESS;
-               }
+  inst.error = NULL;
+  p ++;
 
-             /* Shifts of 32 are encoded as 0, for those shifts that
-                support it.  */
-             if (num == 32)
-               num = 0;
+  if (my_get_expression (& inst.reloc.exp, & p))
+    return FAIL;
 
-             inst.instruction |= (num << 7) | shft->value;
-             *str = p;
-             return SUCCESS;
-           }
+  /* Validate some simple #expressions.  */
+  if (inst.reloc.exp.X_op == O_constant)
+    {
+      unsigned num = inst.reloc.exp.X_add_number;
 
-         inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
-         inst.reloc.pc_rel = 0;
-         inst.instruction |= shft->value;
-         *str = p;
-         return SUCCESS;
-       }
-      else
+      /* 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)
+         )
        {
-         inst.error = unrestrict ? _("shift requires register or #expression")
-           : _("shift requires #expression");
-         *str = p;
-         return FAIL;
+         /* 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;
+           }
        }
+
+      /* Shifts of 32 are encoded as 0, for those shifts that
+        support it.  */
+      if (num == 32)
+       num = 0;
+
+      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 */
-/* by altering the instuction. A bit of a hack really */
-/*      MOV <-> MVN
+/* Do those data_ops which can take a negative immediate constant
+   by altering the instuction.  A bit of a hack really.
+        MOV <-> MVN
         AND <-> BIC
         ADC <-> SBC
         by inverting the second operand, and
         ADD <-> SUB
         CMP <-> CMN
-        by negating the second operand.
-*/
+        by negating the second operand.  */
+
 static int
 negate_data_op (instruction, value)
-     unsigned long *instruction;
-     unsigned long value;
+     unsigned long * instruction;
+     unsigned long   value;
 {
   int op, new_inst;
   unsigned long negated, inverted;
@@ -2209,96 +3819,96 @@ negate_data_op (instruction, value)
   op = (*instruction >> DATA_OP_SHIFT) & 0xf;
   switch (op)
     {
-      /* First negates */
-    case OPCODE_SUB:             /* ADD <-> SUB */
+      /* First negates */
+    case OPCODE_SUB:             /* ADD <-> SUB  */
       new_inst = OPCODE_ADD;
       value = negated;
       break;
 
-    case OPCODE_ADD: 
-      new_inst = OPCODE_SUB;               
+    case OPCODE_ADD:
+      new_inst = OPCODE_SUB;
       value = negated;
       break;
 
-    case OPCODE_CMP:             /* CMP <-> CMN */
+    case OPCODE_CMP:             /* CMP <-> CMN  */
       new_inst = OPCODE_CMN;
       value = negated;
       break;
 
-    case OPCODE_CMN: 
-      new_inst = OPCODE_CMP;               
+    case OPCODE_CMN:
+      new_inst = OPCODE_CMP;
       value = negated;
       break;
 
-      /* Now Inverted ops */
-    case OPCODE_MOV:             /* MOV <-> MVN */
-      new_inst = OPCODE_MVN;               
+      /* Now Inverted ops */
+    case OPCODE_MOV:             /* MOV <-> MVN  */
+      new_inst = OPCODE_MVN;
       value = inverted;
       break;
 
-    case OPCODE_MVN: 
+    case OPCODE_MVN:
       new_inst = OPCODE_MOV;
       value = inverted;
       break;
 
-    case OPCODE_AND:             /* AND <-> BIC */ 
-      new_inst = OPCODE_BIC;               
+    case OPCODE_AND:             /* AND <-> BIC  */
+      new_inst = OPCODE_BIC;
       value = inverted;
       break;
 
-    case OPCODE_BIC: 
+    case OPCODE_BIC:
       new_inst = OPCODE_AND;
       value = inverted;
       break;
 
-    case OPCODE_ADC:              /* ADC <-> SBC */
-      new_inst = OPCODE_SBC;               
+    case OPCODE_ADC:              /* ADC <-> SBC  */
+      new_inst = OPCODE_SBC;
       value = inverted;
       break;
 
-    case OPCODE_SBC: 
+    case OPCODE_SBC:
       new_inst = OPCODE_ADC;
       value = inverted;
       break;
 
-      /* We cannot do anything */
-    default:  
+      /* We cannot do anything */
+    default:
       return FAIL;
     }
 
-  if (value == FAIL)
+  if (value == (unsigned) FAIL)
     return FAIL;
 
   *instruction &= OPCODE_MASK;
   *instruction |= new_inst << DATA_OP_SHIFT;
-  return value; 
+  return value;
 }
 
 static int
 data_op2 (str)
-     char **str;
+     char ** str;
 {
   int value;
   expressionS expr;
 
-  while (**str == ' ')
-    (*str)++;
-    
+  skip_whitespace (* str);
+
   if (reg_required_here (str, 0) != FAIL)
     {
       if (skip_past_comma (str) == SUCCESS)
-       {
-         /* Shift operation on register */
-         return decode_shift (str, NO_SHIFT_RESTRICT);
-       }
+       /* Shift operation on register.  */
+       return decode_shift (str, NO_SHIFT_RESTRICT);
+
       return SUCCESS;
     }
   else
     {
-      /* Immediate expression */
-      if (*((*str)++) == '#')
+      /* Immediate expression */
+      if (is_immediate_prefix (**str))
        {
+         (*str)++;
          inst.error = NULL;
+
          if (my_get_expression (&inst.reloc.exp, str))
            return FAIL;
 
@@ -2311,7 +3921,7 @@ data_op2 (str)
            {
              if (skip_past_comma (str) == SUCCESS)
                {
-                 /* #x, y -- ie explicit rotation by Y  */
+                 /* #x, y -- ie explicit rotation by Y.  */
                  if (my_get_expression (&expr, str))
                    return FAIL;
 
@@ -2320,8 +3930,8 @@ data_op2 (str)
                      inst.error = _("Constant expression expected");
                      return FAIL;
                    }
-                 /* Rotate must be a multiple of 2 */
+
+                 /* Rotate must be a multiple of 2 */
                  if (((unsigned) expr.X_add_number) > 30
                      || (expr.X_add_number & 1) != 0
                      || ((unsigned) inst.reloc.exp.X_add_number) > 255)
@@ -2335,13 +3945,13 @@ data_op2 (str)
                  return SUCCESS;
                }
 
-             /* Implicit rotation, select a suitable one  */
+             /* Implicit rotation, select a suitable one.  */
              value = validate_immediate (inst.reloc.exp.X_add_number);
 
              if (value == FAIL)
                {
-                 /* Can't be done, perhaps the code reads something like
-                    "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be ok */
+                 /* Can't be done.  Perhaps the code reads something like
+                    "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be OK.  */
                  if ((value = negate_data_op (&inst.instruction,
                                               inst.reloc.exp.X_add_number))
                      == FAIL)
@@ -2358,6 +3968,7 @@ data_op2 (str)
          return SUCCESS;
        }
 
+      (*str)++;
       inst.error = _("Register or shift expression expected");
       return FAIL;
     }
@@ -2365,26 +3976,25 @@ data_op2 (str)
 
 static int
 fp_op2 (str)
-     char **str;
+     char ** str;
 {
-  while (**str == ' ')
-    (*str)++;
+  skip_whitespace (* str);
 
   if (fp_reg_required_here (str, 0) != FAIL)
     return SUCCESS;
   else
     {
-      /* Immediate expression */
+      /* Immediate expression */
       if (*((*str)++) == '#')
        {
          int i;
 
          inst.error = NULL;
-         while (**str == ' ')
-           (*str)++;
 
-         /* First try and match exact strings, this is to guarantee that
-            some formats will work even for cross assembly */
+         skip_whitespace (* str);
+
+         /* First try and match exact strings, this is to guarantee
+            that some formats will work even for cross assembly.  */
 
          for (i = 0; fp_const[i]; i++)
            {
@@ -2393,7 +4003,7 @@ fp_op2 (str)
                  char *start = *str;
 
                  *str += strlen (fp_const[i]);
-                 if (is_end_of_line[(int)**str] || **str == '\0')
+                 if (is_end_of_line[(unsigned char) **str])
                    {
                      inst.instruction |= i + 8;
                      return SUCCESS;
@@ -2415,18 +4025,18 @@ fp_op2 (str)
          inst.error = _("Invalid floating point immediate expression");
          return FAIL;
        }
-      inst.error = _("Floating point register or immediate expression expected");
+      inst.error =
+       _("Floating point register or immediate expression expected");
       return FAIL;
     }
 }
 
 static void
 do_arit (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (reg_required_here (&str, 12) == FAIL
       || skip_past_comma (&str) == FAIL
@@ -2435,7 +4045,7 @@ do_arit (str, flags)
       || data_op2 (&str) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -2446,45 +4056,77 @@ do_arit (str, flags)
 
 static void
 do_adr (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   /* This is a pseudo-op of the form "adr rd, label" to be converted
-     into a relative address of the form "add rd, pc, #label-.-8" */
-
-  while (*str == ' ')
-    str++;
+     into a relative address of the form "add rd, pc, #label-.-8".  */
+  skip_whitespace (str);
 
   if (reg_required_here (&str, 12) == FAIL
       || skip_past_comma (&str) == FAIL
       || my_get_expression (&inst.reloc.exp, &str))
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
+
   /* Frag hacking will turn this into a sub instruction if the offset turns
      out to be negative.  */
   inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
-  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
+  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
   inst.reloc.pc_rel = 1;
   inst.instruction |= flags;
+
+  end_of_line (str);
+}
+
+static void
+do_adrl (str, flags)
+     char * str;
+     unsigned long flags;
+{
+  /* This is a pseudo-op of the form "adrl rd, label" to be converted
+     into a relative address of the form:
+       add rd, pc, #low(label-.-8)"
+       add rd, rd, #high(label-.-8)"  */
+
+  skip_whitespace (str);
+
+  if (reg_required_here (& str, 12) == FAIL
+      || skip_past_comma (& str) == FAIL
+      || my_get_expression (& inst.reloc.exp, & str))
+    {
+      if (!inst.error)
+       inst.error = BAD_ARGS;
+      return;
+    }
+
   end_of_line (str);
+
+  /* Frag hacking will turn this into a sub instruction if the offset turns
+     out to be negative.  */
+  inst.reloc.type              = BFD_RELOC_ARM_ADRL_IMMEDIATE;
+  inst.reloc.exp.X_add_number -= 8; /* PC relative adjust  */
+  inst.reloc.pc_rel            = 1;
+  inst.instruction            |= flags;
+  inst.size                    = INSN_SIZE * 2;
+
   return;
 }
 
 static void
 do_cmp (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (reg_required_here (&str, 16) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -2492,7 +4134,7 @@ do_cmp (str, flags)
       || data_op2 (&str) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -2506,16 +4148,15 @@ do_cmp (str, flags)
 
 static void
 do_mov (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (reg_required_here (&str, 12) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -2523,7 +4164,7 @@ do_mov (str, flags)
       || data_op2 (&str) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -2534,24 +4175,25 @@ do_mov (str, flags)
 
 static int
 ldst_extend (str, hwse)
-     char **str;
-     int hwse;
+     char ** str;
+     int     hwse;
 {
   int add = INDEX_UP;
 
   switch (**str)
     {
     case '#':
+    case '$':
       (*str)++;
-      if (my_get_expression (&inst.reloc.exp, str))
+      if (my_get_expression (& inst.reloc.exp, str))
        return FAIL;
 
       if (inst.reloc.exp.X_op == O_constant)
        {
          int value = inst.reloc.exp.X_add_number;
 
-          if ((hwse && (value < -255 || value > 255))
-               || (value < -4095 || value > 4095))
+         if ((hwse && (value < -255 || value > 255))
+             || (value < -4095 || value > 4095))
            {
              inst.error = _("address offset too large");
              return FAIL;
@@ -2563,42 +4205,47 @@ ldst_extend (str, hwse)
              add = 0;
            }
 
-          /* Halfword and signextension instructions have the
-             immediate value split across bits 11..8 and bits 3..0 */
-          if (hwse)
-            inst.instruction |= add | HWOFFSET_IMM | (value >> 4) << 8 | value & 0xF;
-          else
-            inst.instruction |= add | value;
+         /* Halfword and signextension instructions have the
+             immediate value split across bits 11..8 and bits 3..0.  */
+         if (hwse)
+           inst.instruction |= (add | HWOFFSET_IMM
+                                | ((value >> 4) << 8) | (value & 0xF));
+         else
+           inst.instruction |= add | value;
        }
       else
        {
-          if (hwse)
-            {
-              inst.instruction |= HWOFFSET_IMM;
-              inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-            }
-          else
-            inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+         if (hwse)
+           {
+             inst.instruction |= HWOFFSET_IMM;
+             inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+           }
+         else
+           inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
          inst.reloc.pc_rel = 0;
        }
       return SUCCESS;
 
     case '-':
-      add = 0; /* and fall through */
+      add = 0;
+      /* Fall through.  */
+
     case '+':
-      (*str)++;        /* and fall through */
+      (*str)++;
+      /* Fall through.  */
+
     default:
       if (reg_required_here (str, 0) == FAIL)
        return FAIL;
 
       if (hwse)
-        inst.instruction |= add;
+       inst.instruction |= add;
       else
-        {
-          inst.instruction |= add | OFFSET_REG;
-          if (skip_past_comma (str) == SUCCESS)
-            return decode_shift (str, SHIFT_RESTRICT);
-        }
+       {
+         inst.instruction |= add | OFFSET_REG;
+         if (skip_past_comma (str) == SUCCESS)
+           return decode_shift (str, SHIFT_RESTRICT);
+       }
 
       return SUCCESS;
     }
@@ -2606,7 +4253,7 @@ ldst_extend (str, hwse)
 
 static void
 do_ldst (str, flags)
-     char *str;
+     char *        str;
      unsigned long flags;
 {
   int halfword = 0;
@@ -2616,31 +4263,35 @@ do_ldst (str, flags)
 
   /* This is not ideal, but it is the simplest way of dealing with the
      ARM7T halfword instructions (since they use a different
-     encoding, but the same mnemonic): */
-  if (halfword = ((flags & 0x80000000) != 0))
+     encoding, but the same mnemonic):  */
+  halfword = (flags & 0x80000000) != 0;
+  if (halfword)
     {
       /* This is actually a load/store of a halfword, or a
-         signed-extension load */
-      if ((cpu_variant & ARM_HALFWORD) == 0)
-        {
-          inst.error
+         signed-extension load */
+      if ((cpu_variant & ARM_EXT_HALFWORD) == 0)
+       {
+         inst.error
            = _("Processor does not support halfwords or signed bytes");
-          return;
-        }
+         return;
+       }
 
-      inst.instruction = (inst.instruction & COND_MASK)
-                         | (flags & ~COND_MASK);
+      inst.instruction = ((inst.instruction & COND_MASK)
+                         | (flags & ~COND_MASK));
 
       flags = 0;
     }
 
-  while (*str == ' ')
-    str++;
-    
-  if ((conflict_reg = reg_required_here (&str, 12)) == FAIL)
-    return;
+  skip_whitespace (str);
 
-  if (skip_past_comma (&str) == FAIL)
+  if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
+    {
+      if (!inst.error)
+       inst.error = BAD_ARGS;
+      return;
+    }
+
+  if (skip_past_comma (& str) == FAIL)
     {
       inst.error = _("Address expected");
       return;
@@ -2651,55 +4302,71 @@ do_ldst (str, flags)
       int reg;
 
       str++;
-      while (*str == ' ')
-       str++;
+
+      skip_whitespace (str);
 
       if ((reg = reg_required_here (&str, 16)) == FAIL)
        return;
 
-      conflict_reg = (((conflict_reg == reg)
-                      && (inst.instruction & LOAD_BIT))
-                     ? 1 : 0);
+      /* Conflicts can occur on stores as well as loads.  */
+      conflict_reg = (conflict_reg == reg);
 
-      while (*str == ' ')
-       str++;
+      skip_whitespace (str);
 
       if (*str == ']')
        {
-         str++;
+         str ++;
+
          if (skip_past_comma (&str) == SUCCESS)
            {
-             /* [Rn],... (post inc) */
+             /* [Rn],... (post inc)  */
              if (ldst_extend (&str, halfword) == FAIL)
                return;
              if (conflict_reg)
-               as_warn (_("destination register same as write-back base\n"));
+               {
+                 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
            {
-             /* [Rn] */
-              if (halfword)
-                inst.instruction |= HWOFFSET_IMM;
+             /* [Rn]  */
+             if (halfword)
+               inst.instruction |= HWOFFSET_IMM;
 
-              while (*str == ' ')
-               str++;
+             skip_whitespace (str);
 
-              if (*str == '!')
-               {
-                 if (conflict_reg)
-                  as_warn (_("destination register same as write-back base\n"));
-                 str++;
-                 inst.instruction |= WRITE_BACK;
-               }
+             if (*str == '!')
+               {
+                 if (conflict_reg)
+                   as_warn (_("%s register same as write-back base"),
+                            ((inst.instruction & LOAD_BIT)
+                             ? _("destination") : _("source")));
+                 str++;
+                 inst.instruction |= WRITE_BACK;
+               }
 
              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
        {
-         /* [Rn,...] */
+         /* [Rn,...]  */
          if (skip_past_comma (&str) == FAIL)
            {
              inst.error = _("pre-indexed expression expected");
@@ -2710,8 +4377,7 @@ do_ldst (str, flags)
          if (ldst_extend (&str, halfword) == FAIL)
            return;
 
-         while (*str == ' ')
-           str++;
+         skip_whitespace (str);
 
          if (*str++ != ']')
            {
@@ -2719,13 +4385,14 @@ do_ldst (str, flags)
              return;
            }
 
-         while (*str == ' ')
-           str++;
+         skip_whitespace (str);
 
          if (*str == '!')
            {
              if (conflict_reg)
-               as_tsktsk (_("destination register same as write-back base\n"));
+               as_warn (_("%s register same as write-back base"),
+                        ((inst.instruction & LOAD_BIT)
+                         ? _("destination") : _("source")));
              str++;
              inst.instruction |= WRITE_BACK;
            }
@@ -2733,11 +4400,10 @@ do_ldst (str, flags)
     }
   else if (*str == '=')
     {
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
+      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
       str++;
 
-      while (*str == ' ')
-       str++;
+      skip_whitespace (str);
 
       if (my_get_expression (&inst.reloc.exp, &str))
        return;
@@ -2750,36 +4416,36 @@ do_ldst (str, flags)
        }
 
       if (inst.reloc.exp.X_op == O_constant
-         && (value = validate_immediate(inst.reloc.exp.X_add_number)) != FAIL)
+         && (value = validate_immediate (inst.reloc.exp.X_add_number)) != FAIL)
        {
-         /* This can be done with a mov instruction */
+         /* This can be done with a mov instruction */
          inst.instruction &= LITERAL_MASK;
          inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
          inst.instruction |= (flags & COND_MASK) | (value & 0xfff);
-         end_of_line(str);
-         return; 
+         end_of_line (str);
+         return;
        }
       else
        {
-         /* Insert into literal pool */     
+         /* Insert into literal pool.  */
          if (add_to_lit_pool () == FAIL)
            {
              if (!inst.error)
-               inst.error = _("literal pool insertion failed"); 
+               inst.error = _("literal pool insertion failed");
              return;
            }
 
-         /* Change the instruction exp to point to the pool */
-          if (halfword)
-            {
-              inst.instruction |= HWOFFSET_IMM;
-              inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
-            }
-          else
+         /* Change the instruction exp to point to the pool */
+         if (halfword)
+           {
+             inst.instruction |= HWOFFSET_IMM;
+             inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
+           }
+         else
            inst.reloc.type = BFD_RELOC_ARM_LITERAL;
          inst.reloc.pc_rel = 1;
          inst.instruction |= (REG_PC << 16);
-         pre_inc = 1; 
+         pre_inc = 1;
        }
     }
   else
@@ -2788,18 +4454,21 @@ do_ldst (str, flags)
        return;
 
       if (halfword)
-        {
-          inst.instruction |= HWOFFSET_IMM;
-          inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-        }
+       {
+         inst.instruction |= HWOFFSET_IMM;
+         inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+       }
       else
-        inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
-      inst.reloc.exp.X_add_number -= 8;  /* PC rel adjust */
+       inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+#ifndef TE_WINCE
+      /* PC rel adjust.  */
+      inst.reloc.exp.X_add_number -= 8;
+#endif
       inst.reloc.pc_rel = 1;
       inst.instruction |= (REG_PC << 16);
       pre_inc = 1;
     }
-    
+
   if (pre_inc && (flags & TRANS_BIT))
     inst.error = _("Pre-increment instruction with translate");
 
@@ -2810,13 +4479,13 @@ do_ldst (str, flags)
 
 static long
 reg_list (strp)
-     char **strp;
+     char ** strp;
 {
-  char *str = *strp;
-  long range = 0;
-  int another_range;
+  char * str = * strp;
+  long   range = 0;
+  int    another_range;
 
-  /* We come back here if we get ranges concatenated by '+' or '|' */
+  /* We come back here if we get ranges concatenated by '+' or '|' */
   do
     {
       another_range = 0;
@@ -2825,22 +4494,21 @@ reg_list (strp)
        {
          int in_range = 0;
          int cur_reg = -1;
-      
+
          str++;
          do
            {
              int reg;
-           
-             while (*str == ' ')
-               str++;
+
+             skip_whitespace (str);
 
              if ((reg = reg_required_here (& str, -1)) == FAIL)
                return FAIL;
-             
+
              if (in_range)
                {
                  int i;
-             
+
                  if (reg <= cur_reg)
                    {
                      inst.error = _("Bad range in register list");
@@ -2850,7 +4518,7 @@ reg_list (strp)
                  for (i = cur_reg + 1; i < reg; i++)
                    {
                      if (range & (1 << i))
-                       as_tsktsk 
+                       as_tsktsk
                          (_("Warning: Duplicated register (r%d) in register list"),
                           i);
                      else
@@ -2867,11 +4535,11 @@ reg_list (strp)
 
              range |= 1 << reg;
              cur_reg = reg;
-           } while (skip_past_comma (&str) != FAIL
-                    || (in_range = 1, *str++ == '-'));
+           }
+         while (skip_past_comma (&str) != FAIL
+                || (in_range = 1, *str++ == '-'));
          str--;
-         while (*str == ' ')
-           str++;
+         skip_whitespace (str);
 
          if (*str++ != '}')
            {
@@ -2888,7 +4556,7 @@ reg_list (strp)
 
          if (expr.X_op == O_constant)
            {
-             if (expr.X_add_number 
+             if (expr.X_add_number
                  != (expr.X_add_number & 0x0000ffff))
                {
                  inst.error = _("invalid register mask");
@@ -2901,7 +4569,7 @@ reg_list (strp)
 
                  regno &= -regno;
                  regno = (1 << regno) - 1;
-                 as_tsktsk 
+                 as_tsktsk
                    (_("Warning: Duplicated register (r%d) in register list"),
                     regno);
                }
@@ -2922,15 +4590,15 @@ reg_list (strp)
            }
        }
 
-      while (*str == ' ')
-       str++;
+      skip_whitespace (str);
 
       if (*str == '|' || *str == '+')
        {
          str++;
          another_range = 1;
        }
-    } while (another_range);
+    }
+  while (another_range);
 
   *strp = str;
   return range;
@@ -2938,14 +4606,13 @@ reg_list (strp)
 
 static void
 do_ldmstm (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   int base_reg;
   long range;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((base_reg = reg_required_here (&str, 16)) == FAIL)
     return;
@@ -2956,8 +4623,8 @@ do_ldmstm (str, flags)
       return;
     }
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
+
   if (*str == '!')
     {
       flags |= WRITE_BACK;
@@ -2968,14 +4635,14 @@ do_ldmstm (str, flags)
       || (range = reg_list (&str)) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
   if (*str == '^')
     {
       str++;
-      flags |= MULTI_SET_PSR;
+      flags |= LDM_TYPE_2_OR_3;
     }
 
   inst.instruction |= flags | range;
@@ -2985,34 +4652,35 @@ do_ldmstm (str, flags)
 
 static void
 do_swi (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
+  skip_whitespace (str);
+
   /* Allow optional leading '#'.  */
-  while (*str == ' ')
-    str++;
-  if (*str == '#')
+  if (is_immediate_prefix (*str))
     str++;
 
-  if (my_get_expression (&inst.reloc.exp, &str))
+  if (my_get_expression (& inst.reloc.exp, & str))
     return;
 
   inst.reloc.type = BFD_RELOC_ARM_SWI;
   inst.reloc.pc_rel = 0;
   inst.instruction |= flags;
+
   end_of_line (str);
+
   return;
 }
 
 static void
 do_swap (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   int reg;
-  
-  while (*str == ' ')
-    str++;
+
+  skip_whitespace (str);
 
   if ((reg = reg_required_here (&str, 12)) == FAIL)
     return;
@@ -3027,7 +4695,7 @@ do_swap (str, flags)
       || (reg = reg_required_here (&str, 0)) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3040,24 +4708,22 @@ do_swap (str, flags)
   if (skip_past_comma (&str) == FAIL
       || *str++ != '[')
     {
-      inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((reg = reg_required_here (&str, 16)) == FAIL)
     return;
 
   if (reg == REG_PC)
     {
-      inst.error = bad_pc;
+      inst.error = BAD_PC;
       return;
     }
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (*str++ != ']')
     {
@@ -3072,51 +4738,85 @@ do_swap (str, flags)
 
 static void
 do_branch (str, flags)
-     char *str;
-     unsigned long flags;
+     char * str;
+     unsigned long flags ATTRIBUTE_UNUSED;
 {
   if (my_get_expression (&inst.reloc.exp, &str))
     return;
-  inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH;
+
+#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_BRANCH;
+       inst.reloc.pc_rel = 1;
+      }
+    input_line_pointer = save_in;
+  }
+#else
+  inst.reloc.type   = BFD_RELOC_ARM_PCREL_BRANCH;
   inst.reloc.pc_rel = 1;
+#endif /* OBJ_ELF  */
+
   end_of_line (str);
   return;
 }
 
 static void
 do_bx (str, flags)
-     char *str;
-     unsigned long flags;
+     char * str;
+     unsigned long flags ATTRIBUTE_UNUSED;
 {
   int reg;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((reg = reg_required_here (&str, 0)) == FAIL)
-    return;
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
 
+  /* Note - it is not illegal to do a "bx pc".  Useless, but not illegal.  */
   if (reg == REG_PC)
-    as_tsktsk (_("Use of r15 in bx has undefined behaviour"));
+    as_tsktsk (_("Use of r15 in bx in ARM mode is not really useful"));
 
   end_of_line (str);
-  return;
 }
 
 static void
 do_cdp (str, flags)
-     char *str;
-     unsigned long flags;
+     char * str;
+     unsigned long flags ATTRIBUTE_UNUSED;
 {
   /* Co-processor data operation.
      Format: CDP{cond} CP#,<expr>,CRd,CRn,CRm{,<expr>}  */
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (co_proc_number (&str) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3124,7 +4824,7 @@ do_cdp (str, flags)
       || cp_opc_expr (&str, 20,4) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3132,7 +4832,7 @@ do_cdp (str, flags)
       || cp_reg_required_here (&str, 12) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3140,7 +4840,7 @@ do_cdp (str, flags)
       || cp_reg_required_here (&str, 16) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3148,7 +4848,7 @@ do_cdp (str, flags)
       || cp_reg_required_here (&str, 0) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3157,7 +4857,7 @@ do_cdp (str, flags)
       if (cp_opc_expr (&str, 5, 3) == FAIL)
        {
          if (!inst.error)
-           inst.error = bad_args;
+           inst.error = BAD_ARGS;
          return;
        }
     }
@@ -3168,19 +4868,18 @@ do_cdp (str, flags)
 
 static void
 do_lstc (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   /* Co-processor register load/store.
      Format: <LDC|STC{cond}[L] CP#,CRd,<address>  */
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (co_proc_number (&str) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3188,7 +4887,7 @@ do_lstc (str, flags)
       || cp_reg_required_here (&str, 12) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3196,7 +4895,7 @@ do_lstc (str, flags)
       || cp_address_required_here (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3207,19 +4906,18 @@ do_lstc (str, flags)
 
 static void
 do_co_reg (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   /* Co-processor register transfer.
      Format: <MCR|MRC>{cond} CP#,<expr1>,Rd,CRn,CRm{,<expr2>}  */
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (co_proc_number (&str) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3227,7 +4925,7 @@ do_co_reg (str, flags)
       || cp_opc_expr (&str, 21, 3) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3235,7 +4933,7 @@ do_co_reg (str, flags)
       || reg_required_here (&str, 12) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3243,7 +4941,7 @@ do_co_reg (str, flags)
       || cp_reg_required_here (&str, 16) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3251,7 +4949,7 @@ do_co_reg (str, flags)
       || cp_reg_required_here (&str, 0) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3260,10 +4958,14 @@ do_co_reg (str, flags)
       if (cp_opc_expr (&str, 5, 3) == FAIL)
        {
          if (!inst.error)
-           inst.error = bad_args;
+           inst.error = BAD_ARGS;
          return;
        }
     }
+  if (flags)
+    {
+      inst.error = BAD_COND;
+    }
 
   end_of_line (str);
   return;
@@ -3271,19 +4973,18 @@ do_co_reg (str, flags)
 
 static void
 do_fp_ctrl (str, flags)
-     char *str;
-     unsigned long flags;
+     char * str;
+     unsigned long flags ATTRIBUTE_UNUSED;
 {
   /* FP control registers.
      Format: <WFS|RFS|WFC|RFC>{cond} Rn  */
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (reg_required_here (&str, 12) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3293,11 +4994,10 @@ do_fp_ctrl (str, flags)
 
 static void
 do_fp_ldst (str, flags)
-     char *str;
-     unsigned long flags;
+     char * str;
+     unsigned long flags ATTRIBUTE_UNUSED;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   switch (inst.suffix)
     {
@@ -3319,7 +5019,7 @@ do_fp_ldst (str, flags)
   if (fp_reg_required_here (&str, 12) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3327,7 +5027,7 @@ do_fp_ldst (str, flags)
       || cp_address_required_here (&str) == FAIL)
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3336,22 +5036,21 @@ do_fp_ldst (str, flags)
 
 static void
 do_fp_ldmstm (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
   int num_regs;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (fp_reg_required_here (&str, 12) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
-  /* Get Number of registers to transfer */
+  /* Get Number of registers to transfer */
   if (skip_past_comma (&str) == FAIL
       || my_get_expression (&inst.reloc.exp, &str))
     {
@@ -3405,23 +5104,21 @@ do_fp_ldmstm (str, flags)
          || *str != '[')
        {
          if (! inst.error)
-           inst.error = bad_args;
+           inst.error = BAD_ARGS;
          return;
        }
 
       str++;
-      while (*str == ' ')
-       str++;
+      skip_whitespace (str);
 
       if ((reg = reg_required_here (&str, 16)) == FAIL)
        return;
 
-      while (*str == ' ')
-       str++;
+      skip_whitespace (str);
 
       if (*str != ']')
        {
-         inst.error = bad_args;
+         inst.error = BAD_ARGS;
          return;
        }
 
@@ -3432,7 +5129,8 @@ do_fp_ldmstm (str, flags)
          str++;
          if (reg == REG_PC)
            {
-             inst.error = _("R15 not allowed as base register with write-back");
+             inst.error =
+               _("R15 not allowed as base register with write-back");
              return;
            }
        }
@@ -3441,14 +5139,14 @@ do_fp_ldmstm (str, flags)
 
       if (flags & CP_T_Pre)
        {
-         /* Pre-decrement */
+         /* Pre-decrement */
          offset = 3 * num_regs;
          if (write_back)
            flags |= CP_T_WB;
        }
       else
        {
-         /* Post-increment */
+         /* Post-increment */
          if (write_back)
            {
              flags |= CP_T_WB;
@@ -3469,7 +5167,7 @@ do_fp_ldmstm (str, flags)
           || cp_address_required_here (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3478,11 +5176,10 @@ do_fp_ldmstm (str, flags)
 
 static void
 do_fp_dyadic (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   switch (inst.suffix)
     {
@@ -3501,7 +5198,7 @@ do_fp_dyadic (str, flags)
   if (fp_reg_required_here (&str, 12) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3509,7 +5206,7 @@ do_fp_dyadic (str, flags)
       || fp_reg_required_here (&str, 16) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3517,7 +5214,7 @@ do_fp_dyadic (str, flags)
       || fp_op2 (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3528,11 +5225,10 @@ do_fp_dyadic (str, flags)
 
 static void
 do_fp_monadic (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   switch (inst.suffix)
     {
@@ -3551,7 +5247,7 @@ do_fp_monadic (str, flags)
   if (fp_reg_required_here (&str, 12) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3559,7 +5255,7 @@ do_fp_monadic (str, flags)
       || fp_op2 (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3570,16 +5266,15 @@ do_fp_monadic (str, flags)
 
 static void
 do_fp_cmp (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (fp_reg_required_here (&str, 16) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3587,7 +5282,7 @@ do_fp_cmp (str, flags)
       || fp_op2 (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3598,11 +5293,10 @@ do_fp_cmp (str, flags)
 
 static void
 do_fp_from_reg (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   switch (inst.suffix)
     {
@@ -3621,7 +5315,7 @@ do_fp_from_reg (str, flags)
   if (fp_reg_required_here (&str, 16) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3629,7 +5323,7 @@ do_fp_from_reg (str, flags)
       || reg_required_here (&str, 12) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3640,11 +5334,10 @@ do_fp_from_reg (str, flags)
 
 static void
 do_fp_to_reg (str, flags)
-     char *str;
+     char * str;
      unsigned long flags;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (reg_required_here (&str, 12) == FAIL)
     return;
@@ -3653,7 +5346,7 @@ do_fp_to_reg (str, flags)
       || fp_reg_required_here (&str, 0) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -3662,17 +5355,18 @@ do_fp_to_reg (str, flags)
   return;
 }
 
-/* Thumb specific routines */
+/* Thumb specific routines */
 
 /* Parse and validate that a register is of the right form, this saves
-   repeated checking of this information in many similar cases. 
-   Unlike the 32-bit case we do not insert the register into the opcode 
-   here, since the position is often unknown until the full instruction 
+   repeated checking of this information in many similar cases.
+   Unlike the 32-bit case we do not insert the register into the opcode
+   here, since the position is often unknown until the full instruction
    has been parsed.  */
+
 static int
 thumb_reg (strp, hi_lo)
-     char **strp;
-     int hi_lo;
+     char ** strp;
+     int     hi_lo;
 {
   int reg;
 
@@ -3706,25 +5400,25 @@ thumb_reg (strp, hi_lo)
 
 /* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode
    was SUB.  */
+
 static void
 thumb_add_sub (str, subtract)
-     char *str;
-     int subtract;
+     char * str;
+     int    subtract;
 {
   int Rd, Rs, Rn = FAIL;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
       || skip_past_comma (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
-  if (*str == '#')
+  if (is_immediate_prefix (*str))
     {
       Rs = Rd;
       str++;
@@ -3738,12 +5432,12 @@ thumb_add_sub (str, subtract)
 
       if (skip_past_comma (&str) == FAIL)
        {
-         /* Two operand format, shuffle the registers and pretend there 
-            are 3 */
+         /* Two operand format, shuffle the registers
+            and pretend there are 3.  */
          Rn = Rs;
          Rs = Rd;
        }
-      else if (*str == '#')
+      else if (is_immediate_prefix (*str))
        {
          str++;
          if (my_get_expression (&inst.reloc.exp, &str))
@@ -3754,7 +5448,7 @@ thumb_add_sub (str, subtract)
     }
 
   /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
-     for the latter case, EXPR contains the immediate that was found. */
+     for the latter case, EXPR contains the immediate that was found.  */
   if (Rn != FAIL)
     {
       /* All register format.  */
@@ -3766,7 +5460,7 @@ thumb_add_sub (str, subtract)
              return;
            }
 
-         /* Can't do this for SUB */
+         /* Can't do this for SUB */
          if (subtract)
            {
              inst.error = _("subtract valid only on lo regs");
@@ -3800,7 +5494,7 @@ thumb_add_sub (str, subtract)
       if (inst.reloc.exp.X_op != O_constant)
        {
          /* Value isn't known yet, all we can do is store all the fragments
-            we know about in the instruction and let the reloc hacking 
+            we know about in the instruction and let the reloc hacking
             work it all out.  */
          inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs;
          inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
@@ -3817,7 +5511,7 @@ thumb_add_sub (str, subtract)
              offset = -offset;
              subtract = 1;
 
-             /* Quick check, in case offset is MIN_INT */
+             /* Quick check, in case offset is MIN_INT */
              if (offset < 0)
                {
                  inst.error = _("immediate value out of range");
@@ -3871,48 +5565,48 @@ thumb_add_sub (str, subtract)
            }
        }
     }
+
   end_of_line (str);
 }
 
 static void
 thumb_shift (str, shift)
-     char *str;
-     int shift;
+     char * str;
+     int    shift;
 {
   int Rd, Rs, Rn = FAIL;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
       || skip_past_comma (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
-  if (*str == '#')
+  if (is_immediate_prefix (*str))
     {
       /* Two operand immediate format, set Rs to Rd.  */
       Rs = Rd;
-      str++;
+      str ++;
       if (my_get_expression (&inst.reloc.exp, &str))
        return;
     }
   else
     {
-      if ((Rs =  thumb_reg (&str, THUMB_REG_LO)) == FAIL)
+      if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
        return;
 
       if (skip_past_comma (&str) == FAIL)
        {
-         /* Two operand format, shuffle the registers and pretend there
-            are 3 */
+         /* Two operand format, shuffle the registers
+            and pretend there are 3.  */
          Rn = Rs;
          Rs = Rd;
        }
-      else if (*str == '#')
+      else if (is_immediate_prefix (*str))
        {
          str++;
          if (my_get_expression (&inst.reloc.exp, &str))
@@ -3923,7 +5617,7 @@ thumb_shift (str, shift)
     }
 
   /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
-     for the latter case, EXPR contains the immediate that was found. */
+     for the latter case, EXPR contains the immediate that was found.  */
 
   if (Rn != FAIL)
     {
@@ -3954,8 +5648,7 @@ thumb_shift (str, shift)
       if (inst.reloc.exp.X_op != O_constant)
        {
          /* Value isn't known yet, create a dummy reloc and let reloc
-            hacking fix it up */
-
+            hacking fix it up.  */
          inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
        }
       else
@@ -3968,11 +5661,11 @@ thumb_shift (str, shift)
              return;
            }
 
-         /* Shifts of zero are handled by converting to LSL */
+         /* Shifts of zero are handled by converting to LSL */
          if (shift_value == 0)
            inst.instruction = T_OPCODE_LSL_I;
 
-         /* Shifts of 32 are encoded as a shift of zero */
+         /* Shifts of 32 are encoded as a shift of zero */
          if (shift_value == 32)
            shift_value = 0;
 
@@ -3981,28 +5674,28 @@ thumb_shift (str, shift)
 
       inst.instruction |= Rd | (Rs << 3);
     }
+
   end_of_line (str);
 }
 
 static void
 thumb_mov_compare (str, move)
-     char *str;
-     int move;
+     char * str;
+     int    move;
 {
   int Rd, Rs = FAIL;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
       || skip_past_comma (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
-  if (*str == '#')
+  if (is_immediate_prefix (*str))
     {
       str++;
       if (my_get_expression (&inst.reloc.exp, &str))
@@ -4017,7 +5710,7 @@ thumb_mov_compare (str, move)
        {
          if (move == THUMB_MOVE)
            /* A move of two lowregs is encoded as ADD Rd, Rs, #0
-              since a MOV instruction produces unpredictable results */
+              since a MOV instruction produces unpredictable results */
            inst.instruction = T_OPCODE_ADD_I3;
          else
            inst.instruction = T_OPCODE_CMP_LR;
@@ -4075,20 +5768,19 @@ thumb_mov_compare (str, move)
 
 static void
 thumb_load_store (str, load_store, size)
-     char *str;
-     int load_store;
-     int size;
+     char * str;
+     int    load_store;
+     int    size;
 {
   int Rd, Rb, Ro = FAIL;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
       || skip_past_comma (&str) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
@@ -4100,7 +5792,7 @@ thumb_load_store (str, load_store, size)
 
       if (skip_past_comma (&str) != FAIL)
        {
-         if (*str == '#')
+         if (is_immediate_prefix (*str))
            {
              str++;
              if (my_get_expression (&inst.reloc.exp, &str))
@@ -4124,17 +5816,16 @@ thumb_load_store (str, load_store, size)
     }
   else if (*str == '=')
     {
-      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
+      /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op */
       str++;
 
-      while (*str == ' ')
-       str++;
+      skip_whitespace (str);
 
       if (my_get_expression (& inst.reloc.exp, & str))
        return;
 
       end_of_line (str);
-      
+
       if (   inst.reloc.exp.X_op != O_constant
          && inst.reloc.exp.X_op != O_symbol)
        {
@@ -4145,25 +5836,26 @@ thumb_load_store (str, load_store, size)
       if (inst.reloc.exp.X_op == O_constant
          && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
        {
-         /* This can be done with a mov instruction */
+         /* This can be done with a mov instruction */
 
          inst.instruction  = T_OPCODE_MOV_I8 | (Rd << 8);
          inst.instruction |= inst.reloc.exp.X_add_number;
-         return; 
+         return;
        }
 
-      /* Insert into literal pool */     
+      /* Insert into literal pool.  */
       if (add_to_lit_pool () == FAIL)
        {
          if (!inst.error)
-           inst.error = "literal pool insertion failed"; 
+           inst.error = "literal pool insertion failed";
          return;
        }
 
       inst.reloc.type   = BFD_RELOC_ARM_THUMB_OFFSET;
       inst.reloc.pc_rel = 1;
       inst.instruction  = T_OPCODE_LDR_PC | (Rd << 8);
-      inst.reloc.exp.X_add_number += 4; /* Adjust ARM pipeline offset to Thumb */
+      /* Adjust ARM pipeline offset to Thumb.  */
+      inst.reloc.exp.X_add_number += 4;
 
       return;
     }
@@ -4174,7 +5866,7 @@ thumb_load_store (str, load_store, size)
 
       inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
       inst.reloc.pc_rel = 1;
-      inst.reloc.exp.X_add_number -= 4; /* Pipeline offset */
+      inst.reloc.exp.X_add_number -= 4; /* Pipeline offset */
       inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
       end_of_line (str);
       return;
@@ -4228,7 +5920,7 @@ thumb_load_store (str, load_store, size)
     }
   else if (Ro == FAIL)
     {
-      /* Immediate offset */
+      /* Immediate offset */
       if (size == THUMB_WORD)
        inst.instruction = (load_store == THUMB_LOAD
                            ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW);
@@ -4244,7 +5936,7 @@ thumb_load_store (str, load_store, size)
       if (inst.reloc.exp.X_op == O_constant)
        {
          unsigned offset = inst.reloc.exp.X_add_number;
-         
+
          if (offset & ~(0x1f << size))
            {
              inst.error = _("Invalid offset");
@@ -4257,7 +5949,7 @@ thumb_load_store (str, load_store, size)
     }
   else
     {
-      /* Register offset */
+      /* Register offset */
       if (size == THUMB_WORD)
        inst.instruction = (load_store == THUMB_LOAD
                            ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW);
@@ -4276,33 +5968,30 @@ thumb_load_store (str, load_store, size)
 
 static void
 do_t_nop (str)
-     char *str;
+     char * str;
 {
-  /* Do nothing */
+  /* Do nothing */
   end_of_line (str);
   return;
 }
 
-/* Handle the Format 4 instructions that do not have equivalents in other 
+/* Handle the Format 4 instructions that do not have equivalents in other
    formats.  That is, ADC, AND, EOR, SBC, ROR, TST, NEG, CMN, ORR, MUL,
    BIC and MVN.  */
+
 static void
 do_t_arit (str)
-     char *str;
+     char * str;
 {
   int Rd, Rs, Rn;
 
-  while (*str == ' ')
-    str++;
-
-  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
-    return;
+  skip_whitespace (str);
 
-  if (skip_past_comma (&str) == FAIL
+  if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
+      || skip_past_comma (&str) == FAIL
       || (Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
     {
-      if (! inst.error)
-       inst.error = bad_args;
+      inst.error = BAD_ARGS;
       return;
     }
 
@@ -4314,9 +6003,9 @@ do_t_arit (str)
       if (inst.instruction == T_OPCODE_TST
          || inst.instruction == T_OPCODE_CMN
          || inst.instruction == T_OPCODE_NEG
-         || inst.instruction == T_OPCODE_MVN)
+         || inst.instruction == T_OPCODE_MVN)
        {
-         inst.error = bad_args;
+         inst.error = BAD_ARGS;
          return;
        }
 
@@ -4341,21 +6030,21 @@ do_t_arit (str)
 
 static void
 do_t_add (str)
-     char *str;
+     char * str;
 {
   thumb_add_sub (str, 0);
 }
 
 static void
 do_t_asr (str)
-     char *str;
+     char * str;
 {
   thumb_shift (str, THUMB_ASR);
 }
 
 static void
 do_t_branch9 (str)
-     char *str;
+     char * str;
 {
   if (my_get_expression (&inst.reloc.exp, &str))
     return;
@@ -4366,7 +6055,7 @@ do_t_branch9 (str)
 
 static void
 do_t_branch12 (str)
-     char *str;
+     char * str;
 {
   if (my_get_expression (&inst.reloc.exp, &str))
     return;
@@ -4385,23 +6074,23 @@ find_real_start (symbolP)
   const char * name = S_GET_NAME (symbolP);
   symbolS *    new_target;
 
-  /* This definitonmust agree with the one in gcc/config/arm/thumb.c */
+  /* This definiton must agree with the one in gcc/config/arm/thumb.c.  */
 #define STUB_NAME ".real_start_of"
 
   if (name == NULL)
-    abort();
+    abort ();
 
   /* Names that start with '.' are local labels, not function entry points.
      The compiler may generate BL instructions to these labels because it
      needs to perform a branch to a far away location.  */
   if (name[0] == '.')
     return symbolP;
-  
+
   real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1);
   sprintf (real_start, "%s%s", STUB_NAME, name);
 
   new_target = symbol_find (real_start);
-  
+
   if (new_target == NULL)
     {
       as_warn ("Failed to find real start of function: %s\n", name);
@@ -4413,13 +6102,13 @@ find_real_start (symbolP)
   return new_target;
 }
 
-
 static void
 do_t_branch23 (str)
-     char *str;
+     char * str;
 {
-  if (my_get_expression (&inst.reloc.exp, &str))
+  if (my_get_expression (& inst.reloc.exp, & str))
     return;
+
   inst.reloc.type   = BFD_RELOC_THUMB_PCREL_BRANCH23;
   inst.reloc.pc_rel = 1;
   end_of_line (str);
@@ -4432,17 +6121,17 @@ do_t_branch23 (str)
       && inst.reloc.exp.X_add_symbol != NULL
       && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
       && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
-    inst.reloc.exp.X_add_symbol = find_real_start (inst.reloc.exp.X_add_symbol);
+    inst.reloc.exp.X_add_symbol =
+      find_real_start (inst.reloc.exp.X_add_symbol);
 }
 
 static void
 do_t_bx (str)
-     char *str;
+     char * str;
 {
   int reg;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((reg = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
     return;
@@ -4459,20 +6148,19 @@ do_t_bx (str)
 
 static void
 do_t_compare (str)
-     char *str;
+     char * str;
 {
   thumb_mov_compare (str, THUMB_COMPARE);
 }
 
 static void
 do_t_ldmstm (str)
-     char *str;
+     char * str;
 {
   int Rb;
   long range;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
     return;
@@ -4486,13 +6174,13 @@ do_t_ldmstm (str)
       || (range = reg_list (&str)) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
   if (inst.reloc.type != BFD_RELOC_NONE)
     {
-      /* This really doesn't seem worth it. */
+      /* This really doesn't seem worth it.  */
       inst.reloc.type = BFD_RELOC_NONE;
       inst.error = _("Expression too complex");
       return;
@@ -4510,33 +6198,32 @@ do_t_ldmstm (str)
 
 static void
 do_t_ldr (str)
-     char *str;
+     char * str;
 {
   thumb_load_store (str, THUMB_LOAD, THUMB_WORD);
 }
 
 static void
 do_t_ldrb (str)
-     char *str;
+     char * str;
 {
   thumb_load_store (str, THUMB_LOAD, THUMB_BYTE);
 }
 
 static void
 do_t_ldrh (str)
-     char *str;
+     char * str;
 {
   thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD);
 }
 
 static void
 do_t_lds (str)
-     char *str;
+     char * str;
 {
   int Rd, Rb, Ro;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
       || skip_past_comma (&str) == FAIL
@@ -4557,44 +6244,43 @@ do_t_lds (str)
 
 static void
 do_t_lsl (str)
-     char *str;
+     char * str;
 {
   thumb_shift (str, THUMB_LSL);
 }
 
 static void
 do_t_lsr (str)
-     char *str;
+     char * str;
 {
   thumb_shift (str, THUMB_LSR);
 }
 
 static void
 do_t_mov (str)
-     char *str;
+     char * str;
 {
   thumb_mov_compare (str, THUMB_MOVE);
 }
 
 static void
 do_t_push_pop (str)
-     char *str;
+     char * str;
 {
   long range;
 
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if ((range = reg_list (&str)) == FAIL)
     {
       if (! inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
   if (inst.reloc.type != BFD_RELOC_NONE)
     {
-      /* This really doesn't seem worth it. */
+      /* This really doesn't seem worth it.  */
       inst.reloc.type = BFD_RELOC_NONE;
       inst.error = _("Expression too complex");
       return;
@@ -4623,38 +6309,37 @@ do_t_push_pop (str)
 
 static void
 do_t_str (str)
-     char *str;
+     char * str;
 {
   thumb_load_store (str, THUMB_STORE, THUMB_WORD);
 }
 
 static void
 do_t_strb (str)
-     char *str;
+     char * str;
 {
   thumb_load_store (str, THUMB_STORE, THUMB_BYTE);
 }
 
 static void
 do_t_strh (str)
-     char *str;
+     char * str;
 {
   thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD);
 }
 
 static void
 do_t_sub (str)
-     char *str;
+     char * str;
 {
   thumb_add_sub (str, 1);
 }
 
 static void
 do_t_swi (str)
-     char *str;
+     char * str;
 {
-  while (*str == ' ')
-    str++;
+  skip_whitespace (str);
 
   if (my_get_expression (&inst.reloc.exp, &str))
     return;
@@ -4666,26 +6351,30 @@ do_t_swi (str)
 
 static void
 do_t_adr (str)
-     char *str;
+     char * str;
 {
+  int reg;
+
   /* This is a pseudo-op of the form "adr rd, label" to be converted
-     into a relative address of the form "add rd, pc, #label-.-4" */
-  while (*str == ' ')
-    str++;
+     into a relative address of the form "add rd, pc, #label-.-4".  */
+  skip_whitespace (str);
 
-  if (reg_required_here (&str, 4) == FAIL  /* Store Rd in temporary location inside instruction.  */
+  /* Store Rd in temporary location inside instruction.  */
+  if ((reg = reg_required_here (&str, 4)) == FAIL
+      || (reg > 7)  /* For Thumb reg must be r0..r7.  */
       || skip_past_comma (&str) == FAIL
       || my_get_expression (&inst.reloc.exp, &str))
     {
       if (!inst.error)
-       inst.error = bad_args;
+       inst.error = BAD_ARGS;
       return;
     }
 
   inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
-  inst.reloc.exp.X_add_number -= 4; /* PC relative adjust */
+  inst.reloc.exp.X_add_number -= 4; /* PC relative adjust */
   inst.reloc.pc_rel = 1;
-  inst.instruction |= REG_PC; /* Rd is already placed into the instruction */
+  inst.instruction |= REG_PC; /* Rd is already placed into the instruction.  */
+
   end_of_line (str);
 }
 
@@ -4693,10 +6382,10 @@ static void
 insert_reg (entry)
      int entry;
 {
-  int len = strlen (reg_table[entry].name) + 2;
-  char *buf = (char *) xmalloc (len);
-  char *buf2 = (char *) xmalloc (len);
-  int i = 0;
+  int    len  = strlen (reg_table[entry].name) + 2;
+  char * buf  = (char *) xmalloc (len);
+  char * buf2 = (char *) xmalloc (len);
+  int    i    = 0;
 
 #ifdef REGISTER_PREFIX
   buf[i++] = REGISTER_PREFIX;
@@ -4709,8 +6398,8 @@ insert_reg (entry)
 
   buf2[i] = '\0';
 
-  hash_insert (arm_reg_hsh, buf, (PTR) &reg_table[entry]);
-  hash_insert (arm_reg_hsh, buf2, (PTR) &reg_table[entry]);
+  hash_insert (arm_reg_hsh, buf,  (PTR) & reg_table[entry]);
+  hash_insert (arm_reg_hsh, buf2, (PTR) & reg_table[entry]);
 }
 
 static void
@@ -4719,7 +6408,7 @@ insert_reg_alias (str, regnum)
      int regnum;
 {
   struct reg_entry *new =
-    (struct reg_entry *)xmalloc (sizeof (struct reg_entry));
+    (struct reg_entry *) xmalloc (sizeof (struct reg_entry));
   char *name = xmalloc (strlen (str) + 1);
   strcpy (name, str);
 
@@ -4735,31 +6424,32 @@ set_constant_flonums ()
   int i;
 
   for (i = 0; i < NUM_FLOAT_VALS; i++)
-    if (atof_ieee ((char *)fp_const[i], 'x', fp_values[i]) == NULL)
+    if (atof_ieee ((char *) fp_const[i], 'x', fp_values[i]) == NULL)
       abort ();
 }
 
 void
 md_begin ()
 {
-  int i;
-  
-  if ((arm_ops_hsh = hash_new ()) == NULL
+  unsigned mach;
+  unsigned int i;
+
+  if (   (arm_ops_hsh = hash_new ()) == NULL
       || (arm_tops_hsh = hash_new ()) == NULL
       || (arm_cond_hsh = hash_new ()) == NULL
       || (arm_shift_hsh = hash_new ()) == NULL
       || (arm_reg_hsh = hash_new ()) == NULL
       || (arm_psr_hsh = hash_new ()) == NULL)
     as_fatal (_("Virtual memory exhausted"));
-    
+
   for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
     hash_insert (arm_ops_hsh, insns[i].template, (PTR) (insns + i));
   for (i = 0; i < sizeof (tinsns) / sizeof (struct thumb_opcode); i++)
     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));
 
@@ -4771,54 +6461,80 @@ md_begin ()
 #if defined OBJ_COFF || defined OBJ_ELF
   {
     unsigned int flags = 0;
-    
-    /* Set the flags in the private structure */
+
+    /* Set the flags in the private structure */
     if (uses_apcs_26)      flags |= F_APCS26;
     if (support_interwork) flags |= F_INTERWORK;
     if (uses_apcs_float)   flags |= F_APCS_FLOAT;
     if (pic_code)          flags |= F_PIC;
+    if ((cpu_variant & FPU_ALL) == FPU_NONE) flags |= F_SOFT_FLOAT;
 
     bfd_set_private_flags (stdoutput, flags);
-  }
-#endif
-  
-  {
-    unsigned mach;
-    
-    /* Record the CPU type as well */
-    switch (cpu_variant & ARM_CPU_MASK)
+
+    /* 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)
       {
-      case ARM_2:
-       mach = bfd_mach_arm_2;
-       break;
-       
-      case ARM_3: /* also ARM_250 */
-       mach = bfd_mach_arm_2a;
-       break;
+       asection * sec;
 
-      default:
-      case ARM_6 | ARM_3 | ARM_2:      /* Actually no CPU type defined */
-       mach = bfd_mach_arm_4;
-       break;
-       
-      case ARM_7:                      /* also ARM_6 */
-       mach = bfd_mach_arm_3;
-       break;
-      }
+       sec = bfd_make_section (stdoutput, ".arm.atpcs");
 
-    /* Catch special cases */
-    if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT))
-      {
-       if (cpu_variant & ARM_THUMB)
-         mach = bfd_mach_arm_4T;
-       else if ((cpu_variant & ARM_ARCHv4) == ARM_ARCHv4)
-         mach = bfd_mach_arm_4;
-       else if (cpu_variant & ARM_LONGMUL)
-         mach = bfd_mach_arm_3M;
+       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);
+         }
       }
-       
-    bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
   }
+#endif
+
+  /* Record the CPU type as well.  */
+  switch (cpu_variant & ARM_CPU_MASK)
+    {
+    case ARM_2:
+      mach = bfd_mach_arm_2;
+      break;
+
+    case ARM_3:                /* Also ARM_250.  */
+      mach = bfd_mach_arm_2a;
+      break;
+
+    default:
+    case ARM_6 | ARM_3 | ARM_2:        /* Actually no CPU type defined.  */
+      mach = bfd_mach_arm_4;
+      break;
+
+    case ARM_7:                /* Also ARM_6.  */
+      mach = bfd_mach_arm_3;
+      break;
+    }
+
+  /* Catch special cases.  */
+  if (cpu_variant & ARM_EXT_XSCALE)
+    mach = bfd_mach_arm_XScale;
+  else if (cpu_variant & ARM_EXT_V5E)
+    mach = bfd_mach_arm_5TE;
+  else if (cpu_variant & ARM_EXT_V5)
+    {
+      if (cpu_variant & ARM_EXT_THUMB)
+       mach = bfd_mach_arm_5T;
+      else
+       mach = bfd_mach_arm_5;
+    }
+  else if (cpu_variant & ARM_EXT_HALFWORD)
+    {
+      if (cpu_variant & ARM_EXT_THUMB)
+       mach = bfd_mach_arm_4T;
+      else
+       mach = bfd_mach_arm_4;
+    }
+  else if (cpu_variant & ARM_EXT_LONGMUL)
+    mach = bfd_mach_arm_3M;
+
+  bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
 }
 
 /* Turn an integer of n bytes (in val) into a stream of bytes appropriate
@@ -4826,13 +6542,13 @@ md_begin ()
    This knows about the endian-ness of the target machine and does
    THE RIGHT THING, whatever it is.  Possible values for n are 1 (byte)
    2 (short) and 4 (long)  Floating numbers are put out as a series of
-   LITTLENUMS (shorts, here at least)
-   */
+   LITTLENUMS (shorts, here at least).  */
+
 void
 md_number_to_chars (buf, val, n)
-     char *buf;
+     char * buf;
      valueT val;
-     int n;
+     int    n;
 {
   if (target_big_endian)
     number_to_chars_bigendian (buf, val, n);
@@ -4840,13 +6556,13 @@ md_number_to_chars (buf, val, n)
     number_to_chars_littleendian (buf, val, n);
 }
 
-static valueT 
+static valueT
 md_chars_to_number (buf, n)
-     char *buf;
-     int n;
+     char * buf;
+     int    n;
 {
   valueT result = 0;
-  unsigned char *where = (unsigned char *) buf;
+  unsigned char * where = (unsigned char *) buf;
 
   if (target_big_endian)
     {
@@ -4869,8 +6585,8 @@ md_chars_to_number (buf, n)
 }
 
 /* Turn a string in input_line_pointer into a floating point constant
-   of type TYPE, and store the appropriate bytes in *litP.  The number
-   of LITTLENUMS emitted is stored in *sizeP .  An error message is
+   of type TYPE, and store the appropriate bytes in *LITP.  The number
+   of LITTLENUMS emitted is stored in *SIZEP.  An error message is
    returned, or NULL on OK.
 
    Note that fp constants aren't represent in the normal way on the ARM.
@@ -4884,9 +6600,9 @@ md_chars_to_number (buf, n)
 
 char *
 md_atof (type, litP, sizeP)
-     char type;
-     char *litP;
-     int *sizeP;
+     char   type;
+     char * litP;
+     int *  sizeP;
 {
   int prec;
   LITTLENUM_TYPE words[MAX_LITTLENUMS];
@@ -4952,66 +6668,88 @@ md_atof (type, litP, sizeP)
   return 0;
 }
 
-/* We have already put the pipeline compensation in the instruction */
+/* The knowledge of the PC's pipeline offset is built into the insns
+   themselves.  */
 
 long
 md_pcrel_from (fixP)
-     fixS *fixP;
+     fixS * fixP;
 {
-  if (   fixP->fx_addsy
+  if (fixP->fx_addsy
       && S_GET_SEGMENT (fixP->fx_addsy) == undefined_section
       && fixP->fx_subsy == NULL)
-    return 0;  /* HACK */
+    return 0;
 
   if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD))
     {
       /* PC relative addressing on the Thumb is slightly odd
         as the bottom two bits of the PC are forced to zero
-        for the calculation */
+        for the calculation */
       return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3;
     }
 
+#ifdef TE_WINCE
+  /* The pattern was adjusted to accomodate CE's off-by-one fixups,
+     so we un-adjust here to compensate for the accomodation.  */
+  return fixP->fx_where + fixP->fx_frag->fr_address + 8;
+#else
   return fixP->fx_where + fixP->fx_frag->fr_address;
+#endif
 }
 
-/* Round up a section size to the appropriate boundary. */
+/* Round up a section size to the appropriate boundary.  */
+
 valueT
 md_section_align (segment, size)
-     segT segment;
+     segT   segment ATTRIBUTE_UNUSED;
      valueT size;
 {
-/* start-sanitize-armelf */
 #ifdef OBJ_ELF
-  /* Don't align the dwarf2 debug sections */
-  if (!strncmp(segment->name,".debug",5))
-    return size;
-#endif
-/* end-sanitize-armelf */
-  /* Round all sects to multiple of 4 */
+  return size;
+#else
+  /* Round all sects to multiple of 4.  */
   return (size + 3) & ~3;
+#endif
 }
 
-/* We have no need to default values of symbols.  */
+/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
+   Otherwise we have no need to default values of symbols.  */
 
-/* ARGSUSED */
 symbolS *
 md_undefined_symbol (name)
-     char *name;
+     char * name ATTRIBUTE_UNUSED;
 {
+#ifdef OBJ_ELF
+  if (name[0] == '_' && name[1] == 'G'
+      && streq (name, GLOBAL_OFFSET_TABLE_NAME))
+    {
+      if (!GOT_symbol)
+       {
+         if (symbol_find (name))
+           as_bad ("GOT already in the symbol table");
+
+         GOT_symbol = symbol_new (name, undefined_section,
+                                  (valueT) 0, & zero_address_frag);
+       }
+
+      return GOT_symbol;
+    }
+#endif
+
   return 0;
 }
 
-/* arm_reg_parse () := if it looks like a register, return its token and 
-   advance the pointer. */
+/* arm_reg_parse () := if it looks like a register, return its token and
+   advance the pointer.  */
 
 static int
 arm_reg_parse (ccp)
-     register char **ccp;
+     register char ** ccp;
 {
-  char *start = *ccp;
-  char c;
-  char *p;
-  struct reg_entry *reg;
+  char * start = * ccp;
+  char   c;
+  char * p;
+  struct reg_entry * reg;
 
 #ifdef REGISTER_PREFIX
   if (*start != REGISTER_PREFIX)
@@ -5034,7 +6772,7 @@ arm_reg_parse (ccp)
   *--p = 0;
   reg = (struct reg_entry *) hash_find (arm_reg_hsh, start);
   *p = c;
-  
+
   if (reg)
     {
       *ccp = p;
@@ -5044,51 +6782,27 @@ arm_reg_parse (ccp)
   return FAIL;
 }
 
-static int
-arm_psr_parse (ccp)
-     register char **ccp;
-{
-  char *start = *ccp;
-  char c, *p;
-  CONST struct asm_psr *psr;
-
-  p = start;
-  c = *p++;
-  while (isalpha (c) || c == '_')
-    c = *p++;
-
-  *--p = 0;  
-  psr = (CONST struct asm_psr *) hash_find (arm_psr_hsh, start);
-  *p = c;
-
-  if (psr)
-    {
-      *ccp = p;
-      return psr->number;
-    }
-
-  return FAIL;
-}
-
 int
 md_apply_fix3 (fixP, val, seg)
-     fixS *fixP;
-     valueT *val;
-     segT seg;
-{
-  offsetT value = *val;
-  offsetT newval;
-  unsigned int newimm;
-  unsigned long temp;
-  int sign;
-  char *buf = fixP->fx_where + fixP->fx_frag->fr_literal;
-  arm_fix_data *arm_data = (arm_fix_data *) fixP->tc_fix_data;
+     fixS *   fixP;
+     valueT * val;
+     segT     seg;
+{
+  offsetT        value = * val;
+  offsetT        newval;
+  unsigned int   newimm;
+  unsigned long  temp;
+  int            sign;
+  char *         buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+  arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data;
 
   assert (fixP->fx_r_type < BFD_RELOC_UNUSED);
 
   /* Note whether this will delete the relocation.  */
-#if 0 /* patch from REarnshaw to JDavis (disabled for the moment, since it doesn't work fully) */
-  if ((fixP->fx_addsy == 0 || fixP->fx_addsy->sy_value.X_op == O_constant)
+#if 0
+  /* Patch from REarnshaw to JDavis (disabled for the moment, since it
+     doesn't work fully.)  */
+  if ((fixP->fx_addsy == 0 || symbol_constant_p (fixP->fx_addsy))
       && !fixP->fx_pcrel)
 #else
   if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
@@ -5104,14 +6818,18 @@ md_apply_fix3 (fixP, val, seg)
          && S_IS_DEFINED (fixP->fx_addsy)
          && S_GET_SEGMENT (fixP->fx_addsy) != seg)
        {
-         if (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH)
+         if (target_oabi
+             && (fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
+                 || fixP->fx_r_type == BFD_RELOC_ARM_PCREL_BLX
+                 ))
            value = 0;
          else
            value += md_pcrel_from (fixP);
        }
     }
 
-  fixP->fx_addnumber = value;  /* Remember value for emit_reloc */
+  /* Remember value for emit_reloc.  */
+  fixP->fx_addnumber = value;
 
   switch (fixP->fx_r_type)
     {
@@ -5125,7 +6843,8 @@ md_apply_fix3 (fixP, val, seg)
          && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL)
        {
          as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("invalid constant after fixup\n"));
+                       _("invalid constant (%lx) after fixup"),
+                       (unsigned long) value);
          break;
        }
 
@@ -5133,15 +6852,67 @@ md_apply_fix3 (fixP, val, seg)
       md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
       break;
 
-     case BFD_RELOC_ARM_OFFSET_IMM:
+    case BFD_RELOC_ARM_ADRL_IMMEDIATE:
+      {
+       unsigned int highpart = 0;
+       unsigned int newinsn  = 0xe1a00000; /* nop.  */
+       newimm = validate_immediate (value);
+       temp = md_chars_to_number (buf, INSN_SIZE);
+
+       /* If the instruction will fail, see if we can fix things up by
+          changing the opcode.  */
+       if (newimm == (unsigned int) FAIL
+           && (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL)
+         {
+           /* No ?  OK - try using two ADD instructions to generate
+               the value.  */
+           newimm = validate_immediate_twopart (value, & highpart);
+
+           /* Yes - then make sure that the second instruction is
+               also an add.  */
+           if (newimm != (unsigned int) FAIL)
+             newinsn = temp;
+           /* Still No ?  Try using a negated value.  */
+           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%lx"),
+                             value);
+               break;
+             }
+
+           /* Replace the first operand in the 2nd instruction (which
+              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 |= ((newinsn & 0x0f000) << 4);
+         }
+
+       newimm |= (temp & 0xfffff000);
+       md_number_to_chars (buf, (valueT) newimm, INSN_SIZE);
+
+       highpart |= (newinsn & 0xfffff000);
+       md_number_to_chars (buf + INSN_SIZE, (valueT) highpart, INSN_SIZE);
+      }
+      break;
+
+    case BFD_RELOC_ARM_OFFSET_IMM:
       sign = value >= 0;
-      if ((value = validate_offset_imm (value, 0)) == FAIL)
-        {
-          as_bad (_("bad immediate value for offset (%d)"), val);
-          break;
-        }
+
       if (value < 0)
-       value = -value;
+       value = - value;
+
+      if (validate_offset_imm (value, 0) == FAIL)
+       {
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("bad immediate value for offset (%ld)"),
+                       (long) value);
+         break;
+       }
 
       newval = md_chars_to_number (buf, INSN_SIZE);
       newval &= 0xff7ff000;
@@ -5149,37 +6920,40 @@ md_apply_fix3 (fixP, val, seg)
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
-     case BFD_RELOC_ARM_OFFSET_IMM8:
-     case BFD_RELOC_ARM_HWLITERAL:
+    case BFD_RELOC_ARM_OFFSET_IMM8:
+    case BFD_RELOC_ARM_HWLITERAL:
       sign = value >= 0;
-      if ((value = validate_offset_imm (value, 1)) == FAIL)
-        {
-          if (fixP->fx_r_type == BFD_RELOC_ARM_HWLITERAL)
-           as_bad_where (fixP->fx_file, fixP->fx_line, 
-                       _("invalid literal constant: pool needs to be closer\n"));
-          else
-            as_bad (_("bad immediate value for offset (%d)"), value);
-          break;
-        }
 
       if (value < 0)
-       value = -value;
+       value = - value;
+
+      if (validate_offset_imm (value, 1) == FAIL)
+       {
+         if (fixP->fx_r_type == BFD_RELOC_ARM_HWLITERAL)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("invalid literal constant: pool needs to be closer"));
+         else
+           as_bad (_("bad immediate value for half-word offset (%ld)"),
+                   (long) value);
+         break;
+       }
 
       newval = md_chars_to_number (buf, INSN_SIZE);
       newval &= 0xff7ff0f0;
-      newval |= ((value >> 4) << 8) | value & 0xf | (sign ? INDEX_UP : 0);
+      newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0);
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
     case BFD_RELOC_ARM_LITERAL:
       sign = value >= 0;
+
       if (value < 0)
-       value = -value;
+       value = - value;
 
-      if ((value = validate_offset_imm (value, 0)) == FAIL)
+      if (validate_offset_imm (value, 0) == FAIL)
        {
-         as_bad_where (fixP->fx_file, fixP->fx_line, 
-                       _("invalid literal constant: pool needs to be closer\n"));
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("invalid literal constant: pool needs to be closer"));
          break;
        }
 
@@ -5192,7 +6966,7 @@ md_apply_fix3 (fixP, val, seg)
     case BFD_RELOC_ARM_SHIFT_IMM:
       newval = md_chars_to_number (buf, INSN_SIZE);
       if (((unsigned long) value) > 32
-         || (value == 32 
+         || (value == 32
              && (((newval & 0x60) == 0) || (newval & 0x60) == 0x60)))
        {
          as_bad_where (fixP->fx_file, fixP->fx_line,
@@ -5201,12 +6975,13 @@ md_apply_fix3 (fixP, val, seg)
        }
 
       if (value == 0)
-       newval &= ~0x60;        /* Shifts of zero must be done as lsl */
+       /* Shifts of zero must be done as lsl.  */
+       newval &= ~0x60;
       else if (value == 32)
        value = 0;
       newval &= 0xfffff07f;
       newval |= (value & 0x1f) << 7;
-      md_number_to_chars (buf, newval , INSN_SIZE);
+      md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
     case BFD_RELOC_ARM_SWI:
@@ -5222,11 +6997,11 @@ md_apply_fix3 (fixP, val, seg)
       else
        {
          if (((unsigned long) value) > 0x00ffffff)
-           as_bad_where (fixP->fx_file, fixP->fx_line, 
+           as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("Invalid swi expression"));
          newval = md_chars_to_number (buf, INSN_SIZE) & 0xff000000;
          newval |= value;
-         md_number_to_chars (buf, newval , INSN_SIZE);
+         md_number_to_chars (buf, newval, INSN_SIZE);
        }
       break;
 
@@ -5239,81 +7014,204 @@ md_apply_fix3 (fixP, val, seg)
       break;
 
     case BFD_RELOC_ARM_PCREL_BRANCH:
-      value = (value >> 2) & 0x00ffffff;
       newval = md_chars_to_number (buf, INSN_SIZE);
-      value = (value + (newval & 0x00ffffff)) & 0x00ffffff;
-      newval = value | (newval & 0xff000000);
+
+      /* Sign-extend a 24-bit number.  */
+#define SEXT24(x)      ((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000)
+
+#ifdef OBJ_ELF
+      if (! target_oabi)
+       value = fixP->fx_offset;
+#endif
+
+      /* We are going to store value (shifted right by two) in the
+        instruction, in a 24 bit, signed field.  Thus we need to check
+        that none of the top 8 bits of the shifted value (top 7 bits of
+         the unshifted, unsigned value) are set, or that they are all set.  */
+      if ((value & ~ ((offsetT) 0x1ffffff)) != 0
+         && ((value & ~ ((offsetT) 0x1ffffff)) != ~ ((offsetT) 0x1ffffff)))
+       {
+#ifdef OBJ_ELF
+         /* Normally we would be stuck at this point, since we cannot store
+            the absolute address that is the destination of the branch in the
+            24 bits of the branch instruction.  If however, we happen to know
+            that the destination of the branch is in the same section as the
+            branch instruciton itself, then we can compute the relocation for
+            ourselves and not have to bother the linker with it.
+
+            FIXME: The tests for OBJ_ELF and ! target_oabi are only here
+            because I have not worked out how to do this for OBJ_COFF or
+            target_oabi.  */
+         if (! target_oabi
+             && fixP->fx_addsy != NULL
+             && S_IS_DEFINED (fixP->fx_addsy)
+             && S_GET_SEGMENT (fixP->fx_addsy) == seg)
+           {
+             /* Get pc relative value to go into the branch.  */
+             value = * val;
+
+             /* Permit a backward branch provided that enough bits
+                are set.  Allow a forwards branch, provided that
+                enough bits are clear.  */
+             if (   (value & ~ ((offsetT) 0x1ffffff)) == ~ ((offsetT) 0x1ffffff)
+                 || (value & ~ ((offsetT) 0x1ffffff)) == 0)
+               fixP->fx_done = 1;
+           }
+
+         if (! fixP->fx_done)
+#endif
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("gas can't handle same-section branch dest >= 0x04000000"));
+       }
+
+      value >>= 2;
+      value += SEXT24 (newval);
+
+      if (    (value & ~ ((offsetT) 0xffffff)) != 0
+         && ((value & ~ ((offsetT) 0xffffff)) != ~ ((offsetT) 0xffffff)))
+       as_bad_where (fixP->fx_file, fixP->fx_line,
+                     _("out of range branch"));
+
+      newval = (value & 0x00ffffff) | (newval & 0xff000000);
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
-    case BFD_RELOC_THUMB_PCREL_BRANCH9: /* conditional branch */
+    case BFD_RELOC_ARM_PCREL_BLX:
+      {
+       offsetT hbit;
+       newval = md_chars_to_number (buf, INSN_SIZE);
+
+#ifdef OBJ_ELF
+       if (! target_oabi)
+         value = fixP->fx_offset;
+#endif
+       hbit   = (value >> 1) & 1;
+       value  = (value >> 2) & 0x00ffffff;
+       value  = (value + (newval & 0x00ffffff)) & 0x00ffffff;
+       newval = value | (newval & 0xfe000000) | (hbit << 24);
+       md_number_to_chars (buf, newval, INSN_SIZE);
+      }
+      break;
+
+    case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch.  */
       newval = md_chars_to_number (buf, THUMB_SIZE);
       {
-        addressT diff = (newval & 0xff) << 1;
-        if (diff & 0x100)
-         diff |= ~0xff;
-
-        value += diff;
-        if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("Branch out of range"));
-        newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
+       addressT diff = (newval & 0xff) << 1;
+       if (diff & 0x100)
+         diff |= ~0xff;
+
+       value += diff;
+       if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("Branch out of range"));
+       newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
       }
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
 
-    case BFD_RELOC_THUMB_PCREL_BRANCH12: /* unconditional branch */
+    case BFD_RELOC_THUMB_PCREL_BRANCH12: /* Unconditional branch.  */
       newval = md_chars_to_number (buf, THUMB_SIZE);
       {
-        addressT diff = (newval & 0x7ff) << 1;
-        if (diff & 0x800)
-         diff |= ~0x7ff;
-
-        value += diff;
-        if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
-         as_bad_where (fixP->fx_file, fixP->fx_line,
-                       _("Branch out of range"));
-        newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
+       addressT diff = (newval & 0x7ff) << 1;
+       if (diff & 0x800)
+         diff |= ~0x7ff;
+
+       value += diff;
+       if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("Branch out of range"));
+       newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
       }
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
 
+    case BFD_RELOC_THUMB_PCREL_BLX:
     case BFD_RELOC_THUMB_PCREL_BRANCH23:
       {
-        offsetT newval2;
-        addressT diff;
-       
+       offsetT newval2;
+       addressT diff;
+
        newval  = md_chars_to_number (buf, THUMB_SIZE);
-        newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
-        diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1);
-        if (diff & 0x400000)
+       newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+       diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1);
+       if (diff & 0x400000)
          diff |= ~0x3fffff;
-        value += diff;
-        if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
+#ifdef OBJ_ELF
+       value = fixP->fx_offset;
+#endif
+       value += diff;
+       if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
          as_bad_where (fixP->fx_file, fixP->fx_line,
                        _("Branch with link out of range"));
 
-        newval  = (newval  & 0xf800) | ((value & 0x7fffff) >> 12);
-        newval2 = (newval2 & 0xf800) | ((value & 0xfff) >> 1);
-        md_number_to_chars (buf, newval, THUMB_SIZE);
-        md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+       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);
       }
       break;
 
     case BFD_RELOC_8:
       if (fixP->fx_done || fixP->fx_pcrel)
        md_number_to_chars (buf, value, 1);
+#ifdef OBJ_ELF
+      else if (!target_oabi)
+       {
+         value = fixP->fx_offset;
+         md_number_to_chars (buf, value, 1);
+       }
+#endif
       break;
 
     case BFD_RELOC_16:
       if (fixP->fx_done || fixP->fx_pcrel)
        md_number_to_chars (buf, value, 2);
+#ifdef OBJ_ELF
+      else if (!target_oabi)
+       {
+         value = fixP->fx_offset;
+         md_number_to_chars (buf, value, 2);
+       }
+#endif
+      break;
+
+#ifdef OBJ_ELF
+    case BFD_RELOC_ARM_GOT32:
+    case BFD_RELOC_ARM_GOTOFF:
+      md_number_to_chars (buf, 0, 4);
       break;
+#endif
 
     case BFD_RELOC_RVA:
     case BFD_RELOC_32:
       if (fixP->fx_done || fixP->fx_pcrel)
        md_number_to_chars (buf, value, 4);
+#ifdef OBJ_ELF
+      else if (!target_oabi)
+       {
+         value = fixP->fx_offset;
+         md_number_to_chars (buf, value, 4);
+       }
+#endif
+      break;
+
+#ifdef OBJ_ELF
+    case BFD_RELOC_ARM_PLT32:
+      /* It appears the instruction is fully prepared at this point.  */
+      break;
+#endif
+
+    case BFD_RELOC_ARM_GOTPC:
+      md_number_to_chars (buf, value, 4);
       break;
 
     case BFD_RELOC_ARM_CP_OFF_IMM:
@@ -5324,17 +7222,18 @@ md_apply_fix3 (fixP, val, seg)
       if (value < 0)
        value = -value;
       newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00;
-      newval |= (value >> 2) | (sign ?  INDEX_UP : 0);
-      md_number_to_chars (buf, newval , INSN_SIZE);
+      newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+      md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
     case BFD_RELOC_ARM_THUMB_OFFSET:
       newval = md_chars_to_number (buf, THUMB_SIZE);
-      /* Exactly what ranges, and where the offset is inserted depends on
-        the type of instruction, we can establish this from the top 4 bits */
+      /* Exactly what ranges, and where the offset is inserted depends
+        on the type of instruction, we can establish this from the
+        top 4 bits.  */
       switch (newval >> 12)
        {
-       case 4: /* PC load */
+       case 4: /* PC load */
          /* Thumb PC loads are somewhat odd, bit 1 of the PC is
             forced to zero for these loads, so we will need to round
             up the offset if the instruction address is not word
@@ -5344,47 +7243,49 @@ md_apply_fix3 (fixP, val, seg)
          if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3)
            as_bad_where (fixP->fx_file, fixP->fx_line,
                          _("Invalid offset, target not word aligned (0x%08X)"),
-                          (unsigned int)(fixP->fx_frag->fr_address + fixP->fx_where + value));
+                         (unsigned int) (fixP->fx_frag->fr_address
+                                         + fixP->fx_where + value));
 
          if ((value + 2) & ~0x3fe)
            as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("Invalid offset"));
+                         _("Invalid offset, value too big (0x%08lX)"), value);
 
-          /* Round up, since pc will be rounded down.  */
+         /* Round up, since pc will be rounded down.  */
          newval |= (value + 2) >> 2;
          break;
 
-       case 9: /* SP load/store */
+       case 9: /* SP load/store */
          if (value & ~0x3fc)
            as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("Invalid offset"));
+                         _("Invalid offset, value too big (0x%08lX)"), value);
          newval |= value >> 2;
          break;
 
-       case 6: /* Word load/store */
+       case 6: /* Word load/store */
          if (value & ~0x7c)
            as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("Invalid offset"));
-         newval |= value << 4; /* 6 - 2 */
+                         _("Invalid offset, value too big (0x%08lX)"), value);
+         newval |= value << 4; /* 6 - 2 */
          break;
 
-       case 7: /* Byte load/store */
+       case 7: /* Byte load/store */
          if (value & ~0x1f)
            as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("Invalid offset"));
+                         _("Invalid offset, value too big (0x%08lX)"), value);
          newval |= value << 6;
          break;
 
-       case 8: /* Halfword load/store */
+       case 8: /* Halfword load/store */
          if (value & ~0x3e)
            as_bad_where (fixP->fx_file, fixP->fx_line,
-                         _("Invalid offset"));
-         newval |= value << 5; /* 6 - 1 */
+                         _("Invalid offset, value too big (0x%08lX)"), value);
+         newval |= value << 5; /* 6 - 1 */
          break;
 
        default:
          as_bad_where (fixP->fx_file, fixP->fx_line,
-                       "Unable to process relocation for thumb opcode: %x", newval);
+                       "Unable to process relocation for thumb opcode: %lx",
+                       (unsigned long) newval);
          break;
        }
       md_number_to_chars (buf, newval, THUMB_SIZE);
@@ -5393,93 +7294,102 @@ md_apply_fix3 (fixP, val, seg)
     case BFD_RELOC_ARM_THUMB_ADD:
       /* This is a complicated relocation, since we use it for all of
          the following immediate relocations:
-            3bit ADD/SUB
-            8bit ADD/SUB
-            9bit ADD/SUB SP word-aligned
-           10bit ADD PC/SP word-aligned
+
+           3bit ADD/SUB
+           8bit ADD/SUB
+           9bit ADD/SUB SP word-aligned
+          10bit ADD PC/SP word-aligned
 
          The type of instruction being processed is encoded in the
          instruction field:
-           0x8000  SUB
-           0x00F0  Rd
-           0x000F  Rs
+
+          0x8000  SUB
+          0x00F0  Rd
+          0x000F  Rs
       */
       newval = md_chars_to_number (buf, THUMB_SIZE);
       {
-        int rd = (newval >> 4) & 0xf;
-        int rs = newval & 0xf;
-        int subtract = newval & 0x8000;
-
-        if (rd == REG_SP)
-          {
-            if (value & ~0x1fc)
-              as_bad_where (fixP->fx_file, fixP->fx_line,
-                            _("Invalid immediate for stack address calculation"));
-            newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
-            newval |= value >> 2;
-          }
-        else if (rs == REG_PC || rs == REG_SP)
-          {
-            if (subtract ||
-                value & ~0x3fc)
-              as_bad_where (fixP->fx_file, fixP->fx_line,
-                            _("Invalid immediate for address calculation (value = 0x%08X)"), value);
-            newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP);
-            newval |= rd << 8;
-            newval |= value >> 2;
-          }
-        else if (rs == rd)
-          {
-            if (value & ~0xff)
-              as_bad_where (fixP->fx_file, fixP->fx_line,
-                            _("Invalid 8bit immediate"));
-            newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
-            newval |= (rd << 8) | value;
-          }
-        else
-          {
-            if (value & ~0x7)
-              as_bad_where (fixP->fx_file, fixP->fx_line,
-                            _("Invalid 3bit immediate"));
-            newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
-            newval |= rd | (rs << 3) | (value << 6);
-          }
+       int rd = (newval >> 4) & 0xf;
+       int rs = newval & 0xf;
+       int subtract = newval & 0x8000;
+
+       if (rd == REG_SP)
+         {
+           if (value & ~0x1fc)
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("Invalid immediate for stack address calculation"));
+           newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
+           newval |= value >> 2;
+         }
+       else if (rs == REG_PC || rs == REG_SP)
+         {
+           if (subtract ||
+               value & ~0x3fc)
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("Invalid immediate for address calculation (value = 0x%08lX)"),
+                           (unsigned long) value);
+           newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP);
+           newval |= rd << 8;
+           newval |= value >> 2;
+         }
+       else if (rs == rd)
+         {
+           if (value & ~0xff)
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("Invalid 8bit immediate"));
+           newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
+           newval |= (rd << 8) | value;
+         }
+       else
+         {
+           if (value & ~0x7)
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("Invalid 3bit immediate"));
+           newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
+           newval |= rd | (rs << 3) | (value << 6);
+         }
       }
-      md_number_to_chars (buf, newval , THUMB_SIZE);
+      md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
 
     case BFD_RELOC_ARM_THUMB_IMM:
       newval = md_chars_to_number (buf, THUMB_SIZE);
       switch (newval >> 11)
-        {
-        case 0x04: /* 8bit immediate MOV */
-        case 0x05: /* 8bit immediate CMP */
-          if (value < 0 || value > 255)
-            as_bad_where (fixP->fx_file, fixP->fx_line,
-                          _("Invalid immediate: %d is too large"), value);
-          newval |= value;
-          break;
-
-        default:
-          abort ();
-        }
-      md_number_to_chars (buf, newval , THUMB_SIZE);
+       {
+       case 0x04: /* 8bit immediate MOV.  */
+       case 0x05: /* 8bit immediate CMP.  */
+         if (value < 0 || value > 255)
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("Invalid immediate: %ld is too large"),
+                         (long) value);
+         newval |= value;
+         break;
+
+       default:
+         abort ();
+       }
+      md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
 
     case BFD_RELOC_ARM_THUMB_SHIFT:
-      /* 5bit shift value (0..31) */
+      /* 5bit shift value (0..31) */
       if (value < 0 || value > 31)
        as_bad_where (fixP->fx_file, fixP->fx_line,
-                     _("Illegal Thumb shift value: %d"), value);
+                     _("Illegal Thumb shift value: %ld"), (long) value);
       newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f;
       newval |= value << 6;
-      md_number_to_chars (buf, newval , THUMB_SIZE);
+      md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
 
+    case BFD_RELOC_VTABLE_INHERIT:
+    case BFD_RELOC_VTABLE_ENTRY:
+      fixP->fx_done = 0;
+      return 1;
+
     case BFD_RELOC_NONE:
     default:
       as_bad_where (fixP->fx_file, fixP->fx_line,
-                   _("Bad relocation fixup type (%d)\n"), fixP->fx_r_type);
+                   _("Bad relocation fixup type (%d)"), fixP->fx_r_type);
     }
 
   return 1;
@@ -5487,17 +7397,19 @@ md_apply_fix3 (fixP, val, seg)
 
 /* Translate internal representation of relocation info to BFD target
    format.  */
+
 arelent *
 tc_gen_reloc (section, fixp)
-     asection *section;
-     fixS *fixp;
+     asection * section ATTRIBUTE_UNUSED;
+     fixS * fixp;
 {
-  arelent *reloc;
+  arelent * reloc;
   bfd_reloc_code_real_type code;
 
   reloc = (arelent *) xmalloc (sizeof (arelent));
 
-  reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym;
+  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
 
   /* @@ Why fx_addnumber sometimes and fx_offset other times?  */
@@ -5534,27 +7446,44 @@ tc_gen_reloc (section, fixp)
        }
 
     case BFD_RELOC_ARM_PCREL_BRANCH:
-    case BFD_RELOC_RVA:      
+    case BFD_RELOC_ARM_PCREL_BLX:
+    case BFD_RELOC_RVA:
     case BFD_RELOC_THUMB_PCREL_BRANCH9:
     case BFD_RELOC_THUMB_PCREL_BRANCH12:
     case BFD_RELOC_THUMB_PCREL_BRANCH23:
+    case BFD_RELOC_THUMB_PCREL_BLX:
+    case BFD_RELOC_VTABLE_ENTRY:
+    case BFD_RELOC_VTABLE_INHERIT:
       code = fixp->fx_r_type;
       break;
 
     case BFD_RELOC_ARM_LITERAL:
     case BFD_RELOC_ARM_HWLITERAL:
       /* If this is called then the a literal has been referenced across
-        a section boundry - possibly due to an implicit dump */
+        a section boundary - possibly due to an implicit dump.  */
       as_bad_where (fixp->fx_file, fixp->fx_line,
-                   _("Literal referenced across section boundry (Implicit dump?)"));
+                   _("Literal referenced across section boundary (Implicit dump?)"));
       return NULL;
 
+#ifdef OBJ_ELF
+    case BFD_RELOC_ARM_GOT32:
+    case BFD_RELOC_ARM_GOTOFF:
+    case BFD_RELOC_ARM_PLT32:
+      code = fixp->fx_r_type;
+      break;
+#endif
+
     case BFD_RELOC_ARM_IMMEDIATE:
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("Internal_relocation (type %d) not fixed up (IMMEDIATE)"),
                    fixp->fx_r_type);
       return NULL;
 
+    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"));
+      return NULL;
+
     case BFD_RELOC_ARM_OFFSET_IMM:
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("Internal_relocation (type %d) not fixed up (OFFSET_IMM)"),
@@ -5564,6 +7493,7 @@ tc_gen_reloc (section, fixp)
     default:
       {
        char * type;
+
        switch (fixp->fx_r_type)
          {
          case BFD_RELOC_ARM_IMMEDIATE:    type = "IMMEDIATE";    break;
@@ -5577,15 +7507,25 @@ tc_gen_reloc (section, fixp)
          case BFD_RELOC_ARM_THUMB_SHIFT:  type = "THUMB_SHIFT";  break;
          case BFD_RELOC_ARM_THUMB_IMM:    type = "THUMB_IMM";    break;
          case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
-         default:                         type = "<unknown>";    break;
+         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;
       }
     }
 
+#ifdef OBJ_ELF
+  if (code == BFD_RELOC_32_PCREL
+      && GOT_symbol
+      && fixp->fx_addsy == GOT_symbol)
+    {
+      code = BFD_RELOC_ARM_GOTPC;
+      reloc->addend = fixp->fx_offset = reloc->address;
+    }
+#endif
+
   reloc->howto = bfd_reloc_type_lookup (stdoutput, code);
 
   if (reloc->howto == NULL)
@@ -5596,24 +7536,28 @@ tc_gen_reloc (section, fixp)
       return NULL;
     }
 
+  /* HACK: Since arm ELF uses Rel instead of Rela, encode the
+     vtable entry to be used in the relocation's section offset.  */
+  if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    reloc->address = fixp->fx_offset;
+
   return reloc;
 }
 
 int
 md_estimate_size_before_relax (fragP, segtype)
-     fragS *fragP;
-     segT segtype;
+     fragS * fragP ATTRIBUTE_UNUSED;
+     segT    segtype ATTRIBUTE_UNUSED;
 {
   as_fatal (_("md_estimate_size_before_relax\n"));
-  return (1);
+  return 1;
 }
 
 static void
-output_inst (str)
-     char *str;
+output_inst PARAMS ((void))
 {
-  char *to = NULL;
-    
+  char * to = NULL;
+
   if (inst.error)
     {
       as_bad (inst.error);
@@ -5621,39 +7565,52 @@ output_inst (str)
     }
 
   to = frag_more (inst.size);
+
   if (thumb_mode && (inst.size > THUMB_SIZE))
     {
       assert (inst.size == (2 * THUMB_SIZE));
       md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
-      md_number_to_chars (to + 2, inst.instruction, THUMB_SIZE);
+      md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
+    }
+  else if (inst.size > INSN_SIZE)
+    {
+      assert (inst.size == (2 * INSN_SIZE));
+      md_number_to_chars (to, inst.instruction, INSN_SIZE);
+      md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
     }
   else
     md_number_to_chars (to, inst.instruction, inst.size);
 
   if (inst.reloc.type != BFD_RELOC_NONE)
     fix_new_arm (frag_now, to - frag_now->fr_literal,
-                inst.size, &inst.reloc.exp, inst.reloc.pc_rel,
+                inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
                 inst.reloc.type);
 
-  return;
+#ifdef OBJ_ELF
+  dwarf2_emit_insn (inst.size);
+#endif
 }
 
 void
 md_assemble (str)
-     char *str;
+     char * str;
 {
-  char c;
-  char *p, *q, *start;
+  char   c;
+  char * p;
+  char * q;
+  char * start;
 
-  /* Align the instruction */
-  /* this may not be the right thing to do but ... */
-  /* arm_align (2, 0); */
-  listing_prev_line (); /* Defined in listing.h */
+  /* Align the instruction.
+     This may not be the right thing to do but ...  */
+#if 0
+  arm_align (2, 0);
+#endif
+  listing_prev_line (); /* Defined in listing.h.  */
 
-  /* Align the previous label if needed */
+  /* Align the previous label if needed */
   if (last_label_seen != NULL)
     {
-      last_label_seen->sy_frag = frag_now;
+      symbol_set_frag (last_label_seen, frag_now);
       S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
       S_SET_SEGMENT (last_label_seen, now_seg);
     }
@@ -5661,15 +7618,14 @@ md_assemble (str)
   memset (&inst, '\0', sizeof (inst));
   inst.reloc.type = BFD_RELOC_NONE;
 
-  if (*str == ' ')
-    str++;                     /* Skip leading white space */
-    
-  /* scan up to the end of the op-code, which must end in white space or
-     end of string */
+  skip_whitespace (str);
+
+  /* Scan up to the end of the op-code, which must end in white space or
+     end of string.  */
   for (start = p = str; *p != '\0'; p++)
     if (*p == ' ')
       break;
-    
+
   if (p == str)
     {
       as_bad (_("No operator -- statement `%s'\n"), str);
@@ -5678,62 +7634,82 @@ md_assemble (str)
 
   if (thumb_mode)
     {
-      CONST struct thumb_opcode *opcode;
+      CONST struct thumb_opcode * opcode;
 
       c = *p;
       *p = '\0';
       opcode = (CONST struct thumb_opcode *) hash_find (arm_tops_hsh, str);
       *p = c;
+
       if (opcode)
        {
+         /* Check that this instruction is supported for this CPU.  */
+         if (thumb_mode == 1 && (opcode->variants & cpu_variant) == 0)
+           {
+             as_bad (_("selected processor does not support this opcode"));
+             return;
+           }
+
          inst.instruction = opcode->value;
          inst.size = opcode->size;
-         (*opcode->parms)(p);
-         output_inst (start);
+         (*opcode->parms) (p);
+         output_inst ();
          return;
        }
     }
   else
     {
-      CONST struct asm_opcode *opcode;
+      CONST struct asm_opcode * opcode;
+      unsigned long cond_code;
 
       inst.size = INSN_SIZE;
-      /* p now points to the end of the opcode, probably white space, but we
+      /* P now points to the end of the opcode, probably white space, but we
         have to break the opcode up in case it contains condionals and flags;
         keep trying with progressively smaller basic instructions until one
-        matches, or we run out of opcode. */
+        matches, or we run out of opcode.  */
       q = (p - str > LONGEST_INST) ? str + LONGEST_INST : p;
+
       for (; q != str; q--)
        {
          c = *q;
          *q = '\0';
+
          opcode = (CONST struct asm_opcode *) hash_find (arm_ops_hsh, str);
          *q = c;
+
          if (opcode && opcode->template)
            {
              unsigned long flag_bits = 0;
-             char *r;
+             char * r;
 
-             /* Check that this instruction is supported for this CPU */
+             /* Check that this instruction is supported for this CPU */
              if ((opcode->variants & cpu_variant) == 0)
                goto try_shorter;
 
              inst.instruction = opcode->value;
-             if (q == p)               /* Just a simple opcode */
+             if (q == p)               /* Just a simple opcode */
                {
-                 if (opcode->comp_suffix != 0)
-                   as_bad (_("Opcode `%s' must have suffix from <%s>\n"), str,
-                           opcode->comp_suffix);
+                 if (opcode->comp_suffix)
+                   {
+                     if (*opcode->comp_suffix != '\0')
+                       as_bad (_("Opcode `%s' must have suffix from list: <%s>"),
+                               str, opcode->comp_suffix);
+                     else
+                       /* Not a conditional instruction.  */
+                       (*opcode->parms) (q, 0);
+                   }
                  else
                    {
+                     /* A conditional instruction with default condition.  */
                      inst.instruction |= COND_ALWAYS;
-                     (*opcode->parms)(q, 0);
+                     (*opcode->parms) (q, 0);
                    }
-                 output_inst (start);
+                 output_inst ();
                  return;
                }
 
-             /* Now check for a conditional */
+             /* Not just a simple opcode.  Check if extra is a
+                 conditional.  */
              r = q;
              if (p - r >= 2)
                {
@@ -5749,18 +7725,32 @@ md_assemble (str)
                        as_tsktsk (
 _("Warning: Use of the 'nv' conditional is deprecated\n"));
 
-                     inst.instruction |= cond->value;
+                     cond_code = cond->value;
                      r += 2;
                    }
                  else
-                   inst.instruction |= COND_ALWAYS;
+                   cond_code = COND_ALWAYS;
                }
              else
-               inst.instruction |= COND_ALWAYS;
+               cond_code = COND_ALWAYS;
 
-             /* if there is a compulsory suffix, it should come here, before
-                any optional flags. */
-             if (opcode->comp_suffix)
+             /* Apply the conditional, or complain it's not allowed.  */
+             if (opcode->comp_suffix && *opcode->comp_suffix == '\0')
+               {
+                 /* Instruction isn't conditional.  */
+                 if (cond_code != COND_ALWAYS)
+                   {
+                     as_bad (_("Opcode `%s' is unconditional\n"), str);
+                     return;
+                   }
+               }
+             else
+               /* Instruction is conditional: set the condition into it.  */
+               inst.instruction |= cond_code;
+
+             /* If there is a compulsory suffix, it should come here
+                before any optional flags.  */
+             if (opcode->comp_suffix && *opcode->comp_suffix != '\0')
                {
                  CONST char *s = opcode->comp_suffix;
 
@@ -5774,8 +7764,8 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
 
                  if (*s == '\0')
                    {
-                     as_bad (_("Opcode `%s' must have suffix from <%s>\n"), str,
-                             opcode->comp_suffix);
+                     as_bad (_("Opcode `%s' must have suffix from <%s>\n"),
+                             str, opcode->comp_suffix);
                      return;
                    }
 
@@ -5798,7 +7788,7 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
 
                      for (flagno = 0; flag[flagno].template; flagno++)
                        {
-                         if (! strcmp (r, flag[flagno].template))
+                         if (streq (r, flag[flagno].template))
                            {
                              flag_bits |= flag[flagno].set_bits;
                              break;
@@ -5814,7 +7804,7 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
                }
 
              (*opcode->parms) (p, flag_bits);
-             output_inst (start);
+             output_inst ();
              return;
            }
 
@@ -5824,29 +7814,31 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
     }
 
   /* It wasn't an instruction, but it might be a register alias of the form
-     alias .req reg
-     */
+     alias .req reg.  */
   q = p;
-  while (*q == ' ')
-    q++;
+  skip_whitespace (q);
 
   c = *p;
   *p = '\0';
-    
+
   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;
-      while (*q == ' ')
-       q++;
+      skip_whitespace (q);
 
       for (r = q; *r != '\0'; r++)
        if (*r == ' ')
          break;
-      
+
       if (r != q)
        {
          int regnum;
@@ -5857,24 +7849,21 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
          *r = d;
 
          reg = arm_reg_parse (& str);
-         
+
          if (reg == FAIL)
            {
              if (regnum != FAIL)
-               {
-                 insert_reg_alias (str, regnum);
-               }
+               insert_reg_alias (str, regnum);
              else
-               {
-                 as_warn (_("register '%s' does not exist\n"), q);
-               }
+               as_warn (_("register '%s' does not exist\n"), q);
            }
          else if (regnum != FAIL)
            {
              if (reg != regnum)
-               as_warn (_("ignoring redefinition of register alias '%s'"), copy_of_str );
-             
-             /* Do not warn abpout redefinitions to the same alias.  */
+               as_warn (_("ignoring redefinition of register alias '%s'"),
+                        copy_of_str);
+
+             /* Do not warn about redefinitions to the same alias.  */
            }
          else
            as_warn (_("ignoring redefinition of register alias '%s' to non-existant register '%s'"),
@@ -5882,7 +7871,7 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
        }
       else
        as_warn (_("ignoring incomplete .req pseuso op"));
-      
+
       *p = c;
       return;
     }
@@ -5891,33 +7880,39 @@ _("Warning: Use of the 'nv' conditional is deprecated\n"));
   as_bad (_("bad instruction `%s'"), start);
 }
 
-/*
- * md_parse_option
- *    Invocation line includes a switch not recognized by the base assembler.
- *    See if it's a processor-specific option.  These are:
- *    Cpu variants, the arm part is optional:
- *            -m[arm]1                Currently not supported.
- *            -m[arm]2, -m[arm]250    Arm 2 and Arm 250 processor
- *            -m[arm]3                Arm 3 processor
- *            -m[arm]6,               Arm 6 processors
- *            -m[arm]7[t][[d]m]       Arm 7 processors
- *            -mall                   All (except the ARM1)
- *    FP variants:
- *            -mfpa10, -mfpa11        FPA10 and 11 co-processor instructions
- *            -mfpe-old               (No float load/store multiples)
- *            -mno-fpu                Disable all floating point instructions
- *    Run-time endian selection:
- *            -EB                     big endian cpu
- *            -EL                     little endian cpu
- *    ARM Procedure Calling Standard:
- *           -mapcs-32               32 bit APCS
- *           -mapcs-26               26 bit APCS
- *           -mapcs-float            Pass floats in float regs
- *           -mapcs-reentrant        Position independent code
- *            -mthumb-interwork       Code supports Arm/Thumb interworking
- */
-
-CONST char *md_shortopts = "m:";
+/* md_parse_option
+      Invocation line includes a switch not recognized by the base assembler.
+      See if it's a processor-specific option.  These are:
+      Cpu variants, the arm part is optional:
+              -m[arm]1                Currently not supported.
+              -m[arm]2, -m[arm]250    Arm 2 and Arm 250 processor
+              -m[arm]3                Arm 3 processor
+              -m[arm]6[xx],           Arm 6 processors
+              -m[arm]7[xx][t][[d]m]   Arm 7 processors
+              -m[arm]8[10]            Arm 8 processors
+              -m[arm]9[20][tdmi]      Arm 9 processors
+              -mstrongarm[110[0]]     StrongARM processors
+              -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
+              -mfpe-old               (No float load/store multiples)
+              -mno-fpu                Disable all floating point instructions
+      Run-time endian selection:
+              -EB                     big endian cpu
+              -EL                     little endian cpu
+      ARM Procedure Calling Standard:
+             -mapcs-32               32 bit APCS
+             -mapcs-26               26 bit APCS
+             -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";
+
 struct option md_longopts[] =
 {
 #ifdef ARM_BI_ENDIAN
@@ -5925,17 +7920,22 @@ struct option md_longopts[] =
   {"EB", no_argument, NULL, OPTION_EB},
 #define OPTION_EL (OPTION_MD_BASE + 1)
   {"EL", no_argument, NULL, OPTION_EL},
+#ifdef OBJ_ELF
+#define OPTION_OABI (OPTION_MD_BASE +2)
+  {"oabi", no_argument, NULL, OPTION_OABI},
+#endif
 #endif
   {NULL, no_argument, NULL, 0}
 };
+
 size_t md_longopts_size = sizeof (md_longopts);
 
 int
 md_parse_option (c, arg)
-     int c;
-     char *arg;
+     int    c;
+     char * arg;
 {
-  char *str = arg;
+  char * str = arg;
 
   switch (c)
     {
@@ -5952,42 +7952,50 @@ md_parse_option (c, arg)
       switch (*str)
        {
        case 'f':
-         if (! strcmp (str, "fpa10"))
+         if (streq (str, "fpa10"))
            cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA10;
-         else if (! strcmp (str, "fpa11"))
+         else if (streq (str, "fpa11"))
            cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_FPA11;
-         else if (! strcmp (str, "fpe-old"))
+         else if (streq (str, "fpe-old"))
            cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_CORE;
          else
            goto bad;
          break;
 
        case 'n':
-         if (! strcmp (str, "no-fpu"))
+         if (streq (str, "no-fpu"))
            cpu_variant &= ~FPU_ALL;
          break;
 
-        case 't':
-          /* Limit assembler to generating only Thumb instructions: */
-          if (! strcmp (str, "thumb"))
-            {
-              cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB;
-              cpu_variant = (cpu_variant & ~FPU_ALL) | FPU_NONE;
-              thumb_mode = 1;
-            }
-          else if (! strcmp (str, "thumb-interwork"))
-            {
-              cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_THUMB | ARM_ARCHv4;
+#ifdef OBJ_ELF
+       case 'o':
+         if (streq (str, "oabi"))
+           target_oabi = true;
+         break;
+#endif
+
+       case 't':
+         /* Limit assembler to generating only Thumb instructions:  */
+         if (streq (str, "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_EXT_THUMB) == 0)
+               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCH_V4T;
 #if defined OBJ_COFF || defined OBJ_ELF
-              support_interwork = true;
+             support_interwork = true;
 #endif
-            }
-          else
+           }
+         else
            goto bad;
-          break;
+         break;
 
        default:
-         if (! strcmp (str, "all"))
+         if (streq (str, "all"))
            {
              cpu_variant = ARM_ALL | FPU_ALL;
              return 1;
@@ -5999,24 +8007,24 @@ md_parse_option (c, arg)
                 to us, so we must parse them here.  */
 
              str += 5;
-             
-             if (! strcmp (str, "32"))
+
+             if (streq (str, "32"))
                {
                  uses_apcs_26 = false;
                  return 1;
                }
-             else if (! strcmp (str, "26"))
+             else if (streq (str, "26"))
                {
                  uses_apcs_26 = true;
                  return 1;
                }
-             else if (! strcmp (str, "frame"))
+             else if (streq (str, "frame"))
                {
                  /* Stack frames are being generated - does not affect
                     linkage of code.  */
                  return 1;
                }
-             else if (! strcmp (str, "stack-check"))
+             else if (streq (str, "stack-check"))
                {
                  /* Stack checking is being performed - does not affect
                     linkage, but does require that the functions
@@ -6025,7 +8033,7 @@ md_parse_option (c, arg)
 
                  return 1;
                }
-             else if (! strcmp (str, "float"))
+             else if (streq (str, "float"))
                {
                  /* Floating point arguments are being passed in the floating
                     point registers.  This does affect linking, since this
@@ -6035,143 +8043,222 @@ md_parse_option (c, arg)
                  uses_apcs_float = true;
                  return 1;
                }
-             else if (! strcmp (str, "reentrant"))
+             else if (streq (str, "reentrant"))
                {
                  /* Reentrant code has been generated.  This does affect
                     linking, since there is no point in linking reentrant/
-                    position independent code with absolute position code. */
+                    position independent code with absolute position code.  */
                  pic_code = true;
                  return 1;
                }
-             
+
              as_bad (_("Unrecognised APCS switch -m%s"), arg);
              return 0;
-           }
+           }
+
+         if (! strcmp (str, "atpcs"))
+           {
+             atpcs = true;
+             return 1;
+           }
 #endif
-         /* Strip off optional "arm" */
+         /* Strip off optional "arm" */
          if (! strncmp (str, "arm", 3))
            str += 3;
 
          switch (*str)
            {
            case '1':
-             if (! strcmp (str, "1"))
+             if (streq (str, "1"))
                cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_1;
              else
                goto bad;
              break;
 
            case '2':
-             if (! strcmp (str, "2"))
+             if (streq (str, "2"))
                cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2;
-             else if (! strcmp (str, "250"))
+             else if (streq (str, "250"))
                cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_250;
              else
                goto bad;
              break;
 
            case '3':
-             if (! strcmp (str, "3"))
+             if (streq (str, "3"))
                cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3;
              else
                goto bad;
              break;
 
-           case 's':
-             if (! strcmp (str, "strongarm") || ! strcmp (str, "strongarm110"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7 | ARM_ARCHv4 | ARM_LONGMUL;
-             else
-               goto bad;
+           case '6':
+             switch (strtol (str, NULL, 10))
+               {
+               case 6:
+               case 60:
+               case 600:
+               case 610:
+               case 620:
+                 cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_6;
+                 break;
+               default:
+                 goto bad;
+               }
              break;
-               
+
+           case '7':
+             /* Eat the processor name.  */
+             switch (strtol (str, & str, 10))
+               {
+               case 7:
+               case 70:
+               case 700:
+               case 710:
+               case 720:
+               case 7100:
+               case 7500:
+                 break;
+               default:
+                 goto bad;
+               }
+             cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
+             for (; *str; str++)
+               {
+                 switch (*str)
+                   {
+                   case 't':
+                     cpu_variant |= ARM_ARCH_V4T;
+                     break;
+
+                   case 'm':
+                     cpu_variant |= ARM_EXT_LONGMUL;
+                     break;
+
+                   case 'f': /* fe => fp enabled cpu.  */
+                     if (str[1] == 'e')
+                       ++ str;
+                     else
+                       goto bad;
+
+                   case 'c': /* Left over from 710c processor name.  */
+                   case 'd': /* Debug.  */
+                   case 'i': /* Embedded ICE.  */
+                     /* Included for completeness in ARM processor naming.  */
+                     break;
+
+                   default:
+                     goto bad;
+                   }
+               }
+             break;
+
            case '8':
-             if (! strcmp (str, "8"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7 | ARM_ARCHv4 | ARM_LONGMUL;
+             if (streq (str, "8") || streq (str, "810"))
+               cpu_variant = (cpu_variant & ~ARM_ANY)
+                 | ARM_8 | ARM_ARCH_V4;
              else
                goto bad;
              break;
 
-           case '6':
-             if (! strcmp (str, "6"))
-               cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_6;
+           case '9':
+             if (streq (str, "9"))
+               cpu_variant = (cpu_variant & ~ARM_ANY)
+                 | ARM_9 | ARM_ARCH_V4T;
+             else if (streq (str, "920"))
+               cpu_variant = (cpu_variant & ~ARM_ANY)
+                 | ARM_9 | ARM_ARCH_V4;
+             else if (streq (str, "920t"))
+               cpu_variant = (cpu_variant & ~ARM_ANY)
+                 | ARM_9 | ARM_ARCH_V4T;
+             else if (streq (str, "9tdmi"))
+               cpu_variant = (cpu_variant & ~ARM_ANY)
+                 | ARM_9 | ARM_ARCH_V4T;
              else
                goto bad;
              break;
 
-           case '7':
-              str++; /* eat the '7' */
-              cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
-              for (; *str; str++)
-                {
-                switch (* str)
-                  {
-                  case 't':
-                    cpu_variant |= (ARM_THUMB | ARM_ARCHv4);
-                    break;
-
-                  case 'm':
-                    cpu_variant |= ARM_LONGMUL;
-                    break;
-
-                 case 'f': /* fe => fp enabled cpu.  */
-                   if (str[1] == 'e')
-                     ++ str;
-                   else
-                     goto bad;
-                   
-                 case 'c': /* Unknown */
-                  case 'd': /* debug */
-                  case 'i': /* embedded ice */
-                    /* Included for completeness in ARM processor naming. */
-                    break;
-
-                  default:
-                    goto bad;
-                  }
-                }
+           case 's':
+             if (streq (str, "strongarm")
+                 || streq (str, "strongarm110")
+                 || streq (str, "strongarm1100"))
+               cpu_variant = (cpu_variant & ~ARM_ANY)
+                 | 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 */
+             /* Select variant based on architecture rather than
+                 processor.  */
              switch (*++str)
                {
                case '2':
                  switch (*++str)
                    {
-                   case 'a': cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3; break;
-                   case 0:   cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2; break;
-                   default:  as_bad (_("Invalid architecture variant -m%s"), arg); break;
+                   case 'a':
+                     cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_3;
+                     break;
+                   case 0:
+                     cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_2;
+                     break;
+                   default:
+                     as_bad (_("Invalid architecture variant -m%s"), arg);
+                     break;
                    }
                  break;
-                 
+
                case '3':
-                   cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
-                    
+                 cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7;
+
                  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;
+                   default:
+                     as_bad (_("Invalid architecture variant -m%s"), arg);
+                     break;
                    }
                  break;
-                 
+
                case '4':
-                 cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_ARCHv4;
-                 
+                 cpu_variant = (cpu_variant & ~ARM_ANY) | ARM_7 | ARM_ARCH_V4;
+
+                 switch (*++str)
+                   {
+                   case 't': cpu_variant |= ARM_EXT_THUMB; break;
+                   case 0:   break;
+                   default:
+                     as_bad (_("Invalid architecture variant -m%s"), arg);
+                     break;
+                   }
+                 break;
+
+               case '5':
+                 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); break;
+                   default:
+                     as_bad (_("Invalid architecture variant -m%s"), arg);
+                     break;
                    }
                  break;
-                 
+
                default:
                  as_bad (_("Invalid architecture variant -m%s"), arg);
                  break;
                }
              break;
-             
+
            default:
            bad:
              as_bad (_("Invalid processor variant -m%s"), arg);
@@ -6180,41 +8267,47 @@ md_parse_option (c, arg)
        }
       break;
 
+#if defined OBJ_ELF || defined OBJ_COFF
+    case 'k':
+      pic_code = 1;
+      break;
+#endif
+
     default:
       return 0;
     }
 
-   return 1;
+  return 1;
 }
 
 void
 md_show_usage (fp)
-     FILE *fp;
+     FILE * fp;
 {
-  fprintf (fp,
-_("\
+  fprintf (fp, _("\
+ ARM Specific Assembler Options:\n\
   -m[arm][<processor name>] select processor variant\n\
-  -m[arm]v[2|2a|3|3m|4|4t select architecture variant\n\
+  -m[arm]v[2|2a|3|3m|4|4t|5[t][e]] select architecture variant\n\
   -mthumb                   only allow Thumb instructions\n\
   -mthumb-interwork         mark the assembled code as supporting interworking\n\
   -mall                     allow any instruction\n\
   -mfpa10, -mfpa11          select floating point architecture\n\
   -mfpe-old                 don't allow floating-point multiple instructions\n\
-  -mno-fpu                  don't allow any floating-point instructions.\n"));
+  -mno-fpu                  don't allow any floating-point instructions.\n\
+  -k                        generate PIC code.\n"));
 #if defined OBJ_COFF || defined OBJ_ELF
-  fprintf (fp,
-_("\
-  -mapcs-32, -mapcs-26      specify which ARM Procedure Calling Standard is in use\n"));
-  fprintf (fp,
-_("\
-  -mapcs-float              floating point args are passed in floating point regs\n"));
-  fprintf (fp,
-_("\
-  -mapcs-reentrant          position independent/reentrant code has been generated\n"));
+  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
+#ifdef OBJ_ELF
+  fprintf (fp, _("\
+  -moabi                    support the old ELF ABI\n"));
 #endif
 #ifdef ARM_BI_ENDIAN
-  fprintf (fp,
-_("\
+  fprintf (fp, _("\
   -EB                       assemble code for a big endian cpu\n\
   -EL                       assemble code for a little endian cpu\n"));
 #endif
@@ -6229,15 +8322,15 @@ _("\
 
 static void
 fix_new_arm (frag, where, size, exp, pc_rel, reloc)
-     fragS *frag;
-     int where;
-     short int size;
-     expressionS *exp;
-     int pc_rel;
-     int reloc;
+     fragS *       frag;
+     int           where;
+     short int     size;
+     expressionS * exp;
+     int           pc_rel;
+     int           reloc;
 {
-  fixS *new_fix;
-  arm_fix_data *arm_data;
+  fixS *           new_fix;
+  arm_fix_data *   arm_data;
 
   switch (exp->X_op)
     {
@@ -6254,26 +8347,63 @@ fix_new_arm (frag, where, size, exp, pc_rel, reloc)
       break;
     }
 
-  /* Mark whether the fix is to a THUMB instruction, or an ARM instruction */
-  arm_data = (arm_fix_data *) obstack_alloc (&notes, sizeof (arm_fix_data));
+  /* Mark whether the fix is to a THUMB instruction, or an ARM
+     instruction.  */
+  arm_data = (arm_fix_data *) obstack_alloc (& notes, sizeof (arm_fix_data));
   new_fix->tc_fix_data = (PTR) arm_data;
   arm_data->thumb_mode = thumb_mode;
 
   return;
 }
 
-/* A good place to do this, although this was probably not intended
- * for this kind of use.  We need to dump the literal pool before
- * references are made to a null symbol pointer.  */
+/* This fix_new is called by cons via TC_CONS_FIX_NEW.  */
+
 void
-arm_cleanup ()
+cons_fix_new_arm (frag, where, size, exp)
+     fragS *       frag;
+     int           where;
+     int           size;
+     expressionS * exp;
 {
-  if (current_poolP != NULL)
+  bfd_reloc_code_real_type type;
+  int pcrel = 0;
+
+  /* Pick a reloc.
+     FIXME: @@ Should look at CPU word size.  */
+  switch (size)
     {
-      subseg_set (text_section, 0); /* Put it at the end of text section */
-      s_ltorg (0);
-      listing_prev_line ();
+    case 1:
+      type = BFD_RELOC_8;
+      break;
+    case 2:
+      type = BFD_RELOC_16;
+      break;
+    case 4:
+    default:
+      type = BFD_RELOC_32;
+      break;
+    case 8:
+      type = BFD_RELOC_64;
+      break;
     }
+
+  fix_new_exp (frag, where, (int) size, exp, pcrel, type);
+}
+
+/* A good place to do this, although this was probably not intended
+   for this kind of use.  We need to dump the literal pool before
+   references are made to a null symbol pointer.  */
+
+void
+arm_cleanup ()
+{
+  if (current_poolP == NULL)
+    return;
+
+  /* Put it at the end of text section.  */
+  subseg_set (text_section, 0);
+  s_ltorg (0);
+  listing_prev_line ();
 }
 
 void
@@ -6284,15 +8414,48 @@ arm_start_line_hook ()
 
 void
 arm_frob_label (sym)
-     symbolS *sym;
+     symbolS * sym;
 {
   last_label_seen = sym;
+
   ARM_SET_THUMB (sym, thumb_mode);
+
 #if defined OBJ_COFF || defined OBJ_ELF
   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
@@ -6300,7 +8463,7 @@ arm_frob_label (sym)
         correctly.  */
 
       THUMB_SET_FUNC (sym, 1);
-      
+
       label_is_thumb_function_name = false;
     }
 }
@@ -6308,77 +8471,76 @@ arm_frob_label (sym)
 /* Adjust the symbol table.  This marks Thumb symbols as distinct from
    ARM ones.  */
 
-#ifdef OBJ_ELF
-#define S_GET_STORAGE_CLASS(S)   (elf_symbol ((S)->bsym)->internal_elf_sym.st_other)
-#define S_SET_STORAGE_CLASS(S,V) (elf_symbol ((S)->bsym)->internal_elf_sym.st_other = (V))
-#endif
 void
 arm_adjust_symtab ()
 {
-#if defined OBJ_COFF || defined OBJ_ELF
+#ifdef OBJ_COFF
   symbolS * sym;
 
   for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
     {
       if (ARM_IS_THUMB (sym))
-        {
+       {
          if (THUMB_IS_FUNC (sym))
            {
              /* Mark the symbol as a Thumb function.  */
              if (   S_GET_STORAGE_CLASS (sym) == C_STAT
-                 || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */
+                 || S_GET_STORAGE_CLASS (sym) == C_LABEL)  /* This can happen!  */
                S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
+
              else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
                S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
              else
-               as_bad (_("%s: unexpected function type: %d"), S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
+               as_bad (_("%s: unexpected function type: %d"),
+                       S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
            }
           else switch (S_GET_STORAGE_CLASS (sym))
-            {
-              case C_EXT:
-                S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
-                break;
-              case C_STAT:
-                S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
-                break;
-              case C_LABEL:
-                S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
-                break;
-              default: /* do nothing */ 
-                break;
-            }
-        }
+           {
+           case C_EXT:
+             S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
+             break;
+           case C_STAT:
+             S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
+             break;
+           case C_LABEL:
+             S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
+             break;
+           default:
+             /* Do nothing.  */
+             break;
+           }
+       }
 
-#ifdef OBJ_COFF
       if (ARM_IS_INTERWORK (sym))
-       coffsymbol(sym->bsym)->native->u.syment.n_flags = 0xFF;
-#endif
+       coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF;
     }
 #endif
-}
-
 #ifdef OBJ_ELF
-void
-armelf_frob_symbol (symp, puntp)
-    symbolS *symp;
-    int *puntp;
+  symbolS * sym;
+  char      bind;
 
-{
-   elf_frob_symbol (symp, puntp);
+  for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+    {
+      if (ARM_IS_THUMB (sym))
+       {
+         elf_symbol_type * elf_sym;
 
-   if (S_IS_EXTERNAL (symp))
-      S_SET_STORAGE_CLASS(symp, C_EXT);
+         elf_sym = elf_symbol (symbol_get_bfdsym (sym));
+         bind = ELF_ST_BIND (elf_sym);
 
-   if (S_GET_STORAGE_CLASS (symp) == C_NULL)
-     {
-     if (S_GET_SEGMENT (symp) == text_section
-              && symp != seg_info (text_section)->sym)
-            S_SET_STORAGE_CLASS (symp, C_LABEL);
-          else
-            S_SET_STORAGE_CLASS (symp, C_STAT);
-      }
-} 
+         /* 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);
+         else
+           elf_sym->internal_elf_sym.st_info =
+             ELF_ST_INFO (bind, STT_ARM_16BIT);
+       }
+    }
 #endif
+}
+
 int
 arm_data_in_code ()
 {
@@ -6389,20 +8551,19 @@ arm_data_in_code ()
       *input_line_pointer = 0;
       return 1;
     }
+
   return 0;
 }
 
 char *
 arm_canonicalize_symbol_name (name)
-     char *name;
+     char * name;
 {
   int len;
 
   if (thumb_mode && (len = strlen (name)) > 5
-      && ! strcmp (name + len - 5, "/data"))
-    {
-      *(name + len - 5) = 0;
-    }
+      && streq (name + len - 5, "/data"))
+    *(name + len - 5) = 0;
 
   return name;
 }
@@ -6415,7 +8576,7 @@ arm_validate_fix (fixP)
      the THUMB_FUNC attribute, then we must be calling a function which has
      the (interfacearm) attribute.  We look for the Thumb entry point to that
      function and change the branch to refer to that function instead.  */
-  if (   fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
+  if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
       && fixP->fx_addsy != NULL
       && S_IS_DEFINED (fixP->fx_addsy)
       && ! THUMB_IS_FUNC (fixP->fx_addsy))
@@ -6427,3 +8588,195 @@ arm_validate_fix (fixP)
   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
+   bottom bit of their addresses.  The MIPS version of this function
+   also prevents relocations that are mips-16 specific, but I do not
+   know why it does this.
+
+   FIXME:
+   There is one other problem that ought to be addressed here, but
+   which currently is not:  Taking the address of a label (rather
+   than a function) and then later jumping to that address.  Such
+   addresses also ought to have their bottom bit set (assuming that
+   they reside in Thumb code), but at the moment they will not.  */
+
+boolean
+arm_fix_adjustable (fixP)
+   fixS * fixP;
+{
+  if (fixP->fx_addsy == NULL)
+    return 1;
+
+  /* Prevent all adjustments to global symbols.  */
+  if (S_IS_EXTERN (fixP->fx_addsy))
+    return 0;
+
+  if (S_IS_WEAK (fixP->fx_addsy))
+    return 0;
+
+  if (THUMB_IS_FUNC (fixP->fx_addsy)
+      && fixP->fx_subsy == NULL)
+    return 0;
+
+  /* We need the symbol name for the VTABLE entries.  */
+  if (   fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+    return 0;
+
+  return 1;
+}
+
+const char *
+elf32_arm_target_format ()
+{
+  if (target_big_endian)
+    {
+      if (target_oabi)
+       return "elf32-bigarm-oabi";
+      else
+       return "elf32-bigarm";
+    }
+  else
+    {
+      if (target_oabi)
+       return "elf32-littlearm-oabi";
+      else
+       return "elf32-littlearm";
+    }
+}
+
+void
+armelf_frob_symbol (symp, puntp)
+     symbolS * symp;
+     int *     puntp;
+{
+  elf_frob_symbol (symp, puntp);
+}
+
+int
+arm_force_relocation (fixp)
+     struct fix * fixp;
+{
+  if (   fixp->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+      || fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY
+      || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
+      || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BLX
+      || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX
+      || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23)
+    return 1;
+
+  return 0;
+}
+
+static bfd_reloc_code_real_type
+arm_parse_reloc ()
+{
+  char         id [16];
+  char *       ip;
+  unsigned int i;
+  static struct
+  {
+    char * str;
+    int    len;
+    bfd_reloc_code_real_type reloc;
+  }
+  reloc_map[] =
+  {
+#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)"
+       branch instructions generated by GCC for PLT relocs.  */
+    MAP ("(plt)",    BFD_RELOC_ARM_PLT32),
+    { NULL, 0,         BFD_RELOC_UNUSED }
+#undef MAP
+  };
+
+  for (i = 0, ip = input_line_pointer;
+       i < sizeof (id) && (isalnum (*ip) || ispunct (*ip));
+       i++, ip++)
+    id[i] = tolower (*ip);
+
+  for (i = 0; reloc_map[i].str; i++)
+    if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0)
+      break;
+
+  input_line_pointer += reloc_map[i].len;
+
+  return reloc_map[i].reloc;
+}
+
+static void
+s_arm_elf_cons (nbytes)
+     int nbytes;
+{
+  expressionS exp;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  if (is_it_end_of_statement ())
+    {
+      demand_empty_rest_of_line ();
+      return;
+    }
+
+#ifdef md_cons_align
+  md_cons_align (nbytes);
+#endif
+
+  do
+    {
+      bfd_reloc_code_real_type reloc;
+
+      expression (& exp);
+
+      if (exp.X_op == O_symbol
+         && * input_line_pointer == '('
+         && (reloc = arm_parse_reloc ()) != BFD_RELOC_UNUSED)
+       {
+         reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
+         int size = bfd_get_reloc_size (howto);
+
+         if (size > nbytes)
+           as_bad ("%s relocations do not fit in %d bytes",
+                   howto->name, nbytes);
+         else
+           {
+             register char *p = frag_more ((int) nbytes);
+             int offset = nbytes - size;
+
+             fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
+                          &exp, 0, reloc);
+           }
+       }
+      else
+       emit_expr (&exp, (unsigned int) nbytes);
+    }
+  while (*input_line_pointer++ == ',');
+
+  /* Put terminator back into stream.  */
+  input_line_pointer --;
+  demand_empty_rest_of_line ();
+}
+
+#endif /* OBJ_ELF */
This page took 0.159808 seconds and 4 git commands to generate.