* bfd/bfd-in.h (bfd_is_arm_mapping_symbol_name): Rename from
[deliverable/binutils-gdb.git] / gas / config / tc-arm.c
index 752cd3c04a62f0bdd0c4d13348085c0172466493..298468fd01d5bd630f84436b1b9ce4e8f89500a9 100644 (file)
@@ -1,5 +1,6 @@
 /* tc-arm.c -- Assemble for the ARM
-   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004
+   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+   2004, 2005
    Free Software Foundation, Inc.
    Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
        Modified by David Taylor (dtaylor@armltd.co.uk)
 #ifdef OBJ_ELF
 #include "elf/arm.h"
 #include "dwarf2dbg.h"
+#include "dw2gencfi.h"
 #endif
 
-/* XXX Set this to 1 after the next binutils release */
+/* XXX Set this to 1 after the next binutils release */
 #define WARN_DEPRECATED 0
 
+#ifdef OBJ_ELF
+/* Must be at least the size of the largest unwind opcode (currently two).  */
+#define ARM_OPCODE_CHUNK_SIZE 8
+
+/* This structure holds the unwinding state.  */
+
+static struct
+{
+  symbolS *       proc_start;
+  symbolS *       table_entry;
+  symbolS *       personality_routine;
+  int             personality_index;
+  /* The segment containing the function.  */
+  segT            saved_seg;
+  subsegT         saved_subseg;
+  /* Opcodes generated from this function.  */
+  unsigned char * opcodes;
+  int             opcode_count;
+  int             opcode_alloc;
+  /* The number of bytes pushed to the stack.  */
+  offsetT         frame_size;
+  /* We don't add stack adjustment opcodes immediately so that we can merge
+     multiple adjustments.  We can also omit the final adjustment
+     when using a frame pointer.  */
+  offsetT         pending_offset;
+  /* These two fields are set by both unwind_movsp and unwind_setfp.  They
+     hold the reg+offset to use when restoring sp from a frame pointer.  */
+  offsetT         fp_offset;
+  int             fp_reg;
+  /* Nonzero if an unwind_setfp directive has been seen.  */
+  unsigned        fp_used:1;
+  /* Nonzero if the last opcode restores sp from fp_reg.  */
+  unsigned        sp_restored:1;
+} unwind;
+
+/* Bit N indicates that an R_ARM_NONE relocation has been output for
+   __aeabi_unwind_cpp_prN already if set. This enables dependencies to be
+   emitted only once per section, to save unnecessary bloat.  */
+static unsigned int marked_pr_dependency = 0;
+
+#endif /* OBJ_ELF */
+
 enum arm_float_abi
 {
   ARM_FLOAT_ABI_HARD,
@@ -77,29 +121,28 @@ enum arm_float_abi
 #endif
 #endif
 
-#ifdef TE_LINUX
-#define FPU_DEFAULT FPU_ARCH_FPA
-#endif
-
-#ifdef TE_NetBSD
-#ifdef OBJ_ELF
-#define FPU_DEFAULT FPU_ARCH_VFP       /* Soft-float, but VFP order.  */
-#else
-/* Legacy a.out format.  */
-#define FPU_DEFAULT FPU_ARCH_FPA       /* Soft-float, but FPA order.  */
-#endif
-#endif
-
-/* For backwards compatibility we default to the FPA.  */
 #ifndef FPU_DEFAULT
-#define FPU_DEFAULT FPU_ARCH_FPA
-#endif
+# ifdef TE_LINUX
+#  define FPU_DEFAULT FPU_ARCH_FPA
+# elif defined (TE_NetBSD)
+#  ifdef OBJ_ELF
+#   define FPU_DEFAULT FPU_ARCH_VFP    /* Soft-float, but VFP order.  */
+#  else
+    /* Legacy a.out format.  */
+#   define FPU_DEFAULT FPU_ARCH_FPA    /* Soft-float, but FPA order.  */
+#  endif
+# elif defined (TE_VXWORKS)
+#  define FPU_DEFAULT FPU_ARCH_VFP     /* Soft-float, VFP order.  */
+# else
+   /* For backwards compatibility, default to FPA.  */
+#  define FPU_DEFAULT FPU_ARCH_FPA
+# endif
+#endif /* ifndef FPU_DEFAULT */
 
 #define streq(a, b)           (strcmp (a, b) == 0)
 #define skip_whitespace(str)  while (*(str) == ' ') ++(str)
 
 static unsigned long cpu_variant;
-static int target_oabi = 0;
 
 /* Flags stored in private area of BFD structure.  */
 static int uses_apcs_26      = FALSE;
@@ -121,7 +164,11 @@ static int march_fpu_opt = -1;
 static int mfpu_opt = -1;
 static int mfloat_abi_opt = -1;
 #ifdef OBJ_ELF
+# ifdef EABI_DEFAULT
+static int meabi_flags = EABI_DEFAULT;
+# else
 static int meabi_flags = EF_ARM_EABI_UNKNOWN;
+# endif
 #endif
 
 /* This array holds the chars that always start a comment.  If the
@@ -1196,78 +1243,9 @@ validate_offset_imm (unsigned int val, int hwse)
 \f
 #ifdef OBJ_ELF
 /* This code is to handle mapping symbols as defined in the ARM ELF spec.
-   (This text is taken from version B-02 of the spec):
-
-      4.4.7 Mapping and tagging symbols
-
-      A section of an ARM ELF file can contain a mixture of ARM code,
-      Thumb code, and data.  There are inline transitions between code
-      and data at literal pool boundaries. There can also be inline
-      transitions between ARM code and Thumb code, for example in
-      ARM-Thumb inter-working veneers.  Linkers, machine-level
-      debuggers, profiling tools, and disassembly tools need to map
-      images accurately. For example, setting an ARM breakpoint on a
-      Thumb location, or in a literal pool, can crash the program
-      being debugged, ruining the debugging session.
-
-      ARM ELF entities are mapped (see section 4.4.7.1 below) and
-      tagged (see section 4.4.7.2 below) using local symbols (with
-      binding STB_LOCAL).  To assist consumers, mapping and tagging
-      symbols should be collated first in the symbol table, before
-      other symbols with binding STB_LOCAL.
-
-      To allow properly collated mapping and tagging symbols to be
-      skipped by consumers that have no interest in them, the first
-      such symbol should have the name $m and its st_value field equal
-      to the total number of mapping and tagging symbols (including
-      the $m) in the symbol table.
-
-      4.4.7.1 Mapping symbols
-
-      $a    Labels the first byte of a sequence of ARM instructions.
-            Its type is STT_FUNC.
-
-      $d    Labels the first byte of a sequence of data items.
-            Its type is STT_OBJECT.
-
-      $t    Labels the first byte of a sequence of Thumb instructions.
-            Its type is STT_FUNC.
-
-      This list of mapping symbols may be extended in the future.
-
-      Section-relative mapping symbols
-
-      Mapping symbols defined in a section define a sequence of
-      half-open address intervals that cover the address range of the
-      section. Each interval starts at the address defined by a
-      mapping symbol, and continues up to, but not including, the
-      address defined by the next (in address order) mapping symbol or
-      the end of the section. A corollary is that there must be a
-      mapping symbol defined at the beginning of each section.
-      Consumers can ignore the size of a section-relative mapping
-      symbol. Producers can set it to 0.
-
-      Absolute mapping symbols
-
-      Because of the need to crystallize a Thumb address with the
-      Thumb-bit set, absolute symbol of type STT_FUNC (symbols of type
-      STT_FUNC defined in section SHN_ABS) need to be mapped with $a
-      or $t.
-
-      The extent of a mapping symbol defined in SHN_ABS is [st_value,
-      st_value + st_size), or [st_value, st_value + 1) if st_size = 0,
-      where [x, y) denotes the half-open address range from x,
-      inclusive, to y, exclusive.
-
-      In the absence of a mapping symbol, a consumer can interpret a
-      function symbol with an odd value as the Thumb code address
-      obtained by clearing the least significant bit of the
-      value. This interpretation is deprecated, and it may not work in
-      the future.
-
-   Note - the Tagging symbols ($b, $f, $p $m) have been dropped from
-   the EABI (which is still under development), so they are not
-   implemented here.  */
+   (See "Mapping symbols", section 4.5.5, ARM AAELF version 1.0).
+   Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag),
+   and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped.  */
 
 static enum mstate mapstate = MAP_UNDEFINED;
 
@@ -1289,15 +1267,15 @@ mapping_state (enum mstate state)
     {
     case MAP_DATA:
       symname = "$d";
-      type = BSF_OBJECT;
+      type = BSF_NO_FLAGS;
       break;
     case MAP_ARM:
       symname = "$a";
-      type = BSF_FUNCTION;
+      type = BSF_NO_FLAGS;
       break;
     case MAP_THUMB:
       symname = "$t";
-      type = BSF_FUNCTION;
+      type = BSF_NO_FLAGS;
       break;
     case MAP_UNDEFINED:
       return;
@@ -1305,7 +1283,7 @@ mapping_state (enum mstate state)
       abort ();
     }
 
-  seg_info (now_seg)->tc_segment_info_data = state;
+  seg_info (now_seg)->tc_segment_info_data.mapstate = state;
 
   symbolP = symbol_new (symname, now_seg, (valueT) frag_now_fix (), frag_now);
   symbol_table_insert (symbolP);
@@ -1337,6 +1315,12 @@ void
 arm_elf_change_section (void)
 {
   flagword flags;
+  segment_info_type *seginfo;
+
+  /* Link an unlinked unwind index table section to the .text section.  */
+  if (elf_section_type (now_seg) == SHT_ARM_EXIDX
+      && elf_linked_to_section (now_seg) == NULL)
+    elf_linked_to_section (now_seg) = text_section;
 
   if (!SEG_NORMAL (now_seg))
     return;
@@ -1347,7 +1331,18 @@ arm_elf_change_section (void)
   if ((flags & SEC_ALLOC) == 0)
     return;
 
-  mapstate = seg_info (now_seg)->tc_segment_info_data;
+  seginfo = seg_info (now_seg);
+  mapstate = seginfo->tc_segment_info_data.mapstate;
+  marked_pr_dependency = seginfo->tc_segment_info_data.marked_pr_dependency;
+}
+
+int
+arm_elf_section_type (const char * str, size_t len)
+{
+  if (len == 5 && strncmp (str, "exidx", 5) == 0)
+    return SHT_ARM_EXIDX;
+
+  return -1;
 }
 #else
 #define mapping_state(a)
@@ -2631,18 +2626,6 @@ do_msr (char * str)
       return;
     }
 
-#if 0  /* The first edition of the ARM architecture manual stated that
-         writing anything other than the flags with an immediate operation
-         had UNPREDICTABLE effects.  This constraint was removed in the
-         second edition of the specification.  */
-  if ((cpu_variant & ARM_EXT_V5) != ARM_EXT_V5
-      && inst.instruction & ((PSR_c | PSR_x | PSR_s) << PSR_SHIFT))
-    {
-      inst.error = _("immediate value cannot be used to set this field");
-      return;
-    }
-#endif
-
   inst.instruction |= INST_IMMEDIATE;
 
   if (inst.reloc.exp.X_add_symbol)
@@ -2774,7 +2757,7 @@ do_mul (char * str)
 }
 
 static void
-do_mla (char * str)
+do_mlas (char * str, bfd_boolean is_mls)
 {
   int rd, rm;
 
@@ -2806,7 +2789,9 @@ do_mla (char * str)
       return;
     }
 
-  if (rm == rd)
+  /* This restriction does not apply to mls (nor to mla in v6, but
+     that's hard to detect at present).  */
+  if (rm == rd && !is_mls)
     as_tsktsk (_("rd and rm should be different in mla"));
 
   if (skip_past_comma (&str) == FAIL
@@ -2827,6 +2812,18 @@ do_mla (char * str)
   end_of_line (str);
 }
 
