]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-ppc.c
objdump: add DWARF support for AIX
[thirdparty/binutils-gdb.git] / gas / config / tc-ppc.c
index 89c7bfe45dd604ad82fbbbeb63d6db299dd58c1d..567f8011a5f5fbaf338f850d03012726ba93f74c 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
-   Copyright (C) 1994-2016 Free Software Foundation, Inc.
+   Copyright (C) 1994-2021 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
    This file is part of GAS, the GNU Assembler.
 #include "dwarf2dbg.h"
 #endif
 
-#ifdef TE_PE
-#include "coff/pe.h"
-#endif
-
 #ifdef OBJ_XCOFF
 #include "coff/xcoff.h"
 #include "libxcoff.h"
@@ -50,11 +46,7 @@ static int set_target_endian = 0;
 
 /* Whether to use user friendly register names.  */
 #ifndef TARGET_REG_NAMES_P
-#ifdef TE_PE
-#define TARGET_REG_NAMES_P TRUE
-#else
-#define TARGET_REG_NAMES_P FALSE
-#endif
+#define TARGET_REG_NAMES_P false
 #endif
 
 /* Macros for calculating LO, HI, HA, HIGHER, HIGHERA, HIGHEST,
@@ -91,7 +83,7 @@ static int set_target_endian = 0;
    applied to constants.  */
 #define REPORT_OVERFLOW_HI 0
 
-static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
+static bool reg_names_p = TARGET_REG_NAMES_P;
 
 static void ppc_macro (char *, const struct powerpc_macro *);
 static void ppc_byte (int);
@@ -133,20 +125,7 @@ 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
-static void ppc_previous (int);
-static void ppc_pdata (int);
-static void ppc_ydata (int);
-static void ppc_reldata (int);
-static void ppc_rdata (int);
-static void ppc_ualong (int);
-static void ppc_znop (int);
-static void ppc_pe_comm (int);
-static void ppc_pe_section (int);
-static void ppc_pe_function (int);
-static void ppc_pe_tocd (int);
+static void ppc_elf_gnu_attribute (int);
 #endif
 \f
 /* Generic assembler global variables which must be defined by all
@@ -206,15 +185,17 @@ ppc_cpu_t sticky = 0;
 /* Value for ELF e_flags EF_PPC64_ABI.  */
 unsigned int ppc_abiversion = 0;
 
+#ifdef OBJ_ELF
 /* Flags set on encountering toc relocs.  */
-enum {
+static enum {
   has_large_toc_reloc = 1,
   has_small_toc_reloc = 2
 } toc_reloc_types;
+#endif
 
 /* Warn on emitting data to code sections.  */
 int warn_476;
-unsigned long last_insn;
+uint64_t last_insn;
 segT last_seg;
 subsegT last_subseg;
 \f
@@ -268,22 +249,7 @@ const pseudo_typeS md_pseudo_table[] =
   { "lcomm",   ppc_elf_lcomm,  0 },
   { "localentry", ppc_elf_localentry,  0 },
   { "abiversion", ppc_elf_abiversion,  0 },
-#endif
-
-#ifdef TE_PE
-  /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format.  */
-  { "previous", ppc_previous,   0 },
-  { "pdata",    ppc_pdata,      0 },
-  { "ydata",    ppc_ydata,      0 },
-  { "reldata",  ppc_reldata,    0 },
-  { "rdata",    ppc_rdata,      0 },
-  { "ualong",   ppc_ualong,     0 },
-  { "znop",     ppc_znop,       0 },
-  { "comm",    ppc_pe_comm,    0 },
-  { "lcomm",   ppc_pe_comm,    1 },
-  { "section",  ppc_pe_section, 0 },
-  { "function",        ppc_pe_function,0 },
-  { "tocd",     ppc_pe_tocd,    0 },
+  { "gnu_attribute", ppc_elf_gnu_attribute, 0},
 #endif
 
 #if defined (OBJ_XCOFF) || defined (OBJ_ELF)
@@ -303,7 +269,8 @@ const pseudo_typeS md_pseudo_table[] =
 struct pd_reg
   {
     const char *name;
-    int value;
+    unsigned short value;
+    unsigned short flags;
   };
 
 /* List of registers that are pre-defined:
@@ -327,11 +294,9 @@ struct pd_reg
    There are individual registers as well:
    sp or r.sp     has the value 1
    rtoc or r.toc  has the value 2
-   fpscr          has the value 0
    xer            has the value 1
    lr             has the value 8
    ctr            has the value 9
-   pmr            has the value 0
    dar            has the value 19
    dsisr          has the value 18
    dec            has the value 22
@@ -343,463 +308,470 @@ struct pd_reg
 
 static const struct pd_reg pre_defined_registers[] =
 {
-  { "cr.0", 0 },    /* Condition Registers */
-  { "cr.1", 1 },
-  { "cr.2", 2 },
-  { "cr.3", 3 },
-  { "cr.4", 4 },
-  { "cr.5", 5 },
-  { "cr.6", 6 },
-  { "cr.7", 7 },
-
-  { "cr0", 0 },
-  { "cr1", 1 },
-  { "cr2", 2 },
-  { "cr3", 3 },
-  { "cr4", 4 },
-  { "cr5", 5 },
-  { "cr6", 6 },
-  { "cr7", 7 },
-
-  { "ctr", 9 },
-
-  { "dar", 19 },    /* Data Access Register */
-  { "dec", 22 },    /* Decrementer */
-  { "dsisr", 18 },  /* Data Storage Interrupt Status Register */
-
-  { "f.0", 0 },     /* Floating point registers */
-  { "f.1", 1 },
-  { "f.10", 10 },
-  { "f.11", 11 },
-  { "f.12", 12 },
-  { "f.13", 13 },
-  { "f.14", 14 },
-  { "f.15", 15 },
-  { "f.16", 16 },
-  { "f.17", 17 },
-  { "f.18", 18 },
-  { "f.19", 19 },
-  { "f.2", 2 },
-  { "f.20", 20 },
-  { "f.21", 21 },
-  { "f.22", 22 },
-  { "f.23", 23 },
-  { "f.24", 24 },
-  { "f.25", 25 },
-  { "f.26", 26 },
-  { "f.27", 27 },
-  { "f.28", 28 },
-  { "f.29", 29 },
-  { "f.3", 3 },
-  { "f.30", 30 },
-  { "f.31", 31 },
-
-  { "f.32", 32 },    /* Extended floating point scalar registers (ISA 2.06).  */
-  { "f.33", 33 },
-  { "f.34", 34 },
-  { "f.35", 35 },
-  { "f.36", 36 },
-  { "f.37", 37 },
-  { "f.38", 38 },
-  { "f.39", 39 },
-  { "f.4", 4 },
-  { "f.40", 40 },
-  { "f.41", 41 },
-  { "f.42", 42 },
-  { "f.43", 43 },
-  { "f.44", 44 },
-  { "f.45", 45 },
-  { "f.46", 46 },
-  { "f.47", 47 },
-  { "f.48", 48 },
-  { "f.49", 49 },
-  { "f.5", 5 },
-  { "f.50", 50 },
-  { "f.51", 51 },
-  { "f.52", 52 },
-  { "f.53", 53 },
-  { "f.54", 54 },
-  { "f.55", 55 },
-  { "f.56", 56 },
-  { "f.57", 57 },
-  { "f.58", 58 },
-  { "f.59", 59 },
-  { "f.6", 6 },
-  { "f.60", 60 },
-  { "f.61", 61 },
-  { "f.62", 62 },
-  { "f.63", 63 },
-  { "f.7", 7 },
-  { "f.8", 8 },
-  { "f.9", 9 },
-
-  { "f0", 0 },
-  { "f1", 1 },
-  { "f10", 10 },
-  { "f11", 11 },
-  { "f12", 12 },
-  { "f13", 13 },
-  { "f14", 14 },
-  { "f15", 15 },
-  { "f16", 16 },
-  { "f17", 17 },
-  { "f18", 18 },
-  { "f19", 19 },
-  { "f2", 2 },
-  { "f20", 20 },
-  { "f21", 21 },
-  { "f22", 22 },
-  { "f23", 23 },
-  { "f24", 24 },
-  { "f25", 25 },
-  { "f26", 26 },
-  { "f27", 27 },
-  { "f28", 28 },
-  { "f29", 29 },
-  { "f3", 3 },
-  { "f30", 30 },
-  { "f31", 31 },
-
-  { "f32", 32 },    /* Extended floating point scalar registers (ISA 2.06).  */
-  { "f33", 33 },
-  { "f34", 34 },
-  { "f35", 35 },
-  { "f36", 36 },
-  { "f37", 37 },
-  { "f38", 38 },
-  { "f39", 39 },
-  { "f4", 4 },
-  { "f40", 40 },
-  { "f41", 41 },
-  { "f42", 42 },
-  { "f43", 43 },
-  { "f44", 44 },
-  { "f45", 45 },
-  { "f46", 46 },
-  { "f47", 47 },
-  { "f48", 48 },
-  { "f49", 49 },
-  { "f5", 5 },
-  { "f50", 50 },
-  { "f51", 51 },
-  { "f52", 52 },
-  { "f53", 53 },
-  { "f54", 54 },
-  { "f55", 55 },
-  { "f56", 56 },
-  { "f57", 57 },
-  { "f58", 58 },
-  { "f59", 59 },
-  { "f6", 6 },
-  { "f60", 60 },
-  { "f61", 61 },
-  { "f62", 62 },
-  { "f63", 63 },
-  { "f7", 7 },
-  { "f8", 8 },
-  { "f9", 9 },
-
-  { "fpscr", 0 },
+  /* VSX accumulators.  */
+  { "a0", 0, PPC_OPERAND_ACC },
+  { "a1", 1, PPC_OPERAND_ACC },
+  { "a2", 2, PPC_OPERAND_ACC },
+  { "a3", 3, PPC_OPERAND_ACC },
+  { "a4", 4, PPC_OPERAND_ACC },
+  { "a5", 5, PPC_OPERAND_ACC },
+  { "a6", 6, PPC_OPERAND_ACC },
+  { "a7", 7, PPC_OPERAND_ACC },
+
+  /* Condition Registers */
+  { "cr.0", 0, PPC_OPERAND_CR_REG },
+  { "cr.1", 1, PPC_OPERAND_CR_REG },
+  { "cr.2", 2, PPC_OPERAND_CR_REG },
+  { "cr.3", 3, PPC_OPERAND_CR_REG },
+  { "cr.4", 4, PPC_OPERAND_CR_REG },
+  { "cr.5", 5, PPC_OPERAND_CR_REG },
+  { "cr.6", 6, PPC_OPERAND_CR_REG },
+  { "cr.7", 7, PPC_OPERAND_CR_REG },
+
+  { "cr0", 0, PPC_OPERAND_CR_REG },
+  { "cr1", 1, PPC_OPERAND_CR_REG },
+  { "cr2", 2, PPC_OPERAND_CR_REG },
+  { "cr3", 3, PPC_OPERAND_CR_REG },
+  { "cr4", 4, PPC_OPERAND_CR_REG },
+  { "cr5", 5, PPC_OPERAND_CR_REG },
+  { "cr6", 6, PPC_OPERAND_CR_REG },
+  { "cr7", 7, PPC_OPERAND_CR_REG },
+
+  { "ctr", 9, PPC_OPERAND_SPR },
+  { "dar", 19, PPC_OPERAND_SPR },
+  { "dec", 22, PPC_OPERAND_SPR },
+  { "dsisr", 18, PPC_OPERAND_SPR },
+
+  /* Floating point registers */
+  { "f.0", 0, PPC_OPERAND_FPR },
+  { "f.1", 1, PPC_OPERAND_FPR },
+  { "f.10", 10, PPC_OPERAND_FPR },
+  { "f.11", 11, PPC_OPERAND_FPR },
+  { "f.12", 12, PPC_OPERAND_FPR },
+  { "f.13", 13, PPC_OPERAND_FPR },
+  { "f.14", 14, PPC_OPERAND_FPR },
+  { "f.15", 15, PPC_OPERAND_FPR },
+  { "f.16", 16, PPC_OPERAND_FPR },
+  { "f.17", 17, PPC_OPERAND_FPR },
+  { "f.18", 18, PPC_OPERAND_FPR },
+  { "f.19", 19, PPC_OPERAND_FPR },
+  { "f.2", 2, PPC_OPERAND_FPR },
+  { "f.20", 20, PPC_OPERAND_FPR },
+  { "f.21", 21, PPC_OPERAND_FPR },
+  { "f.22", 22, PPC_OPERAND_FPR },
+  { "f.23", 23, PPC_OPERAND_FPR },
+  { "f.24", 24, PPC_OPERAND_FPR },
+  { "f.25", 25, PPC_OPERAND_FPR },
+  { "f.26", 26, PPC_OPERAND_FPR },
+  { "f.27", 27, PPC_OPERAND_FPR },
+  { "f.28", 28, PPC_OPERAND_FPR },
+  { "f.29", 29, PPC_OPERAND_FPR },
+  { "f.3", 3, PPC_OPERAND_FPR },
+  { "f.30", 30, PPC_OPERAND_FPR },
+  { "f.31", 31, PPC_OPERAND_FPR },
+  { "f.32", 32, PPC_OPERAND_VSR },
+  { "f.33", 33, PPC_OPERAND_VSR },
+  { "f.34", 34, PPC_OPERAND_VSR },
+  { "f.35", 35, PPC_OPERAND_VSR },
+  { "f.36", 36, PPC_OPERAND_VSR },
+  { "f.37", 37, PPC_OPERAND_VSR },
+  { "f.38", 38, PPC_OPERAND_VSR },
+  { "f.39", 39, PPC_OPERAND_VSR },
+  { "f.4", 4, PPC_OPERAND_FPR },
+  { "f.40", 40, PPC_OPERAND_VSR },
+  { "f.41", 41, PPC_OPERAND_VSR },
+  { "f.42", 42, PPC_OPERAND_VSR },
+  { "f.43", 43, PPC_OPERAND_VSR },
+  { "f.44", 44, PPC_OPERAND_VSR },
+  { "f.45", 45, PPC_OPERAND_VSR },
+  { "f.46", 46, PPC_OPERAND_VSR },
+  { "f.47", 47, PPC_OPERAND_VSR },
+  { "f.48", 48, PPC_OPERAND_VSR },
+  { "f.49", 49, PPC_OPERAND_VSR },
+  { "f.5", 5, PPC_OPERAND_FPR },
+  { "f.50", 50, PPC_OPERAND_VSR },
+  { "f.51", 51, PPC_OPERAND_VSR },
+  { "f.52", 52, PPC_OPERAND_VSR },
+  { "f.53", 53, PPC_OPERAND_VSR },
+  { "f.54", 54, PPC_OPERAND_VSR },
+  { "f.55", 55, PPC_OPERAND_VSR },
+  { "f.56", 56, PPC_OPERAND_VSR },
+  { "f.57", 57, PPC_OPERAND_VSR },
+  { "f.58", 58, PPC_OPERAND_VSR },
+  { "f.59", 59, PPC_OPERAND_VSR },
+  { "f.6", 6, PPC_OPERAND_FPR },
+  { "f.60", 60, PPC_OPERAND_VSR },
+  { "f.61", 61, PPC_OPERAND_VSR },
+  { "f.62", 62, PPC_OPERAND_VSR },
+  { "f.63", 63, PPC_OPERAND_VSR },
+  { "f.7", 7, PPC_OPERAND_FPR },
+  { "f.8", 8, PPC_OPERAND_FPR },
+  { "f.9", 9, PPC_OPERAND_FPR },
+
+  { "f0", 0, PPC_OPERAND_FPR },
+  { "f1", 1, PPC_OPERAND_FPR },
+  { "f10", 10, PPC_OPERAND_FPR },
+  { "f11", 11, PPC_OPERAND_FPR },
+  { "f12", 12, PPC_OPERAND_FPR },
+  { "f13", 13, PPC_OPERAND_FPR },
+  { "f14", 14, PPC_OPERAND_FPR },
+  { "f15", 15, PPC_OPERAND_FPR },
+  { "f16", 16, PPC_OPERAND_FPR },
+  { "f17", 17, PPC_OPERAND_FPR },
+  { "f18", 18, PPC_OPERAND_FPR },
+  { "f19", 19, PPC_OPERAND_FPR },
+  { "f2", 2, PPC_OPERAND_FPR },
+  { "f20", 20, PPC_OPERAND_FPR },
+  { "f21", 21, PPC_OPERAND_FPR },
+  { "f22", 22, PPC_OPERAND_FPR },
+  { "f23", 23, PPC_OPERAND_FPR },
+  { "f24", 24, PPC_OPERAND_FPR },
+  { "f25", 25, PPC_OPERAND_FPR },
+  { "f26", 26, PPC_OPERAND_FPR },
+  { "f27", 27, PPC_OPERAND_FPR },
+  { "f28", 28, PPC_OPERAND_FPR },
+  { "f29", 29, PPC_OPERAND_FPR },
+  { "f3", 3, PPC_OPERAND_FPR },
+  { "f30", 30, PPC_OPERAND_FPR },
+  { "f31", 31, PPC_OPERAND_FPR },
+  { "f32", 32, PPC_OPERAND_VSR },
+  { "f33", 33, PPC_OPERAND_VSR },
+  { "f34", 34, PPC_OPERAND_VSR },
+  { "f35", 35, PPC_OPERAND_VSR },
+  { "f36", 36, PPC_OPERAND_VSR },
+  { "f37", 37, PPC_OPERAND_VSR },
+  { "f38", 38, PPC_OPERAND_VSR },
+  { "f39", 39, PPC_OPERAND_VSR },
+  { "f4", 4, PPC_OPERAND_FPR },
+  { "f40", 40, PPC_OPERAND_VSR },
+  { "f41", 41, PPC_OPERAND_VSR },
+  { "f42", 42, PPC_OPERAND_VSR },
+  { "f43", 43, PPC_OPERAND_VSR },
+  { "f44", 44, PPC_OPERAND_VSR },
+  { "f45", 45, PPC_OPERAND_VSR },
+  { "f46", 46, PPC_OPERAND_VSR },
+  { "f47", 47, PPC_OPERAND_VSR },
+  { "f48", 48, PPC_OPERAND_VSR },
+  { "f49", 49, PPC_OPERAND_VSR },
+  { "f5", 5, PPC_OPERAND_FPR },
+  { "f50", 50, PPC_OPERAND_VSR },
+  { "f51", 51, PPC_OPERAND_VSR },
+  { "f52", 52, PPC_OPERAND_VSR },
+  { "f53", 53, PPC_OPERAND_VSR },
+  { "f54", 54, PPC_OPERAND_VSR },
+  { "f55", 55, PPC_OPERAND_VSR },
+  { "f56", 56, PPC_OPERAND_VSR },
+  { "f57", 57, PPC_OPERAND_VSR },
+  { "f58", 58, PPC_OPERAND_VSR },
+  { "f59", 59, PPC_OPERAND_VSR },
+  { "f6", 6, PPC_OPERAND_FPR },
+  { "f60", 60, PPC_OPERAND_VSR },
+  { "f61", 61, PPC_OPERAND_VSR },
+  { "f62", 62, PPC_OPERAND_VSR },
+  { "f63", 63, PPC_OPERAND_VSR },
+  { "f7", 7, PPC_OPERAND_FPR },
+  { "f8", 8, PPC_OPERAND_FPR },
+  { "f9", 9, PPC_OPERAND_FPR },
 
   /* Quantization registers used with pair single instructions.  */
-  { "gqr.0", 0 },
-  { "gqr.1", 1 },
-  { "gqr.2", 2 },
-  { "gqr.3", 3 },
-  { "gqr.4", 4 },
-  { "gqr.5", 5 },
-  { "gqr.6", 6 },
-  { "gqr.7", 7 },
-  { "gqr0", 0 },
-  { "gqr1", 1 },
-  { "gqr2", 2 },
-  { "gqr3", 3 },
-  { "gqr4", 4 },
-  { "gqr5", 5 },
-  { "gqr6", 6 },
-  { "gqr7", 7 },
-
-  { "lr", 8 },     /* Link Register */
-
-  { "pmr", 0 },
-
-  { "r.0", 0 },    /* General Purpose Registers */
-  { "r.1", 1 },
-  { "r.10", 10 },
-  { "r.11", 11 },
-  { "r.12", 12 },
-  { "r.13", 13 },
-  { "r.14", 14 },
-  { "r.15", 15 },
-  { "r.16", 16 },
-  { "r.17", 17 },
-  { "r.18", 18 },
-  { "r.19", 19 },
-  { "r.2", 2 },
-  { "r.20", 20 },
-  { "r.21", 21 },
-  { "r.22", 22 },
-  { "r.23", 23 },
-  { "r.24", 24 },
-  { "r.25", 25 },
-  { "r.26", 26 },
-  { "r.27", 27 },
-  { "r.28", 28 },
-  { "r.29", 29 },
-  { "r.3", 3 },
-  { "r.30", 30 },
-  { "r.31", 31 },
-  { "r.4", 4 },
-  { "r.5", 5 },
-  { "r.6", 6 },
-  { "r.7", 7 },
-  { "r.8", 8 },
-  { "r.9", 9 },
-
-  { "r.sp", 1 },   /* Stack Pointer */
-
-  { "r.toc", 2 },  /* Pointer to the table of contents */
-
-  { "r0", 0 },     /* More general purpose registers */
-  { "r1", 1 },
-  { "r10", 10 },
-  { "r11", 11 },
-  { "r12", 12 },
-  { "r13", 13 },
-  { "r14", 14 },
-  { "r15", 15 },
-  { "r16", 16 },
-  { "r17", 17 },
-  { "r18", 18 },
-  { "r19", 19 },
-  { "r2", 2 },
-  { "r20", 20 },
-  { "r21", 21 },
-  { "r22", 22 },
-  { "r23", 23 },
-  { "r24", 24 },
-  { "r25", 25 },
-  { "r26", 26 },
-  { "r27", 27 },
-  { "r28", 28 },
-  { "r29", 29 },
-  { "r3", 3 },
-  { "r30", 30 },
-  { "r31", 31 },
-  { "r4", 4 },
-  { "r5", 5 },
-  { "r6", 6 },
-  { "r7", 7 },
-  { "r8", 8 },
-  { "r9", 9 },
-
-  { "rtoc", 2 },  /* Table of contents */
-
-  { "sdr1", 25 }, /* Storage Description Register 1 */
-
-  { "sp", 1 },
-
-  { "srr0", 26 }, /* Machine Status Save/Restore Register 0 */
-  { "srr1", 27 }, /* Machine Status Save/Restore Register 1 */
-
-  { "v.0", 0 },     /* Vector (Altivec/VMX) registers */
-  { "v.1", 1 },
-  { "v.10", 10 },
-  { "v.11", 11 },
-  { "v.12", 12 },
-  { "v.13", 13 },
-  { "v.14", 14 },
-  { "v.15", 15 },
-  { "v.16", 16 },
-  { "v.17", 17 },
-  { "v.18", 18 },
-  { "v.19", 19 },
-  { "v.2", 2 },
-  { "v.20", 20 },
-  { "v.21", 21 },
-  { "v.22", 22 },
-  { "v.23", 23 },
-  { "v.24", 24 },
-  { "v.25", 25 },
-  { "v.26", 26 },
-  { "v.27", 27 },
-  { "v.28", 28 },
-  { "v.29", 29 },
-  { "v.3", 3 },
-  { "v.30", 30 },
-  { "v.31", 31 },
-  { "v.4", 4 },
-  { "v.5", 5 },
-  { "v.6", 6 },
-  { "v.7", 7 },
-  { "v.8", 8 },
-  { "v.9", 9 },
-
-  { "v0", 0 },
-  { "v1", 1 },
-  { "v10", 10 },
-  { "v11", 11 },
-  { "v12", 12 },
-  { "v13", 13 },
-  { "v14", 14 },
-  { "v15", 15 },
-  { "v16", 16 },
-  { "v17", 17 },
-  { "v18", 18 },
-  { "v19", 19 },
-  { "v2", 2 },
-  { "v20", 20 },
-  { "v21", 21 },
-  { "v22", 22 },
-  { "v23", 23 },
-  { "v24", 24 },
-  { "v25", 25 },
-  { "v26", 26 },
-  { "v27", 27 },
-  { "v28", 28 },
-  { "v29", 29 },
-  { "v3", 3 },
-  { "v30", 30 },
-  { "v31", 31 },
-  { "v4", 4 },
-  { "v5", 5 },
-  { "v6", 6 },
-  { "v7", 7 },
-  { "v8", 8 },
-  { "v9", 9 },
-
-  { "vs.0", 0 },     /* Vector Scalar (VSX) registers (ISA 2.06).  */
-  { "vs.1", 1 },
-  { "vs.10", 10 },
-  { "vs.11", 11 },
-  { "vs.12", 12 },
-  { "vs.13", 13 },
-  { "vs.14", 14 },
-  { "vs.15", 15 },
-  { "vs.16", 16 },
-  { "vs.17", 17 },
-  { "vs.18", 18 },
-  { "vs.19", 19 },
-  { "vs.2", 2 },
-  { "vs.20", 20 },
-  { "vs.21", 21 },
-  { "vs.22", 22 },
-  { "vs.23", 23 },
-  { "vs.24", 24 },
-  { "vs.25", 25 },
-  { "vs.26", 26 },
-  { "vs.27", 27 },
-  { "vs.28", 28 },
-  { "vs.29", 29 },
-  { "vs.3", 3 },
-  { "vs.30", 30 },
-  { "vs.31", 31 },
-  { "vs.32", 32 },
-  { "vs.33", 33 },
-  { "vs.34", 34 },
-  { "vs.35", 35 },
-  { "vs.36", 36 },
-  { "vs.37", 37 },
-  { "vs.38", 38 },
-  { "vs.39", 39 },
-  { "vs.4", 4 },
-  { "vs.40", 40 },
-  { "vs.41", 41 },
-  { "vs.42", 42 },
-  { "vs.43", 43 },
-  { "vs.44", 44 },
-  { "vs.45", 45 },
-  { "vs.46", 46 },
-  { "vs.47", 47 },
-  { "vs.48", 48 },
-  { "vs.49", 49 },
-  { "vs.5", 5 },
-  { "vs.50", 50 },
-  { "vs.51", 51 },
-  { "vs.52", 52 },
-  { "vs.53", 53 },
-  { "vs.54", 54 },
-  { "vs.55", 55 },
-  { "vs.56", 56 },
-  { "vs.57", 57 },
-  { "vs.58", 58 },
-  { "vs.59", 59 },
-  { "vs.6", 6 },
-  { "vs.60", 60 },
-  { "vs.61", 61 },
-  { "vs.62", 62 },
-  { "vs.63", 63 },
-  { "vs.7", 7 },
-  { "vs.8", 8 },
-  { "vs.9", 9 },
-
-  { "vs0", 0 },
-  { "vs1", 1 },
-  { "vs10", 10 },
-  { "vs11", 11 },
-  { "vs12", 12 },
-  { "vs13", 13 },
-  { "vs14", 14 },
-  { "vs15", 15 },
-  { "vs16", 16 },
-  { "vs17", 17 },
-  { "vs18", 18 },
-  { "vs19", 19 },
-  { "vs2", 2 },
-  { "vs20", 20 },
-  { "vs21", 21 },
-  { "vs22", 22 },
-  { "vs23", 23 },
-  { "vs24", 24 },
-  { "vs25", 25 },
-  { "vs26", 26 },
-  { "vs27", 27 },
-  { "vs28", 28 },
-  { "vs29", 29 },
-  { "vs3", 3 },
-  { "vs30", 30 },
-  { "vs31", 31 },
-  { "vs32", 32 },
-  { "vs33", 33 },
-  { "vs34", 34 },
-  { "vs35", 35 },
-  { "vs36", 36 },
-  { "vs37", 37 },
-  { "vs38", 38 },
-  { "vs39", 39 },
-  { "vs4", 4 },
-  { "vs40", 40 },
-  { "vs41", 41 },
-  { "vs42", 42 },
-  { "vs43", 43 },
-  { "vs44", 44 },
-  { "vs45", 45 },
-  { "vs46", 46 },
-  { "vs47", 47 },
-  { "vs48", 48 },
-  { "vs49", 49 },
-  { "vs5", 5 },
-  { "vs50", 50 },
-  { "vs51", 51 },
-  { "vs52", 52 },
-  { "vs53", 53 },
-  { "vs54", 54 },
-  { "vs55", 55 },
-  { "vs56", 56 },
-  { "vs57", 57 },
-  { "vs58", 58 },
-  { "vs59", 59 },
-  { "vs6", 6 },
-  { "vs60", 60 },
-  { "vs61", 61 },
-  { "vs62", 62 },
-  { "vs63", 63 },
-  { "vs7", 7 },
-  { "vs8", 8 },
-  { "vs9", 9 },
-
-  { "xer", 1 },
-
+  { "gqr.0", 0, PPC_OPERAND_GQR },
+  { "gqr.1", 1, PPC_OPERAND_GQR },
+  { "gqr.2", 2, PPC_OPERAND_GQR },
+  { "gqr.3", 3, PPC_OPERAND_GQR },
+  { "gqr.4", 4, PPC_OPERAND_GQR },
+  { "gqr.5", 5, PPC_OPERAND_GQR },
+  { "gqr.6", 6, PPC_OPERAND_GQR },
+  { "gqr.7", 7, PPC_OPERAND_GQR },
+  { "gqr0", 0, PPC_OPERAND_GQR },
+  { "gqr1", 1, PPC_OPERAND_GQR },
+  { "gqr2", 2, PPC_OPERAND_GQR },
+  { "gqr3", 3, PPC_OPERAND_GQR },
+  { "gqr4", 4, PPC_OPERAND_GQR },
+  { "gqr5", 5, PPC_OPERAND_GQR },
+  { "gqr6", 6, PPC_OPERAND_GQR },
+  { "gqr7", 7, PPC_OPERAND_GQR },
+
+  { "lr", 8, PPC_OPERAND_SPR },
+
+  /* General Purpose Registers */
+  { "r.0", 0, PPC_OPERAND_GPR },
+  { "r.1", 1, PPC_OPERAND_GPR },
+  { "r.10", 10, PPC_OPERAND_GPR },
+  { "r.11", 11, PPC_OPERAND_GPR },
+  { "r.12", 12, PPC_OPERAND_GPR },
+  { "r.13", 13, PPC_OPERAND_GPR },
+  { "r.14", 14, PPC_OPERAND_GPR },
+  { "r.15", 15, PPC_OPERAND_GPR },
+  { "r.16", 16, PPC_OPERAND_GPR },
+  { "r.17", 17, PPC_OPERAND_GPR },
+  { "r.18", 18, PPC_OPERAND_GPR },
+  { "r.19", 19, PPC_OPERAND_GPR },
+  { "r.2", 2, PPC_OPERAND_GPR },
+  { "r.20", 20, PPC_OPERAND_GPR },
+  { "r.21", 21, PPC_OPERAND_GPR },
+  { "r.22", 22, PPC_OPERAND_GPR },
+  { "r.23", 23, PPC_OPERAND_GPR },
+  { "r.24", 24, PPC_OPERAND_GPR },
+  { "r.25", 25, PPC_OPERAND_GPR },
+  { "r.26", 26, PPC_OPERAND_GPR },
+  { "r.27", 27, PPC_OPERAND_GPR },
+  { "r.28", 28, PPC_OPERAND_GPR },
+  { "r.29", 29, PPC_OPERAND_GPR },
+  { "r.3", 3, PPC_OPERAND_GPR },
+  { "r.30", 30, PPC_OPERAND_GPR },
+  { "r.31", 31, PPC_OPERAND_GPR },
+  { "r.4", 4, PPC_OPERAND_GPR },
+  { "r.5", 5, PPC_OPERAND_GPR },
+  { "r.6", 6, PPC_OPERAND_GPR },
+  { "r.7", 7, PPC_OPERAND_GPR },
+  { "r.8", 8, PPC_OPERAND_GPR },
+  { "r.9", 9, PPC_OPERAND_GPR },
+
+  { "r.sp", 1, PPC_OPERAND_GPR },
+
+  { "r.toc", 2, PPC_OPERAND_GPR },
+
+  { "r0", 0, PPC_OPERAND_GPR },
+  { "r1", 1, PPC_OPERAND_GPR },
+  { "r10", 10, PPC_OPERAND_GPR },
+  { "r11", 11, PPC_OPERAND_GPR },
+  { "r12", 12, PPC_OPERAND_GPR },
+  { "r13", 13, PPC_OPERAND_GPR },
+  { "r14", 14, PPC_OPERAND_GPR },
+  { "r15", 15, PPC_OPERAND_GPR },
+  { "r16", 16, PPC_OPERAND_GPR },
+  { "r17", 17, PPC_OPERAND_GPR },
+  { "r18", 18, PPC_OPERAND_GPR },
+  { "r19", 19, PPC_OPERAND_GPR },
+  { "r2", 2, PPC_OPERAND_GPR },
+  { "r20", 20, PPC_OPERAND_GPR },
+  { "r21", 21, PPC_OPERAND_GPR },
+  { "r22", 22, PPC_OPERAND_GPR },
+  { "r23", 23, PPC_OPERAND_GPR },
+  { "r24", 24, PPC_OPERAND_GPR },
+  { "r25", 25, PPC_OPERAND_GPR },
+  { "r26", 26, PPC_OPERAND_GPR },
+  { "r27", 27, PPC_OPERAND_GPR },
+  { "r28", 28, PPC_OPERAND_GPR },
+  { "r29", 29, PPC_OPERAND_GPR },
+  { "r3", 3, PPC_OPERAND_GPR },
+  { "r30", 30, PPC_OPERAND_GPR },
+  { "r31", 31, PPC_OPERAND_GPR },
+  { "r4", 4, PPC_OPERAND_GPR },
+  { "r5", 5, PPC_OPERAND_GPR },
+  { "r6", 6, PPC_OPERAND_GPR },
+  { "r7", 7, PPC_OPERAND_GPR },
+  { "r8", 8, PPC_OPERAND_GPR },
+  { "r9", 9, PPC_OPERAND_GPR },
+
+  { "rtoc", 2, PPC_OPERAND_GPR },
+
+  { "sdr1", 25, PPC_OPERAND_SPR },
+
+  { "sp", 1, PPC_OPERAND_GPR },
+
+  { "srr0", 26, PPC_OPERAND_SPR },
+  { "srr1", 27, PPC_OPERAND_SPR },
+
+  /* Vector (Altivec/VMX) registers */
+  { "v.0", 0, PPC_OPERAND_VR },
+  { "v.1", 1, PPC_OPERAND_VR },
+  { "v.10", 10, PPC_OPERAND_VR },
+  { "v.11", 11, PPC_OPERAND_VR },
+  { "v.12", 12, PPC_OPERAND_VR },
+  { "v.13", 13, PPC_OPERAND_VR },
+  { "v.14", 14, PPC_OPERAND_VR },
+  { "v.15", 15, PPC_OPERAND_VR },
+  { "v.16", 16, PPC_OPERAND_VR },
+  { "v.17", 17, PPC_OPERAND_VR },
+  { "v.18", 18, PPC_OPERAND_VR },
+  { "v.19", 19, PPC_OPERAND_VR },
+  { "v.2", 2, PPC_OPERAND_VR },
+  { "v.20", 20, PPC_OPERAND_VR },
+  { "v.21", 21, PPC_OPERAND_VR },
+  { "v.22", 22, PPC_OPERAND_VR },
+  { "v.23", 23, PPC_OPERAND_VR },
+  { "v.24", 24, PPC_OPERAND_VR },
+  { "v.25", 25, PPC_OPERAND_VR },
+  { "v.26", 26, PPC_OPERAND_VR },
+  { "v.27", 27, PPC_OPERAND_VR },
+  { "v.28", 28, PPC_OPERAND_VR },
+  { "v.29", 29, PPC_OPERAND_VR },
+  { "v.3", 3, PPC_OPERAND_VR },
+  { "v.30", 30, PPC_OPERAND_VR },
+  { "v.31", 31, PPC_OPERAND_VR },
+  { "v.4", 4, PPC_OPERAND_VR },
+  { "v.5", 5, PPC_OPERAND_VR },
+  { "v.6", 6, PPC_OPERAND_VR },
+  { "v.7", 7, PPC_OPERAND_VR },
+  { "v.8", 8, PPC_OPERAND_VR },
+  { "v.9", 9, PPC_OPERAND_VR },
+
+  { "v0", 0, PPC_OPERAND_VR },
+  { "v1", 1, PPC_OPERAND_VR },
+  { "v10", 10, PPC_OPERAND_VR },
+  { "v11", 11, PPC_OPERAND_VR },
+  { "v12", 12, PPC_OPERAND_VR },
+  { "v13", 13, PPC_OPERAND_VR },
+  { "v14", 14, PPC_OPERAND_VR },
+  { "v15", 15, PPC_OPERAND_VR },
+  { "v16", 16, PPC_OPERAND_VR },
+  { "v17", 17, PPC_OPERAND_VR },
+  { "v18", 18, PPC_OPERAND_VR },
+  { "v19", 19, PPC_OPERAND_VR },
+  { "v2", 2, PPC_OPERAND_VR },
+  { "v20", 20, PPC_OPERAND_VR },
+  { "v21", 21, PPC_OPERAND_VR },
+  { "v22", 22, PPC_OPERAND_VR },
+  { "v23", 23, PPC_OPERAND_VR },
+  { "v24", 24, PPC_OPERAND_VR },
+  { "v25", 25, PPC_OPERAND_VR },
+  { "v26", 26, PPC_OPERAND_VR },
+  { "v27", 27, PPC_OPERAND_VR },
+  { "v28", 28, PPC_OPERAND_VR },
+  { "v29", 29, PPC_OPERAND_VR },
+  { "v3", 3, PPC_OPERAND_VR },
+  { "v30", 30, PPC_OPERAND_VR },
+  { "v31", 31, PPC_OPERAND_VR },
+  { "v4", 4, PPC_OPERAND_VR },
+  { "v5", 5, PPC_OPERAND_VR },
+  { "v6", 6, PPC_OPERAND_VR },
+  { "v7", 7, PPC_OPERAND_VR },
+  { "v8", 8, PPC_OPERAND_VR },
+  { "v9", 9, PPC_OPERAND_VR },
+
+  /* Vector Scalar (VSX) registers (ISA 2.06).  */
+  { "vs.0", 0, PPC_OPERAND_VSR },
+  { "vs.1", 1, PPC_OPERAND_VSR },
+  { "vs.10", 10, PPC_OPERAND_VSR },
+  { "vs.11", 11, PPC_OPERAND_VSR },
+  { "vs.12", 12, PPC_OPERAND_VSR },
+  { "vs.13", 13, PPC_OPERAND_VSR },
+  { "vs.14", 14, PPC_OPERAND_VSR },
+  { "vs.15", 15, PPC_OPERAND_VSR },
+  { "vs.16", 16, PPC_OPERAND_VSR },
+  { "vs.17", 17, PPC_OPERAND_VSR },
+  { "vs.18", 18, PPC_OPERAND_VSR },
+  { "vs.19", 19, PPC_OPERAND_VSR },
+  { "vs.2", 2, PPC_OPERAND_VSR },
+  { "vs.20", 20, PPC_OPERAND_VSR },
+  { "vs.21", 21, PPC_OPERAND_VSR },
+  { "vs.22", 22, PPC_OPERAND_VSR },
+  { "vs.23", 23, PPC_OPERAND_VSR },
+  { "vs.24", 24, PPC_OPERAND_VSR },
+  { "vs.25", 25, PPC_OPERAND_VSR },
+  { "vs.26", 26, PPC_OPERAND_VSR },
+  { "vs.27", 27, PPC_OPERAND_VSR },
+  { "vs.28", 28, PPC_OPERAND_VSR },
+  { "vs.29", 29, PPC_OPERAND_VSR },
+  { "vs.3", 3, PPC_OPERAND_VSR },
+  { "vs.30", 30, PPC_OPERAND_VSR },
+  { "vs.31", 31, PPC_OPERAND_VSR },
+  { "vs.32", 32, PPC_OPERAND_VSR },
+  { "vs.33", 33, PPC_OPERAND_VSR },
+  { "vs.34", 34, PPC_OPERAND_VSR },
+  { "vs.35", 35, PPC_OPERAND_VSR },
+  { "vs.36", 36, PPC_OPERAND_VSR },
+  { "vs.37", 37, PPC_OPERAND_VSR },
+  { "vs.38", 38, PPC_OPERAND_VSR },
+  { "vs.39", 39, PPC_OPERAND_VSR },
+  { "vs.4", 4, PPC_OPERAND_VSR },
+  { "vs.40", 40, PPC_OPERAND_VSR },
+  { "vs.41", 41, PPC_OPERAND_VSR },
+  { "vs.42", 42, PPC_OPERAND_VSR },
+  { "vs.43", 43, PPC_OPERAND_VSR },
+  { "vs.44", 44, PPC_OPERAND_VSR },
+  { "vs.45", 45, PPC_OPERAND_VSR },
+  { "vs.46", 46, PPC_OPERAND_VSR },
+  { "vs.47", 47, PPC_OPERAND_VSR },
+  { "vs.48", 48, PPC_OPERAND_VSR },
+  { "vs.49", 49, PPC_OPERAND_VSR },
+  { "vs.5", 5, PPC_OPERAND_VSR },
+  { "vs.50", 50, PPC_OPERAND_VSR },
+  { "vs.51", 51, PPC_OPERAND_VSR },
+  { "vs.52", 52, PPC_OPERAND_VSR },
+  { "vs.53", 53, PPC_OPERAND_VSR },
+  { "vs.54", 54, PPC_OPERAND_VSR },
+  { "vs.55", 55, PPC_OPERAND_VSR },
+  { "vs.56", 56, PPC_OPERAND_VSR },
+  { "vs.57", 57, PPC_OPERAND_VSR },
+  { "vs.58", 58, PPC_OPERAND_VSR },
+  { "vs.59", 59, PPC_OPERAND_VSR },
+  { "vs.6", 6, PPC_OPERAND_VSR },
+  { "vs.60", 60, PPC_OPERAND_VSR },
+  { "vs.61", 61, PPC_OPERAND_VSR },
+  { "vs.62", 62, PPC_OPERAND_VSR },
+  { "vs.63", 63, PPC_OPERAND_VSR },
+  { "vs.7", 7, PPC_OPERAND_VSR },
+  { "vs.8", 8, PPC_OPERAND_VSR },
+  { "vs.9", 9, PPC_OPERAND_VSR },
+
+  { "vs0", 0, PPC_OPERAND_VSR },
+  { "vs1", 1, PPC_OPERAND_VSR },
+  { "vs10", 10, PPC_OPERAND_VSR },
+  { "vs11", 11, PPC_OPERAND_VSR },
+  { "vs12", 12, PPC_OPERAND_VSR },
+  { "vs13", 13, PPC_OPERAND_VSR },
+  { "vs14", 14, PPC_OPERAND_VSR },
+  { "vs15", 15, PPC_OPERAND_VSR },
+  { "vs16", 16, PPC_OPERAND_VSR },
+  { "vs17", 17, PPC_OPERAND_VSR },
+  { "vs18", 18, PPC_OPERAND_VSR },
+  { "vs19", 19, PPC_OPERAND_VSR },
+  { "vs2", 2, PPC_OPERAND_VSR },
+  { "vs20", 20, PPC_OPERAND_VSR },
+  { "vs21", 21, PPC_OPERAND_VSR },
+  { "vs22", 22, PPC_OPERAND_VSR },
+  { "vs23", 23, PPC_OPERAND_VSR },
+  { "vs24", 24, PPC_OPERAND_VSR },
+  { "vs25", 25, PPC_OPERAND_VSR },
+  { "vs26", 26, PPC_OPERAND_VSR },
+  { "vs27", 27, PPC_OPERAND_VSR },
+  { "vs28", 28, PPC_OPERAND_VSR },
+  { "vs29", 29, PPC_OPERAND_VSR },
+  { "vs3", 3, PPC_OPERAND_VSR },
+  { "vs30", 30, PPC_OPERAND_VSR },
+  { "vs31", 31, PPC_OPERAND_VSR },
+  { "vs32", 32, PPC_OPERAND_VSR },
+  { "vs33", 33, PPC_OPERAND_VSR },
+  { "vs34", 34, PPC_OPERAND_VSR },
+  { "vs35", 35, PPC_OPERAND_VSR },
+  { "vs36", 36, PPC_OPERAND_VSR },
+  { "vs37", 37, PPC_OPERAND_VSR },
+  { "vs38", 38, PPC_OPERAND_VSR },
+  { "vs39", 39, PPC_OPERAND_VSR },
+  { "vs4", 4, PPC_OPERAND_VSR },
+  { "vs40", 40, PPC_OPERAND_VSR },
+  { "vs41", 41, PPC_OPERAND_VSR },
+  { "vs42", 42, PPC_OPERAND_VSR },
+  { "vs43", 43, PPC_OPERAND_VSR },
+  { "vs44", 44, PPC_OPERAND_VSR },
+  { "vs45", 45, PPC_OPERAND_VSR },
+  { "vs46", 46, PPC_OPERAND_VSR },
+  { "vs47", 47, PPC_OPERAND_VSR },
+  { "vs48", 48, PPC_OPERAND_VSR },
+  { "vs49", 49, PPC_OPERAND_VSR },
+  { "vs5", 5, PPC_OPERAND_VSR },
+  { "vs50", 50, PPC_OPERAND_VSR },
+  { "vs51", 51, PPC_OPERAND_VSR },
+  { "vs52", 52, PPC_OPERAND_VSR },
+  { "vs53", 53, PPC_OPERAND_VSR },
+  { "vs54", 54, PPC_OPERAND_VSR },
+  { "vs55", 55, PPC_OPERAND_VSR },
+  { "vs56", 56, PPC_OPERAND_VSR },
+  { "vs57", 57, PPC_OPERAND_VSR },
+  { "vs58", 58, PPC_OPERAND_VSR },
+  { "vs59", 59, PPC_OPERAND_VSR },
+  { "vs6", 6, PPC_OPERAND_VSR },
+  { "vs60", 60, PPC_OPERAND_VSR },
+  { "vs61", 61, PPC_OPERAND_VSR },
+  { "vs62", 62, PPC_OPERAND_VSR },
+  { "vs63", 63, PPC_OPERAND_VSR },
+  { "vs7", 7, PPC_OPERAND_VSR },
+  { "vs8", 8, PPC_OPERAND_VSR },
+  { "vs9", 9, PPC_OPERAND_VSR },
+
+  { "xer", 1, PPC_OPERAND_SPR }
 };
 
 #define REG_NAME_CNT   (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
