2001-06-18 Philip Blundell <philb@gnu.org>
[external/binutils.git] / gas / config / tc-arm.c
index 4bab476..99aff8e 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-arm.c -- Assemble for the ARM
-   Copyright (C) 1994, 95, 96, 97, 98, 1999, 2000
+   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
    Free Software Foundation, Inc.
    Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
        Modified by David Taylor (dtaylor@armltd.co.uk)
@@ -344,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}
 };
 
@@ -1279,12 +1279,23 @@ add_to_lit_pool ()
              == inst.reloc.exp.X_add_number)
          && literals[lit_count].exp.X_unsigned == inst.reloc.exp.X_unsigned)
        break;
+
+      if (literals[lit_count].exp.X_op == inst.reloc.exp.X_op
+          && inst.reloc.exp.X_op == O_symbol
+          && (literals[lit_count].exp.X_add_number
+             == inst.reloc.exp.X_add_number)
+          && (literals[lit_count].exp.X_add_symbol
+             == inst.reloc.exp.X_add_symbol)
+          && (literals[lit_count].exp.X_op_symbol
+             == inst.reloc.exp.X_op_symbol))
+        break;
+
       lit_count++;
     }
 
   if (lit_count == next_literal_pool_place) /* New entry.  */
     {
-      if (next_literal_pool_place > MAX_LITERAL_POOL_SIZE)
+      if (next_literal_pool_place >= MAX_LITERAL_POOL_SIZE)
        {
          inst.error = _("Literal Pool Overflow");
          return FAIL;
@@ -2278,12 +2289,17 @@ do_msr (str, flags)
       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
 
   flags |= INST_IMMEDIATE;
 
@@ -2493,27 +2509,27 @@ static int
 accum0_required_here (str)
      char ** str;
 {
-  static char buff [128];      /* Note the address is taken.  Hence, static. */
+  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;
@@ -2521,9 +2537,9 @@ accum0_required_here (str)
 
 /* 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 
+   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
@@ -2535,11 +2551,11 @@ ld_mode_required_here (string)
   int    pre_inc = 0;
 
   skip_whitespace (str);
-    
+
   if (* str == '[')
     {
       str++;
-      
+
       skip_whitespace (str);
 
       if ((rn = reg_required_here (& str, 16)) == FAIL)
@@ -2550,7 +2566,7 @@ ld_mode_required_here (string)
       if (* str == ']')
        {
          str ++;
-         
+
          if (skip_past_comma (& str) == SUCCESS)
            {
              /* [Rn],... (post inc) */
@@ -2580,7 +2596,7 @@ ld_mode_required_here (string)
            }
 
          pre_inc = 1;
-         
+
          if (ldst_extend (& str, 1) == FAIL)
            return FAIL;
 
@@ -2615,14 +2631,14 @@ ld_mode_required_here (string)
       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;
 }
 
@@ -2649,7 +2665,7 @@ do_smla (str, flags)
       || (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) 
+  else if (rd == REG_PC || rm == REG_PC || rs == REG_PC || rn == REG_PC)
     inst.error = BAD_PC;
 
   else if (flags)
@@ -2693,7 +2709,7 @@ do_smlal (str, flags)
 
   if (rdlo == rdhi)
     as_tsktsk (_("rdhi and rdlo must be different"));
-  
+
   if (flags)
     inst.error = BAD_FLAGS;
   else
@@ -2762,10 +2778,10 @@ do_qadd (str, flags)
 
 /* ARM V5E (el Segundo)
    MCRRcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
-   MRRCcc <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.  
+   respectively, when coproc == 0, opcode == 0, and CRm == 0.
 
    Result unpredicatable if Rd or Rn is R15.  */
 
@@ -2811,7 +2827,7 @@ do_co_reg2c (str, flags)
 
   /* Unpredictable result if rd or rn is R15.  */
   if (rd == REG_PC || rn == REG_PC)
-    as_tsktsk 
+    as_tsktsk
       (_("Warning: Instruction unpredictable when using r15"));
 
   if (skip_past_comma (& str) == FAIL
@@ -2828,7 +2844,6 @@ do_co_reg2c (str, flags)
   end_of_line (str);
 }
 
-
 /* ARM V5 count-leading-zeroes instruction (argument parse)
      CLZ{<cond>} <Rd>, <Rm>
      Condition defaults to COND_ALWAYS.
@@ -2961,7 +2976,7 @@ do_cdp2 (str, flags)
 
   if (flags)
     inst.error = BAD_FLAGS;
-  
+
   end_of_line (str);
 }
 
@@ -3043,7 +3058,7 @@ do_t_bkpt (str)
   expressionS expr;
   unsigned long number;
 
-  skip_whitespace (str);     
+  skip_whitespace (str);
 
   /* Allow optional leading '#'.  */
   if (is_immediate_prefix (*str))
@@ -3055,9 +3070,9 @@ do_t_bkpt (str)
       inst.error = _("bad or missing expression");
       return;
     }
