use xstrdup and friends more
[deliverable/binutils-gdb.git] / gas / config / tc-ppc.c
index f8c5d350810af18b2e041773cf93d58ce368c8ac..23a84a78f6ee20cf0da85f2ed5e7d818121657c3 100644 (file)
@@ -1,6 +1,5 @@
 /* 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.
+   Copyright (C) 1994-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
    This file is part of GAS, the GNU Assembler.
@@ -28,6 +27,7 @@
 
 #ifdef OBJ_ELF
 #include "elf/ppc.h"
+#include "elf/ppc64.h"
 #include "dwarf2dbg.h"
 #endif
 
 #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.  */
@@ -80,7 +85,11 @@ static int set_target_endian = 0;
    compensating for #lo being treated as a signed number.  */
 #define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000)
 
-#define SEX16(val) ((((val) & 0xffff) ^ 0x8000) - 0x8000)
+#define SEX16(val) (((val) ^ 0x8000) - 0x8000)
+
+/* For the time being on ppc64, don't report overflow on @h and @ha
+   applied to constants.  */
+#define REPORT_OVERFLOW_HI 0
 
 static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
 
@@ -104,6 +113,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);
@@ -119,9 +129,10 @@ static void ppc_vbyte (int);
 #endif
 
 #ifdef OBJ_ELF
-static void ppc_elf_cons (int);
 static void ppc_elf_rdata (int);
 static void ppc_elf_lcomm (int);
+static void ppc_elf_localentry (int);
+static void ppc_elf_abiversion (int);
 #endif
 
 #ifdef TE_PE
@@ -180,15 +191,32 @@ 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;
+
+/* Value for ELF e_flags EF_PPC64_ABI.  */
+unsigned int ppc_abiversion = 0;
 
 /* Flags set on encountering toc relocs.  */
 enum {
   has_large_toc_reloc = 1,
   has_small_toc_reloc = 2
 } toc_reloc_types;
+
+/* Warn on emitting data to code sections.  */
+int warn_476;
+unsigned long last_insn;
+segT last_seg;
+subsegT last_subseg;
 \f
 /* The target specific pseudo-ops which we support.  */
 
@@ -210,6 +238,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 },
@@ -233,14 +262,12 @@ const pseudo_typeS md_pseudo_table[] =
 #endif
 
 #ifdef OBJ_ELF
-  { "llong",   ppc_elf_cons,   8 },
-  { "quad",    ppc_elf_cons,   8 },
-  { "long",    ppc_elf_cons,   4 },
-  { "word",    ppc_elf_cons,   2 },
-  { "short",   ppc_elf_cons,   2 },
+  { "llong",   cons,           8 },
   { "rdata",   ppc_elf_rdata,  0 },
   { "rodata",  ppc_elf_rdata,  0 },
   { "lcomm",   ppc_elf_lcomm,  0 },
+  { "localentry", ppc_elf_localentry,  0 },
+  { "abiversion", ppc_elf_abiversion,  0 },
 #endif
 
 #ifdef TE_PE
@@ -275,7 +302,7 @@ const pseudo_typeS md_pseudo_table[] =
 /* Structure to hold information about predefined registers.  */
 struct pd_reg
   {
-    char *name;
+    const char *name;
     int value;
   };
 
@@ -833,7 +860,7 @@ register_name (expressionS *expressionP)
   else if (!reg_names_p || !ISALPHA (name[0]))
     return FALSE;
 
-  c = get_symbol_end ();
+  c = get_symbol_name (&name);
   reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
 
   /* Put back the delimiting char.  */
@@ -978,6 +1005,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 +1053,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,13 +1068,17 @@ 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},
+  {"ppc476-workaround", no_argument, &warn_476, 1},
+  {"no-ppc476-workaround", no_argument, &warn_476, 0},
   {NULL, no_argument, NULL, 0}
 };
 const size_t md_longopts_size = sizeof (md_longopts);
 
 int
-md_parse_option (int c, char *arg)
+md_parse_option (int c, const char *arg)
 {
   ppc_cpu_t new_cpu;
 
@@ -1043,6 +1097,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 +1135,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 +1148,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 +1186,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 +1242,18 @@ 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;
+
+    case 0:
+      break;
+
     default:
       return 0;
     }
@@ -1199,7 +1281,8 @@ PowerPC options:\n\
 -m476                   generate code for PowerPC 476\n\
 -m7400, -m7410, -m7450, -m7455\n\
                         generate code for PowerPC 7400/7410/7450/7455\n\
--m750cl                 generate code for PowerPC 750cl\n"));
+-m750cl                 generate code for PowerPC 750cl\n\
+-m821, -m850, -m860     generate code for PowerPC 821/850/860\n"));
   fprintf (stream, _("\
 -mppc64, -m620          generate code for PowerPC 620/625/630\n\
 -mppc64bridge           generate code for PowerPC 64, including bridge insns\n\
@@ -1210,17 +1293,23 @@ 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\
+-mpower9, -mpwr9        generate code for Power9 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 +1318,19 @@ 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\
+-ppc476-workaround      warn if emitting data to code sections\n"));
 }
 \f
 /* Set ppc_cpu if it is not already set.  */
@@ -1262,7 +1355,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 +1371,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 +1383,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,11 +1396,13 @@ 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;
 }
 
