]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/tc-ppc.c
[gdb/python] Fix GDB_PY_{LL,LLU}_ARG on platform without long long
[thirdparty/binutils-gdb.git] / gas / config / tc-ppc.c
index 5511e722108ba07995af3fcf40c2367e5bc069e6..8c00d01f6f289226b459ad1a48ecabc12efa291c 100644 (file)
@@ -1,5 +1,5 @@
 /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000)
-   Copyright (C) 1994-2021 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.
@@ -85,7 +85,6 @@ static int set_target_endian = 0;
 
 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)
@@ -107,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);
@@ -118,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
@@ -227,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 },
@@ -240,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
@@ -268,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;
   };
@@ -342,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 },
@@ -774,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.  */
 
@@ -804,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.
- */
+/* Called for a non-symbol, non-number operand.  Handles %reg.  */
 
-static bool
-register_name (expressionS *expressionP)
+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 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 },
@@ -881,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;
+  const struct pd_reg *reg = NULL;
 
-  if (! cr_operand)
-    return 0;
-
-  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
@@ -930,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)
@@ -944,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"));
 
@@ -965,12 +980,13 @@ ppc_optimize_expr (expressionS *left, operatorT op, expressionS *right)
 /* Whether to target xcoff64/elf64.  */
 static unsigned int ppc_obj64 = BFD_DEFAULT_TARGET_SIZE == 64;
 
+/* A separate obstack for use by ppc_hash, so that we can quickly
+   throw away hash table memory .  */
+struct obstack insn_obstack;
+
 /* Opcode hash table.  */
 static htab_t ppc_hash;
 
-/* Macro hash table.  */
-static htab_t ppc_macro_hash;
-
 #ifdef OBJ_ELF
 /* What type of shared library support to use.  */
 static enum { SHLIB_NONE, SHLIB_PIC, SHLIB_MRELOCATABLE } shlib = SHLIB_NONE;
@@ -1014,18 +1030,15 @@ ppc_xcoff_section_is_initialized (struct ppc_xcoff_section *section)
 
 /* Initialize a ppc_xcoff_section.
    Dummy symbols are used to ensure the position of .text over .data
-   and .tdata.  These symbols won't be output.  */
+   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,
-                       bool need_dummy)
+ppc_init_xcoff_section (struct ppc_xcoff_section *s, segT seg)
 {
   s->segment = seg;
   s->next_subsegment = 2;
-  if (need_dummy)
-    {
-      s->csects = symbol_make ("dummy\001");
-      symbol_get_tc (s->csects)->within = s->csects;
-    }
+  s->csects = symbol_make ("dummy\001");
+  symbol_get_tc (s->csects)->within = s->csects;
 }
 
 /* The current csect.  */
@@ -1379,6 +1392,12 @@ PowerPC options:\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,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)
@@ -1490,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 (startswith (default_cpu, "powerpc"))
+      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
@@ -1548,7 +1567,7 @@ ppc_target_format (void)
 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.  */
@@ -1574,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
@@ -1608,42 +1627,58 @@ insn_validate (const struct powerpc_opcode *op)
   return false;
 }
 
-/* Insert opcodes and macros into hash tables.  Called at startup and
-   for .machine pseudo.  */
+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 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;
   bool bad_insn = false;
 
   if (ppc_hash != NULL)
