]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-ppc.c
Automatic date update in version.in
[thirdparty/binutils-gdb.git] / gas / config / tc-ppc.c
index 042c95b1889f8ecaa6d19e2c09d825c48940e4f4..8c00d01f6f289226b459ad1a48ecabc12efa291c 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
-   Copyright (C) 1994-2019 Free Software Foundation, Inc.
+   Copyright (C) 1994-2024 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,9 +83,8 @@ 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);
 
 #if defined (OBJ_XCOFF) || defined (OBJ_ELF)
@@ -115,8 +106,10 @@ static void ppc_es (int);
 static void ppc_csect (int);
 static void ppc_dwsect (int);
 static void ppc_change_csect (symbolS *, offsetT);
+static void ppc_file (int);
 static void ppc_function (int);
 static void ppc_extern (int);
+static void ppc_globl (int);
 static void ppc_lglobl (int);
 static void ppc_ref (int);
 static void ppc_section (int);
@@ -126,6 +119,8 @@ static void ppc_rename (int);
 static void ppc_toc (int);
 static void ppc_xcoff_cons (int);
 static void ppc_vbyte (int);
+static void ppc_weak (int);
+static void ppc_GNU_visibility (int);
 #endif
 
 #ifdef OBJ_ELF
@@ -135,20 +130,6 @@ static void ppc_elf_localentry (int);
 static void ppc_elf_abiversion (int);
 static void ppc_elf_gnu_attribute (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);
-#endif
 \f
 /* Generic assembler global variables which must be defined by all
    targets.  */
@@ -249,7 +230,9 @@ const pseudo_typeS md_pseudo_table[] =
   { "ei",      ppc_biei,       1 },
   { "es",      ppc_es,         0 },
   { "extern",  ppc_extern,     0 },
+  { "file",    ppc_file,       0 },
   { "function",        ppc_function,   0 },
+  { "globl",    ppc_globl,     0 },
   { "lglobl",  ppc_lglobl,     0 },
   { "ref",     ppc_ref,        0 },
   { "rename",  ppc_rename,     0 },
@@ -262,6 +245,12 @@ const pseudo_typeS md_pseudo_table[] =
   { "word",    ppc_xcoff_cons, 1 },
   { "short",   ppc_xcoff_cons, 1 },
   { "vbyte",    ppc_vbyte,     0 },
+  { "weak",     ppc_weak,      0 },
+
+  /* Enable GNU syntax for symbol visibility.  */
+  {"internal",  ppc_GNU_visibility, SYM_V_INTERNAL},
+  {"hidden",    ppc_GNU_visibility, SYM_V_HIDDEN},
+  {"protected", ppc_GNU_visibility, SYM_V_PROTECTED},
 #endif
 
 #ifdef OBJ_ELF
@@ -274,22 +263,6 @@ const pseudo_typeS md_pseudo_table[] =
   { "gnu_attribute", ppc_elf_gnu_attribute, 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 },
-#endif
-
 #if defined (OBJ_XCOFF) || defined (OBJ_ELF)
   { "tc",      ppc_tc,         0 },
   { "machine",  ppc_machine,    0 },
@@ -306,7 +279,7 @@ const pseudo_typeS md_pseudo_table[] =
 /* Structure to hold information about predefined registers.  */
 struct pd_reg
   {
-    const char *name;
+    char name[6];
     unsigned short value;
     unsigned short flags;
   };
@@ -346,6 +319,16 @@ struct pd_reg
 
 static const struct pd_reg pre_defined_registers[] =
 {
+  /* 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 },
@@ -370,6 +353,16 @@ static const struct pd_reg pre_defined_registers[] =
   { "dec", 22, PPC_OPERAND_SPR },
   { "dsisr", 18, PPC_OPERAND_SPR },
 
+  /* Dense Math Registers.  */
+  { "dm0", 0, PPC_OPERAND_DMR },
+  { "dm1", 1, PPC_OPERAND_DMR },
+  { "dm2", 2, PPC_OPERAND_DMR },
+  { "dm3", 3, PPC_OPERAND_DMR },
+  { "dm4", 4, PPC_OPERAND_DMR },
+  { "dm5", 5, PPC_OPERAND_DMR },
+  { "dm6", 6, PPC_OPERAND_DMR },
+  { "dm7", 7, PPC_OPERAND_DMR },
+
   /* Floating point registers */
   { "f.0", 0, PPC_OPERAND_FPR },
   { "f.1", 1, PPC_OPERAND_FPR },
@@ -802,8 +795,6 @@ static const struct pd_reg pre_defined_registers[] =
   { "xer", 1, PPC_OPERAND_SPR }
 };
 
-#define REG_NAME_CNT   (sizeof (pre_defined_registers) / sizeof (struct pd_reg))
-
 /* Given NAME, find the register number associated with that name, return
    the integer value associated with the given name or -1 on failure.  */
 
@@ -832,76 +823,43 @@ reg_name_search (const struct pd_reg *regs, int regcount, const char *name)
   return NULL;
 }
 
-/*
- * Summary of register_name.
- *
- * in: Input_line_pointer points to 1st char of operand.
- *
- * out:        A expressionS.
- *      The operand may have been a register: in this case, X_op == O_register,
- *      X_add_number is set to the register number, and truth is returned.
- *     Input_line_pointer->(next non-blank) char after operand, or is in its
- *      original state.
- */
-
-static bfd_boolean
-register_name (expressionS *expressionP)
+/* Called for a non-symbol, non-number operand.  Handles %reg.  */
+
+void
+md_operand (expressionS *expressionP)
 {
   const struct pd_reg *reg;
   char *name;
   char *start;
   char c;
 
-  /* Find the spelling of the operand.  */
-  start = name = input_line_pointer;
-  if (name[0] == '%' && ISALPHA (name[1]))
-    name = ++input_line_pointer;
+  if (input_line_pointer[0] != '%' || !ISALPHA (input_line_pointer[1]))
+    return;
 
-  else if (!reg_names_p || !ISALPHA (name[0]))
-    return FALSE;
+  start = input_line_pointer;
+  ++input_line_pointer;
 
   c = get_symbol_name (&name);
-  reg = reg_name_search (pre_defined_registers, REG_NAME_CNT, name);
-
-  /* Put back the delimiting char.  */
+  reg = reg_name_search (pre_defined_registers,
+                        ARRAY_SIZE (pre_defined_registers), name);
   *input_line_pointer = c;
 
-  /* Look to see if it's in the register table.  */
   if (reg != NULL)
     {
       expressionP->X_op = O_register;
       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;
     }
-
-  /* Reset the line as if we had not done anything.  */
-  input_line_pointer = start;
-  return FALSE;
+  else
+    input_line_pointer = start;
 }
-\f
-/* This function is called for each symbol seen in an expression.  It
-   handles the special parsing which PowerPC assemblers are supposed
-   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[] =
+/* Extra names to recognise in a condition code.  This table is sorted.  */
+static const struct pd_reg cr_cond[] =
 {
-  { "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 },
@@ -909,29 +867,58 @@ static const struct pd_reg cr_names[] =
   { "un", 3, PPC_OPERAND_CR_BIT }
 };
 
-/* Parsing function.  This returns non-zero if it recognized an
-   expression.  */
+/* This function is called for each symbol seen in an expression.  It
+   handles the special parsing which PowerPC assemblers are supposed
+   to use for condition codes, and recognises other registers when
+   -mregnames.  */
 
-int
-ppc_parse_name (const char *name, expressionS *exp)
+void
+ppc_parse_name (const char *name, expressionS *exp, enum expr_mode mode)
 {
-  const struct pd_reg *reg;
-
-  if (! cr_operand)
-    return 0;
+  const struct pd_reg *reg = NULL;
 
-  if (*name == '%')
-    ++name;
-  reg = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0],
-                        name);
-  if (reg == NULL)
-    return 0;
+  if (cr_operand)
+    reg = reg_name_search (cr_cond, ARRAY_SIZE (cr_cond), name);
+  if (reg == NULL && (cr_operand || reg_names_p))
+    reg = reg_name_search (pre_defined_registers,
+                          ARRAY_SIZE (pre_defined_registers), name);
+  if (reg != NULL)
+    {
+      exp->X_op = O_register;
+      exp->X_add_number = reg->value;
+      exp->X_md = reg->flags;
+      return;
+    }
 
-  exp->X_op = O_register;
-  exp->X_add_number = reg->value;
-  exp->X_md = reg->flags;
+  /* The following replaces code in expr.c operand() after the
+     md_parse_name call.  There is too much difference between targets
+     in the way X_md is used to move this code into expr.c.  If you
+     do, you'll get failures on x86 due to uninitialised X_md fields,
+     failures on alpha and other targets due to creating register
+     symbols as O_constant rather than O_register, and failures on arc
+     and others due to expecting expr() to leave X_md alone.  */
+  symbolS *sym = symbol_find_or_make (name);
 
