Add Intel AVX-512 support
[deliverable/binutils-gdb.git] / gas / config / tc-ppc.c
index df298dbb2e8c877d0c61df068464426332da19e3..5c413d3dab98acffc6aa545b3353d76318a1812a 100644 (file)
@@ -1,6 +1,7 @@
 /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
    Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+   2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
+   Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
    This file is part of GAS, the GNU Assembler.
 #include "coff/pe.h"
 #endif
 
+#ifdef OBJ_XCOFF
+#include "coff/xcoff.h"
+#include "libxcoff.h"
+#endif
+
 /* This is the assembler for the PowerPC or POWER (RS/6000) chips.  */
 
 /* Tell the main code what the endianness is.  */
@@ -104,6 +110,7 @@ static void ppc_ec (int);
 static void ppc_ef (int);
 static void ppc_es (int);
 static void ppc_csect (int);
+static void ppc_dwsect (int);
 static void ppc_change_csect (symbolS *, offsetT);
 static void ppc_function (int);
 static void ppc_extern (int);
@@ -180,9 +187,17 @@ const char ppc_symbol_chars[] = "%[";
 /* The dwarf2 data alignment, adjusted for 32 or 64 bit.  */
 int ppc_cie_data_alignment;
 
+/* The dwarf2 minimum instruction length.  */
+int ppc_dwarf2_line_min_insn_length;
+
+/* More than this number of nops in an alignment op gets a branch
+   instead.  */
+unsigned long nop_limit = 4;
+
 /* The type of processor we are assembling for.  This is one or more
    of the PPC_OPCODE flags defined in opcode/ppc.h.  */
 ppc_cpu_t ppc_cpu = 0;
+ppc_cpu_t sticky = 0;
 
 /* Flags set on encountering toc relocs.  */
 enum {
@@ -210,6 +225,7 @@ const pseudo_typeS md_pseudo_table[] =
   { "bi",      ppc_biei,       0 },
   { "bs",      ppc_bs,         0 },
   { "csect",   ppc_csect,      0 },
+  { "dwsect",  ppc_dwsect,     0 },
   { "data",    ppc_section,    'd' },
   { "eb",      ppc_eb,         0 },
   { "ec",      ppc_ec,         0 },
@@ -978,6 +994,28 @@ static symbolS *ppc_current_block;
    cause BFD to set the section number of a symbol to N_DEBUG.  */
 static asection *ppc_coff_debug_section;
 
+/* Structure to set the length field of the dwarf sections.  */
+struct dw_subsection {
+  /* Subsections are simply linked.  */
+  struct dw_subsection *link;
+
+  /* The subsection number.  */
+  subsegT subseg;
+
+  /* Expression to compute the length of the section.  */
+  expressionS end_exp;
+};
+
+static struct dw_section {
+  /* Corresponding section.  */
+  segT sect;
+
+  /* Simply linked list of subsections with a label.  */
+  struct dw_subsection *list_subseg;
+
+  /* The anonymous subsection.  */
+  struct dw_subsection *anon_subseg;
+} dw_sections[XCOFF_DWSECT_NBR_NAMES];
 #endif /* OBJ_XCOFF */
 
 #ifdef TE_PE
@@ -1004,6 +1042,7 @@ symbolS *GOT_symbol;              /* Pre-defined "_GLOBAL_OFFSET_TABLE" */
 #define PPC_APUINFO_SPE                0x100
 #define PPC_APUINFO_EFS                0x101
 #define PPC_APUINFO_BRLOCK     0x102
+#define PPC_APUINFO_VLE                0x104
 
 /*
  * We keep a list of APUinfo
@@ -1018,7 +1057,9 @@ const char *const md_shortopts = "b:l:usm:K:VQ:";
 #else
 const char *const md_shortopts = "um:";
 #endif
+#define OPTION_NOPS (OPTION_MD_BASE + 0)
 const struct option md_longopts[] = {
+  {"nops", required_argument, NULL, OPTION_NOPS},
   {NULL, no_argument, NULL, 0}
 };
 const size_t md_longopts_size = sizeof (md_longopts);
@@ -1043,6 +1084,8 @@ md_parse_option (int c, char *arg)
        {
          target_big_endian = 0;
          set_target_endian = 1;
+         if (ppc_cpu & PPC_OPCODE_VLE)
+           as_bad (_("the use of -mvle requires big endian."));
        }
       else
        return 0;
@@ -1079,6 +1122,8 @@ md_parse_option (int c, char *arg)
        {
 #ifdef BFD64
          ppc_obj64 = 1;
+         if (ppc_cpu & PPC_OPCODE_VLE)
+           as_bad (_("the use of -mvle requires -a32."));
 #else
          as_fatal (_("%s unsupported"), "-a64");
 #endif
@@ -1090,8 +1135,18 @@ md_parse_option (int c, char *arg)
       break;
 
     case 'm':
-      if ((new_cpu = ppc_parse_cpu (ppc_cpu, arg)) != 0)
-       ppc_cpu = new_cpu;
+      new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, arg);
+      if (new_cpu != 0)
+       {
+         ppc_cpu = new_cpu;
+         if (strcmp (arg, "vle") == 0)
+           {
+             if (set_target_endian && target_big_endian == 0)
+               as_bad (_("the use of -mvle requires big endian."));
+             if (ppc_obj64)
+               as_bad (_("the use of -mvle requires -a32."));
+           }
+       }
 
       else if (strcmp (arg, "regnames") == 0)
        reg_names_p = TRUE;
@@ -1118,12 +1173,14 @@ md_parse_option (int c, char *arg)
       else if (strcmp (arg, "emb") == 0)
        ppc_flags |= EF_PPC_EMB;
 
-      /* -mlittle/-mbig set the endianess.  */
+      /* -mlittle/-mbig set the endianness.  */
       else if (strcmp (arg, "little") == 0
               || strcmp (arg, "little-endian") == 0)
        {
          target_big_endian = 0;
          set_target_endian = 1;
+         if (ppc_cpu & PPC_OPCODE_VLE)
+           as_bad (_("the use of -mvle requires big endian."));
        }
 
       else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0)
@@ -1172,6 +1229,15 @@ md_parse_option (int c, char *arg)
       break;
 #endif
 
+    case OPTION_NOPS:
+      {
+       char *end;
+       nop_limit = strtoul (optarg, &end, 0);
+       if (*end)
+         as_bad (_("--nops needs a numeric argument"));
+      }
+      break;
+
     default:
       return 0;
     }
@@ -1210,17 +1276,22 @@ PowerPC options:\n\
                         generate code for Power5 architecture\n\
 -mpower6, -mpwr6        generate code for Power6 architecture\n\
 -mpower7, -mpwr7        generate code for Power7 architecture\n\
+-mpower8, -mpwr8        generate code for Power8 architecture\n\
 -mcell                  generate code for Cell Broadband Engine architecture\n\
 -mcom                   generate code Power/PowerPC common instructions\n\
 -many                   generate code for any architecture (PWR/PWRX/PPC)\n"));
   fprintf (stream, _("\
 -maltivec               generate code for AltiVec\n\
 -mvsx                   generate code for Vector-Scalar (VSX) instructions\n\
+-mhtm                   generate code for Hardware Transactional Memory\n\
 -me300                  generate code for PowerPC e300 family\n\
 -me500, -me500x2        generate code for Motorola e500 core complex\n\
 -me500mc,               generate code for Freescale e500mc core complex\n\
 -me500mc64,             generate code for Freescale e500mc64 core complex\n\
+-me5500,                generate code for Freescale e5500 core complex\n\
+-me6500,                generate code for Freescale e6500 core complex\n\
 -mspe                   generate code for Motorola SPE instructions\n\
+-mvle                   generate code for Freescale VLE instructions\n\
 -mtitan                 generate code for AppliedMicro Titan core complex\n\
 -mregnames              Allow symbolic names for registers\n\
 -mno-regnames           Do not allow symbolic names for registers\n"));
@@ -1229,15 +1300,18 @@ PowerPC options:\n\
 -mrelocatable           support for GCC's -mrelocatble option\n\
 -mrelocatable-lib       support for GCC's -mrelocatble-lib option\n\
 -memb                   set PPC_EMB bit in ELF flags\n\
--mlittle, -mlittle-endian, -l, -le\n\
+-mlittle, -mlittle-endian, -le\n\
                         generate code for a little endian machine\n\
--mbig, -mbig-endian, -b, -be\n\
+-mbig, -mbig-endian, -be\n\
                         generate code for a big endian machine\n\
 -msolaris               generate code for Solaris\n\
 -mno-solaris            do not generate code for Solaris\n\
+-K PIC                  set EF_PPC_RELOCATABLE_LIB in ELF flags\n\
 -V                      print assembler version number\n\
 -Qy, -Qn                ignored\n"));
 #endif
+  fprintf (stream, _("\
+-nops=count             when aligning, more than COUNT nops uses a branch\n"));
 }
 \f
 /* Set ppc_cpu if it is not already set.  */
@@ -1248,7 +1322,7 @@ ppc_set_cpu (void)
   const char *default_os  = TARGET_OS;
   const char *default_cpu = TARGET_CPU;
 
-  if ((ppc_cpu & ~PPC_OPCODE_ANY) == 0)
+  if ((ppc_cpu & ~(ppc_cpu_t) PPC_OPCODE_ANY) == 0)
     {
       if (ppc_obj64)
        ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_64;
@@ -1262,7 +1336,7 @@ ppc_set_cpu (void)
       else if (strncmp (default_cpu, "powerpc", 7) == 0)
        ppc_cpu |= PPC_OPCODE_PPC;
       else
-       as_fatal (_("Unknown default cpu = %s, os = %s"),
+       as_fatal (_("unknown default cpu = %s, os = %s"),
                  default_cpu, default_os);
     }
 }
@@ -1278,9 +1352,11 @@ ppc_arch (void)
 
   if ((ppc_cpu & PPC_OPCODE_PPC) != 0)
     return bfd_arch_powerpc;
-  else if ((ppc_cpu & PPC_OPCODE_POWER) != 0)
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+    return bfd_arch_powerpc;
+  if ((ppc_cpu & PPC_OPCODE_POWER) != 0)
     return bfd_arch_rs6000;
-  else if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0)
+  if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0)
     {
       if (strcmp (default_cpu, "rs6000") == 0)
        return bfd_arch_rs6000;
@@ -1288,7 +1364,7 @@ ppc_arch (void)
        return bfd_arch_powerpc;
     }
 
-  as_fatal (_("Neither Power nor PowerPC opcodes were selected."));
+  as_fatal (_("neither Power nor PowerPC opcodes were selected."));
   return bfd_arch_unknown;
 }
 
@@ -1301,6 +1377,8 @@ ppc_mach (void)
     return bfd_mach_rs6k;
   else if (ppc_cpu & PPC_OPCODE_TITAN)
     return bfd_mach_ppc_titan;
+  else if (ppc_cpu & PPC_OPCODE_VLE)
+    return bfd_mach_ppc_vle;
   else
     return bfd_mach_ppc;
 }
@@ -1315,14 +1393,16 @@ ppc_target_format (void)
   return "xcoff-powermac";
 #else
 #  ifdef TE_AIX5
-    return (ppc_obj64 ? "aix5coff64-rs6000" : "aixcoff-rs6000");
+  return (ppc_obj64 ? "aix5coff64-rs6000" : "aixcoff-rs6000");
 #  else
-    return (ppc_obj64 ? "aixcoff64-rs6000" : "aixcoff-rs6000");
+  return (ppc_obj64 ? "aixcoff64-rs6000" : "aixcoff-rs6000");
 #  endif
 #endif
 #endif
 #ifdef OBJ_ELF
-# ifdef TE_VXWORKS
+# ifdef TE_FreeBSD
+  return (ppc_obj64 ? "elf64-powerpc-freebsd" : "elf32-powerpc-freebsd");
+# elif defined (TE_VXWORKS)
   return "elf32-powerpc-vxworks";
 # else
   return (target_big_endian
@@ -1332,8 +1412,56 @@ ppc_target_format (void)
 #endif
 }
 