+static void
+do_mla (char *str)
+{
+  do_mlas (str, FALSE);
+}
+
+static void
+do_mls (char *str)
+{
+  do_mlas (str, TRUE);
+}
+
 /* 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).
@@ -4472,6 +4469,286 @@ do_cpsi (char * str)
   end_of_line (str);
 }
 
+/* ARM V6T2 bitfield manipulation instructions.  */
+
+static int
+five_bit_unsigned_immediate (char **str)
+{
+  expressionS expr;
+
+  skip_whitespace (*str);
+  if (!is_immediate_prefix (**str))
+    {
+      inst.error = _("immediate expression expected");
+      return -1;
+    }
+  (*str)++;
+  if (my_get_expression (&expr, str))
+    {
+      inst.error = _("bad expression");
+      return -1;
+    }
+  if (expr.X_op != O_constant)
+    {
+      inst.error = _("constant expression expected");
+      return -1;
+    }
+  if (expr.X_add_number < 0 || expr.X_add_number > 32)
+    {
+      inst.error = _("immediate value out of range");
+      return -1;
+    }
+  
+  return expr.X_add_number;
+}
+
+static void
+bfci_lsb_and_width (char *str)
+{
+  int lsb, width;
+
+  if ((lsb = five_bit_unsigned_immediate (&str)) == -1)
+    return;
+
+  if (skip_past_comma (&str) == FAIL)
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+  if ((width = five_bit_unsigned_immediate (&str)) == -1)
+    return;
+
+  end_of_line (str);
+
+  if (width == 0 || lsb == 32)
+    {
+      inst.error = _("immediate value out of range");
+      return;
+    }
+  else if (width + lsb > 32)
+    {
+      inst.error = _("bit-field extends past end of register");
+      return;
+    }
+
+  /* Convert to LSB/MSB and write to register.  */
+  inst.instruction |= lsb << 7;
+  inst.instruction |= (width + lsb - 1) << 16;
+}
+
+static void
+do_bfc (char *str)
+{
+  int rd;
+
+  /* Rd.  */
+  skip_whitespace (str);
+  if (((rd = reg_required_here (&str, 12)) == FAIL)
+      || (skip_past_comma (&str) == FAIL))
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+  else if (rd == REG_PC)
+    {
+      inst.error = BAD_PC;
+      return;
+    }
+
+  bfci_lsb_and_width (str);
+}
+
+static void
+do_bfi (char *str)
+{
+  int rd, rm;
+
+  /* Rd.  */
+  skip_whitespace (str);
+  if (((rd = reg_required_here (&str, 12)) == FAIL)
+      || (skip_past_comma (&str) == FAIL))
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+  else if (rd == REG_PC)
+    {
+      inst.error = BAD_PC;
+      return;
+    }
+
+  /* Rm.  Accept #0 in this position as an alternative syntax for bfc.  */
+  skip_whitespace (str);
+  if (is_immediate_prefix (*str))
+    {
+      expressionS expr;
+      str++;
+      if (my_get_expression (&expr, &str))
+       {
+         inst.error = _("bad expression");
+         return;
+       }
+      if (expr.X_op != O_constant)
+       {
+         inst.error = _("constant expression expected");
+         return;
+       }
+      if (expr.X_add_number != 0)
+       {
+         inst.error = _("immediate value out of range");
+         return;
+       }
+      inst.instruction |= 0x0000000f;  /* Rm = PC -> bfc, not bfi.  */
+    }
+  else
+    {
+      if ((rm = reg_required_here (&str, 0)) == FAIL)
+       {
+         inst.error = BAD_ARGS;
+         return;
+       }
+      else if (rm == REG_PC)
+       {
+         inst.error = BAD_PC;
+         return;
+       }
+    }
+  if (skip_past_comma (&str) == FAIL)
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+
+  bfci_lsb_and_width (str);
+}
+
+static void
+do_bfx (char *str)
+{
+  int lsb, width;
+
+  /* Rd.  */
+  skip_whitespace (str);
+  if (reg_required_here (&str, 12) == FAIL
+      || skip_past_comma (&str) == FAIL)
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+
+  /* Rm.  */
+  skip_whitespace (str);
+  if (reg_required_here (&str, 0) == FAIL
+      || skip_past_comma (&str) == FAIL)
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+
+  if ((lsb = five_bit_unsigned_immediate (&str)) == -1)
+    return;
+
+  if (skip_past_comma (&str) == FAIL)
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+  if ((width = five_bit_unsigned_immediate (&str)) == -1)
+    return;
+
+  end_of_line (str);
+
+  if (width == 0 || lsb == 32)
+    {
+      inst.error = _("immediate value out of range");
+      return;
+    }
+  else if (width + lsb > 32)
+    {
+      inst.error = _("bit-field extends past end of register");
+      return;
+    }
+
+  inst.instruction |= lsb << 7;
+  inst.instruction |= (width - 1) << 16;
+}
+
+static void
+do_rbit (char *str)
+{
+  /* Rd.  */
+  skip_whitespace (str);
+  if (reg_required_here (&str, 12) == FAIL
+      || skip_past_comma (&str) == FAIL)
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+
+  /* Rm.  */
+  skip_whitespace (str);
+  if (reg_required_here (&str, 0) == FAIL)
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+
+  end_of_line (str);
+}
+
+/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>.  */
+static void
+do_mov16 (char *str)
+{
+  int rd;
+  expressionS expr;
+
+  /* Rd.  */
+  skip_whitespace (str);
+  if (((rd = reg_required_here (&str, 12)) == FAIL)
+      || (skip_past_comma (&str) == FAIL))
+    {
+      inst.error = BAD_ARGS;
+      return;
+    }
+  else if (rd == REG_PC)
+    {
+      inst.error = BAD_PC;
+      return;
+    }
+
+  /* Imm16.  */
+  skip_whitespace (str);
+  if (!is_immediate_prefix (*str))
+    {
+      inst.error = _("immediate expression expected");
+      return;
+    }
+  str++;
+  if (my_get_expression (&expr, &str))
+    {
+      inst.error = _("bad expression");
+      return;
+    }
+  if (expr.X_op != O_constant)
+    {
+      inst.error = _("constant expression expected");
+      return;
+    }
+  if (expr.X_add_number < 0 || expr.X_add_number > 65535)
+    {
+      inst.error = _("immediate value out of range");
+      return;
+    }
+
+  end_of_line (str);
+
+  /* The value is in two pieces: 0:11, 16:19.  */
+  inst.instruction |= (expr.X_add_number & 0x00000fff);
+  inst.instruction |= (expr.X_add_number & 0x0000f000) << 4;
+}
+  
+
 /* THUMB V5 breakpoint instruction (argument parse)
        BKPT <immed_8>.  */
 
@@ -4511,6 +4788,7 @@ do_t_bkpt (char * str)
   end_of_line (str);
 }
 
+#ifdef OBJ_ELF
 static bfd_reloc_code_real_type
 arm_parse_reloc (void)
 {
@@ -4534,6 +4812,11 @@ arm_parse_reloc (void)
     MAP ("(target1)", BFD_RELOC_ARM_TARGET1),
     MAP ("(sbrel)", BFD_RELOC_ARM_SBREL32),
     MAP ("(target2)", BFD_RELOC_ARM_TARGET2),
+    MAP ("(tlsgd)", BFD_RELOC_ARM_TLS_GD32),
+    MAP ("(tlsldm)", BFD_RELOC_ARM_TLS_LDM32),
+    MAP ("(tlsldo)", BFD_RELOC_ARM_TLS_LDO32),
+    MAP ("(gottpoff)", BFD_RELOC_ARM_TLS_IE32),
+    MAP ("(tpoff)", BFD_RELOC_ARM_TLS_LE32),
     { NULL, 0,         BFD_RELOC_UNUSED }
 #undef MAP
   };
@@ -4551,6 +4834,7 @@ arm_parse_reloc (void)
 
   return reloc_map[i].reloc;
 }
+#endif
 
 /* ARM V5 branch-link-exchange (argument parse) for BLX(1) only.
    Expects inst.instruction is set for BLX(1).
@@ -6475,8 +6759,86 @@ do_ldstv4 (char * str)
   end_of_line (str);
 }
 
-static long
-reg_list (char ** strp)
+static void
+do_ldsttv4 (char * str)
+{
+  int conflict_reg;
+
+  skip_whitespace (str);
+
+  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;
+    }
+
+  if (*str == '[')
+    {
+      int reg;
+
+      str++;
+
+      skip_whitespace (str);
+
+      if ((reg = reg_required_here (&str, 16)) == FAIL)
+       return;
+
+      /* ldrt/strt always use post-indexed addressing, so if the base is
+        the same as Rd, we warn.  */
+      if (conflict_reg == reg)
+       as_warn (_("%s register same as write-back base"),
+                ((inst.instruction & LOAD_BIT)
+                 ? _("destination") : _("source")));
+
+      skip_whitespace (str);
+
+      if (*str == ']')
+       {
+         str ++;
+
+         if (skip_past_comma (&str) == SUCCESS)
+           {
+             /* [Rn],... (post inc)  */
+             if (ldst_extend_v4 (&str) == FAIL)
+               return;
+           }
+         else
+           {
+             /* [Rn]  */
+             skip_whitespace (str);
+
+             /* Skip a write-back '!'.  */
+             if (*str == '!')
+               str++;
+
+             inst.instruction |= (INDEX_UP|HWOFFSET_IMM);
+           }
+       }
+      else
+       {
+         inst.error = _("post-indexed expression expected");
+         return;
+       }
+    }
+  else
+    {
+      inst.error = _("post-indexed expression expected");
+      return;
+    }
+
+  end_of_line (str);
+}
+
+
+static long
+reg_list (char ** strp)
 {
   char * str = * strp;
   long   range = 0;
@@ -7256,32 +7618,40 @@ do_fpa_to_reg (char * str)
   end_of_line (str);
 }
 
+/* Encode a VFP SP register number.  */
+
+static void
+vfp_sp_encode_reg (int reg, enum vfp_sp_reg_pos pos)
+{
+  switch (pos)
+    {
+    case VFP_REG_Sd:
+      inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
+      break;
+
+    case VFP_REG_Sn:
+      inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
+      break;
+
+    case VFP_REG_Sm:
+      inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
+      break;
+
+    default:
+      abort ();
+    }
+}
+
 static int
 vfp_sp_reg_required_here (char ** str,
                          enum vfp_sp_reg_pos pos)
 {
   int    reg;
-  char *start = *str;
+  char * start = *str;
 
   if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_SN].htab)) != FAIL)
     {
-      switch (pos)
-       {
-       case VFP_REG_Sd:
-         inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
-         break;
-
-       case VFP_REG_Sn:
-         inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
-         break;
-
-       case VFP_REG_Sm:
-         inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
-         break;
-
-       default:
-         abort ();
-       }
+      vfp_sp_encode_reg (reg, pos);
       return reg;
     }
 
@@ -7431,21 +7801,21 @@ do_vfp_reg_from_sp (char * str)
   end_of_line (str);
 }
 
-/* Parse and encode a VFP SP register list, storing the initial
-   register in position POS and returning the range as the result.  If
-   the string is invalid return FAIL (an invalid range).  */
+/* Parse a VFP register list.  If the string is invalid return FAIL.
+   Otherwise return the number of registers, and set PBASE to the first
+   register.  Double precision registers are matched if DP is nonzero.  */
 