-extern char*
+extern const char*
 ppc_target_format (void)
 {
 #ifdef OBJ_COFF
@@ -1315,14 +1412,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 +1431,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 != (int) 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 +1503,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 +1539,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 +1632,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.  */
@@ -1525,6 +1669,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.  */
@@ -1629,7 +1774,7 @@ ppc_insert_operand (unsigned long insn,
                    const struct powerpc_operand *operand,
                    offsetT val,
                    ppc_cpu_t cpu,
-                   char *file,
+                   const char *file,
                    unsigned int line)
 {
   long min, max, right;
@@ -1638,10 +1783,23 @@ ppc_insert_operand (unsigned long insn,
   right = max & -max;
   min = 0;
 
-  if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+  if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0)
+    {
+      /* Extend the allowed range for addis to [-65536, 65535].
+        Similarly for some VLE high part insns.  For 64-bit it
+        would be good to disable this for signed fields since the
+        value is sign extended into the high 32 bits of the register.
+        If the value is, say, an address, then we might care about
+        the high bits.  However, gcc as of 2014-06 uses unsigned
+        values when loading the high part of 64-bit constants using
+        lis.
+        Use the same extended range for cmpli, to allow at least
+        [-32768, 65535].  */
+      min = ~max & -right;
+    }
+  else if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
     {
-      if ((operand->flags & PPC_OPERAND_SIGNOPT) == 0)
-       max = (max >> 1) & -right;
+      max = (max >> 1) & -right;
       min = ~max & -right;
     }
 
@@ -1691,8 +1849,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;
 }
@@ -1704,7 +1864,7 @@ static bfd_reloc_code_real_type
 ppc_elf_suffix (char **str_p, expressionS *exp_p)
 {
   struct map_bfd {
-    char *string;
+    const char *string;
     unsigned int length : 8;
     unsigned int valid32 : 1;
     unsigned int valid64 : 1;
@@ -1774,6 +1934,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 +1946,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),
@@ -1791,6 +1955,8 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP32 ("bitfld",           BFD_RELOC_PPC_EMB_BIT_FLD),
     MAP32 ("relsda",           BFD_RELOC_PPC_EMB_RELSDA),
     MAP32 ("xgot",             BFD_RELOC_PPC_TOC16),
+    MAP64 ("high",             BFD_RELOC_PPC64_ADDR16_HIGH),
+    MAP64 ("higha",            BFD_RELOC_PPC64_ADDR16_HIGHA),
     MAP64 ("higher",           BFD_RELOC_PPC64_HIGHER),
     MAP64 ("highera",          BFD_RELOC_PPC64_HIGHER_S),
     MAP64 ("highest",          BFD_RELOC_PPC64_HIGHEST),
@@ -1800,19 +1966,24 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP64 ("toc@l",            BFD_RELOC_PPC64_TOC16_LO),
     MAP64 ("toc@h",            BFD_RELOC_PPC64_TOC16_HI),
     MAP64 ("toc@ha",           BFD_RELOC_PPC64_TOC16_HA),
+    MAP64 ("dtprel@high",      BFD_RELOC_PPC64_DTPREL16_HIGH),
+    MAP64 ("dtprel@higha",     BFD_RELOC_PPC64_DTPREL16_HIGHA),
     MAP64 ("dtprel@higher",    BFD_RELOC_PPC64_DTPREL16_HIGHER),
     MAP64 ("dtprel@highera",   BFD_RELOC_PPC64_DTPREL16_HIGHERA),
     MAP64 ("dtprel@highest",   BFD_RELOC_PPC64_DTPREL16_HIGHEST),
     MAP64 ("dtprel@highesta",  BFD_RELOC_PPC64_DTPREL16_HIGHESTA),
+    MAP64 ("localentry",       BFD_RELOC_PPC64_ADDR64_LOCAL),
+    MAP64 ("tprel@high",       BFD_RELOC_PPC64_TPREL16_HIGH),
+    MAP64 ("tprel@higha",      BFD_RELOC_PPC64_TPREL16_HIGHA),
     MAP64 ("tprel@higher",     BFD_RELOC_PPC64_TPREL16_HIGHER),
     MAP64 ("tprel@highera",    BFD_RELOC_PPC64_TPREL16_HIGHERA),
     MAP64 ("tprel@highest",    BFD_RELOC_PPC64_TPREL16_HIGHEST),
     MAP64 ("tprel@highesta",   BFD_RELOC_PPC64_TPREL16_HIGHESTA),
-    { (char *) 0, 0, 0, 0,     BFD_RELOC_UNUSED }
+    { (char *) 0, 0, 0, 0,     BFD_RELOC_NONE }
   };
 
   if (*str++ != '@')
-    return BFD_RELOC_UNUSED;
+    return BFD_RELOC_NONE;
 
   for (ch = *str, str2 = ident;
        (str2 < ident + sizeof (ident) - 1
@@ -1898,64 +2069,49 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
        return (bfd_reloc_code_real_type) reloc;
       }
 
-  return BFD_RELOC_UNUSED;
+  return BFD_RELOC_NONE;
 }
 
-/* Like normal .long/.short/.word, except support @got, etc.
-   Clobbers input_line_pointer, checks end-of-line.  */
-static void
-ppc_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long, 8=.llong */)
+/* Support @got, etc. on constants emitted via .short, .int etc.  */
+
+bfd_reloc_code_real_type
+ppc_elf_parse_cons (expressionS *exp, unsigned int nbytes)
 {
-  expressionS exp;
-  bfd_reloc_code_real_type reloc;
+  expression (exp);
+  if (nbytes >= 2 && *input_line_pointer == '@')
+    return ppc_elf_suffix (&input_line_pointer, exp);
+  return BFD_RELOC_NONE;
+}
 
-  if (is_it_end_of_statement ())
-    {
-      demand_empty_rest_of_line ();
-      return;
-    }
+/* Warn when emitting data to code sections, unless we are emitting
+   a relocation that ld --ppc476-workaround uses to recognise data
+   *and* there was an unconditional branch prior to the data.  */
 
-  do
+void
+ppc_elf_cons_fix_check (expressionS *exp ATTRIBUTE_UNUSED,
+                       unsigned int nbytes, fixS *fix)
+{
+  if (warn_476
+      && (now_seg->flags & SEC_CODE) != 0
+      && (nbytes != 4
+         || fix == NULL
+         || !(fix->fx_r_type == BFD_RELOC_32
+              || fix->fx_r_type == BFD_RELOC_CTOR
+              || fix->fx_r_type == BFD_RELOC_32_PCREL)
+         || !(last_seg == now_seg && last_subseg == now_subseg)
+         || !((last_insn & (0x3f << 26)) == (18u << 26)
+              || ((last_insn & (0x3f << 26)) == (16u << 26)
+                  && (last_insn & (0x14 << 21)) == (0x14 << 21))
+              || ((last_insn & (0x3f << 26)) == (19u << 26)
+                  && (last_insn & (0x3ff << 1)) == (16u << 1)
+                  && (last_insn & (0x14 << 21)) == (0x14 << 21)))))
     {
-      expression (&exp);
-      if (exp.X_op == O_symbol
-         && *input_line_pointer == '@'
-         && (reloc = ppc_elf_suffix (&input_line_pointer,
-                                     &exp)) != BFD_RELOC_UNUSED)
-       {
-         reloc_howto_type *reloc_howto;
-         int size;
-
-         reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
-         size = bfd_get_reloc_size (reloc_howto);
+      /* Flag that we've warned.  */
+      if (fix != NULL)
+       fix->fx_tcbit = 1;
 
-         if (size > nbytes)
-           {
-             as_bad (_("%s relocations do not fit in %d bytes\n"),
-                     reloc_howto->name, nbytes);
-           }
-         else
-           {
-             char *p;
-             int offset;
-
-             p = frag_more (nbytes);
-             memset (p, 0, nbytes);
-             offset = 0;
-             if (target_big_endian)
-               offset = nbytes - size;
-             fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
-                          &exp, 0, reloc);
-           }
-       }
-      else
-       emit_expr (&exp, (unsigned int) nbytes);
+      as_warn (_("data in executable section"));
     }
-  while (*input_line_pointer++ == ',');
-
-  /* Put terminator back into stream.  */
-  input_line_pointer--;
-  demand_empty_rest_of_line ();
 }
 
 /* Solaris pseduo op to change to the .rodata section.  */
@@ -1987,16 +2143,15 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
   char *pfrag;
   int align2;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (&name);
 
-  /* just after name is now '\0'.  */
+  /* Just after name is now '\0'.  */
   p = input_line_pointer;
   *p = c;
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
   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 +2184,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 +2192,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 +2210,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;
        }
@@ -2064,7 +2219,7 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
     align2 = 0;
 
   record_alignment (bss_section, align2);
-  subseg_set (bss_section, 0);
+  subseg_set (bss_section, 1);
   if (align2)
     frag_align (align2, 0, 0);
   if (S_GET_SEGMENT (symbolP) == bss_section)