+/* Validate one entry in powerpc_opcodes[] or vle_opcodes[].
+   Return TRUE if there's a problem, otherwise FALSE.  */
+
+static bfd_boolean
+insn_validate (const struct powerpc_opcode *op)
+{
+  const unsigned char *o;
+  unsigned long omask = op->mask;
+
+  /* The mask had better not trim off opcode bits.  */
+  if ((op->opcode & omask) != op->opcode)
+    {
+      as_bad (_("mask trims opcode bits for %s"), op->name);
+      return TRUE;
+    }
+
+  /* The operands must not overlap the opcode or each other.  */
+  for (o = op->operands; *o; ++o)
+    {
+      if (*o >= num_powerpc_operands)
+        {
+         as_bad (_("operand index error for %s"), op->name);
+         return TRUE;
+        }
+      else
+        {
+         const struct powerpc_operand *operand = &powerpc_operands[*o];
+         if (operand->shift != PPC_OPSHIFT_INV)
+           {
+             unsigned long mask;
+
+             if (operand->shift >= 0)
+               mask = operand->bitm << operand->shift;
+             else
+               mask = operand->bitm >> -operand->shift;
+             if (omask & mask)
+               {
+                 as_bad (_("operand %d overlap in %s"),
+                         (int) (o - op->operands), op->name);
+                 return TRUE;
+               }
+             omask |= mask;
+           }
+        }
+    }
+  return FALSE;
+}
+
 /* Insert opcodes and macros into hash tables.  Called at startup and
-   for .cpu pseudo.  */
+   for .machine pseudo.  */
 
 static void
 ppc_setup_opcodes (void)
@@ -1356,6 +1484,10 @@ ppc_setup_opcodes (void)
     {
       unsigned int i;
 
+      /* An index into powerpc_operands is stored in struct fix
+        fx_pcrel_adjust which is 8 bits wide.  */
+      gas_assert (num_powerpc_operands < 256);
+
       /* Check operand masks.  Code here and in the disassembler assumes
         all the 1's in the mask are contiguous.  */
       for (i = 0; i < num_powerpc_operands; ++i)
@@ -1388,89 +1520,82 @@ ppc_setup_opcodes (void)
     {
       if (ENABLE_CHECKING)
        {
-         const unsigned char *o;
-         unsigned long omask = op->mask;
-
          if (op != powerpc_opcodes)
            {
+             int old_opcode = PPC_OP (op[-1].opcode);
+             int new_opcode = PPC_OP (op[0].opcode);
+
+#ifdef PRINT_OPCODE_TABLE
+             printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+                     op->name, (unsigned int) (op - powerpc_opcodes),
+                     (unsigned int) new_opcode, (unsigned int) op->opcode,
+                     (unsigned int) op->mask, (unsigned long long) op->flags);
+#endif
+
              /* The major opcodes had better be sorted.  Code in the
                 disassembler assumes the insns are sorted according to
                 major opcode.  */
-             if (PPC_OP (op[0].opcode) < PPC_OP (op[-1].opcode))
+             if (new_opcode < old_opcode)
                {
                  as_bad (_("major opcode is not sorted for %s"),
                          op->name);
                  bad_insn = TRUE;
                }
-
-             /* Warn if the table isn't more strictly ordered.
-                Unfortunately it doesn't seem possible to order the
-                table on much more than the major opcode, which makes
-                it difficult to implement a binary search in the
-                disassembler.  The problem is that we have multiple
-                ways to disassemble instructions, and we usually want
-                to choose a more specific form (with more bits set in
-                the opcode) than a more general form.  eg. all of the
-                following are equivalent:
-                bne label      # opcode = 0x40820000, mask = 0xff830003
-                bf  2,label    # opcode = 0x40800000, mask = 0xff800003
-                bc  4,2,label  # opcode = 0x40000000, mask = 0xfc000003
-
-                There are also cases where the table needs to be out
-                of order to disassemble the correct instruction for
-                processor variants.  */
-             else if (0)
-               {
-                 unsigned long t1 = op[0].opcode;
-                 unsigned long t2 = op[-1].opcode;
-
-                 if (((t1 ^ t2) & 0xfc0007ff) == 0
-                     && (t1 & 0xfc0006df) == 0x7c000286)
-                   {
-                     /* spr field is split.  */
-                     t1 = ((t1 & ~0x1ff800)
-                           | ((t1 & 0xf800) << 5) | ((t1 & 0x1f0000) >> 5));
-                     t2 = ((t2 & ~0x1ff800)
-                           | ((t2 & 0xf800) << 5) | ((t2 & 0x1f0000) >> 5));
-                   }
-                 if (t1 < t2)
-                   as_warn (_("%s (%08lx %08lx) after %s (%08lx %08lx)"),
-                            op[0].name, op[0].opcode, op[0].mask,
-                            op[-1].name, op[-1].opcode, op[-1].mask);
-               }
            }
+         bad_insn |= insn_validate (op);
+       }
+
+      if ((ppc_cpu & op->flags) != 0
+         && !(ppc_cpu & op->deprecated))
+       {
+         const char *retval;
 
-         /* The mask had better not trim off opcode bits.  */
-         if ((op->opcode & omask) != op->opcode)
+         retval = hash_insert (ppc_hash, op->name, (void *) op);
+         if (retval != NULL)
            {
-             as_bad (_("mask trims opcode bits for %s"),
+             as_bad (_("duplicate instruction %s"),
                      op->name);
              bad_insn = TRUE;
            }
+       }
+    }
 
-         /* The operands must not overlap the opcode or each other.  */
-         for (o = op->operands; *o; ++o)
-           if (*o >= num_powerpc_operands)
-             {
-               as_bad (_("operand index error for %s"),
-                       op->name);
-               bad_insn = TRUE;
-             }
-           else
-             {
-               const struct powerpc_operand *operand = &powerpc_operands[*o];
-               if (operand->shift >= 0)
-                 {
-                   unsigned long mask = operand->bitm << operand->shift;
-                   if (omask & mask)
-                     {
-                       as_bad (_("operand %d overlap in %s"),
-                               (int) (o - op->operands), op->name);
-                       bad_insn = TRUE;
-                     }
-                   omask |= mask;
-                 }
-             }
+  if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
+    for (op = powerpc_opcodes; op < op_end; op++)
+      hash_insert (ppc_hash, op->name, (void *) op);
+
+  op_end = vle_opcodes + vle_num_opcodes;
+  for (op = vle_opcodes; op < op_end; op++)
+    {
+      if (ENABLE_CHECKING)
+       {
+         if (op != vle_opcodes)
+           {
+             unsigned old_seg, new_seg;
+
+             old_seg = VLE_OP (op[-1].opcode, op[-1].mask);
+             old_seg = VLE_OP_TO_SEG (old_seg);
+             new_seg = VLE_OP (op[0].opcode, op[0].mask);
+             new_seg = VLE_OP_TO_SEG (new_seg);
+
+#ifdef PRINT_OPCODE_TABLE
+             printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+                     op->name, (unsigned int) (op - powerpc_opcodes),
+                     (unsigned int) new_seg, (unsigned int) op->opcode,
+                     (unsigned int) op->mask, (unsigned long long) op->flags);
+#endif
+             /* The major opcodes had better be sorted.  Code in the
+                disassembler assumes the insns are sorted according to
+                major opcode.  */
+             if (new_seg < old_seg)
+               {
+                 as_bad (_("major opcode is not sorted for %s"),
+                         op->name);
+                 bad_insn = TRUE;
+               }
+           }
+
+         bad_insn |= insn_validate (op);
        }
 
       if ((ppc_cpu & op->flags) != 0
@@ -1488,8 +1613,8 @@ ppc_setup_opcodes (void)
        }
     }
 
-  if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
-    for (op = powerpc_opcodes; op < op_end; op++)
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+    for (op = vle_opcodes; op < op_end; op++)
       hash_insert (ppc_hash, op->name, (void *) op);
 
   /* Insert the macros into a hash table.  */
@@ -1498,7 +1623,7 @@ ppc_setup_opcodes (void)
   macro_end = powerpc_macros + powerpc_num_macros;
   for (macro = powerpc_macros; macro < macro_end; macro++)
     {
-      if ((macro->flags & ppc_cpu) != 0)
+      if ((macro->flags & ppc_cpu) != 0 || (ppc_cpu & PPC_OPCODE_ANY) != 0)
        {
          const char *retval;
 
@@ -1525,6 +1650,7 @@ md_begin (void)
   ppc_set_cpu ();
 
   ppc_cie_data_alignment = ppc_obj64 ? -8 : -4;
+  ppc_dwarf2_line_min_insn_length = (ppc_cpu & PPC_OPCODE_VLE) ? 2 : 4;
 
 #ifdef OBJ_ELF
   /* Set the ELF flags if desired.  */
@@ -1691,8 +1817,10 @@ ppc_insert_operand (unsigned long insn,
       if (errmsg != (const char *) NULL)
        as_bad_where (file, line, "%s", errmsg);
     }
-  else
+  else if (operand->shift >= 0)
     insn |= ((long) val & operand->bitm) << operand->shift;
+  else
+    insn |= ((long) val & operand->bitm) >> -operand->shift;
 
   return insn;
 }
@@ -1774,6 +1902,9 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP32 ("local",            BFD_RELOC_PPC_LOCAL24PC),
     MAP32 ("pltrel",           BFD_RELOC_32_PLT_PCREL),
     MAP32 ("sdarel",           BFD_RELOC_GPREL16),
+    MAP32 ("sdarel@l",         BFD_RELOC_PPC_VLE_SDAREL_LO16A),
+    MAP32 ("sdarel@h",         BFD_RELOC_PPC_VLE_SDAREL_HI16A),
+    MAP32 ("sdarel@ha",                BFD_RELOC_PPC_VLE_SDAREL_HA16A),
     MAP32 ("naddr",            BFD_RELOC_PPC_EMB_NADDR32),
     MAP32 ("naddr16",          BFD_RELOC_PPC_EMB_NADDR16),
     MAP32 ("naddr@l",          BFD_RELOC_PPC_EMB_NADDR16_LO),
@@ -1783,6 +1914,7 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP32 ("sda2rel",          BFD_RELOC_PPC_EMB_SDA2REL),
     MAP32 ("sda2i16",          BFD_RELOC_PPC_EMB_SDA2I16),
     MAP32 ("sda21",            BFD_RELOC_PPC_EMB_SDA21),
+    MAP32 ("sda21@l",          BFD_RELOC_PPC_VLE_SDA21_LO),
     MAP32 ("mrkref",           BFD_RELOC_PPC_EMB_MRKREF),
     MAP32 ("relsect",          BFD_RELOC_PPC_EMB_RELSEC16),
     MAP32 ("relsect@l",                BFD_RELOC_PPC_EMB_RELST_LO),
@@ -1996,7 +2128,7 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
-      as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+      as_bad (_("expected comma after symbol-name: rest of line ignored."));
       ignore_rest_of_line ();
       return;
     }
@@ -2029,7 +2161,7 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
 
   if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
     {
-      as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+      as_bad (_("ignoring attempt to re-define symbol `%s'."),
              S_GET_NAME (symbolP));
       ignore_rest_of_line ();
       return;
@@ -2037,7 +2169,7 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
 
   if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size)
     {
-      as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
+      as_bad (_("length of .lcomm \"%s\" is already %ld. Not changed to %ld."),
              S_GET_NAME (symbolP),
              (long) S_GET_VALUE (symbolP),
              (long) size);
@@ -2055,7 +2187,7 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
       for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2);
       if (align != 1)
        {
-         as_bad (_("Common alignment not a power of 2"));
+         as_bad (_("common alignment not a power of 2"));
          ignore_rest_of_line ();
          return;
        }
@@ -2117,7 +2249,7 @@ ppc_elf_validate_fix (fixS *fixp, segT seg)
              || fixp->fx_r_type != BFD_RELOC_CTOR)
            {
              as_bad_where (fixp->fx_file, fixp->fx_line,
-                           _("Relocation cannot be done when using -mrelocatable"));
+                           _("relocation cannot be done when using -mrelocatable"));
            }
        }
       return;
@@ -2168,11 +2300,26 @@ ppc_frob_file_before_adjust (void)
       && toc_reloc_types != has_large_toc_reloc
       && bfd_section_size (stdoutput, toc) > 0x10000)
     as_warn (_("TOC section size exceeds 64k"));
+}
 
-  /* Don't emit .TOC. symbol.  */
-  symp = symbol_find (".TOC.");
-  if (symp != NULL)
-    symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+/* .TOC. used in an opd entry as .TOC.@tocbase doesn't need to be
+   emitted.  Other uses of .TOC. will cause the symbol to be marked
+   with BSF_KEEP in md_apply_fix.  */
+
+void
+ppc_elf_adjust_symtab (void)
+{
+  if (ppc_obj64)
+    {
+      symbolS *symp;
+      symp = symbol_find (".TOC.");
+      if (symp != NULL)
+       {
+         asymbol *bsym = symbol_get_bfdsym (symp);
+         if ((bsym->flags & BSF_KEEP) == 0)
+           symbol_remove (symp, &symbol_rootP, &symbol_lastP);
+       }
+    }
 }
 #endif /* OBJ_ELF */
 \f