@@ -807,7 +779,7 @@ static const struct pd_reg pre_defined_registers[] =
 /* Given NAME, find the register number associated with that name, return
    the integer value associated with the given name or -1 on failure.  */
 
-static int
+static const struct pd_reg *
 reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
 {
   int middle, low, high;
@@ -825,11 +797,11 @@ reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
       else if (cmp > 0)
        low = middle + 1;
       else
-       return regs[middle].value;
+       return &regs[middle];
     }
   while (low <= high);
 
-  return -1;
+  return NULL;
 }
 
 /*
@@ -844,10 +816,10 @@ reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
  *      original state.
  */
 
-static bfd_boolean
+static bool
 register_name (expressionS *expressionP)
 {
-  int reg_number;
+  const struct pd_reg *reg;
   char *name;
   char *start;
   char c;
@@ -858,29 +830,30 @@ register_name (expressionS *expressionP)
     name = ++input_line_pointer;
 
   else if (!reg_names_p || !ISALPHA (name[0]))
-    return FALSE;
+    return false;
 
   c = get_symbol_name (&name);
-  reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
+  reg = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
 
   /* Put back the delimiting char.  */
   *input_line_pointer = c;
 
   /* Look to see if it's in the register table.  */
-  if (reg_number >= 0)
+  if (reg != NULL)
     {
       expressionP->X_op = O_register;
-      expressionP->X_add_number = reg_number;
+      expressionP->X_add_number = reg->value;
+      expressionP->X_md = reg->flags;
 
       /* Make the rest nice.  */
       expressionP->X_add_symbol = NULL;
       expressionP->X_op_symbol = NULL;
-      return TRUE;
+      return true;
     }
 
   /* Reset the line as if we had not done anything.  */
   input_line_pointer = start;
-  return FALSE;
+  return false;
 }
 \f
 /* This function is called for each symbol seen in an expression.  It
@@ -888,24 +861,24 @@ register_name (expressionS *expressionP)
    to use for condition codes.  */
 
 /* Whether to do the special parsing.  */
-static bfd_boolean cr_operand;
+static bool cr_operand;
 
 /* Names to recognize in a condition code.  This table is sorted.  */
 static const struct pd_reg cr_names[] =
 {
-  { "cr0", 0 },
-  { "cr1", 1 },
-  { "cr2", 2 },
-  { "cr3", 3 },
-  { "cr4", 4 },
-  { "cr5", 5 },
-  { "cr6", 6 },
-  { "cr7", 7 },
-  { "eq", 2 },
-  { "gt", 1 },
-  { "lt", 0 },
-  { "so", 3 },
-  { "un", 3 }
+  { "cr0", 0, PPC_OPERAND_CR_REG },
+  { "cr1", 1, PPC_OPERAND_CR_REG },
+  { "cr2", 2, PPC_OPERAND_CR_REG },
+  { "cr3", 3, PPC_OPERAND_CR_REG },
+  { "cr4", 4, PPC_OPERAND_CR_REG },
+  { "cr5", 5, PPC_OPERAND_CR_REG },
+  { "cr6", 6, PPC_OPERAND_CR_REG },
+  { "cr7", 7, PPC_OPERAND_CR_REG },
+  { "eq", 2, PPC_OPERAND_CR_BIT },
+  { "gt", 1, PPC_OPERAND_CR_BIT },
+  { "lt", 0, PPC_OPERAND_CR_BIT },
+  { "so", 3, PPC_OPERAND_CR_BIT },
+  { "un", 3, PPC_OPERAND_CR_BIT }
 };
 
 /* Parsing function.  This returns non-zero if it recognized an
@@ -914,23 +887,78 @@ static const struct pd_reg cr_names[] =
 int
 ppc_parse_name (const char *name, expressionS *exp)
 {
-  int val;
+  const struct pd_reg *reg;
 
   if (! cr_operand)
     return 0;
 
   if (*name == '%')
     ++name;
-  val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
+  reg = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
                         name);
-  if (val < 0)
+  if (reg == NULL)
     return 0;
 
-  exp->X_op = O_constant;
-  exp->X_add_number = val;
+  exp->X_op = O_register;
+  exp->X_add_number = reg->value;
+  exp->X_md = reg->flags;
 
   return 1;
 }
+
+/* Propagate X_md and check register expressions.  This is to support
+   condition codes like 4*cr5+eq.  */
+
+int
+ppc_optimize_expr (expressionS *left, operatorT op, expressionS *right)
+{
+  /* Accept 4*cr<n> and cr<n>*4.  */
+  if (op == O_multiply
+      && ((right->X_op == O_register
+          && right->X_md == PPC_OPERAND_CR_REG
+          && left->X_op == O_constant
+          && left->X_add_number == 4)
+         || (left->X_op == O_register
+             && left->X_md == PPC_OPERAND_CR_REG
+             && right->X_op == O_constant
+             && right->X_add_number == 4)))
+    {
+      left->X_op = O_register;
+      left->X_md = PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT;
+      left->X_add_number *= right->X_add_number;
+      return 1;
+    }
+
+  /* Accept the above plus <cr bit>, and <cr bit> plus the above.  */
+  if (right->X_op == O_register
+      && left->X_op == O_register
+      && op == O_add
+      && ((right->X_md == PPC_OPERAND_CR_BIT
+          && left->X_md == (PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT))
+         || (right->X_md == (PPC_OPERAND_CR_REG | PPC_OPERAND_CR_BIT)
+             && left->X_md == PPC_OPERAND_CR_BIT)))
+    {
+      left->X_md = PPC_OPERAND_CR_BIT;
+      right->X_op = O_constant;
+      return 0;
+    }
+
+  /* Accept reg +/- constant.  */
+  if (left->X_op == O_register
+      && !((op == O_add || op == O_subtract) && right->X_op == O_constant))
+    as_warn (_("invalid register expression"));
+
+  /* Accept constant + reg.  */
+  if (right->X_op == O_register)
+    {
+      if (op == O_add && left->X_op == O_constant)
+       left->X_md = right->X_md;
+      else
+       as_warn (_("invalid register expression"));
+    }
+
+  return 0;
+}
 \f
 /* Local variables.  */
 
@@ -938,10 +966,10 @@ ppc_parse_name (const char *name, expressionS *exp)
 static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
 
 /* Opcode hash table.  */
-static struct hash_control *ppc_hash;
+static htab_t ppc_hash;
 
 /* Macro hash table.  */
-static struct hash_control *ppc_macro_hash;
+static htab_t ppc_macro_hash;
 
 #ifdef OBJ_ELF
 /* What type of shared library support to use.  */
@@ -952,33 +980,53 @@ static flagword ppc_flags = 0;
 
 /* Whether this is Solaris or not.  */
 #ifdef TARGET_SOLARIS_COMMENT
-#define SOLARIS_P TRUE
+#define SOLARIS_P true
 #else
-#define SOLARIS_P FALSE
+#define SOLARIS_P false
 #endif
 
-static bfd_boolean msolaris = SOLARIS_P;
+static bool msolaris = SOLARIS_P;
 #endif
 
 #ifdef OBJ_XCOFF
 
 /* The RS/6000 assembler uses the .csect pseudo-op to generate code
    using a bunch of different sections.  These assembler sections,
-   however, are all encompassed within the .text or .data sections of
-   the final output file.  We handle this by using different
-   subsegments within these main segments.  */
-
-/* Next subsegment to allocate within the .text segment.  */
-static subsegT ppc_text_subsegment = 2;
-
-/* Linked list of csects in the text section.  */
-static symbolS *ppc_text_csects;
-
-/* Next subsegment to allocate within the .data segment.  */
-static subsegT ppc_data_subsegment = 2;
+   however, are all encompassed within the .text, .data or .bss sections
+   of the final output file.  We handle this by using different
+   subsegments within these main segments.
+   .tdata and .tbss sections only have one type of csects for now,
+   but it's better to follow the same construction like the others.  */
+
+struct ppc_xcoff_section ppc_xcoff_text_section;
+struct ppc_xcoff_section ppc_xcoff_data_section;
+struct ppc_xcoff_section ppc_xcoff_bss_section;
+struct ppc_xcoff_section ppc_xcoff_tdata_section;
+struct ppc_xcoff_section ppc_xcoff_tbss_section;
+
+/* Return true if the ppc_xcoff_section structure is already
+   initialized.  */
+static bool
+ppc_xcoff_section_is_initialized (struct ppc_xcoff_section *section)
+{
+  return section->segment != NULL;
+}
 
-/* Linked list of csects in the data section.  */
-static symbolS *ppc_data_csects;
+/* Initialize a ppc_xcoff_section.
+   Dummy symbols are used to ensure the position of .text over .data
+   and .tdata.  These symbols won't be output.  */
+static void
+ppc_init_xcoff_section (struct ppc_xcoff_section *s, segT seg,
+                       bool need_dummy)
+{
+  s->segment = seg;
+  s->next_subsegment = 2;
+  if (need_dummy)
+    {
+      s->csects = symbol_make ("dummy\001");
+      symbol_get_tc (s->csects)->within = s->csects;
+    }
+}
 
 /* The current csect.  */
 static symbolS *ppc_current_csect;