-static long
-vfp_sp_reg_list (char ** str, enum vfp_sp_reg_pos pos)
+static int
+vfp_parse_reg_list (char **str, int *pbase, int dp)
 {
-  long range = 0;
-  int base_reg = 0;
+  int base_reg;
   int new_base;
-  long base_bits = 0;
+  int regtype;
+  int max_regs;
   int count = 0;
-  long tempinst;
-  unsigned long mask = 0;
   int warned = 0;
+  unsigned long mask = 0;
+  int i;
 
   if (**str != '{')
     return FAIL;
@@ -7453,21 +7823,31 @@ vfp_sp_reg_list (char ** str, enum vfp_sp_reg_pos pos)
   (*str)++;
   skip_whitespace (*str);
 
-  tempinst = inst.instruction;
-
-  do
+  if (dp)
+    {
+      regtype = REG_TYPE_DN;
+      max_regs = 16;
+    }
+  else
     {
-      inst.instruction = 0;
+      regtype = REG_TYPE_SN;
+      max_regs = 32;
+    }
 
-      if ((new_base = vfp_sp_reg_required_here (str, pos)) == FAIL)
-       return FAIL;
+  base_reg = max_regs;
 
-      if (count == 0 || base_reg > new_base)
+  do
+    {
+      new_base = arm_reg_parse (str, all_reg_maps[regtype].htab);
+      if (new_base == FAIL)
        {
-         base_reg = new_base;
-         base_bits = inst.instruction;
+         inst.error = _(all_reg_maps[regtype].expected);
+         return FAIL;
        }
 
+      if (new_base < base_reg)
+       base_reg = new_base;
+
       if (mask & (1 << new_base))
        {
          inst.error = _("invalid register list");
@@ -7492,10 +7872,10 @@ vfp_sp_reg_list (char ** str, enum vfp_sp_reg_pos pos)
          (*str)++;
 
          if ((high_range
-              = arm_reg_parse (str, all_reg_maps[REG_TYPE_SN].htab))
+              = arm_reg_parse (str, all_reg_maps[regtype].htab))
              == FAIL)
            {
-             inst.error = _(all_reg_maps[REG_TYPE_SN].expected);
+             inst.error = _(all_reg_maps[regtype].expected);
              return FAIL;
            }
 
@@ -7520,37 +7900,33 @@ vfp_sp_reg_list (char ** str, enum vfp_sp_reg_pos pos)
     }
   while (skip_past_comma (str) != FAIL);
 
-  if (**str != '}')
-    {
-      inst.error = _("invalid register list");
-      return FAIL;
-    }
-
   (*str)++;
 
-  range = count;
-
   /* Sanity check -- should have raised a parse error above.  */
-  if (count == 0 || count > 32)
+  if (count == 0 || count > max_regs)
     abort ();
 
+  *pbase = base_reg;
+
   /* Final test -- the registers must be consecutive.  */
-  while (count--)
+  mask >>= base_reg;
+  for (i = 0; i < count; i++)
     {
-      if ((mask & (1 << base_reg++)) == 0)
+      if ((mask & (1u << i)) == 0)
        {
          inst.error = _("non-contiguous register range");
          return FAIL;
        }
     }
 
-  inst.instruction = tempinst | base_bits;
-  return range;
+  return count;
 }
 
 static void
 do_vfp_reg2_from_sp2 (char * str)
 {
+  int reg;
+
   skip_whitespace (str);
 
   if (reg_required_here (&str, 12) == FAIL
@@ -7564,11 +7940,12 @@ do_vfp_reg2_from_sp2 (char * str)
     }
 
   /* We require exactly two consecutive SP registers.  */
-  if (vfp_sp_reg_list (&str, VFP_REG_Sm) != 2)
+  if (vfp_parse_reg_list (&str, &reg, 0) != 2)
     {
       if (! inst.error)
        inst.error = _("only two consecutive VFP SP registers allowed here");
     }
+  vfp_sp_encode_reg (reg, VFP_REG_Sm);
 
   end_of_line (str);
 }
@@ -7595,14 +7972,17 @@ do_vfp_sp_from_reg (char * str)
 static void
 do_vfp_sp2_from_reg2 (char * str)
 {
+  int reg;
+
   skip_whitespace (str);
 
   /* We require exactly two consecutive SP registers.  */
-  if (vfp_sp_reg_list (&str, VFP_REG_Sm) != 2)
+  if (vfp_parse_reg_list (&str, &reg, 0) != 2)
     {
       if (! inst.error)
        inst.error = _("only two consecutive VFP SP registers allowed here");
     }
+  vfp_sp_encode_reg (reg, VFP_REG_Sm);
 
   if (skip_past_comma (&str) == FAIL
       || reg_required_here (&str, 12) == FAIL
@@ -7837,122 +8217,12 @@ do_vfp_dp_ldst (char * str)
   end_of_line (str);
 }
 
-static long
-vfp_dp_reg_list (char ** str)
-{
-  long range = 0;
-  int base_reg = 0;
-  int new_base;
-  int count = 0;
-  long tempinst;
-  unsigned long mask = 0;
-  int warned = 0;
-
-  if (**str != '{')
-    return FAIL;
-
-  (*str)++;
-  skip_whitespace (*str);
-
-  tempinst = inst.instruction;
-
-  do
-    {
-      inst.instruction = 0;
-
-      if ((new_base = vfp_dp_reg_required_here (str, VFP_REG_Dd)) == FAIL)
-       return FAIL;
-
-      if (count == 0 || base_reg > new_base)
-       {
-         base_reg = new_base;
-         range = inst.instruction;
-       }
-
-      if (mask & (1 << new_base))
-       {
-         inst.error = _("invalid register list");
-         return FAIL;
-       }
-
-      if ((mask >> new_base) != 0 && ! warned)
-       {
-         as_tsktsk (_("register list not in ascending order"));
-         warned = 1;
-       }
-
-      mask |= 1 << new_base;
-      count++;
-
-      skip_whitespace (*str);
-
-      if (**str == '-') /* We have the start of a range expression */
-       {
-         int high_range;
-
-         (*str)++;
-
-         if ((high_range
-              = arm_reg_parse (str, all_reg_maps[REG_TYPE_DN].htab))
-             == FAIL)
-           {
-             inst.error = _(all_reg_maps[REG_TYPE_DN].expected);
-             return FAIL;
-           }
-
-         if (high_range <= new_base)
-           {
-             inst.error = _("register range not in ascending order");
-             return FAIL;
-           }
-
-         for (new_base++; new_base <= high_range; new_base++)
-           {
-             if (mask & (1 << new_base))
-               {
-                 inst.error = _("invalid register list");
-                 return FAIL;
-               }
-
-             mask |= 1 << new_base;
-             count++;
-           }
-       }
-    }
-  while (skip_past_comma (str) != FAIL);
-
-  if (**str != '}')
-    {
-      inst.error = _("invalid register list");
-      return FAIL;
-    }
-
-  (*str)++;
-
-  range |= 2 * count;
-
-  /* Sanity check -- should have raised a parse error above.  */
-  if (count == 0 || count > 16)
-    abort ();
-
-  /* Final test -- the registers must be consecutive.  */
-  while (count--)
-    {
-      if ((mask & (1 << base_reg++)) == 0)
-       {
-         inst.error = _("non-contiguous register range");
-         return FAIL;
-       }
-    }
-
-  inst.instruction = tempinst;
-  return range;
-}
 
 static void
 vfp_sp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
 {
-  long range;
+  int count;
+  int reg;
 
   skip_whitespace (str);
 
@@ -7973,21 +8243,23 @@ vfp_sp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
     }
 
   if (skip_past_comma (&str) == FAIL
-      || (range = vfp_sp_reg_list (&str, VFP_REG_Sd)) == FAIL)
+      || (count = vfp_parse_reg_list (&str, &reg, 0)) == FAIL)
     {
       if (!inst.error)
        inst.error = BAD_ARGS;
       return;
     }
+  vfp_sp_encode_reg (reg, VFP_REG_Sd);
 
-  inst.instruction |= range;
+  inst.instruction |= count;
   end_of_line (str);
 }
 
 static void
 vfp_dp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
 {
-  long range;
+  int count;
+  int reg;
 
   skip_whitespace (str);
 
@@ -8008,17 +8280,18 @@ vfp_dp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
     }
 
   if (skip_past_comma (&str) == FAIL
-      || (range = vfp_dp_reg_list (&str)) == FAIL)
+      || (count = vfp_parse_reg_list (&str, &reg, 1)) == FAIL)
     {
       if (!inst.error)
        inst.error = BAD_ARGS;
       return;
     }
 
+  count <<= 1;
   if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
-    range += 1;
+    count += 1;
 
-  inst.instruction |= range;
+  inst.instruction |= (reg << 12) | count;
   end_of_line (str);
 }
 
@@ -9418,10 +9691,10 @@ do_t_ldmstm (char * str)
       return;
     }
 
-  if (inst.reloc.type != BFD_RELOC_NONE)
+  if (inst.reloc.type != BFD_RELOC_UNUSED)
     {
       /* This really doesn't seem worth it.  */
-      inst.reloc.type = BFD_RELOC_NONE;
+      inst.reloc.type = BFD_RELOC_UNUSED;
       inst.error = _("expression too complex");
       return;
     }
@@ -9510,10 +9783,10 @@ do_t_push_pop (char * str)
       return;
     }
 
-  if (inst.reloc.type != BFD_RELOC_NONE)
+  if (inst.reloc.type != BFD_RELOC_UNUSED)
     {
       /* This really doesn't seem worth it.  */
-      inst.reloc.type = BFD_RELOC_NONE;
+      inst.reloc.type = BFD_RELOC_UNUSED;
       inst.error = _("expression too complex");
       return;
     }
@@ -10057,10 +10330,25 @@ static const struct asm_opcode insns[] =
   { "wfe",       0xe320f002, 3,  ARM_EXT_V6K,      do_empty},
   { "wfi",       0xe320f003, 3,  ARM_EXT_V6K,      do_empty},
   { "yield",     0xe320f001, 5,  ARM_EXT_V6K,      do_empty},
-  
+
   /*  ARM V6Z.  */
   { "smi",       0xe1600070, 3,  ARM_EXT_V6Z,      do_smi},
 
+  /*  ARM V6T2.  */
+  { "bfc",       0xe7c0001f, 3,  ARM_EXT_V6T2,     do_bfc},
+  { "bfi",       0xe7c00010, 3,  ARM_EXT_V6T2,     do_bfi},
+  { "mls",       0xe0600090, 3,  ARM_EXT_V6T2,     do_mls},
+  { "movw",      0xe3000000, 4,  ARM_EXT_V6T2,     do_mov16},
+  { "movt",      0xe3400000, 4,  ARM_EXT_V6T2,     do_mov16},
+  { "rbit",      0xe3ff0f30, 4,  ARM_EXT_V6T2,     do_rbit},
+  { "sbfx",      0xe7a00050, 4,  ARM_EXT_V6T2,     do_bfx},
+  { "ubfx",      0xe7e00050, 4,  ARM_EXT_V6T2,     do_bfx},
+
+  { "ldrht",     0xe03000b0, 3,  ARM_EXT_V6T2,     do_ldsttv4},
+  { "ldrsht",    0xe03000f0, 3,  ARM_EXT_V6T2,     do_ldsttv4},
+  { "ldrsbt",    0xe03000d0, 3,  ARM_EXT_V6T2,     do_ldsttv4},
+  { "strht",     0xe02000b0, 3,  ARM_EXT_V6T2,     do_ldsttv4},
+
   /* Core FPA instruction set (V1).  */
   {"wfs",        0xee200110, 3,  FPU_FPA_EXT_V1,   do_fpa_ctrl},
   {"rfs",        0xee300110, 3,  FPU_FPA_EXT_V1,   do_fpa_ctrl},
@@ -10919,57 +11207,8 @@ build_arm_ops_hsh (void)
     }
 }
 
-#if 0 /* Suppressed - for now.  */
-#if defined OBJ_ELF || defined OBJ_COFF
-
-#ifdef OBJ_ELF
-#define arm_Note Elf_External_Note
-#else
-typedef struct
-{
-  unsigned char        namesz[4];      /* Size of entry's owner string.  */
-  unsigned char        descsz[4];      /* Size of the note descriptor.  */
-  unsigned char        type[4];        /* Interpretation of the descriptor.  */
-  char         name[1];        /* Start of the name+desc data.  */
-} arm_Note;
-#endif
-
-/* The description is kept to a fix sized in order to make updating
-   it and merging it easier.  */
-#define ARM_NOTE_DESCRIPTION_LENGTH    8
-
-static void
-arm_add_note (const char * name,
-             const char * description,
-             unsigned int type)
-{
-  arm_Note     note ATTRIBUTE_UNUSED;
-  char *       p;
-  unsigned int name_len;
-
-  name_len = (strlen (name) + 1 + 3) & ~3;
-
-  p = frag_more (sizeof (note.namesz));
-  md_number_to_chars (p, (valueT) name_len, sizeof (note.namesz));
-
-  p = frag_more (sizeof (note.descsz));
-  md_number_to_chars (p, (valueT) ARM_NOTE_DESCRIPTION_LENGTH, sizeof (note.descsz));
-
-  p = frag_more (sizeof (note.type));
-  md_number_to_chars (p, (valueT) type, sizeof (note.type));
-
-  p = frag_more (name_len);
-  strcpy (p, name);
-
-  p = frag_more (ARM_NOTE_DESCRIPTION_LENGTH);
-  strncpy (p, description, ARM_NOTE_DESCRIPTION_LENGTH);
-  frag_align (2, 0, 0);
-}
-#endif
-#endif
-
-\f
-static const struct thumb_opcode tinsns[] =
+\f
+static const struct thumb_opcode tinsns[] =
 {
   /* Thumb v1 (ARMv4T).  */
   {"adc",      0x4140,         2,      ARM_EXT_V4T, do_t_arit},
@@ -11046,6 +11285,12 @@ static const struct thumb_opcode tinsns[] =
   {"sxtb",     0xb240,         2,      ARM_EXT_V6,  do_t_arit},
   {"uxth",     0xb280,         2,      ARM_EXT_V6,  do_t_arit},
   {"uxtb",     0xb2c0,         2,      ARM_EXT_V6,  do_t_arit},
+
+  /* ARM V6K.  */
+  {"sev",      0xbf40,         2,      ARM_EXT_V6K, do_empty},
+  {"wfe",      0xbf20,         2,      ARM_EXT_V6K, do_empty},
+  {"wfi",      0xbf30,         2,      ARM_EXT_V6K, do_empty},
+  {"yield",    0xbf10,         2,      ARM_EXT_V6K, do_empty},
 };
 
 void