@@ -2271,6 +2418,26 @@ parse_toc_entry (enum toc_size_qualifier *toc_kind)
   return 1;
 }
 #endif
+
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+/* See whether a symbol is in the TOC section.  */
+
+static int
+ppc_is_toc_sym (symbolS *sym)
+{
+#ifdef OBJ_XCOFF
+  return (symbol_get_tc (sym)->symbol_class == XMC_TC
+         || symbol_get_tc (sym)->symbol_class == XMC_TC0);
+#endif
+#ifdef OBJ_ELF
+  const char *sname = segment_name (S_GET_SEGMENT (sym));
+  if (ppc_obj64)
+    return strcmp (sname, ".toc") == 0;
+  else
+    return strcmp (sname, ".got") == 0;
+#endif
+}
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
 \f
 
 #ifdef OBJ_ELF
@@ -2319,6 +2486,22 @@ struct ppc_fixup
 
 #define MAX_INSN_FIXUPS (5)
 
+/* Form I16L.  */
+#define E_OR2I_INSN            0x7000C000
+#define E_AND2I_DOT_INSN       0x7000C800
+#define E_OR2IS_INSN           0x7000D000
+#define E_LIS_INSN             0x7000E000
+#define        E_AND2IS_DOT_INSN       0x7000E800
+
+/* Form I16A.  */
+#define E_ADD2I_DOT_INSN       0x70008800
+#define E_ADD2IS_INSN          0x70009000
+#define E_CMP16I_INSN          0x70009800
+#define E_MULL2I_INSN          0x7000A000
+#define E_CMPL16I_INSN         0x7000A800
+#define E_CMPH16I_INSN         0x7000B000
+#define E_CMPHL16I_INSN                0x7000B800
+
 /* This routine is called for each instruction to be assembled.  */
 
 void
@@ -2336,9 +2519,7 @@ md_assemble (char *str)
   char *f;
   int addr_mod;
   int i;
-#ifdef OBJ_ELF
-  bfd_reloc_code_real_type reloc;
-#endif
+  unsigned int insn_length;
 
   /* Get the opcode.  */
   for (s = str; *s != '\0' && ! ISSPACE (*s); s++)
@@ -2354,7 +2535,7 @@ md_assemble (char *str)
 
       macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str);
       if (macro == (const struct powerpc_macro *) NULL)
-       as_bad (_("Unrecognized opcode: `%s'"), str);
+       as_bad (_("unrecognized opcode: `%s'"), str);
       else
        ppc_macro (s, macro);
 
@@ -2507,9 +2688,8 @@ md_assemble (char *str)
 
          if (!valid_toc)
            {
-             /* Note: message has already been issued.
-                FIXME: what sort of recovery should we do?
-                demand_rest_of_line (); return; ?  */
+             ignore_rest_of_line ();
+             break;
            }
 
          /* Now get the symbol following the ']'.  */
@@ -2549,12 +2729,12 @@ md_assemble (char *str)
                 toc entries.  We don't support them today.  Is this
                 the right way to say that?  */
              toc_reloc = BFD_RELOC_UNUSED;
-             as_bad (_("Unimplemented toc32 expression modifier"));
+             as_bad (_("unimplemented toc32 expression modifier"));
              break;
            case must_be_64:
              /* FIXME: see above.  */
              toc_reloc = BFD_RELOC_UNUSED;