-  return 1;
+  /* If we have an absolute symbol or a reg, then we know its value
+     now.  Copy the symbol value expression to propagate X_md.  */
+  bool done = false;
+  if (mode != expr_defer
+      && !S_FORCE_RELOC (sym, 0))
+    {
+      segT segment = S_GET_SEGMENT (sym);
+      if (segment == absolute_section || segment == reg_section)
+       {
+         resolve_symbol_value (sym);
+         *exp = *symbol_get_value_expression (sym);
+         done = true;
+       }
+    }
+  if (!done)
+    {
+      exp->X_op = O_symbol;
+      exp->X_add_symbol = sym;
+      exp->X_add_number = 0;
+    }
 }
 
 /* Propagate X_md and check register expressions.  This is to support
@@ -958,9 +945,9 @@ ppc_optimize_expr (expressionS *left, operatorT op, expressionS *right)
     }
 
   /* Accept the above plus <cr bit>, and <cr bit> plus the above.  */
-  if (right->X_op == O_register
+  if (op == O_add
       && left->X_op == O_register
-      && op == O_add
+      && right->X_op == O_register
       && ((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)
@@ -972,7 +959,7 @@ ppc_optimize_expr (expressionS *left, operatorT op, expressionS *right)
     }
 
   /* Accept reg +/- constant.  */
-  if (left->X_op == O_register
+  if (left && left->X_op == O_register
       && !((op == O_add || op == O_subtract) && right->X_op == O_constant))
     as_warn (_("invalid register expression"));
 
@@ -993,11 +980,12 @@ ppc_optimize_expr (expressionS *left, operatorT op, expressionS *right)
 /* Whether to target xcoff64/elf64.  */
 static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
 
-/* Opcode hash table.  */
-static struct hash_control *ppc_hash;
+/* A separate obstack for use by ppc_hash, so that we can quickly
+   throw away hash table memory .  */
+struct obstack insn_obstack;
 
-/* Macro hash table.  */
-static struct hash_control *ppc_macro_hash;
+/* Opcode hash table.  */
+static htab_t ppc_hash;
 
 #ifdef OBJ_ELF
 /* What type of shared library support to use.  */
@@ -1008,33 +996,50 @@ 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.  Moreover, they allow all algorithms here to be sure that
+   csects isn't NULL.  These symbols won't be output.  */
+static void
+ppc_init_xcoff_section (struct ppc_xcoff_section *s, segT seg)
+{
+  s->segment = seg;
+  s->next_subsegment = 2;
+  s->csects = symbol_make ("dummy\001");
+  symbol_get_tc (s->csects)->within = s->csects;
+}
 
 /* The current csect.  */
 static symbolS *ppc_current_csect;
@@ -1085,21 +1090,6 @@ 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" */
 unsigned long *ppc_apuinfo_list;
@@ -1217,10 +1207,10 @@ md_parse_option (int c, const char *arg)
        }
 
       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
@@ -1259,13 +1249,13 @@ 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)
@@ -1327,7 +1317,7 @@ is_ppc64_target (const bfd_target *targ, void *data ATTRIBUTE_UNUSED)
     {
 #ifdef OBJ_ELF
     case bfd_target_elf_flavour:
-      return strncmp (targ->name, "elf64-powerpc", 13) == 0;
+      return startswith (targ->name, "elf64-powerpc");
 #endif
 #ifdef OBJ_XCOFF
     case bfd_target_xcoff_flavour:
@@ -1400,6 +1390,14 @@ PowerPC options:\n"));
   fprintf (stream, _("\
 -mpower9, -mpwr9        generate code for Power9 architecture\n"));
   fprintf (stream, _("\
+-mpower10, -mpwr10      generate code for Power10 architecture\n"));
+  fprintf (stream, _("\
+-mpower11, -mpwr11      generate code for Power11 architecture\n"));
+  fprintf (stream, _("\
+-mlibresoc              generate code for Libre-SOC architecture\n"));
+  fprintf (stream, _("\
+-mfuture                generate code for 'future' architecture\n"));
+  fprintf (stream, _("\
 -mcell                  generate code for Cell Broadband Engine architecture\n"));
   fprintf (stream, _("\
 -mcom                   generate code for Power/PowerPC common instructions\n"));
@@ -1479,14 +1477,14 @@ ppc_set_cpu (void)
        else
          /* The minimum supported cpu for 64-bit little-endian is power8.  */
          ppc_cpu |= ppc_parse_cpu (ppc_cpu, &sticky, "power8");
-      else if (strncmp (default_os, "aix", 3) == 0
+      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"),
@@ -1500,9 +1498,11 @@ ppc_set_cpu (void)
 enum bfd_architecture
 ppc_arch (void)
 {
-  const char *default_cpu = TARGET_CPU;
   ppc_set_cpu ();
 
+#ifdef OBJ_ELF
+  return bfd_arch_powerpc;
+#else
   if ((ppc_cpu & PPC_OPCODE_PPC) != 0)
     return bfd_arch_powerpc;
   if ((ppc_cpu & PPC_OPCODE_VLE) != 0)
@@ -1511,14 +1511,12 @@ ppc_arch (void)
     return bfd_arch_rs6000;
   if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0)
     {
-      if (strcmp (default_cpu, "rs6000") == 0)
-       return bfd_arch_rs6000;
-      else if (strncmp (default_cpu, "powerpc", 7) == 0)
+      const char *default_cpu = TARGET_CPU;
+      if (startswith (default_cpu, "powerpc"))
        return bfd_arch_powerpc;
     }
-
-  as_fatal (_("neither Power nor PowerPC opcodes were selected."));
-  return bfd_arch_unknown;
+  return bfd_arch_rs6000;
+#endif
 }
 
 unsigned long
@@ -1540,9 +1538,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
@@ -1568,27 +1564,27 @@ 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;
+  const ppc_opindex_t *o;
   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)
     {
-      bfd_boolean optional = FALSE;
+      bool optional = false;
       if (*o >= num_powerpc_operands)
         {
          as_bad (_("operand index error for %s"), op->name);
-         return TRUE;
+         return true;
         }
       else
         {
@@ -1597,16 +1593,16 @@ insn_validate (const struct powerpc_opcode *op)
          if (operand->shift == (int) PPC_OPSHIFT_INV)
            {
              const char *errmsg;
-             int64_t val;
+             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 == (int) PPC_OPSHIFT_SH6)
+           mask = (0x1f << 11) | 0x2;
          else if (operand->shift >= 0)
            mask = operand->bitm << operand->shift;
          else
@@ -1615,65 +1611,81 @@ insn_validate (const struct powerpc_opcode *op)
            {
              as_bad (_("operand %d overlap in %s"),
                      (int) (o - op->operands), op->name);
-             return TRUE;
+             return true;
            }
          omask |= mask;
          if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
-           optional = TRUE;
+           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 true;
            }
         }
     }
-  return FALSE;
+  return false;
+}
+
+static void *
+insn_calloc (size_t n, size_t size)
+{
+  size_t amt = n * size;
+  void *ret = obstack_alloc (&insn_obstack, amt);
+  memset (ret, 0, amt);
+  return ret;
 }
 
-/* Insert opcodes and macros into hash tables.  Called at startup and
-   for .machine pseudo.  */
+/* Insert opcodes into hash tables.  Called at startup and for
+   .machine pseudo.  */
 
 static void
 ppc_setup_opcodes (void)
 {
   const struct powerpc_opcode *op;
   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);
-  if (ppc_macro_hash != NULL)
-    hash_die (ppc_macro_hash);
+    {
+      htab_delete (ppc_hash);
+      _obstack_free (&insn_obstack, NULL);
+    }
+
+  obstack_begin (&insn_obstack, chunksize);
 
   /* Insert the opcodes into a hash table.  */
-  ppc_hash = hash_new ();
+  ppc_hash = htab_create_alloc (5000, hash_string_tuple, eq_string_tuple,
+                               NULL, insn_calloc, NULL);
 
   if (ENABLE_CHECKING)
     {
       unsigned int i;
 
       /* An index into powerpc_operands is stored in struct fix
-        fx_pcrel_adjust which is 8 bits wide.  */
-      gas_assert (num_powerpc_operands < 256);
+        fx_pcrel_adjust which is a 16 bit field.  */
+      gas_assert (num_powerpc_operands <= PPC_OPINDEX_MAX + 1);
 
       /* Check operand masks.  Code here and in the disassembler assumes
         all the 1's in the mask are contiguous.  */
       for (i = 0; i < num_powerpc_operands; ++i)
        {
          uint64_t mask = powerpc_operands[i].bitm;
+         unsigned long flags = powerpc_operands[i].flags;
          uint64_t right_bit;
          unsigned int j;
 
+         if ((flags & PPC_OPERAND_PLUS1) != 0
+              && (flags & PPC_OPERAND_NONZERO) != 0)
+           as_bad ("mutually exclusive operand flags");
+
          right_bit = mask & -mask;
          mask += right_bit;
          right_bit = mask & -mask;
          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],
@@ -1681,7 +1693,7 @@ ppc_setup_opcodes (void)
              {
                as_bad (_("powerpc_operands[%d] duplicates powerpc_operands[%d]"),
                        j, i);
-               bad_insn = TRUE;
+               bad_insn = true;
              }
        }
     }