@@ -2079,6 +2234,101 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* Pseudo op to set symbol local entry point.  */
+static void
+ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED)
+{
+  char *name;
+  char c = get_symbol_name (&name);
+  char *p;
+  expressionS exp;
+  symbolS *sym;
+  asymbol *bfdsym;
+  elf_symbol_type *elfsym;
+
+  p = input_line_pointer;
+  *p = c;
+  SKIP_WHITESPACE_AFTER_NAME ();
+  if (*input_line_pointer != ',')
+    {
+      *p = 0;
+      as_bad (_("expected comma after name `%s' in .localentry directive"),
+             name);
+      *p = c;
+      ignore_rest_of_line ();
+      return;
+    }
+  input_line_pointer++;
+  expression (&exp);
+  if (exp.X_op == O_absent)
+    {
+      as_bad (_("missing expression in .localentry directive"));
+      exp.X_op = O_constant;
+      exp.X_add_number = 0;
+    }
+  *p = 0;
+  sym = symbol_find_or_make (name);
+  *p = c;
+
+  if (resolve_expression (&exp)
+      && exp.X_op == O_constant)
+    {
+      unsigned char encoded = PPC64_SET_LOCAL_ENTRY_OFFSET (exp.X_add_number);
+
+      if (exp.X_add_number != (offsetT) PPC64_LOCAL_ENTRY_OFFSET (encoded))
+        as_bad (_(".localentry expression for `%s' "
+                 "is not a valid power of 2"), S_GET_NAME (sym));
+      else
+       {
+         bfdsym = symbol_get_bfdsym (sym);
+         elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         gas_assert (elfsym);
+         elfsym->internal_elf_sym.st_other &= ~STO_PPC64_LOCAL_MASK;
+         elfsym->internal_elf_sym.st_other |= encoded;
+         if (ppc_abiversion == 0)
+           ppc_abiversion = 2;
+       }
+    }
+  else
+    as_bad (_(".localentry expression for `%s' "
+             "does not evaluate to a constant"), S_GET_NAME (sym));
+
+  demand_empty_rest_of_line ();
+}
+
+/* Pseudo op to set ABI version.  */
+static void
+ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+
+  expression (&exp);
+  if (exp.X_op == O_absent)
+    {
+      as_bad (_("missing expression in .abiversion directive"));
+      exp.X_op = O_constant;
+      exp.X_add_number = 0;
+    }
+
+  if (resolve_expression (&exp)
+      && exp.X_op == O_constant)
+    ppc_abiversion = exp.X_add_number;
+  else
+    as_bad (_(".abiversion expression does not evaluate to a constant"));
+  demand_empty_rest_of_line ();
+}
+
+/* Set ABI version in output file.  */
+void
+ppc_elf_end (void)
+{
+  if (ppc_obj64 && ppc_abiversion != 0)
+    {
+      elf_elfheader (stdoutput)->e_flags &= ~EF_PPC64_ABI;
+      elf_elfheader (stdoutput)->e_flags |= ppc_abiversion & EF_PPC64_ABI;
+    }
+}
+
 /* Validate any relocations emitted for -mrelocatable, possibly adding
    fixups for word relocations in writable segments, so we can adjust
    them at runtime.  */
@@ -2095,8 +2345,7 @@ ppc_elf_validate_fix (fixS *fixp, segT seg)
       return;
 
     case SHLIB_MRELOCATABLE:
