Remove syntactic sugar
[deliverable/binutils-gdb.git] / bfd / elf32-ppc.c
index 5bfa33b7b5f4d9f37c031fa2e3062b0e319a9bcd..f631b271f7e10ab848831b7d0f301c9256789725 100644 (file)
@@ -1,5 +1,5 @@
 /* PowerPC-specific support for 32-bit ELF
-   Copyright (C) 1994-2014 Free Software Foundation, Inc.
+   Copyright (C) 1994-2016 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
    This file is part of BFD, the Binary File Descriptor library.
@@ -35,7 +35,6 @@
 #include "elf32-ppc.h"
 #include "elf-vxworks.h"
 #include "dwarf2.h"
-#include "elf-linux-psinfo.h"
 
 typedef enum split16_format_type
 {
@@ -50,8 +49,6 @@ static bfd_reloc_status_type ppc_elf_addr16_ha_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
 static bfd_reloc_status_type ppc_elf_unhandled_reloc
   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
-static void ppc_elf_vle_split16
-  (bfd *, bfd_byte *, bfd_vma, bfd_vma, split16_format_type);
 
 /* Branch prediction bit for branch taken relocs.  */
 #define BRANCH_PREDICT_BIT 0x200000
@@ -149,6 +146,7 @@ static const bfd_vma ppc_elf_vxworks_pic_plt0_entry
 #define ADD_3_12_2     0x7c6c1214
 #define ADD_11_0_11    0x7d605a14
 #define B              0x48000000
+#define BA             0x48000002
 #define BCL_20_31      0x429f0005
 #define BCTR           0x4e800420
 #define BEQLR          0x4d820020