-             as_bad (_("Unimplemented toc64 expression modifier"));
+             as_bad (_("unimplemented toc64 expression modifier"));
              break;
            default:
              fprintf (stderr,
@@ -2585,12 +2765,15 @@ md_assemble (char *str)
       else
 #endif         /* TE_PE */
        {
-         if ((reg_names_p && (operand->flags & PPC_OPERAND_CR) != 0)
+         if ((reg_names_p
+               && (((operand->flags & PPC_OPERAND_CR_BIT) != 0)
+                  || ((operand->flags & PPC_OPERAND_CR_REG) != 0)))
              || !register_name (&ex))
            {
              char save_lex = lex_type['%'];
 
-             if ((operand->flags & PPC_OPERAND_CR) != 0)
+             if (((operand->flags & PPC_OPERAND_CR_REG) != 0)
+                 || (operand->flags & PPC_OPERAND_CR_BIT) != 0)
                {
                  cr_operand = TRUE;
                  lex_type['%'] |= LEX_BEGIN_NAME;
@@ -2617,6 +2800,7 @@ md_assemble (char *str)
        {
 #ifdef OBJ_ELF
          /* Allow @HA, @L, @H on constants.  */
+         bfd_reloc_code_real_type reloc;
          char *orig_str = str;
 
          if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
@@ -2682,9 +2866,10 @@ md_assemble (char *str)
          insn = ppc_insert_operand (insn, operand, ex.X_add_number,
                                     ppc_cpu, (char *) NULL, 0);
        }
-#ifdef OBJ_ELF
       else
        {
+         bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
+#ifdef OBJ_ELF
          if (ex.X_op == O_symbol && str[0] == '(')
            {
              const char *sym_name = S_GET_NAME (ex.X_add_symbol);
@@ -2758,6 +2943,73 @@ md_assemble (char *str)
                  break;
                }
 
+             /* If VLE-mode convert LO/HI/HA relocations.  */
+             if (opcode->flags & PPC_OPCODE_VLE)
+               {
+                 int tmp_insn = insn & opcode->mask;
+                 
+                 int use_d_reloc = (tmp_insn == E_OR2I_INSN
+                                    || tmp_insn == E_AND2I_DOT_INSN
+                                    || tmp_insn == E_OR2IS_INSN
+                                    || tmp_insn == E_LIS_INSN
+                                    || tmp_insn == E_AND2IS_DOT_INSN);
+
+
+                 int use_a_reloc = (tmp_insn == E_ADD2I_DOT_INSN
+                                    || tmp_insn == E_ADD2IS_INSN
+                                    || tmp_insn == E_CMP16I_INSN
+                                    || tmp_insn == E_MULL2I_INSN
+                                    || tmp_insn == E_CMPL16I_INSN
+                                    || tmp_insn == E_CMPH16I_INSN
+                                    || tmp_insn == E_CMPHL16I_INSN);
+
+                 switch (reloc)
+                   {
+                   default:
+                     break;
+
+                   case BFD_RELOC_PPC_EMB_SDA21:
+                     reloc = BFD_RELOC_PPC_VLE_SDA21;
+                     break;
+
+                   case BFD_RELOC_LO16:
+                     if (use_d_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_LO16D;
+                     else if (use_a_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_LO16A;
+                     break;
+
+                   case BFD_RELOC_HI16:
+                     if (use_d_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_HI16D;
+                     else if (use_a_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_HI16A;
+                     break;
+        
+                   case BFD_RELOC_HI16_S:
+                     if (use_d_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_HA16D;
+                     else if (use_a_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_HA16A;
+                     break;
+
+                   case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+                     if (use_d_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_SDAREL_LO16D;
+                     break;
+
+                   case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+                     if (use_d_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_SDAREL_HI16D;
+                     break;
+
+                   case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+                     if (use_d_reloc)
+                       reloc = BFD_RELOC_PPC_VLE_SDAREL_HA16D;
+                     break;
+                   }
+               }
+
              /* For the absolute forms of branches, convert the PC
                 relative form back into the absolute.  */
              if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
@@ -2795,8 +3047,7 @@ md_assemble (char *str)
                  break;
                }
 
-             if (ppc_obj64
-                 && (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
+             if ((operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
                {
                  switch (reloc)
                    {
@@ -2856,6 +3107,66 @@ md_assemble (char *str)
                    }
                }
            }
+#endif /* OBJ_ELF */
+
+         if (reloc != BFD_RELOC_UNUSED)
+           ;
+         /* Determine a BFD reloc value based on the operand information.
+            We are only prepared to turn a few of the operands into
+            relocs.  */
+         else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+                  && operand->bitm == 0x3fffffc
+                  && operand->shift == 0)
+           reloc = BFD_RELOC_PPC_B26;
+         else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+                  && operand->bitm == 0xfffc
+                  && operand->shift == 0)
+           reloc = BFD_RELOC_PPC_B16;
+         else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+                  && operand->bitm == 0x1fe
+                  && operand->shift == -1)
+           reloc = BFD_RELOC_PPC_VLE_REL8;
+         else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+                  && operand->bitm == 0xfffe
+                  && operand->shift == 0)
+           reloc = BFD_RELOC_PPC_VLE_REL15;
+         else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+                  && operand->bitm == 0x1fffffe
+                  && operand->shift == 0)
+           reloc = BFD_RELOC_PPC_VLE_REL24;
+         else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
+                  && operand->bitm == 0x3fffffc
+                  && operand->shift == 0)
+           reloc = BFD_RELOC_PPC_BA26;
+         else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
+                  && operand->bitm == 0xfffc
+                  && operand->shift == 0)
+           reloc = BFD_RELOC_PPC_BA16;
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+         else if ((operand->flags & PPC_OPERAND_PARENS) != 0
+                  && (operand->bitm & 0xfff0) == 0xfff0
+                  && operand->shift == 0)
+           {
+             if (ppc_is_toc_sym (ex.X_add_symbol))
+               {
+                 reloc = BFD_RELOC_PPC_TOC16;
+#ifdef OBJ_ELF
+                 if (ppc_obj64
+                     && (operand->flags & PPC_OPERAND_DS) != 0)
+                   reloc = BFD_RELOC_PPC64_TOC16_DS;
+#endif
+               }
+             else
+               {
+                 reloc = BFD_RELOC_16;
+#ifdef OBJ_ELF
+                 if (ppc_obj64
+                     && (operand->flags & PPC_OPERAND_DS) != 0)
+                   reloc = BFD_RELOC_PPC64_ADDR16_DS;
+#endif
+               }
+           }
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
 
          /* We need to generate a fixup for this expression.  */
          if (fc >= MAX_INSN_FIXUPS)
@@ -2865,18 +3176,6 @@ md_assemble (char *str)
          fixups[fc].reloc = reloc;
          ++fc;
        }
-#else /* OBJ_ELF */
-      else
-       {
-         /* We need to generate a fixup for this expression.  */
-         if (fc >= MAX_INSN_FIXUPS)
-           as_fatal (_("too many fixups"));
-         fixups[fc].exp = ex;
-         fixups[fc].opindex = *opindex_ptr;
-         fixups[fc].reloc = BFD_RELOC_UNUSED;
-         ++fc;
-       }
-#endif /* OBJ_ELF */
 
       if (need_paren)
        {
@@ -2904,7 +3203,10 @@ md_assemble (char *str)
       if (*str != endc
          && (endc != ',' || *str != '\0'))
        {
-         as_bad (_("syntax error; found `%c' but expected `%c'"), *str, endc);
+         if (*str == '\0')
+           as_bad (_("syntax error; end of line, expected `%c'"), endc);
+         else
+           as_bad (_("syntax error; found `%c', expected `%c'"), *str, endc);
          break;
        }
 
@@ -2919,8 +3221,9 @@ md_assemble (char *str)
     as_bad (_("junk at end of line: `%s'"), str);
 
 #ifdef OBJ_ELF
-  /* Do we need/want a APUinfo section? */
-  if ((ppc_cpu & (PPC_OPCODE_E500 | PPC_OPCODE_E500MC)) != 0)
+  /* Do we need/want an APUinfo section? */
+  if ((ppc_cpu & (PPC_OPCODE_E500 | PPC_OPCODE_E500MC | PPC_OPCODE_VLE)) != 0
+      && !ppc_obj64)
     {
       /* These are all version "1".  */
       if (opcode->flags & PPC_OPCODE_SPE)
@@ -2937,43 +3240,59 @@ md_assemble (char *str)
        ppc_apuinfo_section_add (PPC_APUINFO_CACHELCK, 1);
       if (opcode->flags & PPC_OPCODE_RFMCI)
        ppc_apuinfo_section_add (PPC_APUINFO_RFMCI, 1);
+      if (opcode->flags & PPC_OPCODE_VLE)
+       ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
     }
 #endif
 
   /* Write out the instruction.  */
-  f = frag_more (4);
-  addr_mod = frag_now_fix () & 3;
+  /* Differentiate between two and four byte insns.  */
+  if (ppc_mach () == bfd_mach_ppc_vle)
+    {
+      if (PPC_OP_SE_VLE (insn))
+        insn_length = 2;
+      else
+        insn_length = 4;
+      addr_mod = frag_now_fix () & 1;
+    }
+  else
+    {
+      insn_length = 4;
+      addr_mod = frag_now_fix () & 3;
+    }
+  /* All instructions can start on a 2 byte boundary for VLE.  */
+  f = frag_more (insn_length);
   if (frag_now->has_code && frag_now->insn_addr != addr_mod)
-    as_bad (_("instruction address is not a multiple of 4"));
+    {
+      if (ppc_mach() == bfd_mach_ppc_vle)
+        as_bad (_("instruction address is not a multiple of 2"));
+      else
+        as_bad (_("instruction address is not a multiple of 4"));
+    }
   frag_now->insn_addr = addr_mod;
   frag_now->has_code = 1;
-  md_number_to_chars (f, insn, 4);
+  md_number_to_chars (f, insn, insn_length);
 
 #ifdef OBJ_ELF
-  dwarf2_emit_insn (4);
+  dwarf2_emit_insn (insn_length);
 #endif
 
-  /* Create any fixups.  At this point we do not use a
-     bfd_reloc_code_real_type, but instead just use the
-     BFD_RELOC_UNUSED plus the operand index.  This lets us easily
-     handle fixups for any operand type, although that is admittedly
-     not a very exciting feature.  We pick a BFD reloc type in
-     md_apply_fix.  */
+  /* Create any fixups.  */
   for (i = 0; i < fc; i++)
     {
+      fixS *fixP;
       if (fixups[i].reloc != BFD_RELOC_UNUSED)
        {
          reloc_howto_type *reloc_howto;
          int size;
          int offset;
-         fixS *fixP;
 
          reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc);
          if (!reloc_howto)
            abort ();
 
          size = bfd_get_reloc_size (reloc_howto);
-         offset = target_big_endian ? (4 - size) : 0;
+         offset = target_big_endian ? (insn_length - size) : 0;
 
          if (size < 1 || size > 4)
            abort ();
@@ -2984,41 +3303,20 @@ md_assemble (char *str)
                              &fixups[i].exp,
                              reloc_howto->pc_relative,
                              fixups[i].reloc);
-
-         /* Turn off complaints that the addend is too large for things like
-            foo+100000@ha.  */
-         switch (fixups[i].reloc)
-           {
-           case BFD_RELOC_16_GOTOFF:
-           case BFD_RELOC_PPC_TOC16:
-           case BFD_RELOC_LO16:
-           case BFD_RELOC_HI16:
-           case BFD_RELOC_HI16_S:
-#ifdef OBJ_ELF
-           case BFD_RELOC_PPC64_HIGHER:
-           case BFD_RELOC_PPC64_HIGHER_S:
-           case BFD_RELOC_PPC64_HIGHEST:
-           case BFD_RELOC_PPC64_HIGHEST_S:
-#endif
-             fixP->fx_no_overflow = 1;
-             break;
-           default:
-             break;
-           }
        }
       else
        {
          const struct powerpc_operand *operand;
 
          operand = &powerpc_operands[fixups[i].opindex];
-         fix_new_exp (frag_now,
-                      f - frag_now->fr_literal,
-                      4,
-                      &fixups[i].exp,
-                      (operand->flags & PPC_OPERAND_RELATIVE) != 0,
-                      ((bfd_reloc_code_real_type)
-                       (fixups[i].opindex + (int) BFD_RELOC_UNUSED)));
+         fixP = fix_new_exp (frag_now,
+                             f - frag_now->fr_literal,
+                             insn_length,
+                             &fixups[i].exp,
+                             (operand->flags & PPC_OPERAND_RELATIVE) != 0,
+                             BFD_RELOC_UNUSED);
        }
+      fixP->fx_pcrel_adjust = fixups[i].opindex;
     }
 }
 
@@ -3457,6 +3755,158 @@ ppc_change_csect (symbolS *sym, offsetT align)
   ppc_current_csect = sym;
 }
 
+static void
+ppc_change_debug_section (unsigned int idx, subsegT subseg)
+{
+  segT sec;
+  flagword oldflags;
+  const struct xcoff_dwsect_name *dw = &xcoff_dwsect_names[idx];
+
+  sec = subseg_new (dw->name, subseg);
+  oldflags = bfd_get_section_flags (stdoutput, sec);
+  if (oldflags == SEC_NO_FLAGS)
+    {
+      /* Just created section.  */
+      gas_assert (dw_sections[idx].sect == NULL);
+
+      bfd_set_section_flags (stdoutput, sec, SEC_DEBUGGING);
+      bfd_set_section_alignment (stdoutput, sec, 0);
+      dw_sections[idx].sect = sec;
+    }
+
+  /* Not anymore in a csect.  */
+  ppc_current_csect = NULL;
+}
+
+/* The .dwsect pseudo-op.  Defines a DWARF section.  Syntax is:
+     .dwsect flag [, opt-label ]
+*/
+
+static void
+ppc_dwsect (int ignore ATTRIBUTE_UNUSED)
+{
+  offsetT flag;
+  symbolS *opt_label;
+  const struct xcoff_dwsect_name *dw;
+  struct dw_subsection *subseg;
+  struct dw_section *dws;
+  int i;
+
+  /* Find section.  */
+  flag = get_absolute_expression ();
+  dw = NULL;
+  for (i = 0; i < XCOFF_DWSECT_NBR_NAMES; i++)
+    if (xcoff_dwsect_names[i].flag == flag)
+      {
+        dw = &xcoff_dwsect_names[i];
+        break;
+      }
+
+  /* Parse opt-label.  */
+  if (*input_line_pointer == ',')
+    {
+      const char *label;
+      char c;
+
+      ++input_line_pointer;
+
+      label = input_line_pointer;
+      c = get_symbol_end ();
+      opt_label = symbol_find_or_make (label);
+      *input_line_pointer = c;
+    }
+  else
+    opt_label = NULL;
+
+  demand_empty_rest_of_line ();
+
+  /* Return now in case of unknown subsection.  */
+  if (dw == NULL)
+    {
+      as_bad (_("no known dwarf XCOFF section for flag 0x%08x\n"),
+              (unsigned)flag);
+      return;
+    }
+
+  /* Find the subsection.  */
+  dws = &dw_sections[i];
+  subseg = NULL;
+  if (opt_label != NULL && S_IS_DEFINED (opt_label))
+    {
+      /* Sanity check (note that in theory S_GET_SEGMENT mustn't be null).  */
+      if (dws->sect == NULL || S_GET_SEGMENT (opt_label) != dws->sect)
+        {
+          as_bad (_("label %s was not defined in this dwarf section"),
+                  S_GET_NAME (opt_label));
+          subseg = dws->anon_subseg;
+          opt_label = NULL;
+        }
+      else
+        subseg = symbol_get_tc (opt_label)->u.dw;
+    }
+
+  if (subseg != NULL)
+    {
+      /* Switch to the subsection.  */
+      ppc_change_debug_section (i, subseg->subseg);
+    }
+  else
+    {
+      /* Create a new dw subsection.  */
+      subseg = (struct dw_subsection *)
+        xmalloc (sizeof (struct dw_subsection));
+
+      if (opt_label == NULL)
+        {
+          /* The anonymous one.  */
+          subseg->subseg = 0;
+          subseg->link = NULL;
+          dws->anon_subseg = subseg;
+        }
+      else
+        {
+          /* A named one.  */
+          if (dws->list_subseg != NULL)
+            subseg->subseg = dws->list_subseg->subseg + 1;
+          else
+            subseg->subseg = 1;
+
+          subseg->link = dws->list_subseg;
+          dws->list_subseg = subseg;
+          symbol_get_tc (opt_label)->u.dw = subseg;
+        }
+
+      ppc_change_debug_section (i, subseg->subseg);
+
+      if (dw->def_size)
+        {
+          /* Add the length field.  */
+          expressionS *exp = &subseg->end_exp;
+          int sz;
+
+          if (opt_label != NULL)
+            symbol_set_value_now (opt_label);
+
+          /* Add the length field.  Note that according to the AIX assembler
+             manual, the size of the length field is 4 for powerpc32 but
+             12 for powerpc64.  */
+          if (ppc_obj64)
+            {
+              /* Write the 64bit marker.  */
+              md_number_to_chars (frag_more (4), -1, 4);
+            }
+
+          exp->X_op = O_subtract;
+          exp->X_op_symbol = symbol_temp_new_now ();
+          exp->X_add_symbol = symbol_temp_make ();
+
+          sz = ppc_obj64 ? 8 : 4;
+          exp->X_add_number = -sz;
+          emit_expr (exp, sz);
+        }
+    }
+}
+
 /* This function handles the .text and .data pseudo-ops.  These
    pseudo-ops aren't really used by XCOFF; we implement them for the
    convenience of people who aren't used to XCOFF.  */
@@ -3502,7 +3952,7 @@ ppc_named_section (int ignore ATTRIBUTE_UNUSED)
     real_name = ".data[RW]";
   else
     {
-      as_bad (_("The XCOFF file format does not support arbitrary sections"));
+      as_bad (_("the XCOFF file format does not support arbitrary sections"));
       *input_line_pointer = c;
       ignore_rest_of_line ();
       return;
@@ -3726,28 +4176,30 @@ ppc_stabx (int ignore ATTRIBUTE_UNUSED)
 
   symbol_get_tc (sym)->output = 1;
 
-  if (S_GET_STORAGE_CLASS (sym) == C_STSYM) {
-
-    symbol_get_tc (sym)->within = ppc_current_block;
+  if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
+    {
+      /* In this case :
 
-    /* In this case :
+         .bs name
+         .stabx        "z",arrays_,133,0
+         .es
 
-       .bs name
-       .stabx  "z",arrays_,133,0
-       .es
+         .comm arrays_,13768,3
 
-       .comm arrays_,13768,3
+         resolve_symbol_value will copy the exp's "within" into sym's when the
+         offset is 0.  Since this seems to be corner case problem,
+         only do the correction for storage class C_STSYM.  A better solution
+         would be to have the tc field updated in ppc_symbol_new_hook.  */
 
-       resolve_symbol_value will copy the exp's "within" into sym's when the
-       offset is 0.  Since this seems to be corner case problem,
-       only do the correction for storage class C_STSYM.  A better solution
-       would be to have the tc field updated in ppc_symbol_new_hook.  */
+      if (exp.X_op == O_symbol)
+        {
+          if (ppc_current_block == NULL)
+            as_bad (_(".stabx of storage class stsym must be within .bs/.es"));
 
-    if (exp.X_op == O_symbol)
-      {
-       symbol_get_tc (exp.X_add_symbol)->within = ppc_current_block;
-      }
-  }
+          symbol_get_tc (sym)->within = ppc_current_block;
+          symbol_get_tc (exp.X_add_symbol)->within = ppc_current_block;
+        }
+    }
 
   if (exp.X_op != O_symbol
       || ! S_IS_EXTERNAL (exp.X_add_symbol)
@@ -3842,11 +4294,9 @@ ppc_function (int ignore ATTRIBUTE_UNUSED)
            {
              /* The fifth argument is the function size.  */
              ++input_line_pointer;
-             symbol_get_tc (ext_sym)->size = symbol_new ("L0\001",
-                                                         absolute_section,
-                                                         (valueT) 0,
-                                                         &zero_address_frag);
-             pseudo_set (symbol_get_tc (ext_sym)->size);
+             symbol_get_tc (ext_sym)->u.size = symbol_new
+                ("L0\001", absolute_section,(valueT) 0, &zero_address_frag);
+             pseudo_set (symbol_get_tc (ext_sym)->u.size);
            }
        }
     }
@@ -4208,12 +4658,39 @@ ppc_vbyte (int dummy ATTRIBUTE_UNUSED)
   cons (byte_count);
 }
 
-#endif /* OBJ_XCOFF */
-#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
-\f
-/* The .tc pseudo-op.  This is used when generating either XCOFF or
-   ELF.  This takes two or more arguments.
-
+void
+ppc_xcoff_end (void)
+{
+  int i;
+
+  for (i = 0; i < XCOFF_DWSECT_NBR_NAMES; i++)
+    {
+      struct dw_section *dws = &dw_sections[i];
+      struct dw_subsection *dwss;
+
+      if (dws->anon_subseg)
+        {
+          dwss = dws->anon_subseg;
+          dwss->link = dws->list_subseg;
+        }
+      else
+        dwss = dws->list_subseg;
+
+      for (; dwss != NULL; dwss = dwss->link)
+        if (dwss->end_exp.X_add_symbol != NULL)
+          {
+            subseg_set (dws->sect, dwss->subseg);
+            symbol_set_value_now (dwss->end_exp.X_add_symbol);
+          }
+    }
+}
+
+#endif /* OBJ_XCOFF */
+#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+\f
+/* The .tc pseudo-op.  This is used when generating either XCOFF or
+   ELF.  This takes two or more arguments.
+
    When generating XCOFF output, the first argument is the name to
    give to this location in the toc; this will be a symbol with class
    TC.  The rest of the arguments are N-byte values to actually put at
@@ -4362,7 +4839,7 @@ ppc_machine (int ignore ATTRIBUTE_UNUSED)
          else
            ppc_cpu = cpu_history[--curr_hist];
        }
-      else if ((new_cpu = ppc_parse_cpu (ppc_cpu, cpu_string)) != 0)
+      else if ((new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, cpu_string)) != 0)
        ppc_cpu = new_cpu;
       else
        as_bad (_("invalid machine `%s'"), cpu_string);
@@ -4373,23 +4850,6 @@ ppc_machine (int ignore ATTRIBUTE_UNUSED)
 
   demand_empty_rest_of_line ();
 }
-
-/* See whether a symbol is in the TOC section.  */
-
-static int
-ppc_is_toc_sym (symbolS *sym)
-{
-#ifdef OBJ_XCOFF
-  return symbol_get_tc (sym)->symbol_class == XMC_TC;
-#endif
-#ifdef OBJ_ELF
-  const char *sname = segment_name (S_GET_SEGMENT (sym));
-  if (ppc_obj64)
-    return strcmp (sname, ".toc") == 0;
-  else
-    return strcmp (sname, ".got") == 0;
-#endif
-}
 #endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
 \f
 #ifdef TE_PE
@@ -4412,11 +4872,9 @@ ppc_set_current_section (segT new)
 static void
 ppc_previous (int ignore ATTRIBUTE_UNUSED)
 {
-  symbolS *tmp;
-
   if (ppc_previous_section == NULL)
     {
-      as_warn (_("No previous section to return to. Directive ignored."));
+      as_warn (_("no previous section to return to, ignored."));
       return;
     }
 
@@ -4585,15 +5043,11 @@ ppc_znop (int ignore ATTRIBUTE_UNUSED)
 {
   unsigned long insn;
   const struct powerpc_opcode *opcode;
-  expressionS ex;
   char *f;
   symbolS *sym;
   char *symbol_name;
   char c;
   char *name;
-  unsigned int exp;
-  flagword flags;
-  asection *sec;
 
   /* Strip out the symbol name.  */
   symbol_name = input_line_pointer;
@@ -4651,7 +5105,7 @@ ppc_pe_comm (int lcomm)
   SKIP_WHITESPACE ();
   if (*input_line_pointer != ',')
     {
-      as_bad (_("Expected comma after symbol-name: rest of line ignored."));
+      as_bad (_("expected comma after symbol-name: rest of line ignored."));
       ignore_rest_of_line ();
       return;
     }
@@ -4687,7 +5141,7 @@ ppc_pe_comm (int lcomm)
   *p = c;
   if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
     {
-      as_bad (_("Ignoring attempt to re-define symbol `%s'."),
+      as_bad (_("ignoring attempt to re-define symbol `%s'."),
              S_GET_NAME (symbolP));
       ignore_rest_of_line ();
       return;
@@ -4696,7 +5150,7 @@ ppc_pe_comm (int lcomm)
   if (S_GET_VALUE (symbolP))
     {
       if (S_GET_VALUE (symbolP) != (valueT) temp)
-       as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."),
+       as_bad (_("length of .comm \"%s\" is already %ld. Not changed to %ld."),
                S_GET_NAME (symbolP),
                (long) S_GET_VALUE (symbolP),
                (long) temp);
@@ -4826,7 +5280,7 @@ ppc_pe_section (int ignore ATTRIBUTE_UNUSED)
                {
                  /* Section Contents */
                case 'a': /* unknown */
-                 as_bad (_("Unsupported section attribute -- 'a'"));
+                 as_bad (_("unsupported section attribute -- 'a'"));
                  break;
                case 'c': /* code section */
                  flags |= SEC_CODE;
@@ -5034,7 +5488,8 @@ ppc_symbol_new_hook (symbolS *sym)
   tc->real_name = NULL;
   tc->subseg = 0;
   tc->align = 0;
-  tc->size = NULL;
+  tc->u.size = NULL;
+  tc->u.dw = NULL;
   tc->within = NULL;
 
   if (ppc_stab_symbol)
@@ -5102,7 +5557,7 @@ ppc_symbol_new_hook (symbolS *sym)
     }
 
   if (tc->symbol_class == -1)
-    as_bad (_("Unrecognized symbol suffix"));
+    as_bad (_("unrecognized symbol suffix"));
 }
 
 /* Set the class of a label based on where it is defined.  This
@@ -5121,6 +5576,7 @@ ppc_frob_label (symbolS *sym)
       symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
                     &symbol_rootP, &symbol_lastP);
       symbol_get_tc (ppc_current_csect)->within = sym;
+      symbol_get_tc (sym)->within = ppc_current_csect;
     }
 
 #ifdef OBJ_ELF
@@ -5192,11 +5648,11 @@ ppc_frob_symbol (symbolS *sym)
       if (ppc_last_function != (symbolS *) NULL)
        as_bad (_("two .function pseudo-ops with no intervening .ef"));
       ppc_last_function = sym;
-      if (symbol_get_tc (sym)->size != (symbolS *) NULL)
+      if (symbol_get_tc (sym)->u.size != (symbolS *) NULL)
        {
-         resolve_symbol_value (symbol_get_tc (sym)->size);
+         resolve_symbol_value (symbol_get_tc (sym)->u.size);
          SA_SET_SYM_FSIZE (sym,
-                           (long) S_GET_VALUE (symbol_get_tc (sym)->size));
+                           (long) S_GET_VALUE (symbol_get_tc (sym)->u.size));
        }
     }
   else if (S_GET_STORAGE_CLASS (sym) == C_FCN
@@ -5380,13 +5836,22 @@ ppc_frob_symbol (symbolS *sym)
   else if (S_GET_STORAGE_CLASS (sym) == C_STSYM)
     {
       symbolS *block;
-      symbolS *csect;
+      valueT base;
 
-      /* The value is the offset from the enclosing csect.  */
       block = symbol_get_tc (sym)->within;
-      csect = symbol_get_tc (block)->within;
-      resolve_symbol_value (csect);
-      S_SET_VALUE (sym, S_GET_VALUE (sym) - S_GET_VALUE (csect));
+      if (block)
+        {
+          /* The value is the offset from the enclosing csect.  */
+          symbolS *csect;
+
+          csect = symbol_get_tc (block)->within;
+          resolve_symbol_value (csect);
+          base = S_GET_VALUE (csect);
+        }
+      else
+        base = 0;
+
+      S_SET_VALUE (sym, S_GET_VALUE (sym) - base);
     }
   else if (S_GET_STORAGE_CLASS (sym) == C_BINCL
           || S_GET_STORAGE_CLASS (sym) == C_EINCL)
@@ -5454,6 +5919,10 @@ ppc_frob_section (asection *sec)
 {
   static bfd_vma vma = 0;
 
+  /* Dwarf sections start at 0.  */
+  if (bfd_get_section_flags (NULL, sec) & SEC_DEBUGGING)
+    return;
+
   vma = md_section_align (sec, vma);
   bfd_set_section_vma (stdoutput, sec, vma);
   vma += bfd_section_size (stdoutput, sec);
@@ -5549,6 +6018,10 @@ ppc_fix_adjustable (fixS *fix)
   if (symseg == absolute_section)
     return 0;
 
+  /* Always adjust symbols in debugging sections.  */
+  if (bfd_get_section_flags (stdoutput, symseg) & SEC_DEBUGGING)
+    return 1;
+
   if (ppc_toc_csect != (symbolS *) NULL
       && fix->fx_addsy != ppc_toc_csect
       && symseg == data_section
@@ -5593,55 +6066,17 @@ ppc_fix_adjustable (fixS *fix)
          || (ppc_after_toc_frag != NULL
              && val >= ppc_after_toc_frag->fr_address)))
     {
-      symbolS *csect;
-      symbolS *next_csect;
-
-      if (symseg == text_section)
-       csect = ppc_text_csects;
-      else if (symseg == data_section)
-       csect = ppc_data_csects;
-      else
-       abort ();
+      symbolS *csect = tc->within;
 
-      /* Skip the initial dummy symbol.  */
-      csect = symbol_get_tc (csect)->next;
+      /* If the symbol was not declared by a label (eg: a section symbol),
+         use the section instead of the csect.  This doesn't happen in
+         normal AIX assembly code.  */
+      if (csect == NULL)
+        csect = seg_info (symseg)->sym;
 
-      if (csect != (symbolS *) NULL)
-       {
-         while ((next_csect = symbol_get_tc (csect)->next) != (symbolS *) NULL
-                && (symbol_get_frag (next_csect)->fr_address <= val))
-           {
-             /* If the csect address equals the symbol value, then we
-                have to look through the full symbol table to see
-                whether this is the csect we want.  Note that we will
-                only get here if the csect has zero length.  */
-             if (symbol_get_frag (csect)->fr_address == val
-                 && S_GET_VALUE (csect) == val)
-               {
-                 symbolS *scan;
+      fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
+      fix->fx_addsy = csect;
 
-                 for (scan = symbol_next (csect);
-                      scan != NULL;
-                      scan = symbol_next (scan))
-                   {
-                     if (symbol_get_tc (scan)->subseg != 0)
-                       break;
-                     if (scan == fix->fx_addsy)
-                       break;
-                   }
-
-                 /* If we found the symbol before the next csect
-                    symbol, then this is the csect we want.  */
-                 if (scan == fix->fx_addsy)
-                   break;
-               }
-
-             csect = next_csect;
-           }
-
-         fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
-         fix->fx_addsy = csect;
-       }
       return 0;
     }
 
@@ -5683,6 +6118,13 @@ ppc_force_relocation (fixS *fix)
   return generic_force_reloc (fix);
 }
 
+void
+ppc_new_dot_label (symbolS *sym)
+{
+  /* Anchor this label to the current csect for relocations.  */
+  symbol_get_tc (sym)->within = ppc_current_csect;
+}
+
 #endif /* OBJ_XCOFF */
 
 #ifdef OBJ_ELF
@@ -5721,6 +6163,8 @@ ppc_fix_adjustable (fixS *fix)
          && fix->fx_r_type != BFD_RELOC_LO16_GOTOFF
          && fix->fx_r_type != BFD_RELOC_HI16_GOTOFF
          && fix->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
+         && fix->fx_r_type != BFD_RELOC_PPC64_GOT16_DS
+         && fix->fx_r_type != BFD_RELOC_PPC64_GOT16_LO_DS
          && fix->fx_r_type != BFD_RELOC_GPREL16
          && fix->fx_r_type != BFD_RELOC_VTABLE_INHERIT
          && fix->fx_r_type != BFD_RELOC_VTABLE_ENTRY
@@ -5729,6 +6173,24 @@ ppc_fix_adjustable (fixS *fix)
 }
 #endif
 
+void
+ppc_frag_check (struct frag *fragP)
+{
+  if (!fragP->has_code)
+    return;
+
+  if (ppc_mach() == bfd_mach_ppc_vle)
+    {
+      if (((fragP->fr_address + fragP->insn_addr) & 1) != 0)
+        as_bad (_("instruction address is not a multiple of 2"));
+    }
+  else
+    {
+      if (((fragP->fr_address + fragP->insn_addr) & 3) != 0)
+        as_bad (_("instruction address is not a multiple of 4"));
+    }
+}
+
 /* Implement HANDLE_ALIGN.  This writes the NOP pattern into an
    rs_align_code frag.  */
 
@@ -5738,17 +6200,50 @@ ppc_handle_align (struct frag *fragP)
   valueT count = (fragP->fr_next->fr_address
                  - (fragP->fr_address + fragP->fr_fix));
 
-  if (count != 0 && (count & 3) == 0)
+  if (ppc_mach() == bfd_mach_ppc_vle && count != 0 && (count & 1) == 0)
+    {
+      char *dest = fragP->fr_literal + fragP->fr_fix;
+
+      fragP->fr_var = 2;
+      md_number_to_chars (dest, 0x4400, 2);
+    }
+  else if (count != 0 && (count & 3) == 0)
     {
       char *dest = fragP->fr_literal + fragP->fr_fix;
 
       fragP->fr_var = 4;
+
+      if (count > 4 * nop_limit && count < 0x2000000)
+       {
+         struct frag *rest;
+
+         /* Make a branch, then follow with nops.  Insert another
+            frag to handle the nops.  */
+         md_number_to_chars (dest, 0x48000000 + count, 4);
+         count -= 4;
+         if (count == 0)
+           return;
+
+         rest = xmalloc (SIZEOF_STRUCT_FRAG + 4);
+         memcpy (rest, fragP, SIZEOF_STRUCT_FRAG);
+         fragP->fr_next = rest;
+         fragP = rest;
+         rest->fr_address += rest->fr_fix + 4;
+         rest->fr_fix = 0;
+         /* If we leave the next frag as rs_align_code we'll come here
+            again, resulting in a bunch of branches rather than a
+            branch followed by nops.  */
+         rest->fr_type = rs_align;
+         dest = rest->fr_literal;
+       }
+
       md_number_to_chars (dest, 0x60000000, 4);
 
       if ((ppc_cpu & PPC_OPCODE_POWER6) != 0
-         || (ppc_cpu & PPC_OPCODE_POWER7) != 0)
+         || (ppc_cpu & PPC_OPCODE_POWER7) != 0
+         || (ppc_cpu & PPC_OPCODE_POWER8) != 0)
        {
-         /* For power6 and power7, we want the last nop to be a group
+         /* For power6, power7 and power8, we want the last nop to be a group
             terminating one.  Do this by inserting an rs_fill frag immediately
             after this one, with its address set to the last nop location.
             This will automatically reduce the number of nops in the current
@@ -5766,9 +6261,16 @@ ppc_handle_align (struct frag *fragP)
              dest = group_nop->fr_literal;
            }
 
-         if ((ppc_cpu & PPC_OPCODE_POWER7) != 0)
-           /* power7 group terminating nop: "ori 2,2,0".  */
-           md_number_to_chars (dest, 0x60420000, 4);
+         if ((ppc_cpu & PPC_OPCODE_POWER7) != 0
+             || (ppc_cpu & PPC_OPCODE_POWER8) != 0)
+           {
+             if (ppc_cpu & PPC_OPCODE_E500MC)
+               /* e500mc group terminating nop: "ori 0,0,0".  */
+               md_number_to_chars (dest, 0x60000000, 4);
+             else
+               /* power7/power8 group terminating nop: "ori 2,2,0".  */
+               md_number_to_chars (dest, 0x60420000, 4);
+           }
          else
            /* power6 group terminating nop: "ori 1,1,0".  */
            md_number_to_chars (dest, 0x60210000, 4);
@@ -5777,18 +6279,14 @@ ppc_handle_align (struct frag *fragP)
 }
 
 /* Apply a fixup to the object code.  This is called for all the
-   fixups we generated by the call to fix_new_exp, above.  In the call
-   above we used a reloc code which was the largest legal reloc code
-   plus the operand index.  Here we undo that to recover the operand
-   index.  At this point all symbol values should be fully resolved,
-   and we attempt to completely resolve the reloc.  If we can not do
-   that, we determine the correct reloc code and put it back in the
-   fixup.  */
+   fixups we generated by the calls to fix_new_exp, above.  */
 
 void
 md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
   valueT value = * valP;
+  offsetT fieldval;
+  const struct powerpc_operand *operand;
 
 #ifdef OBJ_ELF
   if (fixP->fx_addsy != NULL)
@@ -5827,17 +6325,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
     }
 
-  if ((int) fixP->fx_r_type >= (int) BFD_RELOC_UNUSED)
+  operand = NULL;
+  if (fixP->fx_pcrel_adjust != 0)
     {
-      int opindex;
-      const struct powerpc_operand *operand;
-      char *where;
-      unsigned long insn;
-
-      opindex = (int) fixP->fx_r_type - (int) BFD_RELOC_UNUSED;
+      /* This is a fixup on an instruction.  */
+      int opindex = fixP->fx_pcrel_adjust & 0xff;
 
       operand = &powerpc_operands[opindex];
-
 #ifdef OBJ_XCOFF
       /* An instruction like `lwz 9,sym(30)' when `sym' is not a TOC symbol
         does not generate a reloc.  It uses the offset of `sym' within its
@@ -5857,90 +6351,247 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          fixP->fx_done = 1;
        }
 #endif
+    }
 
-      /* Fetch the instruction, insert the fully resolved operand
-        value, and stuff the instruction back again.  */
-      where = fixP->fx_frag->fr_literal + fixP->fx_where;
-      if (target_big_endian)
-       insn = bfd_getb32 ((unsigned char *) where);
-      else
-       insn = bfd_getl32 ((unsigned char *) where);
-      insn = ppc_insert_operand (insn, operand, (offsetT) value,
-                                fixP->tc_fix_data.ppc_cpu,
-                                fixP->fx_file, fixP->fx_line);
-      if (target_big_endian)
-       bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
-      else
-       bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+  /* Calculate value to be stored in field.  */
+  fieldval = value;
+  switch (fixP->fx_r_type)
+    {
+#ifdef OBJ_ELF
+    case BFD_RELOC_PPC64_ADDR16_LO_DS:
+    case BFD_RELOC_PPC_VLE_LO16A:
+    case BFD_RELOC_PPC_VLE_LO16D:
+#endif
+    case BFD_RELOC_LO16:
+    case BFD_RELOC_LO16_PCREL:
+      fieldval = value & 0xffff;
+    sign_extend_16:
+      if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+       fieldval = (fieldval ^ 0x8000) - 0x8000;
+      fixP->fx_no_overflow = 1;
+      break;
 
-      if (fixP->fx_done)
-       /* Nothing else to do here.  */
-       return;
+#ifdef OBJ_ELF
+    case BFD_RELOC_PPC_VLE_HI16A:
+    case BFD_RELOC_PPC_VLE_HI16D:
+#endif
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_HI16_PCREL:
+      fieldval = PPC_HI (value);
+      goto sign_extend_16;
 
-      gas_assert (fixP->fx_addsy != NULL);
+#ifdef OBJ_ELF
+    case BFD_RELOC_PPC_VLE_HA16A:
+    case BFD_RELOC_PPC_VLE_HA16D:
+#endif
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_HI16_S_PCREL:
+      fieldval = PPC_HA (value);
+      goto sign_extend_16;
 
-      /* Determine a BFD reloc value based on the operand information.
-        We are only prepared to turn a few of the operands into
-        relocs.  */
-      if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
-         && operand->bitm == 0x3fffffc
-         && operand->shift == 0)
-       fixP->fx_r_type = BFD_RELOC_PPC_B26;
-      else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
-         && operand->bitm == 0xfffc
-         && operand->shift == 0)
-       {
-         fixP->fx_r_type = BFD_RELOC_PPC_B16;
-#ifdef OBJ_XCOFF
-         fixP->fx_size = 2;
-         if (target_big_endian)
-           fixP->fx_where += 2;
+#ifdef OBJ_ELF
+    case BFD_RELOC_PPC64_HIGHER:
+      fieldval = PPC_HIGHER (value);
+      goto sign_extend_16;
+
+    case BFD_RELOC_PPC64_HIGHER_S:
+      fieldval = PPC_HIGHERA (value);
+      goto sign_extend_16;
+
+    case BFD_RELOC_PPC64_HIGHEST:
+      fieldval = PPC_HIGHEST (value);
+      goto sign_extend_16;
+
+    case BFD_RELOC_PPC64_HIGHEST_S:
+      fieldval = PPC_HIGHESTA (value);
+      goto sign_extend_16;
 #endif
-       }
-      else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
-              && operand->bitm == 0x3fffffc
-              && operand->shift == 0)
-       fixP->fx_r_type = BFD_RELOC_PPC_BA26;
-      else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
-              && operand->bitm == 0xfffc
-              && operand->shift == 0)
+
+    default:
+      break;
+    }
+
+  if (operand != NULL)
+    {
+      /* Handle relocs in an insn.  */
+      char *where;
+      unsigned long insn;
+
+      switch (fixP->fx_r_type)
        {
-         fixP->fx_r_type = BFD_RELOC_PPC_BA16;
+#ifdef OBJ_ELF
+         /* The following relocs can't be calculated by the assembler.
+            Leave the field zero.  */
+       case BFD_RELOC_PPC_TPREL16:
+       case BFD_RELOC_PPC_TPREL16_LO:
+       case BFD_RELOC_PPC_TPREL16_HI:
+       case BFD_RELOC_PPC_TPREL16_HA:
+       case BFD_RELOC_PPC_DTPREL16:
+       case BFD_RELOC_PPC_DTPREL16_LO:
+       case BFD_RELOC_PPC_DTPREL16_HI:
+       case BFD_RELOC_PPC_DTPREL16_HA:
+       case BFD_RELOC_PPC_GOT_TLSGD16:
+       case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+       case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+       case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+       case BFD_RELOC_PPC_GOT_TLSLD16:
+       case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+       case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+       case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+       case BFD_RELOC_PPC_GOT_TPREL16:
+       case BFD_RELOC_PPC_GOT_TPREL16_LO:
+       case BFD_RELOC_PPC_GOT_TPREL16_HI:
+       case BFD_RELOC_PPC_GOT_TPREL16_HA:
+       case BFD_RELOC_PPC_GOT_DTPREL16:
+       case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+       case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+       case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+       case BFD_RELOC_PPC64_TPREL16_DS:
+       case BFD_RELOC_PPC64_TPREL16_LO_DS:
+       case BFD_RELOC_PPC64_TPREL16_HIGHER:
+       case BFD_RELOC_PPC64_TPREL16_HIGHERA:
+       case BFD_RELOC_PPC64_TPREL16_HIGHEST:
+       case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+       case BFD_RELOC_PPC64_DTPREL16_DS:
+       case BFD_RELOC_PPC64_DTPREL16_LO_DS:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHER:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
+         gas_assert (fixP->fx_addsy != NULL);
+         S_SET_THREAD_LOCAL (fixP->fx_addsy);
+         fieldval = 0;
+         break;
+
+         /* These also should leave the field zero for the same
+            reason.  Note that older versions of gas wrote values
+            here.  If we want to go back to the old behaviour, then
+            all _LO and _LO_DS cases will need to be treated like
+            BFD_RELOC_LO16_PCREL above.  Similarly for _HI etc.  */
+       case BFD_RELOC_16_GOTOFF:
+       case BFD_RELOC_LO16_GOTOFF:
+       case BFD_RELOC_HI16_GOTOFF:
+       case BFD_RELOC_HI16_S_GOTOFF:
+       case BFD_RELOC_LO16_PLTOFF:
+       case BFD_RELOC_HI16_PLTOFF:
+       case BFD_RELOC_HI16_S_PLTOFF:
+       case BFD_RELOC_GPREL16:
+       case BFD_RELOC_16_BASEREL:
+       case BFD_RELOC_LO16_BASEREL:
+       case BFD_RELOC_HI16_BASEREL:
+       case BFD_RELOC_HI16_S_BASEREL:
+       case BFD_RELOC_PPC_TOC16:
+       case BFD_RELOC_PPC64_TOC16_LO:
+       case BFD_RELOC_PPC64_TOC16_HI:
+       case BFD_RELOC_PPC64_TOC16_HA:
+       case BFD_RELOC_PPC64_PLTGOT16:
+       case BFD_RELOC_PPC64_PLTGOT16_LO:
+       case BFD_RELOC_PPC64_PLTGOT16_HI:
+       case BFD_RELOC_PPC64_PLTGOT16_HA:
+       case BFD_RELOC_PPC64_GOT16_DS:
+       case BFD_RELOC_PPC64_GOT16_LO_DS:
+       case BFD_RELOC_PPC64_PLT16_LO_DS:
+       case BFD_RELOC_PPC64_SECTOFF_DS:
+       case BFD_RELOC_PPC64_SECTOFF_LO_DS:
+       case BFD_RELOC_PPC64_TOC16_DS:
+       case BFD_RELOC_PPC64_TOC16_LO_DS:
+       case BFD_RELOC_PPC64_PLTGOT16_DS:
+       case BFD_RELOC_PPC64_PLTGOT16_LO_DS:
+       case BFD_RELOC_PPC_EMB_NADDR16:
+       case BFD_RELOC_PPC_EMB_NADDR16_LO:
+       case BFD_RELOC_PPC_EMB_NADDR16_HI:
+       case BFD_RELOC_PPC_EMB_NADDR16_HA:
+       case BFD_RELOC_PPC_EMB_SDAI16:
+       case BFD_RELOC_PPC_EMB_SDA2I16:
+       case BFD_RELOC_PPC_EMB_SDA2REL:
+       case BFD_RELOC_PPC_EMB_SDA21:
+       case BFD_RELOC_PPC_EMB_MRKREF:
+       case BFD_RELOC_PPC_EMB_RELSEC16:
+       case BFD_RELOC_PPC_EMB_RELST_LO:
+       case BFD_RELOC_PPC_EMB_RELST_HI:
+       case BFD_RELOC_PPC_EMB_RELST_HA:
+       case BFD_RELOC_PPC_EMB_BIT_FLD:
+       case BFD_RELOC_PPC_EMB_RELSDA:
+       case BFD_RELOC_PPC_VLE_SDA21:
+       case BFD_RELOC_PPC_VLE_SDA21_LO:
+       case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+       case BFD_RELOC_PPC_VLE_SDAREL_LO16D:
+       case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+       case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
+       case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+       case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+         gas_assert (fixP->fx_addsy != NULL);
+         /* Fall thru */
+
+       case BFD_RELOC_PPC_TLS:
+       case BFD_RELOC_PPC_TLSGD:
+       case BFD_RELOC_PPC_TLSLD:
+         fieldval = 0;
+         break;
+#endif
+
 #ifdef OBJ_XCOFF
-         fixP->fx_size = 2;
-         if (target_big_endian)
-           fixP->fx_where += 2;
+       case BFD_RELOC_PPC_B16:
+         /* Adjust the offset to the instruction boundary.  */
+         fieldval += 2;
+         break;
 #endif
+
+       default:
+         break;
        }
-#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
-      else if ((operand->flags & PPC_OPERAND_PARENS) != 0
-              && (operand->bitm & 0xfff0) == 0xfff0
-              && operand->shift == 0)
-       {
-         if (ppc_is_toc_sym (fixP->fx_addsy))
-           {
-             fixP->fx_r_type = BFD_RELOC_PPC_TOC16;
+
 #ifdef OBJ_ELF
-             if (ppc_obj64
-                 && (operand->flags & PPC_OPERAND_DS) != 0)
-               fixP->fx_r_type = BFD_RELOC_PPC64_TOC16_DS;
+/* powerpc uses RELA style relocs, so if emitting a reloc the field
+   contents can stay at zero.  */
+#define APPLY_RELOC fixP->fx_done
+#else
+#define APPLY_RELOC 1
 #endif
+      if ((fieldval != 0 && APPLY_RELOC) || operand->insert != NULL)
+       {
+         /* Fetch the instruction, insert the fully resolved operand
+            value, and stuff the instruction back again.  */
+         where = fixP->fx_frag->fr_literal + fixP->fx_where;
+         if (target_big_endian)
+           {
+             if (fixP->fx_size == 4)
+               insn = bfd_getb32 ((unsigned char *) where);
+             else
+               insn = bfd_getb16 ((unsigned char *) where);
            }
          else
            {
-             fixP->fx_r_type = BFD_RELOC_16;
-#ifdef OBJ_ELF
-             if (ppc_obj64
-                 && (operand->flags & PPC_OPERAND_DS) != 0)
-               fixP->fx_r_type = BFD_RELOC_PPC64_ADDR16_DS;
-#endif
+             if (fixP->fx_size == 4)
+               insn = bfd_getl32 ((unsigned char *) where);
+             else
+               insn = bfd_getl16 ((unsigned char *) where);
            }
-         fixP->fx_size = 2;
+         insn = ppc_insert_operand (insn, operand, fieldval,
+                                    fixP->tc_fix_data.ppc_cpu,
+                                    fixP->fx_file, fixP->fx_line);
          if (target_big_endian)
-           fixP->fx_where += 2;
+           {
+             if (fixP->fx_size == 4)
+               bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+             else
+               bfd_putb16 ((bfd_vma) insn, (unsigned char *) where);
+           }
+         else
+           {
+             if (fixP->fx_size == 4)
+               bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+             else
+               bfd_putl16 ((bfd_vma) insn, (unsigned char *) where);
+           }
        }
-#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
-      else
+
+      if (fixP->fx_done)
+       /* Nothing else to do here.  */
+       return;
+
+      gas_assert (fixP->fx_addsy != NULL);
+      if (fixP->fx_r_type == BFD_RELOC_UNUSED)
        {
          char *sfile;
          unsigned int sline;
@@ -5960,49 +6611,87 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     }
   else
     {
-#ifdef OBJ_ELF
-      ppc_elf_validate_fix (fixP, seg);
-#endif
+      /* Handle relocs in data.  */
       switch (fixP->fx_r_type)
        {
-       case BFD_RELOC_CTOR:
-         if (ppc_obj64)
-           goto ctor64;
-         /* fall through */
-
-       case BFD_RELOC_32:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_32_PCREL;
-         /* fall through */
+       case BFD_RELOC_VTABLE_INHERIT:
+         if (fixP->fx_addsy
+             && !S_IS_DEFINED (fixP->fx_addsy)
+             && !S_IS_WEAK (fixP->fx_addsy))
+           S_SET_WEAK (fixP->fx_addsy);
+         /* Fall thru */
 
-       case BFD_RELOC_RVA:
-       case BFD_RELOC_32_PCREL:
-       case BFD_RELOC_PPC_EMB_NADDR32:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 4);
+       case BFD_RELOC_VTABLE_ENTRY:
+         fixP->fx_done = 0;
          break;
 
-       case BFD_RELOC_64:
-       ctor64:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_64_PCREL;
-         /* fall through */
+#ifdef OBJ_ELF
+         /* These can appear with @l etc. in data.  */
+       case BFD_RELOC_LO16:
+       case BFD_RELOC_LO16_PCREL:
+       case BFD_RELOC_HI16:
+       case BFD_RELOC_HI16_PCREL:
+       case BFD_RELOC_HI16_S:
+       case BFD_RELOC_HI16_S_PCREL:
+       case BFD_RELOC_PPC64_HIGHER:
+       case BFD_RELOC_PPC64_HIGHER_S:
+       case BFD_RELOC_PPC64_HIGHEST:
+       case BFD_RELOC_PPC64_HIGHEST_S:
+         break;
 
-       case BFD_RELOC_64_PCREL:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 8);
+       case BFD_RELOC_PPC_DTPMOD:
+       case BFD_RELOC_PPC_TPREL:
+       case BFD_RELOC_PPC_DTPREL:
+         S_SET_THREAD_LOCAL (fixP->fx_addsy);
          break;
 
-       case BFD_RELOC_GPREL16:
-       case BFD_RELOC_16_GOT_PCREL:
+         /* Just punt all of these to the linker.  */
+       case BFD_RELOC_PPC_B16_BRTAKEN:
+       case BFD_RELOC_PPC_B16_BRNTAKEN:
        case BFD_RELOC_16_GOTOFF:
        case BFD_RELOC_LO16_GOTOFF:
        case BFD_RELOC_HI16_GOTOFF:
        case BFD_RELOC_HI16_S_GOTOFF:
+       case BFD_RELOC_LO16_PLTOFF:
+       case BFD_RELOC_HI16_PLTOFF:
+       case BFD_RELOC_HI16_S_PLTOFF:
+       case BFD_RELOC_PPC_COPY:
+       case BFD_RELOC_PPC_GLOB_DAT:
        case BFD_RELOC_16_BASEREL:
        case BFD_RELOC_LO16_BASEREL:
        case BFD_RELOC_HI16_BASEREL:
        case BFD_RELOC_HI16_S_BASEREL:
+       case BFD_RELOC_PPC_TLS:
+       case BFD_RELOC_PPC_DTPREL16_LO:
+       case BFD_RELOC_PPC_DTPREL16_HI:
+       case BFD_RELOC_PPC_DTPREL16_HA:
+       case BFD_RELOC_PPC_TPREL16_LO:
+       case BFD_RELOC_PPC_TPREL16_HI:
+       case BFD_RELOC_PPC_TPREL16_HA:
+       case BFD_RELOC_PPC_GOT_TLSGD16:
+       case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+       case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+       case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+       case BFD_RELOC_PPC_GOT_TLSLD16:
+       case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+       case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+       case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+       case BFD_RELOC_PPC_GOT_DTPREL16:
+       case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+       case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+       case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+       case BFD_RELOC_PPC_GOT_TPREL16:
+       case BFD_RELOC_PPC_GOT_TPREL16_LO:
+       case BFD_RELOC_PPC_GOT_TPREL16_HI:
+       case BFD_RELOC_PPC_GOT_TPREL16_HA:
+       case BFD_RELOC_24_PLT_PCREL:
+       case BFD_RELOC_PPC_LOCAL24PC:
+       case BFD_RELOC_32_PLT_PCREL:
+       case BFD_RELOC_GPREL16:
+       case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+       case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+       case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+       case BFD_RELOC_PPC_EMB_NADDR32:
        case BFD_RELOC_PPC_EMB_NADDR16:
        case BFD_RELOC_PPC_EMB_NADDR16_LO:
        case BFD_RELOC_PPC_EMB_NADDR16_HI:
@@ -6010,287 +6699,136 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        case BFD_RELOC_PPC_EMB_SDAI16:
        case BFD_RELOC_PPC_EMB_SDA2REL:
        case BFD_RELOC_PPC_EMB_SDA2I16:
+       case BFD_RELOC_PPC_EMB_SDA21:
+       case BFD_RELOC_PPC_VLE_SDA21_LO:
+       case BFD_RELOC_PPC_EMB_MRKREF:
        case BFD_RELOC_PPC_EMB_RELSEC16:
        case BFD_RELOC_PPC_EMB_RELST_LO:
        case BFD_RELOC_PPC_EMB_RELST_HI:
        case BFD_RELOC_PPC_EMB_RELST_HA:
+       case BFD_RELOC_PPC_EMB_BIT_FLD:
        case BFD_RELOC_PPC_EMB_RELSDA:
+       case BFD_RELOC_PPC64_TOC:
        case BFD_RELOC_PPC_TOC16:
-#ifdef OBJ_ELF
        case BFD_RELOC_PPC64_TOC16_LO:
        case BFD_RELOC_PPC64_TOC16_HI:
        case BFD_RELOC_PPC64_TOC16_HA:
-#endif
-         if (fixP->fx_pcrel)
-           {
-             if (fixP->fx_addsy != NULL)
-               as_bad_where (fixP->fx_file, fixP->fx_line,
-                             _("cannot emit PC relative %s relocation against %s"),
-                             bfd_get_reloc_code_name (fixP->fx_r_type),
-                             S_GET_NAME (fixP->fx_addsy));
-             else
-               as_bad_where (fixP->fx_file, fixP->fx_line,
-                             _("cannot emit PC relative %s relocation"),
-                             bfd_get_reloc_code_name (fixP->fx_r_type));
-           }
-
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 2);
+       case BFD_RELOC_PPC64_DTPREL16_HIGHER:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
+       case BFD_RELOC_PPC64_TPREL16_HIGHER:
+       case BFD_RELOC_PPC64_TPREL16_HIGHERA:
+       case BFD_RELOC_PPC64_TPREL16_HIGHEST:
+       case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+         fixP->fx_done = 0;
          break;
+#endif
 
+#ifdef OBJ_XCOFF
+       case BFD_RELOC_NONE:
+#endif
+       case BFD_RELOC_CTOR:
+       case BFD_RELOC_32:
+       case BFD_RELOC_32_PCREL:
+       case BFD_RELOC_RVA:
+       case BFD_RELOC_64:
+       case BFD_RELOC_64_PCREL:
        case BFD_RELOC_16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_16_PCREL;
-         /* fall through */
-
        case BFD_RELOC_16_PCREL:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 2);
+       case BFD_RELOC_8:
          break;
 
-       case BFD_RELOC_LO16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
-         /* fall through */
+       default:
+         fprintf (stderr,
+                  _("Gas failure, reloc value %d\n"), fixP->fx_r_type);
+         fflush (stderr);
+         abort ();
+       }
 
-       case BFD_RELOC_LO16_PCREL:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             value, 2);
+      if (fixP->fx_size && APPLY_RELOC)
+       md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+                           fieldval, fixP->fx_size);
+    }
+
+  /* We are only able to convert some relocs to pc-relative.  */
+  if (!fixP->fx_done && fixP->fx_pcrel)
+    {
+      switch (fixP->fx_r_type)
+       {
+       case BFD_RELOC_LO16:
+         fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
          break;
 
-         /* This case happens when you write, for example,
-            lis %r3,(L1-L2)@ha
-            where L1 and L2 are defined later.  */
        case BFD_RELOC_HI16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
-         /* fall through */
-
-       case BFD_RELOC_HI16_PCREL:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             PPC_HI (value), 2);
+         fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
          break;
 
        case BFD_RELOC_HI16_S:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
-         /* fall through */
-
-       case BFD_RELOC_HI16_S_PCREL:
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             PPC_HA (value), 2);
-         break;
-
-#ifdef OBJ_XCOFF
-       case BFD_RELOC_NONE:
+         fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
          break;