@@ -11099,7 +11344,7 @@ md_begin (void)
     }
   else if (mfpu_opt == -1)
     {
-#if !(defined (TE_LINUX) || defined (TE_NetBSD))
+#if !(defined (TE_LINUX) || defined (TE_NetBSD) || defined (TE_VXWORKS)) 
       /* Some environments specify a default FPU.  If they don't, infer it
         from the processor.  */
       if (mcpu_fpu_opt != -1)
@@ -11126,6 +11371,7 @@ md_begin (void)
 
   cpu_variant = mcpu_cpu_opt | mfpu_opt;
 
+#if defined OBJ_COFF || defined OBJ_ELF
   {
     unsigned int flags = 0;
 
@@ -11136,7 +11382,6 @@ md_begin (void)
       {
       case EF_ARM_EABI_UNKNOWN:
 #endif
-#if defined OBJ_COFF || defined OBJ_ELF
        /* Set the flags in the private structure.  */
        if (uses_apcs_26)      flags |= F_APCS26;
        if (support_interwork) flags |= F_INTERWORK;
@@ -11162,13 +11407,13 @@ md_begin (void)
        /* Using VFP conventions (even if soft-float).  */
        if (cpu_variant & FPU_VFP_EXT_NONE)
          flags |= F_VFP_FLOAT;
-#endif
+
 #if defined OBJ_ELF
        if (cpu_variant & FPU_ARCH_MAVERICK)
            flags |= EF_ARM_MAVERICK_FLOAT;
        break;
 
-      case EF_ARM_EABI_VER3:
+      case EF_ARM_EABI_VER4:
        /* No additional flags to set.  */
        break;
 
@@ -11176,7 +11421,6 @@ md_begin (void)
        abort ();
       }
 #endif
-#if defined OBJ_COFF || defined OBJ_ELF
     bfd_set_private_flags (stdoutput, flags);
 
     /* We have run out flags in the COFF header to encode the
@@ -11196,8 +11440,8 @@ md_begin (void)
            bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
          }
       }
-#endif
   }
+#endif
 
   /* Record the CPU type as well.  */
   switch (cpu_variant & ARM_CPU_MASK)
@@ -11245,62 +11489,6 @@ md_begin (void)
   else if (cpu_variant & ARM_EXT_V3M)
     mach = bfd_mach_arm_3M;
 
-#if 0 /* Suppressed - for now.  */
-#if defined (OBJ_ELF) || defined (OBJ_COFF)
-
-  /* Create a .note section to fully identify this arm binary.  */
-
-#define NOTE_ARCH_STRING       "arch: "
-
-#if defined OBJ_COFF && ! defined NT_VERSION
-#define NT_VERSION  1
-#define NT_ARCH     2
-#endif
-
-  {
-    segT current_seg = now_seg;
-    subsegT current_subseg = now_subseg;
-    asection * arm_arch;
-    const char * arch_string;
-
-    arm_arch = bfd_make_section_old_way (stdoutput, ARM_NOTE_SECTION);
-
-#ifdef OBJ_COFF
-    bfd_set_section_flags (stdoutput, arm_arch,
-                          SEC_DATA | SEC_ALLOC | SEC_LOAD | SEC_LINK_ONCE \
-                          | SEC_HAS_CONTENTS);
-#else
-    bfd_set_section_flags (stdoutput, arm_arch,
-                          SEC_READONLY | SEC_HAS_CONTENTS);
-#endif
-    arm_arch->output_section = arm_arch;
-    subseg_set (arm_arch, 0);
-
-    switch (mach)
-      {
-      default:
-      case bfd_mach_arm_unknown: arch_string = "unknown"; break;
-      case bfd_mach_arm_2:       arch_string = "armv2"; break;
-      case bfd_mach_arm_2a:      arch_string = "armv2a"; break;
-      case bfd_mach_arm_3:       arch_string = "armv3"; break;
-      case bfd_mach_arm_3M:      arch_string = "armv3M"; break;
-      case bfd_mach_arm_4:       arch_string = "armv4"; break;
-      case bfd_mach_arm_4T:      arch_string = "armv4t"; break;
-      case bfd_mach_arm_5:       arch_string = "armv5"; break;
-      case bfd_mach_arm_5T:      arch_string = "armv5t"; break;
-      case bfd_mach_arm_5TE:     arch_string = "armv5te"; break;
-      case bfd_mach_arm_XScale:  arch_string = "XScale"; break;
-      case bfd_mach_arm_ep9312:  arch_string = "ep9312"; break;
-      case bfd_mach_arm_iWMMXt:  arch_string = "iWMMXt"; break;
-      }
-
-    arm_add_note (NOTE_ARCH_STRING, arch_string, NT_ARCH);
-
-    subseg_set (current_seg, current_subseg);
-  }
-#endif
-#endif /* Suppressed code.  */
-
   bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
 }
 
@@ -11515,17 +11703,10 @@ md_apply_fix3 (fixS *   fixP,
   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);
+  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 || symbol_constant_p (fixP->fx_addsy))
-      && !fixP->fx_pcrel)
-#else
   if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
-#endif
     fixP->fx_done = 1;
 
   /* If this symbol is in a different section then we need to leave it for
@@ -11536,15 +11717,7 @@ md_apply_fix3 (fixS *   fixP,
       if (fixP->fx_addsy != NULL
          && S_IS_DEFINED (fixP->fx_addsy)
          && S_GET_SEGMENT (fixP->fx_addsy) != seg)
-       {
-         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);
-       }
+       value += md_pcrel_from (fixP);
     }
 
   /* Remember value for emit_reloc.  */