@@ -188,11 +186,11 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
   /* This reloc does nothing.  */
   HOWTO (R_PPC_NONE,           /* type */
         0,                     /* rightshift */
-        2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        3,                     /* size (0 = byte, 1 = short, 2 = long) */
+        0,                     /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_NONE",          /* name */
         FALSE,                 /* partial_inplace */
@@ -207,7 +205,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_ADDR32",        /* name */
         FALSE,                 /* partial_inplace */
@@ -223,7 +221,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         26,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_signed, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_ADDR24",        /* name */
         FALSE,                 /* partial_inplace */
@@ -300,7 +298,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_signed, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_ADDR14",        /* name */
         FALSE,                 /* partial_inplace */
@@ -317,7 +315,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_signed, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_ADDR14_BRTAKEN",/* name */
         FALSE,                 /* partial_inplace */
@@ -334,7 +332,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_signed, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_ADDR14_BRNTAKEN",/* name */
         FALSE,                 /* partial_inplace */
@@ -415,7 +413,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_GOT16",         /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -431,7 +429,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_GOT16_LO",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -446,8 +444,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_GOT16_HI",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -462,8 +460,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc_elf_addr16_ha_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_GOT16_HA",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -478,8 +476,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         26,                    /* bitsize */
         TRUE,                  /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_signed,  /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_PLTREL24",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -497,8 +495,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_COPY",          /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -513,8 +511,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_GLOB_DAT",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -528,8 +526,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_JMP_SLOT",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -545,7 +543,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc,  /* special_function */
         "R_PPC_RELATIVE",      /* name */
         FALSE,                 /* partial_inplace */
@@ -577,7 +575,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_UADDR32",       /* name */
         FALSE,                 /* partial_inplace */
@@ -607,7 +605,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         TRUE,                  /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_REL32",         /* name */
         FALSE,                 /* partial_inplace */
@@ -623,8 +621,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_PLT32",         /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -639,8 +637,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         TRUE,                  /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_PLTREL32",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -656,7 +654,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_PLT16_LO",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -671,8 +669,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_PLT16_HI",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -687,8 +685,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc_elf_addr16_ha_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_PLT16_HA",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -704,7 +702,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_SDAREL16",      /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -718,8 +716,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_SECTOFF",       /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -734,7 +732,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_SECTOFF_LO",    /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -748,8 +746,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_SECTOFF_HI",    /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -763,8 +761,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        ppc_elf_addr16_ha_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_SECTOFF_HA",    /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1239,8 +1237,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_NADDR32",   /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1254,8 +1252,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_NADDR16",   /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1270,7 +1268,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont,/* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_ADDR16_LO", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1285,7 +1283,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_NADDR16_HI", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1302,7 +1300,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_dont, /* complain_on_overflow */
-        ppc_elf_addr16_ha_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_NADDR16_HA", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1319,7 +1317,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_SDAI16",    /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1336,7 +1334,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_SDA2I16",   /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1352,7 +1350,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_SDA2REL",   /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1369,7 +1367,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_SDA21",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1393,7 +1391,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_EMB_RELSDA",    /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1427,7 +1425,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         "R_PPC_VLE_REL15",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
-        0xfe,                  /* dst_mask */
+        0xfffe,                /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
   /* A relative 24 bit branch.  */
@@ -1449,11 +1447,11 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
   HOWTO (R_PPC_VLE_LO16A,      /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_VLE_LO16A",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1464,12 +1462,12 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
   HOWTO (R_PPC_VLE_LO16D,      /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_LO16D",             /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_LO16D",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f07ff,              /* dst_mask */
@@ -1477,14 +1475,14 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
 
   /* Bits 16-31 split16a format.  */
   HOWTO (R_PPC_VLE_HI16A,      /* type */
-        0,                     /* rightshift */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_HI16A",             /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_HI16A",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f007ff,             /* dst_mask */
@@ -1492,14 +1490,14 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
 
   /* Bits 16-31 split16d format.  */
   HOWTO (R_PPC_VLE_HI16D,      /* type */
-        0,                     /* rightshift */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_HI16D",             /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_HI16D",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f07ff,              /* dst_mask */
@@ -1507,14 +1505,14 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
 
   /* Bits 16-31 (High Adjusted) in split16a format.  */
   HOWTO (R_PPC_VLE_HA16A,      /* type */
-        0,                     /* rightshift */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_HA16A",             /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_HA16A",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f007ff,             /* dst_mask */
@@ -1522,135 +1520,136 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
 
   /* Bits 16-31 (High Adjusted) in split16d format.  */
   HOWTO (R_PPC_VLE_HA16D,      /* type */
-        0,                     /* rightshift */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_HA16D",             /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_HA16D",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f07ff,              /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  /* This reloc does nothing.  */
-  HOWTO (R_PPC_VLE_SDA21,              /* type */
+  /* This reloc is like R_PPC_EMB_SDA21 but only applies to e_add16i
+     instructions.  If the register base is 0 then the linker changes
+     the e_add16i to an e_li instruction.  */
+  HOWTO (R_PPC_VLE_SDA21,      /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
-        "R_PPC_VLE_SDA21",             /* name */
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_SDA21",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
-  /* This reloc does nothing.  */
+  /* Like R_PPC_VLE_SDA21 but ignore overflow.  */
   HOWTO (R_PPC_VLE_SDA21_LO,   /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_VLE_SDA21_LO",  /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
-        0,                     /* dst_mask */
+        0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   /* The 16 LSBS relative to _SDA_BASE_ in split16a format.  */
   HOWTO (R_PPC_VLE_SDAREL_LO16A,/* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield,    /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_SDAREL_LO16A",      /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_SDAREL_LO16A", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f007ff,             /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   /* The 16 LSBS relative to _SDA_BASE_ in split16d format.  */
-  /* This reloc does nothing.  */
   HOWTO (R_PPC_VLE_SDAREL_LO16D, /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield,    /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_SDAREL_LO16D",              /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_SDAREL_LO16D", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f07ff,              /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   /* Bits 16-31 relative to _SDA_BASE_ in split16a format.  */
-  HOWTO (R_PPC_VLE_SDAREL_HI16A,       /* type */
-        0,                     /* rightshift */
+  HOWTO (R_PPC_VLE_SDAREL_HI16A, /* type */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield,    /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_SDAREL_HI16A",      /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_SDAREL_HI16A", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f007ff,             /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   /* Bits 16-31 relative to _SDA_BASE_ in split16d format.  */
-  HOWTO (R_PPC_VLE_SDAREL_HI16D,       /* type */
-        0,                     /* rightshift */
+  HOWTO (R_PPC_VLE_SDAREL_HI16D, /* type */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield,    /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_SDAREL_HI16D",      /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_SDAREL_HI16D", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f07ff,              /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   /* Bits 16-31 (HA) relative to _SDA_BASE split16a format.  */
-  HOWTO (R_PPC_VLE_SDAREL_HA16A,       /* type */
-        0,                     /* rightshift */
+  HOWTO (R_PPC_VLE_SDAREL_HA16A, /* type */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield,    /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_SDAREL_HA16A",      /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_SDAREL_HA16A", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f007ff,             /* dst_mask */
         FALSE),                /* pcrel_offset */
 
   /* Bits 16-31 (HA) relative to _SDA_BASE split16d format.  */
-  HOWTO (R_PPC_VLE_SDAREL_HA16D,       /* type */
-        0,                     /* rightshift */
+  HOWTO (R_PPC_VLE_SDAREL_HA16D, /* type */
+        16,                    /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
-        32,                    /* bitsize */
+        16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield,    /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
-        "R_PPC_VLE_SDAREL_HA16D",      /* name */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
+        "R_PPC_VLE_SDAREL_HA16D", /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
         0x1f07ff,              /* dst_mask */
@@ -1662,8 +1661,8 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         32,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
-        bfd_elf_generic_reloc,  /* special_function */
+        complain_overflow_dont, /* complain_on_overflow */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_IRELATIVE",     /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1677,7 +1676,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         TRUE,                  /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_signed, /* complain_on_overflow */
         bfd_elf_generic_reloc, /* special_function */
         "R_PPC_REL16",         /* name */
         FALSE,                 /* partial_inplace */
@@ -1731,6 +1730,21 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         0xffff,                /* dst_mask */
         TRUE),                 /* pcrel_offset */
 
+  /* Like R_PPC_REL16_HA but for split field in addpcis.  */
+  HOWTO (R_PPC_REL16DX_HA,     /* type */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        ppc_elf_addr16_ha_reloc, /* special_function */
+        "R_PPC_REL16DX_HA",    /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1fffc1,              /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
   /* GNU extension to record C++ vtable hierarchy.  */
   HOWTO (R_PPC_GNU_VTINHERIT,  /* type */
         0,                     /* rightshift */
@@ -1769,7 +1783,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
         complain_overflow_signed, /* complain_on_overflow */
-        bfd_elf_generic_reloc, /* special_function */
+        ppc_elf_unhandled_reloc, /* special_function */
         "R_PPC_TOC16",         /* name */
         FALSE,                 /* partial_inplace */
         0,                     /* src_mask */
@@ -1804,30 +1818,28 @@ struct elf_external_ppc_linux_prpsinfo32
     char pr_psargs[80];                        /* Initial part of arg list.  */
   };
 
-/* Helper macro to swap (properly handling endianess) things from the
-   `elf_internal_prpsinfo' structure to the `elf_external_ppc_prpsinfo32'
-   structure.
-
-   Note that FROM should be a pointer, and TO should be the explicit type.  */
-
-#define PPC_LINUX_PRPSINFO32_SWAP_FIELDS(abfd, from, to)             \
-  do                                                                 \
-    {                                                                \
-      H_PUT_8 (abfd, from->pr_state, &to.pr_state);                  \
-      H_PUT_8 (abfd, from->pr_sname, &to.pr_sname);                  \
-      H_PUT_8 (abfd, from->pr_zomb, &to.pr_zomb);                    \
-      H_PUT_8 (abfd, from->pr_nice, &to.pr_nice);                    \
-      H_PUT_32 (abfd, from->pr_flag, to.pr_flag);                    \
-      H_PUT_32 (abfd, from->pr_uid, to.pr_uid);                              \
-      H_PUT_32 (abfd, from->pr_gid, to.pr_gid);                              \
-      H_PUT_32 (abfd, from->pr_pid, to.pr_pid);                              \
-      H_PUT_32 (abfd, from->pr_ppid, to.pr_ppid);                    \
-      H_PUT_32 (abfd, from->pr_pgrp, to.pr_pgrp);                    \
-      H_PUT_32 (abfd, from->pr_sid, to.pr_sid);                              \
-      strncpy (to.pr_fname, from->pr_fname, sizeof (to.pr_fname));    \
-      strncpy (to.pr_psargs, from->pr_psargs, sizeof (to.pr_psargs)); \
-    } while (0)
+/* Helper function to copy an elf_internal_linux_prpsinfo in host
+   endian to an elf_external_ppc_linux_prpsinfo32 in target endian.  */
 
+static inline void
+swap_ppc_linux_prpsinfo32_out (bfd *obfd,
+                              const struct elf_internal_linux_prpsinfo *from,
+                              struct elf_external_ppc_linux_prpsinfo32 *to)
+{
+  bfd_put_8 (obfd, from->pr_state, &to->pr_state);
+  bfd_put_8 (obfd, from->pr_sname, &to->pr_sname);
+  bfd_put_8 (obfd, from->pr_zomb, &to->pr_zomb);
+  bfd_put_8 (obfd, from->pr_nice, &to->pr_nice);
+  bfd_put_32 (obfd, from->pr_flag, to->pr_flag);
+  bfd_put_32 (obfd, from->pr_uid, to->pr_uid);
+  bfd_put_32 (obfd, from->pr_gid, to->pr_gid);
+  bfd_put_32 (obfd, from->pr_pid, to->pr_pid);
+  bfd_put_32 (obfd, from->pr_ppid, to->pr_ppid);
+  bfd_put_32 (obfd, from->pr_pgrp, to->pr_pgrp);
+  bfd_put_32 (obfd, from->pr_sid, to->pr_sid);
+  strncpy (to->pr_fname, from->pr_fname, sizeof (to->pr_fname));
+  strncpy (to->pr_psargs, from->pr_psargs, sizeof (to->pr_psargs));
+}
 \f
 /* Initialize the ppc_elf_howto_table, so that linear accesses can be done.  */
 
@@ -1989,6 +2001,7 @@ ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     case BFD_RELOC_LO16_PCREL:         r = R_PPC_REL16_LO;             break;
     case BFD_RELOC_HI16_PCREL:         r = R_PPC_REL16_HI;             break;
     case BFD_RELOC_HI16_S_PCREL:       r = R_PPC_REL16_HA;             break;
+    case BFD_RELOC_PPC_REL16DX_HA:     r = R_PPC_REL16DX_HA;           break;
     case BFD_RELOC_VTABLE_INHERIT:     r = R_PPC_GNU_VTINHERIT;        break;
     case BFD_RELOC_VTABLE_ENTRY:       r = R_PPC_GNU_VTENTRY;          break;
     }
@@ -2019,19 +2032,28 @@ ppc_elf_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED,
                       arelent *cache_ptr,
                       Elf_Internal_Rela *dst)
 {
+  unsigned int r_type;
+
   /* Initialize howto table if not already done.  */
   if (!ppc_elf_howto_table[R_PPC_ADDR32])
     ppc_elf_howto_init ();
 
-  BFD_ASSERT (ELF32_R_TYPE (dst->r_info) < (unsigned int) R_PPC_max);
-  cache_ptr->howto = ppc_elf_howto_table[ELF32_R_TYPE (dst->r_info)];
+  r_type = ELF32_R_TYPE (dst->r_info);
+  if (r_type >= R_PPC_max)
+    {
+      _bfd_error_handler (_("%B: unrecognised PPC reloc number: %d"),
+                         abfd, r_type);
+      bfd_set_error (bfd_error_bad_value);
+      r_type = R_PPC_NONE;
+    }
+  cache_ptr->howto = ppc_elf_howto_table[r_type];
 
   /* Just because the above assert didn't trigger doesn't mean that
      ELF32_R_TYPE (dst->r_info) is necessarily a valid relocation.  */
   if (!cache_ptr->howto)
     {
-      (*_bfd_error_handler) (_("%B: invalid relocation type %d"),
-                             abfd, ELF32_R_TYPE (dst->r_info));
+      _bfd_error_handler (_("%B: invalid relocation type %d"),
+                         abfd, r_type);
       bfd_set_error (bfd_error_bad_value);
 
       cache_ptr->howto = ppc_elf_howto_table[R_PPC_NONE];
@@ -2049,7 +2071,10 @@ ppc_elf_addr16_ha_reloc (bfd *abfd ATTRIBUTE_UNUSED,
                         bfd *output_bfd,
                         char **error_message ATTRIBUTE_UNUSED)
 {
-  bfd_vma relocation;
+  enum elf_ppc_reloc_type r_type;
+  long insn;
+  bfd_size_type octets;
+  bfd_vma value;
 
   if (output_bfd != NULL)
     {
@@ -2057,23 +2082,28 @@ ppc_elf_addr16_ha_reloc (bfd *abfd ATTRIBUTE_UNUSED,
       return bfd_reloc_ok;
     }
 
-  if (reloc_entry->address > bfd_get_section_limit (abfd, input_section))
-    return bfd_reloc_outofrange;
-
-  if (bfd_is_com_section (symbol->section))
-    relocation = 0;
-  else
-    relocation = symbol->value;
-
-  relocation += symbol->section->output_section->vma;
-  relocation += symbol->section->output_offset;
-  relocation += reloc_entry->addend;
-  if (reloc_entry->howto->pc_relative)
-    relocation -= reloc_entry->address;
-
-  reloc_entry->addend += (relocation & 0x8000) << 1;
-
-  return bfd_reloc_continue;
+  reloc_entry->addend += 0x8000;
+  r_type = reloc_entry->howto->type;
+  if (r_type != R_PPC_REL16DX_HA)
+    return bfd_reloc_continue;
+
+  value = 0;
+  if (!bfd_is_com_section (symbol->section))
+    value = symbol->value;
+  value += (reloc_entry->addend
+           + symbol->section->output_offset
+           + symbol->section->output_section->vma);
+  value -= (reloc_entry->address
+           + input_section->output_offset
+           + input_section->output_section->vma);
+  value >>= 16;
+
+  octets = reloc_entry->address * bfd_octets_per_byte (abfd);
+  insn = bfd_get_32 (abfd, (bfd_byte *) data + octets);
+  insn &= ~0x1fffc1;
+  insn |= (value & 0xffc1) | ((value & 0x3e) << 15);
+  bfd_put_32 (abfd, insn, (bfd_byte *) data + octets);
+  return bfd_reloc_ok;
 }
 
 static bfd_reloc_status_type
@@ -2166,13 +2196,94 @@ ppc_elf_mkobject (bfd *abfd)
                                  PPC32_ELF_DATA);
 }
 
+/* When defaulting arch/mach, decode apuinfo to find a better match.  */
+
+bfd_boolean
+_bfd_elf_ppc_set_arch (bfd *abfd)
+{
+  unsigned long mach = 0;
+  asection *s;
+  unsigned char *contents;
+
+  if (abfd->arch_info->bits_per_word == 32
+      && bfd_big_endian (abfd))
+    {
+
+      for (s = abfd->sections; s != NULL; s = s->next)
+       if ((elf_section_data (s)->this_hdr.sh_flags & SHF_PPC_VLE) != 0)
+         break;
+      if (s != NULL)
+       mach = bfd_mach_ppc_vle;
+    }
+
+  if (mach == 0)
+    {
+      s = bfd_get_section_by_name (abfd, APUINFO_SECTION_NAME);
+      if (s != NULL && bfd_malloc_and_get_section (abfd, s, &contents))
+       {
+         unsigned int apuinfo_size = bfd_get_32 (abfd, contents + 4);
+         unsigned int i;
+
+         for (i = 20; i < apuinfo_size + 20 && i + 4 <= s->size; i += 4)
+           {
+             unsigned int val = bfd_get_32 (abfd, contents + i);
+             switch (val >> 16)
+               {
+               case PPC_APUINFO_PMR:
+               case PPC_APUINFO_RFMCI:
+                 if (mach == 0)
+                   mach = bfd_mach_ppc_titan;
+                 break;
+
+               case PPC_APUINFO_ISEL:
+               case PPC_APUINFO_CACHELCK:
+                 if (mach == bfd_mach_ppc_titan)
+                   mach = bfd_mach_ppc_e500mc;
+                 break;
+
+               case PPC_APUINFO_SPE:
+               case PPC_APUINFO_EFS:
+               case PPC_APUINFO_BRLOCK:
+                 if (mach != bfd_mach_ppc_vle)
+                   mach = bfd_mach_ppc_e500;
+                 break;
+
+               case PPC_APUINFO_VLE:
+                 mach = bfd_mach_ppc_vle;
+                 break;
+
+               default:
+                 mach = -1ul;
+               }
+           }
+         free (contents);
+       }
+    }
+
+  if (mach != 0 && mach != -1ul)
+    {
+      const bfd_arch_info_type *arch;
+
+      for (arch = abfd->arch_info->next; arch; arch = arch->next)
+       if (arch->mach == mach)
+         {
+           abfd->arch_info = arch;
+           break;
+         }
+    }
+  return TRUE;
+}
+
 /* Fix bad default arch selected for a 32 bit input bfd when the
-   default is 64 bit.  */
+   default is 64 bit.  Also select arch based on apuinfo.  */
 
 static bfd_boolean
 ppc_elf_object_p (bfd *abfd)
 {
-  if (abfd->arch_info->the_default && abfd->arch_info->bits_per_word == 64)
+  if (!abfd->arch_info->the_default)
+    return TRUE;
+
+  if (abfd->arch_info->bits_per_word == 64)
     {
       Elf_Internal_Ehdr *i_ehdr = elf_elfheader (abfd);
 
@@ -2183,7 +2294,7 @@ ppc_elf_object_p (bfd *abfd)
          BFD_ASSERT (abfd->arch_info->bits_per_word == 32);
        }
     }
-  return TRUE;
+  return _bfd_elf_ppc_set_arch (abfd);
 }
 
 /* Function to set whether a module needs the -mrelocatable bit set.  */
@@ -2264,14 +2375,15 @@ ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
 }
 
 char *
-elfcore_write_ppc_linux_prpsinfo32 (bfd *abfd, char *buf, int *bufsiz,
-                                     const struct elf_internal_linux_prpsinfo *prpsinfo)
+elfcore_write_ppc_linux_prpsinfo32
+  (bfd *abfd,
+   char *buf,
+   int *bufsiz,
+   const struct elf_internal_linux_prpsinfo *prpsinfo)
 {
   struct elf_external_ppc_linux_prpsinfo32 data;
 
-  memset (&data, 0, sizeof (data));
-  PPC_LINUX_PRPSINFO32_SWAP_FIELDS (abfd, prpsinfo, data);
-
+  swap_ppc_linux_prpsinfo32_out (abfd, prpsinfo, &data);
   return elfcore_write_note (abfd, buf, bufsiz,
                             "CORE", NT_PRPSINFO, &data, sizeof (data));
 }
@@ -2332,18 +2444,6 @@ ppc_elf_lookup_section_flags (char *flag_name)
   return 0;
 }
 
-/* Add the VLE flag if required.  */
-
-bfd_boolean
-ppc_elf_section_processing (bfd *abfd, Elf_Internal_Shdr *shdr)
-{
-  if (bfd_get_mach (abfd) == bfd_mach_ppc_vle
-      && (shdr->sh_flags & SHF_EXECINSTR) != 0)
-    shdr->sh_flags |= SHF_PPC_VLE;
-
-  return TRUE;
-}
-
 /* Return address for Ith PLT stub in section PLT, for relocation REL
    or (bfd_vma) -1 if it should not be included.  */
 
@@ -2423,10 +2523,7 @@ bfd_boolean
 ppc_elf_modify_segment_map (bfd *abfd,
                            struct bfd_link_info *info ATTRIBUTE_UNUSED)
 {
-  struct elf_segment_map *m, *n;
-  bfd_size_type amt;
-  unsigned int j, k;
-  bfd_boolean sect0_vle, sectj_vle;
+  struct elf_segment_map *m;
 
   /* At this point in the link, output sections have already been sorted by
      LMA and assigned to segments.  All that is left to do is to ensure
@@ -2436,25 +2533,59 @@ ppc_elf_modify_segment_map (bfd *abfd,
 
   for (m = elf_seg_map (abfd); m != NULL; m = m->next)
     {
-      if (m->count == 0)
+      struct elf_segment_map *n;
+      bfd_size_type amt;
+      unsigned int j, k;
+      unsigned int p_flags;
+
+      if (m->p_type != PT_LOAD || m->count == 0)
        continue;
 
-      sect0_vle = (elf_section_flags (m->sections[0]) & SHF_PPC_VLE) != 0;
-      for (j = 1; j < m->count; ++j)
+      for (p_flags = PF_R, j = 0; j != m->count; ++j)
        {
-         sectj_vle = (elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0;
+         if ((m->sections[j]->flags & SEC_READONLY) == 0)
+           p_flags |= PF_W;
+         if ((m->sections[j]->flags & SEC_CODE) != 0)
+           {
+             p_flags |= PF_X;
+             if ((elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0)
+               p_flags |= PF_PPC_VLE;
+             break;
+           }
+       }
+      if (j != m->count)
+       while (++j != m->count)
+         {
+           unsigned int p_flags1 = PF_R;
 
-         if (sectj_vle != sect0_vle)
-           break;
-        }
-      if (j >= m->count)
+           if ((m->sections[j]->flags & SEC_READONLY) == 0)
+             p_flags1 |= PF_W;
+           if ((m->sections[j]->flags & SEC_CODE) != 0)
+             {
+               p_flags1 |= PF_X;
+               if ((elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0)
+                 p_flags1 |= PF_PPC_VLE;
+               if (((p_flags1 ^ p_flags) & PF_PPC_VLE) != 0)
+                 break;
+             }
+           p_flags |= p_flags1;
+         }
+      /* If we're splitting a segment which originally contained rw
+        sections then those sections might now only be in one of the
+        two parts.  So always set p_flags if splitting, even if we
+        are being called for objcopy with p_flags_valid set.  */
+      if (j != m->count || !m->p_flags_valid)
+       {
+         m->p_flags_valid = 1;
+         m->p_flags = p_flags;
+       }
+      if (j == m->count)
        continue;
 
-      /* sections 0..j-1 stay in this (current) segment,
+      /* Sections 0..j-1 stay in this (current) segment,
         the remainder are put in a new segment.
         The scan resumes with the new segment.  */
 
-      /* Fix the new segment.  */
       amt = sizeof (struct elf_segment_map);
       amt += (m->count - j - 1) * sizeof (asection *);
       n = (struct elf_segment_map *) bfd_zalloc (abfd, amt);
@@ -2462,20 +2593,13 @@ ppc_elf_modify_segment_map (bfd *abfd,
         return FALSE;
 
       n->p_type = PT_LOAD;
-      n->p_flags = PF_X | PF_R;
-      if (sectj_vle)
-        n->p_flags |= PF_PPC_VLE;
       n->count = m->count - j;
       for (k = 0; k < n->count; ++k)
-        {
-          n->sections[k] = m->sections[j+k];
-          m->sections[j+k] = NULL;
-       }
+       n->sections[k] = m->sections[j + k];
+      m->count = j;
+      m->p_size_valid = 0;
       n->next = m->next;
       m->next = n;
-
-      /* Fix the current segment  */
-      m->count = j;
     }
 
   return TRUE;
@@ -2488,16 +2612,16 @@ ppc_elf_modify_segment_map (bfd *abfd,
 
 static const struct bfd_elf_special_section ppc_elf_special_sections[] =
 {
-  { STRING_COMMA_LEN (".plt"),             0, SHT_NOBITS,   SHF_ALLOC + SHF_EXECINSTR },
-  { STRING_COMMA_LEN (".sbss"),           -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { STRING_COMMA_LEN (".sbss2"),          -2, SHT_PROGBITS, SHF_ALLOC },
-  { STRING_COMMA_LEN (".sdata"),          -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
-  { STRING_COMMA_LEN (".sdata2"),         -2, SHT_PROGBITS, SHF_ALLOC },
-  { STRING_COMMA_LEN (".tags"),            0, SHT_ORDERED,  SHF_ALLOC },
-  { STRING_COMMA_LEN (".PPC.EMB.apuinfo"), 0, SHT_NOTE,     0 },
-  { STRING_COMMA_LEN (".PPC.EMB.sbss0"),   0, SHT_PROGBITS, SHF_ALLOC },
-  { STRING_COMMA_LEN (".PPC.EMB.sdata0"),  0, SHT_PROGBITS, SHF_ALLOC },
-  { NULL,                              0,  0, 0,            0 }
+  { STRING_COMMA_LEN (".plt"), 0, SHT_NOBITS, SHF_ALLOC + SHF_EXECINSTR },
+  { STRING_COMMA_LEN (".sbss"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".sbss2"), -2, SHT_PROGBITS, SHF_ALLOC },
+  { STRING_COMMA_LEN (".sdata"), -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { STRING_COMMA_LEN (".sdata2"), -2, SHT_PROGBITS, SHF_ALLOC },
+  { STRING_COMMA_LEN (".tags"), 0, SHT_ORDERED, SHF_ALLOC },
+  { STRING_COMMA_LEN (APUINFO_SECTION_NAME), 0, SHT_NOTE, 0 },
+  { STRING_COMMA_LEN (".PPC.EMB.sbss0"), 0, SHT_PROGBITS, SHF_ALLOC },
+  { STRING_COMMA_LEN (".PPC.EMB.sdata0"), 0, SHT_PROGBITS, SHF_ALLOC },
+  { NULL, 0, 0, 0, 0 }
 };
 
 /* This is what we want for new plt/got.  */
@@ -2606,9 +2730,6 @@ apuinfo_list_finish (void)
   head = NULL;
 }
 
-#define APUINFO_SECTION_NAME   ".PPC.EMB.apuinfo"
-#define APUINFO_LABEL          "APUinfo"
-
 /* Scan the input BFDs and create a linked list of
    the APUinfo values that will need to be emitted.  */
 
@@ -2629,7 +2750,7 @@ ppc_elf_begin_write_processing (bfd *abfd, struct bfd_link_info *link_info)
   apuinfo_list_init ();
 
   /* Read in the input sections contents.  */
-  for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link_next)
+  for (ibfd = link_info->input_bfds; ibfd; ibfd = ibfd->link.next)
     {
       unsigned long datum;
 
@@ -2706,7 +2827,7 @@ ppc_elf_begin_write_processing (bfd *abfd, struct bfd_link_info *link_info)
     free (buffer);
 
   if (error_message)
-    (*_bfd_error_handler) (error_message, ibfd, APUINFO_SECTION_NAME);
+    _bfd_error_handler (error_message, ibfd, APUINFO_SECTION_NAME);
 }
 
 /* Prevent the output section from accumulating the input sections'
@@ -2746,7 +2867,7 @@ ppc_elf_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED)
   buffer = bfd_malloc (length);
   if (buffer == NULL)
     {
-      (*_bfd_error_handler)
+      _bfd_error_handler
        (_("failed to allocate space for new APUinfo section."));
       return;
     }
@@ -2766,10 +2887,10 @@ ppc_elf_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED)
     }
 
   if (length != asec->size)
-    (*_bfd_error_handler) (_("failed to compute new APUinfo section."));
+    _bfd_error_handler (_("failed to compute new APUinfo section."));
 
   if (! bfd_set_section_contents (abfd, asec, buffer, (file_ptr) 0, length))
-    (*_bfd_error_handler) (_("failed to install new APUinfo section."));
+    _bfd_error_handler (_("failed to install new APUinfo section."));
 
   free (buffer);
 
@@ -2919,7 +3040,6 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
     }
 
   count = relplt->size / sizeof (Elf32_External_Rela);
-  stub_vma = glink_vma - (bfd_vma) count * 16;
   /* If the stubs are those for -shared/-pie then we might have
      multiple stubs for each plt entry.  If that is the case then
      there is no way to associate stubs with their plt entries short
@@ -2950,9 +3070,10 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
   if (s == NULL)
     return -1;
 
+  stub_vma = glink_vma;
   names = (char *) (s + count + 1 + (resolv_vma != 0));
-  p = relplt->relocation;
-  for (i = 0; i < count; i++, p++)
+  p = relplt->relocation + count - 1;
+  for (i = 0; i < count; i++)
     {
       size_t len;
 
@@ -2963,6 +3084,9 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
        s->flags |= BSF_GLOBAL;
       s->flags |= BSF_SYNTHETIC;
       s->section = glink;
+      stub_vma -= 16;
+      if (strcmp ((*p->sym_ptr_ptr)->name, "__tls_get_addr_opt") == 0)
+       stub_vma -= 32;
       s->value = stub_vma - glink->vma;
       s->name = names;
       s->udata.p = NULL;
@@ -2979,7 +3103,7 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
       memcpy (names, "@plt", sizeof ("@plt"));
       names += sizeof ("@plt");
       ++s;
-      stub_vma += 16;
+      --p;
     }
 
   /* Add a symbol at the start of the glink branch table.  */
@@ -3071,7 +3195,7 @@ must_be_dyn_reloc (struct bfd_link_info *info,
     case R_PPC_TPREL16_LO:
     case R_PPC_TPREL16_HI:
     case R_PPC_TPREL16_HA:
-      return !info->executable;
+      return !bfd_link_executable (info);
     }
 }
 
@@ -3126,7 +3250,11 @@ struct ppc_elf_link_hash_entry
 
   /* Nonzero if we have seen a small data relocation referring to this
      symbol.  */
-  unsigned char has_sda_refs;
+  unsigned char has_sda_refs : 1;
+
+  /* Flag use of given relocations.  */
+  unsigned char has_addr16_ha : 1;
+  unsigned char has_addr16_lo : 1;
 };
 
 #define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent))
@@ -3250,7 +3378,8 @@ static struct bfd_link_hash_table *
 ppc_elf_link_hash_table_create (bfd *abfd)
 {
   struct ppc_elf_link_hash_table *ret;
-  static struct ppc_elf_params default_params = { PLT_OLD, 0, 1, 0, 0, 4096 };
+  static struct ppc_elf_params default_params
+    = { PLT_OLD, 0, 1, 0, 0, 12, 0, 0 };
 
   ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table));
   if (ret == NULL)
@@ -3296,6 +3425,7 @@ ppc_elf_link_params (struct bfd_link_info *info, struct ppc_elf_params *params)
 
   if (htab)
     htab->params = params;
+  params->pagesize_p2 = bfd_log2 (params->pagesize);
 }
 
 /* Create .got and the related sections.  */
@@ -3338,6 +3468,36 @@ ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
   return TRUE;
 }
 
+/* Create a special linker section, used for R_PPC_EMB_SDAI16 and
+   R_PPC_EMB_SDA2I16 pointers.  These sections become part of .sdata
+   and .sdata2.  Create _SDA_BASE_ and _SDA2_BASE too.  */
+
+static bfd_boolean
+ppc_elf_create_linker_section (bfd *abfd,
+                              struct bfd_link_info *info,
+                              flagword flags,
+                              elf_linker_section_t *lsect)
+{
+  asection *s;
+
+  flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+           | SEC_LINKER_CREATED);
+
+  s = bfd_make_section_anyway_with_flags (abfd, lsect->name, flags);
+  if (s == NULL)
+    return FALSE;
+  lsect->section = s;
+
+  /* Define the sym on the first section of this name.  */
+  s = bfd_get_section_by_name (abfd, lsect->name);
+
+  lsect->sym = _bfd_elf_define_linkage_sym (abfd, info, s, lsect->sym_name);
+  if (lsect->sym == NULL)
+    return FALSE;
+  lsect->sym->root.u.def.value = 0x8000;
+  return TRUE;
+}
+
 static bfd_boolean
 ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
 {
@@ -3379,6 +3539,15 @@ ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
   if (s == NULL
       || ! bfd_set_section_alignment (abfd, s, 2))
     return FALSE;
+
+  if (!ppc_elf_create_linker_section (abfd, info, 0,
+                                     &htab->sdata[0]))
+    return FALSE;
+
+  if (!ppc_elf_create_linker_section (abfd, info, SEC_READONLY,
+                                     &htab->sdata[1]))
+    return FALSE;
+
   return TRUE;
 }
 
@@ -3413,7 +3582,7 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
   if (s == NULL)
     return FALSE;
 
-  if (! info->shared)
+  if (! bfd_link_pic (info))
     {
       htab->relbss = bfd_get_linker_section (abfd, ".rela.bss");
       flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
@@ -3570,7 +3739,7 @@ ppc_elf_add_symbol_hook (bfd *abfd,
                         bfd_vma *valp)
 {
   if (sym->st_shndx == SHN_COMMON
-      && !info->relocatable
+      && !bfd_link_relocatable (info)
       && is_ppc_elf (info->output_bfd)
       && sym->st_size <= elf_gp_size (abfd))
     {
@@ -3597,59 +3766,14 @@ ppc_elf_add_symbol_hook (bfd *abfd,
       *valp = sym->st_size;
     }
 
-  if ((abfd->flags & DYNAMIC) == 0
-      && (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
-         || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE))
-    elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
+  if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+      && (abfd->flags & DYNAMIC) == 0
+      && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
+    elf_tdata (info->output_bfd)->has_gnu_symbols |= elf_gnu_symbol_ifunc;
 
   return TRUE;
 }
 \f
-static bfd_boolean
-create_sdata_sym (struct bfd_link_info *info, elf_linker_section_t *lsect)
-{
-  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
-
-  lsect->sym = elf_link_hash_lookup (&htab->elf, lsect->sym_name,
-                                    TRUE, FALSE, TRUE);
-  if (lsect->sym == NULL)
-    return FALSE;
-  if (lsect->sym->root.type == bfd_link_hash_new)
-    lsect->sym->non_elf = 0;
-  lsect->sym->ref_regular = 1;
-  _bfd_elf_link_hash_hide_symbol (info, lsect->sym, TRUE);
-  return TRUE;
-}
-
-/* Create a special linker section.  */
-
-static bfd_boolean
-ppc_elf_create_linker_section (bfd *abfd,
-                              struct bfd_link_info *info,
-                              flagword flags,
-                              elf_linker_section_t *lsect)
-{
-  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
-  asection *s;
-
-  flags |= (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-           | SEC_LINKER_CREATED);
-
-  /* Record the first bfd that needs the special sections.  */
-  if (!htab->elf.dynobj)
-    htab->elf.dynobj = abfd;
-
-  s = bfd_make_section_anyway_with_flags (htab->elf.dynobj,
-                                         lsect->name,
-                                         flags);
-  if (s == NULL
-      || !bfd_set_section_alignment (htab->elf.dynobj, s, 2))
-    return FALSE;
-  lsect->section = s;
-
-  return create_sdata_sym (info, lsect);
-}
-
 /* Find a linker generated pointer with a given addend and type.  */
 
 static elf_linker_section_pointers_t *
@@ -3668,10 +3792,10 @@ elf_find_pointer_linker_section
 /* Allocate a pointer to live in a linker created section.  */
 
 static bfd_boolean
-elf_create_pointer_linker_section (bfd *abfd,
-                                  elf_linker_section_t *lsect,
-                                  struct elf_link_hash_entry *h,
-                                  const Elf_Internal_Rela *rel)
+elf_allocate_pointer_linker_section (bfd *abfd,
+                                    elf_linker_section_t *lsect,
+                                    struct elf_link_hash_entry *h,
+                                    const Elf_Internal_Rela *rel)
 {
   elf_linker_section_pointers_t **ptr_linker_section_ptr = NULL;
   elf_linker_section_pointers_t *linker_section_ptr;
@@ -3739,6 +3863,8 @@ elf_create_pointer_linker_section (bfd *abfd,
   linker_section_ptr->lsect = lsect;
   *ptr_linker_section_ptr = linker_section_ptr;
 
+  if (!bfd_set_section_alignment (lsect->section->owner, lsect->section, 2))
+    return FALSE;
   linker_section_ptr->offset = lsect->section->size;
   lsect->section->size += 4;
 
@@ -3835,13 +3961,14 @@ is_branch_reloc (enum elf_ppc_reloc_type r_type)
          || r_type == R_PPC_ADDR24
          || r_type == R_PPC_ADDR14
          || r_type == R_PPC_ADDR14_BRTAKEN
-         || r_type == R_PPC_ADDR14_BRNTAKEN);
+         || r_type == R_PPC_ADDR14_BRNTAKEN
+         || r_type == R_PPC_VLE_REL24);
 }
 
 static void
 bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
 {
-  (*_bfd_error_handler)
+  _bfd_error_handler
     (_("%B: relocation %s cannot be used when making a shared object"),
      abfd,
      ppc_elf_howto_table[r_type]->name);
@@ -3866,7 +3993,7 @@ ppc_elf_check_relocs (bfd *abfd,
   asection *got2, *sreloc;
   struct elf_link_hash_entry *tga;
 
-  if (info->relocatable)
+  if (bfd_link_relocatable (info))
     return TRUE;
 
   /* Don't do anything special with non-loaded, non-alloced sections.
@@ -3911,6 +4038,7 @@ ppc_elf_check_relocs (bfd *abfd,
       enum elf_ppc_reloc_type r_type;
       struct elf_link_hash_entry *h;
       int tls_type;
+      struct plt_entry **ifunc;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx < symtab_hdr->sh_info)
@@ -3943,6 +4071,7 @@ ppc_elf_check_relocs (bfd *abfd,
 
       tls_type = 0;
       r_type = ELF32_R_TYPE (rel->r_info);
+      ifunc = NULL;
       if (h == NULL && !htab->is_vxworks)
        {
          Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache,
@@ -3952,8 +4081,6 @@ ppc_elf_check_relocs (bfd *abfd,
 
          if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
-             struct plt_entry **ifunc;
-
              /* Set PLT_IFUNC flag for this sym, no GOT entry yet.  */
              ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                             PLT_IFUNC);
@@ -3963,14 +4090,14 @@ ppc_elf_check_relocs (bfd *abfd,
              /* STT_GNU_IFUNC symbols must have a PLT entry;
                 In a non-pie executable even when there are
                 no plt calls.  */
-             if (!info->shared
+             if (!bfd_link_pic (info)
                  || is_branch_reloc (r_type))
                {
                  bfd_vma addend = 0;
                  if (r_type == R_PPC_PLTREL24)
                    {
                      ppc_elf_tdata (abfd)->makes_plt_call = 1;
-                     if (info->shared)
+                     if (bfd_link_pic (info))
                        addend = rel->r_addend;
                    }
                  if (!update_plt_info (abfd, ifunc, got2, addend))
@@ -4021,7 +4148,7 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_GOT_TPREL16_LO:
        case R_PPC_GOT_TPREL16_HI:
        case R_PPC_GOT_TPREL16_HA:
-         if (!info->executable)
+         if (bfd_link_pic (info))
            info->flags |= DF_STATIC_TLS;
          tls_type = TLS_TLS | TLS_TPREL;
          goto dogottls;
@@ -4060,7 +4187,7 @@ ppc_elf_check_relocs (bfd *abfd,
 
          /* We may also need a plt entry if the symbol turns out to be
             an ifunc.  */
-         if (h != NULL && !info->shared)
+         if (h != NULL && !bfd_link_pic (info))
            {
              if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
                return FALSE;
@@ -4069,17 +4196,14 @@ ppc_elf_check_relocs (bfd *abfd,
 
          /* Indirect .sdata relocation.  */
        case R_PPC_EMB_SDAI16:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              bad_shared_reloc (abfd, r_type);
              return FALSE;
            }
-         if (htab->sdata[0].section == NULL
-             && !ppc_elf_create_linker_section (abfd, info, 0,
-                                                &htab->sdata[0]))
-           return FALSE;
-         if (!elf_create_pointer_linker_section (abfd, &htab->sdata[0],
-                                                 h, rel))
+         htab->sdata[0].sym->ref_regular = 1;
+         if (!elf_allocate_pointer_linker_section (abfd, &htab->sdata[0],
+                                                   h, rel))
            return FALSE;
          if (h != NULL)
            {
@@ -4090,17 +4214,14 @@ ppc_elf_check_relocs (bfd *abfd,
 
          /* Indirect .sdata2 relocation.  */
        case R_PPC_EMB_SDA2I16:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              bad_shared_reloc (abfd, r_type);
              return FALSE;
            }
-         if (htab->sdata[1].section == NULL
-             && !ppc_elf_create_linker_section (abfd, info, SEC_READONLY,
-                                                &htab->sdata[1]))
-           return FALSE;
-         if (!elf_create_pointer_linker_section (abfd, &htab->sdata[1],
-                                                 h, rel))
+         htab->sdata[1].sym->ref_regular = 1;
+         if (!elf_allocate_pointer_linker_section (abfd, &htab->sdata[1],
+                                                   h, rel))
            return FALSE;
          if (h != NULL)
            {
@@ -4109,21 +4230,16 @@ ppc_elf_check_relocs (bfd *abfd,
            }
          break;
 
+       case R_PPC_SDAREL16:
+         htab->sdata[0].sym->ref_regular = 1;
+         /* Fall thru */
+
        case R_PPC_VLE_SDAREL_LO16A:
        case R_PPC_VLE_SDAREL_LO16D:
        case R_PPC_VLE_SDAREL_HI16A:
        case R_PPC_VLE_SDAREL_HI16D:
        case R_PPC_VLE_SDAREL_HA16A:
        case R_PPC_VLE_SDAREL_HA16D:
-       case R_PPC_SDAREL16:
-         if (htab->sdata[0].sym == NULL
-             && !create_sdata_sym (info, &htab->sdata[0]))
-           return FALSE;
-
-         if (htab->sdata[1].sym == NULL
-             && !create_sdata_sym (info, &htab->sdata[1]))
-           return FALSE;
-
          if (h != NULL)
            {
              ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
@@ -4143,14 +4259,12 @@ ppc_elf_check_relocs (bfd *abfd,
          break;
 
        case R_PPC_EMB_SDA2REL:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              bad_shared_reloc (abfd, r_type);
              return FALSE;
            }
-         if (htab->sdata[1].sym == NULL
-             && !create_sdata_sym (info, &htab->sdata[1]))
-           return FALSE;
+         htab->sdata[1].sym->ref_regular = 1;
          if (h != NULL)
            {
              ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
@@ -4162,17 +4276,11 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_VLE_SDA21:
        case R_PPC_EMB_SDA21:
        case R_PPC_EMB_RELSDA:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              bad_shared_reloc (abfd, r_type);
              return FALSE;
            }
-         if (htab->sdata[0].sym == NULL
-             && !create_sdata_sym (info, &htab->sdata[0]))
-           return FALSE;
-         if (htab->sdata[1].sym == NULL
-             && !create_sdata_sym (info, &htab->sdata[1]))
-           return FALSE;
          if (h != NULL)
            {
              ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
@@ -4185,7 +4293,7 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_EMB_NADDR16_LO:
        case R_PPC_EMB_NADDR16_HI:
        case R_PPC_EMB_NADDR16_HA:
-         if (info->shared)
+         if (bfd_link_pic (info))
            {
              bad_shared_reloc (abfd, r_type);
              return FALSE;
@@ -4206,21 +4314,20 @@ ppc_elf_check_relocs (bfd *abfd,
 #ifdef DEBUG
          fprintf (stderr, "Reloc requires a PLT entry\n");
 #endif
-         /* This symbol requires a procedure linkage table entry.  We
-            actually build the entry in finish_dynamic_symbol,
-            because this might be a case of linking PIC code without
-            linking in any dynamic objects, in which case we don't
-            need to generate a procedure linkage table after all.  */
-
+         /* This symbol requires a procedure linkage table entry.  */
          if (h == NULL)
            {
-             /* It does not make sense to have a procedure linkage
-                table entry for a local symbol.  */
-             info->callbacks->einfo (_("%P: %H: %s reloc against local symbol\n"),
-                                     abfd, sec, rel->r_offset,
-                                     ppc_elf_howto_table[r_type]->name);
-             bfd_set_error (bfd_error_bad_value);
-             return FALSE;
+             if (ifunc == NULL)
+               {
+                 /* It does not make sense to have a procedure linkage
+                    table entry for a non-ifunc local symbol.  */
+                 info->callbacks->einfo
+                   (_("%P: %H: %s reloc against local symbol\n"),
+                    abfd, sec, rel->r_offset,
+                    ppc_elf_howto_table[r_type]->name);
+                 bfd_set_error (bfd_error_bad_value);
+                 return FALSE;
+               }
            }
          else
            {
@@ -4229,7 +4336,7 @@ ppc_elf_check_relocs (bfd *abfd,
              if (r_type == R_PPC_PLTREL24)
                {
                  ppc_elf_tdata (abfd)->makes_plt_call = 1;
-                 if (info->shared)
+                 if (bfd_link_pic (info))
                    addend = rel->r_addend;
                }
              h->needs_plt = 1;
@@ -4256,6 +4363,7 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_REL16_LO:
        case R_PPC_REL16_HI:
        case R_PPC_REL16_HA:
+       case R_PPC_REL16DX_HA:
          ppc_elf_tdata (abfd)->has_rel16 = 1;
          break;
 
@@ -4295,14 +4403,6 @@ ppc_elf_check_relocs (bfd *abfd,
            }
          if (h != NULL && h->type == STT_GNU_IFUNC)
            {
-             if (info->shared)
-               {
-                 info->callbacks->einfo (_("%P: %H: @local call to ifunc %s\n"),
-                                         abfd, sec, rel->r_offset,
-                                         h->root.root.string);
-                 bfd_set_error (bfd_error_bad_value);
-                 return FALSE;
-               }
              h->needs_plt = 1;
              if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
                return FALSE;
@@ -4331,7 +4431,7 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_TPREL16_LO:
        case R_PPC_TPREL16_HI:
        case R_PPC_TPREL16_HA:
-         if (!info->executable)
+         if (bfd_link_pic (info))
            info->flags |= DF_STATIC_TLS;
          goto dodyn;
 
@@ -4344,7 +4444,7 @@ ppc_elf_check_relocs (bfd *abfd,
          if (h == NULL
              && got2 != NULL
              && (sec->flags & SEC_CODE) != 0
-             && info->shared
+             && bfd_link_pic (info)
              && htab->plt_type == PLT_UNSET)
            {
              /* Old -fPIC gcc code has .long LCTOC1-LCFx just before
@@ -4379,7 +4479,7 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_ADDR16_HA:
        case R_PPC_UADDR32:
        case R_PPC_UADDR16:
-         if (h != NULL && !info->shared)
+         if (h != NULL && !bfd_link_pic (info))
            {
              /* We may need a plt entry if the symbol turns out to be
                 a function defined in a dynamic object.  */
@@ -4389,6 +4489,10 @@ ppc_elf_check_relocs (bfd *abfd,
              /* We may need a copy reloc too.  */
              h->non_got_ref = 1;
              h->pointer_equality_needed = 1;
+             if (r_type == R_PPC_ADDR16_HA)
+               ppc_elf_hash_entry (h)->has_addr16_ha = 1;
+             if (r_type == R_PPC_ADDR16_LO)
+               ppc_elf_hash_entry (h)->has_addr16_lo = 1;
            }
          goto dodyn;
 
@@ -4413,7 +4517,7 @@ ppc_elf_check_relocs (bfd *abfd,
        case R_PPC_ADDR14:
        case R_PPC_ADDR14_BRTAKEN:
        case R_PPC_ADDR14_BRNTAKEN:
-         if (h != NULL && !info->shared)
+         if (h != NULL && !bfd_link_pic (info))
            {
              /* We may need a plt entry if the symbol turns out to be
                 a function defined in a dynamic object.  */
@@ -4445,14 +4549,14 @@ ppc_elf_check_relocs (bfd *abfd,
             may need to keep relocations for symbols satisfied by a
             dynamic library if we manage to avoid copy relocs for the
             symbol.  */
-         if ((info->shared
+         if ((bfd_link_pic (info)
               && (must_be_dyn_reloc (info, r_type)
                   || (h != NULL
                       && (!SYMBOLIC_BIND (info, h)
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
              || (ELIMINATE_COPY_RELOCS
-                 && !info->shared
+                 && !bfd_link_pic (info)
                  && h != NULL
                  && (h->root.type == bfd_link_hash_defweak
                      || !h->def_regular)))
@@ -4549,68 +4653,87 @@ ppc_elf_check_relocs (bfd *abfd,
   return TRUE;
 }
 \f
-
-/* Merge object attributes from IBFD into OBFD.  Raise an error if
-   there are conflicting attributes.  */
-static bfd_boolean
-ppc_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
+/* Warn for conflicting Tag_GNU_Power_ABI_FP attributes between IBFD
+   and OBFD, and merge non-conflicting ones.  */
+void
+_bfd_elf_ppc_merge_fp_attributes (bfd *ibfd, bfd *obfd)
 {
   obj_attribute *in_attr, *in_attrs;
   obj_attribute *out_attr, *out_attrs;
 
-  if (!elf_known_obj_attributes_proc (obfd)[0].i)
-    {
-      /* This is the first object.  Copy the attributes.  */
-      _bfd_elf_copy_obj_attributes (ibfd, obfd);
-
-      /* Use the Tag_null value to indicate the attributes have been
-        initialized.  */
-      elf_known_obj_attributes_proc (obfd)[0].i = 1;
-
-      return TRUE;
-    }
-
   in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
   out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
 
-  /* Check for conflicting Tag_GNU_Power_ABI_FP attributes and merge
-     non-conflicting ones.  */
   in_attr = &in_attrs[Tag_GNU_Power_ABI_FP];
   out_attr = &out_attrs[Tag_GNU_Power_ABI_FP];
+
   if (in_attr->i != out_attr->i)
     {
-      out_attr->type = 1;
-      if (out_attr->i == 0)
-       out_attr->i = in_attr->i;
-      else if (in_attr->i == 0)
+      int in_fp = in_attr->i & 3;
+      int out_fp = out_attr->i & 3;
+
+      if (in_fp == 0)
        ;
-      else if (out_attr->i == 1 && in_attr->i == 2)
+      else if (out_fp == 0)
+       {
+         out_attr->type = 1;
+         out_attr->i ^= in_fp;
+       }
+      else if (out_fp != 2 && in_fp == 2)
        _bfd_error_handler
          (_("Warning: %B uses hard float, %B uses soft float"), obfd, ibfd);
-      else if (out_attr->i == 1 && in_attr->i == 3)
+      else if (out_fp == 2 && in_fp != 2)
+       _bfd_error_handler
+         (_("Warning: %B uses hard float, %B uses soft float"), ibfd, obfd);
+      else if (out_fp == 1 && in_fp == 3)
        _bfd_error_handler
-         (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
-         obfd, ibfd);
-      else if (out_attr->i == 3 && in_attr->i == 1)
+         (_("Warning: %B uses double-precision hard float, "
+            "%B uses single-precision hard float"), obfd, ibfd);
+      else if (out_fp == 3 && in_fp == 1)
        _bfd_error_handler
-         (_("Warning: %B uses double-precision hard float, %B uses single-precision hard float"),
-         ibfd, obfd);
-      else if (out_attr->i == 3 && in_attr->i == 2)
+         (_("Warning: %B uses double-precision hard float, "
+            "%B uses single-precision hard float"), ibfd, obfd);
+
+      in_fp = in_attr->i & 0xc;
+      out_fp = out_attr->i & 0xc;
+      if (in_fp == 0)
+       ;
+      else if (out_fp == 0)
+       {
+         out_attr->type = 1;
+         out_attr->i ^= in_fp;
+       }
+      else if (out_fp != 2 * 4 && in_fp == 2 * 4)
        _bfd_error_handler
-         (_("Warning: %B uses soft float, %B uses single-precision hard float"),
-         ibfd, obfd);
-      else if (out_attr->i == 2 && (in_attr->i == 1 || in_attr->i == 3))
+         (_("Warning: %B uses 64-bit long double, "
+            "%B uses 128-bit long double"), ibfd, obfd);
+      else if (in_fp != 2 * 4 && out_fp == 2 * 4)
        _bfd_error_handler
-         (_("Warning: %B uses hard float, %B uses soft float"), ibfd, obfd);
-      else if (in_attr->i > 3)
+         (_("Warning: %B uses 64-bit long double, "
+            "%B uses 128-bit long double"), obfd, ibfd);
+      else if (out_fp == 1 * 4 && in_fp == 3 * 4)
        _bfd_error_handler
-         (_("Warning: %B uses unknown floating point ABI %d"), ibfd,
-          in_attr->i);
-      else
+         (_("Warning: %B uses IBM long double, "
+            "%B uses IEEE long double"), ibfd, obfd);
+      else if (out_fp == 3 * 4 && in_fp == 1 * 4)
        _bfd_error_handler
-         (_("Warning: %B uses unknown floating point ABI %d"), obfd,
-          out_attr->i);
+         (_("Warning: %B uses IBM long double, "
+            "%B uses IEEE long double"), obfd, ibfd);
     }
+}
+
+/* Merge object attributes from IBFD into OBFD.  Warn if
+   there are conflicting attributes.  */
+static bfd_boolean
+ppc_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
+{
+  obj_attribute *in_attr, *in_attrs;
+  obj_attribute *out_attr, *out_attrs;
+
+  _bfd_elf_ppc_merge_fp_attributes (ibfd, obfd);
+
+  in_attrs = elf_known_obj_attributes (ibfd)[OBJ_ATTR_GNU];
+  out_attrs = elf_known_obj_attributes (obfd)[OBJ_ATTR_GNU];
 
   /* Check for conflicting Tag_GNU_Power_ABI_Vector attributes and
      merge non-conflicting ones.  */
@@ -4618,48 +4741,36 @@ ppc_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
   out_attr = &out_attrs[Tag_GNU_Power_ABI_Vector];
   if (in_attr->i != out_attr->i)
     {
-      const char *in_abi = NULL, *out_abi = NULL;
-
-      switch (in_attr->i)
-       {
-       case 1: in_abi = "generic"; break;
-       case 2: in_abi = "AltiVec"; break;
-       case 3: in_abi = "SPE"; break;
-       }
+      int in_vec = in_attr->i & 3;
+      int out_vec = out_attr->i & 3;
 
-      switch (out_attr->i)
+      if (in_vec == 0)
+       ;
+      else if (out_vec == 0)
        {
-       case 1: out_abi = "generic"; break;
-       case 2: out_abi = "AltiVec"; break;
-       case 3: out_abi = "SPE"; break;
+         out_attr->type = 1;
+         out_attr->i = in_vec;
        }
-
-      out_attr->type = 1;
-      if (out_attr->i == 0)
-       out_attr->i = in_attr->i;
-      else if (in_attr->i == 0)
-       ;
       /* For now, allow generic to transition to AltiVec or SPE
         without a warning.  If GCC marked files with their stack
         alignment and used don't-care markings for files which are
         not affected by the vector ABI, we could warn about this
         case too.  */
-      else if (out_attr->i == 1)
-       out_attr->i = in_attr->i;
-      else if (in_attr->i == 1)
+      else if (in_vec == 1)
        ;
-      else if (in_abi == NULL)
-       _bfd_error_handler
-         (_("Warning: %B uses unknown vector ABI %d"), ibfd,
-          in_attr->i);
-      else if (out_abi == NULL)
+      else if (out_vec == 1)
+       {
+         out_attr->type = 1;
+         out_attr->i = in_vec;
+       }
+      else if (out_vec < in_vec)
        _bfd_error_handler
-         (_("Warning: %B uses unknown vector ABI %d"), obfd,
-          in_attr->i);
-      else
+         (_("Warning: %B uses AltiVec vector ABI, %B uses SPE vector ABI"),
+          obfd, ibfd);
+      else if (out_vec > in_vec)
        _bfd_error_handler
-         (_("Warning: %B uses vector ABI \"%s\", %B uses \"%s\""),
-          ibfd, obfd, in_abi, out_abi);
+         (_("Warning: %B uses AltiVec vector ABI, %B uses SPE vector ABI"),
+          ibfd, obfd);
     }
 
   /* Check for conflicting Tag_GNU_Power_ABI_Struct_Return attributes
@@ -4668,25 +4779,24 @@ ppc_elf_merge_obj_attributes (bfd *ibfd, bfd *obfd)
   out_attr = &out_attrs[Tag_GNU_Power_ABI_Struct_Return];
   if (in_attr->i != out_attr->i)
     {
-      out_attr->type = 1;
-      if (out_attr->i == 0)
-       out_attr->i = in_attr->i;
-      else if (in_attr->i == 0)
+      int in_struct = in_attr->i & 3;
+      int out_struct = out_attr->i & 3;
+
+      if (in_struct == 0 || in_struct == 3)
        ;
-      else if (out_attr->i == 1 && in_attr->i == 2)
-       _bfd_error_handler
-         (_("Warning: %B uses r3/r4 for small structure returns, %B uses memory"), obfd, ibfd);
-      else if (out_attr->i == 2 && in_attr->i == 1)
-       _bfd_error_handler
-         (_("Warning: %B uses r3/r4 for small structure returns, %B uses memory"), ibfd, obfd);
-      else if (in_attr->i > 2)
-       _bfd_error_handler
-         (_("Warning: %B uses unknown small structure return convention %d"), ibfd,
-          in_attr->i);
-      else
-       _bfd_error_handler
-         (_("Warning: %B uses unknown small structure return convention %d"), obfd,
-          out_attr->i);
+      else if (out_struct == 0)
+       {
+         out_attr->type = 1;
+         out_attr->i = in_struct;
+       }
+      else if (out_struct < in_struct)
+       _bfd_error_handler
+         (_("Warning: %B uses r3/r4 for small structure returns, "
+            "%B uses memory"), obfd, ibfd);
+      else if (out_struct > in_struct)
+       _bfd_error_handler
+         (_("Warning: %B uses r3/r4 for small structure returns, "
+            "%B uses memory"), ibfd, obfd);
     }
 
   /* Merge Tag_compatibility attributes and any common GNU ones.  */
@@ -4738,7 +4848,7 @@ ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
          && (old_flags & (EF_PPC_RELOCATABLE | EF_PPC_RELOCATABLE_LIB)) == 0)
        {
          error = TRUE;
-         (*_bfd_error_handler)
+         _bfd_error_handler
            (_("%B: compiled with -mrelocatable and linked with "
               "modules compiled normally"), ibfd);
        }
@@ -4746,7 +4856,7 @@ ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
               && (old_flags & EF_PPC_RELOCATABLE) != 0)
        {
          error = TRUE;
-         (*_bfd_error_handler)
+         _bfd_error_handler
            (_("%B: compiled normally and linked with "
               "modules compiled with -mrelocatable"), ibfd);
        }
@@ -4773,7 +4883,7 @@ ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
       if (new_flags != old_flags)
        {
          error = TRUE;
-         (*_bfd_error_handler)
+         _bfd_error_handler
            (_("%B: uses different e_flags (0x%lx) fields "
               "than previous modules (0x%lx)"),
             ibfd, (long) new_flags, (long) old_flags);
@@ -4790,20 +4900,20 @@ ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
 }
 
 static void
-ppc_elf_vle_split16 (bfd *output_bfd, bfd_byte *contents,
-                 bfd_vma offset, bfd_vma relocation,
-                split16_format_type split16_format)
+ppc_elf_vle_split16 (bfd *output_bfd, bfd_byte *loc,
+                    bfd_vma value,
+                    split16_format_type split16_format)
 
 {
-  bfd_vma insn, top5, bottom11;
+  unsigned int insn, top5;
 
-  insn = bfd_get_32 (output_bfd, contents + offset);
-  top5 = relocation >> 11;
-  top5 = top5 << (split16_format == split16a_type ? 20 : 16);
-  bottom11 = relocation & 0x7ff;
+  insn = bfd_get_32 (output_bfd, loc);
+  top5 = value & 0xf800;
+  top5 = top5 << (split16_format == split16a_type ? 9 : 5);
+  insn &= (split16_format == split16a_type ? ~0x1f007ff : ~0x1f07ff);
   insn |= top5;
-  insn |= bottom11;
-  bfd_put_32 (output_bfd, insn, contents + offset);
+  insn |= value & 0x7ff;
+  bfd_put_32 (output_bfd, insn, loc);
 }
 
 \f
@@ -4824,7 +4934,7 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       if (htab->params->plt_style == PLT_OLD)
        htab->plt_type = PLT_OLD;
-      else if (info->shared
+      else if (bfd_link_pic (info)
               && htab->elf.dynamic_sections_created
               && (h = elf_link_hash_lookup (&htab->elf, "_mcount",
                                             FALSE, FALSE, TRUE)) != NULL
@@ -4852,7 +4962,7 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
             --secure-plt and we never see REL16 relocs.  */
          if (plt_type == PLT_UNSET)
            plt_type = PLT_OLD;
-         for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link_next)
+         for (ibfd = info->input_bfds; ibfd; ibfd = ibfd->link.next)
            if (is_ppc_elf (ibfd))
              {
                if (ppc_elf_tdata (ibfd)->has_rel16)
@@ -4940,7 +5050,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
   const Elf_Internal_Rela *rel, *relend;
   asection *got2;
 
-  if (info->relocatable)
+  if (bfd_link_relocatable (info))
     return TRUE;
 
   if ((sec->flags & SEC_ALLOC) == 0)
@@ -4986,7 +5096,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
       if (!htab->is_vxworks
          && h == NULL
          && local_got_refcounts != NULL
-         && (!info->shared
+         && (!bfd_link_pic (info)
              || is_branch_reloc (r_type)))
        {
          struct plt_entry **local_plt = (struct plt_entry **)
@@ -4999,7 +5109,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
              bfd_vma addend = 0;
              struct plt_entry *ent;
 
-             if (r_type == R_PPC_PLTREL24 && info->shared)
+             if (r_type == R_PPC_PLTREL24 && bfd_link_pic (info))
                addend = rel->r_addend;
              ent = find_plt_ent (ifunc, got2, addend);
              if (ent->plt.refcount > 0)
@@ -5034,7 +5144,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
            {
              if (h->got.refcount > 0)
                h->got.refcount--;
-             if (!info->shared)
+             if (!bfd_link_pic (info))
                {
                  struct plt_entry *ent;
 
@@ -5070,7 +5180,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
        case R_PPC_ADDR14_BRNTAKEN:
        case R_PPC_UADDR32:
        case R_PPC_UADDR16:
-         if (info->shared)
+         if (bfd_link_pic (info))
            break;
 
        case R_PPC_PLT32:
@@ -5084,7 +5194,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
              bfd_vma addend = 0;
              struct plt_entry *ent;
 
-             if (r_type == R_PPC_PLTREL24 && info->shared)
+             if (r_type == R_PPC_PLTREL24 && bfd_link_pic (info))
                addend = rel->r_addend;
              ent = find_plt_ent (&h->plt.plist, got2, addend);
              if (ent != NULL && ent->plt.refcount > 0)
@@ -5110,6 +5220,9 @@ ppc_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
   htab = ppc_elf_hash_table (info);
   htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
                                             FALSE, FALSE, TRUE);
+  if (htab->plt_type != PLT_NEW)
+    htab->params->no_tls_get_addr_opt = TRUE;
+
   if (!htab->params->no_tls_get_addr_opt)
     {
       struct elf_link_hash_entry *opt, *tga;
@@ -5141,6 +5254,7 @@ ppc_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
                  tga->root.type = bfd_link_hash_indirect;
                  tga->root.u.i.link = &opt->root;
                  ppc_elf_copy_indirect_symbol (info, opt, tga);
+                 opt->forced_local = 0;
                  if (opt->dynindx != -1)
                    {
                      /* Use __tls_get_addr_opt in dynamic relocations.  */
@@ -5207,7 +5321,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
   struct ppc_elf_link_hash_table *htab;
   int pass;
 
-  if (info->relocatable || !info->executable)
+  if (!bfd_link_executable (info))
     return TRUE;
 
   htab = ppc_elf_hash_table (info);
@@ -5221,7 +5335,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
      notify relocate_section that optimization can be done, and
      adjust got and plt refcounts.  */
   for (pass = 0; pass < 2; ++pass)
-    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+    for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
       {
        Elf_Internal_Sym *locsyms = NULL;
        Elf_Internal_Shdr *symtab_hdr = &elf_symtab_hdr (ibfd);
@@ -5380,7 +5494,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                      struct plt_entry *ent;
                      bfd_vma addend = 0;
 
-                     if (info->shared
+                     if (bfd_link_pic (info)
                          && ELF32_R_TYPE (rel[1].r_info) == R_PPC_PLTREL24)
                        addend = rel[1].r_addend;
                      ent = find_plt_ent (&htab->tls_get_addr->plt.plist,
@@ -5530,9 +5644,24 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
             will go to this object, or will remain undefined.  */
          h->plt.plist = NULL;
          h->needs_plt = 0;
+         h->pointer_equality_needed = 0;
        }
       else
        {
+         /* Taking a function's address in a read/write section
+            doesn't require us to define the function symbol in the
+            executable on a plt call stub.  A dynamic reloc can
+            be used instead.  */
+         if (h->pointer_equality_needed
+             && h->type != STT_GNU_IFUNC
+             && !htab->is_vxworks
+             && !ppc_elf_hash_entry (h)->has_sda_refs
+             && !readonly_dynrelocs (h))
+           {
+             h->pointer_equality_needed = 0;
+             h->non_got_ref = 0;
+           }
+
          /* After adjust_dynamic_symbol, non_got_ref set in the
             non-shared case means that we have allocated space in
             .dynbss for the symbol and thus dyn_relocs for this
@@ -5542,14 +5671,15 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
             relocations against this symbol to the PLT entry.  Allow
             dynamic relocs if the reference is weak, and the dynamic
             relocs will not cause text relocation.  */
-         if (!h->ref_regular_nonweak
-             && h->non_got_ref
-             && h->type != STT_GNU_IFUNC
-             && !htab->is_vxworks
-             && !ppc_elf_hash_entry (h)->has_sda_refs
-             && !readonly_dynrelocs (h))
+         else if (!h->ref_regular_nonweak
+                  && h->non_got_ref
+                  && h->type != STT_GNU_IFUNC
+                  && !htab->is_vxworks
+                  && !ppc_elf_hash_entry (h)->has_sda_refs
+                  && !readonly_dynrelocs (h))
            h->non_got_ref = 0;
        }
+      h->protected_def = 0;
       return TRUE;
     }
   else
@@ -5576,13 +5706,42 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      only references to the symbol are via the global offset table.
      For such cases we need not do anything here; the relocations will
      be handled correctly by relocate_section.  */
-  if (info->shared)
-    return TRUE;
+  if (bfd_link_pic (info))
+    {
+      h->protected_def = 0;
+      return TRUE;
+    }
 
   /* If there are no references to this symbol that do not use the
      GOT, we don't need to generate a copy reloc.  */
   if (!h->non_got_ref)
-    return TRUE;
+    {
+      h->protected_def = 0;
+      return TRUE;
+    }
+
+  /* Protected variables do not work with .dynbss.  The copy in
+     .dynbss won't be used by the shared library with the protected
+     definition for the variable.  Editing to PIC, or text relocations
+     are preferable to an incorrect program.  */
+  if (h->protected_def)
+    {
+      if (ELIMINATE_COPY_RELOCS
+         && ppc_elf_hash_entry (h)->has_addr16_ha
+         && ppc_elf_hash_entry (h)->has_addr16_lo
+         && htab->params->pic_fixup == 0
+         && info->disable_target_specific_optimizations <= 1)
+       htab->params->pic_fixup = 1;
+      h->non_got_ref = 0;
+      return TRUE;
+    }
+
+  /* If -z nocopyreloc was given, we won't generate them either.  */
+  if (info->nocopyreloc)
+    {
+      h->non_got_ref = 0;
+      return TRUE;
+    }
 
    /* If we didn't find any dynamic relocs in read-only sections, then
       we'll be keeping the dynamic relocs and avoiding the copy reloc.
@@ -5636,7 +5795,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       h->needs_copy = 1;
     }
 
-  return _bfd_elf_adjust_dynamic_copy (h, s);
+  return _bfd_elf_adjust_dynamic_copy (info, h, s);
 }
 \f
 /* Generate a symbol to mark plt call stubs.  For non-PIC code the sym is
@@ -5656,7 +5815,7 @@ add_stub_sym (struct plt_entry *ent,
   const char *stub;
   struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
 
-  if (info->shared)
+  if (bfd_link_pic (info))
     stub = ".plt_pic32.";
   else
     stub = ".plt_call32.";
@@ -5687,6 +5846,7 @@ add_stub_sym (struct plt_entry *ent,
       sh->ref_regular_nonweak = 1;
       sh->forced_local = 1;
       sh->non_elf = 0;
+      sh->root.linker_def = 1;
     }
   return TRUE;
 }
@@ -5764,7 +5924,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
              }
 
            dyn = htab->elf.dynamic_sections_created;
-           if (info->shared
+           if (bfd_link_pic (info)
                || h->type == STT_GNU_IFUNC
                || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))
              {
@@ -5782,7 +5942,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                    ent->plt.offset = plt_offset;
 
                    s = htab->glink;
-                   if (!doneone || info->shared)
+                   if (!doneone || bfd_link_pic (info))
                      {
                        glink_offset = s->size;
                        s->size += GLINK_ENTRY_SIZE;
@@ -5791,7 +5951,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                          s->size += TLS_GET_ADDR_GLINK_SIZE - GLINK_ENTRY_SIZE;
                      }
                    if (!doneone
-                       && !info->shared
+                       && !bfd_link_pic (info)
                        && h->def_dynamic
                        && !h->def_regular)
                      {
@@ -5830,7 +5990,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                           relocations, and is required to make
                           function pointers compare as equal between
                           the normal executable and the shared library.  */
-                       if (! info->shared
+                       if (! bfd_link_pic (info)
                            && h->def_dynamic
                            && !h->def_regular)
                          {
@@ -5864,7 +6024,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                        if (htab->plt_type == PLT_VXWORKS)
                          {
                            /* Allocate space for the unloaded relocations.  */
-                           if (!info->shared
+                           if (!bfd_link_pic (info)
                                && htab->elf.dynamic_sections_created)
                              {
                                if (ent->plt.offset
@@ -5907,7 +6067,13 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     }
 
   eh = (struct ppc_elf_link_hash_entry *) h;
-  if (eh->elf.got.refcount > 0)
+  if (eh->elf.got.refcount > 0
+      || (ELIMINATE_COPY_RELOCS
+         && !eh->elf.def_regular
+         && eh->elf.protected_def
+         && eh->has_addr16_ha
+         && eh->has_addr16_lo
+         && htab->params->pic_fixup > 0))
     {
       bfd_boolean dyn;
       unsigned int need;
@@ -5950,7 +6116,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
        {
          eh->elf.got.offset = allocate_got (htab, need);
          dyn = htab->elf.dynamic_sections_created;
-         if ((info->shared
+         if ((bfd_link_pic (info)
               || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, &eh->elf))
              && (ELF_ST_VISIBILITY (eh->elf.other) == STV_DEFAULT
                  || eh->elf.root.type != bfd_link_hash_undefweak))
@@ -5981,7 +6147,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
      space for relocs that have become local due to symbol visibility
      changes.  */
 
-  if (info->shared)
+  if (bfd_link_pic (info))
     {
       /* Relocs that use pc_count are those that appear on a call insn,
         or certain REL relocs (see must_be_dyn_reloc) that can be
@@ -6050,7 +6216,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
         dynamic.  */
 
       if (!h->non_got_ref
-         && !h->def_regular)
+         && !h->def_regular
+         && !(h->protected_def
+              && eh->has_addr16_ha
+              && eh->has_addr16_lo
+              && htab->params->pic_fixup > 0))
        {
          /* Make sure this symbol is output as a dynamic symbol.
             Undefined weak syms won't yet be marked as dynamic.  */
@@ -6120,7 +6290,7 @@ static const unsigned char glink_eh_frame_cie[] =
 /* Set the sizes of the dynamic sections.  */
 
 static bfd_boolean
-ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
+ppc_elf_size_dynamic_sections (bfd *output_bfd,
                               struct bfd_link_info *info)
 {
   struct ppc_elf_link_hash_table *htab;
@@ -6138,7 +6308,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   if (elf_hash_table (info)->dynamic_sections_created)
     {
       /* Set the contents of the .interp section to the interpreter.  */
-      if (info->executable)
+      if (bfd_link_executable (info) && !info->nointerp)
        {
          s = bfd_get_linker_section (htab->elf.dynobj, ".interp");
          BFD_ASSERT (s != NULL);
@@ -6154,7 +6324,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
-  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
+  for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
     {
       bfd_signed_vma *local_got;
       bfd_signed_vma *end_local_got;
@@ -6238,7 +6408,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
            else
              {
                *local_got = allocate_got (htab, need);
-               if (info->shared)
+               if (bfd_link_pic (info))
                  {
                    asection *srel = htab->relgot;
                    if ((*lgot_masks & PLT_IFUNC) != 0)
@@ -6273,7 +6443,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
                ent->plt.offset = plt_offset;
 
                s = htab->glink;
-               if (!doneone || info->shared)
+               if (!doneone || bfd_link_pic (info))
                  {
                    glink_offset = s->size;
                    s->size += GLINK_ENTRY_SIZE;
@@ -6297,7 +6467,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   if (htab->tlsld_got.refcount > 0)
     {
       htab->tlsld_got.offset = allocate_got (htab, 8);
-      if (info->shared)
+      if (bfd_link_pic (info))
        htab->relgot->size += sizeof (Elf32_External_Rela);
     }
   else
@@ -6321,17 +6491,22 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       htab->elf.hgot->root.u.def.value = g_o_t;
     }
-  if (info->shared)
+  if (bfd_link_pic (info))
     {
       struct elf_link_hash_entry *sda = htab->sdata[0].sym;
-      if (sda != NULL
-         && !(sda->root.type == bfd_link_hash_defined
-              || sda->root.type == bfd_link_hash_defweak))
-       {
-         sda->root.type = bfd_link_hash_defined;
-         sda->root.u.def.section = htab->elf.hgot->root.u.def.section;
-         sda->root.u.def.value = htab->elf.hgot->root.u.def.value;
-       }
+
+      sda->root.u.def.section = htab->elf.hgot->root.u.def.section;
+      sda->root.u.def.value = htab->elf.hgot->root.u.def.value;
+    }
+  if (info->emitrelocations)
+    {
+      struct elf_link_hash_entry *sda = htab->sdata[0].sym;
+
+      if (sda != NULL && sda->ref_regular)
+       sda->root.u.def.section->flags |= SEC_KEEP;
+      sda = htab->sdata[1].sym;
+      if (sda != NULL && sda->ref_regular)
+       sda->root.u.def.section->flags |= SEC_KEEP;
     }
 
   if (htab->glink != NULL
@@ -6339,7 +6514,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       && htab->elf.dynamic_sections_created)
     {
       htab->glink_pltresolve = htab->glink->size;
-      /* Space for the branch table.  */
+      /* Space for the branch table.  ??? We don't need entries for
+        non-dynamic symbols in this table.  This case can arise with
+        static ifuncs or forced local ifuncs.  */
       htab->glink->size += htab->glink->size / (GLINK_ENTRY_SIZE / 4) - 4;
       /* Pad out to align the start of PLTresolve.  */
       htab->glink->size += -htab->glink->size & (htab->params->ppc476_workaround
@@ -6363,6 +6540,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
              sh->ref_regular_nonweak = 1;
              sh->forced_local = 1;
              sh->non_elf = 0;
+             sh->root.linker_def = 1;
            }
          sh = elf_link_hash_lookup (&htab->elf, "__glink_PLTresolve",
                                     TRUE, FALSE, FALSE);
@@ -6378,6 +6556,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
              sh->ref_regular_nonweak = 1;
              sh->forced_local = 1;
              sh->non_elf = 0;
+             sh->root.linker_def = 1;
            }
        }
     }
@@ -6390,7 +6569,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
     {
       s = htab->glink_eh_frame;
       s->size = sizeof (glink_eh_frame_cie) + 20;
-      if (info->shared)
+      if (bfd_link_pic (info))
        {
          s->size += 4;
          if (htab->glink->size - GLINK_PLTRESOLVE + 8 >= 256)
@@ -6425,12 +6604,15 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
               || s == htab->sgotplt
               || s == htab->sbss
               || s == htab->dynbss
-              || s == htab->dynsbss
-              || s == htab->sdata[0].section
-              || s == htab->sdata[1].section)
+              || s == htab->dynsbss)
        {
          /* Strip these too.  */
        }
+      else if (s == htab->sdata[0].section
+              || s == htab->sdata[1].section)
+       {
+         strip_section = (s->flags & SEC_KEEP) == 0;
+       }
       else if (CONST_STRNEQ (bfd_get_section_name (htab->elf.dynobj, s),
                             ".rela"))
        {
@@ -6484,7 +6666,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 #define add_dynamic_entry(TAG, VAL) \
   _bfd_elf_add_dynamic_entry (info, TAG, VAL)
 
-      if (info->executable)
+      if (bfd_link_executable (info))
        {
          if (!add_dynamic_entry (DT_DEBUG, 0))
            return FALSE;
@@ -6563,7 +6745,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       /* Augmentation.  */
       p += 1;
 
-      if (info->shared
+      if (bfd_link_pic (info)
          && htab->elf.dynamic_sections_created)
        {
          bfd_vma adv = (htab->glink->size - GLINK_PLTRESOLVE + 8) >> 2;
@@ -6600,6 +6782,46 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
+/* Arrange to have _SDA_BASE_ or _SDA2_BASE_ stripped from the output
+   if it looks like nothing is using them.  */
+
+static void
+maybe_strip_sdasym (bfd *output_bfd, elf_linker_section_t *lsect)
+{
+  struct elf_link_hash_entry *sda = lsect->sym;
+
+  if (sda != NULL && !sda->ref_regular && sda->dynindx == -1)
+    {
+      asection *s;
+
+      s = bfd_get_section_by_name (output_bfd, lsect->name);
+      if (s == NULL || bfd_section_removed_from_list (output_bfd, s))
+       {
+         s = bfd_get_section_by_name (output_bfd, lsect->bss_name);
+         if (s == NULL || bfd_section_removed_from_list (output_bfd, s))
+           {
+             sda->def_regular = 0;
+             /* This is somewhat magic.  See elf_link_output_extsym.  */
+             sda->ref_dynamic = 1;
+             sda->forced_local = 0;
+           }
+       }
+    }
+}
+
+void
+ppc_elf_maybe_strip_sdata_syms (struct bfd_link_info *info)
+{
+  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+  if (htab != NULL)
+    {
+      maybe_strip_sdasym (info->output_bfd, &htab->sdata[0]);
+      maybe_strip_sdasym (info->output_bfd, &htab->sdata[1]);
+    }
+}
+
+
 /* Return TRUE if symbol should be hashed in the `.gnu.hash' section.  */
 
 static bfd_boolean
@@ -6641,6 +6863,7 @@ static const int stub_entry[] =
 struct ppc_elf_relax_info
 {
   unsigned int workaround_size;
+  unsigned int picfixup_size;
 };
 
 /* This function implements long branch trampolines, and the ppc476
@@ -6654,9 +6877,9 @@ ppc_elf_relax_section (bfd *abfd,
                       struct bfd_link_info *link_info,
                       bfd_boolean *again)
 {
-  struct one_fixup
+  struct one_branch_fixup
   {
-    struct one_fixup *next;
+    struct one_branch_fixup *next;
     asection *tsec;
     /* Final link, can use the symbol offset.  For a
        relocatable link we use the symbol's index.  */
@@ -6669,12 +6892,12 @@ ppc_elf_relax_section (bfd *abfd,
   Elf_Internal_Sym *isymbuf = NULL;
   Elf_Internal_Rela *internal_relocs = NULL;
   Elf_Internal_Rela *irel, *irelend = NULL;
-  struct one_fixup *fixups = NULL;
+  struct one_branch_fixup *branch_fixups = NULL;
   struct ppc_elf_relax_info *relax_info = NULL;
   unsigned changes = 0;
   bfd_boolean workaround_change;
   struct ppc_elf_link_hash_table *htab;
-  bfd_size_type trampbase, trampoff, newsize;
+  bfd_size_type trampbase, trampoff, newsize, picfixup_size;
   asection *got2;
   bfd_boolean maybe_pasted;
 
@@ -6690,7 +6913,7 @@ ppc_elf_relax_section (bfd *abfd,
   /* We cannot represent the required PIC relocs in the output, so don't
      do anything.  The linker doesn't support mixing -shared and -r
      anyway.  */
-  if (link_info->relocatable && link_info->shared)
+  if (bfd_link_relocatable (link_info) && bfd_link_pic (link_info))
     return TRUE;
 
   htab = ppc_elf_hash_table (link_info);
@@ -6706,7 +6929,8 @@ ppc_elf_relax_section (bfd *abfd,
              || isec->sec_info_type == SEC_INFO_TYPE_TARGET);
   isec->sec_info_type = SEC_INFO_TYPE_TARGET;
 
-  if (htab->params->ppc476_workaround)
+  if (htab->params->ppc476_workaround
+      || htab->params->pic_fixup > 0)
     {
       if (elf_section_data (isec)->sec_info == NULL)
        {
@@ -6727,8 +6951,9 @@ ppc_elf_relax_section (bfd *abfd,
     trampoff += 4;
 
   symtab_hdr = &elf_symtab_hdr (abfd);
-
-  if (htab->params->branch_trampolines)
+  picfixup_size = 0;
+  if (htab->params->branch_trampolines
+      || htab->params->pic_fixup > 0)
     {
       /* Get a copy of the native relocations.  */
       if (isec->reloc_count != 0)
@@ -6747,9 +6972,9 @@ ppc_elf_relax_section (bfd *abfd,
          unsigned long r_type = ELF32_R_TYPE (irel->r_info);
          bfd_vma toff, roff;
          asection *tsec;
-         struct one_fixup *f;
+         struct one_branch_fixup *f;
          size_t insn_offset = 0;
-         bfd_vma max_branch_offset, val;
+         bfd_vma max_branch_offset = 0, val;
          bfd_byte *hit_addr;
          unsigned long t0;
          struct elf_link_hash_entry *h;
@@ -6770,6 +6995,11 @@ ppc_elf_relax_section (bfd *abfd,
              max_branch_offset = 1 << 15;
              break;
 
+           case R_PPC_ADDR16_HA:
+             if (htab->params->pic_fixup > 0)
+               break;
+             continue;
+
            default:
              continue;
            }
@@ -6827,7 +7057,7 @@ ppc_elf_relax_section (bfd *abfd,
                       || h->root.type == bfd_link_hash_undefweak)
                {
                  tsec = bfd_und_section_ptr;
-                 toff = link_info->relocatable ? indx : 0;
+                 toff = bfd_link_relocatable (link_info) ? indx : 0;
                }
              else
                continue;
@@ -6835,8 +7065,7 @@ ppc_elf_relax_section (bfd *abfd,
              /* If this branch is to __tls_get_addr then we may later
                 optimise away the call.  We won't be needing a long-
                 branch stub in that case.  */
-             if (link_info->executable
-                 && !link_info->relocatable
+             if (bfd_link_executable (link_info)
                  && h == htab->tls_get_addr
                  && irel != internal_relocs)
                {
@@ -6891,6 +7120,17 @@ ppc_elf_relax_section (bfd *abfd,
              sym_type = h->type;
            }
 
+         if (r_type == R_PPC_ADDR16_HA)
+           {
+             if (h != NULL
+                 && !h->def_regular
+                 && h->protected_def
+                 && ppc_elf_hash_entry (h)->has_addr16_ha
+                 && ppc_elf_hash_entry (h)->has_addr16_lo)
+               picfixup_size += 12;
+             continue;
+           }
+
          /* The condition here under which we call find_plt_ent must
             match that in relocate_section.  If we call find_plt_ent here
             but not in relocate_section, or vice versa, then the branch
@@ -6916,7 +7156,7 @@ ppc_elf_relax_section (bfd *abfd,
              bfd_vma addend = 0;
              struct plt_entry *ent;
 
-             if (r_type == R_PPC_PLTREL24 && link_info->shared)
+             if (r_type == R_PPC_PLTREL24 && bfd_link_pic (link_info))
                addend = irel->r_addend;
              ent = find_plt_ent (plist, got2, addend);
              if (ent != NULL)
@@ -6982,7 +7222,7 @@ ppc_elf_relax_section (bfd *abfd,
            toff += irel->r_addend;
 
          /* Attempted -shared link of non-pic code loses.  */
-         if ((!link_info->relocatable
+         if ((!bfd_link_relocatable (link_info)
               && tsec == bfd_und_section_ptr)
              || tsec->output_section == NULL
              || (tsec->owner != NULL
@@ -6993,7 +7233,7 @@ ppc_elf_relax_section (bfd *abfd,
 
          /* If the branch is in range, no need to do anything.  */
          if (tsec != bfd_und_section_ptr
-             && (!link_info->relocatable
+             && (!bfd_link_relocatable (link_info)
                  /* A relocatable link may have sections moved during
                     final link, so do not presume they remain in range.  */
                  || tsec->output_section == isec->output_section))
@@ -7008,7 +7248,7 @@ ppc_elf_relax_section (bfd *abfd,
            }
 
          /* Look for an existing fixup to this address.  */
-         for (f = fixups; f ; f = f->next)
+         for (f = branch_fixups; f ; f = f->next)
            if (f->tsec == tsec && f->toff == toff)
              break;
 
@@ -7023,7 +7263,7 @@ ppc_elf_relax_section (bfd *abfd,
                   one.  We'll report an error later.  */
                continue;
 
-             if (link_info->shared)
+             if (bfd_link_pic (link_info))
                {
                  size = 4 * ARRAY_SIZE (shared_stub_entry);
                  insn_offset = 12;
@@ -7053,11 +7293,11 @@ ppc_elf_relax_section (bfd *abfd,
 
              /* Record the fixup so we don't do it again this section.  */
              f = bfd_malloc (sizeof (*f));
-             f->next = fixups;
+             f->next = branch_fixups;
              f->tsec = tsec;
              f->toff = toff;
              f->trampoff = trampoff;
-             fixups = f;
+             branch_fixups = f;
 
              trampoff += size;
              changes++;
@@ -7107,10 +7347,10 @@ ppc_elf_relax_section (bfd *abfd,
            }
        }
 
-      while (fixups != NULL)
+      while (branch_fixups != NULL)
        {
-         struct one_fixup *f = fixups;
-         fixups = fixups->next;
+         struct one_branch_fixup *f = branch_fixups;
+         branch_fixups = branch_fixups->next;
          free (f);
        }
     }
@@ -7118,7 +7358,7 @@ ppc_elf_relax_section (bfd *abfd,
   workaround_change = FALSE;
   newsize = trampoff;
   if (htab->params->ppc476_workaround
-      && (!link_info->relocatable
+      && (!bfd_link_relocatable (link_info)
          || isec->output_section->alignment_power >= htab->params->pagesize_p2))
     {
       bfd_vma addr, end_addr;
@@ -7126,7 +7366,7 @@ ppc_elf_relax_section (bfd *abfd,
       bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
 
       addr = isec->output_section->vma + isec->output_offset;
-      end_addr = addr + trampoff - 1;
+      end_addr = addr + trampoff;
       addr &= -pagesize;
       crossings = ((end_addr & -pagesize) - addr) >> htab->params->pagesize_p2;
       if (crossings != 0)
@@ -7134,7 +7374,7 @@ ppc_elf_relax_section (bfd *abfd,
          /* Keep space aligned, to ensure the patch code itself does
             not cross a page.  Don't decrease size calculated on a
             previous pass as otherwise we might never settle on a layout.  */
-         newsize = 15 - (end_addr & 15);
+         newsize = 15 - ((end_addr - 1) & 15);
          newsize += crossings * 16;
          if (relax_info->workaround_size < newsize)
            {
@@ -7147,7 +7387,15 @@ ppc_elf_relax_section (bfd *abfd,
       newsize = trampoff + relax_info->workaround_size;
     }
 
-  if (changes || workaround_change)
+  if (htab->params->pic_fixup > 0)
+    {
+      picfixup_size -= relax_info->picfixup_size;
+      if (picfixup_size != 0)
+       relax_info->picfixup_size += picfixup_size;
+      newsize += relax_info->picfixup_size;
+    }
+
+  if (changes != 0 || picfixup_size != 0 || workaround_change)
     isec->size = newsize;
 
   if (isymbuf != NULL
@@ -7174,6 +7422,7 @@ ppc_elf_relax_section (bfd *abfd,
        }
     }
 
+  changes += picfixup_size;
   if (changes != 0)
     {
       /* Append sufficient NOP relocs so we can write out relocation
@@ -7208,10 +7457,10 @@ ppc_elf_relax_section (bfd *abfd,
   return TRUE;
 
  error_return:
-  while (fixups != NULL)
+  while (branch_fixups != NULL)
     {
-      struct one_fixup *f = fixups;
-      fixups = fixups->next;
+      struct one_branch_fixup *f = branch_fixups;
+      branch_fixups = branch_fixups->next;
       free (f);
     }
   if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents)
@@ -7317,7 +7566,7 @@ write_glink_stub (struct plt_entry *ent, asection *plt_sec, unsigned char *p,
         + plt_sec->output_section->vma
         + plt_sec->output_offset);
 
-  if (info->shared)
+  if (bfd_link_pic (info))
     {
       bfd_vma got = 0;
 
@@ -7338,7 +7587,7 @@ write_glink_stub (struct plt_entry *ent, asection *plt_sec, unsigned char *p,
          p += 4;
          bfd_put_32 (output_bfd, BCTR, p);
          p += 4;
-         bfd_put_32 (output_bfd, NOP, p);
+         bfd_put_32 (output_bfd, htab->params->ppc476_workaround ? BA : NOP, p);
          p += 4;
        }
       else
@@ -7474,7 +7723,9 @@ is_insn_ds_form (unsigned int insn)
 static bfd_boolean
 is_insn_dq_form (unsigned int insn)
 {
-  return (insn & (0x3f << 26)) == 56u << 26; /* lq */
+  return ((insn & (0x3f << 26)) == 56u << 26 /* lq */
+         || ((insn & (0x3f << 26)) == (61u << 26) /* lxv, stxv */
+             && (insn & 3) == 1));
 }
 
 /* The RELOCATE_SECTION function is called by the ELF backend linker
@@ -7520,6 +7771,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
   struct elf_link_hash_entry **sym_hashes;
   struct ppc_elf_link_hash_table *htab;
   Elf_Internal_Rela *rel;
+  Elf_Internal_Rela *wrel;
   Elf_Internal_Rela *relend;
   Elf_Internal_Rela outrel;
   asection *got2;
@@ -7527,13 +7779,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
   bfd_boolean ret = TRUE;
   bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
   bfd_boolean is_vxworks_tls;
+  unsigned int picfixup_size = 0;
+  struct ppc_elf_relax_info *relax_info = NULL;
 
 #ifdef DEBUG
   _bfd_error_handler ("ppc_elf_relocate_section called for %B section %A, "
                      "%ld relocations%s",
                      input_bfd, input_section,
                      (long) input_section->reloc_count,
-                     (info->relocatable) ? " (relocatable)" : "");
+                     (bfd_link_relocatable (info)) ? " (relocatable)" : "");
 #endif
 
   got2 = bfd_get_section_by_name (input_bfd, ".got2");
@@ -7548,12 +7802,14 @@ ppc_elf_relocate_section (bfd *output_bfd,
   sym_hashes = elf_sym_hashes (input_bfd);
   /* We have to handle relocations in vxworks .tls_vars sections
      specially, because the dynamic loader is 'weird'.  */
-  is_vxworks_tls = (htab->is_vxworks && info->shared
+  is_vxworks_tls = (htab->is_vxworks && bfd_link_pic (info)
                    && !strcmp (input_section->output_section->name,
                                ".tls_vars"));
-  rel = relocs;
+  if (input_section->sec_info_type == SEC_INFO_TYPE_TARGET)
+    relax_info = elf_section_data (input_section)->sec_info;
+  rel = wrel = relocs;
   relend = relocs + input_section->reloc_count;
-  for (; rel < relend; rel++)
+  for (; rel < relend; wrel++, rel++)
     {
       enum elf_ppc_reloc_type r_type;
       bfd_vma addend;
@@ -7570,7 +7826,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
       bfd_boolean warned;
       unsigned int tls_type, tls_mask, tls_gd;
       struct plt_entry **ifunc;
+      struct reloc_howto_struct alt_howto;
 
+    again:
       r_type = ELF32_R_TYPE (rel->r_info);
       sym = NULL;
       sec = NULL;
@@ -7607,11 +7865,25 @@ ppc_elf_relocate_section (bfd *output_bfd,
          howto = NULL;
          if (r_type < R_PPC_max)
            howto = ppc_elf_howto_table[r_type];
-         RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
-                                          rel, 1, relend, howto, 0, contents);
+
+         _bfd_clear_contents (howto, input_bfd, input_section,
+                              contents + rel->r_offset);
+         wrel->r_offset = rel->r_offset;
+         wrel->r_info = 0;
+         wrel->r_addend = 0;
+
+         /* For ld -r, remove relocations in debug sections against
+            sections defined in discarded sections.  Not done for
+            non-debug to preserve relocs in .eh_frame which the
+            eh_frame editing code expects to be present.  */
+         if (bfd_link_relocatable (info)
+             && (input_section->flags & SEC_DEBUGGING))
+           wrel--;
+
+         continue;
        }
 
-      if (info->relocatable)
+      if (bfd_link_relocatable (info))
        {
          if (got2 != NULL
              && r_type == R_PPC_PLTREL24
@@ -7624,7 +7896,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
          if (r_type != R_PPC_RELAX_PLT
              && r_type != R_PPC_RELAX_PLTREL24
              && r_type != R_PPC_RELAX)
-           continue;
+           goto copy_reloc;
        }
 
       /* TLS optimizations.  Replace instruction sequences and relocs
@@ -7667,10 +7939,12 @@ ppc_elf_relocate_section (bfd *output_bfd,
            {
              bfd_vma insn;
 
-             insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+             insn = bfd_get_32 (output_bfd,
+                                contents + rel->r_offset - d_offset);
              insn &= 31 << 21;
              insn |= 0x3c020000;       /* addis 0,2,0 */
-             bfd_put_32 (output_bfd, insn, contents + rel->r_offset - d_offset);
+             bfd_put_32 (output_bfd, insn,
+                         contents + rel->r_offset - d_offset);
              r_type = R_PPC_TPREL16_HA;
              rel->r_info = ELF32_R_INFO (r_symndx, r_type);
            }
@@ -7713,8 +7987,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
                          + R_PPC_GOT_TPREL16);
              else
                {
-                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
                  rel->r_offset -= d_offset;
+                 bfd_put_32 (output_bfd, NOP, contents + rel->r_offset);
                  r_type = R_PPC_NONE;
                }
              rel->r_info = ELF32_R_INFO (r_symndx, r_type);
@@ -7747,12 +8021,16 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  && branch_reloc_hash_match (input_bfd, rel + 1,
                                              htab->tls_get_addr))
                offset = rel[1].r_offset;
+             /* We read the low GOT_TLS insn because we need to keep
+                the destination reg.  It may be something other than
+                the usual r3, and moved to r3 before the call by
+                intervening code.  */
+             insn1 = bfd_get_32 (output_bfd,
+                                 contents + rel->r_offset - d_offset);
              if ((tls_mask & tls_gd) != 0)
                {
                  /* IE */
-                 insn1 = bfd_get_32 (output_bfd,
-                                     contents + rel->r_offset - d_offset);
-                 insn1 &= (1 << 26) - 1;
+                 insn1 &= (0x1f << 21) | (0x1f << 16);
                  insn1 |= 32 << 26;    /* lwz */
                  if (offset != (bfd_vma) -1)
                    {
@@ -7767,7 +8045,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
              else
                {
                  /* LE */
-                 insn1 = 0x3c620000;   /* addis 3,2,0 */
+                 insn1 &= 0x1f << 21;
+                 insn1 |= 0x3c020000;  /* addis r,2,0 */
                  if (tls_gd == 0)
                    {
                      /* Was an LD reloc.  */
@@ -7801,8 +8080,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                {
                  /* We changed the symbol on an LD reloc.  Start over
                     in order to get h, sym, sec etc. right.  */
-                 rel--;
-                 continue;
+                 goto again;
                }
            }
          break;
@@ -7860,8 +8138,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
              /* Zap the reloc on the _tls_get_addr call too.  */
              BFD_ASSERT (rel->r_offset - d_offset == rel[1].r_offset);
              rel[1].r_info = ELF32_R_INFO (STN_UNDEF, R_PPC_NONE);
-             rel--;
-             continue;
+             goto again;
            }
          break;
        }
@@ -7902,6 +8179,115 @@ ppc_elf_relocate_section (bfd *output_bfd,
          }
        }
 
+      if (ELIMINATE_COPY_RELOCS
+         && h != NULL
+         && !h->def_regular
+         && h->protected_def
+         && ppc_elf_hash_entry (h)->has_addr16_ha
+         && ppc_elf_hash_entry (h)->has_addr16_lo
+         && htab->params->pic_fixup > 0)
+       {
+         /* Convert lis;addi or lis;load/store accessing a protected
+            variable defined in a shared library to PIC.  */
+         unsigned int insn;
+
+         if (r_type == R_PPC_ADDR16_HA)
+           {
+             insn = bfd_get_32 (output_bfd,
+                                contents + rel->r_offset - d_offset);
+             if ((insn & (0x3f << 26)) == (15u << 26)
+                 && (insn & (0x1f << 16)) == 0 /* lis */)
+               {
+                 bfd_byte *p;
+                 bfd_vma off;
+                 bfd_vma got_addr;
+
+                 p = (contents + input_section->size
+                      - relax_info->workaround_size
+                      - relax_info->picfixup_size
+                      + picfixup_size);
+                 off = (p - contents) - (rel->r_offset - d_offset);
+                 if (off > 0x1fffffc || (off & 3) != 0)
+                   info->callbacks->einfo
+                     (_("%P: %H: fixup branch overflow\n"),
+                      input_bfd, input_section, rel->r_offset);
+
+                 bfd_put_32 (output_bfd, B | off,
+                             contents + rel->r_offset - d_offset);
+                 got_addr = (htab->got->output_section->vma
+                             + htab->got->output_offset
+                             + (h->got.offset & ~1));
+                 wrel->r_offset = (p - contents) + d_offset;
+                 wrel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_HA);
+                 wrel->r_addend = got_addr;
+                 insn &= ~0xffff;
+                 insn |= ((unsigned int )(got_addr + 0x8000) >> 16) & 0xffff;
+                 bfd_put_32 (output_bfd, insn, p);
+
+                 /* Convert lis to lwz, loading address from GOT.  */
+                 insn &= ~0xffff;
+                 insn ^= (32u ^ 15u) << 26;
+                 insn |= (insn & (0x1f << 21)) >> 5;
+                 insn |= got_addr & 0xffff;
+                 bfd_put_32 (output_bfd, insn, p + 4);
+
+                 bfd_put_32 (output_bfd, B | ((-4 - off) & 0x3ffffff), p + 8);
+                 picfixup_size += 12;
+
+                 /* Use one of the spare relocs, so --emit-relocs
+                    output is reasonable.  */
+                 memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
+                 wrel++, rel++;
+                 rel->r_offset = wrel[-1].r_offset + 4;
+                 rel->r_info = ELF32_R_INFO (0, R_PPC_ADDR16_LO);
+                 rel->r_addend = wrel[-1].r_addend;
+
+                 /* Continue on as if we had a got reloc, to output
+                    dynamic reloc.  */
+                 r_type = R_PPC_GOT16_LO;
+               }
+             else
+               info->callbacks->einfo
+                 (_("%P: %H: error: %s with unexpected instruction %x\n"),
+                  input_bfd, input_section, rel->r_offset,
+                  "R_PPC_ADDR16_HA", insn);
+           }
+         else if (r_type == R_PPC_ADDR16_LO)
+           {
+             insn = bfd_get_32 (output_bfd,
+                                contents + rel->r_offset - d_offset);
+             if ((insn & (0x3f << 26)) == 14u << 26    /* addi */
+                 || (insn & (0x3f << 26)) == 32u << 26 /* lwz */
+                 || (insn & (0x3f << 26)) == 34u << 26 /* lbz */
+                 || (insn & (0x3f << 26)) == 36u << 26 /* stw */
+                 || (insn & (0x3f << 26)) == 38u << 26 /* stb */
+                 || (insn & (0x3f << 26)) == 40u << 26 /* lhz */
+                 || (insn & (0x3f << 26)) == 42u << 26 /* lha */
+                 || (insn & (0x3f << 26)) == 44u << 26 /* sth */
+                 || (insn & (0x3f << 26)) == 46u << 26 /* lmw */
+                 || (insn & (0x3f << 26)) == 47u << 26 /* stmw */
+                 || (insn & (0x3f << 26)) == 48u << 26 /* lfs */
+                 || (insn & (0x3f << 26)) == 50u << 26 /* lfd */
+                 || (insn & (0x3f << 26)) == 52u << 26 /* stfs */
+                 || (insn & (0x3f << 26)) == 54u << 26 /* stfd */
+                 || ((insn & (0x3f << 26)) == 58u << 26 /* lwa,ld,lmd */
+                     && (insn & 3) != 1)
+                 || ((insn & (0x3f << 26)) == 62u << 26 /* std, stmd */
+                     && ((insn & 3) == 0 || (insn & 3) == 3)))
+               {
+                 /* Arrange to apply the reloc addend, if any.  */
+                 relocation = 0;
+                 unresolved_reloc = FALSE;
+                 rel->r_info = ELF32_R_INFO (0, r_type);
+               }
+             else
+               info->callbacks->einfo
+                 (_("%P: %H: error: %s with unexpected instruction %x\n"),
+                  input_bfd, input_section, rel->r_offset,
+                  "R_PPC_ADDR16_LO", insn);
+           }
+       }
+
       ifunc = NULL;
       if (!htab->is_vxworks)
        {
@@ -7924,16 +8310,36 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
          ent = NULL;
          if (ifunc != NULL
-             && (!info->shared
+             && (!bfd_link_pic (info)
                  || is_branch_reloc (r_type)))
            {
              addend = 0;
-             if (r_type == R_PPC_PLTREL24 && info->shared)
+             if (r_type == R_PPC_PLTREL24 && bfd_link_pic (info))
                addend = rel->r_addend;
              ent = find_plt_ent (ifunc, got2, addend);
            }
          if (ent != NULL)
            {
+             if (bfd_link_pic (info)
+                 && ent->sec != got2
+                 && htab->plt_type != PLT_NEW
+                 && (!htab->elf.dynamic_sections_created
+                     || h == NULL
+                     || h->dynindx == -1))
+               {
+                 /* Uh oh, we are going to create a pic glink stub
+                    for an ifunc (here for h == NULL and later in
+                    finish_dynamic_symbol for h != NULL), and
+                    apparently are using code compiled with
+                    -mbss-plt.  The difficulty is that -mbss-plt code
+                    gives no indication via a magic PLTREL24 addend
+                    whether r30 is equal to _GLOBAL_OFFSET_TABLE_ or
+                    is pointing into a .got2 section (and how far
+                    into .got2).  */
+                   info->callbacks->einfo
+                     (_("%X%P: %H: unsupported bss-plt -fPIC ifunc %s\n"),
+                      input_bfd, input_section, rel->r_offset, sym_name);
+               }
              if (h == NULL && (ent->plt.offset & 1) == 0)
                {
                  Elf_Internal_Rela rela;
@@ -7988,7 +8394,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
          bfd_set_error (bfd_error_bad_value);
          ret = FALSE;
-         continue;
+         goto copy_reloc;
 
        case R_PPC_NONE:
        case R_PPC_TLS:
@@ -7997,7 +8403,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_EMB_MRKREF:
        case R_PPC_GNU_VTINHERIT:
        case R_PPC_GNU_VTENTRY:
-         continue;
+         goto copy_reloc;
 
          /* GOT16 relocations.  Like an ADDR16 using the symbol's
             address in the GOT as relocation value instead of the
@@ -8056,8 +8462,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
              {
                bfd_boolean dyn;
                dyn = htab->elf.dynamic_sections_created;
-               if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
-                   || (info->shared
+               if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)
+                   || (bfd_link_pic (info)
                        && SYMBOL_REFERENCES_LOCAL (info, h)))
                  /* This is actually a static link, or it is a
                     -Bsymbolic link and the symbol is defined
@@ -8125,7 +8531,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                      }
 
                    /* Generate relocs for the dynamic linker.  */
-                   if ((info->shared || indx != 0)
+                   if ((bfd_link_pic (info) || indx != 0)
                        && (offp == &htab->tlsld_got.offset
                            || h == NULL
                            || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
@@ -8169,7 +8575,12 @@ ppc_elf_relocate_section (bfd *output_bfd,
                          {
                            outrel.r_addend += relocation;
                            if (tls_ty & (TLS_GD | TLS_DTPREL | TLS_TPREL))
-                             outrel.r_addend -= htab->elf.tls_sec->vma;
+                             {
+                               if (htab->elf.tls_sec == NULL)
+                                 outrel.r_addend = 0;
+                               else
+                                 outrel.r_addend -= htab->elf.tls_sec->vma;
+                             }
                          }
                        loc = rsec->contents;
                        loc += (rsec->reloc_count++
@@ -8187,9 +8598,14 @@ ppc_elf_relocate_section (bfd *output_bfd,
                          value = 1;
                        else if (tls_ty != 0)
                          {
-                           value -= htab->elf.tls_sec->vma + DTP_OFFSET;
-                           if (tls_ty == (TLS_TLS | TLS_TPREL))
-                             value += DTP_OFFSET - TP_OFFSET;
+                           if (htab->elf.tls_sec == NULL)
+                             value = 0;
+                           else
+                             {
+                               value -= htab->elf.tls_sec->vma + DTP_OFFSET;
+                               if (tls_ty == (TLS_TLS | TLS_TPREL))
+                                 value += DTP_OFFSET - TP_OFFSET;
+                             }
 
                            if (tls_ty == (TLS_TLS | TLS_GD))
                              {
@@ -8236,6 +8652,10 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  }
              }
 
+           /* If here for a picfixup, we're done.  */
+           if (r_type != ELF32_R_TYPE (rel->r_info))
+             goto copy_reloc;
+
            relocation = (htab->got->output_section->vma
                          + htab->got->output_offset
                          + off
@@ -8252,22 +8672,35 @@ ppc_elf_relocate_section (bfd *output_bfd,
                 howto->name,
                 sym_name);
          }
-       break;
+         break;
 
-       /* Relocations that need no special processing.  */
+         /* Relocations that need no special processing.  */
        case R_PPC_LOCAL24PC:
          /* It makes no sense to point a local relocation
             at a symbol not in this object.  */
          if (unresolved_reloc)
            {
-             if (! (*info->callbacks->undefined_symbol) (info,
-                                                         h->root.root.string,
-                                                         input_bfd,
-                                                         input_section,
-                                                         rel->r_offset,
-                                                         TRUE))
-               return FALSE;
-             continue;
+             (*info->callbacks->undefined_symbol) (info,
+                                                   h->root.root.string,
+                                                   input_bfd,
+                                                   input_section,
+                                                   rel->r_offset,
+                                                   TRUE);
+             goto copy_reloc;
+           }
+         if (h != NULL && h->type == STT_GNU_IFUNC && bfd_link_pic (info))
+           {
+             /* @local on an ifunc does not really make sense since
+                the ifunc resolver can take you anywhere.  More
+                seriously, calls to ifuncs must go through a plt call
+                stub, and for pic the plt call stubs uses r30 to
+                access the PLT.  The problem is that a call that is
+                local won't have the +32k reloc addend trick marking
+                -fPIC code, so the linker won't know whether r30 is
+                _GLOBAL_OFFSET_TABLE_ or pointing into a .got2 section.  */
+             info->callbacks->einfo (_("%X%P: %H: @local call to ifunc %s\n"),
+                                     input_bfd, input_section, rel->r_offset,
+                                     h->root.root.string);
            }
          break;
 
@@ -8275,7 +8708,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_DTPREL16_LO:
        case R_PPC_DTPREL16_HI:
        case R_PPC_DTPREL16_HA:
-         addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
          break;
 
          /* Relocations that may need to be propagated if this is a shared
@@ -8299,18 +8733,21 @@ ppc_elf_relocate_section (bfd *output_bfd,
                bfd_put_32 (output_bfd, insn, p);
              break;
            }
-         addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + TP_OFFSET;
          /* The TPREL16 relocs shouldn't really be used in shared
             libs as they will result in DT_TEXTREL being set, but
             support them anyway.  */
          goto dodyn;
 
        case R_PPC_TPREL32:
-         addend -= htab->elf.tls_sec->vma + TP_OFFSET;
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + TP_OFFSET;
          goto dodyn;
 
        case R_PPC_DTPREL32:
-         addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
+         if (htab->elf.tls_sec != NULL)
+           addend -= htab->elf.tls_sec->vma + DTP_OFFSET;
          goto dodyn;
 
        case R_PPC_DTPMOD32:
@@ -8322,6 +8759,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_REL16_LO:
        case R_PPC_REL16_HI:
        case R_PPC_REL16_HA:
+       case R_PPC_REL16DX_HA:
          break;
 
        case R_PPC_REL32:
@@ -8356,7 +8794,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_ADDR14:
        case R_PPC_ADDR14_BRTAKEN:
        case R_PPC_ADDR14_BRNTAKEN:
-         if (h != NULL && !info->shared)
+         if (h != NULL && !bfd_link_pic (info))
            break;
          /* fall through */
 
@@ -8365,7 +8803,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
              || is_vxworks_tls)
            break;
 
-         if ((info->shared
+         if ((bfd_link_pic (info)
               && !(h != NULL
                    && ((h->root.type == bfd_link_hash_undefined
                         && (ELF_ST_VISIBILITY (h->other) == STV_HIDDEN
@@ -8375,11 +8813,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
               && (must_be_dyn_reloc (info, r_type)
                   || !SYMBOL_CALLS_LOCAL (info, h)))
              || (ELIMINATE_COPY_RELOCS
-                 && !info->shared
+                 && !bfd_link_pic (info)
                  && h != NULL
                  && h->dynindx != -1
                  && !h->non_got_ref
-                 && !h->def_regular))
+                 && !h->def_regular
+                 && !(h->protected_def
+                      && ppc_elf_hash_entry (h)->has_addr16_ha
+                      && ppc_elf_hash_entry (h)->has_addr16_lo
+                      && htab->params->pic_fixup > 0)))
            {
              int skip;
              bfd_byte *loc;
@@ -8498,7 +8940,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
              bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc);
 
              if (skip == -1)
-               continue;
+               goto copy_reloc;
 
              /* This reloc will be computed at runtime.  We clear the memory
                 so that it contains predictable value.  */
@@ -8522,7 +8964,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
              if (r_type == R_PPC_RELAX_PLTREL24)
                {
-                 if (info->shared)
+                 if (bfd_link_pic (info))
                    got2_addend = addend;
                  addend = 0;
                }
@@ -8545,7 +8987,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
            size_t insn_offset = rel->r_offset;
            unsigned int insn;
 
-           if (info->shared)
+           if (bfd_link_pic (info))
              {
                relocation -= (input_section->output_section->vma
                               + input_section->output_offset
@@ -8564,7 +9006,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
              }
 
            relocation += addend;
-           if (info->relocatable)
+           if (bfd_link_relocatable (info))
              relocation = 0;
 
            /* First insn is HA, second is LO.  */
@@ -8591,12 +9033,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
               relocs to describe this relocation.  */
            BFD_ASSERT (ELF32_R_TYPE (relend[-1].r_info) == R_PPC_NONE);
            /* The relocs are at the bottom 2 bytes */
-           rel[0].r_offset += d_offset;
-           memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
-           rel[0].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
-           rel[1].r_offset += 4;
-           rel[1].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
-           rel++;
+           wrel->r_offset = rel->r_offset + d_offset;
+           wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
+           wrel->r_addend = rel->r_addend;
+           memmove (wrel + 1, wrel, (relend - wrel - 1) * sizeof (*wrel));
+           wrel++, rel++;
+           wrel->r_offset += 4;
+           wrel->r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
          }
          continue;
 
@@ -8649,8 +9092,10 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_PLTREL24:
          if (h != NULL && ifunc == NULL)
            {
-             struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
-                                                   info->shared ? addend : 0);
+             struct plt_entry *ent;
+
+             ent = find_plt_ent (&h->plt.plist, got2,
+                                 bfd_link_pic (info) ? addend : 0);
              if (ent == NULL
                  || htab->plt == NULL)
                {
@@ -8696,10 +9141,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
            addend -= SYM_VAL (sda);
 
            name = bfd_get_section_name (output_bfd, sec->output_section);
-           if (! ((CONST_STRNEQ (name, ".sdata")
-                   && (name[6] == 0 || name[6] == '.'))
-                  || (CONST_STRNEQ (name, ".sbss")
-                      && (name[5] == 0 || name[5] == '.'))))
+           if (!(strcmp (name, ".sdata") == 0
+                 || strcmp (name, ".sbss") == 0))
              {
                info->callbacks->einfo
                  (_("%P: %B: the target (%s) of a %s relocation is "
@@ -8728,8 +9171,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
            addend -= SYM_VAL (sda);
 
            name = bfd_get_section_name (output_bfd, sec->output_section);
-           if (! (CONST_STRNEQ (name, ".sdata2")
-                  || CONST_STRNEQ (name, ".sbss2")))
+           if (!(strcmp (name, ".sdata2") == 0
+                 || strcmp (name, ".sbss2") == 0))
              {
                info->callbacks->einfo
                  (_("%P: %B: the target (%s) of a %s relocation is "
@@ -8743,46 +9186,40 @@ ppc_elf_relocate_section (bfd *output_bfd,
          break;
 
        case R_PPC_VLE_LO16A:
-         relocation = (relocation + addend) & 0xffff;
-         ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                               relocation, split16a_type);
-         continue;
+         relocation = relocation + addend;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16a_type);
+         goto copy_reloc;
 
        case R_PPC_VLE_LO16D:
-         relocation = (relocation + addend) & 0xffff;
-         ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                               relocation, split16d_type);
-         continue;
+         relocation = relocation + addend;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16d_type);
+         goto copy_reloc;
 
        case R_PPC_VLE_HI16A:
-         relocation = ((relocation + addend) >> 16) & 0xffff;
-         ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                               relocation, split16a_type);
-         continue;
+         relocation = (relocation + addend) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16a_type);
+         goto copy_reloc;
 
        case R_PPC_VLE_HI16D:
-         relocation = ((relocation + addend) >> 16) & 0xffff;
-         ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                               relocation, split16d_type);
-         continue;
+         relocation = (relocation + addend) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16d_type);
+         goto copy_reloc;
 
        case R_PPC_VLE_HA16A:
-         {
-           bfd_vma value = relocation + addend;
-           value = (((value >> 16) + ((value & 0x8000) ? 1 : 0)) & 0xffff);
-           ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                 value, split16a_type);
-         }
-         continue;
+         relocation = (relocation + addend + 0x8000) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16a_type);
+         goto copy_reloc;
 
        case R_PPC_VLE_HA16D:
-         {
-           bfd_vma value = relocation + addend;
-           value = (((value >> 16) + ((value & 0x8000) ? 1 : 0)) & 0xffff);
-           ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                 value, split16d_type);
-         }
-         continue;
+         relocation = (relocation + addend + 0x8000) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16d_type);
+         goto copy_reloc;
 
          /* Relocate against either _SDA_BASE_, _SDA2_BASE_, or 0.  */
        case R_PPC_EMB_SDA21:
@@ -8792,6 +9229,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
          {
            const char *name;
            int reg;
+           unsigned int insn;
            struct elf_link_hash_entry *sda = NULL;
 
            if (sec == NULL || sec->output_section == NULL)
@@ -8801,16 +9239,14 @@ ppc_elf_relocate_section (bfd *output_bfd,
              }
 
            name = bfd_get_section_name (output_bfd, sec->output_section);
-           if (((CONST_STRNEQ (name, ".sdata")
-                 && (name[6] == 0 || name[6] == '.'))
-                || (CONST_STRNEQ (name, ".sbss")
-                    && (name[5] == 0 || name[5] == '.'))))
+           if (strcmp (name, ".sdata") == 0
+               || strcmp (name, ".sbss") == 0)
              {
                reg = 13;
                sda = htab->sdata[0].sym;
              }
-           else if (CONST_STRNEQ (name, ".sdata2")
-                    || CONST_STRNEQ (name, ".sbss2"))
+           else if (strcmp (name, ".sdata2") == 0
+                    || strcmp (name, ".sbss2") == 0)
              {
                reg = 2;
                sda = htab->sdata[1].sym;
@@ -8832,7 +9268,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
                bfd_set_error (bfd_error_bad_value);
                ret = FALSE;
-               continue;
+               goto copy_reloc;
              }
 
            if (sda != NULL)
@@ -8845,32 +9281,41 @@ ppc_elf_relocate_section (bfd *output_bfd,
                addend -= SYM_VAL (sda);
              }
 
+           insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
            if (reg == 0
                && (r_type == R_PPC_VLE_SDA21
                    || r_type == R_PPC_VLE_SDA21_LO))
              {
-               /* Use the split20 format.  */
-               bfd_vma insn, bits12to15, bits21to31;
-               bfd_vma value  = (relocation + rel->r_offset) & 0xffff;
-               /* Propagate sign bit, if necessary.  */
-               insn = (value & 0x8000) ? 0x70107800 : 0x70000000;
-               bits12to15 = value & 0x700;
-               bits21to31 = value & 0x7ff;
-               insn |= bits12to15;
-               insn |= bits21to31;
-               bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
-               continue;
+               relocation = relocation + addend;
+               addend = 0;
+
+               /* Force e_li insn, keeping RT from original insn.  */
+               insn &= 0x1f << 21;
+               insn |= 28u << 26;
+
+               /* We have an li20 field, bits 17..20, 11..15, 21..31.  */
+               /* Top 4 bits of value to 17..20.  */
+               insn |= (relocation & 0xf0000) >> 5;
+               /* Next 5 bits of the value to 11..15.  */
+               insn |= (relocation & 0xf800) << 5;
+               /* And the final 11 bits of the value to bits 21 to 31.  */
+               insn |= relocation & 0x7ff;
+
+               bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
+
+               if (r_type == R_PPC_VLE_SDA21
+                   && ((relocation + 0x80000) & 0xffffffff) > 0x100000)
+                 goto overflow;
+               goto copy_reloc;
              }
            else if (r_type == R_PPC_EMB_SDA21
                     || r_type == R_PPC_VLE_SDA21
                     || r_type == R_PPC_VLE_SDA21_LO)
              {
-               bfd_vma insn;  /* Fill in register field.  */
-
-               insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
+               /* Fill in register field.  */
                insn = (insn & ~RA_REGISTER_MASK) | (reg << RA_REGISTER_SHIFT);
-               bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
              }
+           bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
          }
          break;
 
@@ -8893,23 +9338,21 @@ ppc_elf_relocate_section (bfd *output_bfd,
              }
 
            name = bfd_get_section_name (output_bfd, sec->output_section);
-           if (((CONST_STRNEQ (name, ".sdata")
-                 && (name[6] == 0 || name[6] == '.'))
-                || (CONST_STRNEQ (name, ".sbss")
-                    && (name[5] == 0 || name[5] == '.'))))
+           if (strcmp (name, ".sdata") == 0
+               || strcmp (name, ".sbss") == 0)
              {
                //reg = 13;
                sda = htab->sdata[0].sym;
              }
-           else if (CONST_STRNEQ (name, ".sdata2")
-                    || CONST_STRNEQ (name, ".sbss2"))
+           else if (strcmp (name, ".sdata2") == 0
+                    || strcmp (name, ".sbss2") == 0)
              {
                //reg = 2;
                sda = htab->sdata[1].sym;
              }
            else
              {
-               (*_bfd_error_handler)
+               _bfd_error_handler
                  (_("%B: the target (%s) of a %s relocation is "
                     "in the wrong output section (%s)"),
                   input_bfd,
@@ -8919,7 +9362,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
                bfd_set_error (bfd_error_bad_value);
                ret = FALSE;
-               continue;
+               goto copy_reloc;
              }
 
            if (sda != NULL)
@@ -8931,49 +9374,42 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  }
              }
 
-          value = sda->root.u.def.section->output_section->vma
-                  + sda->root.u.def.section->output_offset;
-
-          if (r_type == R_PPC_VLE_SDAREL_LO16A)
-             {
-               value = (value + addend) & 0xffff;
-               ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                     value, split16a_type);
-             }
-          else if (r_type == R_PPC_VLE_SDAREL_LO16D)
+           value = (sda->root.u.def.section->output_section->vma
+                    + sda->root.u.def.section->output_offset
+                    + addend);
+
+           if (r_type == R_PPC_VLE_SDAREL_LO16A)
+             ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                  value, split16a_type);
+           else if (r_type == R_PPC_VLE_SDAREL_LO16D)
+             ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                  value, split16d_type);
+           else if (r_type == R_PPC_VLE_SDAREL_HI16A)
              {
-               value = (value + addend) & 0xffff;
-               ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                     value, split16d_type);
+               value = value >> 16;
+               ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                    value, split16a_type);
              }
-          else if (r_type == R_PPC_VLE_SDAREL_HI16A)
+           else if (r_type == R_PPC_VLE_SDAREL_HI16D)
              {
-               value = ((value + addend) >> 16) & 0xffff;
-               ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                     value, split16a_type);
+               value = value >> 16;
+               ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                    value, split16d_type);
              }
-          else if (r_type == R_PPC_VLE_SDAREL_HI16D)
+           else if (r_type == R_PPC_VLE_SDAREL_HA16A)
              {
-               value = ((value + addend) >> 16) & 0xffff;
-               ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                     value, split16d_type);
+               value = (value + 0x8000) >> 16;
+               ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                    value, split16a_type);
              }
-          else if (r_type == R_PPC_VLE_SDAREL_HA16A)
+           else if (r_type == R_PPC_VLE_SDAREL_HA16D)
              {
-               value += addend;
-               value = (((value >> 16) + ((value & 0x8000) ? 1 : 0)) & 0xffff);
-               ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                     value, split16a_type);
-             }
-          else if (r_type == R_PPC_VLE_SDAREL_HA16D)
-             {
-               value += addend;
-               value = (((value >> 16) + ((value & 0x8000) ? 1 : 0)) & 0xffff);
-               ppc_elf_vle_split16 (output_bfd, contents, rel->r_offset,
-                                     value, split16d_type);
+               value = (value + 0x8000) >> 16;
+               ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                    value, split16d_type);
              }
          }
-         continue;
+         goto copy_reloc;
 
          /* Relocate against the beginning of the section.  */
        case R_PPC_SECTOFF:
@@ -9021,7 +9457,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
          bfd_set_error (bfd_error_invalid_operation);
          ret = FALSE;
-         continue;
+         goto copy_reloc;
        }
 
       /* Do any further special processing.  */
@@ -9032,6 +9468,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_ADDR16_HA:
        case R_PPC_REL16_HA:
+       case R_PPC_REL16DX_HA:
        case R_PPC_SECTOFF_HA:
        case R_PPC_TPREL16_HA:
        case R_PPC_DTPREL16_HA:
@@ -9081,7 +9518,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
               that make up part of the insn opcode.  */
            unsigned int insn, mask, lobit;
 
-           insn = bfd_get_32 (output_bfd, contents + rel->r_offset - d_offset);
+           insn = bfd_get_32 (output_bfd,
+                              contents + rel->r_offset - d_offset);
            mask = 0;
            if (is_insn_ds_form (insn))
              mask = 3;
@@ -9089,10 +9527,12 @@ ppc_elf_relocate_section (bfd *output_bfd,
              mask = 15;
            else
              break;
-           lobit = mask & (relocation + addend);
+           relocation += addend;
+           addend = insn & mask;
+           lobit = mask & relocation;
            if (lobit != 0)
              {
-               addend -= lobit;
+               relocation ^= lobit;
                info->callbacks->einfo
                  (_("%P: %H: error: %s against `%s' not a multiple of %u\n"),
                   input_bfd, input_section, rel->r_offset,
@@ -9100,7 +9540,6 @@ ppc_elf_relocate_section (bfd *output_bfd,
                bfd_set_error (bfd_error_bad_value);
                ret = FALSE;
              }
-           addend += insn & mask;
          }
          break;
        }
@@ -9130,42 +9569,75 @@ ppc_elf_relocate_section (bfd *output_bfd,
          ret = FALSE;
        }
 
-      r = _bfd_final_link_relocate (howto,
-                                   input_bfd,
-                                   input_section,
-                                   contents,
-                                   rel->r_offset,
-                                   relocation,
-                                   addend);
+      /* 16-bit fields in insns mostly have signed values, but a
+        few insns have 16-bit unsigned values.  Really, we should
+        have different reloc types.  */
+      if (howto->complain_on_overflow != complain_overflow_dont
+         && howto->dst_mask == 0xffff
+         && (input_section->flags & SEC_CODE) != 0)
+       {
+         enum complain_overflow complain = complain_overflow_signed;
+
+         if ((elf_section_flags (input_section) & SHF_PPC_VLE) == 0)
+           {
+             unsigned int insn;
+
+             insn = bfd_get_32 (input_bfd, contents + (rel->r_offset & ~3));
+             if ((insn & (0x3f << 26)) == 10u << 26 /* cmpli */)
+               complain = complain_overflow_bitfield;
+             else if ((insn & (0x3f << 26)) == 28u << 26 /* andi */
+                      || (insn & (0x3f << 26)) == 24u << 26 /* ori */
+                      || (insn & (0x3f << 26)) == 26u << 26 /* xori */)
+               complain = complain_overflow_unsigned;
+           }
+         if (howto->complain_on_overflow != complain)
+           {
+             alt_howto = *howto;
+             alt_howto.complain_on_overflow = complain;
+             howto = &alt_howto;
+           }
+       }
+
+      if (r_type == R_PPC_REL16DX_HA)
+       {
+         /* Split field reloc isn't handled by _bfd_final_link_relocate.  */
+         if (rel->r_offset + 4 > input_section->size)
+           r = bfd_reloc_outofrange;
+         else
+           {
+             unsigned int insn;
+
+             relocation += addend;
+             relocation -= (rel->r_offset
+                            + input_section->output_offset
+                            + input_section->output_section->vma);
+             relocation >>= 16;
+             insn = bfd_get_32 (input_bfd, contents + rel->r_offset);
+             insn &= ~0x1fffc1;
+             insn |= (relocation & 0xffc1) | ((relocation & 0x3e) << 15);
+             bfd_put_32 (input_bfd, insn, contents + rel->r_offset);
+             r = bfd_reloc_ok;
+           }
+       }
+      else
+       r = _bfd_final_link_relocate (howto, input_bfd, input_section, contents,
+                                     rel->r_offset, relocation, addend);
 
       if (r != bfd_reloc_ok)
        {
          if (r == bfd_reloc_overflow)
            {
-             if (warned)
-               continue;
-             if (h != NULL
-                 && h->root.type == bfd_link_hash_undefweak
-                 && howto->pc_relative)
-               {
-                 /* Assume this is a call protected by other code that
-                    detect the symbol is undefined.  If this is the case,
-                    we can safely ignore the overflow.  If not, the
-                    program is hosed anyway, and a little warning isn't
-                    going to help.  */
-
-                 continue;
-               }
-
-             if (! (*info->callbacks->reloc_overflow) (info,
-                                                       (h ? &h->root : NULL),
-                                                       sym_name,
-                                                       howto->name,
-                                                       rel->r_addend,
-                                                       input_bfd,
-                                                       input_section,
-                                                       rel->r_offset))
-               return FALSE;
+           overflow:
+             /* On code like "if (foo) foo();" don't report overflow
+                on a branch to zero when foo is undefined.  */
+             if (!warned
+                 && !(h != NULL
+                      && (h->root.type == bfd_link_hash_undefweak
+                          || h->root.type == bfd_link_hash_undefined)
+                      && is_branch_reloc (r_type)))
+               info->callbacks->reloc_overflow
+                 (info, (h ? &h->root : NULL), sym_name, howto->name,
+                  rel->r_addend, input_bfd, input_section, rel->r_offset);
            }
          else
            {
@@ -9176,6 +9648,31 @@ ppc_elf_relocate_section (bfd *output_bfd,
              ret = FALSE;
            }
        }
+    copy_reloc:
+      if (wrel != rel)
+       *wrel = *rel;
+    }
+
+  if (wrel != rel)
+    {
+      Elf_Internal_Shdr *rel_hdr;
+      size_t deleted = rel - wrel;
+
+      rel_hdr = _bfd_elf_single_rel_hdr (input_section->output_section);
+      rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+      if (rel_hdr->sh_size == 0)
+       {
+         /* It is too late to remove an empty reloc section.  Leave
+            one NONE reloc.
+            ??? What is wrong with an empty section???  */
+         rel_hdr->sh_size = rel_hdr->sh_entsize;
+         deleted -= 1;
+         wrel++;
+       }
+      relend = wrel;
+      rel_hdr = _bfd_elf_single_rel_hdr (input_section);
+      rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+      input_section->reloc_count -= deleted;
     }
 
 #ifdef DEBUG
@@ -9194,18 +9691,28 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
   if (htab->params->ppc476_workaround
       && input_section->sec_info_type == SEC_INFO_TYPE_TARGET
-      && (!info->relocatable
+      && (!bfd_link_relocatable (info)
          || (input_section->output_section->alignment_power
              >= htab->params->pagesize_p2)))
     {
-      struct ppc_elf_relax_info *relax_info;
       bfd_vma start_addr, end_addr, addr;
       bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
 
-      relax_info = elf_section_data (input_section)->sec_info;
       if (relax_info->workaround_size != 0)
-       memset (contents + input_section->size - relax_info->workaround_size,
-               0, relax_info->workaround_size);
+       {
+         bfd_byte *p;
+         unsigned int n;
+         bfd_byte fill[4];
+
+         bfd_put_32 (input_bfd, BA, fill);
+         p = contents + input_section->size - relax_info->workaround_size;
+         n = relax_info->workaround_size >> 2;
+         while (n--)
+           {
+             memcpy (p, fill, 4);
+             p += 4;
+           }
+       }
 
       /* The idea is: Replace the last instruction on a page with a
         branch to a patch area.  Put the insn there followed by a
@@ -9259,27 +9766,56 @@ ppc_elf_relocate_section (bfd *output_bfd,
          if (is_data)
            continue;
 
-         /* Some instructions can be left alone too.  In this
-            category are most insns that unconditionally change
-            control flow, and isync.  Of these, some *must* be left
-            alone, for example, the "bcl 20, 31, label" used in pic
-            sequences to give the address of the next insn.  twui
-            and twu apparently are not safe.  */
+         /* Some instructions can be left alone too.  Unconditional
+            branches, except for bcctr with BO=0x14 (bctr, bctrl),
+            avoid the icache failure.
+
+            The problem occurs due to prefetch across a page boundary
+            where stale instructions can be fetched from the next
+            page, and the mechanism for flushing these bad
+            instructions fails under certain circumstances.  The
+            unconditional branches:
+            1) Branch: b, bl, ba, bla,
+            2) Branch Conditional: bc, bca, bcl, bcla,
+            3) Branch Conditional to Link Register: bclr, bclrl,
+            where (2) and (3) have BO=0x14 making them unconditional,
+            prevent the bad prefetch because the prefetch itself is
+            affected by these instructions.  This happens even if the
+            instruction is not executed.
+
+            A bctr example:
+            .
+            .  lis 9,new_page@ha
+            .  addi 9,9,new_page@l
+            .  mtctr 9
+            .  bctr
+            .  nop
+            .  nop
+            . new_page:
+            .
+            The bctr is not predicted taken due to ctr not being
+            ready, so prefetch continues on past the bctr into the
+            new page which might have stale instructions.  If they
+            fail to be flushed, then they will be executed after the
+            bctr executes.  Either of the following modifications
+            prevent the bad prefetch from happening in the first
+            place:
+            .
+            .  lis 9,new_page@ha        lis 9,new_page@ha
+            .  addi 9,9,new_page@l      addi 9,9,new_page@l
+            .  mtctr 9                  mtctr 9
+            .  bctr                     bctr
+            .  nop                      b somewhere_else
+            .  b somewhere_else         nop
+            . new_page:                new_page:
+            .  */
          insn = bfd_get_32 (input_bfd, contents + offset);
-         if (insn == 0
-             || (insn & (0x3f << 26)) == (18u << 26)        /* b */
-             || ((insn & (0x3f << 26)) == (16u << 26)       /* bc always */
-                 && (insn & (0x14 << 21)) == (0x14 << 21))
-             || ((insn & (0x3f << 26)) == (19u << 26)       /* blr, bctr */
-                 && (insn & (0x14 << 21)) == (0x14 << 21)
-                 && (insn & (0x1ff << 1)) == (16u << 1))
-             || (insn & (0x3f << 26)) == (17u << 26)        /* sc */
+         if ((insn & (0x3f << 26)) == (18u << 26)          /* b,bl,ba,bla */
+             || ((insn & (0x3f << 26)) == (16u << 26)      /* bc,bcl,bca,bcla*/
+                 && (insn & (0x14 << 21)) == (0x14 << 21)) /*   with BO=0x14 */
              || ((insn & (0x3f << 26)) == (19u << 26)
-                 && ((insn & (0x3ff << 1)) == (38u << 1)    /* rfmci */
-                     || (insn & (0x3ff << 1)) == (50u << 1) /* rfi */
-                     || (insn & (0x3ff << 1)) == (51u << 1) /* rfci */
-                     || (insn & (0x3ff << 1)) == (82u << 1) /* rfsvc */
-                     || (insn & (0x3ff << 1)) == (150u << 1))) /* isync */)
+                 && (insn & (0x3ff << 1)) == (16u << 1)    /* bclr,bclrl */
+                 && (insn & (0x14 << 21)) == (0x14 << 21)))/*   with BO=0x14 */
            continue;
 
          patch_addr = (start_addr + input_section->size
@@ -9292,6 +9828,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
              && rel->r_offset >= offset
              && rel->r_offset < offset + 4)
            {
+             asection *sreloc;
+
              /* If the insn we are patching had a reloc, adjust the
                 reloc r_offset so that the reloc applies to the moved
                 location.  This matters for -r and --emit-relocs.  */
@@ -9304,6 +9842,57 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  relend[-1] = tmp;
                }
              relend[-1].r_offset += patch_off - offset;
+
+             /* Adjust REL16 addends too.  */
+             switch (ELF32_R_TYPE (relend[-1].r_info))
+               {
+               case R_PPC_REL16:
+               case R_PPC_REL16_LO:
+               case R_PPC_REL16_HI:
+               case R_PPC_REL16_HA:
+                 relend[-1].r_addend += patch_off - offset;
+                 break;
+               default:
+                 break;
+               }
+
+             /* If we are building a PIE or shared library with
+                non-PIC objects, perhaps we had a dynamic reloc too?
+                If so, the dynamic reloc must move with the insn.  */
+             sreloc = elf_section_data (input_section)->sreloc;
+             if (sreloc != NULL)
+               {
+                 Elf32_External_Rela *slo, *shi, *srelend;
+                 bfd_vma soffset;
+
+                 slo = (Elf32_External_Rela *) sreloc->contents;
+                 shi = srelend = slo + sreloc->reloc_count;
+                 soffset = (offset + input_section->output_section->vma
+                            + input_section->output_offset);
+                 while (slo < shi)
+                   {
+                     Elf32_External_Rela *srel = slo + (shi - slo) / 2;
+                     bfd_elf32_swap_reloca_in (output_bfd, (bfd_byte *) srel,
+                                               &outrel);
+                     if (outrel.r_offset < soffset)
+                       slo = srel + 1;
+                     else if (outrel.r_offset > soffset + 3)
+                       shi = srel;
+                     else
+                       {
+                         if (srel + 1 != srelend)
+                           {
+                             memmove (srel, srel + 1,
+                                      (srelend - (srel + 1)) * sizeof (*srel));
+                             srel = srelend - 1;
+                           }
+                         outrel.r_offset += patch_off - offset;
+                         bfd_elf32_swap_reloca_out (output_bfd, &outrel,
+                                                    (bfd_byte *) srel);
+                         break;
+                       }
+                   }
+               }
            }
          else
            rel = NULL;
@@ -9314,9 +9903,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
              bfd_vma delta = ((insn & 0xfffc) ^ 0x8000) - 0x8000;
 
              delta += offset - patch_off;
-             if (info->relocatable && rel != NULL)
+             if (bfd_link_relocatable (info) && rel != NULL)
                delta = 0;
-             if (!info->relocatable && rel != NULL)
+             if (!bfd_link_relocatable (info) && rel != NULL)
                {
                  enum elf_ppc_reloc_type r_type;
 
@@ -9442,11 +10031,11 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
                got_offset = (reloc_index + 3) * 4;
 
                /* Use the right PLT. */
-               plt_entry = info->shared ? ppc_elf_vxworks_pic_plt_entry
+               plt_entry = bfd_link_pic (info) ? ppc_elf_vxworks_pic_plt_entry
                            : ppc_elf_vxworks_plt_entry;
 
                /* Fill in the .plt on VxWorks.  */
-               if (info->shared)
+               if (bfd_link_pic (info))
                  {
                    bfd_put_32 (output_bfd,
                                plt_entry[0] | PPC_HA (got_offset),
@@ -9504,7 +10093,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
                                         + ent->plt.offset + 16),
                            htab->sgotplt->contents + got_offset);
 
-               if (!info->shared)
+               if (!bfd_link_pic (info))
                  {
                    /* Fill in a couple of entries in .rela.plt.unloaded.  */
                    loc = htab->srelplt2->contents
@@ -9626,7 +10215,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
                  }
              }
            else if (h->type == STT_GNU_IFUNC
-                    && !info->shared)
+                    && !bfd_link_pic (info))
              {
                /* Set the value of ifunc symbols in a non-pie
                   executable to the glink entry.  This is to avoid
@@ -9678,7 +10267,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
            write_glink_stub (ent, splt, p, info);
 
-           if (!info->shared)
+           if (!bfd_link_pic (info))
              /* We only need one non-PIC glink stub.  */
              break;
          }
@@ -9873,11 +10462,11 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
   if (splt && splt->size > 0)
     {
       /* Use the right PLT. */
-      const bfd_vma *plt_entry = (info->shared
+      const bfd_vma *plt_entry = (bfd_link_pic (info)
                                  ? ppc_elf_vxworks_pic_plt0_entry
                                  : ppc_elf_vxworks_plt0_entry);
 
-      if (!info->shared)
+      if (!bfd_link_pic (info))
        {
          bfd_vma got_value = SYM_VAL (htab->elf.hgot);
 
@@ -9898,7 +10487,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
       bfd_put_32 (output_bfd, plt_entry[6], splt->contents + 24);
       bfd_put_32 (output_bfd, plt_entry[7], splt->contents + 28);
 
-      if (! info->shared)
+      if (! bfd_link_pic (info))
        {
          Elf_Internal_Rela rela;
          bfd_byte *loc;
@@ -10066,7 +10655,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
       p += htab->glink_pltresolve;
       endp = htab->glink->contents;
       endp += htab->glink->size - GLINK_PLTRESOLVE;
-      while (p < endp - 8 * 4)
+      while (p < endp - (htab->params->ppc476_workaround ? 0 : 8 * 4))
        {
          bfd_put_32 (output_bfd, B + endp - p, p);
          p += 4;
@@ -10081,14 +10670,51 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
              + htab->glink->output_section->vma
              + htab->glink->output_offset);
 
+      if (htab->params->ppc476_workaround)
+       {
+         /* Ensure that a call stub at the end of a page doesn't
+            result in prefetch over the end of the page into the
+            glink branch table.  */
+         bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
+         bfd_vma page_addr;
+         bfd_vma glink_start = (htab->glink->output_section->vma
+                                + htab->glink->output_offset);
+
+         for (page_addr = res0 & -pagesize;
+              page_addr > glink_start;
+              page_addr -= pagesize)
+           {
+             /* We have a plt call stub that may need fixing.  */
+             bfd_byte *loc;
+             unsigned int insn;
+
+             loc = htab->glink->contents + page_addr - 4 - glink_start;
+             insn = bfd_get_32 (output_bfd, loc);
+             if (insn == BCTR)
+               {
+                 /* By alignment, we know that there must be at least
+                    one other call stub before this one.  */
+                 insn = bfd_get_32 (output_bfd, loc - 16);
+                 if (insn == BCTR)
+                   bfd_put_32 (output_bfd, B | (-16 & 0x3fffffc), loc);
+                 else
+                   bfd_put_32 (output_bfd, B | (-20 & 0x3fffffc), loc);
+               }
+           }
+       }
+
       /* Last comes the PLTresolve stub.  */
-      if (info->shared)
+      if (bfd_link_pic (info))
        {
          bfd_vma bcl;
 
          for (i = 0; i < ARRAY_SIZE (pic_plt_resolve); i++)
            {
-             bfd_put_32 (output_bfd, pic_plt_resolve[i], p);
+             unsigned int insn = pic_plt_resolve[i];
+
+             if (htab->params->ppc476_workaround && insn == NOP)
+               insn = BA + 0;
+             bfd_put_32 (output_bfd, insn, p);
              p += 4;
            }
          p -= 4 * ARRAY_SIZE (pic_plt_resolve);
@@ -10122,7 +10748,11 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
        {
          for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
            {
-             bfd_put_32 (output_bfd, plt_resolve[i], p);
+             unsigned int insn = plt_resolve[i];
+
+             if (htab->params->ppc476_workaround && insn == NOP)
+               insn = BA + 0;
+             bfd_put_32 (output_bfd, insn, p);
              p += 4;
            }
          p -= 4 * ARRAY_SIZE (plt_resolve);
@@ -10179,20 +10809,21 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
   return ret;
 }
 \f
-#define TARGET_LITTLE_SYM      bfd_elf32_powerpcle_vec
+#define TARGET_LITTLE_SYM      powerpc_elf32_le_vec
 #define TARGET_LITTLE_NAME     "elf32-powerpcle"
-#define TARGET_BIG_SYM         bfd_elf32_powerpc_vec
+#define TARGET_BIG_SYM         powerpc_elf32_vec
 #define TARGET_BIG_NAME                "elf32-powerpc"
 #define ELF_ARCH               bfd_arch_powerpc
 #define ELF_TARGET_ID          PPC32_ELF_DATA
 #define ELF_MACHINE_CODE       EM_PPC
 #ifdef __QNXTARGET__
 #define ELF_MAXPAGESIZE                0x1000
+#define ELF_COMMONPAGESIZE     0x1000
 #else
 #define ELF_MAXPAGESIZE                0x10000
+#define ELF_COMMONPAGESIZE     0x10000
 #endif
 #define ELF_MINPAGESIZE                0x1000
-#define ELF_COMMONPAGESIZE     0x1000
 #define elf_info_to_howto      ppc_elf_info_to_howto
 
 #ifdef  EM_CYGNUS_POWERPC
@@ -10247,7 +10878,6 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #define elf_backend_action_discarded           ppc_elf_action_discarded
 #define elf_backend_init_index_section         _bfd_elf_init_1_index_section
 #define elf_backend_lookup_section_flags_hook  ppc_elf_lookup_section_flags
-#define elf_backend_section_processing         ppc_elf_section_processing
 
 #include "elf32-target.h"
 
@@ -10257,7 +10887,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #undef  TARGET_LITTLE_NAME
 
 #undef  TARGET_BIG_SYM
-#define TARGET_BIG_SYM  bfd_elf32_powerpc_freebsd_vec
+#define TARGET_BIG_SYM  powerpc_elf32_fbsd_vec
 #undef  TARGET_BIG_NAME
 #define TARGET_BIG_NAME "elf32-powerpc-freebsd"
 
@@ -10275,7 +10905,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #undef TARGET_LITTLE_NAME
 
 #undef TARGET_BIG_SYM
-#define TARGET_BIG_SYM         bfd_elf32_powerpc_vxworks_vec
+#define TARGET_BIG_SYM         powerpc_elf32_vxworks_vec
 #undef TARGET_BIG_NAME
 #define TARGET_BIG_NAME                "elf32-powerpc-vxworks"
 
This page took 0.095735 seconds and 4 git commands to generate.