Support R_PPC64_ADDR64_LOCAL
[external/binutils.git] / gas / config / tc-ppc.c
index a764168..838f522 100644 (file)
@@ -29,6 +29,7 @@
 
 #ifdef OBJ_ELF
 #include "elf/ppc.h"
+#include "elf/ppc64.h"
 #include "dwarf2dbg.h"
 #endif
 
@@ -86,7 +87,11 @@ static int set_target_endian = 0;
    compensating for #lo being treated as a signed number.  */
 #define PPC_HIGHESTA(v) PPC_HIGHEST ((v) + 0x8000)
 
-#define SEX16(val) ((((val) & 0xffff) ^ 0x8000) - 0x8000)
+#define SEX16(val) (((val) ^ 0x8000) - 0x8000)
+
+/* For the time being on ppc64, don't report overflow on @h and @ha
+   applied to constants.  */
+#define REPORT_OVERFLOW_HI 0
 
 static bfd_boolean reg_names_p = TARGET_REG_NAMES_P;
 
@@ -129,6 +134,8 @@ static void ppc_vbyte (int);
 static void ppc_elf_cons (int);
 static void ppc_elf_rdata (int);
 static void ppc_elf_lcomm (int);
+static void ppc_elf_localentry (int);
+static void ppc_elf_abiversion (int);
 #endif
 
 #ifdef TE_PE
@@ -199,6 +206,9 @@ unsigned long nop_limit = 4;
 ppc_cpu_t ppc_cpu = 0;
 ppc_cpu_t sticky = 0;
 