-      if (fixp->fx_r_type <= BFD_RELOC_UNUSED
-         && fixp->fx_r_type != BFD_RELOC_16_GOTOFF
+      if (fixp->fx_r_type != BFD_RELOC_16_GOTOFF
          && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF
          && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF
          && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF
@@ -2117,7 +2366,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;
@@ -2141,7 +2390,6 @@ ppc_frob_file_before_adjust (void)
       const char *name;
       char *dotname;
       symbolS *dotsym;
-      size_t len;
 
       name = S_GET_NAME (symp);
       if (name[0] == '.')
@@ -2151,10 +2399,7 @@ ppc_frob_file_before_adjust (void)
          || S_IS_DEFINED (symp))
        continue;
 
-      len = strlen (name) + 1;
-      dotname = xmalloc (len + 1);
-      dotname[0] = '.';
-      memcpy (dotname + 1, name, len);
+      dotname = concat (".", name, (char *) NULL);
       dotsym = symbol_find_noref (dotname, 1);
       free (dotname);
       if (dotsym != NULL && (symbol_used_p (dotsym)
@@ -2168,11 +2413,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
@@ -2227,8 +2487,7 @@ parse_toc_entry (enum toc_size_qualifier *toc_kind)
   SKIP_WHITESPACE ();
 
   /* Find the spelling of the operand.  */
-  toc_spec = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (&toc_spec);
 
   if (strcmp (toc_spec, "toc") == 0)
     {
@@ -2257,7 +2516,7 @@ parse_toc_entry (enum toc_size_qualifier *toc_kind)
   /* Now find the ']'.  */
   *input_line_pointer = c;
 
-  SKIP_WHITESPACE ();       /* leading whitespace could be there.  */
+  SKIP_WHITESPACE_AFTER_NAME ();       /* leading whitespace could be there.  */
   c = *input_line_pointer++; /* input_line_pointer->past char in c.  */
 
   if (c != ']')
@@ -2271,6 +2530,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 +2598,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 +2631,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 +2647,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);
 
@@ -2455,12 +2748,18 @@ md_assemble (char *str)
       if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
          && skip_optional)
        {
+         long val = ppc_optional_operand_value (operand);
          if (operand->insert)
            {
-             insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
+             insn = (*operand->insert) (insn, val, ppc_cpu, &errmsg);
              if (errmsg != (const char *) NULL)
                as_bad ("%s", errmsg);
            }
+         else if (operand->shift >= 0)
+           insn |= ((long) val & operand->bitm) << operand->shift;
+         else
+           insn |= ((long) val & operand->bitm) >> -operand->shift;
+
          if ((operand->flags & PPC_OPERAND_NEXT) != 0)
            next_opindex = *opindex_ptr + 1;
          continue;
@@ -2507,9 +2806,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 ']'.  */
@@ -2548,13 +2846,13 @@ md_assemble (char *str)
              /* FIXME: these next two specifically specify 32/64 bit
                 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"));
+             toc_reloc = BFD_RELOC_NONE;
+             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"));
+             toc_reloc = BFD_RELOC_NONE;
+             as_bad (_("unimplemented toc64 expression modifier"));
              break;
            default:
              fprintf (stderr,
@@ -2585,12 +2883,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,9 +2918,10 @@ 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)
+         if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
            switch (reloc)
              {
              default:
@@ -2627,64 +2929,86 @@ md_assemble (char *str)
                break;
 
              case BFD_RELOC_LO16:
-               /* X_unsigned is the default, so if the user has done
-                  something which cleared it, we always produce a
-                  signed value.  */
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number &= 0xffff;
-               else
+               ex.X_add_number &= 0xffff;
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
                  ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_HI16:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HI (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HI (ex.X_add_number));
+               if (REPORT_OVERFLOW_HI && ppc_obj64)
+                 {
+                   /* PowerPC64 @h is tested for overflow.  */
+                   ex.X_add_number = (addressT) ex.X_add_number >> 16;
+                   if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                     {
+                       addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
+                       ex.X_add_number
+                         = ((addressT) ex.X_add_number ^ sign) - sign;
+                     }
+                   break;
+                 }
+               /* Fall thru */
+
+             case BFD_RELOC_PPC64_ADDR16_HIGH:
+               ex.X_add_number = PPC_HI (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_HI16_S:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HA (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HA (ex.X_add_number));
+               if (REPORT_OVERFLOW_HI && ppc_obj64)
+                 {
+                   /* PowerPC64 @ha is tested for overflow.  */
+                   ex.X_add_number
+                     = ((addressT) ex.X_add_number + 0x8000) >> 16;
+                   if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                     {
+                       addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
+                       ex.X_add_number
+                         = ((addressT) ex.X_add_number ^ sign) - sign;
+                     }
+                   break;
+                 }
+               /* Fall thru */
+
+             case BFD_RELOC_PPC64_ADDR16_HIGHA:
+               ex.X_add_number = PPC_HA (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHER:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHER (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHER (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHER (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHER_S:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHERA (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHEST:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHEST (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHEST_S:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHESTA (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
              }
 #endif /* OBJ_ELF */
          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_NONE;
+#ifdef OBJ_ELF
          if (ex.X_op == O_symbol && str[0] == '(')
            {
              const char *sym_name = S_GET_NAME (ex.X_add_symbol);
@@ -2700,7 +3024,7 @@ md_assemble (char *str)
                  expression (&tls_exp);
                  if (tls_exp.X_op == O_symbol)
                    {
-                     reloc = BFD_RELOC_UNUSED;
+                     reloc = BFD_RELOC_NONE;
                      if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0)
                        {
                          reloc = BFD_RELOC_PPC_TLSGD;
@@ -2711,7 +3035,7 @@ md_assemble (char *str)
                          reloc = BFD_RELOC_PPC_TLSLD;
                          input_line_pointer += 7;
                        }
-                     if (reloc != BFD_RELOC_UNUSED)
+                     if (reloc != BFD_RELOC_NONE)
                        {
                          SKIP_WHITESPACE ();
                          str = input_line_pointer;
@@ -2728,7 +3052,7 @@ md_assemble (char *str)
                }
            }
 
-         if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED)
+         if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
            {
              /* Some TLS tweaks.  */
              switch (reloc)
@@ -2758,104 +3082,227 @@ md_assemble (char *str)
                  break;
                }
 
-             /* For the absolute forms of branches, convert the PC
-                relative form back into the absolute.  */
-             if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+             /* addpcis.  */
+             if (opcode->opcode == (19 << 26) + (2 << 1)
+                 && reloc == BFD_RELOC_HI16_S)
+               reloc = BFD_RELOC_PPC_REL16DX_HA;
+
+             /* 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)
                    {
-                   case BFD_RELOC_PPC_B26:
-                     reloc = BFD_RELOC_PPC_BA26;
+                   default:
                      break;
-                   case BFD_RELOC_PPC_B16:
-                     reloc = BFD_RELOC_PPC_BA16;
+
+                   case BFD_RELOC_PPC_EMB_SDA21:
+                     reloc = BFD_RELOC_PPC_VLE_SDA21;
                      break;
-                   case BFD_RELOC_PPC_B16_BRTAKEN:
-                     reloc = BFD_RELOC_PPC_BA16_BRTAKEN;
+
+                   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_PPC_B16_BRNTAKEN:
-                     reloc = BFD_RELOC_PPC_BA16_BRNTAKEN;
+
+                   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;
-                   default:
+
+                   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;
                    }
                }
+           }
+#endif /* OBJ_ELF */
 
+         if (reloc != BFD_RELOC_NONE)
+           ;
+         /* 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
+                                     | PPC_OPERAND_ABSOLUTE)) != 0
+                  && operand->bitm == 0x3fffffc
+                  && operand->shift == 0)
+           reloc = BFD_RELOC_PPC_B26;
+         else if ((operand->flags & (PPC_OPERAND_RELATIVE
+                                     | PPC_OPERAND_ABSOLUTE)) != 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_NEGATIVE) == 0
+                  && (operand->bitm & 0xfff0) == 0xfff0
+                  && operand->shift == 0)
+           {
+             reloc = BFD_RELOC_16;
+#if defined OBJ_XCOFF || defined OBJ_ELF
+             /* Note: the symbol may be not yet defined.  */
+             if ((operand->flags & PPC_OPERAND_PARENS) != 0
+                 && ppc_is_toc_sym (ex.X_add_symbol))
+               {
+                 reloc = BFD_RELOC_PPC_TOC16;
+#ifdef OBJ_ELF
+                 as_warn (_("assuming %s on symbol"),
+                          ppc_obj64 ? "@toc" : "@xgot");
+#endif
+               }
+#endif
+           }
+
+         /* For the absolute forms of branches, convert the PC
+            relative form back into the absolute.  */
+         if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+           {
              switch (reloc)
                {
-               case BFD_RELOC_PPC_TOC16:
-                 toc_reloc_types |= has_small_toc_reloc;
+               case BFD_RELOC_PPC_B26:
+                 reloc = BFD_RELOC_PPC_BA26;
                  break;
-               case BFD_RELOC_PPC64_TOC16_LO:
-               case BFD_RELOC_PPC64_TOC16_HI:
-               case BFD_RELOC_PPC64_TOC16_HA:
-                 toc_reloc_types |= has_large_toc_reloc;
+               case BFD_RELOC_PPC_B16:
+                 reloc = BFD_RELOC_PPC_BA16;
+                 break;
+#ifdef OBJ_ELF
+               case BFD_RELOC_PPC_B16_BRTAKEN:
+                 reloc = BFD_RELOC_PPC_BA16_BRTAKEN;
+                 break;
+               case BFD_RELOC_PPC_B16_BRNTAKEN:
+                 reloc = BFD_RELOC_PPC_BA16_BRNTAKEN;
                  break;
+#endif
                default:
                  break;
                }
+           }
 
-             if (ppc_obj64
-                 && (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
+#ifdef OBJ_ELF
+         switch (reloc)
+           {
+           case BFD_RELOC_PPC_TOC16:
+             toc_reloc_types |= has_small_toc_reloc;
+             break;
+           case BFD_RELOC_PPC64_TOC16_LO:
+           case BFD_RELOC_PPC64_TOC16_HI:
+           case BFD_RELOC_PPC64_TOC16_HA:
+             toc_reloc_types |= has_large_toc_reloc;
+             break;
+           default:
+             break;
+           }
+
+         if (ppc_obj64
+             && (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
+           {
+             switch (reloc)
                {
-                 switch (reloc)
-                   {
-                   case BFD_RELOC_16:
-                     reloc = BFD_RELOC_PPC64_ADDR16_DS;
-                     break;
-                   case BFD_RELOC_LO16:
-                     reloc = BFD_RELOC_PPC64_ADDR16_LO_DS;
-                     break;
-                   case BFD_RELOC_16_GOTOFF:
-                     reloc = BFD_RELOC_PPC64_GOT16_DS;
-                     break;
-                   case BFD_RELOC_LO16_GOTOFF:
-                     reloc = BFD_RELOC_PPC64_GOT16_LO_DS;
-                     break;
-                   case BFD_RELOC_LO16_PLTOFF:
-                     reloc = BFD_RELOC_PPC64_PLT16_LO_DS;
-                     break;
-                   case BFD_RELOC_16_BASEREL:
-                     reloc = BFD_RELOC_PPC64_SECTOFF_DS;
-                     break;
-                   case BFD_RELOC_LO16_BASEREL:
-                     reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_TOC16:
-                     reloc = BFD_RELOC_PPC64_TOC16_DS;
-                     break;
-                   case BFD_RELOC_PPC64_TOC16_LO:
-                     reloc = BFD_RELOC_PPC64_TOC16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC64_PLTGOT16:
-                     reloc = BFD_RELOC_PPC64_PLTGOT16_DS;
-                     break;
-                   case BFD_RELOC_PPC64_PLTGOT16_LO:
-                     reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_DTPREL16:
-                     reloc = BFD_RELOC_PPC64_DTPREL16_DS;
-                     break;
-                   case BFD_RELOC_PPC_DTPREL16_LO:
-                     reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_TPREL16:
-                     reloc = BFD_RELOC_PPC64_TPREL16_DS;
-                     break;
-                   case BFD_RELOC_PPC_TPREL16_LO:
-                     reloc = BFD_RELOC_PPC64_TPREL16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_GOT_DTPREL16:
-                   case BFD_RELOC_PPC_GOT_DTPREL16_LO:
-                   case BFD_RELOC_PPC_GOT_TPREL16:
-                   case BFD_RELOC_PPC_GOT_TPREL16_LO:
-                     break;
-                   default:
-                     as_bad (_("unsupported relocation for DS offset field"));
-                     break;
-                   }
+               case BFD_RELOC_16:
+                 reloc = BFD_RELOC_PPC64_ADDR16_DS;
+                 break;
+               case BFD_RELOC_LO16:
+                 reloc = BFD_RELOC_PPC64_ADDR16_LO_DS;
+                 break;
+               case BFD_RELOC_16_GOTOFF:
+                 reloc = BFD_RELOC_PPC64_GOT16_DS;
+                 break;
+               case BFD_RELOC_LO16_GOTOFF:
+                 reloc = BFD_RELOC_PPC64_GOT16_LO_DS;
+                 break;
+               case BFD_RELOC_LO16_PLTOFF:
+                 reloc = BFD_RELOC_PPC64_PLT16_LO_DS;
+                 break;
+               case BFD_RELOC_16_BASEREL:
+                 reloc = BFD_RELOC_PPC64_SECTOFF_DS;
+                 break;
+               case BFD_RELOC_LO16_BASEREL:
+                 reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_TOC16:
+                 reloc = BFD_RELOC_PPC64_TOC16_DS;
+                 break;
+               case BFD_RELOC_PPC64_TOC16_LO:
+                 reloc = BFD_RELOC_PPC64_TOC16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC64_PLTGOT16:
+                 reloc = BFD_RELOC_PPC64_PLTGOT16_DS;
+                 break;
+               case BFD_RELOC_PPC64_PLTGOT16_LO:
+                 reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_DTPREL16:
+                 reloc = BFD_RELOC_PPC64_DTPREL16_DS;
+                 break;
+               case BFD_RELOC_PPC_DTPREL16_LO:
+                 reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_TPREL16:
+                 reloc = BFD_RELOC_PPC64_TPREL16_DS;
+                 break;
+               case BFD_RELOC_PPC_TPREL16_LO:
+                 reloc = BFD_RELOC_PPC64_TPREL16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_GOT_DTPREL16:
+               case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+               case BFD_RELOC_PPC_GOT_TPREL16:
+               case BFD_RELOC_PPC_GOT_TPREL16_LO:
+                 break;
+               default:
+                 as_bad (_("unsupported relocation for DS offset field"));
+                 break;
                }
            }
+#endif
 
          /* We need to generate a fixup for this expression.  */
          if (fc >= MAX_INSN_FIXUPS)
@@ -2865,18 +3312,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)
        {
@@ -2922,8 +3357,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)
@@ -2940,46 +3376,67 @@ 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);
+      /* Only set the VLE flag if the instruction has been pulled via
+         the VLE instruction set.  This way the flag is guaranteed to
+         be set for VLE-only instructions or for VLE-only processors,
+         however it'll remain clear for dual-mode instructions on
+         dual-mode and, more importantly, standard-mode processors.  */
+      if ((ppc_cpu & 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);
+  last_insn = insn;
+  last_seg = now_seg;
+  last_subseg = now_subseg;
 
 #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++)
     {
-      if (fixups[i].reloc != BFD_RELOC_UNUSED)
+      fixS *fixP;
+      if (fixups[i].reloc != BFD_RELOC_NONE)
        {
          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;
-
-         if (size < 1 || size > 4)
-           abort ();
+         offset = target_big_endian ? (insn_length - size) : 0;
 
          fixP = fix_new_exp (frag_now,
                              f - frag_now->fr_literal + offset,
@@ -2987,41 +3444,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_NONE);
        }
+      fixP->fx_pcrel_adjust = fixups[i].opindex;
     }
 }
 
@@ -3083,7 +3519,7 @@ ppc_macro (char *str, const struct powerpc_macro *macro)
     }
 
   /* Put the string together.  */
-  complete = s = (char *) alloca (len + 1);
+  complete = s = (char *) xmalloc (len + 1);
   format = macro->format;
   while (*format != '\0')
     {
@@ -3101,6 +3537,7 @@ ppc_macro (char *str, const struct powerpc_macro *macro)
 
   /* Assemble the constructed instruction.  */
   md_assemble (complete);
+  free (complete);
 }
 \f
 #ifdef OBJ_ELF
@@ -3134,6 +3571,8 @@ ppc_section_flags (flagword flags, bfd_vma attr ATTRIBUTE_UNUSED, int type)
 static void
 ppc_byte (int ignore ATTRIBUTE_UNUSED)
 {
+  int count = 0;
+
   if (*input_line_pointer != '\"')
     {
       cons (1);
@@ -3157,8 +3596,11 @@ ppc_byte (int ignore ATTRIBUTE_UNUSED)
        }
 
       FRAG_APPEND_1_CHAR (c);
+      ++count;
     }
 
+  if (warn_476 && count != 0 && (now_seg->flags & SEC_CODE) != 0)
+    as_warn (_("data in executable section"));
   demand_empty_rest_of_line ();
 }
 \f
@@ -3189,10 +3631,9 @@ ppc_comm (int lcomm)
   symbolS *sym;
   char *pfrag;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
   end_name = input_line_pointer;
-  *end_name = endc;
+  (void) restore_line_pointer (endc);
 
   if (*input_line_pointer != ',')
     {
@@ -3231,11 +3672,6 @@ ppc_comm (int lcomm)
       char *lcomm_name;
       char lcomm_endc;
 
-      if (size <= 4)
-       align = 2;
-      else
-       align = 3;
-
       /* The third argument to .lcomm appears to be the real local
         common symbol to create.  References to the symbol named in
         the first argument are turned into references to the third
@@ -3248,12 +3684,30 @@ ppc_comm (int lcomm)
        }
       ++input_line_pointer;
 
-      lcomm_name = input_line_pointer;
-      lcomm_endc = get_symbol_end ();
+      lcomm_endc = get_symbol_name (&lcomm_name);
 
       lcomm_sym = symbol_find_or_make (lcomm_name);
 
-      *input_line_pointer = lcomm_endc;
+      (void) restore_line_pointer (lcomm_endc);
+
+      /* The fourth argument to .lcomm is the alignment.  */
+      if (*input_line_pointer != ',')
+       {
+         if (size <= 4)
+           align = 2;
+         else
+           align = 3;
+       }
+      else
+       {
+         ++input_line_pointer;
+         align = get_absolute_expression ();
+         if (align <= 0)
+           {
+             as_warn (_("ignoring bad alignment"));
+             align = 2;
+           }
+       }
     }
 
   *end_name = '\0';
@@ -3337,12 +3791,11 @@ ppc_csect (int ignore ATTRIBUTE_UNUSED)
   symbolS *sym;
   offsetT align;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   sym = symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   if (S_GET_NAME (sym)[0] == '\0')
     {
@@ -3460,6 +3913,157 @@ 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 == ',')
+    {
+      char *label;
+      char c;
+
+      ++input_line_pointer;
+
+      c = get_symbol_name (&label);
+      opt_label = symbol_find_or_make (label);
+      (void) restore_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.  */
@@ -3496,8 +4100,7 @@ ppc_named_section (int ignore ATTRIBUTE_UNUSED)
   char c;
   symbolS *sym;
 
-  user_name = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (&user_name);
 
   if (strcmp (user_name, ".text") == 0)
     real_name = ".text[PR]";
@@ -3505,13 +4108,13 @@ ppc_named_section (int ignore ATTRIBUTE_UNUSED)
     real_name = ".data[RW]";
   else
     {
-      as_bad (_("The XCOFF file format does not support arbitrary sections"));
-      *input_line_pointer = c;
+      as_bad (_("the XCOFF file format does not support arbitrary sections"));
+      (void) restore_line_pointer (c);
       ignore_rest_of_line ();
       return;
     }
 
-  *input_line_pointer = c;
+  (void) restore_line_pointer (c);
 
   sym = symbol_find_or_make (real_name);
 
@@ -3528,12 +4131,11 @@ ppc_extern (int ignore ATTRIBUTE_UNUSED)
   char *name;
   char endc;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   (void) symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   demand_empty_rest_of_line ();
 }
@@ -3547,12 +4149,11 @@ ppc_lglobl (int ignore ATTRIBUTE_UNUSED)
   char endc;
   symbolS *sym;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   sym = symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   symbol_get_tc (sym)->output = 1;
 
@@ -3585,14 +4186,13 @@ ppc_ref (int ignore ATTRIBUTE_UNUSED)
 
   do
     {
-      name = input_line_pointer;
-      c = get_symbol_end ();
+      c = get_symbol_name (&name);
 
       fix_at_start (symbol_get_frag (ppc_current_csect), 0,
                    symbol_find_or_make (name), 0, FALSE, BFD_RELOC_NONE);
 
       *input_line_pointer = c;
-      SKIP_WHITESPACE ();
+      SKIP_WHITESPACE_AFTER_NAME ();
       c = *input_line_pointer;
       if (c == ',')
        {
@@ -3622,12 +4222,11 @@ ppc_rename (int ignore ATTRIBUTE_UNUSED)
   symbolS *sym;
   int len;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   sym = symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   if (*input_line_pointer != ',')
     {
@@ -3729,28 +4328,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)
@@ -3784,8 +4385,7 @@ ppc_function (int ignore ATTRIBUTE_UNUSED)
   symbolS *ext_sym;
   symbolS *lab_sym;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   /* Ignore any [PR] suffix.  */
   name = ppc_canonicalize_symbol_name (name);
@@ -3796,7 +4396,7 @@ ppc_function (int ignore ATTRIBUTE_UNUSED)
 
   ext_sym = symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   if (*input_line_pointer != ',')
     {
@@ -3806,12 +4406,11 @@ ppc_function (int ignore ATTRIBUTE_UNUSED)
     }
   ++input_line_pointer;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   lab_sym = symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   if (ext_sym != lab_sym)
     {
@@ -3845,11 +4444,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);
            }
        }
     }
@@ -3992,12 +4589,11 @@ ppc_bs (int ignore ATTRIBUTE_UNUSED)
   if (ppc_current_block != NULL)
     as_bad (_("nested .bs blocks"));
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   csect = symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   sym = symbol_make (".bs");
   S_SET_SEGMENT (sym, now_seg);
@@ -4211,6 +4807,33 @@ ppc_vbyte (int dummy ATTRIBUTE_UNUSED)
   cons (byte_count);
 }
 
+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
@@ -4247,12 +4870,11 @@ ppc_tc (int ignore ATTRIBUTE_UNUSED)
        return;
       }
 
-    name = input_line_pointer;
-    endc = get_symbol_end ();
+    endc = get_symbol_name (&name);
 
     sym = symbol_find_or_make (name);
 
-    *input_line_pointer = endc;
+    (void) restore_line_pointer (endc);
 
     if (S_IS_DEFINED (sym))
       {
@@ -4318,6 +4940,7 @@ ppc_tc (int ignore ATTRIBUTE_UNUSED)
 static void
 ppc_machine (int ignore ATTRIBUTE_UNUSED)
 {
+  char c;
   char *cpu_string;
 #define MAX_HISTORY 100
   static ppc_cpu_t *cpu_history;
@@ -4325,19 +4948,9 @@ ppc_machine (int ignore ATTRIBUTE_UNUSED)
 
   SKIP_WHITESPACE ();
 
-  if (*input_line_pointer == '"')
-    {
-      int len;
-      cpu_string = demand_copy_C_string (&len);
-    }
-  else
-    {
-      char c;
-      cpu_string = input_line_pointer;
-      c = get_symbol_end ();
-      cpu_string = xstrdup (cpu_string);
-      *input_line_pointer = c;
-    }
+  c = get_symbol_name (&cpu_string);
+  cpu_string = xstrdup (cpu_string);
+  (void) restore_line_pointer (c);
 
   if (cpu_string != NULL)
     {
@@ -4365,7 +4978,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);
@@ -4376,23 +4989,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
@@ -4415,11 +5011,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;
     }
 
@@ -4588,28 +5182,22 @@ 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;
-  c = get_symbol_end ();
+  c = get_symbol_name (&symbol_name);
 
-  name = xmalloc (input_line_pointer - symbol_name + 1);
-  strcpy (name, symbol_name);
+  name = xstrdup (symbol_name);
 
   sym = symbol_find_or_make (name);
 
   *input_line_pointer = c;
 
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
 
   /* Look up the opcode in the hash table.  */
   opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop");
@@ -4645,16 +5233,15 @@ ppc_pe_comm (int lcomm)
   symbolS *symbolP;
   offsetT align;
 
-  name = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (&name);
 
   /* just after name is now '\0'.  */
   p = input_line_pointer;
   *p = c;
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
   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;
     }
@@ -4690,7 +5277,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;
@@ -4699,7 +5286,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);
@@ -4776,15 +5363,13 @@ ppc_pe_section (int ignore ATTRIBUTE_UNUSED)
   segT sec;
   int align;
 
-  section_name = input_line_pointer;
-  c = get_symbol_end ();
+  c = get_symbol_name (&section_name);
 
-  name = xmalloc (input_line_pointer - section_name + 1);
-  strcpy (name, section_name);
+  name = xstrdup (section_name);
 
   *input_line_pointer = c;
 
-  SKIP_WHITESPACE ();
+  SKIP_WHITESPACE_AFTER_NAME ();
 
   exp = 0;
   flags = SEC_NO_FLAGS;
@@ -4829,7 +5414,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;
@@ -4932,12 +5517,11 @@ ppc_pe_function (int ignore ATTRIBUTE_UNUSED)
   char endc;
   symbolS *ext_sym;
 
-  name = input_line_pointer;
-  endc = get_symbol_end ();
+  endc = get_symbol_name (&name);
 
   ext_sym = symbol_find_or_make (name);
 
-  *input_line_pointer = endc;
+  (void) restore_line_pointer (endc);
 
   S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
   SF_SET_FUNCTION (ext_sym);
@@ -5037,7 +5621,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)
@@ -5105,7 +5690,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
@@ -5124,6 +5709,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
@@ -5176,9 +5762,7 @@ ppc_frob_symbol (symbolS *sym)
          char *snew;
 
          len = s - name;
-         snew = xmalloc (len + 1);
-         memcpy (snew, name, len);
-         snew[len] = '\0';
+         snew = xstrndup (name, len);
 
          S_SET_NAME (sym, snew);
        }
@@ -5195,11 +5779,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
@@ -5383,13 +5967,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)
@@ -5457,6 +6050,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);
@@ -5464,7 +6061,7 @@ ppc_frob_section (asection *sec)
 
 #endif /* OBJ_XCOFF */
 \f