-    htab_delete (ppc_hash);
-  if (ppc_macro_hash != NULL)
-    htab_delete (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 = str_htab_create ();
+  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;
@@ -1671,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
@@ -1722,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
@@ -1752,97 +1791,88 @@ ppc_setup_opcodes (void)
     for (op = prefix_opcodes; op < op_end; op++)
       str_hash_insert (ppc_hash, op->name, op, 0);
 
-  op_end = vle_opcodes + vle_num_opcodes;
-  for (op = vle_opcodes; op < op_end; op++)
+  if ((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)
-         && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
+  /* 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++)
        {
-         as_bad (_("duplicate %s"), op->name);
-         bad_insn = true;
+         if (ENABLE_CHECKING)
+           {
+             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;
-                 }
                }
-
+             prev_seg = seg;
              bad_insn |= insn_validate (op);
            }
 
-         if ((ppc_cpu & op->flags) != 0
-             && !(ppc_cpu & op->deprecated)
-             && str_hash_insert (ppc_hash, op->name, op, 0) != NULL)
-           {
-             as_bad (_("duplicate %s"), op->name);
-             bad_insn = true;
-           }
+         str_hash_insert (ppc_hash, op->name, op, 0);
        }
-
-      for (op = spe2_opcodes; op < op_end; op++)
-       str_hash_insert (ppc_hash, op->name, op, 0);
     }
 
-  /* Insert the macros into a hash table.  */
-  ppc_macro_hash = str_htab_create ();
-
-  macro_end = powerpc_macros + powerpc_num_macros;
-  for (macro = powerpc_macros; macro < macro_end; macro++)
-    if (((macro->flags & ppc_cpu) != 0
-        || (ppc_cpu & PPC_OPCODE_ANY) != 0)
-       && str_hash_insert (ppc_macro_hash, macro->name, macro, 0) != NULL)
-      {
-       as_bad (_("duplicate %s"), macro->name);
-       bad_insn = true;
-      }
-
   if (bad_insn)
     abort ();
 }
@@ -1881,12 +1911,23 @@ md_begin (void)
   /* Create XCOFF sections with .text in first, as it's creating dummy symbols
      to serve as initial csects.  This forces the text csects to precede the
      data csects.  These symbols will not be output.  */
-  ppc_init_xcoff_section (&ppc_xcoff_text_section, text_section, true);
-  ppc_init_xcoff_section (&ppc_xcoff_data_section, data_section, true);
-  ppc_init_xcoff_section (&ppc_xcoff_bss_section, bss_section, false);
+  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
 }
 
+void
+ppc_md_end (void)
+{
+  if (ppc_hash)
+    {
+      htab_delete (ppc_hash);
+      _obstack_free (&insn_obstack, NULL);
+    }
+  ppc_hash = NULL;
+}
+
 void
 ppc_cleanup (void)
 {
@@ -1978,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++;
@@ -2028,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;
 }
@@ -2197,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)
              {
@@ -2238,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;
 
@@ -2258,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;
       }
 
@@ -2544,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)
     {
@@ -2903,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
 
@@ -3126,6 +3184,7 @@ fixup_size (bfd_reloc_code_real_type reloc, bool *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
@@ -3255,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];
@@ -3275,15 +3334,7 @@ md_assemble (char *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 *) str_hash_find (ppc_macro_hash,
-                                                           str);
-      if (macro == (const struct powerpc_macro *) NULL)
-       as_bad (_("unrecognized opcode: `%s'"), str);
-      else
-       ppc_macro (s, macro);
-
+      as_bad (_("unrecognized opcode: `%s'"), str);
       ppc_clear_labels ();
       return;
     }
@@ -3360,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;
@@ -3397,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);
@@ -3419,28 +3470,15 @@ md_assemble (char *str)
       /* Gather the operand.  */
       hold = input_line_pointer;
       input_line_pointer = str;
-
-      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;
-       }
-
+      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;
 
+      resolve_register (&ex);
+
       if (ex.X_op == O_illegal)
        as_bad (_("illegal operand"));
       else if (ex.X_op == O_absent)