+/* Value for ELF e_flags EF_PPC64_ABI.  */
+unsigned int ppc_abiversion = 0;
+
 /* Flags set on encountering toc relocs.  */
 enum {
   has_large_toc_reloc = 1,
@@ -257,6 +267,8 @@ const pseudo_typeS md_pseudo_table[] =
   { "rdata",   ppc_elf_rdata,  0 },
   { "rodata",  ppc_elf_rdata,  0 },
   { "lcomm",   ppc_elf_lcomm,  0 },
+  { "localentry", ppc_elf_localentry,  0 },
+  { "abiversion", ppc_elf_abiversion,  0 },
 #endif
 
 #ifdef TE_PE
@@ -1526,10 +1538,10 @@ ppc_setup_opcodes (void)
              int new_opcode = PPC_OP (op[0].opcode);
 
 #ifdef PRINT_OPCODE_TABLE
-             printf ("%-14s\t#%04d\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
-                     op->name, op - powerpc_opcodes, (unsigned int) new_opcode,
-                     (unsigned int) op->opcode, (unsigned int) op->mask,
-                     (unsigned long long) op->flags);
+             printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+                     op->name, (unsigned int) (op - powerpc_opcodes),
+                     (unsigned int) new_opcode, (unsigned int) op->opcode,
+                     (unsigned int) op->mask, (unsigned long long) op->flags);
 #endif
 
              /* The major opcodes had better be sorted.  Code in the
@@ -1579,10 +1591,10 @@ ppc_setup_opcodes (void)
              new_seg = VLE_OP_TO_SEG (new_seg);
 
 #ifdef PRINT_OPCODE_TABLE
-             printf ("%-14s\t#%04d\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
-                     op->name, op - powerpc_opcodes, (unsigned int) new_opcode,
-                     (unsigned int) op->opcode, (unsigned int) op->mask,
-                     (unsigned long long) op->flags);
+             printf ("%-14s\t#%04u\tmajor op: 0x%x\top: 0x%x\tmask: 0x%x\tflags: 0x%llx\n",
+                     op->name, (unsigned int) (op - powerpc_opcodes),
+                     (unsigned int) new_seg, (unsigned int) op->opcode,
+                     (unsigned int) op->mask, (unsigned long long) op->flags);
 #endif
              /* The major opcodes had better be sorted.  Code in the
                 disassembler assumes the insns are sorted according to
@@ -1923,6 +1935,8 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP32 ("bitfld",           BFD_RELOC_PPC_EMB_BIT_FLD),
     MAP32 ("relsda",           BFD_RELOC_PPC_EMB_RELSDA),
     MAP32 ("xgot",             BFD_RELOC_PPC_TOC16),
+    MAP64 ("high",             BFD_RELOC_PPC64_ADDR16_HIGH),
+    MAP64 ("higha",            BFD_RELOC_PPC64_ADDR16_HIGHA),
     MAP64 ("higher",           BFD_RELOC_PPC64_HIGHER),
     MAP64 ("highera",          BFD_RELOC_PPC64_HIGHER_S),
     MAP64 ("highest",          BFD_RELOC_PPC64_HIGHEST),
@@ -1932,10 +1946,15 @@ ppc_elf_suffix (char **str_p, expressionS *exp_p)
     MAP64 ("toc@l",            BFD_RELOC_PPC64_TOC16_LO),
     MAP64 ("toc@h",            BFD_RELOC_PPC64_TOC16_HI),
     MAP64 ("toc@ha",           BFD_RELOC_PPC64_TOC16_HA),
+    MAP64 ("dtprel@high",      BFD_RELOC_PPC64_DTPREL16_HIGH),
+    MAP64 ("dtprel@higha",     BFD_RELOC_PPC64_DTPREL16_HIGHA),
     MAP64 ("dtprel@higher",    BFD_RELOC_PPC64_DTPREL16_HIGHER),
     MAP64 ("dtprel@highera",   BFD_RELOC_PPC64_DTPREL16_HIGHERA),
     MAP64 ("dtprel@highest",   BFD_RELOC_PPC64_DTPREL16_HIGHEST),
     MAP64 ("dtprel@highesta",  BFD_RELOC_PPC64_DTPREL16_HIGHESTA),
+    MAP64 ("localentry",       BFD_RELOC_PPC64_ADDR64_LOCAL),
+    MAP64 ("tprel@high",       BFD_RELOC_PPC64_TPREL16_HIGH),
+    MAP64 ("tprel@higha",      BFD_RELOC_PPC64_TPREL16_HIGHA),
     MAP64 ("tprel@higher",     BFD_RELOC_PPC64_TPREL16_HIGHER),
     MAP64 ("tprel@highera",    BFD_RELOC_PPC64_TPREL16_HIGHERA),
     MAP64 ("tprel@highest",    BFD_RELOC_PPC64_TPREL16_HIGHEST),
@@ -2050,8 +2069,7 @@ ppc_elf_cons (int nbytes /* 1=.byte, 2=.word, 4=.long, 8=.llong */)
   do
     {
       expression (&exp);
-      if (exp.X_op == O_symbol
-         && *input_line_pointer == '@'
+      if (*input_line_pointer == '@'
          && (reloc = ppc_elf_suffix (&input_line_pointer,
                                      &exp)) != BFD_RELOC_UNUSED)
        {
@@ -2196,7 +2214,7 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
     align2 = 0;
 
   record_alignment (bss_section, align2);
-  subseg_set (bss_section, 0);
+  subseg_set (bss_section, 1);
   if (align2)
     frag_align (align2, 0, 0);
   if (S_GET_SEGMENT (symbolP) == bss_section)
@@ -2211,6 +2229,101 @@ ppc_elf_lcomm (int xxx ATTRIBUTE_UNUSED)
   demand_empty_rest_of_line ();
 }
 
+/* Pseudo op to set symbol local entry point.  */
+static void
+ppc_elf_localentry (int ignore ATTRIBUTE_UNUSED)
+{
+  char *name = input_line_pointer;
+  char c = get_symbol_end ();
+  char *p;
+  expressionS exp;
+  symbolS *sym;
+  asymbol *bfdsym;
+  elf_symbol_type *elfsym;
+
+  p = input_line_pointer;
+  *p = c;
+  SKIP_WHITESPACE ();
+  if (*input_line_pointer != ',')
+    {
+      *p = 0;
+      as_bad (_("expected comma after name `%s' in .localentry directive"),
+             name);
+      *p = c;
+      ignore_rest_of_line ();
+      return;
+    }
+  input_line_pointer++;
+  expression (&exp);
+  if (exp.X_op == O_absent)
+    {
+      as_bad (_("missing expression in .localentry directive"));
+      exp.X_op = O_constant;
+      exp.X_add_number = 0;
+    }
+  *p = 0;
+  sym = symbol_find_or_make (name);
+  *p = c;
+
+  if (resolve_expression (&exp)
+      && exp.X_op == O_constant)
+    {
+      unsigned char encoded = PPC64_SET_LOCAL_ENTRY_OFFSET (exp.X_add_number);
+
+      if (exp.X_add_number != (offsetT) PPC64_LOCAL_ENTRY_OFFSET (encoded))
+        as_bad (_(".localentry expression for `%s' "
+                 "is not a valid power of 2"), S_GET_NAME (sym));
+      else
+       {
+         bfdsym = symbol_get_bfdsym (sym);
+         elfsym = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         gas_assert (elfsym);
+         elfsym->internal_elf_sym.st_other &= ~STO_PPC64_LOCAL_MASK;
+         elfsym->internal_elf_sym.st_other |= encoded;
+         if (ppc_abiversion == 0)
+           ppc_abiversion = 2;
+       }
+    }
+  else
+    as_bad (_(".localentry expression for `%s' "
+             "does not evaluate to a constant"), S_GET_NAME (sym));
+
+  demand_empty_rest_of_line ();
+}
+
+/* Pseudo op to set ABI version.  */
+static void
+ppc_elf_abiversion (int ignore ATTRIBUTE_UNUSED)
+{
+  expressionS exp;
+
+  expression (&exp);
+  if (exp.X_op == O_absent)
+    {
+      as_bad (_("missing expression in .abiversion directive"));
+      exp.X_op = O_constant;
+      exp.X_add_number = 0;
+    }
+
+  if (resolve_expression (&exp)
+      && exp.X_op == O_constant)
+    ppc_abiversion = exp.X_add_number;
+  else
+    as_bad (_(".abiversion expression does not evaluate to a constant"));
+  demand_empty_rest_of_line ();
+}
+
+/* Set ABI version in output file.  */
+void
+ppc_elf_end (void)
+{
+  if (ppc_obj64 && ppc_abiversion != 0)
+    {
+      elf_elfheader (stdoutput)->e_flags &= ~EF_PPC64_ABI;
+      elf_elfheader (stdoutput)->e_flags |= ppc_abiversion & EF_PPC64_ABI;
+    }
+}
+
 /* Validate any relocations emitted for -mrelocatable, possibly adding
    fixups for word relocations in writable segments, so we can adjust
    them at runtime.  */
@@ -2318,8 +2431,6 @@ ppc_elf_adjust_symtab (void)
          asymbol *bsym = symbol_get_bfdsym (symp);
          if ((bsym->flags & BSF_KEEP) == 0)
            symbol_remove (symp, &symbol_rootP, &symbol_lastP);
-         else
-           S_SET_WEAK (symp);
        }
     }
 }
@@ -2421,25 +2532,16 @@ parse_toc_entry (enum toc_size_qualifier *toc_kind)
 }
 #endif
 