-char *
+const char *
 md_atof (int type, char *litp, int *sizep)
 {
   return ieee_md_atof (type, litp, sizep, target_big_endian);
@@ -5492,7 +6089,7 @@ md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT addr)
 #else
   int align = bfd_get_section_alignment (stdoutput, seg);
 
-  return ((addr + (1 << align) - 1) & (-1 << align));
+  return ((addr + (1 << align) - 1) & -(1 << align));
 #endif
 }
 
@@ -5552,6 +6149,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
@@ -5596,55 +6197,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 ();
-
-      /* Skip the initial dummy symbol.  */
-      csect = symbol_get_tc (csect)->next;
-
-      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;
-
-                 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;
-                   }
+      symbolS *csect = tc->within;
 
-                 /* If we found the symbol before the next csect
-                    symbol, then this is the csect we want.  */
-                 if (scan == fix->fx_addsy)
-                   break;
-               }
+      /* 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;
 
-             csect = next_csect;
-           }
+      fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
+      fix->fx_addsy = csect;
 
-         fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
-         fix->fx_addsy = csect;
-       }
       return 0;
     }
 
@@ -5686,6 +6249,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
@@ -5706,6 +6276,22 @@ ppc_force_relocation (fixS *fix)
     case BFD_RELOC_24_PLT_PCREL:
     case BFD_RELOC_PPC64_TOC:
       return 1;
+    case BFD_RELOC_PPC_B26:
+    case BFD_RELOC_PPC_BA26:
+    case BFD_RELOC_PPC_B16:
+    case BFD_RELOC_PPC_BA16:
+      /* All branch fixups targeting a localentry symbol must
+         force a relocation.  */
+      if (fix->fx_addsy)
+       {
+         asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+         elf_symbol_type *elfsym
+           = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         gas_assert (elfsym);
+         if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+           return 1;
+       }
+      break;
     default:
       break;
     }