@@ -1029,35 +1077,8 @@ static struct dw_section {
 } dw_sections[XCOFF_DWSECT_NBR_NAMES];
 #endif /* OBJ_XCOFF */
 
-#ifdef TE_PE
-
-/* Various sections that we need for PE coff support.  */
-static segT ydata_section;
-static segT pdata_section;
-static segT reldata_section;
-static segT rdata_section;
-static segT tocdata_section;
-
-/* The current section and the previous section. See ppc_previous.  */
-static segT ppc_previous_section;
-static segT ppc_current_section;
-
-#endif /* TE_PE */
-
 #ifdef OBJ_ELF
 symbolS *GOT_symbol;           /* Pre-defined "_GLOBAL_OFFSET_TABLE" */
-#define PPC_APUINFO_ISEL       0x40
-#define PPC_APUINFO_PMR                0x41
-#define PPC_APUINFO_RFMCI      0x42
-#define PPC_APUINFO_CACHELCK   0x43
-#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
- */
 unsigned long *ppc_apuinfo_list;
 unsigned int ppc_apuinfo_num;
 unsigned int ppc_apuinfo_num_alloc;
@@ -1149,7 +1170,8 @@ md_parse_option (int c, const char *arg)
 
     case 'm':
       new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, arg);
-      if (new_cpu != 0)
+      /* "raw" is only valid for the disassembler.  */
+      if (new_cpu != 0 && (new_cpu & PPC_OPCODE_RAW) == 0)
        {
          ppc_cpu = new_cpu;
          if (strcmp (arg, "vle") == 0)
@@ -1161,11 +1183,21 @@ md_parse_option (int c, const char *arg)
            }
        }
 
+      else if (strcmp (arg, "no-vle") == 0)
+       {
+         sticky &= ~PPC_OPCODE_VLE;
+
+         new_cpu = ppc_parse_cpu (ppc_cpu, &sticky, "booke");
+         new_cpu &= ~PPC_OPCODE_VLE;
+
+         ppc_cpu = new_cpu;
+       }
+
       else if (strcmp (arg, "regnames") == 0)
-       reg_names_p = TRUE;
+       reg_names_p = true;
 
       else if (strcmp (arg, "no-regnames") == 0)
-       reg_names_p = FALSE;
+       reg_names_p = false;
 
 #ifdef OBJ_ELF
       /* -mrelocatable/-mrelocatable-lib -- warn about initializations
@@ -1204,15 +1236,19 @@ md_parse_option (int c, const char *arg)
 
       else if (strcmp (arg, "solaris") == 0)
        {
-         msolaris = TRUE;
+         msolaris = true;
          ppc_comment_chars = ppc_solaris_comment_chars;
        }
 
       else if (strcmp (arg, "no-solaris") == 0)
        {
-         msolaris = FALSE;
+         msolaris = false;
          ppc_comment_chars = ppc_eabi_comment_chars;
        }
+      else if (strcmp (arg, "spe2") == 0)
+       {
+         ppc_cpu |= PPC_OPCODE_SPE2;
+       }
 #endif
       else
        {
@@ -1261,75 +1297,148 @@ md_parse_option (int c, const char *arg)
   return 1;
 }
 
+static int
+is_ppc64_target (const bfd_target *targ, void *data ATTRIBUTE_UNUSED)
+{
+  switch (targ->flavour)
+    {
+#ifdef OBJ_ELF
+    case bfd_target_elf_flavour:
+      return startswith (targ->name, "elf64-powerpc");
+#endif
+#ifdef OBJ_XCOFF
+    case bfd_target_xcoff_flavour:
+      return (strcmp (targ->name, "aixcoff64-rs6000") == 0
+             || strcmp (targ->name, "aix5coff64-rs6000") == 0);
+#endif
+    default:
+      return 0;
+    }
+}
+
 void
 md_show_usage (FILE *stream)
 {
   fprintf (stream, _("\
-PowerPC options:\n\
--a32                    generate ELF32/XCOFF32\n\
--a64                    generate ELF64/XCOFF64\n\
--u                      ignored\n\
--mpwrx, -mpwr2          generate code for POWER/2 (RIOS2)\n\
--mpwr                   generate code for POWER (RIOS1)\n\
--m601                   generate code for PowerPC 601\n\
+PowerPC options:\n"));
+  fprintf (stream, _("\
+-a32                    generate ELF32/XCOFF32\n"));
+  if (bfd_iterate_over_targets (is_ppc64_target, NULL))
+    fprintf (stream, _("\
+-a64                    generate ELF64/XCOFF64\n"));
+  fprintf (stream, _("\
+-u                      ignored\n"));
+  fprintf (stream, _("\
+-mpwrx, -mpwr2          generate code for POWER/2 (RIOS2)\n"));
+  fprintf (stream, _("\
+-mpwr                   generate code for POWER (RIOS1)\n"));
+  fprintf (stream, _("\
+-m601                   generate code for PowerPC 601\n"));
+  fprintf (stream, _("\
 -mppc, -mppc32, -m603, -m604\n\
-                        generate code for PowerPC 603/604\n\
--m403                   generate code for PowerPC 403\n\
--m405                   generate code for PowerPC 405\n\
--m440                   generate code for PowerPC 440\n\
--m464                   generate code for PowerPC 464\n\
--m476                   generate code for PowerPC 476\n\
+                        generate code for PowerPC 603/604\n"));
+  fprintf (stream, _("\
+-m403                   generate code for PowerPC 403\n"));
+  fprintf (stream, _("\
+-m405                   generate code for PowerPC 405\n"));
+  fprintf (stream, _("\
+-m440                   generate code for PowerPC 440\n"));
+  fprintf (stream, _("\
+-m464                   generate code for PowerPC 464\n"));
+  fprintf (stream, _("\
+-m476                   generate code for PowerPC 476\n"));
+  fprintf (stream, _("\
 -m7400, -m7410, -m7450, -m7455\n\
-                        generate code for PowerPC 7400/7410/7450/7455\n\
--m750cl                 generate code for PowerPC 750cl\n\
+                        generate code for PowerPC 7400/7410/7450/7455\n"));
+  fprintf (stream, _("\
+-m750cl, -mgekko, -mbroadway\n\
+                        generate code for PowerPC 750cl/Gekko/Broadway\n"));
+  fprintf (stream, _("\
 -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\
--mbooke                 generate code for 32-bit PowerPC BookE\n\
--ma2                    generate code for A2 architecture\n\
--mpower4, -mpwr4        generate code for Power4 architecture\n\
+-mppc64, -m620          generate code for PowerPC 620/625/630\n"));
+  fprintf (stream, _("\
+-mppc64bridge           generate code for PowerPC 64, including bridge insns\n"));
+  fprintf (stream, _("\
+-mbooke                 generate code for 32-bit PowerPC BookE\n"));
+  fprintf (stream, _("\
+-ma2                    generate code for A2 architecture\n"));
+  fprintf (stream, _("\
+-mpower4, -mpwr4        generate code for Power4 architecture\n"));
+  fprintf (stream, _("\
 -mpower5, -mpwr5, -mpwr5x\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\
+                        generate code for Power5 architecture\n"));
+  fprintf (stream, _("\
+-mpower6, -mpwr6        generate code for Power6 architecture\n"));
+  fprintf (stream, _("\
+-mpower7, -mpwr7        generate code for Power7 architecture\n"));
+  fprintf (stream, _("\
+-mpower8, -mpwr8        generate code for Power8 architecture\n"));
+  fprintf (stream, _("\
+-mpower9, -mpwr9        generate code for Power9 architecture\n"));
+  fprintf (stream, _("\
+-mpower10, -mpwr10      generate code for Power10 architecture\n"));
+  fprintf (stream, _("\
+-mcell                  generate code for Cell Broadband Engine architecture\n"));
+  fprintf (stream, _("\
+-mcom                   generate code for Power/PowerPC common instructions\n"));
+  fprintf (stream, _("\
 -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\
+-maltivec               generate code for AltiVec\n"));
+  fprintf (stream, _("\
+-mvsx                   generate code for Vector-Scalar (VSX) instructions\n"));
+  fprintf (stream, _("\
+-me300                  generate code for PowerPC e300 family\n"));
+  fprintf (stream, _("\
+-me500, -me500x2        generate code for Motorola e500 core complex\n"));
+  fprintf (stream, _("\
+-me500mc,               generate code for Freescale e500mc core complex\n"));
+  fprintf (stream, _("\
+-me500mc64,             generate code for Freescale e500mc64 core complex\n"));
+  fprintf (stream, _("\
+-me5500,                generate code for Freescale e5500 core complex\n"));
+  fprintf (stream, _("\
+-me6500,                generate code for Freescale e6500 core complex\n"));
+  fprintf (stream, _("\
+-mspe                   generate code for Motorola SPE instructions\n"));
+  fprintf (stream, _("\
+-mspe2                  generate code for Freescale SPE2 instructions\n"));
+  fprintf (stream, _("\
+-mvle                   generate code for Freescale VLE instructions\n"));
+  fprintf (stream, _("\
+-mtitan                 generate code for AppliedMicro Titan core complex\n"));
+  fprintf (stream, _("\
+-mregnames              Allow symbolic names for registers\n"));
+  fprintf (stream, _("\
 -mno-regnames           Do not allow symbolic names for registers\n"));
 #ifdef OBJ_ELF
   fprintf (stream, _("\
--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\
+-mrelocatable           support for GCC's -mrelocatble option\n"));
+  fprintf (stream, _("\
+-mrelocatable-lib       support for GCC's -mrelocatble-lib option\n"));
+  fprintf (stream, _("\
+-memb                   set PPC_EMB bit in ELF flags\n"));
+  fprintf (stream, _("\
 -mlittle, -mlittle-endian, -le\n\
-                        generate code for a little endian machine\n\
+                        generate code for a little endian machine\n"));
+  fprintf (stream, _("\
 -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\
+                        generate code for a big endian machine\n"));
+  fprintf (stream, _("\
+-msolaris               generate code for Solaris\n"));
+  fprintf (stream, _("\
+-mno-solaris            do not generate code for Solaris\n"));
+  fprintf (stream, _("\
+-K PIC                  set EF_PPC_RELOCATABLE_LIB in ELF flags\n"));
+  fprintf (stream, _("\
+-V                      print assembler version number\n"));
+  fprintf (stream, _("\
 -Qy, -Qn                ignored\n"));
 #endif
   fprintf (stream, _("\
--nops=count             when aligning, more than COUNT nops uses a branch\n\
+-nops=count             when aligning, more than COUNT nops uses a branch\n"));
+  fprintf (stream, _("\
 -ppc476-workaround      warn if emitting data to code sections\n"));
 }
 \f
@@ -1344,15 +1453,19 @@ ppc_set_cpu (void)
   if ((ppc_cpu & ~(ppc_cpu_t) PPC_OPCODE_ANY) == 0)
     {
       if (ppc_obj64)
-       ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_64;
-      else if (strncmp (default_os, "aix", 3) == 0
+       if (target_big_endian)
+         ppc_cpu |= PPC_OPCODE_PPC | PPC_OPCODE_64;
+       else
+         /* The minimum supported cpu for 64-bit little-endian is power8.  */
+         ppc_cpu |= ppc_parse_cpu (ppc_cpu, &sticky, "power8");
+      else if (startswith (default_os, "aix")
               && default_os[3] >= '4' && default_os[3] <= '9')
        ppc_cpu |= PPC_OPCODE_COMMON;
-      else if (strncmp (default_os, "aix3", 4) == 0)
+      else if (startswith (default_os, "aix3"))
        ppc_cpu |= PPC_OPCODE_POWER;
       else if (strcmp (default_cpu, "rs6000") == 0)
        ppc_cpu |= PPC_OPCODE_POWER;
-      else if (strncmp (default_cpu, "powerpc", 7) == 0)
+      else if (startswith (default_cpu, "powerpc"))
        ppc_cpu |= PPC_OPCODE_PPC;
       else
        as_fatal (_("unknown default cpu = %s, os = %s"),
@@ -1379,7 +1492,7 @@ ppc_arch (void)
     {
       if (strcmp (default_cpu, "rs6000") == 0)
        return bfd_arch_rs6000;
-      else if (strncmp (default_cpu, "powerpc", 7) == 0)
+      else if (startswith (default_cpu, "powerpc"))
        return bfd_arch_powerpc;
     }
 
@@ -1406,9 +1519,7 @@ extern const char*
 ppc_target_format (void)
 {
 #ifdef OBJ_COFF
-#ifdef TE_PE
-  return target_big_endian ? "pe-powerpc" : "pe-powerpcle";
-#elif TE_POWERMAC
+#if TE_POWERMAC
   return "xcoff-powermac";
 #else
 #  ifdef TE_AIX5
@@ -1434,49 +1545,67 @@ ppc_target_format (void)
 /* Validate one entry in powerpc_opcodes[] or vle_opcodes[].
    Return TRUE if there's a problem, otherwise FALSE.  */
 
-static bfd_boolean
+static bool
 insn_validate (const struct powerpc_opcode *op)
 {
   const unsigned char *o;
-  unsigned long omask = op->mask;
+  uint64_t 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;
+      return true;
     }
 
   /* The operands must not overlap the opcode or each other.  */
   for (o = op->operands; *o; ++o)
     {
+      bool optional = false;
       if (*o >= num_powerpc_operands)
         {
          as_bad (_("operand index error for %s"), op->name);
-         return TRUE;
+         return true;
         }
       else
         {
+         uint64_t mask;
          const struct powerpc_operand *operand = &powerpc_operands[*o];
-         if (operand->shift != (int) PPC_OPSHIFT_INV)
+         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;
+             const char *errmsg;
+             uint64_t val;
+
+             errmsg = NULL;
+             val = -1;
+             if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+               val = -val;
+             else if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
+               val += 1;
+             mask = (*operand->insert) (0, val, ppc_cpu, &errmsg);
+           }
+         else 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;
+         if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
+           optional = true;
+         else if (optional)
+           {
+             as_bad (_("non-optional operand %d follows optional operand in %s"),
+                     (int) (o - op->operands), op->name);
+             return true;
            }
         }
     }
-  return FALSE;
+  return false;
 }
 
 /* Insert opcodes and macros into hash tables.  Called at startup and
@@ -1489,15 +1618,15 @@ ppc_setup_opcodes (void)
   const struct powerpc_opcode *op_end;
   const struct powerpc_macro *macro;
   const struct powerpc_macro *macro_end;
-  bfd_boolean bad_insn = FALSE;
+  bool bad_insn = false;
 
   if (ppc_hash != NULL)
-    hash_die (ppc_hash);
+    htab_delete (ppc_hash);
   if (ppc_macro_hash != NULL)
-    hash_die (ppc_macro_hash);
+    htab_delete (ppc_macro_hash);
 
   /* Insert the opcodes into a hash table.  */
-  ppc_hash = hash_new ();
+  ppc_hash = str_htab_create ();
 
   if (ENABLE_CHECKING)
     {
@@ -1511,8 +1640,8 @@ ppc_setup_opcodes (void)
         all the 1's in the mask are contiguous.  */
       for (i = 0; i < num_powerpc_operands; ++i)
        {
-         unsigned long mask = powerpc_operands[i].bitm;
-         unsigned long right_bit;
+         uint64_t mask = powerpc_operands[i].bitm;
+         uint64_t right_bit;
          unsigned int j;
 
          right_bit = mask & -mask;
@@ -1521,7 +1650,7 @@ ppc_setup_opcodes (void)
          if (mask != right_bit)
            {
              as_bad (_("powerpc_operands[%d].bitm invalid"), i);
-             bad_insn = TRUE;
+             bad_insn = true;
            }
          for (j = i + 1; j < num_powerpc_operands; ++j)
            if (memcmp (&powerpc_operands[i], &powerpc_operands[j],
@@ -1529,7 +1658,7 @@ ppc_setup_opcodes (void)
              {
                as_bad (_("powerpc_operands[%d] duplicates powerpc_operands[%d]"),
                        j, i);
-               bad_insn = TRUE;
+               bad_insn = true;
              }
        }
     }
@@ -1539,122 +1668,181 @@ ppc_setup_opcodes (void)
     {
       if (ENABLE_CHECKING)
        {
-         if (op != powerpc_opcodes)
-           {
-             int old_opcode = PPC_OP (op[-1].opcode);
-             int new_opcode = PPC_OP (op[0].opcode);
+         unsigned 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);
+         printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
+                 op->name, (unsigned int) (op - powerpc_opcodes),
+                 new_opcode, (unsigned long long) op->opcode,
+                 (unsigned long long) 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_opcode < old_opcode)
-               {
-                 as_bad (_("major opcode is not sorted for %s"),
-                         op->name);
-                 bad_insn = TRUE;
-               }
+         /* The major opcodes had better be sorted.  Code in the disassembler
+            assumes the insns are sorted according to major opcode.  */
+         if (op != powerpc_opcodes
+             && new_opcode < PPC_OP (op[-1].opcode))
+           {
+             as_bad (_("major opcode is not sorted for %s"), op->name);
+             bad_insn = true;
+           }
+
+         if ((op->flags & PPC_OPCODE_VLE) != 0)
+           {
+             as_bad (_("%s is enabled by vle flag"), op->name);
+             bad_insn = true;
+           }
+         if (PPC_OP (op->opcode) != 4
+             && PPC_OP (op->opcode) != 31
+             && (op->deprecated & PPC_OPCODE_VLE) == 0)
+           {
+             as_bad (_("%s not disabled by vle flag"), op->name);
+             bad_insn = true;
            }
          bad_insn |= insn_validate (op);
        }
 
       if ((ppc_cpu & op->flags) != 0
-         && !(ppc_cpu & op->deprecated))
+         && !(ppc_cpu & op->deprecated)
+         && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
+       {
+         as_bad (_("duplicate %s"), op->name);
+         bad_insn = true;
+       }
+    }
+
+  if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
+    for (op = powerpc_opcodes; op < op_end; op++)
+      str_hash_insert (ppc_hash, op->name, op, 0);
+
+  op_end = prefix_opcodes + prefix_num_opcodes;
+  for (op = prefix_opcodes; op < op_end; op++)
+    {
+      if (ENABLE_CHECKING)
        {
-         const char *retval;
+         unsigned int new_opcode = PPC_PREFIX_SEG (op[0].opcode);
+
+#ifdef PRINT_OPCODE_TABLE
+         printf ("%-14s\t#%04u\tmajor op/2: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
+                 op->name, (unsigned int) (op - prefix_opcodes),
+                 new_opcode, (unsigned long long) op->opcode,
+                 (unsigned long long) op->mask, (unsigned long long) op->flags);
+#endif
 
-         retval = hash_insert (ppc_hash, op->name, (void *) op);
-         if (retval != NULL)
+         /* The major opcodes had better be sorted.  Code in the disassembler
+            assumes the insns are sorted according to major opcode.  */
+         if (op != prefix_opcodes
+             && new_opcode < PPC_PREFIX_SEG (op[-1].opcode))
            {
-             as_bad (_("duplicate instruction %s"),
-                     op->name);
-             bad_insn = TRUE;
+             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
+         && !(ppc_cpu & op->deprecated)
+         && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
+       {
+         as_bad (_("duplicate %s"), op->name);
+         bad_insn = true;
        }
     }
 
   if ((ppc_cpu & PPC_OPCODE_ANY) != 0)
-    for (op = powerpc_opcodes; op < op_end; op++)
-      hash_insert (ppc_hash, op->name, (void *) op);
+    for (op = prefix_opcodes; op < op_end; op++)
+      str_hash_insert (ppc_hash, op->name, op, 0);
 
   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);
+         unsigned new_seg = VLE_OP_TO_SEG (VLE_OP (op[0].opcode, op[0].mask));
 
 #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);
+         printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
+                 op->name, (unsigned int) (op - vle_opcodes),
+                 (unsigned int) new_seg, (unsigned long long) op->opcode,
+                 (unsigned long long) 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;
-               }
+
+         /* The major opcodes had better be sorted.  Code in the disassembler
+            assumes the insns are sorted according to major opcode.  */
+         if (op != vle_opcodes
+             && new_seg < VLE_OP_TO_SEG (VLE_OP (op[-1].opcode, op[-1].mask)))
+           {
+             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
-         && !(ppc_cpu & op->deprecated))
+         && !(ppc_cpu & op->deprecated)
+         && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
        {
-         const char *retval;
-
-         retval = hash_insert (ppc_hash, op->name, (void *) op);
-         if (retval != NULL)
-           {
-             as_bad (_("duplicate instruction %s"),
-                     op->name);
-             bad_insn = TRUE;
-           }
+         as_bad (_("duplicate %s"), op->name);
+         bad_insn = true;
        }
     }
 
-  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.  */
-  ppc_macro_hash = hash_new ();
-
-  macro_end = powerpc_macros + powerpc_num_macros;
-  for (macro = powerpc_macros; macro < macro_end; macro++)
+  /* SPE2 instructions */
+  if ((ppc_cpu & PPC_OPCODE_SPE2) == PPC_OPCODE_SPE2)
     {
-      if ((macro->flags & ppc_cpu) != 0 || (ppc_cpu & PPC_OPCODE_ANY) != 0)
+      op_end = spe2_opcodes + spe2_num_opcodes;
+      for (op = spe2_opcodes; op < op_end; op++)
        {
-         const char *retval;
+         if (ENABLE_CHECKING)
+           {
+             if (op != spe2_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);
+
+               /* 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);
+           }
 
-         retval = hash_insert (ppc_macro_hash, macro->name, (void *) macro);
-         if (retval != (const char *) NULL)
+         if ((ppc_cpu & op->flags) != 0
+             && !(ppc_cpu & op->deprecated)
+             && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
            {
-             as_bad (_("duplicate macro %s"), macro->name);
-             bad_insn = TRUE;
+             as_bad (_("duplicate %s"), op->name);
+             bad_insn = true;
            }
        }
+
+      for (op = spe2_opcodes; op < op_end; op++)
+       str_hash_insert (ppc_hash, op->name, op, 0);
     }
 
+  /* Insert the macros into a hash table.  */
+  ppc_macro_hash = str_htab_create ();
+
+  macro_end = powerpc_macros + powerpc_num_macros;
+  for (macro = powerpc_macros; macro < macro_end; macro++)
+    if (((macro->flags & ppc_cpu) != 0
+        || (ppc_cpu & PPC_OPCODE_ANY) != 0)
+       && str_hash_insert (ppc_macro_hash, macro->name, macro, 0) != NULL)
+      {
+       as_bad (_("duplicate %s"), macro->name);
+       bad_insn = true;
+      }
+
   if (bad_insn)
     abort ();
 }
@@ -1690,20 +1878,12 @@ md_begin (void)
 #ifdef OBJ_XCOFF
   ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG);
 
-  /* Create dummy symbols to serve as initial csects.  This forces the
-     text csects to precede the data csects.  These symbols will not
-     be output.  */
-  ppc_text_csects = symbol_make ("dummy\001");
-  symbol_get_tc (ppc_text_csects)->within = ppc_text_csects;
-  ppc_data_csects = symbol_make ("dummy\001");
-  symbol_get_tc (ppc_data_csects)->within = ppc_data_csects;
-#endif
-
-#ifdef TE_PE
-
-  ppc_current_section = text_section;
-  ppc_previous_section = 0;
-
+  /* Create XCOFF sections with .text in first, as it's creating dummy symbols
+     to serve as initial csects.  This forces the text csects to precede the
+     data csects.  These symbols will not be output.  */
+  ppc_init_xcoff_section (&ppc_xcoff_text_section, text_section, true);
+  ppc_init_xcoff_section (&ppc_xcoff_data_section, data_section, true);
+  ppc_init_xcoff_section (&ppc_xcoff_bss_section, bss_section, true);
 #endif
 }
 
@@ -1734,10 +1914,8 @@ ppc_cleanup (void)
     unsigned int i;
 
     /* Create the .PPC.EMB.apuinfo section.  */
-    apuinfo_secp = subseg_new (".PPC.EMB.apuinfo", 0);
-    bfd_set_section_flags (stdoutput,
-                          apuinfo_secp,
-                          SEC_HAS_CONTENTS | SEC_READONLY);
+    apuinfo_secp = subseg_new (APUINFO_SECTION_NAME, 0);
+    bfd_set_section_flags (apuinfo_secp, SEC_HAS_CONTENTS | SEC_READONLY);
 
     p = frag_more (4);
     md_number_to_chars (p, (valueT) 8, 4);
@@ -1749,7 +1927,7 @@ ppc_cleanup (void)
     md_number_to_chars (p, (valueT) 2, 4);
 
     p = frag_more (8);