@@ -1694,10 +1706,12 @@ ppc_setup_opcodes (void)
          unsigned int new_opcode = PPC_OP (op[0].opcode);
 
 #ifdef PRINT_OPCODE_TABLE
-         printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%llx\tmask: 0x%llx\tflags: 0x%llx\n",
+         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);
+                 (unsigned long long) op->mask,
+                 (unsigned long long) op->flags);
 #endif
 
          /* The major opcodes had better be sorted.  Code in the disassembler
@@ -1706,42 +1720,36 @@ ppc_setup_opcodes (void)
              && new_opcode < PPC_OP (op[-1].opcode))
            {
              as_bad (_("major opcode is not sorted for %s"), op->name);
-             bad_insn = TRUE;
+             bad_insn = true;
            }
 
          if ((op->flags & PPC_OPCODE_VLE) != 0)
            {
              as_bad (_("%s is enabled by vle flag"), op->name);
-             bad_insn = TRUE;
+             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 = 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_ANY) != 0)
     for (op = powerpc_opcodes; op < op_end; op++)
-      hash_insert (ppc_hash, op->name, (void *) 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++)
@@ -1751,10 +1759,12 @@ ppc_setup_opcodes (void)
          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",
+         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);
+                 (unsigned long long) op->mask,
+                 (unsigned long long) op->flags);
 #endif
 
          /* The major opcodes had better be sorted.  Code in the disassembler
@@ -1763,135 +1773,103 @@ ppc_setup_opcodes (void)
              && new_opcode < PPC_PREFIX_SEG (op[-1].opcode))
            {
              as_bad (_("major opcode is not sorted for %s"), op->name);
-             bad_insn = TRUE;
+             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_ANY) != 0)
     for (op = prefix_opcodes; op < op_end; op++)
-      hash_insert (ppc_hash, op->name, (void *) 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 ((ppc_cpu & (PPC_OPCODE_VLE | PPC_OPCODE_ANY)) != 0)
     {
-      if (ENABLE_CHECKING)
+      unsigned int prev_seg = 0;
+      unsigned int seg;
+
+      op_end = vle_opcodes + vle_num_opcodes;
+      for (op = vle_opcodes; op < op_end; op++)
        {
-         unsigned new_seg = VLE_OP_TO_SEG (VLE_OP (op[0].opcode, op[0].mask));
+         if (ENABLE_CHECKING)
+           {
+             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%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);
+             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) 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 (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;
+             if (seg < prev_seg)
+               {
+                 as_bad (_("major opcode is not sorted for %s"), op->name);
+                 bad_insn = true;
+               }
+             prev_seg = seg;
+             bad_insn |= insn_validate (op);
            }
 
-         bad_insn |= insn_validate (op);
+         str_hash_insert (ppc_hash, op->name, op, 0);
        }
+    }
 
-      if ((ppc_cpu & op->flags) != 0
-         && !(ppc_cpu & op->deprecated))
+  /* LSP instructions */
+  if ((ppc_cpu & (PPC_OPCODE_LSP | PPC_OPCODE_ANY)) != 0)
+    {
+      unsigned int prev_seg = 0;
+      unsigned int seg;
+      op_end = lsp_opcodes + lsp_num_opcodes;
+      for (op = lsp_opcodes; op < op_end; op++)
        {
-         const char *retval;
-
-         retval = hash_insert (ppc_hash, op->name, (void *) op);
-         if (retval != NULL)
+         if (ENABLE_CHECKING)
            {
-             as_bad (_("duplicate instruction %s"),
-                     op->name);
-             bad_insn = TRUE;
+             seg = LSP_OP_TO_SEG (op->opcode);
+             if (seg < prev_seg)
+               {
+                 as_bad (_("opcode is not sorted for %s"), op->name);
+                 bad_insn = true;
+               }
+             prev_seg = seg;
+             bad_insn |= insn_validate (op);
            }
+
+         str_hash_insert (ppc_hash, op->name, op, 0);
        }
     }
 
   /* SPE2 instructions */
-  if ((ppc_cpu & PPC_OPCODE_SPE2) == PPC_OPCODE_SPE2)
+  if ((ppc_cpu & (PPC_OPCODE_SPE2 | PPC_OPCODE_ANY)) != 0)
     {
+      unsigned int prev_seg = 0;
+      unsigned int seg;
       op_end = spe2_opcodes + spe2_num_opcodes;
       for (op = spe2_opcodes; op < op_end; op++)
        {
          if (ENABLE_CHECKING)
            {
-             if (op != spe2_opcodes)
+             seg = VLE_OP_TO_SEG (VLE_OP (op[0].opcode, op[0].mask));
+             if (seg < prev_seg)
                {
-               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 = true;
                }
-
+             prev_seg = seg;
              bad_insn |= insn_validate (op);
            }
 
-         if ((ppc_cpu & op->flags) != 0 && !(ppc_cpu & op->deprecated))
-           {
-             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;
-               }
-           }
-       }
-
-      for (op = spe2_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++)
-    {
-      if ((macro->flags & ppc_cpu) != 0 || (ppc_cpu & PPC_OPCODE_ANY) != 0)
-       {
-         const char *retval;
-
-         retval = hash_insert (ppc_macro_hash, macro->name, (void *) macro);
-         if (retval != (const char *) NULL)
-           {
-             as_bad (_("duplicate macro %s"), macro->name);
-             bad_insn = TRUE;
-           }
+         str_hash_insert (ppc_hash, op->name, op, 0);
        }
     }
 
@@ -1930,21 +1908,24 @@ 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;
+  /* 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);
+  ppc_init_xcoff_section (&ppc_xcoff_data_section, data_section);
+  ppc_init_xcoff_section (&ppc_xcoff_bss_section, bss_section);
 #endif
+}
 
-#ifdef TE_PE
-
-  ppc_current_section = text_section;
-  ppc_previous_section = 0;
-
-#endif
+void
+ppc_md_end (void)
+{
+  if (ppc_hash)
+    {
+      htab_delete (ppc_hash);
+      _obstack_free (&insn_obstack, NULL);
+    }
+  ppc_hash = NULL;
 }
 
 void
@@ -1975,9 +1956,7 @@ ppc_cleanup (void)
 
     /* Create the .PPC.EMB.apuinfo section.  */
     apuinfo_secp = subseg_new (APUINFO_SECTION_NAME, 0);
-    bfd_set_section_flags (stdoutput,
-                          apuinfo_secp,
-                          SEC_HAS_CONTENTS | SEC_READONLY);
+    bfd_set_section_flags (apuinfo_secp, SEC_HAS_CONTENTS | SEC_READONLY);
 
     p = frag_more (4);
     md_number_to_chars (p, (valueT) 8, 4);
@@ -2040,6 +2019,11 @@ ppc_insert_operand (uint64_t insn,
       max = (max >> 1) & -right;
       min = ~max & -right;
     }
+  else if ((operand->flags & PPC_OPERAND_NONZERO) != 0)
+    {
+      ++min;
+      ++max;
+    }
 
   if ((operand->flags & PPC_OPERAND_PLUS1) != 0)
     max++;
@@ -2090,10 +2074,15 @@ ppc_insert_operand (uint64_t insn,
       if (errmsg != (const char *) NULL)
        as_bad_where (file, line, "%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_NONZERO) != 0)
+       --val;
+      if (operand->shift >= 0)
+       insn |= (val & operand->bitm) << operand->shift;
+      else
+       insn |= (val & operand->bitm) >> -operand->shift;
+    }
 
   return insn;
 }
@@ -2225,10 +2214,10 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     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_TLSGD34),
-    MAP64 ("got@tlsld@pcrel",  BFD_RELOC_PPC64_GOT_TLSLD34),
-    MAP64 ("got@tprel@pcrel",  BFD_RELOC_PPC64_GOT_TPREL34),
-    MAP64 ("got@dtprel@pcrel", BFD_RELOC_PPC64_GOT_DTPREL34),
+    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),
@@ -2259,7 +2248,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)
              {
@@ -2300,14 +2289,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;
 
@@ -2320,6 +2307,10 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
            exp_p->X_add_symbol = &abs_symbol;
          }
 
+       if (reloc == BFD_RELOC_PPC64_REL24_NOTOC
+           && (ppc_cpu & PPC_OPCODE_POWER10) == 0)
+         reloc = BFD_RELOC_PPC64_REL24_P9NOTOC;
+
        return (bfd_reloc_code_real_type) reloc;
       }
 
@@ -2545,7 +2536,7 @@ ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED)
       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;
@@ -2606,7 +2597,7 @@ ppc_elf_gnu_attribute (int ignored ATTRIBUTE_UNUSED)
 
 /* Set ABI version in output file.  */
 void