@@ -5720,20 +6306,64 @@ ppc_force_relocation (fixS *fix)
 int
 ppc_fix_adjustable (fixS *fix)
 {
-  return (fix->fx_r_type != BFD_RELOC_16_GOTOFF
-         && 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
+  switch (fix->fx_r_type)
+    {
+      /* All branch fixups targeting a localentry symbol must
+         continue using the symbol.  */
+    case BFD_RELOC_PPC_B26:
+    case BFD_RELOC_PPC_BA26:
+    case BFD_RELOC_PPC_B16:
+    case BFD_RELOC_PPC_BA16:
+    case BFD_RELOC_PPC_B16_BRTAKEN:
+    case BFD_RELOC_PPC_B16_BRNTAKEN:
+    case BFD_RELOC_PPC_BA16_BRTAKEN:
+    case BFD_RELOC_PPC_BA16_BRNTAKEN:
+      if (fix->fx_addsy)
+       {
+         asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+         elf_symbol_type *elfsym
+           = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         gas_assert (elfsym);
+         if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+           return 0;
+       }
+      break;
+    default:
+      break;
+    }
+
+  return (fix->fx_r_type != BFD_RELOC_16_GOTOFF
+         && 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
          && !(fix->fx_r_type >= BFD_RELOC_PPC_TLS
               && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA));
 }
 #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.  */
 
@@ -5743,21 +6373,55 @@ 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
+         || (ppc_cpu & PPC_OPCODE_POWER9) != 0)
        {
-         /* For power6 and power7, 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
-            frag by one.  */
+         /* For power6, power7, power8 and power9, 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 frag by one.  */
          if (count > 4)
            {
              struct frag *group_nop = xmalloc (SIZEOF_STRUCT_FRAG + 4);
@@ -5771,9 +6435,17 @@ 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
+             || (ppc_cpu & PPC_OPCODE_POWER9) != 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/power9 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);
@@ -5782,18 +6454,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)
+md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 {
   valueT value = * valP;
+  offsetT fieldval;
+  const struct powerpc_operand *operand;
 
 #ifdef OBJ_ELF
   if (fixP->fx_addsy != NULL)
@@ -5801,6 +6469,9 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       /* Hack around bfd_install_relocation brain damage.  */
       if (fixP->fx_pcrel)
        value += fixP->fx_frag->fr_address + fixP->fx_where;
+
+      if (fixP->fx_addsy == abs_section_sym)
+       fixP->fx_done = 1;
     }
   else
     fixP->fx_done = 1;