-    strcpy (p, "APUinfo");
+    strcpy (p, APUINFO_LABEL);
 
     for (i = 0; i < ppc_apuinfo_num; i++)
       {
@@ -1769,15 +1947,15 @@ ppc_cleanup (void)
 
 /* Insert an operand value into an instruction.  */
 
-static unsigned long
-ppc_insert_operand (unsigned long insn,
+static uint64_t
+ppc_insert_operand (uint64_t insn,
                    const struct powerpc_operand *operand,
-                   offsetT val,
+                   int64_t val,
                    ppc_cpu_t cpu,
                    const char *file,
                    unsigned int line)
 {
-  long min, max, right;
+  int64_t min, max, right;
 
   max = operand->bitm;
   right = max & -max;
@@ -1785,17 +1963,15 @@ ppc_insert_operand (unsigned long insn,
 
   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
+      /* Extend the allowed range for addis to [-32768, 65535].
+        Similarly for cmpli and 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;
+        lis.  */
+      min = ~(max >> 1) & -right;
     }
   else if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
     {
@@ -1808,7 +1984,7 @@ ppc_insert_operand (unsigned long insn,
 
   if ((operand->flags & PPC_OPERAND_NEGATIVE) != 0)
     {
-      long tmp = min;
+      int64_t tmp = min;
       min = -max;
       max = -tmp;
     }
@@ -1819,20 +1995,23 @@ ppc_insert_operand (unsigned long insn,
         hand but only up to 32 bits.  This shouldn't really be valid,
         but, to permit this code to assemble on a 64-bit host, we
         sign extend the 32-bit value to 64 bits if so doing makes the
-        value valid.  */
+        value valid.  We only do this for operands that are 32-bits or
+        smaller.  */
       if (val > max
-         && (offsetT) (val - 0x80000000 - 0x80000000) >= min
-         && (offsetT) (val - 0x80000000 - 0x80000000) <= max
-         && ((val - 0x80000000 - 0x80000000) & (right - 1)) == 0)
-       val = val - 0x80000000 - 0x80000000;
+         && (operand->bitm & ~0xffffffffULL) == 0
+         && (val - (1LL << 32)) >= min
+         && (val - (1LL << 32)) <= max
+         && ((val - (1LL << 32)) & (right - 1)) == 0)
+       val = val - (1LL << 32);
 
       /* Similarly, people write expressions like ~(1<<15), and expect
         this to be OK for a 32-bit unsigned value.  */
       else if (val < min
-              && (offsetT) (val + 0x80000000 + 0x80000000) >= min
-              && (offsetT) (val + 0x80000000 + 0x80000000) <= max
-              && ((val + 0x80000000 + 0x80000000) & (right - 1)) == 0)
-       val = val + 0x80000000 + 0x80000000;
+              && (operand->bitm & ~0xffffffffULL) == 0
+              && (val + (1LL << 32)) >= min
+              && (val + (1LL << 32)) <= max
+              && ((val + (1LL << 32)) & (right - 1)) == 0)
+       val = val + (1LL << 32);
 
       else if (val < min
               || val > max
@@ -1845,14 +2024,14 @@ ppc_insert_operand (unsigned long insn,
       const char *errmsg;
 
       errmsg = NULL;
-      insn = (*operand->insert) (insn, (long) val, cpu, &errmsg);
+      insn = (*operand->insert) (insn, val, cpu, &errmsg);
       if (errmsg != (const char *) NULL)
        as_bad_where (file, line, "%s", errmsg);
     }
   else if (operand->shift >= 0)
-    insn |= ((long) val & operand->bitm) << operand->shift;
+    insn |= (val & operand->bitm) << operand->shift;
   else
-    insn |= ((long) val & operand->bitm) >> -operand->shift;
+    insn |= (val & operand->bitm) >> -operand->shift;
 
   return insn;
 }
@@ -1979,6 +2158,19 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP64 ("tprel@highera",    BFD_RELOC_PPC64_TPREL16_HIGHERA),
     MAP64 ("tprel@highest",    BFD_RELOC_PPC64_TPREL16_HIGHEST),
     MAP64 ("tprel@highesta",   BFD_RELOC_PPC64_TPREL16_HIGHESTA),
+    MAP64 ("notoc",            BFD_RELOC_PPC64_REL24_NOTOC),
+    MAP64 ("pcrel",            BFD_RELOC_PPC64_PCREL34),
+    MAP64 ("got@pcrel",                BFD_RELOC_PPC64_GOT_PCREL34),
+    MAP64 ("plt@pcrel",                BFD_RELOC_PPC64_PLT_PCREL34),
+    MAP64 ("tls@pcrel",                BFD_RELOC_PPC64_TLS_PCREL),
+    MAP64 ("got@tlsgd@pcrel",  BFD_RELOC_PPC64_GOT_TLSGD_PCREL34),
+    MAP64 ("got@tlsld@pcrel",  BFD_RELOC_PPC64_GOT_TLSLD_PCREL34),
+    MAP64 ("got@tprel@pcrel",  BFD_RELOC_PPC64_GOT_TPREL_PCREL34),
+    MAP64 ("got@dtprel@pcrel", BFD_RELOC_PPC64_GOT_DTPREL_PCREL34),
+    MAP64 ("higher34",         BFD_RELOC_PPC64_ADDR16_HIGHER34),
+    MAP64 ("highera34",                BFD_RELOC_PPC64_ADDR16_HIGHERA34),
+    MAP64 ("highest34",                BFD_RELOC_PPC64_ADDR16_HIGHEST34),
+    MAP64 ("highesta34",       BFD_RELOC_PPC64_ADDR16_HIGHESTA34),
     { (char *) 0, 0, 0, 0,     BFD_RELOC_NONE }
   };
 
@@ -2005,7 +2197,7 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
       {
        int reloc = ptr->reloc;
 
-       if (!ppc_obj64 && exp_p->X_add_number != 0)
+       if (!ppc_obj64 && (exp_p->X_op == O_big || exp_p->X_add_number != 0))
          {
            switch (reloc)
              {
@@ -2013,8 +2205,8 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
              case BFD_RELOC_LO16_GOTOFF:
              case BFD_RELOC_HI16_GOTOFF:
              case BFD_RELOC_HI16_S_GOTOFF:
-               as_warn (_("identifier+constant@got means "
-                          "identifier@got+constant"));
+               as_warn (_("symbol+offset@%s means symbol@%s+offset"),
+                        ptr->string, ptr->string);
                break;
 
              case BFD_RELOC_PPC_GOT_TLSGD16:
@@ -2033,7 +2225,7 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
              case BFD_RELOC_PPC_GOT_TPREL16_LO:
              case BFD_RELOC_PPC_GOT_TPREL16_HI:
              case BFD_RELOC_PPC_GOT_TPREL16_HA:
-               as_bad (_("symbol+offset not supported for got tls"));
+               as_bad (_("symbol+offset@%s not supported"), ptr->string);
                break;
              }
          }
@@ -2046,14 +2238,12 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
 
            input_line_pointer = str;
            expression (&new_exp);
-           if (new_exp.X_op == O_constant)
+           if (new_exp.X_op == O_constant && exp_p->X_op != O_big)
              {
                exp_p->X_add_number += new_exp.X_add_number;
                str = input_line_pointer;
              }
-
-           if (&input_line_pointer != str_p)
-             input_line_pointer = orig_line;
+           input_line_pointer = orig_line;
          }
        *str_p = str;
 
@@ -2273,15 +2463,25 @@ ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED)
   if (resolve_expression (&exp)
       && exp.X_op == O_constant)
     {
-      unsigned char encoded = PPC64_SET_LOCAL_ENTRY_OFFSET (exp.X_add_number);
+      unsigned int encoded, ok;
 
-      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));
+      ok = 1;
+      if (exp.X_add_number == 1 || exp.X_add_number == 7)
+       encoded = exp.X_add_number << STO_PPC64_LOCAL_BIT;
       else
+       {
+         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));
+             ok = 0;
+           }
+       }
+      if (ok)
        {
          bfdsym = symbol_get_bfdsym (sym);
-         elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         elfsym = elf_symbol_from (bfdsym);
          gas_assert (elfsym);
          elfsym->internal_elf_sym.st_other &= ~STO_PPC64_LOCAL_MASK;
          elfsym->internal_elf_sym.st_other |= encoded;
@@ -2318,6 +2518,28 @@ ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* Parse a .gnu_attribute directive.  */
+static void
+ppc_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
+{
+  int tag = obj_elf_vendor_attribute (OBJ_ATTR_GNU);
+
+  /* Check validity of defined powerpc tags.  */
+  if (tag == Tag_GNU_Power_ABI_FP
+      || tag == Tag_GNU_Power_ABI_Vector
+      || tag == Tag_GNU_Power_ABI_Struct_Return)
+    {
+      unsigned int val;
+
+      val = bfd_elf_get_obj_attr_int (stdoutput, OBJ_ATTR_GNU, tag);
+
+      if ((tag == Tag_GNU_Power_ABI_FP && val > 15)
+         || (tag == Tag_GNU_Power_ABI_Vector && val > 3)
+         || (tag == Tag_GNU_Power_ABI_Struct_Return && val > 2))
+       as_warn (_("unknown .gnu_attribute value"));
+    }
+}
+
 /* Set ABI version in output file.  */
 void
 ppc_elf_end (void)
@@ -2327,6 +2549,12 @@ ppc_elf_end (void)
       elf_elfheader (stdoutput)->e_flags &= ~EF_PPC64_ABI;
       elf_elfheader (stdoutput)->e_flags |= ppc_abiversion & EF_PPC64_ABI;
     }
+  /* Any selection of opcodes based on ppc_cpu after gas has finished
+     parsing the file is invalid.  md_apply_fix and ppc_handle_align
+     must select opcodes based on the machine in force at the point
+     where the fixup or alignment frag was created, not the machine in
+     force at the end of file.  */
+  ppc_cpu = 0;
 }
 
 /* Validate any relocations emitted for -mrelocatable, possibly adding
@@ -2390,7 +2618,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] == '.')
@@ -2400,10 +2627,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)
@@ -2415,7 +2639,7 @@ ppc_frob_file_before_adjust (void)
   toc = bfd_get_section_by_name (stdoutput, ".toc");
   if (toc != NULL
       && toc_reloc_types != has_large_toc_reloc
-      && bfd_section_size (stdoutput, toc) > 0x10000)
+      && bfd_section_size (toc) > 0x10000)
     as_warn (_("TOC section size exceeds 64k"));
 }
 
@@ -2439,102 +2663,116 @@ ppc_elf_adjust_symtab (void)
     }
 }
 #endif /* OBJ_ELF */
-\f
-#ifdef TE_PE
-
-/*
- * Summary of parse_toc_entry.
- *
- * in: Input_line_pointer points to the '[' in one of:
- *
- *        [toc] [tocv] [toc32] [toc64]
- *
- *      Anything else is an error of one kind or another.
- *
- * out:
- *   return value: success or failure
- *   toc_kind:     kind of toc reference
- *   input_line_pointer:
- *     success: first char after the ']'
- *     failure: unchanged
- *
- * settings:
- *
- *     [toc]   - rv == success, toc_kind = default_toc
- *     [tocv]  - rv == success, toc_kind = data_in_toc
- *     [toc32] - rv == success, toc_kind = must_be_32
- *     [toc64] - rv == success, toc_kind = must_be_64
- *
- */
 
-enum toc_size_qualifier
+#ifdef OBJ_XCOFF
+/* Parse XCOFF relocations.  */
+static bfd_reloc_code_real_type
+ppc_xcoff_suffix (char **str_p)
 {
-  default_toc, /* The toc cell constructed should be the system default size */
-  data_in_toc, /* This is a direct reference to a toc cell                   */
-  must_be_32,  /* The toc cell constructed must be 32 bits wide              */
-  must_be_64   /* The toc cell constructed must be 64 bits wide              */
-};
+  struct map_bfd {
+    const char *string;
+    unsigned int length : 8;
+    unsigned int valid32 : 1;
+    unsigned int valid64 : 1;
+    unsigned int reloc;
+  };
 
-static int
-parse_toc_entry (enum toc_size_qualifier *toc_kind)
-{
-  char *start;
-  char *toc_spec;
-  char c;
-  enum toc_size_qualifier t;
+  char ident[20];
+  char *str = *str_p;
+  char *str2;
+  int ch;
+  int len;
+  const struct map_bfd *ptr;
 
-  /* Save the input_line_pointer.  */
-  start = input_line_pointer;
+#define MAP(str, reloc)   { str, sizeof (str) - 1, 1, 1, reloc }
+#define MAP32(str, reloc) { str, sizeof (str) - 1, 1, 0, reloc }
+#define MAP64(str, reloc) { str, sizeof (str) - 1, 0, 1, reloc }
 
-  /* Skip over the '[' , and whitespace.  */
-  ++input_line_pointer;
-  SKIP_WHITESPACE ();
+  static const struct map_bfd mapping[] = {
+    MAP ("l",                  BFD_RELOC_PPC_TOC16_LO),
+    MAP ("u",                  BFD_RELOC_PPC_TOC16_HI),
+    MAP32 ("ie",               BFD_RELOC_PPC_TLSIE),
+    MAP32 ("ld",               BFD_RELOC_PPC_TLSLD),
+    MAP32 ("le",               BFD_RELOC_PPC_TLSLE),
+    MAP32 ("m",                BFD_RELOC_PPC_TLSM),
+    MAP32 ("ml",               BFD_RELOC_PPC_TLSML),
+    MAP64 ("ie",               BFD_RELOC_PPC64_TLSIE),
+    MAP64 ("ld",               BFD_RELOC_PPC64_TLSLD),
+    MAP64 ("le",               BFD_RELOC_PPC64_TLSLE),
+    MAP64 ("m",                BFD_RELOC_PPC64_TLSM),
+    MAP64 ("ml",               BFD_RELOC_PPC64_TLSML),
+  };
 
-  /* Find the spelling of the operand.  */
-  c = get_symbol_name (&toc_spec);
+  if (*str++ != '@')
+    return BFD_RELOC_NONE;
 
-  if (strcmp (toc_spec, "toc") == 0)
-    {
-      t = default_toc;
-    }
-  else if (strcmp (toc_spec, "tocv") == 0)
-    {
-      t = data_in_toc;
-    }
-  else if (strcmp (toc_spec, "toc32") == 0)
+  for (ch = *str, str2 = ident;
+       (str2 < ident + sizeof (ident) - 1
+       && (ISALNUM (ch) || ch == '@'));
+       ch = *++str)
     {
-      t = must_be_32;
-    }
-  else if (strcmp (toc_spec, "toc64") == 0)
-    {
-      t = must_be_64;
-    }
-  else
-    {
-      as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec);
-      *input_line_pointer = c;
-      input_line_pointer = start;
-      return 0;
+      *str2++ = TOLOWER (ch);
     }
 
-  /* Now find the ']'.  */
-  *input_line_pointer = c;
+  *str2 = '\0';
+  len = str2 - ident;
 
-  SKIP_WHITESPACE_AFTER_NAME ();       /* leading whitespace could be there.  */
-  c = *input_line_pointer++; /* input_line_pointer->past char in c.  */
+  ch = ident[0];
+  for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+    if (ch == ptr->string[0]
+       && len == ptr->length
+       && memcmp (ident, ptr->string, ptr->length) == 0
+       && (ppc_obj64 ? ptr->valid64 : ptr->valid32))
+      {
+       *str_p = str;
+       return (bfd_reloc_code_real_type) ptr->reloc;
+      }
 
-  if (c != ']')
-    {
-      as_bad (_("syntax error: expected `]', found  `%c'"), c);
-      input_line_pointer = start;
-      return 0;
-    }
+  return BFD_RELOC_NONE;
+}
 
-  *toc_kind = t;
-  return 1;
+/* Restore XCOFF addis instruction to ELF format.
+   AIX often generates addis instructions using "addis RT,D(RA)"
+   format instead of the ELF "addis RT,RA,SI" one.
+   On entry RT_E is at the comma after RT, D_E is at the open
+   parenthesis after D, and RA_E is at the close parenthesis after RA.  */
+static void
+ppc_xcoff_fixup_addis (char *rt_e, char *d_e, char *ra_e)
+{
+  size_t ra_size = ra_e - d_e - 1;
+  char *save_ra = xmalloc (ra_size);
+
+  /* Copy RA.  */
+  memcpy (save_ra, d_e + 1, ra_size);
+  /* Shuffle D to make room for RA, copying the comma too.  */
+  memmove (rt_e + ra_size + 1, rt_e, d_e - rt_e);
+  /* Erase the trailing ')', keeping any rubbish for potential errors.  */
+  memmove (ra_e, ra_e + 1, strlen (ra_e));
+  /* Write RA back.  */
+  memcpy (rt_e + 1, save_ra, ra_size);
+  free (save_ra);
+}
+
+/* Support @ie, etc. on constants emitted via .short, .int etc.  */
+
+bfd_reloc_code_real_type
+ppc_xcoff_parse_cons (expressionS *exp, unsigned int nbytes)
+{
+  expression (exp);
+  if (nbytes >= 2 && *input_line_pointer == '@')
+    return ppc_xcoff_suffix (&input_line_pointer);
+
+  /* There isn't any @ symbol for default TLS relocations (R_TLS).  */
+  if (exp->X_add_symbol != NULL
+      && (symbol_get_tc (exp->X_add_symbol)->symbol_class == XMC_TL
+         || symbol_get_tc (exp->X_add_symbol)->symbol_class == XMC_UL))
+      return (ppc_obj64 ? BFD_RELOC_PPC64_TLSGD: BFD_RELOC_PPC_TLSGD);
+
+  return BFD_RELOC_NONE;
 }
-#endif
 
+#endif /* OBJ_XCOFF */
+\f
 #if defined (OBJ_XCOFF) || defined (OBJ_ELF)
 /* See whether a symbol is in the TOC section.  */
 
@@ -2543,6 +2781,7 @@ 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_TE
          || symbol_get_tc (sym)->symbol_class == XMC_TC0);
 #endif
 #ifdef OBJ_ELF
@@ -2573,14 +2812,13 @@ ppc_apuinfo_section_add (unsigned int apu, unsigned int version)
       if (ppc_apuinfo_num_alloc == 0)
        {
          ppc_apuinfo_num_alloc = 4;
-         ppc_apuinfo_list = (unsigned long *)
-             xmalloc (sizeof (unsigned long) * ppc_apuinfo_num_alloc);
+         ppc_apuinfo_list = XNEWVEC (unsigned long, ppc_apuinfo_num_alloc);
        }
       else
        {
          ppc_apuinfo_num_alloc += 4;
-         ppc_apuinfo_list = (unsigned long *) xrealloc (ppc_apuinfo_list,
-             sizeof (unsigned long) * ppc_apuinfo_num_alloc);
+         ppc_apuinfo_list = XRESIZEVEC (unsigned long, ppc_apuinfo_list,
+                                        ppc_apuinfo_num_alloc);
        }
     }
   ppc_apuinfo_list[ppc_apuinfo_num++] = APUID (apu, version);
@@ -2588,6 +2826,90 @@ ppc_apuinfo_section_add (unsigned int apu, unsigned int version)
 #undef APUID
 #endif
 \f
+/* Various frobbings of labels and their addresses.  */
+
+/* Symbols labelling the current insn.  */
+struct insn_label_list
+{
+  struct insn_label_list *next;
+  symbolS *label;
+};
+
+static struct insn_label_list *insn_labels;
+static struct insn_label_list *free_insn_labels;
+
+static void
+ppc_record_label (symbolS *sym)
+{
+  struct insn_label_list *l;
+
+  if (free_insn_labels == NULL)
+    l = XNEW (struct insn_label_list);
+  else
+    {
+      l = free_insn_labels;
+      free_insn_labels = l->next;
+    }
+
+  l->label = sym;
+  l->next = insn_labels;
+  insn_labels = l;
+}
+
+static void
+ppc_clear_labels (void)
+{
+  while (insn_labels != NULL)
+    {
+      struct insn_label_list *l = insn_labels;
+      insn_labels = l->next;
+      l->next = free_insn_labels;
+      free_insn_labels = l;
+    }
+}
+
+void
+ppc_start_line_hook (void)
+{
+  ppc_clear_labels ();
+}
+
+void
+ppc_new_dot_label (symbolS *sym)
+{
+  ppc_record_label (sym);
+#ifdef OBJ_XCOFF
+  /* Anchor this label to the current csect for relocations.  */
+  symbol_get_tc (sym)->within = ppc_current_csect;
+#endif
+}
+
+void
+ppc_frob_label (symbolS *sym)
+{
+  ppc_record_label (sym);
+
+#ifdef OBJ_XCOFF
+  /* Set the class of a label based on where it is defined.  This handles
+     symbols without suffixes.  Also, move the symbol so that it follows
+     the csect symbol.  */
+  if (ppc_current_csect != (symbolS *) NULL)
+    {
+      if (symbol_get_tc (sym)->symbol_class == -1)
+       symbol_get_tc (sym)->symbol_class = symbol_get_tc (ppc_current_csect)->symbol_class;
+
+      symbol_remove (sym, &symbol_rootP, &symbol_lastP);
+      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;
+    }
+#endif
+
+#ifdef OBJ_ELF
+  dwarf2_emit_label (sym);
+#endif
+}
 
 /* We need to keep a list of fixups.  We can't simply generate them as
    we go, because that would require us to first create the frag, and
@@ -2602,21 +2924,326 @@ 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
+/* Return the field size operated on by RELOC, and whether it is
+   pc-relative in PC_RELATIVE.  */
+
+static unsigned int
+fixup_size (bfd_reloc_code_real_type reloc, bool *pc_relative)
+{
+  unsigned int size = 0;
+  bool pcrel = false;
+
+  switch (reloc)
+    {
+      /* This switch statement must handle all BFD_RELOC values
+        possible in instruction fixups.  As is, it handles all
+        BFD_RELOC values used in bfd/elf64-ppc.c, bfd/elf32-ppc.c,
+        bfd/coff-rs6000.c and bfd/coff64-rs6000.c.
+        Overkill since data and marker relocs need not be handled
+        here, but this way we can be sure a needed fixup reloc isn't
+        accidentally omitted.  */
+    case BFD_RELOC_PPC_EMB_MRKREF:
+    case BFD_RELOC_VTABLE_ENTRY:
+    case BFD_RELOC_VTABLE_INHERIT:
+      break;
+
+    case BFD_RELOC_8:
+      size = 1;
+      break;
+
+    case BFD_RELOC_16:
+    case BFD_RELOC_16_BASEREL:
+    case BFD_RELOC_16_GOTOFF:
+    case BFD_RELOC_GPREL16:
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_HI16_BASEREL:
+    case BFD_RELOC_HI16_GOTOFF:
+    case BFD_RELOC_HI16_PLTOFF:
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_HI16_S_BASEREL:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_HI16_S_PLTOFF:
+    case BFD_RELOC_LO16:
+    case BFD_RELOC_LO16_BASEREL:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_LO16_PLTOFF:
+    case BFD_RELOC_PPC64_ADDR16_DS:
+    case BFD_RELOC_PPC64_ADDR16_HIGH:
+    case BFD_RELOC_PPC64_ADDR16_HIGHA:
+    case BFD_RELOC_PPC64_ADDR16_HIGHER34:
+    case BFD_RELOC_PPC64_ADDR16_HIGHERA34:
+    case BFD_RELOC_PPC64_ADDR16_HIGHEST34:
+    case BFD_RELOC_PPC64_ADDR16_HIGHESTA34:
+    case BFD_RELOC_PPC64_ADDR16_LO_DS:
+    case BFD_RELOC_PPC64_DTPREL16_DS:
+    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_DTPREL16_LO_DS:
+    case BFD_RELOC_PPC64_GOT16_DS:
+    case BFD_RELOC_PPC64_GOT16_LO_DS:
+    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_PLT16_LO_DS:
+    case BFD_RELOC_PPC64_PLTGOT16:
+    case BFD_RELOC_PPC64_PLTGOT16_DS:
+    case BFD_RELOC_PPC64_PLTGOT16_HA:
+    case BFD_RELOC_PPC64_PLTGOT16_HI:
+    case BFD_RELOC_PPC64_PLTGOT16_LO:
+    case BFD_RELOC_PPC64_PLTGOT16_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_HA:
+    case BFD_RELOC_PPC64_TOC16_HI:
+    case BFD_RELOC_PPC64_TOC16_LO:
+    case BFD_RELOC_PPC64_TOC16_LO_DS:
+    case BFD_RELOC_PPC64_TPREL16_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_TPREL16_LO_DS:
+#ifdef OBJ_XCOFF
+    case BFD_RELOC_PPC_BA16:
+#endif
+    case BFD_RELOC_PPC_DTPREL16:
+    case BFD_RELOC_PPC_DTPREL16_HA:
+    case BFD_RELOC_PPC_DTPREL16_HI:
+    case BFD_RELOC_PPC_DTPREL16_LO:
+    case BFD_RELOC_PPC_EMB_NADDR16:
+    case BFD_RELOC_PPC_EMB_NADDR16_HA:
+    case BFD_RELOC_PPC_EMB_NADDR16_HI:
+    case BFD_RELOC_PPC_EMB_NADDR16_LO:
+    case BFD_RELOC_PPC_EMB_RELSDA:
+    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_SDA2I16:
+    case BFD_RELOC_PPC_EMB_SDA2REL:
+    case BFD_RELOC_PPC_EMB_SDAI16:
+    case BFD_RELOC_PPC_GOT_DTPREL16:
+    case BFD_RELOC_PPC_GOT_DTPREL16_HA:
+    case BFD_RELOC_PPC_GOT_DTPREL16_HI:
+    case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+    case BFD_RELOC_PPC_GOT_TLSGD16:
+    case BFD_RELOC_PPC_GOT_TLSGD16_HA:
+    case BFD_RELOC_PPC_GOT_TLSGD16_HI:
+    case BFD_RELOC_PPC_GOT_TLSGD16_LO:
+    case BFD_RELOC_PPC_GOT_TLSLD16:
+    case BFD_RELOC_PPC_GOT_TLSLD16_HA:
+    case BFD_RELOC_PPC_GOT_TLSLD16_HI:
+    case BFD_RELOC_PPC_GOT_TLSLD16_LO:
+    case BFD_RELOC_PPC_GOT_TPREL16:
+    case BFD_RELOC_PPC_GOT_TPREL16_HA:
+    case BFD_RELOC_PPC_GOT_TPREL16_HI:
+    case BFD_RELOC_PPC_GOT_TPREL16_LO:
+    case BFD_RELOC_PPC_TOC16:
+    case BFD_RELOC_PPC_TOC16_HI:
+    case BFD_RELOC_PPC_TOC16_LO:
+    case BFD_RELOC_PPC_TPREL16:
+    case BFD_RELOC_PPC_TPREL16_HA:
+    case BFD_RELOC_PPC_TPREL16_HI:
+    case BFD_RELOC_PPC_TPREL16_LO:
+      size = 2;
+      break;
+
+    case BFD_RELOC_16_PCREL:
+    case BFD_RELOC_HI16_PCREL:
+    case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_LO16_PCREL:
+    case BFD_RELOC_PPC64_REL16_HIGH:
+    case BFD_RELOC_PPC64_REL16_HIGHA:
+    case BFD_RELOC_PPC64_REL16_HIGHER:
+    case BFD_RELOC_PPC64_REL16_HIGHER34:
+    case BFD_RELOC_PPC64_REL16_HIGHERA:
+    case BFD_RELOC_PPC64_REL16_HIGHERA34:
+    case BFD_RELOC_PPC64_REL16_HIGHEST:
+    case BFD_RELOC_PPC64_REL16_HIGHEST34:
+    case BFD_RELOC_PPC64_REL16_HIGHESTA:
+    case BFD_RELOC_PPC64_REL16_HIGHESTA34:
+#ifdef OBJ_XCOFF
+    case BFD_RELOC_PPC_B16:
+#endif
+    case BFD_RELOC_PPC_VLE_REL8:
+      size = 2;
+      pcrel = true;
+      break;
+
+    case BFD_RELOC_32:
+    case BFD_RELOC_32_PLTOFF:
+#ifdef OBJ_XCOFF
+    case BFD_RELOC_CTOR:
+#endif
+    case BFD_RELOC_PPC64_ENTRY:
+    case BFD_RELOC_PPC_16DX_HA:
+#ifndef OBJ_XCOFF
+    case BFD_RELOC_PPC_BA16:
+#endif
+    case BFD_RELOC_PPC_BA16_BRNTAKEN:
+    case BFD_RELOC_PPC_BA16_BRTAKEN:
+    case BFD_RELOC_PPC_BA26:
+    case BFD_RELOC_PPC_EMB_BIT_FLD:
+    case BFD_RELOC_PPC_EMB_NADDR32:
+    case BFD_RELOC_PPC_EMB_SDA21:
+    case BFD_RELOC_PPC_TLS:
+    case BFD_RELOC_PPC_TLSGD:
+    case BFD_RELOC_PPC_TLSLD:
+    case BFD_RELOC_PPC_TLSLE:
+    case BFD_RELOC_PPC_TLSIE:
+    case BFD_RELOC_PPC_TLSM:
+    case BFD_RELOC_PPC_TLSML:
+    case BFD_RELOC_PPC_VLE_HA16A:
+    case BFD_RELOC_PPC_VLE_HA16D:
+    case BFD_RELOC_PPC_VLE_HI16A:
+    case BFD_RELOC_PPC_VLE_HI16D:
+    case BFD_RELOC_PPC_VLE_LO16A:
+    case BFD_RELOC_PPC_VLE_LO16D:
+    case BFD_RELOC_PPC_VLE_SDA21:
+    case BFD_RELOC_PPC_VLE_SDA21_LO:
+    case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+    case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+    case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+    case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
+    case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+    case BFD_RELOC_PPC_VLE_SDAREL_LO16D:
+    case BFD_RELOC_PPC64_TLS_PCREL:
+    case BFD_RELOC_RVA:
+      size = 4;
+      break;
+
+    case BFD_RELOC_24_PLT_PCREL:
+    case BFD_RELOC_32_PCREL:
+    case BFD_RELOC_32_PLT_PCREL:
+    case BFD_RELOC_PPC64_REL24_NOTOC:
+#ifndef OBJ_XCOFF
+    case BFD_RELOC_PPC_B16:
+#endif
+    case BFD_RELOC_PPC_B16_BRNTAKEN:
+    case BFD_RELOC_PPC_B16_BRTAKEN:
+    case BFD_RELOC_PPC_B26:
+    case BFD_RELOC_PPC_LOCAL24PC:
+    case BFD_RELOC_PPC_REL16DX_HA:
+    case BFD_RELOC_PPC_VLE_REL15:
+    case BFD_RELOC_PPC_VLE_REL24:
+      size = 4;
+      pcrel = true;
+      break;
+
+#ifndef OBJ_XCOFF
+    case BFD_RELOC_CTOR:
+#endif
+    case BFD_RELOC_PPC_COPY:
+    case BFD_RELOC_PPC_DTPMOD:
+    case BFD_RELOC_PPC_DTPREL:
+    case BFD_RELOC_PPC_GLOB_DAT:
+    case BFD_RELOC_PPC_TPREL:
+      size = ppc_obj64 ? 8 : 4;
+      break;
+
+    case BFD_RELOC_64:
+    case BFD_RELOC_64_PLTOFF:
+    case BFD_RELOC_PPC64_ADDR64_LOCAL:
+    case BFD_RELOC_PPC64_D28:
+    case BFD_RELOC_PPC64_D34:
+    case BFD_RELOC_PPC64_D34_LO:
+    case BFD_RELOC_PPC64_D34_HI30:
+    case BFD_RELOC_PPC64_D34_HA30:
+    case BFD_RELOC_PPC64_TPREL34:
+    case BFD_RELOC_PPC64_DTPREL34:
+    case BFD_RELOC_PPC64_TOC:
+    case BFD_RELOC_PPC64_TLSGD:
+    case BFD_RELOC_PPC64_TLSLD:
+    case BFD_RELOC_PPC64_TLSLE:
+    case BFD_RELOC_PPC64_TLSIE:
+    case BFD_RELOC_PPC64_TLSM:
+    case BFD_RELOC_PPC64_TLSML:
+      size = 8;
+      break;
+
+    case BFD_RELOC_64_PCREL:
+    case BFD_RELOC_64_PLT_PCREL:
+    case BFD_RELOC_PPC64_GOT_PCREL34:
+    case BFD_RELOC_PPC64_GOT_TLSGD_PCREL34:
+    case BFD_RELOC_PPC64_GOT_TLSLD_PCREL34:
+    case BFD_RELOC_PPC64_GOT_TPREL_PCREL34:
+    case BFD_RELOC_PPC64_GOT_DTPREL_PCREL34:
+    case BFD_RELOC_PPC64_PCREL28:
+    case BFD_RELOC_PPC64_PCREL34:
+    case BFD_RELOC_PPC64_PLT_PCREL34:
+      size = 8;
+      pcrel = true;
+      break;
+
+    default:
+      abort ();
+    }
+
+  if (ENABLE_CHECKING)
+    {
+      reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc);
+      if (reloc_howto != NULL
+         && (size != bfd_get_reloc_size (reloc_howto)
+             || pcrel != reloc_howto->pc_relative))
+       {
+         as_bad (_("%s howto doesn't match size/pcrel in gas"),
+                 reloc_howto->name);
+         abort ();
+       }
+    }
+  *pc_relative = pcrel;
+  return size;
+}
+
+#ifdef OBJ_ELF
+/* If we have parsed a call to __tls_get_addr, parse an argument like
+   (gd0@tlsgd).  *STR is the leading parenthesis on entry.  If an arg
+   is successfully parsed, *STR is updated past the trailing
+   parenthesis and trailing white space, and *TLS_FIX contains the
+   reloc and arg expression.  */
+
+static int
+parse_tls_arg (char **str, const expressionS *exp, struct ppc_fixup *tls_fix)
+{
+  const char *sym_name = S_GET_NAME (exp->X_add_symbol);
+  if (sym_name[0] == '.')
+    ++sym_name;
+
+  tls_fix->reloc = BFD_RELOC_NONE;
+  if (strncasecmp (sym_name, "__tls_get_addr", 14) == 0
+      && (sym_name[14] == 0
+         || strcasecmp (sym_name + 14, "_desc") == 0
+         || strcasecmp (sym_name + 14, "_opt") == 0))
+    {
+      char *hold = input_line_pointer;
+      input_line_pointer = *str + 1;
+      expression (&tls_fix->exp);
+      if (tls_fix->exp.X_op == O_symbol)
+       {
+         if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0)
+           tls_fix->reloc = BFD_RELOC_PPC_TLSGD;
+         else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0)
+           tls_fix->reloc = BFD_RELOC_PPC_TLSLD;
+         if (tls_fix->reloc != BFD_RELOC_NONE)
+           {
+             input_line_pointer += 7;
+             SKIP_WHITESPACE ();
+             *str = input_line_pointer;
+           }
+       }
+      input_line_pointer = hold;
+    }
+  return tls_fix->reloc != BFD_RELOC_NONE;
+}
+#endif
 
 /* This routine is called for each instruction to be assembled.  */
 