-#endif
 
-#ifdef OBJ_ELF
-       case BFD_RELOC_PPC64_HIGHER:
-         if (fixP->fx_pcrel)
-           abort ();
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             PPC_HIGHER (value), 2);
-         break;
-
-       case BFD_RELOC_PPC64_HIGHER_S:
-         if (fixP->fx_pcrel)
-           abort ();
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             PPC_HIGHERA (value), 2);
-         break;
-
-       case BFD_RELOC_PPC64_HIGHEST:
-         if (fixP->fx_pcrel)
-           abort ();
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             PPC_HIGHEST (value), 2);
+       case BFD_RELOC_64:
+         fixP->fx_r_type = BFD_RELOC_64_PCREL;
          break;
 
-       case BFD_RELOC_PPC64_HIGHEST_S:
-         if (fixP->fx_pcrel)
-           abort ();
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                             PPC_HIGHESTA (value), 2);
+       case BFD_RELOC_32:
+         fixP->fx_r_type = BFD_RELOC_32_PCREL;
          break;
 
-       case BFD_RELOC_PPC64_ADDR16_DS:
-       case BFD_RELOC_PPC64_ADDR16_LO_DS:
-       case BFD_RELOC_PPC64_GOT16_DS:
-       case BFD_RELOC_PPC64_GOT16_LO_DS:
-       case BFD_RELOC_PPC64_PLT16_LO_DS:
-       case BFD_RELOC_PPC64_SECTOFF_DS:
-       case BFD_RELOC_PPC64_SECTOFF_LO_DS:
-       case BFD_RELOC_PPC64_TOC16_DS:
-       case BFD_RELOC_PPC64_TOC16_LO_DS:
-       case BFD_RELOC_PPC64_PLTGOT16_DS:
-       case BFD_RELOC_PPC64_PLTGOT16_LO_DS:
-         if (fixP->fx_pcrel)
-           abort ();
-         {
-           char *where = fixP->fx_frag->fr_literal + fixP->fx_where;
-           unsigned long val, mask;
-
-           if (target_big_endian)
-             val = bfd_getb32 (where - 2);
-           else
-             val = bfd_getl32 (where);
-           mask = 0xfffc;
-           /* lq insns reserve the four lsbs.  */
-           if ((ppc_cpu & PPC_OPCODE_POWER4) != 0
-               && (val & (0x3f << 26)) == (56u << 26))
-             mask = 0xfff0;
-           val |= value & mask;
-           if (target_big_endian)
-             bfd_putb16 ((bfd_vma) val, where);
-           else
-             bfd_putl16 ((bfd_vma) val, where);
-         }
+       case BFD_RELOC_16:
+         fixP->fx_r_type = BFD_RELOC_16_PCREL;
          break;
 