-ppc_elf_end (void)
+ppc_elf_md_finish (void)
 {
   if (ppc_obj64 && ppc_abiversion != 0)
     {
@@ -2703,7 +2694,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"));
 }
 
@@ -2727,102 +2718,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
-{
-  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              */
-};
 
-static int
-parse_toc_entry (enum toc_size_qualifier *toc_kind)
+#ifdef OBJ_XCOFF
+/* Parse XCOFF relocations.  */
+static bfd_reloc_code_real_type
+ppc_xcoff_suffix (char **str_p)
 {
-  char *start;
-  char *toc_spec;
-  char c;
-  enum toc_size_qualifier t;
+  struct map_bfd {
+    const char *string;
+    unsigned int length : 8;
+    unsigned int valid32 : 1;
+    unsigned int valid64 : 1;
+    unsigned int reloc;
+  };
 
-  /* Save the input_line_pointer.  */
-  start = input_line_pointer;
+  char ident[20];
+  char *str = *str_p;
+  char *str2;
+  int ch;
+  int len;
+  const struct map_bfd *ptr;
 
-  /* Skip over the '[' , and whitespace.  */
-  ++input_line_pointer;
-  SKIP_WHITESPACE ();
+#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 }
 
-  /* Find the spelling of the operand.  */
-  c = get_symbol_name (&toc_spec);
+  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),
+  };
 
-  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)
-    {
-      t = must_be_32;
-    }
-  else if (strcmp (toc_spec, "toc64") == 0)
-    {
-      t = must_be_64;
-    }
-  else
+  if (*str++ != '@')
+    return BFD_RELOC_NONE;
+
+  for (ch = *str, str2 = ident;
+       (str2 < ident + sizeof (ident) - 1
+       && (ISALNUM (ch) || ch == '@'));
+       ch = *++str)
     {
-      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.  */
 
@@ -2831,6 +2836,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
@@ -2950,8 +2956,13 @@ ppc_frob_label (symbolS *sym)
       symbol_remove (sym, &symbol_rootP, &symbol_lastP);
       symbol_append (sym, symbol_get_tc (ppc_current_csect)->within,
                     &symbol_rootP, &symbol_lastP);
+      /* Update last csect symbol.  */
       symbol_get_tc (ppc_current_csect)->within = sym;
-      symbol_get_tc (sym)->within = ppc_current_csect;
+
+      /* Some labels like .bs are using within differently.
+         So avoid changing it, if it's already set.  */
+      if (symbol_get_tc (sym)->within == NULL)
+       symbol_get_tc (sym)->within = ppc_current_csect;
     }
 #endif
 
@@ -2977,17 +2988,17 @@ struct ppc_fixup
    pc-relative in PC_RELATIVE.  */
 
 static unsigned int
-fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
+fixup_size (bfd_reloc_code_real_type reloc, bool *pc_relative)
 {
   unsigned int size = 0;
-  bfd_boolean pcrel = FALSE;
+  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-ppc, bfd/coff-rs6000.c and bfd/coff64-rs6000.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.  */
@@ -3096,6 +3107,8 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     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:
@@ -3122,12 +3135,10 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
 #endif
     case BFD_RELOC_PPC_VLE_REL8:
       size = 2;
-      pcrel = TRUE;
+      pcrel = true;
       break;
 
-    case BFD_RELOC_16_GOT_PCREL: /* coff reloc, bad name re size.  */
     case BFD_RELOC_32:
-    case BFD_RELOC_32_GOTOFF:
     case BFD_RELOC_32_PLTOFF:
 #ifdef OBJ_XCOFF
     case BFD_RELOC_CTOR:
@@ -3146,6 +3157,10 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     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:
@@ -3169,6 +3184,7 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     case BFD_RELOC_32_PCREL:
     case BFD_RELOC_32_PLT_PCREL:
     case BFD_RELOC_PPC64_REL24_NOTOC:
+    case BFD_RELOC_PPC64_REL24_P9NOTOC:
 #ifndef OBJ_XCOFF
     case BFD_RELOC_PPC_B16:
 #endif
@@ -3180,7 +3196,7 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     case BFD_RELOC_PPC_VLE_REL15:
     case BFD_RELOC_PPC_VLE_REL24:
       size = 4;
-      pcrel = TRUE;
+      pcrel = true;
       break;
 
 #ifndef OBJ_XCOFF
@@ -3205,21 +3221,27 @@ fixup_size (bfd_reloc_code_real_type reloc, bfd_boolean *pc_relative)
     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_TLSGD34:
-    case BFD_RELOC_PPC64_GOT_TLSLD34:
-    case BFD_RELOC_PPC64_GOT_TPREL34:
-    case BFD_RELOC_PPC64_GOT_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:
     case BFD_RELOC_PPC64_PCREL28:
     case BFD_RELOC_PPC64_PCREL34:
     case BFD_RELOC_PPC64_PLT_PCREL34:
       size = 8;
-      pcrel = TRUE;
+      pcrel = true;
       break;
 
     default:
@@ -3257,7 +3279,10 @@ parse_tls_arg (char **str, const expressionS *exp, struct ppc_fixup *tls_fix)
     ++sym_name;
 
   tls_fix->reloc = BFD_RELOC_NONE;
-  if (strcasecmp (sym_name, "__tls_get_addr") == 0)
+  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;
@@ -3289,7 +3314,7 @@ md_assemble (char *str)
   char *s;
   const struct powerpc_opcode *opcode;
   uint64_t insn;
-  const unsigned char *opindex_ptr;
+  const ppc_opindex_t *opindex_ptr;
   int need_paren;
   int next_opindex;
   struct ppc_fixup fixups[MAX_INSN_FIXUPS];
@@ -3306,27 +3331,51 @@ 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);
-      if (macro == (const struct powerpc_macro *) NULL)
-       as_bad (_("unrecognized opcode: `%s'"), str);
-      else
-       ppc_macro (s, macro);
-
+      as_bad (_("unrecognized opcode: `%s'"), str);
       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;
 
+#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
+
   /* 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
@@ -3362,7 +3411,7 @@ md_assemble (char *str)
        {
          if (num_optional_operands == 0)
            {
-             const unsigned char *optr;
+             const ppc_opindex_t *optr;
              int total = 0;
              int provided = 0;
              int omitted;
@@ -3399,8 +3448,8 @@ md_assemble (char *str)
            }
          if (--num_optional_provided < 0)
            {
-             int64_t val = ppc_optional_operand_value (operand, insn, ppc_cpu,
-                                                       num_optional_provided);
+             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);
@@ -3421,142 +3470,14 @@ md_assemble (char *str)
       /* Gather the operand.  */
       hold = input_line_pointer;
       input_line_pointer = str;
+      cr_operand = ((operand->flags & PPC_OPERAND_CR_BIT) != 0
+                   || (operand->flags & PPC_OPERAND_CR_REG) != 0);
+      expression (&ex);
+      cr_operand = false;
+      str = input_line_pointer;
+      input_line_pointer = hold;
 
-#ifdef TE_PE
-      if (*input_line_pointer == '[')
-       {
-         /* 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;
-           }
-
-         /* Now get the symbol following the ']'.  */
-         expression (&ex);
-
-         switch (toc_kind)
-           {
-           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;
-           }
-
-         /* 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;
+      resolve_register (&ex);
 
       if (ex.X_op == O_illegal)
        as_bad (_("illegal operand"));
@@ -3568,7 +3489,8 @@ md_assemble (char *str)
               & ~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)) != 0
+                 | PPC_OPERAND_SPR | PPC_OPERAND_GQR | PPC_OPERAND_ACC
+                 | PPC_OPERAND_DMR)) != 0
              && !((ex.X_md & PPC_OPERAND_GPR) != 0
                   && ex.X_add_number != 0
                   && (operand->flags & PPC_OPERAND_GPR_0) != 0))
@@ -3576,14 +3498,32 @@ md_assemble (char *str)
          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:
@@ -3591,81 +3531,77 @@ 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;
                  }
                /* 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;
                  }
                /* 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
        {
@@ -3793,10 +3729,10 @@ md_assemble (char *str)
                  /* Fall through.  */
                case BFD_RELOC_PPC64_GOT_PCREL34:
                case BFD_RELOC_PPC64_PLT_PCREL34:
-               case BFD_RELOC_PPC64_GOT_TLSGD34:
-               case BFD_RELOC_PPC64_GOT_TLSLD34:
-               case BFD_RELOC_PPC64_GOT_TPREL34:
-               case BFD_RELOC_PPC64_GOT_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:
                  if (operand->bitm != 0x3ffffffffULL
                      || (operand->flags & PPC_OPERAND_NEGATIVE) != 0)
                    as_warn (_("%s unsupported on this instruction"), "@pcrel");
@@ -3838,6 +3774,9 @@ md_assemble (char *str)
                }
            }
 #endif /* OBJ_ELF */
+#ifdef OBJ_XCOFF
+         reloc = ppc_xcoff_suffix (&str);
+#endif /* OBJ_XCOFF */
 
          if (reloc != BFD_RELOC_NONE)
            ;
@@ -4095,7 +4034,7 @@ md_assemble (char *str)
          be set for VLE-only instructions or for VLE-only processors,
          however it'll remain clear for dual-mode instructions on
          dual-mode and, more importantly, standard-mode processors.  */