-  
+
   number = expr.X_add_number;
-  
+
   /* Check it fits an 8 bit unsigned.  */
   if (number != (number & 0xff))
     {
@@ -3082,20 +3097,20 @@ do_branch25 (str, flags)
 {
   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)
@@ -3111,14 +3126,14 @@ do_branch25 (str, flags)
        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);
 }
 
@@ -3127,7 +3142,7 @@ do_branch25 (str, flags)
      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.  
+       into inst.instruction.
    Also, the <target_addr> can be 25 bits, hence has its own reloc.  */
 
 static void
@@ -3143,13 +3158,13 @@ do_blx (str, 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.
@@ -3166,9 +3181,9 @@ do_blx (str, flags)
          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);
@@ -3193,11 +3208,11 @@ do_t_blx (str)
   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 
+     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.  */
@@ -3211,11 +3226,11 @@ do_t_blx (str)
 
       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);
 }
 
@@ -3235,31 +3250,31 @@ do_bkpt (str, flags)
   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;
 
@@ -3270,7 +3285,7 @@ do_bkpt (str, flags)
 }
 
 /* Xscale multiply-accumulate (argument parse)
-     MIAcc   acc0,Rm,Rs        
+     MIAcc   acc0,Rm,Rs
      MIAPHcc acc0,Rm,Rs
      MIAxycc acc0,Rm,Rs.  */
 
@@ -3284,22 +3299,22 @@ do_mia (str, flags)
 
   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. */
+
+  /* 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);
 }
@@ -3317,22 +3332,22 @@ do_mar (str, flags)
 
   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);
 }
@@ -3378,10 +3393,10 @@ do_mra (str, flags)
     end_of_line (str);
 }
 
-/* Xscale: Preload-Cache 
+/* Xscale: Preload-Cache
 
     PLD <addr_mode>
-    
+
   Syntactically, like LDR with B=1, W=0, L=1.  */
 
 static void
@@ -3412,7 +3427,7 @@ do_pld (str, flags)
     return;
 
   skip_whitespace (str);
-  
+
   if (* str == ']')
     {
       /* [Rn], ... ?  */
@@ -3439,7 +3454,7 @@ do_pld (str, flags)
          inst.error = _("pre-indexed expression expected");
          return;
        }
-      
+
       if (ldst_extend (& str, 0) == FAIL)
        return;
 
@@ -3453,13 +3468,13 @@ do_pld (str, flags)
 
       ++ str;
       skip_whitespace (str);
-      
+
       if (* str == '!') /* [Rn]! */
        {
          inst.error = _("writeback used in preload instruction");
          ++ str;
        }
-      
+
       inst.instruction |= PRE_INDEX;
     }
 
@@ -3493,7 +3508,7 @@ do_ldrd (str, flags)
 
       return;
     }
-  
+
   if ((cpu_variant & ARM_EXT_XSCALE) != ARM_EXT_XSCALE)
     {
       static char buff[128];
@@ -3502,15 +3517,15 @@ do_ldrd (str, flags)
       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;
@@ -3524,11 +3539,11 @@ do_ldrd (str, flags)
         inst.error = BAD_ARGS;
       return;
     }
-  
+
   /* inst.instruction has now been zapped with Rd and the addressing mode.  */
   if (rd & 1)          /* Unpredictable result if Rd is odd.  */
     {
-      inst.error = _("Destination register must be even");     
+      inst.error = _("Destination register must be even");
       return;
     }
 
@@ -3543,7 +3558,7 @@ do_ldrd (str, flags)
       ((inst.instruction & WRITE_BACK)
        || (!(inst.instruction & PRE_INDEX))))
     as_warn (_("pre/post-indexing used when modified address register is destination"));
-  
+
   end_of_line (str);
 }
 
@@ -4324,9 +4339,16 @@ do_ldst (str, flags)
              if (ldst_extend (&str, halfword) == FAIL)
                return;
              if (conflict_reg)