@@ -2625,15 +3252,14 @@ md_assemble (char *str)
 {
   char *s;
   const struct powerpc_opcode *opcode;
-  unsigned long insn;
+  uint64_t insn;
   const unsigned char *opindex_ptr;
-  int skip_optional;
   int need_paren;
   int next_opindex;
   struct ppc_fixup fixups[MAX_INSN_FIXUPS];
   int fc;
   char *f;
-  int addr_mod;
+  int addr_mask;
   int i;
   unsigned int insn_length;
 
@@ -2644,77 +3270,65 @@ md_assemble (char *str)
     *s++ = '\0';
 
   /* Look up the opcode in the hash table.  */
-  opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str);
+  opcode = (const struct powerpc_opcode *) str_hash_find (ppc_hash, str);
   if (opcode == (const struct powerpc_opcode *) NULL)
     {
       const struct powerpc_macro *macro;
 
-      macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str);
+      macro = (const struct powerpc_macro *) str_hash_find (ppc_macro_hash,
+                                                           str);
       if (macro == (const struct powerpc_macro *) NULL)
        as_bad (_("unrecognized opcode: `%s'"), str);
       else
        ppc_macro (s, macro);
 
+      ppc_clear_labels ();
       return;
     }
 
   insn = opcode->opcode;
+  if (!target_big_endian
+      && ((insn & ~(1 << 26)) == 46u << 26
+         || (insn & ~(0xc0 << 1)) == (31u << 26 | 533 << 1)))
+    {
+       /* lmw, stmw, lswi, lswx, stswi, stswx */
+      as_bad (_("`%s' invalid when little-endian"), str);
+      ppc_clear_labels ();
+      return;
+    }
 
   str = s;
   while (ISSPACE (*str))
     ++str;
 
-  /* PowerPC operands are just expressions.  The only real issue is
-     that a few operand types are optional.  All cases which might use
-     an optional operand separate the operands only with commas (in some
-     cases parentheses are used, as in ``lwz 1,0(1)'' but such cases never
-     have optional operands).  Most instructions with optional operands
-     have only one.  Those that have more than one optional operand can
-     take either all their operands or none.  So, before we start seriously
-     parsing the operands, we check to see if we have optional operands,
-     and if we do, we count the number of commas to see which operands
-     have been omitted.  */
-  skip_optional = 0;
-  for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++)
-    {
-      const struct powerpc_operand *operand;
-
-      operand = &powerpc_operands[*opindex_ptr];
-      if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
-       {
-         unsigned int opcount;
-         unsigned int num_operands_expected;
-
-         /* There is an optional operand.  Count the number of
-            commas in the input line.  */
-         if (*str == '\0')
-           opcount = 0;
-         else
-           {
-             opcount = 1;
-             s = str;
-             while ((s = strchr (s, ',')) != (char *) NULL)
-               {
-                 ++opcount;
-                 ++s;
-               }
-           }
-
-         /* Compute the number of expected operands.
-            Do not count fake operands.  */
-         for (num_operands_expected = 0, i = 0; opcode->operands[i]; i ++)
-           if ((powerpc_operands [opcode->operands[i]].flags & PPC_OPERAND_FAKE) == 0)
-             ++ num_operands_expected;
-
-         /* If there are fewer operands in the line then are called
-            for by the instruction, we want to skip the optional
-            operands.  */
-         if (opcount < num_operands_expected)
-           skip_optional = 1;
+#ifdef OBJ_XCOFF
+  /* AIX often generates addis instructions using "addis RT, D(RA)"
+     format instead of the classic "addis RT, RA, SI" one.
+     Restore it to the default format as it's the one encoded
+     in ppc opcodes.  */
+    if (!strcmp (opcode->name, "addis"))
+      {
+       char *rt_e = strchr (str, ',');
+       if (rt_e != NULL
+           && strchr (rt_e + 1, ',') == NULL)
+         {
+           char *d_e = strchr (rt_e + 1, '(');
+           if (d_e != NULL && d_e != rt_e + 1)
+             {
+               char *ra_e = strrchr (d_e + 1, ')');
+               if (ra_e != NULL && ra_e != d_e + 1)
+                 ppc_xcoff_fixup_addis (rt_e, d_e, ra_e);
+             }
+         }
+      }
+#endif
 
-         break;
-       }
-    }
+  /* PowerPC operands are just expressions.  The only real issue is
+     that a few operand types are optional.  If an instruction has
+     multiple optional operands and one is omitted, then all optional
+     operands past the first omitted one must also be omitted.  */
+  int num_optional_operands = 0;
+  int num_optional_provided = 0;
 
   /* Gather the operands.  */
   need_paren = 0;
@@ -2737,177 +3351,93 @@ md_assemble (char *str)
        }
       errmsg = NULL;
 
-      /* If this is a fake operand, then we do not expect anything
-        from the input.  */
-      if ((operand->flags & PPC_OPERAND_FAKE) != 0)
-       {
-         insn = (*operand->insert) (insn, 0L, ppc_cpu, &errmsg);
-         if (errmsg != (const char *) NULL)
-           as_bad ("%s", errmsg);
-         continue;
-       }
-
       /* If this is an optional operand, and we are skipping it, just
-        insert a zero.  */
+        insert the default value, usually a zero.  */
       if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0
-         && skip_optional)
+         && !((operand->flags & PPC_OPERAND_OPTIONAL32) != 0 && ppc_obj64))
        {
-         long val = ppc_optional_operand_value (operand);
-         if (operand->insert)
+         if (num_optional_operands == 0)
            {
-             insn = (*operand->insert) (insn, val, ppc_cpu, &errmsg);
-             if (errmsg != (const char *) NULL)
-               as_bad ("%s", errmsg);
+             const unsigned char *optr;
+             int total = 0;
+             int provided = 0;
+             int omitted;
+
+             s = str;
+             for (optr = opindex_ptr; *optr != 0; optr++)
+               {
+                 const struct powerpc_operand *op;
+                 op = &powerpc_operands[*optr];
+
+                 ++total;
+
+                 if ((op->flags & PPC_OPERAND_OPTIONAL) != 0
+                     && !((op->flags & PPC_OPERAND_OPTIONAL32) != 0
+                          && ppc_obj64))
+                   ++num_optional_operands;
+
+                 if (s != NULL && *s != '\0')
+                   {
+                     ++provided;
+
+                     /* Look for the start of the next operand.  */
+                     if ((op->flags & PPC_OPERAND_PARENS) != 0)
+                       s = strpbrk (s, "(,");
+                     else
+                       s = strchr (s, ',');
+
+                     if (s != NULL)
+                       ++s;
+                   }
+               }
+             omitted = total - provided;
+             num_optional_provided = num_optional_operands - omitted;
            }
-         else if (operand->shift >= 0)
-           insn |= ((long) val & operand->bitm) << operand->shift;
-         else
-           insn |= ((long) val & operand->bitm) >> -operand->shift;
+         if (--num_optional_provided < 0)
+           {
+             uint64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu,
+                                                        num_optional_provided);
+             if (operand->insert)
+               {
+                 insn = (*operand->insert) (insn, val, ppc_cpu, &errmsg);
+                 if (errmsg != (const char *) NULL)
+                   as_bad ("%s", errmsg);
+               }
+             else if (operand->shift >= 0)
+               insn |= (val & operand->bitm) << operand->shift;
+             else
+               insn |= (val & operand->bitm) >> -operand->shift;
 
-         if ((operand->flags & PPC_OPERAND_NEXT) != 0)
-           next_opindex = *opindex_ptr + 1;
-         continue;
+             if ((operand->flags & PPC_OPERAND_NEXT) != 0)
+               next_opindex = *opindex_ptr + 1;
+             continue;
+           }
        }
 
       /* Gather the operand.  */
       hold = input_line_pointer;
       input_line_pointer = str;
 
-#ifdef TE_PE
-      if (*input_line_pointer == '[')
+      if ((reg_names_p
+          && (((operand->flags & PPC_OPERAND_CR_BIT) != 0)
+              || ((operand->flags & PPC_OPERAND_CR_REG) != 0)))
+         || !register_name (&ex))
        {
-         /* We are expecting something like the second argument here:
-          *
-          *    lwz r4,[toc].GS.0.static_int(rtoc)
-          *           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-          * The argument following the `]' must be a symbol name, and the
-          * register must be the toc register: 'rtoc' or '2'
-          *
-          * The effect is to 0 as the displacement field
-          * in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or
-          * the appropriate variation) reloc against it based on the symbol.
-          * The linker will build the toc, and insert the resolved toc offset.
-          *
-          * Note:
-          * o The size of the toc entry is currently assumed to be
-          *   32 bits. This should not be assumed to be a hard coded
-          *   number.
-          * o In an effort to cope with a change from 32 to 64 bits,
-          *   there are also toc entries that are specified to be
-          *   either 32 or 64 bits:
-          *     lwz r4,[toc32].GS.0.static_int(rtoc)
-          *     lwz r4,[toc64].GS.0.static_int(rtoc)
-          *   These demand toc entries of the specified size, and the
-          *   instruction probably requires it.
-          */
-
-         int valid_toc;
-         enum toc_size_qualifier toc_kind;
-         bfd_reloc_code_real_type toc_reloc;
-
-         /* Go parse off the [tocXX] part.  */
-         valid_toc = parse_toc_entry (&toc_kind);
-
-         if (!valid_toc)
-           {
-             ignore_rest_of_line ();
-             break;
-           }
+         char save_lex = lex_type['%'];
 
-         /* Now get the symbol following the ']'.  */
-         expression (&ex);
-
-         switch (toc_kind)
+         if (((operand->flags & PPC_OPERAND_CR_REG) != 0)
+             || (operand->flags & PPC_OPERAND_CR_BIT) != 0)
            {
-           case default_toc:
-             /* In this case, we may not have seen the symbol yet,
-                since  it is allowed to appear on a .extern or .globl
-                or just be a label in the .data section.  */
-             toc_reloc = BFD_RELOC_PPC_TOC16;
-             break;
-           case data_in_toc:
-             /* 1. The symbol must be defined and either in the toc
-                section, or a global.
-                2. The reloc generated must have the TOCDEFN flag set
-                in upper bit mess of the reloc type.
-                FIXME: It's a little confusing what the tocv
-                qualifier can be used for.  At the very least, I've
-                seen three uses, only one of which I'm sure I can
-                explain.  */
-             if (ex.X_op == O_symbol)
-               {
-                 gas_assert (ex.X_add_symbol != NULL);
-                 if (symbol_get_bfdsym (ex.X_add_symbol)->section
-                     != tocdata_section)
-                   {
-                     as_bad (_("[tocv] symbol is not a toc symbol"));
-                   }
-               }
-
-             toc_reloc = BFD_RELOC_PPC_TOC16;
-             break;
-           case must_be_32:
-             /* 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_NONE;
-             as_bad (_("unimplemented toc32 expression modifier"));
-             break;
-           case must_be_64:
-             /* FIXME: see above.  */
-             toc_reloc = BFD_RELOC_NONE;
-             as_bad (_("unimplemented toc64 expression modifier"));
-             break;
-           default:
-             fprintf (stderr,
-                      _("Unexpected return value [%d] from parse_toc_entry!\n"),
-                      toc_kind);
-             abort ();
-             break;
+             cr_operand = true;
+             lex_type['%'] |= LEX_BEGIN_NAME;
            }
+         expression (&ex);
+         cr_operand = false;
+         lex_type['%'] = save_lex;
+       }
 
-         /* We need to generate a fixup for this expression.  */
-         if (fc >= MAX_INSN_FIXUPS)
-           as_fatal (_("too many fixups"));
-
-         fixups[fc].reloc = toc_reloc;
-         fixups[fc].exp = ex;
-         fixups[fc].opindex = *opindex_ptr;
-         ++fc;
-
-         /* Ok. We've set up the fixup for the instruction. Now make it
-            look like the constant 0 was found here.  */
-         ex.X_unsigned = 1;
-         ex.X_op = O_constant;
-         ex.X_add_number = 0;
-         ex.X_add_symbol = NULL;
-         ex.X_op_symbol = NULL;
-       }
-
-      else
-#endif         /* TE_PE */
-       {
-         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_REG) != 0)
-                 || (operand->flags & PPC_OPERAND_CR_BIT) != 0)
-               {
-                 cr_operand = TRUE;
-                 lex_type['%'] |= LEX_BEGIN_NAME;
-               }
-             expression (&ex);
-             cr_operand = FALSE;
-             lex_type['%'] = save_lex;
-           }
-       }
-
-      str = input_line_pointer;
-      input_line_pointer = hold;
+      str = input_line_pointer;
+      input_line_pointer = hold;
 
       if (ex.X_op == O_illegal)
        as_bad (_("illegal operand"));
@@ -2915,17 +3445,44 @@ md_assemble (char *str)
        as_bad (_("missing operand"));
       else if (ex.X_op == O_register)
        {
+         if ((ex.X_md
+              & ~operand->flags
+              & (PPC_OPERAND_GPR | PPC_OPERAND_FPR | PPC_OPERAND_VR
+                 | PPC_OPERAND_VSR | PPC_OPERAND_CR_BIT | PPC_OPERAND_CR_REG
+                 | PPC_OPERAND_SPR | PPC_OPERAND_GQR | PPC_OPERAND_ACC)) != 0
+             && !((ex.X_md & PPC_OPERAND_GPR) != 0
+                  && ex.X_add_number != 0
+                  && (operand->flags & PPC_OPERAND_GPR_0) != 0))
+           as_warn (_("invalid register expression"));
          insn = ppc_insert_operand (insn, operand, ex.X_add_number,
                                     ppc_cpu, (char *) NULL, 0);
        }
-      else if (ex.X_op == O_constant)
+      else if (ex.X_op == O_constant
+              || (ex.X_op == O_big && ex.X_add_number > 0))
        {
+         uint64_t val;
+         if (ex.X_op == O_constant)
+           {
+             val = ex.X_add_number;
+             if (sizeof (ex.X_add_number) < sizeof (val)
+                 && (ex.X_add_number < 0) != ex.X_extrabit)
+               val = val ^ ((addressT) -1 ^ (uint64_t) -1);
+           }
+         else
+           val = generic_bignum_to_int64 ();
 #ifdef OBJ_ELF
          /* Allow @HA, @L, @H on constants.  */
-         bfd_reloc_code_real_type reloc;
          char *orig_str = str;
+         bfd_reloc_code_real_type reloc = ppc_elf_suffix (&str, &ex);
 
-         if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
+         if (ex.X_op == O_constant)
+           {
+             val = ex.X_add_number;
+             if (sizeof (ex.X_add_number) < sizeof (val)
+                 && (ex.X_add_number < 0) != ex.X_extrabit)
+               val = val ^ ((addressT) -1 ^ (uint64_t) -1);
+           }
+         if (reloc != BFD_RELOC_NONE)
            switch (reloc)
              {
              default:
@@ -2933,177 +3490,106 @@ md_assemble (char *str)
                break;
 
              case BFD_RELOC_LO16:
-               ex.X_add_number &= 0xffff;
+               val &= 0xffff;
                if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-                 ex.X_add_number = SEX16 (ex.X_add_number);
+                 val = SEX16 (val);
                break;
 
              case BFD_RELOC_HI16:
                if (REPORT_OVERFLOW_HI && ppc_obj64)
                  {
                    /* PowerPC64 @h is tested for overflow.  */
-                   ex.X_add_number = (addressT) ex.X_add_number >> 16;
+                   val = val >> 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;
+                       uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1;
+                       val = (val ^ sign) - sign;
                      }
                    break;
                  }
-               /* Fall thru */
+               /* Fallthru */
 
              case BFD_RELOC_PPC64_ADDR16_HIGH:
-               ex.X_add_number = PPC_HI (ex.X_add_number);
+               val = PPC_HI (val);
                if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-                 ex.X_add_number = SEX16 (ex.X_add_number);
+                 val = SEX16 (val);
                break;
 
              case BFD_RELOC_HI16_S:
                if (REPORT_OVERFLOW_HI && ppc_obj64)
                  {
                    /* PowerPC64 @ha is tested for overflow.  */
-                   ex.X_add_number
-                     = ((addressT) ex.X_add_number + 0x8000) >> 16;
+                   val = (val + 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;
+                       uint64_t sign = (((uint64_t) -1 >> 16) + 1) >> 1;
+                       val = (val ^ sign) - sign;
                      }
                    break;
                  }
-               /* Fall thru */
+               /* Fallthru */
 
              case BFD_RELOC_PPC64_ADDR16_HIGHA:
-               ex.X_add_number = PPC_HA (ex.X_add_number);
+               val = PPC_HA (val);
                if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-                 ex.X_add_number = SEX16 (ex.X_add_number);
+                 val = SEX16 (val);
                break;
 
              case BFD_RELOC_PPC64_HIGHER:
-               ex.X_add_number = PPC_HIGHER (ex.X_add_number);
+               val = PPC_HIGHER (val);
                if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-                 ex.X_add_number = SEX16 (ex.X_add_number);
+                 val = SEX16 (val);
                break;
 
              case BFD_RELOC_PPC64_HIGHER_S:
-               ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
+               val = PPC_HIGHERA (val);
                if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-                 ex.X_add_number = SEX16 (ex.X_add_number);
+                 val = SEX16 (val);
                break;
 
              case BFD_RELOC_PPC64_HIGHEST:
-               ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
+               val = PPC_HIGHEST (val);
                if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-                 ex.X_add_number = SEX16 (ex.X_add_number);
+                 val = SEX16 (val);
                break;
 
              case BFD_RELOC_PPC64_HIGHEST_S:
-               ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
+               val = PPC_HIGHESTA (val);
                if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-                 ex.X_add_number = SEX16 (ex.X_add_number);
+                 val = SEX16 (val);
                break;
              }
 #endif /* OBJ_ELF */
-         insn = ppc_insert_operand (insn, operand, ex.X_add_number,
-                                    ppc_cpu, (char *) NULL, 0);
+         insn = ppc_insert_operand (insn, operand, val, ppc_cpu, NULL, 0);
        }
       else
        {
          bfd_reloc_code_real_type reloc = BFD_RELOC_NONE;
 #ifdef OBJ_ELF
-         if (ex.X_op == O_symbol && str[0] == '(')
+         /* Look for a __tls_get_addr arg using the insane old syntax.  */
+         if (ex.X_op == O_symbol && *str == '(' && fc < MAX_INSN_FIXUPS
+             && parse_tls_arg (&str, &ex, &fixups[fc]))
            {
-             const char *sym_name = S_GET_NAME (ex.X_add_symbol);
-             if (sym_name[0] == '.')
-               ++sym_name;
-
-             if (strcasecmp (sym_name, "__tls_get_addr") == 0)
-               {
-                 expressionS tls_exp;
-
-                 hold = input_line_pointer;
-                 input_line_pointer = str + 1;
-                 expression (&tls_exp);
-                 if (tls_exp.X_op == O_symbol)
-                   {
-                     reloc = BFD_RELOC_NONE;
-                     if (strncasecmp (input_line_pointer, "@tlsgd)", 7) == 0)
-                       {
-                         reloc = BFD_RELOC_PPC_TLSGD;
-                         input_line_pointer += 7;
-                       }
-                     else if (strncasecmp (input_line_pointer, "@tlsld)", 7) == 0)
-                       {
-                         reloc = BFD_RELOC_PPC_TLSLD;
-                         input_line_pointer += 7;
-                       }
-                     if (reloc != BFD_RELOC_NONE)
-                       {
-                         SKIP_WHITESPACE ();
-                         str = input_line_pointer;
-
-                         if (fc >= MAX_INSN_FIXUPS)
-                           as_fatal (_("too many fixups"));
-                         fixups[fc].exp = tls_exp;
-                         fixups[fc].opindex = *opindex_ptr;
-                         fixups[fc].reloc = reloc;
-                         ++fc;
-                       }
-                   }
-                 input_line_pointer = hold;
-               }
+             fixups[fc].opindex = *opindex_ptr;
+             ++fc;
            }
 
          if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_NONE)
            {
-             /* Some TLS tweaks.  */
-             switch (reloc)
-               {
-               default:
-                 break;
-
-               case BFD_RELOC_PPC_TLS:
-                 if (!_bfd_elf_ppc_at_tls_transform (opcode->opcode, 0))
-                   as_bad (_("@tls may not be used with \"%s\" operands"),
-                           opcode->name);
-                 else if (operand->shift != 11)
-                   as_bad (_("@tls may only be used in last operand"));
-                 else
-                   insn = ppc_insert_operand (insn, operand,
-                                              ppc_obj64 ? 13 : 2,
-                                              ppc_cpu, (char *) NULL, 0);
-                 break;
-
-                 /* We'll only use the 32 (or 64) bit form of these relocations
-                    in constants.  Instructions get the 16 bit form.  */
-               case BFD_RELOC_PPC_DTPREL:
-                 reloc = BFD_RELOC_PPC_DTPREL16;
-                 break;
-               case BFD_RELOC_PPC_TPREL:
-                 reloc = BFD_RELOC_PPC_TPREL16;
-                 break;
-               }
-
-             /* 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;
+                 uint64_t tmp_insn = insn & opcode->mask;
 
-                 int use_d_reloc = (tmp_insn == E_OR2I_INSN
+                 int use_a_reloc = (tmp_insn == E_OR2I_INSN
                                     || tmp_insn == E_AND2I_DOT_INSN
                                     || tmp_insn == E_OR2IS_INSN
+                                    || tmp_insn == E_LI_INSN
                                     || tmp_insn == E_LIS_INSN
                                     || tmp_insn == E_AND2IS_DOT_INSN);
 
 
-                 int use_a_reloc = (tmp_insn == E_ADD2I_DOT_INSN
+                 int use_d_reloc = (tmp_insn == E_ADD2I_DOT_INSN
                                     || tmp_insn == E_ADD2IS_INSN
                                     || tmp_insn == E_CMP16I_INSN
                                     || tmp_insn == E_MULL2I_INSN
@@ -3157,8 +3643,99 @@ md_assemble (char *str)
                      break;
                    }
                }
+
+             /* TLS and other tweaks.  */
+             switch (reloc)
+               {
+               default:
+                 break;
+
+               case BFD_RELOC_PPC_TLS:
+               case BFD_RELOC_PPC64_TLS_PCREL:
+                 if (!_bfd_elf_ppc_at_tls_transform (opcode->opcode, 0))
+                   as_bad (_("@tls may not be used with \"%s\" operands"),
+                           opcode->name);
+                 else if (operand->shift != 11)
+                   as_bad (_("@tls may only be used in last operand"));
+                 else
+                   insn = ppc_insert_operand (insn, operand,
+                                              ppc_obj64 ? 13 : 2,
+                                              ppc_cpu, (char *) NULL, 0);
+                 break;
+
+                 /* We'll only use the 32 (or 64) bit form of these relocations
+                    in constants.  Instructions get the 16 or 34 bit form.  */
+               case BFD_RELOC_PPC_DTPREL:
+                 if (operand->bitm == 0x3ffffffffULL)
+                   reloc = BFD_RELOC_PPC64_DTPREL34;
+                 else
+                   reloc = BFD_RELOC_PPC_DTPREL16;
+                 break;
+
+               case BFD_RELOC_PPC_TPREL:
+                 if (operand->bitm == 0x3ffffffffULL)
+                   reloc = BFD_RELOC_PPC64_TPREL34;
+                 else
+                   reloc = BFD_RELOC_PPC_TPREL16;
+                 break;
+
+               case BFD_RELOC_PPC64_PCREL34:
+                 if (operand->bitm == 0xfffffffULL)
+                   {
+                     reloc = BFD_RELOC_PPC64_PCREL28;
+                     break;
+                   }
+                 /* Fall through.  */
+               case BFD_RELOC_PPC64_GOT_PCREL34:
+               case BFD_RELOC_PPC64_PLT_PCREL34:
+               case BFD_RELOC_PPC64_GOT_TLSGD_PCREL34:
+               case BFD_RELOC_PPC64_GOT_TLSLD_PCREL34:
+               case BFD_RELOC_PPC64_GOT_TPREL_PCREL34:
+               case BFD_RELOC_PPC64_GOT_DTPREL_PCREL34:
+                 if (operand->bitm != 0x3ffffffffULL
+                     || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+                   as_warn (_("%s unsupported on this instruction"), "@pcrel");
+                 break;
+
+               case BFD_RELOC_LO16:
+                 if (operand->bitm == 0x3ffffffffULL
+                     && (operand->flags & PPC_OPERAND_NEGATIVE) == 0)
+                   reloc = BFD_RELOC_PPC64_D34_LO;
+                 else if ((operand->bitm | 0xf) != 0xffff
+                          || operand->shift != 0
+                          || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+                   as_warn (_("%s unsupported on this instruction"), "@l");
+                 break;
+
+               case BFD_RELOC_HI16:
+                 if (operand->bitm == 0x3ffffffffULL
+                     && (operand->flags & PPC_OPERAND_NEGATIVE) == 0)
+                   reloc = BFD_RELOC_PPC64_D34_HI30;
+                 else if (operand->bitm != 0xffff
+                          || operand->shift != 0
+                          || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+                   as_warn (_("%s unsupported on this instruction"), "@h");
+                 break;
+
+               case BFD_RELOC_HI16_S:
+                 if (operand->bitm == 0x3ffffffffULL
+                     && (operand->flags & PPC_OPERAND_NEGATIVE) == 0)
+                   reloc = BFD_RELOC_PPC64_D34_HA30;
+                 else if (operand->bitm == 0xffff
+                          && operand->shift == (int) PPC_OPSHIFT_INV
+                          && opcode->opcode == (19 << 26) + (2 << 1))
+                   /* addpcis.  */
+                   reloc = BFD_RELOC_PPC_16DX_HA;
+                 else if (operand->bitm != 0xffff
+                          || operand->shift != 0
+                          || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
+                   as_warn (_("%s unsupported on this instruction"), "@ha");
+               }
            }
 #endif /* OBJ_ELF */
+#ifdef OBJ_XCOFF
+         reloc = ppc_xcoff_suffix (&str);
+#endif /* OBJ_XCOFF */
 
          if (reloc != BFD_RELOC_NONE)
            ;
@@ -3205,6 +3782,10 @@ md_assemble (char *str)
                }
 #endif
            }
+         else if (operand->bitm == 0x3ffffffffULL)
+           reloc = BFD_RELOC_PPC64_D34;
+         else if (operand->bitm == 0xfffffffULL)
+           reloc = BFD_RELOC_PPC64_D28;
 
          /* For the absolute forms of branches, convert the PC
             relative form back into the absolute.  */
@@ -3254,58 +3835,84 @@ md_assemble (char *str)
                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;
                }
            }