-      if ((ppc_cpu & opcode->flags) == PPC_OPCODE_VLE)
+      if (ppc_cpu & opcode->flags & PPC_OPCODE_VLE)
        {
          ppc_apuinfo_section_add (PPC_APUINFO_VLE, 1);
          if (elf_section_data (now_seg) != NULL)
@@ -4130,8 +4069,7 @@ md_assemble (char *str)
   insn_length = 4;
   if ((ppc_cpu & PPC_OPCODE_VLE) != 0 && PPC_OP_SE_VLE (insn))
     insn_length = 2;
-  else if ((opcode->flags & PPC_OPCODE_POWERXX) != 0
-          && PPC_PREFIX_P (insn))
+  else if (PPC_PREFIX_P (insn))
     {
       struct insn_label_list *l;
 
@@ -4141,6 +4079,11 @@ md_assemble (char *str)
         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
@@ -4162,7 +4105,8 @@ md_assemble (char *str)
 
   /* The prefix part of an 8-byte instruction always occupies the lower
      addressed word in a doubleword, regardless of endianness.  */
-  if (!target_big_endian && insn_length == 8)
+  if (insn_length == 8
+      && (sizeof (insn) > sizeof (valueT) || !target_big_endian))
     {
       md_number_to_chars (f, PPC_GET_PREFIX (insn), 4);
       md_number_to_chars (f + 4, PPC_GET_SUFFIX (insn), 4);
@@ -4184,7 +4128,7 @@ md_assemble (char *str)
       fixS *fixP;
       if (fixups[i].reloc != BFD_RELOC_NONE)
        {
-         bfd_boolean pcrel;
+         bool pcrel;
          unsigned int size = fixup_size (fixups[i].reloc, &pcrel);
          int offset = target_big_endian ? (insn_length - size) : 0;
 
@@ -4210,85 +4154,6 @@ md_assemble (char *str)
       fixP->fx_pcrel_adjust = fixups[i].opindex;
     }
 }
-
-/* Handle a macro.  Gather all the operands, transform them as
-   described by the macro, and call md_assemble recursively.  All the
-   operands are separated by commas; we don't accept parentheses
-   around operands here.  */
-
-static void
-ppc_macro (char *str, const struct powerpc_macro *macro)
-{
-  char *operands[10];
-  unsigned int count;
-  char *s;
-  unsigned int len;
-  const char *format;
-  unsigned int arg;
-  char *send;
-  char *complete;
-
-  /* Gather the users operands into the operands array.  */
-  count = 0;
-  s = str;
-  while (1)
-    {
-      if (count >= sizeof operands / sizeof operands[0])
-       break;
-      operands[count++] = s;
-      s = strchr (s, ',');
-      if (s == (char *) NULL)
-       break;
-      *s++ = '\0';
-    }
-
-  if (count != macro->operands)
-    {
-      as_bad (_("wrong number of operands"));
-      return;
-    }
-
-  /* Work out how large the string must be (the size is unbounded
-     because it includes user input).  */
-  len = 0;
-  format = macro->format;
-  while (*format != '\0')
-    {
-      if (*format != '%')
-       {
-         ++len;
-         ++format;
-       }
-      else
-       {
-         arg = strtol (format + 1, &send, 10);
-         know (send != format && arg < count);
-         len += strlen (operands[arg]);
-         format = send;
-       }
-    }
-
-  /* Put the string together.  */
-  complete = s = XNEWVEC (char, len + 1);
-  format = macro->format;
-  while (*format != '\0')
-    {
-      if (*format != '%')
-       *s++ = *format++;
-      else
-       {
-         arg = strtol (format + 1, &send, 10);
-         strcpy (s, operands[arg]);
-         s += strlen (s);
-         format = send;
-       }
-    }
-  *s = '\0';
-
-  /* Assemble the constructed instruction.  */
-  md_assemble (complete);
-  free (complete);
-}
 \f
 #ifdef OBJ_ELF
 /* For ELF, add support for SHT_ORDERED.  */
@@ -4296,7 +4161,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;
@@ -4370,12 +4235,80 @@ 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;
+
+/* Retrieve the visiblity input for pseudo-ops having ones.  */
+static unsigned short
+ppc_xcoff_get_visibility (void) {
+  SKIP_WHITESPACE();
+
+  if (startswith (input_line_pointer, "exported"))
+    {
+      input_line_pointer += 8;
+      return SYM_V_EXPORTED;
+    }
+
+  if (startswith (input_line_pointer, "hidden"))
+    {
+      input_line_pointer += 6;
+      return SYM_V_HIDDEN;
+    }
+
+  if (startswith (input_line_pointer, "internal"))
+    {
+      input_line_pointer += 8;
+      return SYM_V_INTERNAL;
+    }
+
+  if (startswith (input_line_pointer, "protected"))
+    {
+      input_line_pointer += 9;
+      return SYM_V_PROTECTED;
+    }
+
+  return 0;
+}
+
+/* Retrieve visiblity using GNU syntax.  */
+static void ppc_GNU_visibility (int visibility) {
+  int c;
+  char *name;
+  symbolS *symbolP;
+  coff_symbol_type *coffsym;
+
+  do
+    {
+      if ((name = read_symbol_name ()) == NULL)
+       break;
+      symbolP = symbol_find_or_make (name);
+      free (name);
+      coffsym = coffsymbol (symbol_get_bfdsym (symbolP));
+
+      coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+      coffsym->native->u.syment.n_type |= visibility;
+
+      c = *input_line_pointer;
+      if (c == ',')
+       {
+         input_line_pointer ++;
+
+         SKIP_WHITESPACE ();
+
+         if (*input_line_pointer == '\n')
+           c = '\n';
+       }
+    }
+  while (c == ',');
+
+  demand_empty_rest_of_line ();
+}
 
 /* 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)
@@ -4390,6 +4323,8 @@ ppc_comm (int lcomm)
   symbolS *lcomm_sym = NULL;
   symbolS *sym;
   char *pfrag;
+  unsigned short visibility = 0;
+  struct ppc_xcoff_section *section;
 
   endc = get_symbol_name (&name);
   end_name = input_line_pointer;
@@ -4425,6 +4360,19 @@ ppc_comm (int lcomm)
              as_warn (_("ignoring bad alignment"));
              align = 2;
            }
+
+         /* The fourth argument to .comm is the visibility.  */
+         if (*input_line_pointer == ',')
+           {
+             input_line_pointer++;
+             visibility = ppc_xcoff_get_visibility ();
+             if (!visibility)
+               {
+                 as_bad (_("Unknown visibility field in .comm"));
+                 ignore_rest_of_line ();
+                 return;
+               }
+           }
        }
     }
   else
@@ -4482,7 +4430,22 @@ 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));
+         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))
@@ -4503,14 +4466,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)
@@ -4526,12 +4489,20 @@ 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;
     }
 
+  if (!lcomm && visibility)
+    {
+      /* Add visibility to .comm symbol.  */
+      coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+      coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+      coffsym->native->u.syment.n_type |= visibility;
+    }
+
   subseg_set (current_seg, current_subseg);
 
   demand_empty_rest_of_line ();
@@ -4584,7 +4555,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;
@@ -4606,32 +4577,55 @@ 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));
+             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));
+             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.  */
@@ -4659,7 +4653,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)
        ;
@@ -4680,15 +4674,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;
     }
 