-               as_warn (_("%s register same as write-back base"),
-                        ((inst.instruction & LOAD_BIT)
-                         ? _("destination") : _("source")));
+               {
+                 if (flags & TRANS_BIT)
+                   as_warn (_("Rn and Rd must be different in %s"),
+                            ((inst.instruction & LOAD_BIT)
+                             ? "LDRT" : "STRT"));
+                 else
+                   as_warn (_("%s register same as write-back base"),
+                            ((inst.instruction & LOAD_BIT)
+                             ? _("destination") : _("source")));
+               }
            }
          else
            {
@@ -4347,8 +4369,15 @@ do_ldst (str, flags)
                }
 
              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
@@ -6001,7 +6030,7 @@ do_t_arit (str)
 
       if (Rs != Rd)
        {
-         inst.error = _("dest and source1 one must be the same register");
+         inst.error = _("dest and source1 must be the same register");
          return;
        }
       Rs = Rn;
@@ -6464,7 +6493,7 @@ md_begin ()
     if (atpcs)
       {
        asection * sec;
-       
+
        sec = bfd_make_section (stdoutput, ".arm.atpcs");
 
        if (sec != NULL)
@@ -6500,29 +6529,26 @@ md_begin ()
     }
 
   /* Catch special cases.  */
-  if (cpu_variant != (FPU_DEFAULT | CPU_DEFAULT))
+  if (cpu_variant & ARM_EXT_XSCALE)
+    mach = bfd_mach_arm_XScale;
+  else if (cpu_variant & ARM_EXT_V5E)
+    mach = bfd_mach_arm_5TE;
+  else if (cpu_variant & ARM_EXT_V5)
+    {
+      if (cpu_variant & ARM_EXT_THUMB)
+       mach = bfd_mach_arm_5T;
+      else
+       mach = bfd_mach_arm_5;
+    }
+  else if (cpu_variant & ARM_EXT_HALFWORD)
     {
-      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;
+      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);
 }
@@ -7136,6 +7162,15 @@ md_apply_fix3 (fixP, val, seg)
 
        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);
       }
@@ -8405,7 +8440,38 @@ arm_frob_label (sym)
   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
@@ -8538,6 +8604,22 @@ 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
@@ -8714,3 +8796,102 @@ s_arm_elf_cons (nbytes)
 }
 
 #endif /* OBJ_ELF */
+
+/* This is called from HANDLE_ALIGN in write.c.  Fill in the contents
+   of an rs_align_code fragment.  */
+
+void
+arm_handle_align (fragP)
+     fragS *fragP;
+{
+  static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
+  static char const thumb_noop[2] = { 0xc0, 0x46 };
+  static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
+  static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
+
+  int bytes, fix, noop_size;
+  char * p;
+  const char * noop;
+  
+  if (fragP->fr_type != rs_align_code)
+    return;
+
+  bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+  p = fragP->fr_literal + fragP->fr_fix;
+  fix = 0;
+  
+  if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
+    bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+  
+  if (fragP->tc_frag_data)
+    {
+      if (target_big_endian)
+       noop = thumb_bigend_noop;
+      else
+       noop = thumb_noop;
+      noop_size = sizeof (thumb_noop);
+    }
+  else
+    {
+      if (target_big_endian)
+       noop = arm_bigend_noop;
+      else
+       noop = arm_noop;
+      noop_size = sizeof (arm_noop);
+    }
+  
+  if (bytes & (noop_size - 1))
+    {
+      fix = bytes & (noop_size - 1);
+      memset (p, 0, fix);
+      p += fix;
+      bytes -= fix;
+    }
+
+  while (bytes >= noop_size)
+    {
+      memcpy (p, noop, noop_size);
+      p += noop_size;
+      bytes -= noop_size;
+      fix += noop_size;
+    }
+  
+  fragP->fr_fix += fix;
+  fragP->fr_var = noop_size;
+}
+
+/* Called from md_do_align.  Used to create an alignment
+   frag in a code section.  */
+
+void
+arm_frag_align_code (n, max)
+     int n;
+     int max;
+{
+  char * p;
+
+  /* We assume that there will never be a requirment
+     to support alignments greater than 32 bytes.  */
+  if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+    as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
+  
+  p = frag_var (rs_align_code,
+               MAX_MEM_FOR_RS_ALIGN_CODE,
+               1,
+               (relax_substateT) max,
+               (symbolS *) NULL,
+               (offsetT) n,
+               (char *) NULL);
+  *p = 0;
+
+}
+
+/* Perform target specific initialisation of a frag.  */
+
+void
+arm_init_frag (fragP)
+     fragS *fragP;
+{
+  /* Record whether this frag is in an ARM or a THUMB area.  */
+  fragP->tc_frag_data = thumb_mode;
+}