+
+         /* Look for a __tls_get_addr arg after any __tls_get_addr
+            modifiers like @plt.  This fixup must be emitted before
+            the usual call fixup.  */
+         if (ex.X_op == O_symbol && *str == '(' && fc < MAX_INSN_FIXUPS
+             && parse_tls_arg (&str, &ex, &fixups[fc]))
+           {
+             fixups[fc].opindex = *opindex_ptr;
+             ++fc;
+           }
 #endif
 
          /* We need to generate a fixup for this expression.  */
@@ -3331,27 +3938,28 @@ md_assemble (char *str)
            }
        }
       else if ((operand->flags & PPC_OPERAND_PARENS) != 0)
-       {
-         endc = '(';
-         need_paren = 1;
-       }
+       endc = '(';
       else
        endc = ',';
 
       /* The call to expression should have advanced str past any
         whitespace.  */
-      if (*str != endc
-         && (endc != ',' || *str != '\0'))
+      if (*str == endc)
        {
-         if (*str == '\0')
-           as_bad (_("syntax error; end of line, expected `%c'"), endc);
-         else
-           as_bad (_("syntax error; found `%c', expected `%c'"), *str, endc);
+         ++str;
+         if (endc == '(')
+           need_paren = 1;
+       }
+      else if (*str != '\0')
+       {
+         as_bad (_("syntax error; found `%c', expected `%c'"), *str, endc);
+         break;
+       }
+      else if (endc == ')')
+       {
+         as_bad (_("syntax error; end of line, expected `%c'"), endc);
          break;
        }
-
-      if (*str != '\0')
-       ++str;
     }
 
   while (ISSPACE (*str))
@@ -3386,37 +3994,86 @@ md_assemble (char *str)
          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);
+       {
+         ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
+         if (elf_section_data (now_seg) != NULL)
+           elf_section_data (now_seg)->this_hdr.sh_flags |= SHF_PPC_VLE;
+       }
     }
 #endif
 
   /* Write out the instruction.  */
-  /* Differentiate between two and four byte insns.  */
-  if (ppc_mach () == bfd_mach_ppc_vle)
+
+  addr_mask = 3;
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+    /* All instructions can start on a 2 byte boundary for VLE.  */
+    addr_mask = 1;
+
+  if (frag_now->insn_addr != addr_mask)
     {
-      if (PPC_OP_SE_VLE (insn))
-        insn_length = 2;
-      else
-        insn_length = 4;
-      addr_mod = frag_now_fix () & 1;
+      /* Don't emit instructions to a frag started for data, or for a
+        CPU differing in VLE mode.  Data is allowed to be misaligned,
+        and it's possible to start a new frag in the middle of
+        misaligned data.  */
+      frag_wane (frag_now);
+      frag_new (0);
     }
-  else
+
+  /* Check that insns within the frag are aligned.  ppc_frag_check
+     will ensure that the frag start address is aligned.  */
+  if ((frag_now_fix () & addr_mask) != 0)
+    as_bad (_("instruction address is not a multiple of %d"), addr_mask + 1);
+
+  /* Differentiate between two, four, and eight byte insns.  */
+  insn_length = 4;
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0 && PPC_OP_SE_VLE (insn))
+    insn_length = 2;
+  else if ((opcode->flags & PPC_OPCODE_POWER10) != 0
+          && PPC_PREFIX_P (insn))
     {
-      insn_length = 4;
-      addr_mod = frag_now_fix () & 3;
+      struct insn_label_list *l;
+
+      insn_length = 8;
+
+      /* 8-byte prefix instructions are not allowed to cross 64-byte
+        boundaries.  */
+      frag_align_code (6, 4);
+      record_alignment (now_seg, 6);
+#ifdef OBJ_XCOFF
+      /* Update alignment of the containing csect.  */
+      if (symbol_get_tc (ppc_current_csect)->align < 6)
+       symbol_get_tc (ppc_current_csect)->align = 6;
+#endif
+
+      /* Update "dot" in any expressions used by this instruction, and
+        a label attached to the instruction.  By "attached" we mean
+        on the same source line as the instruction and without any
+        intervening semicolons.  */
+      dot_value = frag_now_fix ();
+      dot_frag = frag_now;
+      for (l = insn_labels; l != NULL; l = l->next)
+       {
+         symbol_set_frag (l->label, dot_frag);
+         S_SET_VALUE (l->label, dot_value);
+       }
     }
-  /* All instructions can start on a 2 byte boundary for VLE.  */
+
+  ppc_clear_labels ();
+
   f = frag_more (insn_length);
-  if (frag_now->has_code && frag_now->insn_addr != addr_mod)
+  frag_now->insn_addr = addr_mask;
+
+  /* The prefix part of an 8-byte instruction always occupies the lower
+     addressed word in a doubleword, regardless of endianness.  */
+  if (insn_length == 8
+      && (sizeof (insn) > sizeof (valueT) || !target_big_endian))
     {
-      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"));
+      md_number_to_chars (f, PPC_GET_PREFIX (insn), 4);
+      md_number_to_chars (f + 4, PPC_GET_SUFFIX (insn), 4);
     }
-  frag_now->insn_addr = addr_mod;
-  frag_now->has_code = 1;
-  md_number_to_chars (f, insn, insn_length);
+  else
+    md_number_to_chars (f, insn, insn_length);
+
   last_insn = insn;
   last_seg = now_seg;
   last_subseg = now_subseg;
@@ -3431,22 +4088,15 @@ md_assemble (char *str)
       fixS *fixP;
       if (fixups[i].reloc != BFD_RELOC_NONE)
        {
-         reloc_howto_type *reloc_howto;
-         int size;
-         int offset;
-
-         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 ? (insn_length - size) : 0;
+         bool pcrel;
+         unsigned int size = fixup_size (fixups[i].reloc, &pcrel);
+         int offset = target_big_endian ? (insn_length - size) : 0;
 
          fixP = fix_new_exp (frag_now,
                              f - frag_now->fr_literal + offset,
                              size,
                              &fixups[i].exp,
-                             reloc_howto->pc_relative,
+                             pcrel,
                              fixups[i].reloc);
        }
       else
@@ -3523,7 +4173,7 @@ ppc_macro (char *str, const struct powerpc_macro *macro)
     }
 
   /* Put the string together.  */
-  complete = s = (char *) xmalloc (len + 1);
+  complete = s = XNEWVEC (char, len + 1);
   format = macro->format;
   while (*format != '\0')
     {
@@ -3550,7 +4200,7 @@ ppc_macro (char *str, const struct powerpc_macro *macro)
 int
 ppc_section_type (char *str, size_t len)
 {
-  if (len == 7 && strncmp (str, "ordered", 7) == 0)
+  if (len == 7 && startswith (str, "ordered"))
     return SHT_ORDERED;
 
   return -1;
@@ -3564,6 +4214,16 @@ ppc_section_flags (flagword flags, bfd_vma attr ATTRIBUTE_UNUSED, int type)
 
   return flags;
 }
+
+bfd_vma
+ppc_elf_section_letter (int letter, const char **ptrmsg)
+{
+  if (letter == 'v')
+    return SHF_PPC_VLE;
+
+  *ptrmsg = _("bad .section directive: want a,e,v,w,x,M,S,G,T in string");
+  return -1;
+}
 #endif /* OBJ_ELF */
 
 \f
@@ -3614,12 +4274,14 @@ ppc_byte (int ignore ATTRIBUTE_UNUSED)
 
 /* This is set if we are creating a .stabx symbol, since we don't want
    to handle symbol suffixes for such symbols.  */
-static bfd_boolean ppc_stab_symbol;
+static bool ppc_stab_symbol;
 
 /* The .comm and .lcomm pseudo-ops for XCOFF.  XCOFF puts common
    symbols in the .bss segment as though they were local common
    symbols, and uses a different smclas.  The native Aix 4.3.3 assembler
-   aligns .comm and .lcomm to 4 bytes.  */
+   aligns .comm and .lcomm to 4 bytes.
+   Symbols having a XMC_UL storage class are uninialized thread-local
+   data.  */
 
 static void
 ppc_comm (int lcomm)
@@ -3634,6 +4296,7 @@ ppc_comm (int lcomm)
   symbolS *lcomm_sym = NULL;
   symbolS *sym;
   char *pfrag;
+  struct ppc_xcoff_section *section;
 
   endc = get_symbol_name (&name);
   end_name = input_line_pointer;
@@ -3726,7 +4389,23 @@ ppc_comm (int lcomm)
       return;
     }
 
-  record_alignment (bss_section, align);
+  if (symbol_get_tc (sym)->symbol_class == XMC_UL
+      || (lcomm && symbol_get_tc (lcomm_sym)->symbol_class == XMC_UL))
+    {
+      section = &ppc_xcoff_tbss_section;
+      if (!ppc_xcoff_section_is_initialized (section))
+       {
+         ppc_init_xcoff_section (section,
+                                 subseg_new (".tbss", 0), false);
+         bfd_set_section_flags (section->segment,
+                                SEC_ALLOC | SEC_THREAD_LOCAL);
+         seg_info (section->segment)->bss = 1;
+       }
+    }
+  else
+    section = &ppc_xcoff_bss_section;
+
+  record_alignment (section->segment, align);
 
   if (! lcomm
       || ! S_IS_DEFINED (lcomm_sym))
@@ -3747,14 +4426,14 @@ ppc_comm (int lcomm)
          def_size = 0;
        }
 
-      subseg_set (bss_section, 1);
+      subseg_set (section->segment, 1);
       frag_align (align, 0, 0);
 
       symbol_set_frag (def_sym, frag_now);
       pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym,
                        def_size, (char *) NULL);
       *pfrag = 0;
-      S_SET_SEGMENT (def_sym, bss_section);
+      S_SET_SEGMENT (def_sym, section->segment);
       symbol_get_tc (def_sym)->align = align;
     }
   else if (lcomm)
@@ -3770,7 +4449,7 @@ ppc_comm (int lcomm)
   if (lcomm)
     {
       /* Make sym an offset from lcomm_sym.  */
-      S_SET_SEGMENT (sym, bss_section);
+      S_SET_SEGMENT (sym, section->segment);
       symbol_set_frag (sym, symbol_get_frag (lcomm_sym));
       S_SET_VALUE (sym, symbol_get_frag (lcomm_sym)->fr_offset);
       symbol_get_frag (lcomm_sym)->fr_offset += size;
@@ -3828,7 +4507,7 @@ ppc_change_csect (symbolS *sym, offsetT align)
     subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg);
   else
     {
-      symbolS **list_ptr;
+      struct ppc_xcoff_section *section;
       int after_toc;
       int hold_chunksize;
       symbolS *list;
@@ -3850,32 +4529,57 @@ ppc_change_csect (symbolS *sym, offsetT align)
        case XMC_SV:
        case XMC_TI:
        case XMC_TB:
-         S_SET_SEGMENT (sym, text_section);
-         symbol_get_tc (sym)->subseg = ppc_text_subsegment;
-         ++ppc_text_subsegment;
-         list_ptr = &ppc_text_csects;
+         section = &ppc_xcoff_text_section;
          is_code = 1;
          break;
        case XMC_RW:
        case XMC_TC0:
        case XMC_TC:
+       case XMC_TE:
        case XMC_DS:
        case XMC_UA:
-       case XMC_BS:
        case XMC_UC:
+         section = &ppc_xcoff_data_section;
          if (ppc_toc_csect != NULL
              && (symbol_get_tc (ppc_toc_csect)->subseg + 1
-                 == ppc_data_subsegment))
+                 == section->next_subsegment))
            after_toc = 1;
-         S_SET_SEGMENT (sym, data_section);
-         symbol_get_tc (sym)->subseg = ppc_data_subsegment;
-         ++ppc_data_subsegment;
-         list_ptr = &ppc_data_csects;
+         break;
+       case XMC_BS:
+         section = &ppc_xcoff_bss_section;
+         break;
+       case XMC_TL:
+         section = &ppc_xcoff_tdata_section;
+         /* Create .tdata section if not yet done.  */
+         if (!ppc_xcoff_section_is_initialized (section))
+           {
+             ppc_init_xcoff_section (section, subseg_new (".tdata", 0),
+                                     true);
+             bfd_set_section_flags (section->segment, SEC_ALLOC
+                                    | SEC_LOAD | SEC_RELOC | SEC_DATA
+                                    | SEC_THREAD_LOCAL);
+           }
+         break;
+       case XMC_UL:
+         section = &ppc_xcoff_tbss_section;
+         /* Create .tbss section if not yet done.  */
+         if (!ppc_xcoff_section_is_initialized (section))
+           {
+             ppc_init_xcoff_section (section, subseg_new (".tbss", 0),
+                                     false);
+             bfd_set_section_flags (section->segment, SEC_ALLOC |
+                                    SEC_THREAD_LOCAL);
+             seg_info (section->segment)->bss = 1;
+           }
          break;
        default:
          abort ();
        }
 
+      S_SET_SEGMENT (sym, section->segment);
+      symbol_get_tc (sym)->subseg = section->next_subsegment;
+      ++section->next_subsegment;
+
       /* We set the obstack chunk size to a small value before
         changing subsegments, so that we don't use a lot of memory
         space for what may be a small section.  */
@@ -3903,7 +4607,7 @@ ppc_change_csect (symbolS *sym, offsetT align)
       symbol_get_tc (sym)->output = 1;
       symbol_get_tc (sym)->within = sym;
 
-      for (list = *list_ptr;
+      for (list = section->csects;
           symbol_get_tc (list)->next != (symbolS *) NULL;
           list = symbol_get_tc (list)->next)
        ;
@@ -3924,15 +4628,15 @@ ppc_change_debug_section (unsigned int idx, subsegT subseg)
   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);
+  sec = subseg_new (dw->xcoff_name, subseg);
+  oldflags = bfd_section_flags (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);
+      bfd_set_section_flags (sec, SEC_DEBUGGING);
+      bfd_set_section_alignment (sec, 0);
       dw_sections[idx].sect = sec;
     }
 
@@ -3947,7 +4651,7 @@ ppc_change_debug_section (unsigned int idx, subsegT subseg)
 static void
 ppc_dwsect (int ignore ATTRIBUTE_UNUSED)
 {
-  offsetT flag;
+  valueT flag;
   symbolS *opt_label;
   const struct xcoff_dwsect_name *dw;
   struct dw_subsection *subseg;
@@ -4014,8 +4718,7 @@ ppc_dwsect (int ignore ATTRIBUTE_UNUSED)
   else
     {
       /* Create a new dw subsection.  */
-      subseg = (struct dw_subsection *)
-        xmalloc (sizeof (struct dw_subsection));
+      subseg = XNEW (struct dw_subsection);
 
       if (opt_label == NULL)
         {
@@ -4169,7 +4872,7 @@ ppc_lglobl (int ignore ATTRIBUTE_UNUSED)
 
    (In principle, there's no reason why the relocations _have_ to be at
    the beginning.  Anywhere in the csect would do.  However, inserting
-   at the beginning is what the native assmebler does, and it helps to
+   at the beginning is what the native assembler does, and it helps to
    deal with cases where the .ref statements follow the section contents.)
 
    ??? .refs don't work for empty .csects.  However, the native assembler
@@ -4193,7 +4896,7 @@ ppc_ref (int ignore ATTRIBUTE_UNUSED)
       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);
+                   symbol_find_or_make (name), 0, false, BFD_RELOC_NONE);
 
       *input_line_pointer = c;
       SKIP_WHITESPACE_AFTER_NAME ();
@@ -4269,9 +4972,9 @@ ppc_stabx (int ignore ATTRIBUTE_UNUSED)
     }
   ++input_line_pointer;
 
-  ppc_stab_symbol = TRUE;
+  ppc_stab_symbol = true;
   sym = symbol_make (name);
-  ppc_stab_symbol = FALSE;
+  ppc_stab_symbol = false;
 
   symbol_get_tc (sym)->real_name = name;
 
@@ -4446,10 +5149,13 @@ ppc_function (int ignore ATTRIBUTE_UNUSED)
          expression (& exp);
          if (*input_line_pointer == ',')
            {
-             /* The fifth argument is the function size.  */
+             /* The fifth argument is the function size.
+                If it's omitted, the size will be the containing csect.
+                This will be donce during ppc_frob_symtab.  */
              ++input_line_pointer;
-             symbol_get_tc (ext_sym)->u.size = symbol_new
-                ("L0\001", absolute_section,(valueT) 0, &zero_address_frag);
+             symbol_get_tc (ext_sym)->u.size
+               = symbol_new ("L0\001", absolute_section,
+                             &zero_address_frag, 0);
              pseudo_set (symbol_get_tc (ext_sym)->u.size);
            }
        }
@@ -4742,8 +5448,8 @@ ppc_toc (int ignore ATTRIBUTE_UNUSED)
       symbolS *sym;
       symbolS *list;
 
-      subseg = ppc_data_subsegment;
-      ++ppc_data_subsegment;
+      subseg = ppc_xcoff_data_section.next_subsegment;
+      ++ppc_xcoff_data_section.next_subsegment;
 
       subseg_new (segment_name (data_section), subseg);
       ppc_toc_frag = frag_now;
@@ -4758,7 +5464,7 @@ ppc_toc (int ignore ATTRIBUTE_UNUSED)
 
       ppc_toc_csect = sym;
 
-      for (list = ppc_data_csects;
+      for (list = ppc_xcoff_data_section.csects;
           symbol_get_tc (list)->next != (symbolS *) NULL;
           list = symbol_get_tc (list)->next)
        ;
@@ -4836,6 +5542,7 @@ ppc_xcoff_end (void)
             symbol_set_value_now (dwss->end_exp.X_add_symbol);
           }
     }
+  ppc_cpu = 0;
 }
 
 #endif /* OBJ_XCOFF */
@@ -4855,720 +5562,159 @@ ppc_xcoff_end (void)
    When not generating XCOFF output, the arguments are the same, but
    the first argument is simply ignored.  */
 
-static void
-ppc_tc (int ignore ATTRIBUTE_UNUSED)
-{
-#ifdef OBJ_XCOFF
-
-  /* Define the TOC symbol name.  */
-  {
-    char *name;
-    char endc;
-    symbolS *sym;
-
-    if (ppc_toc_csect == (symbolS *) NULL
-       || ppc_toc_csect != ppc_current_csect)
-      {
-       as_bad (_(".tc not in .toc section"));
-       ignore_rest_of_line ();
-       return;
-      }
-
-    endc = get_symbol_name (&name);
-
-    sym = symbol_find_or_make (name);
-
-    (void) restore_line_pointer (endc);
-
-    if (S_IS_DEFINED (sym))
-      {
-       symbolS *label;
-
-       label = symbol_get_tc (ppc_current_csect)->within;
-       if (symbol_get_tc (label)->symbol_class != XMC_TC0)
-         {
-           as_bad (_(".tc with no label"));
-           ignore_rest_of_line ();
-           return;
-         }
-
-       S_SET_SEGMENT (label, S_GET_SEGMENT (sym));
-       symbol_set_frag (label, symbol_get_frag (sym));
-       S_SET_VALUE (label, S_GET_VALUE (sym));
-
-       while (! is_end_of_line[(unsigned char) *input_line_pointer])
-         ++input_line_pointer;
-
-       return;
-      }
-
-    S_SET_SEGMENT (sym, now_seg);
-    symbol_set_frag (sym, frag_now);
-    S_SET_VALUE (sym, (valueT) frag_now_fix ());
-    symbol_get_tc (sym)->symbol_class = XMC_TC;
-    symbol_get_tc (sym)->output = 1;
-
-    ppc_frob_label (sym);
-  }
-
-#endif /* OBJ_XCOFF */
-#ifdef OBJ_ELF
-  int align;
-
-  /* Skip the TOC symbol name.  */
-  while (is_part_of_name (*input_line_pointer)
-        || *input_line_pointer == ' '
-        || *input_line_pointer == '['
-        || *input_line_pointer == ']'
-        || *input_line_pointer == '{'
-        || *input_line_pointer == '}')
-    ++input_line_pointer;
-
-  /* Align to a four/eight byte boundary.  */
-  align = ppc_obj64 ? 3 : 2;
-  frag_align (align, 0, 0);
-  record_alignment (now_seg, align);
-#endif /* OBJ_ELF */
-
-  if (*input_line_pointer != ',')
-    demand_empty_rest_of_line ();
-  else
-    {
-      ++input_line_pointer;
-      cons (ppc_obj64 ? 8 : 4);
-    }
-}
-
-/* Pseudo-op .machine.  */
-
-static void
-ppc_machine (int ignore ATTRIBUTE_UNUSED)
-{
-  char c;
-  char *cpu_string;
-#define MAX_HISTORY 100
-  static ppc_cpu_t *cpu_history;
-  static int curr_hist;
-
-  SKIP_WHITESPACE ();
-
-  c = get_symbol_name (&cpu_string);
-  cpu_string = xstrdup (cpu_string);
-  (void) restore_line_pointer (c);
-
-  if (cpu_string != NULL)
-    {
-      ppc_cpu_t old_cpu = ppc_cpu;
-      ppc_cpu_t new_cpu;
-      char *p;
-
-      for (p = cpu_string; *p != 0; p++)
-       *p = TOLOWER (*p);
-
-      if (strcmp (cpu_string, "push") == 0)
-       {
-         if (cpu_history == NULL)
-           cpu_history = xmalloc (MAX_HISTORY * sizeof (*cpu_history));
-
-         if (curr_hist >= MAX_HISTORY)
-           as_bad (_(".machine stack overflow"));
-         else
-           cpu_history[curr_hist++] = ppc_cpu;
-       }
-      else if (strcmp (cpu_string, "pop") == 0)
-       {
-         if (curr_hist <= 0)
-           as_bad (_(".machine stack underflow"));
-         else
-           ppc_cpu = cpu_history[--curr_hist];
-       }
-      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);
-
-      if (ppc_cpu != old_cpu)
-       ppc_setup_opcodes ();
-    }
-
-  demand_empty_rest_of_line ();
-}
-#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
-\f
-#ifdef TE_PE
-
-/* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format.  */
-
-/* Set the current section.  */
-static void
-ppc_set_current_section (segT new)
-{
-  ppc_previous_section = ppc_current_section;
-  ppc_current_section = new;
-}
-
-/* pseudo-op: .previous
-   behaviour: toggles the current section with the previous section.
-   errors:    None
-   warnings:  "No previous section"  */
-
-static void
-ppc_previous (int ignore ATTRIBUTE_UNUSED)
-{
-  if (ppc_previous_section == NULL)
-    {
-      as_warn (_("no previous section to return to, ignored."));
-      return;
-    }
-
-  subseg_set (ppc_previous_section, 0);
-
-  ppc_set_current_section (ppc_previous_section);
-}
-
-/* pseudo-op: .pdata
-   behaviour: predefined read only data section
-             double word aligned
-   errors:    None
-   warnings:  None
-   initial:   .section .pdata "adr3"
-             a - don't know -- maybe a misprint
-             d - initialized data
-             r - readable
-             3 - double word aligned (that would be 4 byte boundary)
-
-   commentary:
-   Tag index tables (also known as the function table) for exception
-   handling, debugging, etc.  */
-
-static void
-ppc_pdata (int ignore ATTRIBUTE_UNUSED)
-{
-  if (pdata_section == 0)
-    {
-      pdata_section = subseg_new (".pdata", 0);
-
-      bfd_set_section_flags (stdoutput, pdata_section,
-                            (SEC_ALLOC | SEC_LOAD | SEC_RELOC
-                             | SEC_READONLY | SEC_DATA ));
-
-      bfd_set_section_alignment (stdoutput, pdata_section, 2);
-    }
-  else
-    {
-      pdata_section = subseg_new (".pdata", 0);
-    }
-  ppc_set_current_section (pdata_section);
-}
-
-/* pseudo-op: .ydata
-   behaviour: predefined read only data section
-             double word aligned
-   errors:    None
-   warnings:  None
-   initial:   .section .ydata "drw3"
-             a - don't know -- maybe a misprint
-             d - initialized data
-             r - readable
-             3 - double word aligned (that would be 4 byte boundary)
-   commentary:
-   Tag tables (also known as the scope table) for exception handling,
-   debugging, etc.  */
-
-static void
-ppc_ydata (int ignore ATTRIBUTE_UNUSED)
-{
-  if (ydata_section == 0)
-    {
-      ydata_section = subseg_new (".ydata", 0);
-      bfd_set_section_flags (stdoutput, ydata_section,
-                            (SEC_ALLOC | SEC_LOAD | SEC_RELOC
-                             | SEC_READONLY | SEC_DATA ));
-
-      bfd_set_section_alignment (stdoutput, ydata_section, 3);
-    }
-  else
-    {
-      ydata_section = subseg_new (".ydata", 0);
-    }
-  ppc_set_current_section (ydata_section);
-}
-
-/* pseudo-op: .reldata
-   behaviour: predefined read write data section
-             double word aligned (4-byte)
-             FIXME: relocation is applied to it
-             FIXME: what's the difference between this and .data?
-   errors:    None
-   warnings:  None
-   initial:   .section .reldata "drw3"
-             d - initialized data
-             r - readable
-             w - writeable
-             3 - double word aligned (that would be 8 byte boundary)
-
-   commentary:
-   Like .data, but intended to hold data subject to relocation, such as
-   function descriptors, etc.  */
-
-static void
-ppc_reldata (int ignore ATTRIBUTE_UNUSED)
-{
-  if (reldata_section == 0)
-    {
-      reldata_section = subseg_new (".reldata", 0);
-
-      bfd_set_section_flags (stdoutput, reldata_section,
-                            (SEC_ALLOC | SEC_LOAD | SEC_RELOC
-                             | SEC_DATA));
-
-      bfd_set_section_alignment (stdoutput, reldata_section, 2);
-    }
-  else
-    {
-      reldata_section = subseg_new (".reldata", 0);
-    }
-  ppc_set_current_section (reldata_section);
-}
-
-/* pseudo-op: .rdata
-   behaviour: predefined read only data section
-             double word aligned
-   errors:    None
-   warnings:  None
-   initial:   .section .rdata "dr3"
-             d - initialized data
-             r - readable
-             3 - double word aligned (that would be 4 byte boundary)  */
-
-static void
-ppc_rdata (int ignore ATTRIBUTE_UNUSED)
-{
-  if (rdata_section == 0)
-    {
-      rdata_section = subseg_new (".rdata", 0);
-      bfd_set_section_flags (stdoutput, rdata_section,
-                            (SEC_ALLOC | SEC_LOAD | SEC_RELOC
-                             | SEC_READONLY | SEC_DATA ));
-
-      bfd_set_section_alignment (stdoutput, rdata_section, 2);
-    }
-  else
-    {
-      rdata_section = subseg_new (".rdata", 0);
-    }
-  ppc_set_current_section (rdata_section);
-}
-
-/* pseudo-op: .ualong
-   behaviour: much like .int, with the exception that no alignment is
-             performed.
-             FIXME: test the alignment statement
-   errors:    None
-   warnings:  None  */
-
-static void
-ppc_ualong (int ignore ATTRIBUTE_UNUSED)
-{
-  /* Try for long.  */
-  cons (4);
-}
-
-/* pseudo-op: .znop  <symbol name>
-   behaviour: Issue a nop instruction
-             Issue a IMAGE_REL_PPC_IFGLUE relocation against it, using
-             the supplied symbol name.
-   errors:    None
-   warnings:  Missing symbol name  */
-
-static void
-ppc_znop (int ignore ATTRIBUTE_UNUSED)
-{
-  unsigned long insn;
-  const struct powerpc_opcode *opcode;
-  char *f;
-  symbolS *sym;
-  char *symbol_name;
-  char c;
-  char *name;
-
-  /* Strip out the symbol name.  */
-  c = get_symbol_name (&symbol_name);
-
-  name = xmalloc (input_line_pointer - symbol_name + 1);
-  strcpy (name, symbol_name);
-
-  sym = symbol_find_or_make (name);
-
-  *input_line_pointer = c;
-
-  SKIP_WHITESPACE_AFTER_NAME ();
-
-  /* Look up the opcode in the hash table.  */
-  opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop");
-
-  /* Stick in the nop.  */
-  insn = opcode->opcode;
-
-  /* Write out the instruction.  */
-  f = frag_more (4);
-  md_number_to_chars (f, insn, 4);
-  fix_new (frag_now,
-          f - frag_now->fr_literal,
-          4,
-          sym,
-          0,
-          0,
-          BFD_RELOC_16_GOT_PCREL);
-
-}
-
-/* pseudo-op:
-   behaviour:
-   errors:
-   warnings:  */
-
-static void
-ppc_pe_comm (int lcomm)
-{
-  char *name;
-  char c;
-  char *p;
-  offsetT temp;
-  symbolS *symbolP;
-  offsetT align;
-
-  c = get_symbol_name (&name);
-
-  /* just after name is now '\0'.  */
-  p = input_line_pointer;
-  *p = c;
-  SKIP_WHITESPACE_AFTER_NAME ();
-  if (*input_line_pointer != ',')
-    {
-      as_bad (_("expected comma after symbol-name: rest of line ignored."));
-      ignore_rest_of_line ();
-      return;
-    }
-
-  input_line_pointer++;                /* skip ',' */
-  if ((temp = get_absolute_expression ()) < 0)
-    {
-      as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp);
-      ignore_rest_of_line ();
-      return;
-    }
-
-  if (! lcomm)
-    {
-      /* The third argument to .comm is the alignment.  */
-      if (*input_line_pointer != ',')
-       align = 3;
-      else
-       {
-         ++input_line_pointer;
-         align = get_absolute_expression ();
-         if (align <= 0)
-           {
-             as_warn (_("ignoring bad alignment"));
-             align = 3;
-           }
-       }
-    }
+static void
+ppc_tc (int ignore ATTRIBUTE_UNUSED)
+{
+#ifdef OBJ_XCOFF
 
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
+  /* Define the TOC symbol name.  */
+  {
+    char *name;
+    char endc;
+    symbolS *sym;
 
-  *p = c;
-  if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP))
-    {
-      as_bad (_("ignoring attempt to re-define symbol `%s'."),
-             S_GET_NAME (symbolP));
-      ignore_rest_of_line ();
-      return;
-    }
+    if (ppc_toc_csect == (symbolS *) NULL
+       || ppc_toc_csect != ppc_current_csect)
+      {
+       as_bad (_(".tc not in .toc section"));
+       ignore_rest_of_line ();
+       return;
+      }
 