+         /* Some of course are already pc-relative.  */
+       case BFD_RELOC_LO16_PCREL:
+       case BFD_RELOC_HI16_PCREL:
+       case BFD_RELOC_HI16_S_PCREL:
+       case BFD_RELOC_64_PCREL:
+       case BFD_RELOC_32_PCREL:
+       case BFD_RELOC_16_PCREL:
+       case BFD_RELOC_PPC_B16:
        case BFD_RELOC_PPC_B16_BRTAKEN:
        case BFD_RELOC_PPC_B16_BRNTAKEN:
-       case BFD_RELOC_PPC_BA16_BRTAKEN:
-       case BFD_RELOC_PPC_BA16_BRNTAKEN:
-         break;
-
-       case BFD_RELOC_PPC_TLS:
-       case BFD_RELOC_PPC_TLSGD:
-       case BFD_RELOC_PPC_TLSLD:
-         break;
-
-       case BFD_RELOC_PPC_DTPMOD:
-       case BFD_RELOC_PPC_TPREL16:
-       case BFD_RELOC_PPC_TPREL16_LO:
-       case BFD_RELOC_PPC_TPREL16_HI:
-       case BFD_RELOC_PPC_TPREL16_HA:
-       case BFD_RELOC_PPC_TPREL:
-       case BFD_RELOC_PPC_DTPREL16:
-       case BFD_RELOC_PPC_DTPREL16_LO:
-       case BFD_RELOC_PPC_DTPREL16_HI:
-       case BFD_RELOC_PPC_DTPREL16_HA:
-       case BFD_RELOC_PPC_DTPREL:
-       case BFD_RELOC_PPC_GOT_TLSGD16:
-       case BFD_RELOC_PPC_GOT_TLSGD16_LO:
-       case BFD_RELOC_PPC_GOT_TLSGD16_HI:
-       case BFD_RELOC_PPC_GOT_TLSGD16_HA:
-       case BFD_RELOC_PPC_GOT_TLSLD16:
-       case BFD_RELOC_PPC_GOT_TLSLD16_LO:
-       case BFD_RELOC_PPC_GOT_TLSLD16_HI:
-       case BFD_RELOC_PPC_GOT_TLSLD16_HA:
-       case BFD_RELOC_PPC_GOT_TPREL16:
-       case BFD_RELOC_PPC_GOT_TPREL16_LO:
-       case BFD_RELOC_PPC_GOT_TPREL16_HI:
-       case BFD_RELOC_PPC_GOT_TPREL16_HA:
-       case BFD_RELOC_PPC_GOT_DTPREL16:
-       case BFD_RELOC_PPC_GOT_DTPREL16_LO:
-       case BFD_RELOC_PPC_GOT_DTPREL16_HI:
-       case BFD_RELOC_PPC_GOT_DTPREL16_HA:
-       case BFD_RELOC_PPC64_TPREL16_DS:
-       case BFD_RELOC_PPC64_TPREL16_LO_DS:
-       case BFD_RELOC_PPC64_TPREL16_HIGHER:
-       case BFD_RELOC_PPC64_TPREL16_HIGHERA:
-       case BFD_RELOC_PPC64_TPREL16_HIGHEST:
-       case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
-       case BFD_RELOC_PPC64_DTPREL16_DS:
-       case BFD_RELOC_PPC64_DTPREL16_LO_DS:
-       case BFD_RELOC_PPC64_DTPREL16_HIGHER:
-       case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
-       case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
-       case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
-         S_SET_THREAD_LOCAL (fixP->fx_addsy);
-         break;
-#endif
-         /* Because SDA21 modifies the register field, the size is set to 4
-            bytes, rather than 2, so offset it here appropriately.  */
-       case BFD_RELOC_PPC_EMB_SDA21:
-         if (fixP->fx_pcrel)
-           abort ();
-
-         md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where
-                             + ((target_big_endian) ? 2 : 0),
-                             value, 2);
-         break;
-
-       case BFD_RELOC_8:
-         if (fixP->fx_pcrel)
-           {
-             /* This can occur if there is a bug in the input assembler, eg:
-                ".byte <undefined_symbol> - ."  */
-             if (fixP->fx_addsy)
-               as_bad (_("Unable to handle reference to symbol %s"),
-                       S_GET_NAME (fixP->fx_addsy));
-             else
-               as_bad (_("Unable to resolve expression"));
-             fixP->fx_done = 1;
-           }
-         else
-           md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                               value, 1);
-         break;
-
-       case BFD_RELOC_24_PLT_PCREL:
+       case BFD_RELOC_PPC_B26:
        case BFD_RELOC_PPC_LOCAL24PC:
-         if (!fixP->fx_pcrel && !fixP->fx_done)
-           abort ();
+       case BFD_RELOC_24_PLT_PCREL:
+       case BFD_RELOC_32_PLT_PCREL:
+       case BFD_RELOC_64_PLT_PCREL:
+       case BFD_RELOC_PPC_VLE_REL8:
+       case BFD_RELOC_PPC_VLE_REL15:
+       case BFD_RELOC_PPC_VLE_REL24:
+         break;
 
-         if (fixP->fx_done)
+       default:
+         if (fixP->fx_addsy)
            {
-             char *where;
-             unsigned long insn;
+             char *sfile;
+             unsigned int sline;
 
-             /* Fetch the instruction, insert the fully resolved operand
-                value, and stuff the instruction back again.  */
-             where = fixP->fx_frag->fr_literal + fixP->fx_where;
-             if (target_big_endian)
-               insn = bfd_getb32 ((unsigned char *) where);
-             else
-               insn = bfd_getl32 ((unsigned char *) where);
-             if ((value & 3) != 0)
-               as_bad_where (fixP->fx_file, fixP->fx_line,
-                             _("must branch to an address a multiple of 4"));
-             if ((offsetT) value < -0x40000000
-                 || (offsetT) value >= 0x40000000)
+             /* Use expr_symbol_where to see if this is an
+                expression symbol.  */
+             if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
                as_bad_where (fixP->fx_file, fixP->fx_line,
-                             _("@local or @plt branch destination is too far away, %ld bytes"),
-                             (long) value);
-             insn = insn | (value & 0x03fffffc);
-             if (target_big_endian)
-               bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+                             _("unresolved expression that must"
+                               " be resolved"));
              else