@@ -4770,7 +4764,7 @@ ppc_dwsect (int ignore ATTRIBUTE_UNUSED)
   else
     {
       /* Create a new dw subsection.  */
-      subseg = XNEW (struct dw_subsection);
+      subseg = XCNEW (struct dw_subsection);
 
       if (opt_label == NULL)
         {
@@ -4888,13 +4882,105 @@ static void
 ppc_extern (int ignore ATTRIBUTE_UNUSED)
 {
   char *name;
-  char endc;
+  symbolS *sym;
 
-  endc = get_symbol_name (&name);
+  if ((name = read_symbol_name ()) == NULL)
+    return;
 
-  (void) symbol_find_or_make (name);
+  sym = symbol_find_or_make (name);
+  free (name);
 
-  (void) restore_line_pointer (endc);
+  if (*input_line_pointer == ',')
+    {
+      unsigned short visibility;
+      coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+
+      input_line_pointer++;
+      visibility = ppc_xcoff_get_visibility ();
+      if (!visibility)
+       {
+         as_bad (_("Unknown visibility field in .extern"));
+         ignore_rest_of_line ();
+         return;
+       }
+
+      coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+      coffsym->native->u.syment.n_type |= visibility;
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* XCOFF semantic for .globl says that the second parameter is
+   the symbol visibility.  */
+
+static void
+ppc_globl (int ignore ATTRIBUTE_UNUSED)
+{
+  char *name;
+  symbolS *sym;
+
+  if ((name = read_symbol_name ()) == NULL)
+    return;
+
+  sym = symbol_find_or_make (name);
+  free (name);
+  S_SET_EXTERNAL (sym);
+
+  if (*input_line_pointer == ',')
+    {
+      unsigned short visibility;
+      coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+
+      input_line_pointer++;
+      visibility = ppc_xcoff_get_visibility ();
+      if (!visibility)
+       {
+         as_bad (_("Unknown visibility field in .globl"));
+         ignore_rest_of_line ();
+         return;
+       }
+
+      coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+      coffsym->native->u.syment.n_type |= visibility;
+    }
+
+  demand_empty_rest_of_line ();
+}
+
+/* XCOFF semantic for .weak says that the second parameter is
+   the symbol visibility.  */
+
+static void
+ppc_weak (int ignore ATTRIBUTE_UNUSED)
+{
+  char *name;
+  symbolS *sym;
+
+  if ((name = read_symbol_name ()) == NULL)
+    return;
+
+  sym = symbol_find_or_make (name);
+  free (name);
+  S_SET_WEAK (sym);
+
+  if (*input_line_pointer == ',')
+    {
+      unsigned short visibility;
+      coff_symbol_type *coffsym = coffsymbol (symbol_get_bfdsym (sym));
+
+      input_line_pointer++;
+      visibility = ppc_xcoff_get_visibility ();
+      if (!visibility)
+       {
+         as_bad (_("Unknown visibility field in .weak"));
+         ignore_rest_of_line ();
+         return;
+       }
+
+      coffsym->native->u.syment.n_type &= ~SYM_V_MASK;
+      coffsym->native->u.syment.n_type |= visibility;
+    }
 
   demand_empty_rest_of_line ();
 }
@@ -4948,7 +5034,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 ();
@@ -5024,9 +5110,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;
 
@@ -5108,7 +5194,6 @@ ppc_stabx (int ignore ATTRIBUTE_UNUSED)
             as_bad (_(".stabx of storage class stsym must be within .bs/.es"));
 
           symbol_get_tc (sym)->within = ppc_current_block;
-          symbol_get_tc (exp.X_add_symbol)->within = ppc_current_block;
         }
     }
 
@@ -5127,6 +5212,67 @@ ppc_stabx (int ignore ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* The .file pseudo-op. On XCOFF, .file can have several parameters
+   which are being added to the symbol table to provide additional
+   information.  */
+
+static void
+ppc_file (int ignore ATTRIBUTE_UNUSED)
+{
+  char *sfname, *s1 = NULL, *s2 = NULL, *s3 = NULL;
+  int length, auxnb = 1;
+
+  /* Some assemblers tolerate immediately following '"'.  */
+  if ((sfname = demand_copy_string (&length)) != 0)
+    {
+      coff_symbol_type *coffsym;
+      if (*input_line_pointer == ',')
+       {
+         ++input_line_pointer;
+         s1 = demand_copy_string (&length);
+         auxnb++;
+
+         if (*input_line_pointer == ',')
+           {
+             ++input_line_pointer;
+             s2 = demand_copy_string (&length);
+             auxnb++;
+
+             if (*input_line_pointer == ',')
+               {
+                 ++input_line_pointer;
+                 s3 = demand_copy_string (&length);
+                 auxnb++;
+               }
+           }
+       }
+
+      /* Use coff dot_file creation and adjust auxiliary entries.  */
+      c_dot_file_symbol (sfname);
+      S_SET_NUMBER_AUXILIARY (symbol_rootP, auxnb);
+      coffsym = coffsymbol (symbol_get_bfdsym (symbol_rootP));
+      coffsym->native[1].u.auxent.x_file.x_ftype = XFT_FN;
+
+      if (s1)
+       {
+         coffsym->native[2].u.auxent.x_file.x_ftype = XFT_CT;
+         coffsym->native[2].extrap = s1;
+       }
+      if (s2)
+       {
+         coffsym->native[3].u.auxent.x_file.x_ftype = XFT_CV;
+         coffsym->native[3].extrap = s2;
+       }
+      if (s3)
+       {
+         coffsym->native[4].u.auxent.x_file.x_ftype = XFT_CD;
+         coffsym->native[4].extrap = s3;
+       }
+
+      demand_empty_rest_of_line ();
+    }
+}
+
 /* The .function pseudo-op.  This takes several arguments.  The first
    argument seems to be the external name of the symbol.  The second
    argument seems to be the label for the start of the function.  gcc
@@ -5201,10 +5347,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);
            }
        }
@@ -5497,8 +5646,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;
@@ -5513,7 +5662,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)
        ;
@@ -5567,7 +5716,7 @@ ppc_vbyte (int dummy ATTRIBUTE_UNUSED)
 }
 
 void
-ppc_xcoff_end (void)
+ppc_xcoff_md_finish (void)
 {
   int i;
 
@@ -5661,7 +5810,21 @@ ppc_tc (int ignore ATTRIBUTE_UNUSED)
     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;
+
+    /* 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;
+
+      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;
 
     ppc_frob_label (sym);
@@ -5715,7 +5878,6 @@ ppc_machine (int ignore ATTRIBUTE_UNUSED)
   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++)
@@ -5738,10 +5900,46 @@ ppc_machine (int ignore ATTRIBUTE_UNUSED)
          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);
+       {
+         ppc_cpu_t new_cpu;
+         /* Not using the global "sticky" variable here results in
+            none of the extra functional unit command line options,
+            -many, -maltivec, -mspe, -mspe2, -mvle, -mvsx, being in
+            force after selecting a new cpu with .machine.
+            ".machine altivec" and other extra functional unit
+            options do not count as a new machine, instead they add
+            to currently selected opcodes.  */
+         ppc_cpu_t machine_sticky = 0;
+         /* Unfortunately, some versions of gcc emit a .machine
+            directive very near the start of the compiler's assembly
+            output file.  This is bad because it overrides user -Wa
+            cpu selection.  Worse, there are versions of gcc that
+            emit the *wrong* cpu, not even respecting the -mcpu given
+            to gcc.  See gcc pr101393.  And to compound the problem,
+            as of 20220222 gcc doesn't pass the correct cpu option to
+            gas on the command line.  See gcc pr59828.  Hack around
+            this by keeping sticky options for an early .machine.  */
+         asection *sec;
+         for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
+           {
+             segment_info_type *info = seg_info (sec);
+             /* Are the frags for this section perturbed from their
+                initial state?  Even .align will count here.  */
+             if (info != NULL
+                 && (info->frchainP->frch_root != info->frchainP->frch_last
+                     || info->frchainP->frch_root->fr_type != rs_fill
+                     || info->frchainP->frch_root->fr_fix != 0))
+               break;
+           }
+         new_cpu = ppc_parse_cpu (ppc_cpu,
+                                  sec == NULL ? &sticky : &machine_sticky,
+                                  cpu_string);
+         if (new_cpu != 0)
+           ppc_cpu = new_cpu;
+         else
+           as_bad (_("invalid machine `%s'"), cpu_string);
+       }
 
       if (ppc_cpu != old_cpu)
        ppc_setup_opcodes ();
@@ -5751,593 +5949,20 @@ ppc_machine (int ignore ATTRIBUTE_UNUSED)
 }
 #endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
 \f
-#ifdef TE_PE
-
-/* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format.  */
+#ifdef OBJ_XCOFF
 
-/* Set the current section.  */
-static void
-ppc_set_current_section (segT new)
-{
-  ppc_previous_section = ppc_current_section;
-  ppc_current_section = new;
-}
+/* XCOFF specific symbol and file handling.  */
 
-/* pseudo-op: .previous
-   behaviour: toggles the current section with the previous section.
-   errors:    None
-   warnings:  "No previous section"  */
+/* Canonicalize the symbol name.  We use the to force the suffix, if
+   any, to use square brackets, and to be in upper case.  */
 
-static void
-ppc_previous (int ignore ATTRIBUTE_UNUSED)
+char *
+ppc_canonicalize_symbol_name (char *name)
 {
-  if (ppc_previous_section == NULL)
-    {
-      as_warn (_("no previous section to return to, ignored."));
-      return;
-    }
+  char *s;
 
-  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 - writable
-             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 = xstrdup (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;
-           }
-       }
-    }
-
-  *p = 0;
-  symbolP = symbol_find_or_make (name);
-
-  *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 (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);
-    }
-
-  demand_empty_rest_of_line ();
-}
-
-/*
- * 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 writable
- * 'x' - section is executable
- * 's' - section is sharable
- *
- * Section Alignment:
- * '0' - align to byte boundary
- * '1' - align to halfword boundary
- * '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
-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;
-
-  c = get_symbol_name (&section_name);
-
-  name = xstrdup (section_name);
-
-  *input_line_pointer = c;
-
-  SKIP_WHITESPACE_AFTER_NAME ();
-
-  exp = 0;
-  flags = SEC_NO_FLAGS;
-
-  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;
-
-  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 writable */
-                 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;
-
-                 /* 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;
-       }
-    }
-
-  sec = subseg_new (name, (subsegT) exp);
-
-  ppc_set_current_section (sec);
-
-  if (flags != SEC_NO_FLAGS)
-    {
-      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 ()));
-    }
-
-  bfd_set_section_alignment (stdoutput, sec, align);
-}
-
-static void
-ppc_pe_function (int ignore ATTRIBUTE_UNUSED)
-{
-  char *name;
-  char endc;
-  symbolS *ext_sym;
-
-  endc = get_symbol_name (&name);
-
-  ext_sym = symbol_find_or_make (name);
-
-  (void) restore_line_pointer (endc);
-
-  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);
-
-  demand_empty_rest_of_line ();
-}
-
-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));
-
-      bfd_set_section_alignment (stdoutput, tocdata_section, 2);
-    }
-  else
-    {
-      rdata_section = subseg_new (".tocd", 0);
-    }
-
-  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
-\f
-#ifdef OBJ_XCOFF
-
-/* XCOFF specific symbol and file handling.  */
-
-/* Canonicalize the symbol name.  We use the to force the suffix, if
-   any, to use square brackets, and to be in upper case.  */
-
-char *
-ppc_canonicalize_symbol_name (char *name)
-{
-  char *s;
-
-  if (ppc_stab_symbol)
-    return name;
+  if (ppc_stab_symbol)
+    return name;
 
   for (s = name; *s != '\0' && *s != '{' && *s != '['; s++)
     ;
@@ -6436,12 +6061,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)
@@ -6457,7 +6088,7 @@ ppc_symbol_new_hook (symbolS *sym)
    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