-  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."),
-               S_GET_NAME (symbolP),
-               (long) S_GET_VALUE (symbolP),
-               (long) temp);
-    }
-  else
-    {
-      S_SET_VALUE (symbolP, (valueT) temp);
-      S_SET_EXTERNAL (symbolP);
-      S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
-    }
+    endc = get_symbol_name (&name);
 
-  demand_empty_rest_of_line ();
-}
+    sym = symbol_find_or_make (name);
 
-/*
- * implement the .section pseudo op:
- *     .section name {, "flags"}
- *                ^         ^
- *                |         +--- optional flags: 'b' for bss
- *                |                              'i' for info
- *                +-- section name               'l' for lib
- *                                               'n' for noload
- *                                               'o' for over
- *                                               'w' for data
- *                                              'd' (apparently m88k for data)
- *                                               'x' for text
- * But if the argument is not a quoted string, treat it as a
- * subsegment number.
- *
- * FIXME: this is a copy of the section processing from obj-coff.c, with
- * additions/changes for the moto-pas assembler support. There are three
- * categories:
- *
- * FIXME: I just noticed this. This doesn't work at all really. It it
- *        setting bits that bfd probably neither understands or uses. The
- *        correct approach (?) will have to incorporate extra fields attached
- *        to the section to hold the system specific stuff. (krk)
- *
- * Section Contents:
- * 'a' - unknown - referred to in documentation, but no definition supplied
- * 'c' - section has code
- * 'd' - section has initialized data
- * 'u' - section has uninitialized data
- * 'i' - section contains directives (info)
- * 'n' - section can be discarded
- * 'R' - remove section at link time
- *
- * Section Protection:
- * 'r' - section is readable
- * 'w' - section is writeable
- * 'x' - section is executable
- * 's' - section is sharable
- *
- * Section Alignment:
- * '0' - align to byte boundary
- * '1' - align to halfword undary
- * '2' - align to word boundary
- * '3' - align to doubleword boundary
- * '4' - align to quadword boundary
- * '5' - align to 32 byte boundary
- * '6' - align to 64 byte boundary
- *
- */
+    (void) restore_line_pointer (endc);
 
-void
-ppc_pe_section (int ignore ATTRIBUTE_UNUSED)
-{
-  /* Strip out the section name.  */
-  char *section_name;
-  char c;
-  char *name;
-  unsigned int exp;
-  flagword flags;
-  segT sec;
-  int align;
+    if (S_IS_DEFINED (sym))
+      {
+       symbolS *label;
 
-  c = get_symbol_name (&section_name);
+       label = symbol_get_tc (ppc_current_csect)->within;
+       if (symbol_get_tc (label)->symbol_class != XMC_TC0)
+         {
+           as_bad (_(".tc with no label"));
+           ignore_rest_of_line ();
+           return;
+         }
 
-  name = xmalloc (input_line_pointer - section_name + 1);
-  strcpy (name, section_name);
+       S_SET_SEGMENT (label, S_GET_SEGMENT (sym));
+       symbol_set_frag (label, symbol_get_frag (sym));
+       S_SET_VALUE (label, S_GET_VALUE (sym));
 
-  *input_line_pointer = c;
+       while (! is_end_of_line[(unsigned char) *input_line_pointer])
+         ++input_line_pointer;
 
-  SKIP_WHITESPACE_AFTER_NAME ();
+       return;
+      }
 
-  exp = 0;
-  flags = SEC_NO_FLAGS;
+    S_SET_SEGMENT (sym, now_seg);
+    symbol_set_frag (sym, frag_now);
+    S_SET_VALUE (sym, (valueT) frag_now_fix ());
 
-  if (strcmp (name, ".idata$2") == 0)
-    {
-      align = 0;
-    }
-  else if (strcmp (name, ".idata$3") == 0)
-    {
-      align = 0;
-    }
-  else if (strcmp (name, ".idata$4") == 0)
-    {
-      align = 2;
-    }
-  else if (strcmp (name, ".idata$5") == 0)
-    {
-      align = 2;
-    }
-  else if (strcmp (name, ".idata$6") == 0)
-    {
-      align = 1;
-    }
-  else
-    /* Default alignment to 16 byte boundary.  */
-    align = 4;
+    /* AIX assembler seems to allow any storage class to be set in .tc.
+       But for now, only XMC_TC and XMC_TE are supported by us.  */
+    switch (symbol_get_tc (sym)->symbol_class)
+      {
+      case XMC_TC:
+      case XMC_TE:
+       break;
 
-  if (*input_line_pointer == ',')
-    {
-      ++input_line_pointer;
-      SKIP_WHITESPACE ();
-      if (*input_line_pointer != '"')
-       exp = get_absolute_expression ();
-      else
-       {
-         ++input_line_pointer;
-         while (*input_line_pointer != '"'
-                && ! is_end_of_line[(unsigned char) *input_line_pointer])
-           {
-             switch (*input_line_pointer)
-               {
-                 /* Section Contents */
-               case 'a': /* unknown */
-                 as_bad (_("unsupported section attribute -- 'a'"));
-                 break;
-               case 'c': /* code section */
-                 flags |= SEC_CODE;
-                 break;
-               case 'd': /* section has initialized data */
-                 flags |= SEC_DATA;
-                 break;
-               case 'u': /* section has uninitialized data */
-                 /* FIXME: This is IMAGE_SCN_CNT_UNINITIALIZED_DATA
-                    in winnt.h */
-                 flags |= SEC_ROM;
-                 break;
-               case 'i': /* section contains directives (info) */
-                 /* FIXME: This is IMAGE_SCN_LNK_INFO
-                    in winnt.h */
-                 flags |= SEC_HAS_CONTENTS;
-                 break;
-               case 'n': /* section can be discarded */
-                 flags &=~ SEC_LOAD;
-                 break;
-               case 'R': /* Remove section at link time */
-                 flags |= SEC_NEVER_LOAD;
-                 break;
-#if IFLICT_BRAIN_DAMAGE
-                 /* Section Protection */
-               case 'r': /* section is readable */
-                 flags |= IMAGE_SCN_MEM_READ;
-                 break;
-               case 'w': /* section is writeable */
-                 flags |= IMAGE_SCN_MEM_WRITE;
-                 break;
-               case 'x': /* section is executable */
-                 flags |= IMAGE_SCN_MEM_EXECUTE;
-                 break;
-               case 's': /* section is sharable */
-                 flags |= IMAGE_SCN_MEM_SHARED;
-                 break;
+      default:
+       as_bad (_(".tc with storage class %d not yet supported"),
+               symbol_get_tc (sym)->symbol_class);
+       ignore_rest_of_line ();
+       return;
+      }
+    symbol_get_tc (sym)->output = 1;
 
-                 /* Section Alignment */
-               case '0': /* align to byte boundary */
-                 flags |= IMAGE_SCN_ALIGN_1BYTES;
-                 align = 0;
-                 break;
-               case '1':  /* align to halfword boundary */
-                 flags |= IMAGE_SCN_ALIGN_2BYTES;
-                 align = 1;
-                 break;
-               case '2':  /* align to word boundary */
-                 flags |= IMAGE_SCN_ALIGN_4BYTES;
-                 align = 2;
-                 break;
-               case '3':  /* align to doubleword boundary */
-                 flags |= IMAGE_SCN_ALIGN_8BYTES;
-                 align = 3;
-                 break;
-               case '4':  /* align to quadword boundary */
-                 flags |= IMAGE_SCN_ALIGN_16BYTES;
-                 align = 4;
-                 break;
-               case '5':  /* align to 32 byte boundary */
-                 flags |= IMAGE_SCN_ALIGN_32BYTES;
-                 align = 5;
-                 break;
-               case '6':  /* align to 64 byte boundary */
-                 flags |= IMAGE_SCN_ALIGN_64BYTES;
-                 align = 6;
-                 break;
-#endif
-               default:
-                 as_bad (_("unknown section attribute '%c'"),
-                         *input_line_pointer);
-                 break;
-               }
-             ++input_line_pointer;
-           }
-         if (*input_line_pointer == '"')
-           ++input_line_pointer;
-       }
-    }
+    ppc_frob_label (sym);
+  }
+
+#endif /* OBJ_XCOFF */
+#ifdef OBJ_ELF
+  int align;
 
-  sec = subseg_new (name, (subsegT) exp);
+  /* Skip the TOC symbol name.  */
+  while (is_part_of_name (*input_line_pointer)
+        || *input_line_pointer == ' '
+        || *input_line_pointer == '['
+        || *input_line_pointer == ']'
+        || *input_line_pointer == '{'
+        || *input_line_pointer == '}')
+    ++input_line_pointer;
 
-  ppc_set_current_section (sec);
+  /* Align to a four/eight byte boundary.  */
+  align = ppc_obj64 ? 3 : 2;
+  frag_align (align, 0, 0);
+  record_alignment (now_seg, align);
+#endif /* OBJ_ELF */
 
-  if (flags != SEC_NO_FLAGS)
+  if (*input_line_pointer != ',')
+    demand_empty_rest_of_line ();
+  else
     {
-      if (! bfd_set_section_flags (stdoutput, sec, flags))
-       as_bad (_("error setting flags for \"%s\": %s"),
-               bfd_section_name (stdoutput, sec),
-               bfd_errmsg (bfd_get_error ()));
+      ++input_line_pointer;
+      cons (ppc_obj64 ? 8 : 4);
     }
-
-  bfd_set_section_alignment (stdoutput, sec, align);
 }
 
+/* Pseudo-op .machine.  */
+
 static void
-ppc_pe_function (int ignore ATTRIBUTE_UNUSED)
+ppc_machine (int ignore ATTRIBUTE_UNUSED)
 {
-  char *name;
-  char endc;
-  symbolS *ext_sym;
+  char c;
+  char *cpu_string;
+#define MAX_HISTORY 100
+  static ppc_cpu_t *cpu_history;
+  static int curr_hist;
 
-  endc = get_symbol_name (&name);
+  SKIP_WHITESPACE ();
 
-  ext_sym = symbol_find_or_make (name);
+  c = get_symbol_name (&cpu_string);
+  cpu_string = xstrdup (cpu_string);
+  (void) restore_line_pointer (c);
 
-  (void) restore_line_pointer (endc);
+  if (cpu_string != NULL)
+    {
+      ppc_cpu_t old_cpu = ppc_cpu;
+      ppc_cpu_t new_cpu;
+      char *p;
 
-  S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT);
-  SF_SET_FUNCTION (ext_sym);
-  SF_SET_PROCESS (ext_sym);
-  coff_add_linesym (ext_sym);
+      for (p = cpu_string; *p != 0; p++)
+       *p = TOLOWER (*p);
 
-  demand_empty_rest_of_line ();
-}
+      if (strcmp (cpu_string, "push") == 0)
+       {
+         if (cpu_history == NULL)
+           cpu_history = XNEWVEC (ppc_cpu_t, MAX_HISTORY);
 
-static void
-ppc_pe_tocd (int ignore ATTRIBUTE_UNUSED)
-{
-  if (tocdata_section == 0)
-    {
-      tocdata_section = subseg_new (".tocd", 0);
-      /* FIXME: section flags won't work.  */
-      bfd_set_section_flags (stdoutput, tocdata_section,
-                            (SEC_ALLOC | SEC_LOAD | SEC_RELOC
-                             | SEC_READONLY | SEC_DATA));
+         if (curr_hist >= MAX_HISTORY)
+           as_bad (_(".machine stack overflow"));
+         else
+           cpu_history[curr_hist++] = ppc_cpu;
+       }
+      else if (strcmp (cpu_string, "pop") == 0)
+       {
+         if (curr_hist <= 0)
+           as_bad (_(".machine stack underflow"));
+         else
+           ppc_cpu = cpu_history[--curr_hist];
+       }
+      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);
 
-      bfd_set_section_alignment (stdoutput, tocdata_section, 2);
-    }
-  else
-    {
-      rdata_section = subseg_new (".tocd", 0);
+      if (ppc_cpu != old_cpu)
+       ppc_setup_opcodes ();
     }
 
-  ppc_set_current_section (tocdata_section);
-
   demand_empty_rest_of_line ();
 }
-
-/* Don't adjust TOC relocs to use the section symbol.  */
-
-int
-ppc_pe_fix_adjustable (fixS *fix)
-{
-  return fix->fx_r_type != BFD_RELOC_PPC_TOC16;
-}
-
-#endif
+#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
 \f
 #ifdef OBJ_XCOFF
 
@@ -5682,12 +5828,18 @@ ppc_symbol_new_hook (symbolS *sym)
        tc->symbol_class = XMC_TB;
       else if (strcmp (s, "TC0]") == 0 || strcmp (s, "T0]") == 0)
        tc->symbol_class = XMC_TC0;
+      else if (strcmp (s, "TE]") == 0)
+       tc->symbol_class = XMC_TE;
+      else if (strcmp (s, "TL]") == 0)
+       tc->symbol_class = XMC_TL;
       break;
     case 'U':
       if (strcmp (s, "UA]") == 0)
        tc->symbol_class = XMC_UA;
       else if (strcmp (s, "UC]") == 0)
        tc->symbol_class = XMC_UC;
+      else if (strcmp (s, "UL]") == 0)
+       tc->symbol_class = XMC_UL;
       break;
     case 'X':
       if (strcmp (s, "XO]") == 0)
@@ -5699,35 +5851,11 @@ ppc_symbol_new_hook (symbolS *sym)
     as_bad (_("unrecognized symbol suffix"));
 }
 
-/* Set the class of a label based on where it is defined.  This
-   handles symbols without suffixes.  Also, move the symbol so that it
-   follows the csect symbol.  */
-
-void
-ppc_frob_label (symbolS *sym)
-{
-  if (ppc_current_csect != (symbolS *) NULL)
-    {
-      if (symbol_get_tc (sym)->symbol_class == -1)
-       symbol_get_tc (sym)->symbol_class = symbol_get_tc (ppc_current_csect)->symbol_class;
-
-      symbol_remove (sym, &symbol_rootP, &symbol_lastP);
-      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
-  dwarf2_emit_label (sym);
-#endif
-}
-
 /* This variable is set by ppc_frob_symbol if any absolute symbols are
    seen.  It tells ppc_adjust_symtab whether it needs to look through
    the symbols.  */
 
-static bfd_boolean ppc_saw_abs;
+static bool ppc_saw_abs;
 
 /* Change the name of a symbol just before writing it out.  Set the
    real name if the .rename pseudo-op was used.  Otherwise, remove any
@@ -5743,6 +5871,7 @@ ppc_frob_symbol (symbolS *sym)
   /* Discard symbols that should not be included in the output symbol
      table.  */
   if (! symbol_used_in_reloc_p (sym)
+      && S_GET_STORAGE_CLASS (sym) != C_DWARF
       && ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0
          || (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym))
              && ! symbol_get_tc (sym)->output
@@ -5768,9 +5897,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);
        }
@@ -5784,8 +5911,9 @@ ppc_frob_symbol (symbolS *sym)
 
   if (SF_GET_FUNCTION (sym))
     {
-      if (ppc_last_function != (symbolS *) NULL)
-       as_bad (_("two .function pseudo-ops with no intervening .ef"));
+      /* Make sure coff_last_function is reset. Otherwise, we won't create
+         the auxent for the next function.  */
+      coff_last_function = 0;
       ppc_last_function = sym;
       if (symbol_get_tc (sym)->u.size != (symbolS *) NULL)
        {
@@ -5793,6 +5921,16 @@ ppc_frob_symbol (symbolS *sym)
          SA_SET_SYM_FSIZE (sym,
                            (long) S_GET_VALUE (symbol_get_tc (sym)->u.size));
        }
+      else
+       {
+         /* Size of containing csect.  */
+         symbolS* within = symbol_get_tc (sym)->within;
+         union internal_auxent *csectaux;
+         csectaux = &coffsymbol (symbol_get_bfdsym (within))
+           ->native[S_GET_NUMBER_AUXILIARY(within)].u.auxent;
+
+         SA_SET_SYM_FSIZE (sym, csectaux->x_csect.x_scnlen.l);
+       }
     }
   else if (S_GET_STORAGE_CLASS (sym) == C_FCN
           && strcmp (S_GET_NAME (sym), ".ef") == 0)
@@ -5845,8 +5983,7 @@ ppc_frob_symbol (symbolS *sym)
          /* This is a csect symbol.  x_scnlen is the size of the
             csect.  */
          if (symbol_get_tc (sym)->next == (symbolS *) NULL)
-           a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
-                                                      S_GET_SEGMENT (sym))
+           a->x_csect.x_scnlen.l = (bfd_section_size (S_GET_SEGMENT (sym))
                                     - S_GET_VALUE (sym));
          else
            {
@@ -5854,14 +5991,20 @@ ppc_frob_symbol (symbolS *sym)
              a->x_csect.x_scnlen.l = (S_GET_VALUE (symbol_get_tc (sym)->next)
                                       - S_GET_VALUE (sym));
            }
-         a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD;
+         if (symbol_get_tc (sym)->symbol_class == XMC_BS)
+           a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_CM;
+         else
+           a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD;
        }
-      else if (S_GET_SEGMENT (sym) == bss_section)
+      else if (S_GET_SEGMENT (sym) == bss_section
+              || S_GET_SEGMENT (sym) == ppc_xcoff_tbss_section.segment)
        {
          /* This is a common symbol.  */
          a->x_csect.x_scnlen.l = symbol_get_frag (sym)->fr_offset;
          a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_CM;
-         if (S_IS_EXTERNAL (sym))
+         if (S_GET_SEGMENT (sym) == ppc_xcoff_tbss_section.segment)
+           symbol_get_tc (sym)->symbol_class = XMC_UL;
+         else if (S_IS_EXTERNAL (sym))
            symbol_get_tc (sym)->symbol_class = XMC_RW;
          else
            symbol_get_tc (sym)->symbol_class = XMC_BS;
@@ -5870,7 +6013,7 @@ ppc_frob_symbol (symbolS *sym)
        {
          /* This is an absolute symbol.  The csect will be created by
             ppc_adjust_symtab.  */
-         ppc_saw_abs = TRUE;
+         ppc_saw_abs = true;
          a->x_csect.x_smtyp = XTY_LD;
          if (symbol_get_tc (sym)->symbol_class == -1)
            symbol_get_tc (sym)->symbol_class = XMC_XO;
@@ -5881,7 +6024,7 @@ ppc_frob_symbol (symbolS *sym)
          a->x_csect.x_scnlen.l = 0;
          a->x_csect.x_smtyp = XTY_ER;
        }
-      else if (symbol_get_tc (sym)->symbol_class == XMC_TC)
+      else if (ppc_is_toc_sym (sym))
        {
          symbolS *next;
 
@@ -5891,11 +6034,10 @@ ppc_frob_symbol (symbolS *sym)
          while (symbol_get_tc (next)->symbol_class == XMC_TC0)
            next = symbol_next (next);
          if (next == (symbolS *) NULL
-             || symbol_get_tc (next)->symbol_class != XMC_TC)
+             || (!ppc_is_toc_sym (next)))
            {
              if (ppc_after_toc_frag == (fragS *) NULL)
-               a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput,
-                                                          data_section)
+               a->x_csect.x_scnlen.l = (bfd_section_size (data_section)
                                         - S_GET_VALUE (sym));
              else
                a->x_csect.x_scnlen.l = (ppc_after_toc_frag->fr_address
@@ -5916,9 +6058,11 @@ ppc_frob_symbol (symbolS *sym)
          /* This is a normal symbol definition.  x_scnlen is the
             symbol index of the containing csect.  */
          if (S_GET_SEGMENT (sym) == text_section)
-           csect = ppc_text_csects;
+           csect = ppc_xcoff_text_section.csects;
          else if (S_GET_SEGMENT (sym) == data_section)
-           csect = ppc_data_csects;
+           csect = ppc_xcoff_data_section.csects;
+         else if (S_GET_SEGMENT (sym) == ppc_xcoff_tdata_section.segment)
+           csect = ppc_xcoff_tdata_section.csects;
          else
            abort ();
 
@@ -6001,16 +6145,46 @@ ppc_frob_symbol (symbolS *sym)
       coffsymbol (symbol_get_bfdsym (sym))->native->fix_line = 1;
     }
 
-  return 0;
-}
+  return 0;
+}
+
+/* Adjust the symbol table.  */
+
+void
+ppc_adjust_symtab (void)
+{
+  symbolS *sym;
+  symbolS *anchorSym;
+
+  /* Make sure C_DWARF symbols come right after C_FILE.
+     As the C_FILE might not be defined yet and as C_DWARF
+     might already be ordered, we insert them before the
+     first symbol which isn't a C_FILE or a C_DWARF.  */
+  for (anchorSym = symbol_rootP; anchorSym != NULL;
+       anchorSym = symbol_next (anchorSym))
+    {
+      if (S_GET_STORAGE_CLASS (anchorSym) != C_FILE
+         && S_GET_STORAGE_CLASS (anchorSym) != C_DWARF)
+       break;
+    }
+
+  sym = anchorSym;
+  while (sym != NULL)
+    {
+      if (S_GET_STORAGE_CLASS (sym) != C_DWARF)
+       {
+         sym = symbol_next (sym);
+         continue;
+       }
+
+      symbolS* tsym = sym;
+      sym = symbol_next (sym);
 
-/* Adjust the symbol table.  This creates csect symbols for all
-   absolute symbols.  */
+      symbol_remove (tsym, &symbol_rootP, &symbol_lastP);
+      symbol_insert (tsym, anchorSym, &symbol_rootP, &symbol_lastP);
+    }
 
-void
-ppc_adjust_symtab (void)
-{
-  symbolS *sym;
+  /* Create csect symbols for all absolute symbols.  */
 
   if (! ppc_saw_abs)
     return;
@@ -6025,7 +6199,7 @@ ppc_adjust_symtab (void)
        continue;
 
       csect = symbol_create (".abs[XO]", absolute_section,
-                            S_GET_VALUE (sym), &zero_address_frag);
+                            &zero_address_frag, S_GET_VALUE (sym));
       symbol_get_bfdsym (csect)->value = S_GET_VALUE (sym);
       S_SET_STORAGE_CLASS (csect, C_HIDEXT);
       i = S_GET_NUMBER_AUXILIARY (csect);
@@ -6047,7 +6221,7 @@ ppc_adjust_symtab (void)
       coffsymbol (symbol_get_bfdsym (sym))->native[i].fix_scnlen = 1;
     }
 