-#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
+#ifdef OBJ_XCOFF
 /* See whether a symbol is in the TOC section.  */
 
 static int
 ppc_is_toc_sym (symbolS *sym)
 {
-#ifdef OBJ_XCOFF
   return (symbol_get_tc (sym)->symbol_class == XMC_TC
          || symbol_get_tc (sym)->symbol_class == XMC_TC0);
-#endif
-#ifdef OBJ_ELF
-  const char *sname = segment_name (S_GET_SEGMENT (sym));
-  if (ppc_obj64)
-    return strcmp (sname, ".toc") == 0;
-  else
-    return strcmp (sname, ".got") == 0;
-#endif
 }
-#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
+#endif
 \f
 
 #ifdef OBJ_ELF
@@ -2813,55 +2915,76 @@ md_assemble (char *str)
                break;
 
              case BFD_RELOC_LO16:
-               /* X_unsigned is the default, so if the user has done
-                  something which cleared it, we always produce a
-                  signed value.  */
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number &= 0xffff;
-               else
+               ex.X_add_number &= 0xffff;
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
                  ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_HI16:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HI (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HI (ex.X_add_number));
+               if (REPORT_OVERFLOW_HI && ppc_obj64)
+                 {
+                   /* PowerPC64 @h is tested for overflow.  */
+                   ex.X_add_number = (addressT) ex.X_add_number >> 16;
+                   if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                     {
+                       addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
+                       ex.X_add_number
+                         = ((addressT) ex.X_add_number ^ sign) - sign;
+                     }
+                   break;
+                 }
+               /* Fall thru */
+
+             case BFD_RELOC_PPC64_ADDR16_HIGH:
+               ex.X_add_number = PPC_HI (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_HI16_S:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HA (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HA (ex.X_add_number));
+               if (REPORT_OVERFLOW_HI && ppc_obj64)
+                 {
+                   /* PowerPC64 @ha is tested for overflow.  */
+                   ex.X_add_number
+                     = ((addressT) ex.X_add_number + 0x8000) >> 16;
+                   if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                     {
+                       addressT sign = (((addressT) -1 >> 16) + 1) >> 1;
+                       ex.X_add_number
+                         = ((addressT) ex.X_add_number ^ sign) - sign;
+                     }
+                   break;
+                 }
+               /* Fall thru */
+
+             case BFD_RELOC_PPC64_ADDR16_HIGHA:
+               ex.X_add_number = PPC_HA (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHER:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHER (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHER (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHER (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHER_S:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHERA (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHERA (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHEST:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHEST (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHEST (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
 
              case BFD_RELOC_PPC64_HIGHEST_S:
-               if (ex.X_unsigned && ! (operand->flags & PPC_OPERAND_SIGNED))
-                 ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
-               else
-                 ex.X_add_number = SEX16 (PPC_HIGHESTA (ex.X_add_number));
+               ex.X_add_number = PPC_HIGHESTA (ex.X_add_number);
+               if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
+                 ex.X_add_number = SEX16 (ex.X_add_number);
                break;
              }
 #endif /* OBJ_ELF */
@@ -3011,103 +3134,6 @@ md_assemble (char *str)
                      break;
                    }
                }
-
-             /* For the absolute forms of branches, convert the PC
-                relative form back into the absolute.  */
-             if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
-               {
-                 switch (reloc)
-                   {
-                   case BFD_RELOC_PPC_B26:
-                     reloc = BFD_RELOC_PPC_BA26;
-                     break;
-                   case BFD_RELOC_PPC_B16:
-                     reloc = BFD_RELOC_PPC_BA16;
-                     break;
-                   case BFD_RELOC_PPC_B16_BRTAKEN:
-                     reloc = BFD_RELOC_PPC_BA16_BRTAKEN;
-                     break;
-                   case BFD_RELOC_PPC_B16_BRNTAKEN:
-                     reloc = BFD_RELOC_PPC_BA16_BRNTAKEN;
-                     break;
-                   default:
-                     break;
-                   }
-               }
-
-             switch (reloc)
-               {
-               case BFD_RELOC_PPC_TOC16:
-                 toc_reloc_types |= has_small_toc_reloc;
-                 break;
-               case BFD_RELOC_PPC64_TOC16_LO:
-               case BFD_RELOC_PPC64_TOC16_HI:
-               case BFD_RELOC_PPC64_TOC16_HA:
-                 toc_reloc_types |= has_large_toc_reloc;
-                 break;
-               default:
-                 break;
-               }
-
-             if ((operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
-               {
-                 switch (reloc)
-                   {
-                   case BFD_RELOC_16:
-                     reloc = BFD_RELOC_PPC64_ADDR16_DS;
-                     break;
-                   case BFD_RELOC_LO16:
-                     reloc = BFD_RELOC_PPC64_ADDR16_LO_DS;
-                     break;
-                   case BFD_RELOC_16_GOTOFF:
-                     reloc = BFD_RELOC_PPC64_GOT16_DS;
-                     break;
-                   case BFD_RELOC_LO16_GOTOFF:
-                     reloc = BFD_RELOC_PPC64_GOT16_LO_DS;
-                     break;
-                   case BFD_RELOC_LO16_PLTOFF:
-                     reloc = BFD_RELOC_PPC64_PLT16_LO_DS;
-                     break;
-                   case BFD_RELOC_16_BASEREL:
-                     reloc = BFD_RELOC_PPC64_SECTOFF_DS;
-                     break;
-                   case BFD_RELOC_LO16_BASEREL:
-                     reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_TOC16:
-                     reloc = BFD_RELOC_PPC64_TOC16_DS;
-                     break;
-                   case BFD_RELOC_PPC64_TOC16_LO:
-                     reloc = BFD_RELOC_PPC64_TOC16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC64_PLTGOT16:
-                     reloc = BFD_RELOC_PPC64_PLTGOT16_DS;
-                     break;
-                   case BFD_RELOC_PPC64_PLTGOT16_LO:
-                     reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_DTPREL16:
-                     reloc = BFD_RELOC_PPC64_DTPREL16_DS;
-                     break;
-                   case BFD_RELOC_PPC_DTPREL16_LO:
-                     reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_TPREL16:
-                     reloc = BFD_RELOC_PPC64_TPREL16_DS;
-                     break;
-                   case BFD_RELOC_PPC_TPREL16_LO:
-                     reloc = BFD_RELOC_PPC64_TPREL16_LO_DS;
-                     break;
-                   case BFD_RELOC_PPC_GOT_DTPREL16:
-                   case BFD_RELOC_PPC_GOT_DTPREL16_LO:
-                   case BFD_RELOC_PPC_GOT_TPREL16:
-                   case BFD_RELOC_PPC_GOT_TPREL16_LO:
-                     break;
-                   default:
-                     as_bad (_("unsupported relocation for DS offset field"));
-                     break;
-                   }
-               }
            }
 #endif /* OBJ_ELF */
 
@@ -3116,11 +3142,13 @@ md_assemble (char *str)
          /* Determine a BFD reloc value based on the operand information.
             We are only prepared to turn a few of the operands into
             relocs.  */
-         else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+         else if ((operand->flags & (PPC_OPERAND_RELATIVE
+                                     | PPC_OPERAND_ABSOLUTE)) != 0
                   && operand->bitm == 0x3fffffc
                   && operand->shift == 0)
            reloc = BFD_RELOC_PPC_B26;
-         else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0
+         else if ((operand->flags & (PPC_OPERAND_RELATIVE
+                                     | PPC_OPERAND_ABSOLUTE)) != 0
                   && operand->bitm == 0xfffc
                   && operand->shift == 0)
            reloc = BFD_RELOC_PPC_B16;
@@ -3136,39 +3164,120 @@ md_assemble (char *str)
                   && operand->bitm == 0x1fffffe
                   && operand->shift == 0)
            reloc = BFD_RELOC_PPC_VLE_REL24;
-         else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
-                  && operand->bitm == 0x3fffffc
-                  && operand->shift == 0)
-           reloc = BFD_RELOC_PPC_BA26;
-         else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0
-                  && operand->bitm == 0xfffc
-                  && operand->shift == 0)
-           reloc = BFD_RELOC_PPC_BA16;
-#if defined (OBJ_XCOFF) || defined (OBJ_ELF)
-         else if ((operand->flags & PPC_OPERAND_PARENS) != 0
+         else if ((operand->flags & PPC_OPERAND_NEGATIVE) == 0
                   && (operand->bitm & 0xfff0) == 0xfff0
                   && operand->shift == 0)
            {
-             if (ppc_is_toc_sym (ex.X_add_symbol))
+             reloc = BFD_RELOC_16;
+#ifdef OBJ_XCOFF
+             /* Note: the symbol may be not yet defined.  */
+             if ((operand->flags & PPC_OPERAND_PARENS) != 0
+                 && ppc_is_toc_sym (ex.X_add_symbol))
+               reloc = BFD_RELOC_PPC_TOC16;
+#endif
+           }
+
+         /* For the absolute forms of branches, convert the PC
+            relative form back into the absolute.  */
+         if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0)
+           {
+             switch (reloc)
                {
-                 reloc = BFD_RELOC_PPC_TOC16;
+               case BFD_RELOC_PPC_B26:
+                 reloc = BFD_RELOC_PPC_BA26;
+                 break;
+               case BFD_RELOC_PPC_B16:
+                 reloc = BFD_RELOC_PPC_BA16;
+                 break;
 #ifdef OBJ_ELF
-                 if (ppc_obj64
-                     && (operand->flags & PPC_OPERAND_DS) != 0)
-                   reloc = BFD_RELOC_PPC64_TOC16_DS;
+               case BFD_RELOC_PPC_B16_BRTAKEN:
+                 reloc = BFD_RELOC_PPC_BA16_BRTAKEN;
+                 break;
+               case BFD_RELOC_PPC_B16_BRNTAKEN:
+                 reloc = BFD_RELOC_PPC_BA16_BRNTAKEN;
+                 break;
 #endif
+               default:
+                 break;
                }
-             else
-               {
-                 reloc = BFD_RELOC_16;
+           }
+
 #ifdef OBJ_ELF
-                 if (ppc_obj64
-                     && (operand->flags & PPC_OPERAND_DS) != 0)
-                   reloc = BFD_RELOC_PPC64_ADDR16_DS;
-#endif
+         switch (reloc)
+           {
+           case BFD_RELOC_PPC_TOC16:
+             toc_reloc_types |= has_small_toc_reloc;
+             break;
+           case BFD_RELOC_PPC64_TOC16_LO:
+           case BFD_RELOC_PPC64_TOC16_HI:
+           case BFD_RELOC_PPC64_TOC16_HA:
+             toc_reloc_types |= has_large_toc_reloc;
+             break;
+           default:
+             break;
+           }
+
+         if (ppc_obj64
+             && (operand->flags & (PPC_OPERAND_DS | PPC_OPERAND_DQ)) != 0)
+           {
+             switch (reloc)
+               {
+               case BFD_RELOC_16:
+                 reloc = BFD_RELOC_PPC64_ADDR16_DS;
+                 break;
+               case BFD_RELOC_LO16:
+                 reloc = BFD_RELOC_PPC64_ADDR16_LO_DS;
+                 break;
+               case BFD_RELOC_16_GOTOFF:
+                 reloc = BFD_RELOC_PPC64_GOT16_DS;
+                 break;
+               case BFD_RELOC_LO16_GOTOFF:
+                 reloc = BFD_RELOC_PPC64_GOT16_LO_DS;
+                 break;
+               case BFD_RELOC_LO16_PLTOFF:
+                 reloc = BFD_RELOC_PPC64_PLT16_LO_DS;
+                 break;
+               case BFD_RELOC_16_BASEREL:
+                 reloc = BFD_RELOC_PPC64_SECTOFF_DS;
+                 break;
+               case BFD_RELOC_LO16_BASEREL:
+                 reloc = BFD_RELOC_PPC64_SECTOFF_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_TOC16:
+                 reloc = BFD_RELOC_PPC64_TOC16_DS;
+                 break;
+               case BFD_RELOC_PPC64_TOC16_LO:
+                 reloc = BFD_RELOC_PPC64_TOC16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC64_PLTGOT16:
+                 reloc = BFD_RELOC_PPC64_PLTGOT16_DS;
+                 break;
+               case BFD_RELOC_PPC64_PLTGOT16_LO:
+                 reloc = BFD_RELOC_PPC64_PLTGOT16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_DTPREL16:
+                 reloc = BFD_RELOC_PPC64_DTPREL16_DS;
+                 break;
+               case BFD_RELOC_PPC_DTPREL16_LO:
+                 reloc = BFD_RELOC_PPC64_DTPREL16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_TPREL16:
+                 reloc = BFD_RELOC_PPC64_TPREL16_DS;
+                 break;
+               case BFD_RELOC_PPC_TPREL16_LO:
+                 reloc = BFD_RELOC_PPC64_TPREL16_LO_DS;
+                 break;
+               case BFD_RELOC_PPC_GOT_DTPREL16:
+               case BFD_RELOC_PPC_GOT_DTPREL16_LO:
+               case BFD_RELOC_PPC_GOT_TPREL16:
+               case BFD_RELOC_PPC_GOT_TPREL16_LO:
+                 break;
+               default:
+                 as_bad (_("unsupported relocation for DS offset field"));
+                 break;
                }
            }
-#endif /* defined (OBJ_XCOFF) || defined (OBJ_ELF) */
+#endif
 
          /* We need to generate a fixup for this expression.  */
          if (fc >= MAX_INSN_FIXUPS)
@@ -3528,11 +3637,6 @@ ppc_comm (int lcomm)
       char *lcomm_name;
       char lcomm_endc;
 
-      if (size <= 4)
-       align = 2;
-      else
-       align = 3;
-
       /* The third argument to .lcomm appears to be the real local
         common symbol to create.  References to the symbol named in
         the first argument are turned into references to the third
@@ -3551,6 +3655,25 @@ ppc_comm (int lcomm)
       lcomm_sym = symbol_find_or_make (lcomm_name);
 
       *input_line_pointer = lcomm_endc;
+
+      /* The fourth argument to .lcomm is the alignment.  */
+      if (*input_line_pointer != ',')
+       {
+         if (size <= 4)
+           align = 2;
+         else
+           align = 3;
+       }
+      else
+       {
+         ++input_line_pointer;
+         align = get_absolute_expression ();
+         if (align <= 0)
+           {
+             as_warn (_("ignoring bad alignment"));
+             align = 2;
+           }
+       }
     }
 
   *end_name = '\0';
@@ -6147,6 +6270,22 @@ ppc_force_relocation (fixS *fix)
     case BFD_RELOC_24_PLT_PCREL:
     case BFD_RELOC_PPC64_TOC:
       return 1;
+    case BFD_RELOC_PPC_B26:
+    case BFD_RELOC_PPC_BA26:
+    case BFD_RELOC_PPC_B16:
+    case BFD_RELOC_PPC_BA16:
+      /* All branch fixups targeting a localentry symbol must
+         force a relocation.  */
+      if (fix->fx_addsy)
+       {
+         asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+         elf_symbol_type *elfsym
+           = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         gas_assert (elfsym);
+         if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+           return 1;
+       }
+      break;
     default:
       break;
     }
@@ -6161,6 +6300,32 @@ ppc_force_relocation (fixS *fix)
 int
 ppc_fix_adjustable (fixS *fix)
 {
+  switch (fix->fx_r_type)
+    {
+      /* All branch fixups targeting a localentry symbol must
+         continue using the symbol.  */
+    case BFD_RELOC_PPC_B26:
+    case BFD_RELOC_PPC_BA26:
+    case BFD_RELOC_PPC_B16:
+    case BFD_RELOC_PPC_BA16:
+    case BFD_RELOC_PPC_B16_BRTAKEN:
+    case BFD_RELOC_PPC_B16_BRNTAKEN:
+    case BFD_RELOC_PPC_BA16_BRTAKEN:
+    case BFD_RELOC_PPC_BA16_BRNTAKEN:
+      if (fix->fx_addsy)
+       {
+         asymbol *bfdsym = symbol_get_bfdsym (fix->fx_addsy);
+         elf_symbol_type *elfsym
+           = elf_symbol_from (bfd_asymbol_bfd (bfdsym), bfdsym);
+         gas_assert (elfsym);
+         if ((STO_PPC64_LOCAL_MASK & elfsym->internal_elf_sym.st_other) != 0)
+           return 0;
+       }
+      break;
+    default:
+      break;
+    }
+
   return (fix->fx_r_type != BFD_RELOC_16_GOTOFF
          && fix->fx_r_type != BFD_RELOC_LO16_GOTOFF
          && fix->fx_r_type != BFD_RELOC_HI16_GOTOFF
@@ -6287,6 +6452,8 @@ void
 md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 {
   valueT value = * valP;
+  offsetT fieldval;
+  const struct powerpc_operand *operand;
 
 #ifdef OBJ_ELF
   if (fixP->fx_addsy != NULL)
@@ -6325,16 +6492,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       as_bad_where (fixP->fx_file, fixP->fx_line, _("expression too complex"));
     }
 
+  operand = NULL;
   if (fixP->fx_pcrel_adjust != 0)
     {
-      /* Handle relocs in an insn.  */
-
+      /* This is a fixup on an instruction.  */
       int opindex = fixP->fx_pcrel_adjust & 0xff;
-      const struct powerpc_operand *operand = &powerpc_operands[opindex];
-      char *where;
-      unsigned long insn;
-      offsetT fieldval;
 
+      operand = &powerpc_operands[opindex];
 #ifdef OBJ_XCOFF
       /* An instruction like `lwz 9,sym(30)' when `sym' is not a TOC symbol
         does not generate a reloc.  It uses the offset of `sym' within its
@@ -6353,75 +6517,111 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          value = fixP->fx_offset;
          fixP->fx_done = 1;
        }
+
+       /* During parsing of instructions, a TOC16 reloc is generated for
+          instructions such as 'lwz RT,SYM(RB)' if SYM is a symbol defined
+          in the toc.  But at parse time, SYM may be not yet defined, so
+          check again here.  */
+       if (fixP->fx_r_type == BFD_RELOC_16
+           && fixP->fx_addsy != NULL
+           && ppc_is_toc_sym (fixP->fx_addsy))
+         fixP->fx_r_type = BFD_RELOC_PPC_TOC16;
 #endif
-      fieldval = value;
-      switch (fixP->fx_r_type)
-       {
+    }
+
+  /* Calculate value to be stored in field.  */
+  fieldval = value;
+  switch (fixP->fx_r_type)
+    {
 #ifdef OBJ_ELF
-       case BFD_RELOC_PPC64_ADDR16_LO_DS:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         /* fall through */
+    case BFD_RELOC_PPC64_ADDR16_LO_DS:
+    case BFD_RELOC_PPC_VLE_LO16A:
+    case BFD_RELOC_PPC_VLE_LO16D:
 #endif
-       case BFD_RELOC_LO16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
-         /* fall through */
-       case BFD_RELOC_LO16_PCREL:
-       case BFD_RELOC_PPC_VLE_LO16A:
-       case BFD_RELOC_PPC_VLE_LO16D:
-         fieldval = value & 0xffff;
-       sign_extend_16:
-         if ((operand->flags & PPC_OPERAND_SIGNED) != 0)
-           fieldval = (fieldval ^ 0x8000) - 0x8000;
-         fixP->fx_no_overflow = 1;
+    case BFD_RELOC_LO16:
+    case BFD_RELOC_LO16_PCREL:
+      fieldval = value & 0xffff;
+    sign_extend_16:
+      if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+       fieldval = SEX16 (fieldval);
+      fixP->fx_no_overflow = 1;
+      break;
+
+    case BFD_RELOC_HI16:
+    case BFD_RELOC_HI16_PCREL:
+#ifdef OBJ_ELF
+      if (REPORT_OVERFLOW_HI && ppc_obj64)
+       {
+         fieldval = value >> 16;
+         if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+           {
+             valueT sign = (((valueT) -1 >> 16) + 1) >> 1;
+             fieldval = ((valueT) fieldval ^ sign) - sign;
+           }
          break;
+       }
+      /* Fall thru */
 
-       case BFD_RELOC_HI16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
-         /* fall through */
-       case BFD_RELOC_HI16_PCREL:
-       case BFD_RELOC_PPC_VLE_HI16A:
-       case BFD_RELOC_PPC_VLE_HI16D:
-         fieldval = PPC_HI (value);
-         goto sign_extend_16;
+    case BFD_RELOC_PPC_VLE_HI16A:
+    case BFD_RELOC_PPC_VLE_HI16D:
+    case BFD_RELOC_PPC64_ADDR16_HIGH:
+#endif
+      fieldval = PPC_HI (value);
+      goto sign_extend_16;
 
-       case BFD_RELOC_HI16_S:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
-         /* fall through */
-       case BFD_RELOC_HI16_S_PCREL:
-       case BFD_RELOC_PPC_VLE_HA16A:
-       case BFD_RELOC_PPC_VLE_HA16D:
-         fieldval = PPC_HA (value);
-         goto sign_extend_16;
+    case BFD_RELOC_HI16_S:
+    case BFD_RELOC_HI16_S_PCREL:
+#ifdef OBJ_ELF
+      if (REPORT_OVERFLOW_HI && ppc_obj64)
+       {
+         fieldval = (value + 0x8000) >> 16;
+         if (operand != NULL && (operand->flags & PPC_OPERAND_SIGNED) != 0)
+           {
+             valueT sign = (((valueT) -1 >> 16) + 1) >> 1;
+             fieldval = ((valueT) fieldval ^ sign) - sign;
+           }
+         break;
+       }
+      /* Fall thru */
+
+    case BFD_RELOC_PPC_VLE_HA16A:
+    case BFD_RELOC_PPC_VLE_HA16D:
+    case BFD_RELOC_PPC64_ADDR16_HIGHA:
+#endif
+      fieldval = PPC_HA (value);
+      goto sign_extend_16;
 
 #ifdef OBJ_ELF
-       case BFD_RELOC_PPC64_HIGHER:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         fieldval = PPC_HIGHER (value);
-         goto sign_extend_16;
+    case BFD_RELOC_PPC64_HIGHER:
+      fieldval = PPC_HIGHER (value);
+      goto sign_extend_16;
 
-       case BFD_RELOC_PPC64_HIGHER_S:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         fieldval = PPC_HIGHERA (value);
-         goto sign_extend_16;
+    case BFD_RELOC_PPC64_HIGHER_S:
+      fieldval = PPC_HIGHERA (value);
+      goto sign_extend_16;
 
-       case BFD_RELOC_PPC64_HIGHEST:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         fieldval = PPC_HIGHEST (value);
-         goto sign_extend_16;
+    case BFD_RELOC_PPC64_HIGHEST:
+      fieldval = PPC_HIGHEST (value);
+      goto sign_extend_16;
 
-       case BFD_RELOC_PPC64_HIGHEST_S:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         fieldval = PPC_HIGHESTA (value);
-         goto sign_extend_16;
+    case BFD_RELOC_PPC64_HIGHEST_S:
+      fieldval = PPC_HIGHESTA (value);
+      goto sign_extend_16;
+#endif
+
+    default:
+      break;
+    }
 
+  if (operand != NULL)
+    {
+      /* Handle relocs in an insn.  */
+      char *where;
+      unsigned long insn;
+
+      switch (fixP->fx_r_type)
+       {
+#ifdef OBJ_ELF
          /* The following relocs can't be calculated by the assembler.
             Leave the field zero.  */
        case BFD_RELOC_PPC_TPREL16:
@@ -6450,10 +6650,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        case BFD_RELOC_PPC_GOT_DTPREL16_HA:
        case BFD_RELOC_PPC64_TPREL16_DS:
        case BFD_RELOC_PPC64_TPREL16_LO_DS:
+       case BFD_RELOC_PPC64_TPREL16_HIGH:
+       case BFD_RELOC_PPC64_TPREL16_HIGHA:
        case BFD_RELOC_PPC64_TPREL16_HIGHER:
        case BFD_RELOC_PPC64_TPREL16_HIGHERA:
        case BFD_RELOC_PPC64_TPREL16_HIGHEST:
        case BFD_RELOC_PPC64_TPREL16_HIGHESTA:
+       case BFD_RELOC_PPC64_DTPREL16_HIGH:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHA:
        case BFD_RELOC_PPC64_DTPREL16_DS:
        case BFD_RELOC_PPC64_DTPREL16_LO_DS:
        case BFD_RELOC_PPC64_DTPREL16_HIGHER:
@@ -6463,8 +6667,6 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          gas_assert (fixP->fx_addsy != NULL);
          S_SET_THREAD_LOCAL (fixP->fx_addsy);
          fieldval = 0;
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
          break;
 
          /* These also should leave the field zero for the same
@@ -6531,8 +6733,13 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        case BFD_RELOC_PPC_TLSGD:
        case BFD_RELOC_PPC_TLSLD:
          fieldval = 0;
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
+         break;
+#endif
+
+#ifdef OBJ_XCOFF
+       case BFD_RELOC_PPC_B16:
+         /* Adjust the offset to the instruction boundary.  */
+         fieldval += 2;
          break;
 #endif
 
@@ -6610,79 +6817,9 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
     }
   else
     {
-      int size = 0;
-      offsetT fieldval = value;
-
       /* Handle relocs in data.  */
       switch (fixP->fx_r_type)
        {
-       case BFD_RELOC_CTOR:
-         if (ppc_obj64)
-           goto ctor64;
-         /* fall through */
-
-       case BFD_RELOC_32:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_32_PCREL;
-         /* fall through */
-
-       case BFD_RELOC_32_PCREL:
-       case BFD_RELOC_RVA:
-         size = 4;
-         break;
-
-       case BFD_RELOC_64:
-       ctor64:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_64_PCREL;
-         /* fall through */
-
-       case BFD_RELOC_64_PCREL:
-         size = 8;
-         break;
-
-       case BFD_RELOC_16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_16_PCREL;
-         /* fall through */
-
-       case BFD_RELOC_16_PCREL:
-         size = 2;
-         break;
-
-       case BFD_RELOC_8:
-         if (fixP->fx_pcrel)
-           {
-#ifdef OBJ_ELF
-           bad_pcrel:
-#endif
-             if (fixP->fx_addsy)
-               {
-                 char *sfile;
-                 unsigned int sline;
-
-                 /* Use expr_symbol_where to see if this is an
-                    expression symbol.  */
-                 if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
-                   as_bad_where (fixP->fx_file, fixP->fx_line,
-                                 _("unresolved expression that must"
-                                   " be resolved"));
-                 else
-                   as_bad_where (fixP->fx_file, fixP->fx_line,
-                                 _("cannot emit PC relative %s relocation"
-                                   " against %s"),
-                                 bfd_get_reloc_code_name (fixP->fx_r_type),
-                                 S_GET_NAME (fixP->fx_addsy));
-               }
-             else
-               as_bad_where (fixP->fx_file, fixP->fx_line,
-                             _("unable to resolve expression"));
-             fixP->fx_done = 1;
-           }
-         else
-           size = 1;
-         break;
-
        case BFD_RELOC_VTABLE_INHERIT:
          if (fixP->fx_addsy
              && !S_IS_DEFINED (fixP->fx_addsy)
@@ -6697,54 +6834,18 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 #ifdef OBJ_ELF
          /* These can appear with @l etc. in data.  */
        case BFD_RELOC_LO16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
        case BFD_RELOC_LO16_PCREL:
-         size = 2;
-         break;
-
        case BFD_RELOC_HI16:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
        case BFD_RELOC_HI16_PCREL:
-         size = 2;
-         fieldval = PPC_HI (value);
-         break;
-
        case BFD_RELOC_HI16_S:
-         if (fixP->fx_pcrel)
-           fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
        case BFD_RELOC_HI16_S_PCREL:
-         size = 2;
-         fieldval = PPC_HA (value);
-         break;
-
        case BFD_RELOC_PPC64_HIGHER:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         size = 2;
-         fieldval = PPC_HIGHER (value);
-         break;
-
        case BFD_RELOC_PPC64_HIGHER_S:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         size = 2;
-         fieldval = PPC_HIGHERA (value);
-         break;
-
        case BFD_RELOC_PPC64_HIGHEST:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         size = 2;
-         fieldval = PPC_HIGHEST (value);
-         break;
-
        case BFD_RELOC_PPC64_HIGHEST_S:
-         if (fixP->fx_pcrel)
-           goto bad_pcrel;
-         size = 2;
-         fieldval = PPC_HIGHESTA (value);
+       case BFD_RELOC_PPC64_ADDR16_HIGH:
+       case BFD_RELOC_PPC64_ADDR16_HIGHA:
+       case BFD_RELOC_PPC64_ADDR64_LOCAL:
          break;
 
        case BFD_RELOC_PPC_DTPMOD:
@@ -6821,10 +6922,14 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
        case BFD_RELOC_PPC64_TOC16_LO:
        case BFD_RELOC_PPC64_TOC16_HI:
        case BFD_RELOC_PPC64_TOC16_HA:
+       case BFD_RELOC_PPC64_DTPREL16_HIGH:
+       case BFD_RELOC_PPC64_DTPREL16_HIGHA:
        case BFD_RELOC_PPC64_DTPREL16_HIGHER:
        case BFD_RELOC_PPC64_DTPREL16_HIGHERA:
        case BFD_RELOC_PPC64_DTPREL16_HIGHEST:
        case BFD_RELOC_PPC64_DTPREL16_HIGHESTA:
+       case BFD_RELOC_PPC64_TPREL16_HIGH:
+       case BFD_RELOC_PPC64_TPREL16_HIGHA:
        case BFD_RELOC_PPC64_TPREL16_HIGHER:
        case BFD_RELOC_PPC64_TPREL16_HIGHERA:
        case BFD_RELOC_PPC64_TPREL16_HIGHEST:
@@ -6835,8 +6940,17 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
 
 #ifdef OBJ_XCOFF
        case BFD_RELOC_NONE:
-         break;
 #endif
+       case BFD_RELOC_CTOR:
+       case BFD_RELOC_32:
+       case BFD_RELOC_32_PCREL:
+       case BFD_RELOC_RVA:
+       case BFD_RELOC_64:
+       case BFD_RELOC_64_PCREL:
+       case BFD_RELOC_16:
+       case BFD_RELOC_16_PCREL:
+       case BFD_RELOC_8:
+         break;
 
        default:
          fprintf (stderr,
@@ -6845,9 +6959,85 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
          abort ();
        }
 
-      if (size && APPLY_RELOC)
+      if (fixP->fx_size && APPLY_RELOC)
        md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
-                           fieldval, size);
+                           fieldval, fixP->fx_size);
+    }
+
+  /* We are only able to convert some relocs to pc-relative.  */
+  if (!fixP->fx_done && fixP->fx_pcrel)
+    {
+      switch (fixP->fx_r_type)
+       {
+       case BFD_RELOC_LO16:
+         fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
+         break;
+
+       case BFD_RELOC_HI16:
+         fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
+         break;
+
+       case BFD_RELOC_HI16_S:
+         fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
+         break;
+
+       case BFD_RELOC_64:
+         fixP->fx_r_type = BFD_RELOC_64_PCREL;
+         break;
+
+       case BFD_RELOC_32:
+         fixP->fx_r_type = BFD_RELOC_32_PCREL;
+         break;
+
+       case BFD_RELOC_16:
+         fixP->fx_r_type = BFD_RELOC_16_PCREL;
+         break;
+
+         /* Some of course are already pc-relative.  */
+       case BFD_RELOC_LO16_PCREL:
+       case BFD_RELOC_HI16_PCREL:
+       case BFD_RELOC_HI16_S_PCREL:
+       case BFD_RELOC_64_PCREL:
+       case BFD_RELOC_32_PCREL:
+       case BFD_RELOC_16_PCREL:
+       case BFD_RELOC_PPC_B16:
+       case BFD_RELOC_PPC_B16_BRTAKEN:
+       case BFD_RELOC_PPC_B16_BRNTAKEN:
+       case BFD_RELOC_PPC_B26:
+       case BFD_RELOC_PPC_LOCAL24PC:
+       case BFD_RELOC_24_PLT_PCREL:
+       case BFD_RELOC_32_PLT_PCREL:
+       case BFD_RELOC_64_PLT_PCREL:
+       case BFD_RELOC_PPC_VLE_REL8:
+       case BFD_RELOC_PPC_VLE_REL15:
+       case BFD_RELOC_PPC_VLE_REL24:
+         break;
+
+       default:
+         if (fixP->fx_addsy)
+           {
+             char *sfile;
+             unsigned int sline;
+
+             /* Use expr_symbol_where to see if this is an
+                expression symbol.  */
+             if (expr_symbol_where (fixP->fx_addsy, &sfile, &sline))
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("unresolved expression that must"
+                               " be resolved"));
+             else
+               as_bad_where (fixP->fx_file, fixP->fx_line,
+                             _("cannot emit PC relative %s relocation"
+                               " against %s"),
+                             bfd_get_reloc_code_name (fixP->fx_r_type),
+                             S_GET_NAME (fixP->fx_addsy));
+           }
+         else
+           as_bad_where (fixP->fx_file, fixP->fx_line,
+                         _("unable to resolve expression"));
+         fixP->fx_done = 1;
+         break;
+       }
     }
 
 #ifdef OBJ_ELF
@@ -6883,6 +7073,8 @@ md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED)
       fixP->fx_addnumber =
        - bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixP->fx_addsy))
        - S_GET_VALUE (ppc_toc_csect);
+      /* Set *valP to avoid errors.  */
+      *valP = value;
 #endif
     }
 #endif