@@ -6473,6 +6104,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
@@ -6512,8 +6144,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)
        {
@@ -6521,6 +6154,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;
+         coff_symbol_type *csect = coffsymbol (symbol_get_bfdsym (within));
+         combined_entry_type *csectaux
+           = &csect->native[S_GET_NUMBER_AUXILIARY(within)];
+
+         SA_SET_SYM_FSIZE (sym, csectaux->u.auxent.x_csect.x_scnlen.u64);
+       }
     }
   else if (S_GET_STORAGE_CLASS (sym) == C_FCN
           && strcmp (S_GET_NAME (sym), ".ef") == 0)
@@ -6555,41 +6198,50 @@ ppc_frob_symbol (symbolS *sym)
       || S_GET_STORAGE_CLASS (sym) == C_HIDEXT)
     {
       int i;
-      union internal_auxent *a;
+      combined_entry_type *a;
 
       /* Create a csect aux.  */
       i = S_GET_NUMBER_AUXILIARY (sym);
       S_SET_NUMBER_AUXILIARY (sym, i + 1);
-      a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].u.auxent;
+      a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1];
       if (symbol_get_tc (sym)->symbol_class == XMC_TC0)
        {
          /* This is the TOC table.  */
          know (strcmp (S_GET_NAME (sym), "TOC") == 0);
-         a->x_csect.x_scnlen.l = 0;
-         a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+         a->u.auxent.x_csect.x_scnlen.u64 = 0;
+         a->u.auxent.x_csect.x_smtyp = (2 << 3) | XTY_SD;
        }
       else if (symbol_get_tc (sym)->subseg != 0)
        {
          /* 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))
-                                    - S_GET_VALUE (sym));
+           a->u.auxent.x_csect.x_scnlen.u64
+             = bfd_section_size (S_GET_SEGMENT (sym)) - S_GET_VALUE (sym);
          else
            {
              resolve_symbol_value (symbol_get_tc (sym)->next);
-             a->x_csect.x_scnlen.l = (S_GET_VALUE (symbol_get_tc (sym)->next)
-                                      - S_GET_VALUE (sym));
+             a->u.auxent.x_csect.x_scnlen.u64
+               = 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
+             || symbol_get_tc (sym)->symbol_class == XMC_UL)
+           a->u.auxent.x_csect.x_smtyp
+             = (symbol_get_tc (sym)->align << 3) | XTY_CM;
+         else
+           a->u.auxent.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))
+         a->u.auxent.x_csect.x_scnlen.u64 = symbol_get_frag (sym)->fr_offset;
+         a->u.auxent.x_csect.x_smtyp
+           = (symbol_get_tc (sym)->align << 3) | XTY_CM;
+         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;
@@ -6598,18 +6250,18 @@ ppc_frob_symbol (symbolS *sym)
        {
          /* This is an absolute symbol.  The csect will be created by
             ppc_adjust_symtab.  */
-         ppc_saw_abs = TRUE;
-         a->x_csect.x_smtyp = XTY_LD;
+         ppc_saw_abs = true;
+         a->u.auxent.x_csect.x_smtyp = XTY_LD;
          if (symbol_get_tc (sym)->symbol_class == -1)
            symbol_get_tc (sym)->symbol_class = XMC_XO;
        }
       else if (! S_IS_DEFINED (sym))
        {
          /* This is an external symbol.  */
-         a->x_csect.x_scnlen.l = 0;
-         a->x_csect.x_smtyp = XTY_ER;
+         a->u.auxent.x_csect.x_scnlen.u64 = 0;
+         a->u.auxent.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;
 
@@ -6619,23 +6271,22 @@ 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)
-                                        - S_GET_VALUE (sym));
+               a->u.auxent.x_csect.x_scnlen.u64
+                 = bfd_section_size (data_section) - S_GET_VALUE (sym);
              else
-               a->x_csect.x_scnlen.l = (ppc_after_toc_frag->fr_address
-                                        - S_GET_VALUE (sym));
+               a->u.auxent.x_csect.x_scnlen.u64
+                 = ppc_after_toc_frag->fr_address - S_GET_VALUE (sym);
            }
          else
            {
              resolve_symbol_value (next);
-             a->x_csect.x_scnlen.l = (S_GET_VALUE (next)
-                                      - S_GET_VALUE (sym));
+             a->u.auxent.x_csect.x_scnlen.u64
+               = S_GET_VALUE (next) - S_GET_VALUE (sym);
            }
-         a->x_csect.x_smtyp = (2 << 3) | XTY_SD;
+         a->u.auxent.x_csect.x_smtyp = (2 << 3) | XTY_SD;
        }
       else
        {
@@ -6644,9 +6295,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 ();
 
@@ -6656,7 +6309,7 @@ ppc_frob_symbol (symbolS *sym)
          if (csect == (symbolS *) NULL)
            {
              as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym));
-             a->x_csect.x_scnlen.l = 0;
+             a->u.auxent.x_csect.x_scnlen.u64 = 0;
            }
          else
            {
@@ -6669,22 +6322,21 @@ ppc_frob_symbol (symbolS *sym)
                  csect = symbol_get_tc (csect)->next;
                }
 
-             a->x_csect.x_scnlen.p =
-               coffsymbol (symbol_get_bfdsym (csect))->native;
-             coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].fix_scnlen =
-               1;
+             a->u.auxent.x_csect.x_scnlen.p
+               = coffsymbol (symbol_get_bfdsym (csect))->native;
+             a->fix_scnlen = 1;
            }
-         a->x_csect.x_smtyp = XTY_LD;
+         a->u.auxent.x_csect.x_smtyp = XTY_LD;
        }
 
-      a->x_csect.x_parmhash = 0;
-      a->x_csect.x_snhash = 0;
+      a->u.auxent.x_csect.x_parmhash = 0;
+      a->u.auxent.x_csect.x_snhash = 0;
       if (symbol_get_tc (sym)->symbol_class == -1)
-       a->x_csect.x_smclas = XMC_PR;
+       a->u.auxent.x_csect.x_smclas = XMC_PR;
       else
-       a->x_csect.x_smclas = symbol_get_tc (sym)->symbol_class;
-      a->x_csect.x_stab = 0;
-      a->x_csect.x_snstab = 0;
+       a->u.auxent.x_csect.x_smclas = symbol_get_tc (sym)->symbol_class;
+      a->u.auxent.x_csect.x_stab = 0;
+      a->u.auxent.x_csect.x_snstab = 0;
 
       /* Don't let the COFF backend resort these symbols.  */
       symbol_get_bfdsym (sym)->flags |= BSF_NOT_AT_END;
@@ -6732,13 +6384,43 @@ ppc_frob_symbol (symbolS *sym)
   return 0;
 }
 
-/* Adjust the symbol table.  This creates csect symbols for all
-   absolute symbols.  */
+/* 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);
+
+      symbol_remove (tsym, &symbol_rootP, &symbol_lastP);
+      symbol_insert (tsym, anchorSym, &symbol_rootP, &symbol_lastP);
+    }
+
+  /* Create csect symbols for all absolute symbols.  */
 
   if (! ppc_saw_abs)
     return;
@@ -6747,35 +6429,36 @@ ppc_adjust_symtab (void)
     {
       symbolS *csect;
       int i;
-      union internal_auxent *a;
+      combined_entry_type *a;
 
       if (S_GET_SEGMENT (sym) != absolute_section)
        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);
       S_SET_NUMBER_AUXILIARY (csect, i + 1);
-      a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1].u.auxent;
-      a->x_csect.x_scnlen.l = 0;
-      a->x_csect.x_smtyp = XTY_SD;
-      a->x_csect.x_parmhash = 0;
-      a->x_csect.x_snhash = 0;
-      a->x_csect.x_smclas = XMC_XO;
-      a->x_csect.x_stab = 0;
-      a->x_csect.x_snstab = 0;
+      a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1];
+      a->u.auxent.x_csect.x_scnlen.u64 = 0;
+      a->u.auxent.x_csect.x_smtyp = XTY_SD;
+      a->u.auxent.x_csect.x_parmhash = 0;
+      a->u.auxent.x_csect.x_snhash = 0;
+      a->u.auxent.x_csect.x_smclas = XMC_XO;
+      a->u.auxent.x_csect.x_stab = 0;
+      a->u.auxent.x_csect.x_snstab = 0;
 
       symbol_insert (csect, sym, &symbol_rootP, &symbol_lastP);
 
       i = S_GET_NUMBER_AUXILIARY (sym);