-  ppc_saw_abs = FALSE;
+  ppc_saw_abs = false;
 }
 
 /* Set the VMA for a section.  This is called on all the sections in
@@ -6059,17 +6233,17 @@ ppc_frob_section (asection *sec)
   static bfd_vma vma = 0;
 
   /* Dwarf sections start at 0.  */
-  if (bfd_get_section_flags (NULL, sec) & SEC_DEBUGGING)
+  if (bfd_section_flags (sec) & SEC_DEBUGGING)
     return;
 
   vma = md_section_align (sec, vma);
-  bfd_set_section_vma (stdoutput, sec, vma);
-  vma += bfd_section_size (stdoutput, sec);
+  bfd_set_section_vma (sec, vma);
+  vma += bfd_section_size (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);
@@ -6095,7 +6269,7 @@ md_section_align (asection *seg ATTRIBUTE_UNUSED, valueT addr)
 #ifdef OBJ_ELF
   return addr;
 #else
-  int align = bfd_get_section_alignment (stdoutput, seg);
+  int align = bfd_section_alignment (seg);
 
   return ((addr + (1 << align) - 1) & -(1 << align));
 #endif
@@ -6142,6 +6316,45 @@ md_pcrel_from_section (fixS *fixp, segT sec ATTRIBUTE_UNUSED)
 
 #ifdef OBJ_XCOFF
 
+/* Return the surrending csect for sym when possible.  */
+
+static symbolS*
+ppc_get_csect_to_adjust (symbolS *sym)
+{
+  if (sym == NULL)
+    return NULL;
+
+  valueT val = resolve_symbol_value (sym);
+  TC_SYMFIELD_TYPE *tc = symbol_get_tc (sym);
+  segT symseg = S_GET_SEGMENT (sym);
+
+  if (tc->subseg == 0
+      && tc->symbol_class != XMC_TC0
+      && tc->symbol_class != XMC_TC
+      && tc->symbol_class != XMC_TE
+      && symseg != bss_section
+      && symseg != ppc_xcoff_tbss_section.segment
+      /* Don't adjust if this is a reloc in the toc section.  */
+      && (symseg != data_section
+         || ppc_toc_csect == NULL
+         || val < ppc_toc_frag->fr_address
+         || (ppc_after_toc_frag != NULL
+             && val >= ppc_after_toc_frag->fr_address)))
+    {
+      symbolS* csect = tc->within;
+
+      /* 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;
+
+      return csect;
+    }
+
+  return NULL;
+}
+
 /* This is called to see whether a fixup should be adjusted to use a
    section symbol.  We take the opportunity to change a fixup against
    a symbol in the TOC subsegment into a reloc against the
@@ -6152,13 +6365,13 @@ ppc_fix_adjustable (fixS *fix)
 {
   valueT val = resolve_symbol_value (fix->fx_addsy);
   segT symseg = S_GET_SEGMENT (fix->fx_addsy);
-  TC_SYMFIELD_TYPE *tc;
+  symbolS* csect;
 
   if (symseg == absolute_section)
     return 0;
 
   /* Always adjust symbols in debugging sections.  */
-  if (bfd_get_section_flags (stdoutput, symseg) & SEC_DEBUGGING)
+  if (bfd_section_flags (symseg) & SEC_DEBUGGING)
     return 1;
 
   if (ppc_toc_csect != (symbolS *) NULL
@@ -6178,7 +6391,8 @@ ppc_fix_adjustable (fixS *fix)
 
          if (sy_tc->symbol_class == XMC_TC0)
            continue;
-         if (sy_tc->symbol_class != XMC_TC)
+         if (sy_tc->symbol_class != XMC_TC
+             && sy_tc->symbol_class != XMC_TE)
            break;
          if (val == resolve_symbol_value (sy))
            {
@@ -6193,36 +6407,24 @@ ppc_fix_adjustable (fixS *fix)
     }
 
   /* Possibly adjust the reloc to be against the csect.  */
-  tc = symbol_get_tc (fix->fx_addsy);
-  if (tc->subseg == 0
-      && tc->symbol_class != XMC_TC0
-      && tc->symbol_class != XMC_TC
-      && symseg != bss_section
-      /* Don't adjust if this is a reloc in the toc section.  */
-      && (symseg != data_section
-         || ppc_toc_csect == NULL
-         || val < ppc_toc_frag->fr_address
-         || (ppc_after_toc_frag != NULL
-             && val >= ppc_after_toc_frag->fr_address)))
+  if ((csect = ppc_get_csect_to_adjust (fix->fx_addsy)) != NULL)
     {
-      symbolS *csect = tc->within;
-
-      /* 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;
-
       fix->fx_offset += val - symbol_get_frag (csect)->fr_address;
       fix->fx_addsy = csect;
+    }
 
-      return 0;
+  if ((csect = ppc_get_csect_to_adjust (fix->fx_subsy)) != NULL)
+    {
+      fix->fx_offset -= resolve_symbol_value (fix->fx_subsy)
+       - symbol_get_frag (csect)->fr_address;
+      fix->fx_subsy = csect;
     }
 
   /* Adjust a reloc against a .lcomm symbol to be against the base
      .lcomm.  */
   if (symseg == bss_section
-      && ! S_IS_EXTERNAL (fix->fx_addsy))
+      && ! S_IS_EXTERNAL (fix->fx_addsy)
+      && symbol_get_tc (fix->fx_addsy)->subseg == 0)
     {
       symbolS *sy = symbol_get_frag (fix->fx_addsy)->fr_symbol;
 
@@ -6256,14 +6458,6 @@ 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
@@ -6288,13 +6482,13 @@ ppc_force_relocation (fixS *fix)
     case BFD_RELOC_PPC_BA26:
     case BFD_RELOC_PPC_B16:
     case BFD_RELOC_PPC_BA16:
+    case BFD_RELOC_PPC64_REL24_NOTOC:
       /* 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);
+         elf_symbol_type *elfsym = elf_symbol_from (bfdsym);
          gas_assert (elfsym);
          if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
            return 1;
@@ -6305,7 +6499,7 @@ ppc_force_relocation (fixS *fix)
     }
 
   if (fix->fx_r_type >= BFD_RELOC_PPC_TLS
-      && fix->fx_r_type <= BFD_RELOC_PPC64_DTPREL16_HIGHESTA)
+      && fix->fx_r_type <= BFD_RELOC_PPC64_TLS_PCREL)
     return 1;
 
   return generic_force_reloc (fix);
@@ -6326,11 +6520,11 @@ ppc_fix_adjustable (fixS *fix)
     case BFD_RELOC_PPC_B16_BRNTAKEN:
     case BFD_RELOC_PPC_BA16_BRTAKEN:
     case BFD_RELOC_PPC_BA16_BRNTAKEN:
+    case BFD_RELOC_PPC64_REL24_NOTOC:
       if (fix->fx_addsy)
        {
          asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
-         elf_symbol_type *elfsym
-           = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         elf_symbol_type *elfsym = elf_symbol_from (bfdsym);
          gas_assert (elfsym);
          if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
            return 0;
@@ -6346,52 +6540,95 @@ ppc_fix_adjustable (fixS *fix)
          && 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_PPC64_GOT_PCREL34
+         && fix->fx_r_type != BFD_RELOC_24_PLT_PCREL
+         && fix->fx_r_type != BFD_RELOC_32_PLTOFF
+         && fix->fx_r_type != BFD_RELOC_32_PLT_PCREL
+         && fix->fx_r_type != BFD_RELOC_LO16_PLTOFF
+         && fix->fx_r_type != BFD_RELOC_HI16_PLTOFF
+         && fix->fx_r_type != BFD_RELOC_HI16_S_PLTOFF
+         && fix->fx_r_type != BFD_RELOC_64_PLTOFF
+         && fix->fx_r_type != BFD_RELOC_64_PLT_PCREL
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLT16_LO_DS
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLT_PCREL34
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16_LO
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16_HI
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16_HA
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16_DS
+         && fix->fx_r_type != BFD_RELOC_PPC64_PLTGOT16_LO_DS
          && fix->fx_r_type != BFD_RELOC_GPREL16
+         && fix->fx_r_type != BFD_RELOC_PPC_VLE_SDAREL_LO16A
+         && fix->fx_r_type != BFD_RELOC_PPC_VLE_SDAREL_HI16A
+         && fix->fx_r_type != BFD_RELOC_PPC_VLE_SDAREL_HA16A
          && 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));
+              && fix->fx_r_type <= BFD_RELOC_PPC64_TLS_PCREL));
 }
 #endif
 
 void
 ppc_frag_check (struct frag *fragP)
 {
-  if (!fragP->has_code)
-    return;
+  if ((fragP->fr_address & fragP->insn_addr) != 0)
+    as_bad_where (fragP->fr_file, fragP->fr_line,
+                 _("instruction address is not a multiple of %d"),
+                 fragP->insn_addr + 1);
+}
 
-  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
+/* rs_align_code frag handling.  */
+
+enum ppc_nop_encoding_for_rs_align_code
+{
+  PPC_NOP_VANILLA,
+  PPC_NOP_VLE,
+  PPC_NOP_GROUP_P6,
+  PPC_NOP_GROUP_P7
+};
+
+unsigned int
+ppc_nop_select (void)
+{
+  if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
+    return PPC_NOP_VLE;
+  if ((ppc_cpu & (PPC_OPCODE_POWER9 | PPC_OPCODE_E500MC)) == 0)
     {
-      if (((fragP->fr_address + fragP->insn_addr) & 3) != 0)
-        as_bad (_("instruction address is not a multiple of 4"));
+      if ((ppc_cpu & PPC_OPCODE_POWER7) != 0)
+       return PPC_NOP_GROUP_P7;
+      if ((ppc_cpu & PPC_OPCODE_POWER6) != 0)
+       return PPC_NOP_GROUP_P6;
     }
+  return PPC_NOP_VANILLA;
 }
 
-/* Implement HANDLE_ALIGN.  This writes the NOP pattern into an
-   rs_align_code frag.  */
-
 void
 ppc_handle_align (struct frag *fragP)
 {
   valueT count = (fragP->fr_next->fr_address
                  - (fragP->fr_address + fragP->fr_fix));
+  char *dest = fragP->fr_literal + fragP->fr_fix;
+  enum ppc_nop_encoding_for_rs_align_code nop_select = *dest & 0xff;
+
+  /* Pad with zeros if not inserting a whole number of instructions.
+     We could pad with zeros up to an instruction boundary then follow
+     with nops but odd counts indicate data in an executable section
+     so padding with zeros is most appropriate.  */
+  if (count == 0
+      || (nop_select == PPC_NOP_VLE ? (count & 1) != 0 : (count & 3) != 0))
+    {
+      *dest = 0;
+      return;
+    }
 
-  if (ppc_mach() == bfd_mach_ppc_vle && count != 0 && (count & 1) == 0)
+  if (nop_select == PPC_NOP_VLE)
     {
-      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)
+  else
     {
-      char *dest = fragP->fr_literal + fragP->fr_fix;
-
       fragP->fr_var = 4;
 
       if (count > 4 * nop_limit && count < 0x2000000)
@@ -6420,16 +6657,13 @@ ppc_handle_align (struct frag *fragP)
 
       md_number_to_chars (dest, 0x60000000, 4);
 
-      if ((ppc_cpu & PPC_OPCODE_POWER6) != 0
-         || (ppc_cpu & PPC_OPCODE_POWER7) != 0
-         || (ppc_cpu & PPC_OPCODE_POWER8) != 0
-         || (ppc_cpu & PPC_OPCODE_POWER9) != 0)
+      if (nop_select >= PPC_NOP_GROUP_P6)
        {
-         /* 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.  */
+         /* For power6, power7, and power8, we want the last nop to
+            be a group terminating one.  Do this by inserting an
+            rs_fill frag immediately after this one, with its address
+            set to the last nop location.  This will automatically
+            reduce the number of nops in the current frag by one.  */
          if (count > 4)
            {
              struct frag *group_nop = xmalloc (SIZEOF_STRUCT_FRAG + 4);
@@ -6443,20 +6677,12 @@ ppc_handle_align (struct frag *fragP)
              dest = group_nop->fr_literal;
            }
 
-         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
+         if (nop_select == PPC_NOP_GROUP_P6)
            /* power6 group terminating nop: "ori 1,1,0".  */
            md_number_to_chars (dest, 0x60210000, 4);
+         else
+           /* power7/power8 group terminating nop: "ori 2,2,0".  */
+           md_number_to_chars (dest, 0x60420000, 4);
        }
     }
 }
@@ -6505,10 +6731,98 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
     }
 #endif
 
-  if (fixP->fx_subsy != (symbolS *) NULL)
+  /* We are only able to convert some relocs to pc-relative.  */
+  if (fixP->fx_pcrel)
+    {
+      switch (fixP->fx_r_type)
+       {
+       case BFD_RELOC_64:
+         fixP->fx_r_type = BFD_RELOC_64_PCREL;
+         break;
+
+       case BFD_RELOC_32:
+         fixP->fx_r_type = BFD_RELOC_32_PCREL;
+         break;
+
+       case BFD_RELOC_16:
+         fixP->fx_r_type = BFD_RELOC_16_PCREL;
+         break;
+
+       case BFD_RELOC_LO16:
+         fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
+         break;
+
+       case BFD_RELOC_HI16:
+         fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
+         break;
+
+       case BFD_RELOC_HI16_S:
+         fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
+         break;
+
+       case BFD_RELOC_PPC64_ADDR16_HIGH:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGH;
+         break;
+
+       case BFD_RELOC_PPC64_ADDR16_HIGHA:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHA;
+         break;
+
+       case BFD_RELOC_PPC64_HIGHER:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHER;
+         break;
+
+       case BFD_RELOC_PPC64_HIGHER_S:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHERA;
+         break;
+
+       case BFD_RELOC_PPC64_HIGHEST:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHEST;
+         break;
+
+       case BFD_RELOC_PPC64_HIGHEST_S:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHESTA;
+         break;
+
+       case BFD_RELOC_PPC64_ADDR16_HIGHER34:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHER34;
+         break;
+
+       case BFD_RELOC_PPC64_ADDR16_HIGHERA34:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHERA34;
+         break;
+
+       case BFD_RELOC_PPC64_ADDR16_HIGHEST34:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHEST34;
+         break;
+
+       case BFD_RELOC_PPC64_ADDR16_HIGHESTA34:
+         fixP->fx_r_type = BFD_RELOC_PPC64_REL16_HIGHESTA34;
+         break;
+
+       case BFD_RELOC_PPC_16DX_HA:
+         fixP->fx_r_type = BFD_RELOC_PPC_REL16DX_HA;
+         break;
+
+       case BFD_RELOC_PPC64_D34:
+         fixP->fx_r_type = BFD_RELOC_PPC64_PCREL34;
+         break;
+
+       case BFD_RELOC_PPC64_D28:
+         fixP->fx_r_type = BFD_RELOC_PPC64_PCREL28;
+         break;
+
+       default:
+         break;
+       }
+    }
+  else if (!fixP->fx_done
+          && fixP->fx_r_type == BFD_RELOC_PPC_16DX_HA)
     {
-      /* We can't actually support subtracting a symbol.  */
-      as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
+      /* addpcis is relative to next insn address.  */
+      value -= 4;
+      fixP->fx_r_type = BFD_RELOC_PPC_REL16DX_HA;
+      fixP->fx_pcrel = 1;
     }
 
   operand = NULL;
@@ -6529,8 +6843,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
          && (operand->insert == NULL || ppc_obj64)
          && fixP->fx_addsy != NULL
          && symbol_get_tc (fixP->fx_addsy)->subseg != 0
-         && symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC
-         && symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC0
+         && !ppc_is_toc_sym (fixP->fx_addsy)
          && S_GET_SEGMENT (fixP->fx_addsy) != bss_section)
        {
          value = fixP->fx_offset;
@@ -6544,7 +6857,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        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;
+        fixP->fx_r_type = BFD_RELOC_PPC_TOC16;
 #endif
     }
 
@@ -6579,7 +6892,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
            }
          break;
        }
-      /* Fall thru */
+      /* Fallthru */
 
     case BFD_RELOC_PPC_VLE_HI16A:
     case BFD_RELOC_PPC_VLE_HI16D:
@@ -6590,6 +6903,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 
     case BFD_RELOC_HI16_S:
     case BFD_RELOC_HI16_S_PCREL:
+    case BFD_RELOC_PPC_16DX_HA:
     case BFD_RELOC_PPC_REL16DX_HA:
 #ifdef OBJ_ELF
       if (REPORT_OVERFLOW_HI && ppc_obj64)
@@ -6602,7 +6916,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
            }
          break;
        }
-      /* Fall thru */
+      /* Fallthru */
 
     case BFD_RELOC_PPC_VLE_HA16A:
     case BFD_RELOC_PPC_VLE_HA16D:
@@ -6681,6 +6995,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
        case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
        case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
+       case BFD_RELOC_PPC64_TPREL34:
+       case BFD_RELOC_PPC64_DTPREL34:
+       case BFD_RELOC_PPC64_GOT_TLSGD_PCREL34:
+       case BFD_RELOC_PPC64_GOT_TLSLD_PCREL34:
+       case BFD_RELOC_PPC64_GOT_TPREL_PCREL34:
+       case BFD_RELOC_PPC64_GOT_DTPREL_PCREL34:
          gas_assert (fixP->fx_addsy != NULL);
          S_SET_THREAD_LOCAL (fixP->fx_addsy);
          fieldval = 0;
@@ -6743,12 +7063,15 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
        case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
        case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+       case BFD_RELOC_PPC64_GOT_PCREL34:
+       case BFD_RELOC_PPC64_PLT_PCREL34:
          gas_assert (fixP->fx_addsy != NULL);
-         /* Fall thru */
+         /* Fallthru */
 
        case BFD_RELOC_PPC_TLS:
        case BFD_RELOC_PPC_TLSGD:
        case BFD_RELOC_PPC_TLSLD:
+       case BFD_RELOC_PPC64_TLS_PCREL:
          fieldval = 0;
          break;
 #endif
@@ -6794,9 +7117,12 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 #else
 #define APPLY_RELOC 1
 #endif
+      /* We need to call the insert function even when fieldval is
+        zero if the insert function would translate that zero to a
+        bit pattern other than all zeros.  */
       if ((fieldval != 0 && APPLY_RELOC) || operand->insert != NULL)
        {
-         unsigned long insn;
+         uint64_t insn;
          unsigned char *where;
 
          /* Fetch the instruction, insert the fully resolved operand
@@ -6804,34 +7130,56 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
          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
+             if (fixP->fx_size < 4)
                insn = bfd_getb16 (where);
+             else
+               {
+                 insn = bfd_getb32 (where);
+                 if (fixP->fx_size > 4)
+                   insn = insn << 32 | bfd_getb32 (where + 4);
+               }
            }
          else
            {
-             if (fixP->fx_size == 4)
-               insn = bfd_getl32 (where);
-             else
+             if (fixP->fx_size < 4)
                insn = bfd_getl16 (where);
+             else
+               {
+                 insn = bfd_getl32 (where);
+                 if (fixP->fx_size > 4)
+                   insn = insn << 32 | bfd_getl32 (where + 4);
+               }
            }
          insn = ppc_insert_operand (insn, operand, fieldval,
                                     fixP->tc_fix_data.ppc_cpu,
                                     fixP->fx_file, fixP->fx_line);
          if (target_big_endian)
            {
-             if (fixP->fx_size == 4)
-               bfd_putb32 (insn, where);
-             else
+             if (fixP->fx_size < 4)
                bfd_putb16 (insn, where);
+             else
+               {
+                 if (fixP->fx_size > 4)
+                   {
+                     bfd_putb32 (insn, where + 4);
+                     insn >>= 32;
+                   }
+                 bfd_putb32 (insn, where);
+               }
            }
          else
            {
-             if (fixP->fx_size == 4)
-               bfd_putl32 (insn, where);
-             else
+             if (fixP->fx_size < 4)
                bfd_putl16 (insn, where);
+             else
+               {
+                 if (fixP->fx_size > 4)
+                   {
+                     bfd_putl32 (insn, where + 4);
+                     insn >>= 32;
+                   }
+                 bfd_putl32 (insn, where);
+               }
            }
        }
 
@@ -6868,7 +7216,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
              && !S_IS_DEFINED (fixP->fx_addsy)
              && !S_IS_WEAK (fixP->fx_addsy))
            S_SET_WEAK (fixP->fx_addsy);
-         /* Fall thru */
+         /* Fallthru */
 
        case BFD_RELOC_VTABLE_ENTRY:
          fixP->fx_done = 0;
@@ -6962,6 +7310,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        case BFD_RELOC_PPC_EMB_RELSDA:
        case BFD_RELOC_PPC64_TOC:
        case BFD_RELOC_PPC_TOC16:
+       case BFD_RELOC_PPC_TOC16_LO:
+       case BFD_RELOC_PPC_TOC16_HI:
        case BFD_RELOC_PPC64_TOC16_LO:
        case BFD_RELOC_PPC64_TOC16_HI:
        case BFD_RELOC_PPC64_TOC16_HA:
@@ -6977,11 +7327,43 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        case BFD_RELOC_PPC64_TPREL16_HIGHERA:
        case BFD_RELOC_PPC64_TPREL16_HIGHEST:
        case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+       case BFD_RELOC_PPC64_TLS_PCREL:
          fixP->fx_done = 0;
          break;
 #endif
 
 #ifdef OBJ_XCOFF
+       case BFD_RELOC_PPC_TLSGD:
+       case BFD_RELOC_PPC_TLSLD:
+       case BFD_RELOC_PPC_TLSLE:
+       case BFD_RELOC_PPC_TLSIE:
+       case BFD_RELOC_PPC_TLSM:
+       case BFD_RELOC_PPC64_TLSGD:
+       case BFD_RELOC_PPC64_TLSLD:
+       case BFD_RELOC_PPC64_TLSLE:
+       case BFD_RELOC_PPC64_TLSIE:
+       case BFD_RELOC_PPC64_TLSM:
+         gas_assert (fixP->fx_addsy != NULL);
+         S_SET_THREAD_LOCAL (fixP->fx_addsy);
+         fieldval = 0;
+         break;
+
+         /* TLSML relocations are targeting a XMC_TC symbol named
+            "_$TLSML". We can't check earlier because the relocation
+            can target any symbol name which will be latter .rename
+            to "_$TLSML".  */
+       case BFD_RELOC_PPC_TLSML:
+       case BFD_RELOC_PPC64_TLSML:
+         gas_assert (fixP->fx_addsy != NULL);
+         if (strcmp (symbol_get_tc (fixP->fx_addsy)->real_name, "_$TLSML") != 0)
+           {
+             as_bad_where (fixP->fx_file, fixP->fx_line,
+                           _("R_TLSML relocation doesn't target a "
+                             "symbol named \"_$TLSML\". %s"), S_GET_NAME(fixP->fx_addsy));
+           }
+         fieldval = 0;
+         break;
+
        case BFD_RELOC_NONE:
 #endif
        case BFD_RELOC_CTOR:
@@ -7017,83 +7399,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
                       _("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;
-
-       case BFD_RELOC_HI16:
-         fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
-         break;
-
-       case BFD_RELOC_HI16_S:
-         fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
-         break;
-
-       case BFD_RELOC_64:
-         fixP->fx_r_type = BFD_RELOC_64_PCREL;
-         break;
-
-       case BFD_RELOC_32:
-         fixP->fx_r_type = BFD_RELOC_32_PCREL;
-         break;
-
-       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_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;
-
-       default:
-         if (fixP->fx_addsy)
-           {
-             const char *sfile;
-             unsigned int sline;
-
-             /* 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,
-                             _("unresolved expression that must"
-                               " be resolved"));
-             else
-               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;
-       }
-    }
-
 #ifdef OBJ_ELF
   ppc_elf_validate_fix (fixP, seg);
   fixP->fx_addnumber = value;
@@ -7115,48 +7420,85 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        symbol_get_bfdsym (fixP->fx_addsy)->flags |= BSF_KEEP;
     }
 #else
-  if (fixP->fx_r_type != BFD_RELOC_PPC_TOC16)
-    fixP->fx_addnumber = 0;
-  else
+  if (fixP->fx_r_type == BFD_RELOC_PPC_TOC16
+      || fixP->fx_r_type == BFD_RELOC_PPC_TOC16_HI
+      || fixP->fx_r_type == BFD_RELOC_PPC_TOC16_LO)
     {
-#ifdef TE_PE
-      fixP->fx_addnumber = 0;
-#else
       /* We want to use the offset within the toc, not the actual VMA
         of the symbol.  */
-      fixP->fx_addnumber =
-       - bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixP->fx_addsy))
-       - S_GET_VALUE (ppc_toc_csect);
+      fixP->fx_addnumber = (- bfd_section_vma (S_GET_SEGMENT (fixP->fx_addsy))
+                           - S_GET_VALUE (ppc_toc_csect));
+
+      /* The high bits must be adjusted for the low bits being signed.  */
+      if (fixP->fx_r_type == BFD_RELOC_PPC_TOC16_HI) {
+       fixP->fx_addnumber += 0x8000;
+      }
+
       /* Set *valP to avoid errors.  */
       *valP = value;
-#endif
     }
+  else if (fixP->fx_r_type == BFD_RELOC_PPC_TLSM
+          || fixP->fx_r_type == BFD_RELOC_PPC64_TLSM)
+    /* AIX ld expects the section contents for these relocations
+       to be zero.  Arrange for that to occur when
+       bfd_install_relocation is called.  */
+    fixP->fx_addnumber = (- bfd_section_vma (S_GET_SEGMENT (fixP->fx_addsy))
+                         - S_GET_VALUE (fixP->fx_addsy));
+  else
+    fixP->fx_addnumber = 0;
 #endif
 }
 
 /* Generate a reloc for a fixup.  */
 
-arelent *
+arelent **
 tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
 {
+  static arelent *relocs[3];
   arelent *reloc;
 
-  reloc = (arelent *) xmalloc (sizeof (arelent));
+  relocs[0] = reloc = XNEW (arelent);
+  relocs[1] = NULL;
 
-  reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+  reloc->sym_ptr_ptr = XNEW (asymbol *);
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
   reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+  /* BFD_RELOC_PPC64_TLS_PCREL generates R_PPC64_TLS with an odd r_offset.  */
+  if (fixp->fx_r_type == BFD_RELOC_PPC64_TLS_PCREL)
+    reloc->address++;
   reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
   if (reloc->howto == (reloc_howto_type *) NULL)
     {
       as_bad_where (fixp->fx_file, fixp->fx_line,
                    _("reloc %d not supported by object file format"),
                    (int) fixp->fx_r_type);
-      return NULL;
+      relocs[0] = NULL;
     }
   reloc->addend = fixp->fx_addnumber;
 
-  return reloc;
+  if (fixp->fx_subsy && fixp->fx_addsy)
+    {
+      relocs[1] = reloc = XNEW (arelent);
+      relocs[2] = NULL;
+
+      reloc->sym_ptr_ptr = XNEW (asymbol *);
+      *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_subsy);
+      reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
+
+      reloc->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_PPC_NEG);
+      reloc->addend = fixp->fx_addnumber;
+
+      if (reloc->howto == (reloc_howto_type *) NULL)
+        {
+          as_bad_where (fixp->fx_file, fixp->fx_line,
+            _("reloc %d not supported by object file format"),
+            BFD_RELOC_PPC_NEG);
+         relocs[0] = NULL;
+        }
+    }
+
+
+  return relocs;
 }
 
 void