@@ -3451,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 | PPC_OPERAND_ACC)) != 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))
@@ -3459,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:
@@ -3474,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
        {
@@ -3981,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)
@@ -4016,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_POWER10) != 0
-          && PPC_PREFIX_P (insn))
+  else if (PPC_PREFIX_P (insn))
     {
       struct insn_label_list *l;
 
@@ -4027,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
@@ -4097,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.  */
@@ -4259,6 +4237,72 @@ ppc_byte (int ignore ATTRIBUTE_UNUSED)
    to handle symbol suffixes for such symbols.  */
 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
@@ -4279,6 +4323,7 @@ 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);
@@ -4315,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
@@ -4378,8 +4436,7 @@ ppc_comm (int lcomm)
       section = &ppc_xcoff_tbss_section;
       if (!ppc_xcoff_section_is_initialized (section))
        {
-         ppc_init_xcoff_section (section,
-                                 subseg_new (".tbss", 0), false);
+         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;
@@ -4438,6 +4495,14 @@ ppc_comm (int lcomm)
       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 ();
@@ -4536,8 +4601,7 @@ ppc_change_csect (symbolS *sym, offsetT align)
          /* Create .tdata section if not yet done.  */
          if (!ppc_xcoff_section_is_initialized (section))
            {
-             ppc_init_xcoff_section (section, subseg_new (".tdata", 0),
-                                     true);
+             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);
@@ -4548,8 +4612,7 @@ ppc_change_csect (symbolS *sym, offsetT align)
          /* Create .tbss section if not yet done.  */
          if (!ppc_xcoff_section_is_initialized (section))
            {
-             ppc_init_xcoff_section (section, subseg_new (".tbss", 0),
-                                     false);
+             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;
@@ -4611,7 +4674,7 @@ 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);
+  sec = subseg_new (dw->xcoff_name, subseg);
   oldflags = bfd_section_flags (sec);
   if (oldflags == SEC_NO_FLAGS)
     {
@@ -4701,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)
         {
@@ -4819,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 ();
 }
@@ -5039,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;
         }
     }
 
@@ -5058,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
@@ -5132,7 +5347,9 @@ 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,
@@ -5499,7 +5716,7 @@ ppc_vbyte (int dummy ATTRIBUTE_UNUSED)
 }
 
 void
-ppc_xcoff_end (void)
+ppc_xcoff_md_finish (void)
 {
   int i;
 
@@ -5661,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++)
@@ -5684,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 ();
@@ -5852,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
@@ -5891,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)
        {
@@ -5900,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)
@@ -5934,40 +6198,47 @@ 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 (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
               || 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;
+         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))
@@ -5980,15 +6251,15 @@ 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;
+         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 (ppc_is_toc_sym (sym))
        {
@@ -6003,19 +6274,19 @@ ppc_frob_symbol (symbolS *sym)
              || (!ppc_is_toc_sym (next)))
            {
              if (ppc_after_toc_frag == (fragS *) NULL)
-               a->x_csect.x_scnlen.l = (bfd_section_size (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
        {
@@ -6038,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
            {
@@ -6051,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;
@@ -6114,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;
@@ -6129,7 +6429,7 @@ ppc_adjust_symtab (void)
     {
       symbolS *csect;
       int i;
-      union internal_auxent *a;
+      combined_entry_type *a;
 
       if (S_GET_SEGMENT (sym) != absolute_section)
        continue;
@@ -6140,21 +6440,22 @@ ppc_adjust_symtab (void)
       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;
@@ -6252,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
@@ -6262,7 +6602,7 @@ 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;
@@ -6304,38 +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
-      && 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)))
+  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;
 
@@ -6378,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:
@@ -6388,12 +6712,67 @@ 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)
@@ -6432,6 +6811,7 @@ 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);
@@ -6740,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
@@ -7256,22 +7636,25 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
        case BFD_RELOC_PPC64_TLSM:
          gas_assert (fixP->fx_addsy != NULL);
          S_SET_THREAD_LOCAL (fixP->fx_addsy);
-         fieldval = 0;
          break;
 
-         /* TLSML relocations are targeting a XMC_TC symbol named
-            "_$TLSML". We can't check earlier because the relocation
-            can target any symbol name which will be latter .rename
-            to "_$TLSML".  */
+         /* 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 (strcmp (symbol_get_tc (fixP->fx_addsy)->real_name, "_$TLSML") != 0)
-           {
-             as_bad_where (fixP->fx_file, fixP->fx_line,
-                           _("R_TLSML relocation doesn't target a "
-                             "symbol named \"_$TLSML\". %s"), S_GET_NAME(fixP->fx_addsy));
-           }
+         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;
 
@@ -7331,11 +7714,9 @@ 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_r_type != BFD_RELOC_PPC_TOC16_HI
-      && fixP->fx_r_type != BFD_RELOC_PPC_TOC16_LO)
-    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)
     {
       /* We want to use the offset within the toc, not the actual VMA
         of the symbol.  */
@@ -7350,17 +7731,31 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg)
       /* Set *valP to avoid errors.  */
       *valP = value;
     }
+  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);
@@ -7374,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