-      a = &coffsymbol (symbol_get_bfdsym (sym))->native[i].u.auxent;
-      a->x_csect.x_scnlen.p = coffsymbol (symbol_get_bfdsym (csect))->native;
-      coffsymbol (symbol_get_bfdsym (sym))->native[i].fix_scnlen = 1;
+      a = &coffsymbol (symbol_get_bfdsym (sym))->native[i];
+      a->u.auxent.x_csect.x_scnlen.p
+       = coffsymbol (symbol_get_bfdsym (csect))->native;
+      a->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
@@ -6787,12 +6470,12 @@ 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 */
@@ -6823,7 +6506,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
@@ -6870,6 +6553,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
@@ -6880,13 +6602,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
@@ -6906,7 +6628,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))
            {
@@ -6921,36 +6644,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;
 
@@ -6993,8 +6704,6 @@ ppc_force_relocation (fixS *fix)
 int
 ppc_force_relocation (fixS *fix)
 {
-  /* Branch prediction relocations must force a relocation, as must
-     the vtable description relocs.  */
   switch (fix->fx_r_type)
     {
     case BFD_RELOC_PPC_B16_BRTAKEN:
@@ -7003,19 +6712,73 @@ ppc_force_relocation (fixS *fix)
     case BFD_RELOC_PPC_BA16_BRNTAKEN:
     case BFD_RELOC_24_PLT_PCREL:
     case BFD_RELOC_PPC64_TOC:
+    case BFD_RELOC_16_GOTOFF:
+    case BFD_RELOC_LO16_GOTOFF:
+    case BFD_RELOC_HI16_GOTOFF:
+    case BFD_RELOC_HI16_S_GOTOFF:
+    case BFD_RELOC_LO16_PLTOFF:
+    case BFD_RELOC_HI16_PLTOFF:
+    case BFD_RELOC_HI16_S_PLTOFF:
+    case BFD_RELOC_GPREL16:
+    case BFD_RELOC_16_BASEREL:
+    case BFD_RELOC_LO16_BASEREL:
+    case BFD_RELOC_HI16_BASEREL:
+    case BFD_RELOC_HI16_S_BASEREL:
+    case BFD_RELOC_PPC_TOC16:
+    case BFD_RELOC_PPC64_TOC16_LO:
+    case BFD_RELOC_PPC64_TOC16_HI:
+    case BFD_RELOC_PPC64_TOC16_HA:
+    case BFD_RELOC_PPC64_PLTGOT16:
+    case BFD_RELOC_PPC64_PLTGOT16_LO:
+    case BFD_RELOC_PPC64_PLTGOT16_HI:
+    case BFD_RELOC_PPC64_PLTGOT16_HA:
+    case BFD_RELOC_PPC64_GOT16_DS:
+    case BFD_RELOC_PPC64_GOT16_LO_DS:
+    case BFD_RELOC_PPC64_PLT16_LO_DS:
+    case BFD_RELOC_PPC64_SECTOFF_DS:
+    case BFD_RELOC_PPC64_SECTOFF_LO_DS:
+    case BFD_RELOC_PPC64_TOC16_DS:
+    case BFD_RELOC_PPC64_TOC16_LO_DS:
+    case BFD_RELOC_PPC64_PLTGOT16_DS:
+    case BFD_RELOC_PPC64_PLTGOT16_LO_DS:
+    case BFD_RELOC_PPC_EMB_NADDR16:
+    case BFD_RELOC_PPC_EMB_NADDR16_LO:
+    case BFD_RELOC_PPC_EMB_NADDR16_HI:
+    case BFD_RELOC_PPC_EMB_NADDR16_HA:
+    case BFD_RELOC_PPC_EMB_SDAI16:
+    case BFD_RELOC_PPC_EMB_SDA2I16:
+    case BFD_RELOC_PPC_EMB_SDA2REL:
+    case BFD_RELOC_PPC_EMB_SDA21:
+    case BFD_RELOC_PPC_EMB_MRKREF:
+    case BFD_RELOC_PPC_EMB_RELSEC16:
+    case BFD_RELOC_PPC_EMB_RELST_LO:
+    case BFD_RELOC_PPC_EMB_RELST_HI:
+    case BFD_RELOC_PPC_EMB_RELST_HA:
+    case BFD_RELOC_PPC_EMB_BIT_FLD:
+    case BFD_RELOC_PPC_EMB_RELSDA:
+    case BFD_RELOC_PPC_VLE_SDA21:
+    case BFD_RELOC_PPC_VLE_SDA21_LO:
+    case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+    case BFD_RELOC_PPC_VLE_SDAREL_LO16D:
+    case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+    case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
+    case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+    case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+    case BFD_RELOC_PPC64_PLT_PCREL34:
+    case BFD_RELOC_PPC64_GOT_PCREL34:
       return 1;
     case BFD_RELOC_PPC_B26:
     case BFD_RELOC_PPC_BA26:
     case BFD_RELOC_PPC_B16:
     case BFD_RELOC_PPC_BA16:
     case BFD_RELOC_PPC64_REL24_NOTOC:
+    case BFD_RELOC_PPC64_REL24_P9NOTOC:
       /* 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;
@@ -7048,11 +6811,11 @@ ppc_fix_adjustable (fixS *fix)
     case BFD_RELOC_PPC_BA16_BRTAKEN:
     case BFD_RELOC_PPC_BA16_BRNTAKEN:
     case BFD_RELOC_PPC64_REL24_NOTOC:
+    case BFD_RELOC_PPC64_REL24_P9NOTOC:
       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;
@@ -7068,8 +6831,6 @@ 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_16_GOT_PCREL
-         && fix->fx_r_type != BFD_RELOC_32_GOTOFF
          && 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
@@ -7359,7 +7120,7 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
   if (fixP->fx_pcrel_adjust != 0)
     {
       /* This is a fixup on an instruction.  */
-      int opindex = fixP->fx_pcrel_adjust & 0xff;
+      ppc_opindex_t opindex = fixP->fx_pcrel_adjust & PPC_OPINDEX_MAX;
 
       operand = &powerpc_operands[opindex];
 #ifdef OBJ_XCOFF
@@ -7373,8 +7134,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;
@@ -7388,7 +7148,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
     }
 
@@ -7528,10 +7288,10 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
        case BFD_RELOC_PPC64_TPREL34:
        case BFD_RELOC_PPC64_DTPREL34:
-       case BFD_RELOC_PPC64_GOT_TLSGD34:
-       case BFD_RELOC_PPC64_GOT_TLSLD34:
-       case BFD_RELOC_PPC64_GOT_TPREL34:
-       case BFD_RELOC_PPC64_GOT_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;
@@ -7841,6 +7601,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:
@@ -7862,6 +7624,40 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
 #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);
+         break;
+
+         /* Officially, R_TLSML relocations must be from a TOC entry
+            targeting itself. In practice, this TOC entry is always
+            named (or .rename) "_$TLSML".
+            Thus, as it doesn't seem possible to retrieve the symbol
+            being relocated here, we simply check that the symbol
+            targeted by R_TLSML is indeed a TOC entry named "_$TLSML".
+            FIXME: Find a way to correctly check R_TLSML relocations
+            as described above.  */
+       case BFD_RELOC_PPC_TLSML:
+       case BFD_RELOC_PPC64_TLSML:
+         gas_assert (fixP->fx_addsy != NULL);
+         if ((symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TC
+              || symbol_get_tc (fixP->fx_addsy)->symbol_class != XMC_TE)
+             && 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 "
+                           "TOC entry named \"_$TLSML\": %s"), S_GET_NAME(fixP->fx_addsy));
+         fieldval = 0;
+         break;
+
        case BFD_RELOC_NONE:
 #endif
        case BFD_RELOC_CTOR:
@@ -7918,33 +7714,48 @@ 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
+          || fixP->fx_r_type == BFD_RELOC_PPC_TLSML
+          || fixP->fx_r_type == BFD_RELOC_PPC64_TLSML)
+    /* 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)
+                         - fieldval);
+  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 = XNEW (arelent);
+  relocs[0] = reloc = XNEW (arelent);
+  relocs[1] = NULL;
 
   reloc->sym_ptr_ptr = XNEW (asymbol *);
   *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
@@ -7958,11 +7769,35 @@ tc_gen_reloc (asection *seg ATTRIBUTE_UNUSED, fixS *fixp)
       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 != NULL)
+    {
+      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_subtract (fixp);
+         free (relocs[1]->sym_ptr_ptr);
+         free (relocs[1]);
+         free (relocs[0]->sym_ptr_ptr);
+         free (relocs[0]);
+         relocs[0] = NULL;
+        }
+    }
+
+
+  return relocs;
 }
 
 void