@@ -5832,17 +6503,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
@@ -5861,93 +6528,313 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          value = fixP->fx_offset;
          fixP->fx_done = 1;
        }
+
+       /* During parsing of instructions, a TOC16 reloc is generated for
+          instructions such as 'lwz RT,SYM(RB)' if SYM is a symbol defined
+          in the toc.  But at parse time, SYM may be not yet defined, so
+          check again here.  */
+       if (fixP->fx_r_type == BFD_RELOC_16
+           && fixP->fx_addsy != NULL
+           && ppc_is_toc_sym (fixP->fx_addsy))
+         fixP->fx_r_type = BFD_RELOC_PPC_TOC16;
 #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 = SEX16 (fieldval);
+      fixP->fx_no_overflow = 1;
+      break;
 
-      if (fixP->fx_done)
-       /* Nothing else to do here.  */
-       return;
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_HI16_PCREL:
+#ifdef OBJ_ELF
+      if (REPORT_OVERFLOW_HI && ppc_obj64)
+       {
+         fieldval = value >> 16;
+         if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+           {
+             valueT sign = (((valueT) -1 >> 16) + 1) >> 1;
+             fieldval = ((valueT) fieldval ^ sign) - sign;
+           }
+         break;
+       }
+      /* Fall thru */
 
-      gas_assert (fixP->fx_addsy != NULL);
+    case BFD_RELOC_PPC_VLE_HI16A:
+    case BFD_RELOC_PPC_VLE_HI16D:
+    case BFD_RELOC_PPC64_ADDR16_HIGH:
+#endif
+      fieldval = PPC_HI (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)
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_PPC_REL16DX_HA:
+#ifdef OBJ_ELF
+      if (REPORT_OVERFLOW_HI && ppc_obj64)
        {
-         fixP->fx_r_type = BFD_RELOC_PPC_B16;
-#ifdef OBJ_XCOFF
-         fixP->fx_size = 2;
-         if (target_big_endian)
-           fixP->fx_where += 2;
-#endif
+         fieldval = (value + 0x8000) >> 16;
+         if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+           {
+             valueT sign = (((valueT) -1 >> 16) + 1) >> 1;
+             fieldval = ((valueT) fieldval ^ sign) - sign;
+           }
+         break;
        }