@@ -11552,6 +11725,11 @@ md_apply_fix3 (fixS *   fixP,
 
   switch (fixP->fx_r_type)
     {
+    case BFD_RELOC_NONE:
+      /* This will need to go in the object file.  */
+      fixP->fx_done = 0;
+      break;
+  
     case BFD_RELOC_ARM_IMMEDIATE:
       /* We claim that this fixup has been processed here,
         even if in fact we generate an error because we do
@@ -11763,8 +11941,7 @@ md_apply_fix3 (fixS *   fixP,
 #define SEXT24(x)      ((((x) & 0xffffff) ^ (~ 0x7fffff)) + 0x800000)
 
 #ifdef OBJ_ELF
-      if (! target_oabi)
-       value = fixP->fx_offset;
+      value = fixP->fx_offset;
 #endif
 
       /* We are going to store value (shifted right by two) in the
@@ -11782,11 +11959,9 @@ md_apply_fix3 (fixS *   fixP,
             branch instruction 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
+            FIXME: The test for OBJ_ELF is only here because I have not
+            worked out how to do this for OBJ_COFF.  */
+         if (fixP->fx_addsy != NULL
              && S_IS_DEFINED (fixP->fx_addsy)
              && S_GET_SEGMENT (fixP->fx_addsy) == seg)
            {
@@ -11815,7 +11990,18 @@ md_apply_fix3 (fixS *   fixP,
        as_bad_where (fixP->fx_file, fixP->fx_line,
                      _("out of range branch"));
 
-      newval = (value & 0x00ffffff) | (newval & 0xff000000);
+      if (seg->use_rela_p && !fixP->fx_done)
+       {
+         /* Must unshift the value before storing it in the addend.  */
+         value <<= 2;
+#ifdef OBJ_ELF
+         fixP->fx_offset = value;
+#endif
+         fixP->fx_addnumber = value;
+         newval = newval & 0xff000000;
+       }
+      else
+         newval = (value & 0x00ffffff) | (newval & 0xff000000);
       md_number_to_chars (buf, newval, INSN_SIZE);
       break;
 
@@ -11825,13 +12011,26 @@ md_apply_fix3 (fixS *   fixP,
        newval = md_chars_to_number (buf, INSN_SIZE);
 
 #ifdef OBJ_ELF
-       if (! target_oabi)
-         value = fixP->fx_offset;
+       value = fixP->fx_offset;
 #endif
        hbit   = (value >> 1) & 1;
        value  = (value >> 2) & 0x00ffffff;
        value  = (value + (newval & 0x00ffffff)) & 0x00ffffff;
-       newval = value | (newval & 0xfe000000) | (hbit << 24);
+
+       if (seg->use_rela_p && !fixP->fx_done)
+         {
+           /* Must sign-extend and unshift the value before storing
+              it in the addend.  */
+           value = SEXT24 (value);
+           value = (value << 2) | hbit;
+#ifdef OBJ_ELF
+           fixP->fx_offset = value;
+#endif
+           fixP->fx_addnumber = value;
+           newval = newval & 0xfe000000;
+         }
+       else
+         newval = value | (newval & 0xfe000000) | (hbit << 24);
        md_number_to_chars (buf, newval, INSN_SIZE);
       }
       break;
@@ -11847,7 +12046,16 @@ md_apply_fix3 (fixS *   fixP,
        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);
+       if (seg->use_rela_p && !fixP->fx_done)
+         {
+#ifdef OBJ_ELF
+           fixP->fx_offset = value;
+#endif
+           fixP->fx_addnumber = value;
+           newval = newval & 0xff00;
+         }
+       else
+         newval = (newval & 0xff00) | ((value & 0x1ff) >> 1);
       }
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
@@ -11863,7 +12071,16 @@ md_apply_fix3 (fixS *   fixP,
        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);
+       if (seg->use_rela_p && !fixP->fx_done)
+         {
+#ifdef OBJ_ELF
+           fixP->fx_offset = value;
+#endif
+           fixP->fx_addnumber = value;
+           newval = newval & 0xf800;
+         }
+       else
+         newval = (newval & 0xf800) | ((value & 0xfff) >> 1);
       }
       md_number_to_chars (buf, newval, THUMB_SIZE);
       break;
@@ -11888,24 +12105,39 @@ md_apply_fix3 (fixS *   fixP,
          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);
        if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
          /* For a BLX instruction, make sure that the relocation is rounded up
             to a word boundary.  This follows the semantics of the instruction
             which specifies that bit 1 of the target address will come from bit
             1 of the base address.  */
-         newval2 = (newval2 + 1) & ~ 1;
+         value = (value + 1) & ~ 1;
+
+       if (seg->use_rela_p && !fixP->fx_done)
+         {
+#ifdef OBJ_ELF
+           fixP->fx_offset = value;
+#endif
+           fixP->fx_addnumber = value;
+           newval = newval & 0xf800;
+           newval2 = newval2 & 0xf800;
+         }
+       else
+         {
+           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);
       }
       break;
 
     case BFD_RELOC_8:
+      if (seg->use_rela_p && !fixP->fx_done)
+       break;
       if (fixP->fx_done || fixP->fx_pcrel)
        md_number_to_chars (buf, value, 1);
 #ifdef OBJ_ELF
-      else if (!target_oabi)
+      else
        {
          value = fixP->fx_offset;
          md_number_to_chars (buf, value, 1);
@@ -11914,10 +12146,12 @@ md_apply_fix3 (fixS *   fixP,
       break;
 
     case BFD_RELOC_16:
+      if (seg->use_rela_p && !fixP->fx_done)
+       break;
       if (fixP->fx_done || fixP->fx_pcrel)
        md_number_to_chars (buf, value, 2);
 #ifdef OBJ_ELF
-      else if (!target_oabi)
+      else
        {
          value = fixP->fx_offset;
          md_number_to_chars (buf, value, 2);
@@ -11926,9 +12160,19 @@ md_apply_fix3 (fixS *   fixP,
       break;
 
 #ifdef OBJ_ELF
+     case BFD_RELOC_ARM_TLS_GD32:
+     case BFD_RELOC_ARM_TLS_LE32:
+     case BFD_RELOC_ARM_TLS_IE32:
+     case BFD_RELOC_ARM_TLS_LDM32:
+     case BFD_RELOC_ARM_TLS_LDO32:
+       S_SET_THREAD_LOCAL (fixP->fx_addsy);
+       /* fall through */
+
     case BFD_RELOC_ARM_GOT32:
     case BFD_RELOC_ARM_GOTOFF:
     case BFD_RELOC_ARM_TARGET2:
+      if (seg->use_rela_p && !fixP->fx_done)
+       break;
       md_number_to_chars (buf, 0, 4);
       break;
 #endif
@@ -11939,10 +12183,12 @@ md_apply_fix3 (fixS *   fixP,
     case BFD_RELOC_ARM_ROSEGREL32:
     case BFD_RELOC_ARM_SBREL32:
     case BFD_RELOC_32_PCREL:
+      if (seg->use_rela_p && !fixP->fx_done)
+       break;
       if (fixP->fx_done || fixP->fx_pcrel)
        md_number_to_chars (buf, value, 4);
 #ifdef OBJ_ELF
-      else if (!target_oabi)
+      else
        {
          value = fixP->fx_offset;
          md_number_to_chars (buf, value, 4);
@@ -12159,7 +12405,7 @@ md_apply_fix3 (fixS *   fixP,
       fixP->fx_done = 0;
       return;
 
-    case BFD_RELOC_NONE:
+    case BFD_RELOC_UNUSED:
     default:
       as_bad_where (fixP->fx_file, fixP->fx_line,
                    _("bad relocation fixup type (%d)"), fixP->fx_r_type);
@@ -12215,6 +12461,7 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
          break;
        }
 
+    case BFD_RELOC_NONE:
     case BFD_RELOC_ARM_PCREL_BRANCH:
     case BFD_RELOC_ARM_PCREL_BLX:
     case BFD_RELOC_RVA:
@@ -12244,6 +12491,18 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
     case BFD_RELOC_ARM_SBREL32:
     case BFD_RELOC_ARM_PREL31:
     case BFD_RELOC_ARM_TARGET2:
+    case BFD_RELOC_ARM_TLS_LE32:
+    case BFD_RELOC_ARM_TLS_LDO32:
+      code = fixp->fx_r_type;
+      break;
+
+    case BFD_RELOC_ARM_TLS_GD32:
+    case BFD_RELOC_ARM_TLS_IE32:
+    case BFD_RELOC_ARM_TLS_LDM32:
+      /* BFD will include the symbol's address in the addend.  
+        But we don't want that, so subtract it out again here.  */
+      if (!S_IS_COMMON (fixp->fx_addsy))
+       reloc->addend -= (*reloc->sym_ptr_ptr)->value;
       code = fixp->fx_r_type;
       break;
 #endif
@@ -12279,6 +12538,7 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
 
        switch (fixp->fx_r_type)
          {
+         case BFD_RELOC_NONE:             type = "NONE";         break;
          case BFD_RELOC_ARM_OFFSET_IMM8:  type = "OFFSET_IMM8";  break;
          case BFD_RELOC_ARM_SHIFT_IMM:    type = "SHIFT_IMM";    break;
          case BFD_RELOC_ARM_SMI:          type = "SMI";          break;
@@ -12402,7 +12662,7 @@ output_inst (const char * str)
   else
     md_number_to_chars (to, inst.instruction, inst.size);
 
-  if (inst.reloc.type != BFD_RELOC_NONE)
+  if (inst.reloc.type != BFD_RELOC_UNUSED)
     fix_new_arm (frag_now, to - frag_now->fr_literal,
                 inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
                 inst.reloc.type);
@@ -12419,12 +12679,6 @@ md_assemble (char * str)
   char *p;
   char *start;
 
-  /* Align the instruction.
-     This may not be the right thing to do but ...  */
-#if 0
-  arm_align (2, 0);
-#endif
-
   /* Align the previous label if needed.  */
   if (last_label_seen != NULL)
     {
@@ -12434,7 +12688,7 @@ md_assemble (char * str)
     }
 
   memset (&inst, '\0', sizeof (inst));
-  inst.reloc.type = BFD_RELOC_NONE;
+  inst.reloc.type = BFD_RELOC_UNUSED;
 
   skip_whitespace (str);
 
@@ -12613,7 +12867,6 @@ struct arm_option_table arm_opts[] =
   {"mthumb", N_("assemble Thumb code"),    &thumb_mode,  1, NULL},
   {"mthumb-interwork", N_("support ARM/Thumb interworking"),
    &support_interwork, 1, NULL},
-  {"moabi",  N_("use old ABI (ELF only)"), &target_oabi, 1, NULL},
   {"mapcs-32", N_("code uses 32-bit program counter"), &uses_apcs_26, 0, NULL},
   {"mapcs-26", N_("code uses 26-bit program counter"), &uses_apcs_26, 1, NULL},
   {"mapcs-float", N_("floating point args are in fp regs"), &uses_apcs_float,
@@ -12812,8 +13065,8 @@ static struct arm_cpu_option_table arm_cpus[] =
   {"arm1020",          ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
   {"arm1020t",         ARM_ARCH_V5T,    FPU_ARCH_VFP_V1},
   {"arm1020e",         ARM_ARCH_V5TE,   FPU_ARCH_VFP_V2},
-  {"arm1026ejs",       ARM_ARCH_V5TE  FPU_ARCH_VFP_V2},
-  {"arm1026ej-s",      ARM_ARCH_V5TE  FPU_ARCH_VFP_V2},
+  {"arm1026ejs",       ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
+  {"arm1026ej-s",      ARM_ARCH_V5TEJ,  FPU_ARCH_VFP_V2},
   {"arm1136js",                ARM_ARCH_V6,     FPU_NONE},
   {"arm1136j-s",       ARM_ARCH_V6,     FPU_NONE},
   {"arm1136jfs",       ARM_ARCH_V6,     FPU_ARCH_VFP_V2},
@@ -12865,6 +13118,10 @@ static struct arm_arch_option_table arm_archs[] =
   {"armv6k",            ARM_ARCH_V6K,    FPU_ARCH_VFP},
   {"armv6z",            ARM_ARCH_V6Z,    FPU_ARCH_VFP},
   {"armv6zk",           ARM_ARCH_V6ZK,   FPU_ARCH_VFP},
+  {"armv6t2",          ARM_ARCH_V6T2,   FPU_ARCH_VFP},
+  {"armv6kt2",         ARM_ARCH_V6KT2,  FPU_ARCH_VFP},
+  {"armv6zt2",         ARM_ARCH_V6ZT2,  FPU_ARCH_VFP},
+  {"armv6zkt2",                ARM_ARCH_V6ZKT2, FPU_ARCH_VFP},
   {"xscale",           ARM_ARCH_XSCALE, FPU_ARCH_VFP},
   {"iwmmxt",           ARM_ARCH_IWMMXT, FPU_ARCH_VFP},
   {NULL, 0, 0}
@@ -12939,11 +13196,11 @@ struct arm_eabi_option_table
 };
 
 #ifdef OBJ_ELF
-/* We only know hot to output GNU and ver 3 (AAELF) formats.  */
+/* We only know how to output GNU and ver 4 (AAELF) formats.  */
 static struct arm_eabi_option_table arm_eabis[] =
 {
   {"gnu",      EF_ARM_EABI_UNKNOWN},
-  {"3",                EF_ARM_EABI_VER3},
+  {"4",                EF_ARM_EABI_VER4},
   {NULL, 0}
 };
 #endif
@@ -13410,16 +13667,19 @@ arm_adjust_symtab (void)
          elf_symbol_type * elf_sym;
 
          elf_sym = elf_symbol (symbol_get_bfdsym (sym));
-         bind = ELF_ST_BIND (elf_sym);
-
-         /* 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);
+         bind = ELF_ST_BIND (elf_sym->internal_elf_sym.st_info);
+
+         if (! bfd_is_arm_mapping_symbol_name (elf_sym->symbol.name))
+           { 
+             /* 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
@@ -13542,6 +13802,11 @@ arm_fix_adjustable (fixS * fixP)
   if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
       || fixP->fx_r_type == BFD_RELOC_ARM_GOT32
       || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32
+      || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32
       || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
     return 0;
 
@@ -13555,21 +13820,15 @@ elf32_arm_target_format (void)
   return (target_big_endian
          ? "elf32-bigarm-symbian"
          : "elf32-littlearm-symbian");
+#elif defined (TE_VXWORKS)
+  return (target_big_endian
+         ? "elf32-bigarm-vxworks"
+         : "elf32-littlearm-vxworks");
 #else
   if (target_big_endian)
-    {
-      if (target_oabi)
-       return "elf32-bigarm-oabi";
-      else
-       return "elf32-bigarm";
-    }
+    return "elf32-bigarm";
   else
-    {
-      if (target_oabi)
-       return "elf32-littlearm-oabi";
-      else
-       return "elf32-littlearm";
-    }
+    return "elf32-littlearm";
 #endif
 }
 
@@ -13603,8 +13862,12 @@ s_arm_elf_cons (int nbytes)
   do
     {
       bfd_reloc_code_real_type reloc;
+      char *sym_start;
+      int sym_len;
 
+      sym_start = input_line_pointer;
       expression (& exp);
+      sym_len = input_line_pointer - sym_start;
 
       if (exp.X_op == O_symbol
          && * input_line_pointer == '('
@@ -13618,9 +13881,22 @@ s_arm_elf_cons (int nbytes)
                    howto->name, nbytes);
          else
            {
-             char *p = frag_more ((int) nbytes);
+             char *p;
              int offset = nbytes - size;
-
+             char *saved_buf = alloca (sym_len), *saved_input;
+
+             /* We've parsed an expression stopping at O_symbol.  But there
+                may be more expression left now that we have parsed the
+                relocation marker.  Parse it again.  */
+             saved_input = input_line_pointer - sym_len;
+             memcpy (saved_buf, saved_input, sym_len);
+             memmove (saved_input, sym_start, sym_len);
+             input_line_pointer = saved_input;
+             expression (& exp);
+             memcpy (saved_input, saved_buf, sym_len);
+             assert (input_line_pointer >= saved_input + sym_len);
+
+             p = frag_more ((int) nbytes);
              fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
                           &exp, 0, reloc);
            }
@@ -13678,102 +13954,1266 @@ s_arm_rel31 (int ignored ATTRIBUTE_UNUSED)
 
   demand_empty_rest_of_line ();
 }
+\f
+/* Code to deal with unwinding tables.  */
 
-#endif /* OBJ_ELF */
+static void add_unwind_adjustsp (offsetT);
 
-/* This is called from HANDLE_ALIGN in write.c.  Fill in the contents
-   of an rs_align_code fragment.  */
+/* Switch to section NAME and create section if necessary.  It's
+   rather ugly that we have to manipulate input_line_pointer but I
+   don't see any other way to accomplish the same thing without
+   changing obj-elf.c (which may be the Right Thing, in the end).
+   Copied from tc-ia64.c.  */
 
-void
-arm_handle_align (fragS * fragP)
+static void
+set_section (char *name)
 {
-  static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
-  static char const thumb_noop[2] = { 0xc0, 0x46 };
-  static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
-  static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
+  char *saved_input_line_pointer;
 
-  int bytes, fix, noop_size;
-  char * p;
-  const char * noop;
+  saved_input_line_pointer = input_line_pointer;
+  input_line_pointer = name;
+  obj_elf_section (0);
+  input_line_pointer = saved_input_line_pointer;
+}
 
-  if (fragP->fr_type != rs_align_code)
-    return;
+/* Cenerate and deferred unwind frame offset.  */
 
-  bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
-  p = fragP->fr_literal + fragP->fr_fix;
-  fix = 0;
+static void
+flush_pending_unwind (void)
+{
+  offsetT offset;
 
-  if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
-    bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+  offset = unwind.pending_offset;
+  unwind.pending_offset = 0;
+  if (offset != 0)
+    add_unwind_adjustsp (offset);
+}
 
-  if (fragP->tc_frag_data)
+/* Add an opcode to this list for this function.  Two-byte opcodes should
+   be passed as op[0] << 8 | op[1].  The list of opcodes is built in reverse
+   order.  */
+
+static void
+add_unwind_opcode (valueT op, int length)
+{
+  /* Add any deferred stack adjustment.  */
+  if (unwind.pending_offset)
+    flush_pending_unwind ();
+
+  unwind.sp_restored = 0;
+
+  if (unwind.opcode_count + length > unwind.opcode_alloc)
     {
-      if (target_big_endian)
-       noop = thumb_bigend_noop;
+      unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
+      if (unwind.opcodes)
+       unwind.opcodes = xrealloc (unwind.opcodes,
+                                  unwind.opcode_alloc);
       else
-       noop = thumb_noop;
-      noop_size = sizeof (thumb_noop);
+       unwind.opcodes = xmalloc (unwind.opcode_alloc);
     }
-  else
+  while (length > 0)
     {
-      if (target_big_endian)
-       noop = arm_bigend_noop;
-      else
-       noop = arm_noop;
-      noop_size = sizeof (arm_noop);
+      length--;
+      unwind.opcodes[unwind.opcode_count] = op & 0xff;
+      op >>= 8;
+      unwind.opcode_count++;
     }
+}
 
-  if (bytes & (noop_size - 1))
+/* Add unwind opcodes to adjust the stack pointer.  */
+
+static void
+add_unwind_adjustsp (offsetT offset)
+{
+  valueT op;
+
+  if (offset > 0x200)
     {
-      fix = bytes & (noop_size - 1);
-      memset (p, 0, fix);
-      p += fix;
-      bytes -= fix;
-    }
+      /* We need at most 5 bytes to hold a 32-bit value in a uleb128.  */
+      char bytes[5];
+      int n;
+      valueT o;
 
-  while (bytes >= noop_size)
+      /* Long form: 0xb2, uleb128.  */
+      /* This might not fit in a word so add the individual bytes,
+        remembering the list is built in reverse order.  */
+      o = (valueT) ((offset - 0x204) >> 2);
+      if (o == 0)
+       add_unwind_opcode (0, 1);
+
+      /* Calculate the uleb128 encoding of the offset.  */
+      n = 0;
+      while (o)
+       {
+         bytes[n] = o & 0x7f;
+         o >>= 7;
+         if (o)
+           bytes[n] |= 0x80;
+         n++;
+       }
+      /* Add the insn.  */
+      for (; n; n--)
+       add_unwind_opcode (bytes[n - 1], 1);
+      add_unwind_opcode (0xb2, 1);
+    }
+  else if (offset > 0x100)
     {
-      memcpy (p, noop, noop_size);
-      p += noop_size;
-      bytes -= noop_size;
-      fix += noop_size;
+      /* Two short opcodes.  */
+      add_unwind_opcode (0x3f, 1);
+      op = (offset - 0x104) >> 2;
+      add_unwind_opcode (op, 1);
+    }
+  else if (offset > 0)
+    {
+      /* Short opcode.  */
+      op = (offset - 4) >> 2;
+      add_unwind_opcode (op, 1);
+    }
+  else if (offset < 0)
+    {
+      offset = -offset;
+      while (offset > 0x100)
+       {
+         add_unwind_opcode (0x7f, 1);
+         offset -= 0x100;
+       }
+      op = ((offset - 4) >> 2) | 0x40;
+      add_unwind_opcode (op, 1);
     }
+}
 
-  fragP->fr_fix += fix;
-  fragP->fr_var = noop_size;
+/* Finish the list of unwind opcodes for this function.  */
+static void
+finish_unwind_opcodes (void)
+{
+  valueT op;
+
+  if (unwind.fp_used)
+    {
+      /* Adjust sp as neccessary.  */
+      unwind.pending_offset += unwind.fp_offset - unwind.frame_size;
+      flush_pending_unwind ();
+
+      /* After restoring sp from the frame pointer.  */
+      op = 0x90 | unwind.fp_reg;
+      add_unwind_opcode (op, 1);
+    }
+  else
+    flush_pending_unwind ();
 }
 
-/* Called from md_do_align.  Used to create an alignment
-   frag in a code section.  */
 
-void
-arm_frag_align_code (int n, int max)
+/* Start an exception table entry.  If idx is nonzero this is an index table
+   entry.  */
+
+static void
+start_unwind_section (const segT text_seg, int idx)
 {
-  char * p;
+  const char * text_name;
+  const char * prefix;
+  const char * prefix_once;
+  size_t prefix_len;
+  size_t text_len;
+  char * sec_name;
+  size_t sec_name_len;
 
-  /* We assume that there will never be a requirement
-     to support alignments greater than 32 bytes.  */
-  if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
-    as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+  if (idx)
+    {
+      prefix = ELF_STRING_ARM_unwind;
+      prefix_once = ELF_STRING_ARM_unwind_once;
+    }
+  else
+    {
+      prefix = ELF_STRING_ARM_unwind_info;
+      prefix_once = ELF_STRING_ARM_unwind_info_once;
+    }
 
-  p = frag_var (rs_align_code,
-               MAX_MEM_FOR_RS_ALIGN_CODE,
-               1,
-               (relax_substateT) max,
-               (symbolS *) NULL,
-               (offsetT) n,
-               (char *) NULL);
-  *p = 0;
+  text_name = segment_name (text_seg);
+  if (streq (text_name, ".text"))
+    text_name = "";
+
+  if (strncmp (text_name, ".gnu.linkonce.t.",
+              strlen (".gnu.linkonce.t.")) == 0)
+    {
+      prefix = prefix_once;
+      text_name += strlen (".gnu.linkonce.t.");
+    }
+
+  prefix_len = strlen (prefix);
+  text_len = strlen (text_name);
+  sec_name_len = prefix_len + text_len;
+  sec_name = alloca (sec_name_len + 1);
+  memcpy (sec_name, prefix, prefix_len);
+  memcpy (sec_name + prefix_len, text_name, text_len);
+  sec_name[prefix_len + text_len] = '\0';
+
+  /* Handle COMDAT group.  */
+  if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
+    {
+      char *section;
+      size_t len, group_name_len;
+      const char *group_name = elf_group_name (text_seg);
+
+      if (group_name == NULL)
+        {
+          as_bad ("Group section `%s' has no group signature",
+                  segment_name (text_seg));
+          ignore_rest_of_line ();
+          return;
+        }
+      /* We have to construct a fake section directive.  */
+      group_name_len = strlen (group_name);
+      if (idx)
+       prefix_len = 13;
+      else
+       prefix_len = 16;
+
+      len = (sec_name_len
+             + prefix_len             /* ,"aG",%sectiontype,  */
+             + group_name_len         /* ,group_name  */
+             + 7);                    /* ,comdat  */
+
+      section = alloca (len + 1);
+      memcpy (section, sec_name, sec_name_len);
+      if (idx)
+         memcpy (section + sec_name_len, ",\"aG\",%exidx,", 13);
+      else
+         memcpy (section + sec_name_len, ",\"aG\",%progbits,", 16);
+      memcpy (section + sec_name_len + prefix_len, group_name, group_name_len);
+      memcpy (section + len - 7, ",comdat", 7);
+      section [len] = '\0';
+      set_section (section);
+    }
+  else
+    {
+      set_section (sec_name);
+      bfd_set_section_flags (stdoutput, now_seg,
+                            SEC_LOAD | SEC_ALLOC | SEC_READONLY);
+    }
+
+  /* Set the setion link for index tables.  */
+  if (idx)
+    elf_linked_to_section (now_seg) = text_seg;
 }
 
-/* Perform target specific initialisation of a frag.  */
 
-void
-arm_init_frag (fragS * fragP)
+/* Start an unwind table entry.  HAVE_DATA is nonzero if we have additional
+   personality routine data.  Returns zero, or the index table value for
+   and inline entry.  */
+
+static valueT
+create_unwind_entry (int have_data)
 {
-  /* Record whether this frag is in an ARM or a THUMB area.  */
-  fragP->tc_frag_data = thumb_mode;
-}
+  int size;
+  addressT where;
+  char *ptr;
+  /* The current word of data.  */
+  valueT data;
+  /* The number of bytes left in this word.  */
+  int n;
+
+  finish_unwind_opcodes ();
+
+  /* Remember the current text section.  */
+  unwind.saved_seg = now_seg;
+  unwind.saved_subseg = now_subseg;
+
+  start_unwind_section (now_seg, 0);
+
+  if (unwind.personality_routine == NULL)
+    {
+      if (unwind.personality_index == -2)
+       {
+         if (have_data)
+           as_bad (_("handerdata in cantunwind frame"));
+         return 1; /* EXIDX_CANTUNWIND.  */
+       }
+
+      /* Use a default personality routine if none is specified.  */
+      if (unwind.personality_index == -1)
+       {
+         if (unwind.opcode_count > 3)
+           unwind.personality_index = 1;
+         else
+           unwind.personality_index = 0;
+       }
+
+      /* Space for the personality routine entry.  */
+      if (unwind.personality_index == 0)
+       {
+         if (unwind.opcode_count > 3)
+           as_bad (_("too many unwind opcodes for personality routine 0"));
+
+         if (!have_data)
+           {
+             /* All the data is inline in the index table.  */
+             data = 0x80;
+             n = 3;
+             while (unwind.opcode_count > 0)
+               {
+                 unwind.opcode_count--;
+                 data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+                 n--;
+               }
+
+             /* Pad with "finish" opcodes.  */
+             while (n--)
+               data = (data << 8) | 0xb0;
+
+             return data;
+           }
+         size = 0;
+       }
+      else
+       /* We get two opcodes "free" in the first word.  */
+       size = unwind.opcode_count - 2;
+    }
+  else
+    /* An extra byte is required for the opcode count.  */
+    size = unwind.opcode_count + 1;
+
+  size = (size + 3) >> 2;
+  if (size > 0xff)
+    as_bad (_("too many unwind opcodes"));
+
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
+  unwind.table_entry = expr_build_dot ();
+
+  /* Allocate the table entry.  */
+  ptr = frag_more ((size << 2) + 4);
+  where = frag_now_fix () - ((size << 2) + 4);
+
+  switch (unwind.personality_index)
+    {
+    case -1:
+      /* ??? Should this be a PLT generating relocation?  */
+      /* Custom personality routine.  */
+      fix_new (frag_now, where, 4, unwind.personality_routine, 0, 1,
+              BFD_RELOC_ARM_PREL31);
+
+      where += 4;
+      ptr += 4;
+
+      /* Set the first byte to the number of additional words.  */
+      data = size - 1;
+      n = 3;
+      break;
+
+    /* ABI defined personality routines.  */
+    case 0:
+      /* Three opcodes bytes are packed into the first word.  */
+      data = 0x80;
+      n = 3;
+      break;
+
+    case 1:
+    case 2:
+      /* The size and first two opcode bytes go in the first word.  */
+      data = ((0x80 + unwind.personality_index) << 8) | size;
+      n = 2;
+      break;
+
+    default:
+      /* Should never happen.  */
+      abort ();
+    }
+
+  /* Pack the opcodes into words (MSB first), reversing the list at the same
+     time.  */
+  while (unwind.opcode_count > 0)
+    {
+      if (n == 0)
+       {
+         md_number_to_chars (ptr, data, 4);
+         ptr += 4;
+         n = 4;
+         data = 0;
+       }
+      unwind.opcode_count--;
+      n--;
+      data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+    }
+
+  /* Finish off the last word.  */
+  if (n < 4)
+    {
+      /* Pad with "finish" opcodes.  */
+      while (n--)
+       data = (data << 8) | 0xb0;
+
+      md_number_to_chars (ptr, data, 4);
+    }
+
+  if (!have_data)
+    {
+      /* Add an empty descriptor if there is no user-specified data.   */
+      ptr = frag_more (4);
+      md_number_to_chars (ptr, 0, 4);
+    }
+
+  return 0;
+}
+
+
+/* Parse an unwind_fnstart directive.  Simply records the current location.  */
+
+static void
+s_arm_unwind_fnstart (int ignored ATTRIBUTE_UNUSED)
+{
+  demand_empty_rest_of_line ();
+  /* Mark the start of the function.  */
+  unwind.proc_start = expr_build_dot ();
+
+  /* Reset the rest of the unwind info.  */
+  unwind.opcode_count = 0;
+  unwind.table_entry = NULL;
+  unwind.personality_routine = NULL;
+  unwind.personality_index = -1;
+  unwind.frame_size = 0;
+  unwind.fp_offset = 0;
+  unwind.fp_reg = 13;
+  unwind.fp_used = 0;
+  unwind.sp_restored = 0;
+}
+
+
+/* Parse a handlerdata directive.  Creates the exception handling table entry
+   for the function.  */
+
+static void
+s_arm_unwind_handlerdata (int ignored ATTRIBUTE_UNUSED)
+{
+  demand_empty_rest_of_line ();
+  if (unwind.table_entry)
+    as_bad (_("dupicate .handlerdata directive"));
+
+  create_unwind_entry (1);
+}
+
+/* Parse an unwind_fnend directive.  Generates the index table entry.  */
+
+static void
+s_arm_unwind_fnend (int ignored ATTRIBUTE_UNUSED)
+{
+  long where;
+  char *ptr;
+  valueT val;
+
+  demand_empty_rest_of_line ();
+
+  /* Add eh table entry.  */
+  if (unwind.table_entry == NULL)
+    val = create_unwind_entry (0);
+  else
+    val = 0;
+
+  /* Add index table entry.  This is two words.  */
+  start_unwind_section (unwind.saved_seg, 1);
+  frag_align (2, 0, 0);
+  record_alignment (now_seg, 2);
+
+  ptr = frag_more (8);
+  where = frag_now_fix () - 8;
+
+  /* Self relative offset of the function start.  */
+  fix_new (frag_now, where, 4, unwind.proc_start, 0, 1,
+          BFD_RELOC_ARM_PREL31);
+
+  /* Indicate dependency on EHABI-defined personality routines to the
+     linker, if it hasn't been done already.  */
+  if (unwind.personality_index >= 0 && unwind.personality_index < 3)
+    {
+      char *name[] = { "__aeabi_unwind_cpp_pr0",
+                      "__aeabi_unwind_cpp_pr1",
+                      "__aeabi_unwind_cpp_pr2" };
+      if (!(marked_pr_dependency & (1 << unwind.personality_index)))
+       {
+         symbolS *pr = symbol_find_or_make (name[unwind.personality_index]);
+         fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
+         marked_pr_dependency |= 1 << unwind.personality_index;
+         seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
+           = marked_pr_dependency;
+        }
+    }
+
+  if (val)
+    /* Inline exception table entry.  */
+    md_number_to_chars (ptr + 4, val, 4);
+  else
+    /* Self relative offset of the table entry.  */
+    fix_new (frag_now, where + 4, 4, unwind.table_entry, 0, 1,
+            BFD_RELOC_ARM_PREL31);
+
+  /* Restore the original section.  */
+  subseg_set (unwind.saved_seg, unwind.saved_subseg);
+}
+
+
+/* Parse an unwind_cantunwind directive.  */
+
+static void
+s_arm_unwind_cantunwind (int ignored ATTRIBUTE_UNUSED)
+{
+  demand_empty_rest_of_line ();
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("personality routine specified for cantunwind frame"));
+
+  unwind.personality_index = -2;
+}
+
+
+/* Parse a personalityindex directive.  */
+
+static void
+s_arm_unwind_personalityindex (int ignored ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("duplicate .personalityindex directive"));
+
+  SKIP_WHITESPACE ();
+
+  expression (&exp);
+
+  if (exp.X_op != O_constant
+      || exp.X_add_number < 0 || exp.X_add_number > 15)
+    {
+      as_bad (_("bad personality routine number"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  unwind.personality_index = exp.X_add_number;
+
+  demand_empty_rest_of_line ();
+}
+
+
+/* Parse a personality directive.  */
+
+static void
+s_arm_unwind_personality (int ignored ATTRIBUTE_UNUSED)
+{
+  char *name, *p, c;
+
+  if (unwind.personality_routine || unwind.personality_index != -1)
+    as_bad (_("duplicate .personality directive"));
+
+  SKIP_WHITESPACE ();
+  name = input_line_pointer;
+  c = get_symbol_end ();
+  p = input_line_pointer;
+  unwind.personality_routine = symbol_find_or_make (name);
+  *p = c;
+  SKIP_WHITESPACE ();
+  demand_empty_rest_of_line ();
+}
+
+
+/* Parse a directive saving core registers.  */
+
+static void
+s_arm_unwind_save_core (void)
+{
+  valueT op;
+  long range;
+  int n;
+
+  SKIP_WHITESPACE ();
+  range = reg_list (&input_line_pointer);
+  if (range == FAIL)
+    {
+      as_bad (_("expected register list"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  demand_empty_rest_of_line ();
+
+  /* Turn .unwind_movsp ip followed by .unwind_save {..., ip, ...}
+     into .unwind_save {..., sp...}.  We aren't bothered about the value of
+     ip because it is clobbered by calls.  */
+  if (unwind.sp_restored && unwind.fp_reg == 12
+      && (range & 0x3000) == 0x1000)
+    {
+      unwind.opcode_count--;
+      unwind.sp_restored = 0;
+      range = (range | 0x2000) & ~0x1000;
+      unwind.pending_offset = 0;
+    }
+
+  /* See if we can use the short opcodes.  These pop a block of upto 8
+     registers starting with r4, plus maybe r14.  */
+  for (n = 0; n < 8; n++)
+    {
+      /* Break at the first non-saved register.  */
+      if ((range & (1 << (n + 4))) == 0)
+       break;
+    }
+  /* See if there are any other bits set.  */
+  if (n == 0 || (range & (0xfff0 << n) & 0xbff0) != 0)
+    {
+      /* Use the long form.  */
+      op = 0x8000 | ((range >> 4) & 0xfff);
+      add_unwind_opcode (op, 2);
+    }
+  else
+    {
+      /* Use the short form.  */
+      if (range & 0x4000)
+       op = 0xa8; /* Pop r14.  */
+      else
+       op = 0xa0; /* Do not pop r14.  */
+      op |= (n - 1);
+      add_unwind_opcode (op, 1);
+    }
+
+  /* Pop r0-r3.  */
+  if (range & 0xf)
+    {
+      op = 0xb100 | (range & 0xf);
+      add_unwind_opcode (op, 2);
+    }
+
+  /* Record the number of bytes pushed.  */
+  for (n = 0; n < 16; n++)
+    {
+      if (range & (1 << n))
+       unwind.frame_size += 4;
+    }
+}
+
+
+/* Parse a directive saving FPA registers.  */
+
+static void
+s_arm_unwind_save_fpa (int reg)
+{
+  expressionS exp;
+  int num_regs;
+  valueT op;
+
+  /* Get Number of registers to transfer.  */
+  if (skip_past_comma (&input_line_pointer) != FAIL)
+    expression (&exp);
+  else
+    exp.X_op = O_illegal;
+
+  if (exp.X_op != O_constant)
+    {
+      as_bad (_("expected , <constant>"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  num_regs = exp.X_add_number;
+
+  if (num_regs < 1 || num_regs > 4)
+    {
+      as_bad (_("number of registers must be in the range [1:4]"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  demand_empty_rest_of_line ();
+
+  if (reg == 4)
+    {
+      /* Short form.  */
+      op = 0xb4 | (num_regs - 1);
+      add_unwind_opcode (op, 1);
+    }
+  else
+    {
+      /* Long form.  */
+      op = 0xc800 | (reg << 4) | (num_regs - 1);
+      add_unwind_opcode (op, 2);
+    }
+  unwind.frame_size += num_regs * 12;
+}
+
+
+/* Parse a directive saving VFP registers.  */
+
+static void
+s_arm_unwind_save_vfp (void)
+{
+  int count;
+  int reg;
+  valueT op;
+
+  count = vfp_parse_reg_list (&input_line_pointer, &reg, 1);
+  if (count == FAIL)
+    {
+      as_bad (_("expected register list"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  demand_empty_rest_of_line ();
+
+  if (reg == 8)
+    {
+      /* Short form.  */
+      op = 0xb8 | (count - 1);
+      add_unwind_opcode (op, 1);
+    }
+  else
+    {
+      /* Long form.  */
+      op = 0xb300 | (reg << 4) | (count - 1);
+      add_unwind_opcode (op, 2);
+    }
+  unwind.frame_size += count * 8 + 4;
+}
+
+
+/* Parse a directive saving iWMMXt registers.  */
+
+static void
+s_arm_unwind_save_wmmx (void)
+{
+  int reg;
+  int hi_reg;
+  int i;
+  unsigned wcg_mask;
+  unsigned wr_mask;
+  valueT op;
+
+  if (*input_line_pointer == '{')
+    input_line_pointer++;
+
+  wcg_mask = 0;
+  wr_mask = 0;
+  do
+    {
+      reg = arm_reg_parse (&input_line_pointer,
+                          all_reg_maps[REG_TYPE_IWMMXT].htab);
+
+      if (wr_register (reg))
+       {
+         i = reg & ~WR_PREFIX;
+         if (wr_mask >> i)
+           as_tsktsk (_("register list not in ascending order"));
+         wr_mask |= 1 << i;
+       }
+      else if (wcg_register (reg))
+       {
+         i = (reg & ~WC_PREFIX) - 8;
+         if (wcg_mask >> i)
+           as_tsktsk (_("register list not in ascending order"));
+         wcg_mask |= 1 << i;
+       }
+      else
+       {
+         as_bad (_("expected wr or wcgr"));
+         goto error;
+       }
+
+      SKIP_WHITESPACE ();
+      if (*input_line_pointer == '-')
+       {
+         hi_reg = arm_reg_parse (&input_line_pointer,
+                                 all_reg_maps[REG_TYPE_IWMMXT].htab);
+         if (wr_register (reg) && wr_register (hi_reg))
+           {
+             for (; reg < hi_reg; reg++)
+               wr_mask |= 1 << (reg & ~WR_PREFIX);
+           }
+         else if (wcg_register (reg) && wcg_register (hi_reg))
+           {
+             for (; reg < hi_reg; reg++)
+               wcg_mask |= 1 << ((reg & ~WC_PREFIX) - 8);
+           }
+         else
+           {
+             as_bad (_("bad register range"));
+             goto error;
+           }
+       }
+    }
+  while (skip_past_comma (&input_line_pointer) != FAIL);
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer == '}')
+    input_line_pointer++;
+
+  demand_empty_rest_of_line ();
+
+  if (wr_mask && wcg_mask)
+    {
+      as_bad (_("inconsistent register types"));
+      goto error;
+    }
+
+  /* Generate any deferred opcodes becuuse we're going to be looking at
+     the list.  */
+  flush_pending_unwind ();
+
+  if (wcg_mask)
+    {
+      for (i = 0; i < 16; i++)
+       {
+         if (wcg_mask & (1 << i))
+           unwind.frame_size += 4;
+       }
+      op = 0xc700 | wcg_mask;
+      add_unwind_opcode (op, 2);
+    }
+  else
+    {
+      for (i = 0; i < 16; i++)
+       {
+         if (wr_mask & (1 << i))
+           unwind.frame_size += 8;
+       }
+      /* Attempt to combine with a previous opcode.  We do this because gcc
+        likes to output separate unwind directives for a single block of
+        registers.  */
+      if (unwind.opcode_count > 0)
+       {
+         i = unwind.opcodes[unwind.opcode_count - 1];
+         if ((i & 0xf8) == 0xc0)
+           {
+             i &= 7;
+             /* Only merge if the blocks are contiguous.  */
+             if (i < 6)
+               {
+                 if ((wr_mask & 0xfe00) == (1 << 9))
+                   {
+                     wr_mask |= ((1 << (i + 11)) - 1) & 0xfc00;
+                     unwind.opcode_count--;
+                   }
+               }
+             else if (i == 6 && unwind.opcode_count >= 2)
+               {
+                 i = unwind.opcodes[unwind.opcode_count - 2];
+                 reg = i >> 4;
+                 i &= 0xf;
+
+                 op = 0xffff << (reg - 1);
+                 if (reg > 0
+                     || ((wr_mask & op) == (1u << (reg - 1))))
+                   {
+                     op = (1 << (reg + i + 1)) - 1;
+                     op &= ~((1 << reg) - 1);
+                     wr_mask |= op;
+                     unwind.opcode_count -= 2;
+                   }
+               }
+           }
+       }
+
+      hi_reg = 15;
+      /* We want to generate opcodes in the order the registers have been
+        saved, ie. descending order.  */
+      for (reg = 15; reg >= -1; reg--)
+       {
+         /* Save registers in blocks.  */
+         if (reg < 0
+             || !(wr_mask & (1 << reg)))
+           {
+             /* We found an unsaved reg.  Generate opcodes to save the
+                preceeding block.  */
+             if (reg != hi_reg)
+               {
+                 if (reg == 9)
+                   {
+                     /* Short form.  */
+                     op = 0xc0 | (hi_reg - 10);
+                     add_unwind_opcode (op, 1);
+                   }
+                 else
+                   {
+                     /* Long form.  */
+                     op = 0xc600 | ((reg + 1) << 4) | ((hi_reg - reg) - 1);
+                     add_unwind_opcode (op, 2);
+                   }
+               }
+             hi_reg = reg - 1;
+           }
+       }
+    }
+  return;
+error:
+  ignore_rest_of_line ();
+}
+
+
+/* Parse an unwind_save directive.  */
+
+static void
+s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED)
+{
+  char *saved_ptr;
+  int reg;
+
+  /* Figure out what sort of save we have.  */
+  SKIP_WHITESPACE ();
+  saved_ptr = input_line_pointer;
+
+  reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_FN].htab);
+  if (reg != FAIL)
+    {
+      s_arm_unwind_save_fpa (reg);
+      return;
+    }
+
+  if (*input_line_pointer == '{')
+    input_line_pointer++;
+
+  SKIP_WHITESPACE ();
+
+  reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_RN].htab);
+  if (reg != FAIL)
+    {
+      input_line_pointer = saved_ptr;
+      s_arm_unwind_save_core ();
+      return;
+    }
+
+  reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_DN].htab);
+  if (reg != FAIL)
+    {
+      input_line_pointer = saved_ptr;
+      s_arm_unwind_save_vfp ();
+      return;
+    }
+
+  reg = arm_reg_parse (&input_line_pointer,
+                      all_reg_maps[REG_TYPE_IWMMXT].htab);
+  if (reg != FAIL)
+    {
+      input_line_pointer = saved_ptr;
+      s_arm_unwind_save_wmmx ();
+      return;
+    }
+
+  /* TODO: Maverick registers.  */
+  as_bad (_("unrecognised register"));
+}
+
+
+/* Parse an unwind_movsp directive.  */
+
+static void
+s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED)
+{
+  int reg;
+  valueT op;
+
+  SKIP_WHITESPACE ();
+  reg = reg_required_here (&input_line_pointer, -1);
+  if (reg == FAIL)
+    {
+      as_bad (_("ARM register expected"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (reg == 13 || reg == 15)
+    {
+      as_bad (_("r%d not permitted in .unwind_movsp directive"), reg);
+      ignore_rest_of_line ();
+      return;
+    }
+
+  if (unwind.fp_reg != 13)
+    as_bad (_("unexpected .unwind_movsp directive"));
+
+  /* Generate opcode to restore the value.  */
+  op = 0x90 | reg;
+  add_unwind_opcode (op, 1);
+
+  /* Record the information for later.  */
+  unwind.fp_reg = reg;
+  unwind.fp_offset = unwind.frame_size;
+  unwind.sp_restored = 1;
+  demand_empty_rest_of_line ();
+}
+
+
+/* Parse #<number>.  */
+
+static int
+require_hashconst (int * val)
+{
+  expressionS exp;
+
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer == '#')
+    {
+      input_line_pointer++;
+      expression (&exp);
+    }
+  else
+    exp.X_op = O_illegal;
+
+  if (exp.X_op != O_constant)
+    {
+      as_bad (_("expected #constant"));
+      ignore_rest_of_line ();
+      return FAIL;
+    }
+  *val = exp.X_add_number;
+  return SUCCESS;
+}
+
+/* Parse an unwind_pad directive.  */
+
+static void
+s_arm_unwind_pad (int ignored ATTRIBUTE_UNUSED)
+{
+  int offset;
+
+  if (require_hashconst (&offset) == FAIL)
+    return;
+
+  if (offset & 3)
+    {
+      as_bad (_("stack increment must be multiple of 4"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  /* Don't generate any opcodes, just record the details for later.  */
+  unwind.frame_size += offset;
+  unwind.pending_offset += offset;
+
+  demand_empty_rest_of_line ();
+}
+
+/* Parse an unwind_setfp directive.  */
+
+static void
+s_arm_unwind_setfp (int ignored ATTRIBUTE_UNUSED)
+{
+  int sp_reg;
+  int fp_reg;
+  int offset;
+
+  fp_reg = reg_required_here (&input_line_pointer, -1);
+  if (skip_past_comma (&input_line_pointer) == FAIL)
+    sp_reg = FAIL;
+  else
+    sp_reg = reg_required_here (&input_line_pointer, -1);
+
+  if (fp_reg == FAIL || sp_reg == FAIL)
+    {
+      as_bad (_("expected <reg>, <reg>"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  /* Optonal constant.  */
+  if (skip_past_comma (&input_line_pointer) != FAIL)
+    {
+      if (require_hashconst (&offset) == FAIL)
+       return;
+    }
+  else
+    offset = 0;
+
+  demand_empty_rest_of_line ();
+
+  if (sp_reg != 13 && sp_reg != unwind.fp_reg)
+    {
+      as_bad (_("register must be either sp or set by a previous"
+               "unwind_movsp directive"));
+      return;
+    }
+
+  /* Don't generate any opcodes, just record the information for later.  */
+  unwind.fp_reg = fp_reg;
+  unwind.fp_used = 1;
+  if (sp_reg == 13)
+    unwind.fp_offset = unwind.frame_size - offset;
+  else
+    unwind.fp_offset -= offset;
+}
+
+/* Parse an unwind_raw directive.  */
+
+static void
+s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+  /* This is an arbitary limit.  */
+  unsigned char op[16];
+  int count;
+
+  SKIP_WHITESPACE ();
+  expression (&exp);
+  if (exp.X_op == O_constant
+      && skip_past_comma (&input_line_pointer) != FAIL)
+    {
+      unwind.frame_size += exp.X_add_number;
+      expression (&exp);
+    }
+  else
+    exp.X_op = O_illegal;
+
+  if (exp.X_op != O_constant)
+    {
+      as_bad (_("expected <offset>, <opcode>"));
+      ignore_rest_of_line ();
+      return;
+    }
+
+  count = 0;
+
+  /* Parse the opcode.  */
+  for (;;)
+    {
+      if (count >= 16)
+       {
+         as_bad (_("unwind opcode too long"));
+         ignore_rest_of_line ();
+       }
+      if (exp.X_op != O_constant || exp.X_add_number & ~0xff)
+       {
+         as_bad (_("invalid unwind opcode"));
+         ignore_rest_of_line ();
+         return;
+       }
+      op[count++] = exp.X_add_number;
+
+      /* Parse the next byte.  */
+      if (skip_past_comma (&input_line_pointer) == FAIL)
+       break;
+
+      expression (&exp);
+    }
+
+  /* Add the opcode bytes in reverse order.  */
+  while (count--)
+    add_unwind_opcode (op[count], 1);
+
+  demand_empty_rest_of_line ();
+}
+
+#endif /* OBJ_ELF */
+
+/* This is called from HANDLE_ALIGN in write.c.  Fill in the contents
+   of an rs_align_code fragment.  */
+
+void
+arm_handle_align (fragS * fragP)
+{
+  static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
+  static char const thumb_noop[2] = { 0xc0, 0x46 };
+  static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
+  static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
+
+  int bytes, fix, noop_size;
+  char * p;
+  const char * noop;
+
+  if (fragP->fr_type != rs_align_code)
+    return;
+
+  bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+  p = fragP->fr_literal + fragP->fr_fix;
+  fix = 0;
+
+  if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
+    bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+
+  if (fragP->tc_frag_data)
+    {
+      if (target_big_endian)
+       noop = thumb_bigend_noop;
+      else
+       noop = thumb_noop;
+      noop_size = sizeof (thumb_noop);
+    }
+  else
+    {
+      if (target_big_endian)
+       noop = arm_bigend_noop;
+      else
+       noop = arm_noop;
+      noop_size = sizeof (arm_noop);
+    }
+
+  if (bytes & (noop_size - 1))
+    {
+      fix = bytes & (noop_size - 1);
+      memset (p, 0, fix);
+      p += fix;
+      bytes -= fix;
+    }
+
+  while (bytes >= noop_size)
+    {
+      memcpy (p, noop, noop_size);
+      p += noop_size;
+      bytes -= noop_size;
+      fix += noop_size;
+    }
+
+  fragP->fr_fix += fix;
+  fragP->fr_var = noop_size;
+}
+
+/* Called from md_do_align.  Used to create an alignment
+   frag in a code section.  */
+
+void
+arm_frag_align_code (int n, int max)
+{
+  char * p;
+
+  /* We assume that there will never be a requirement
+     to support alignments greater than 32 bytes.  */
+  if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+    as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+
+  p = frag_var (rs_align_code,
+               MAX_MEM_FOR_RS_ALIGN_CODE,
+               1,
+               (relax_substateT) max,
+               (symbolS *) NULL,
+               (offsetT) n,
+               (char *) NULL);
+  *p = 0;
+}
+
+/* Perform target specific initialisation of a frag.  */
+
+void
+arm_init_frag (fragS * fragP)
+{
+  /* Record whether this frag is in an ARM or a THUMB area.  */
+  fragP->tc_frag_data = thumb_mode;
+}
+
+#ifdef OBJ_ELF
+
+/* Convert REGNAME to a DWARF-2 register number.  */
+
+int
+tc_arm_regname_to_dw2regnum (const char *regname)
+{
+  unsigned int i;
+
+  for (i = 0; rn_table[i].name; i++)
+    if (streq (regname, rn_table[i].name))
+      return rn_table[i].number;
+
+  return -1;
+}
+
+/* Initialize the DWARF-2 unwind information for this procedure.  */
+
+void
+tc_arm_frame_initial_instructions (void)
+{
+  cfi_add_CFA_def_cfa (REG_SP, 0);
+}
+#endif
 
 /* This table describes all the machine specific pseudo-ops the assembler
    has to support.  The fields are:
@@ -13801,6 +15241,17 @@ const pseudo_typeS md_pseudo_table[] =
   { "word",        s_arm_elf_cons, 4 },
   { "long",        s_arm_elf_cons, 4 },
   { "rel31",       s_arm_rel31,   0 },
+  { "fnstart",         s_arm_unwind_fnstart,   0 },
+  { "fnend",           s_arm_unwind_fnend,     0 },
+  { "cantunwind",      s_arm_unwind_cantunwind, 0 },
+  { "personality",     s_arm_unwind_personality, 0 },
+  { "personalityindex",        s_arm_unwind_personalityindex, 0 },
+  { "handlerdata",     s_arm_unwind_handlerdata, 0 },
+  { "save",            s_arm_unwind_save,      0 },
+  { "movsp",           s_arm_unwind_movsp,     0 },
+  { "pad",             s_arm_unwind_pad,       0 },
+  { "setfp",           s_arm_unwind_setfp,     0 },
+  { "unwind_raw",      s_arm_unwind_raw,       0 },
 #else
   { "word",        cons, 4},
 #endif
This page took 0.055781 seconds and 4 git commands to generate.