-               bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("cannot emit PC relative %s relocation"
+                               " against %s"),
+                             bfd_get_reloc_code_name (fixP->fx_r_type),
+                             S_GET_NAME (fixP->fx_addsy));
            }
+         else
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("unable to resolve expression"));
+         fixP->fx_done = 1;
          break;
-
-       case BFD_RELOC_VTABLE_INHERIT:
-         fixP->fx_done = 0;
-         if (fixP->fx_addsy
-             && !S_IS_DEFINED (fixP->fx_addsy)
-             && !S_IS_WEAK (fixP->fx_addsy))
-           S_SET_WEAK (fixP->fx_addsy);
-         break;
-
-       case BFD_RELOC_VTABLE_ENTRY:
-         fixP->fx_done = 0;
-         break;
-
-#ifdef OBJ_ELF
-         /* Generated by reference to `sym@tocbase'.  The sym is
-            ignored by the linker.  */
-       case BFD_RELOC_PPC64_TOC:
-         fixP->fx_done = 0;
-         break;
-#endif
-       default:
-         fprintf (stderr,
-                  _("Gas failure, reloc value %d\n"), fixP->fx_r_type);
-         fflush (stderr);
-         abort ();
        }
     }
 
 #ifdef OBJ_ELF
+  ppc_elf_validate_fix (fixP, seg);
   fixP->fx_addnumber = value;
 
   /* PowerPC uses RELA relocs, ie. the reloc addend is stored separately
@@ -6298,7 +6836,17 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
      then the section contents are immaterial, so don't warn if they
      happen to overflow.  Leave such warnings to ld.  */
   if (!fixP->fx_done)
-    fixP->fx_no_overflow = 1;
+    {
+      fixP->fx_no_overflow = 1;
+
+      /* Arrange to emit .TOC. as a normal symbol if used in anything
+        but .TOC.@tocbase.  */
+      if (ppc_obj64
+         && fixP->fx_r_type != BFD_RELOC_PPC64_TOC
+         && fixP->fx_addsy != NULL
+         && strcmp (S_GET_NAME (fixP->fx_addsy), ".TOC.") == 0)
+       symbol_get_bfdsym (fixP->fx_addsy)->flags |= BSF_KEEP;
+    }
 #else
   if (fixP->fx_r_type != BFD_RELOC_PPC_TOC16)
     fixP->fx_addnumber = 0;
This page took 0.057997 seconds and 4 git commands to generate.