-      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)
+      /* Fall thru */
+
+    case BFD_RELOC_PPC_VLE_HA16A:
+    case BFD_RELOC_PPC_VLE_HA16D:
+    case BFD_RELOC_PPC64_ADDR16_HIGHA:
+#endif
+      fieldval = PPC_HA (value);
+      goto sign_extend_16;
+
+#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
+
+    default:
+      break;
+    }
+
+  if (operand != NULL)
+    {
+      /* Handle relocs in an 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_HIGH:
+       case BFD_RELOC_PPC64_TPREL16_HIGHA:
+       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_HIGH:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHA:
+       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
+
+       case BFD_RELOC_VTABLE_INHERIT:
+       case BFD_RELOC_VTABLE_ENTRY:
+       case BFD_RELOC_PPC_DTPMOD:
+       case BFD_RELOC_PPC_TPREL:
+       case BFD_RELOC_PPC_DTPREL:
+       case BFD_RELOC_PPC_COPY:
+       case BFD_RELOC_PPC_GLOB_DAT:
+       case BFD_RELOC_32_PLT_PCREL:
+       case BFD_RELOC_PPC_EMB_NADDR32:
+       case BFD_RELOC_PPC64_TOC:
+       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_PPC64_ADDR64_LOCAL:
+         as_bad_where (fixP->fx_file, fixP->fx_line,
+                       _("%s unsupported as instruction fixup"),
+                       bfd_get_reloc_code_name (fixP->fx_r_type));
+         fixP->fx_done = 1;
+         return;
+
+       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)
+       {
+         unsigned long insn;
+         unsigned char *where;
+
+         /* Fetch the instruction, insert the fully resolved operand
+            value, and stuff the instruction back again.  */
+         where = (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where;
+         if (target_big_endian)
+           {
+             if (fixP->fx_size == 4)
+               insn = bfd_getb32 (where);
+             else
+               insn = bfd_getb16 (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 (where);
+             else
+               insn = bfd_getl16 (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 (insn, where);
+             else
+               bfd_putb16 (insn, where);
+           }
+         else
+           {
+             if (fixP->fx_size == 4)
+               bfd_putl32 (insn, where);
+             else
+               bfd_putl16 (insn, 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_NONE)
        {
-         char *sfile;
+         const char *sfile;
          unsigned int sline;
 
          /* Use expr_symbol_where to see if this is an expression
@@ -5965,49 +6852,90 @@ 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:
+       case BFD_RELOC_PPC64_ADDR16_HIGH:
+       case BFD_RELOC_PPC64_ADDR16_HIGHA:
+       case BFD_RELOC_PPC64_ADDR64_LOCAL:
+         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:
@@ -6015,287 +6943,151 @@ 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_HIGH:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHA:
+       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_HIGH:
+       case BFD_RELOC_PPC64_TPREL16_HIGHA:
+       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);
+      if (warn_476
+         && (seg->flags & SEC_CODE) != 0
+         && fixP->fx_size == 4
+         && fixP->fx_done
+         && !fixP->fx_tcbit
+         && (fixP->fx_r_type == BFD_RELOC_32
+             || fixP->fx_r_type == BFD_RELOC_CTOR
+             || fixP->fx_r_type == BFD_RELOC_32_PCREL))
+       as_warn_where (fixP->fx_file, fixP->fx_line,
+                      _("data in executable section"));
+    }
+
+  /* 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);
+         fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
          break;
 
-#ifdef OBJ_XCOFF
-       case BFD_RELOC_NONE:
-         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_PPC_REL16DX_HA:
+       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);
+       case BFD_RELOC_PPC_B26:
+       case BFD_RELOC_PPC_LOCAL24PC:
+       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;
 
-       case BFD_RELOC_8:
-         if (fixP->fx_pcrel)
+       default:
+         if (fixP->fx_addsy)
            {
-             /* 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_LOCAL24PC:
-         if (!fixP->fx_pcrel && !fixP->fx_done)
-           abort ();
+             const char *sfile;
+             unsigned int sline;
 
-         if (fixP->fx_done)
-           {
-             char *where;
-             unsigned long insn;
-
-             /* 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)
+             /* 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,
-                             _("must branch to an address a multiple of 4"));
-             if ((offsetT) value < -0x40000000
-                 || (offsetT) value >= 0x40000000)
-               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
@@ -6303,7 +7095,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;
@@ -6317,6 +7119,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       fixP->fx_addnumber =
        - bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixP->fx_addsy))
        - S_GET_VALUE (ppc_toc_csect);
+      /* Set *valP to avoid errors.  */
+      *valP = value;
 #endif
     }
 #endif
@@ -6360,7 +7164,7 @@ tc_ppc_regname_to_dw2regnum (char *regname)
   unsigned int i;
   const char *p;
   char *q;
-  static struct { char *name; int dw2regnum; } regnames[] =
+  static struct { const char *name; int dw2regnum; } regnames[] =
     {
       { "sp", 1 }, { "r.sp", 1 }, { "rtoc", 2 }, { "r.toc", 2 },
       { "mq", 64 }, { "lr", 65 }, { "ctr", 66 }, { "ap", 67 },
This page took 0.097898 seconds and 4 git commands to generate.