Automatic date update in version.in
[deliverable/binutils-gdb.git] / bfd / elf32-ppc.c
index 60adae8a127e130459e99c6bab2a87f6ab419dca..b96dbc8faadce847a006c87173e314bd8f88adb1 100644 (file)
@@ -1,6 +1,5 @@
 /* PowerPC-specific support for 32-bit ELF
-   Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
-   2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
+   Copyright (C) 1994-2015 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support.
 
    This file is part of BFD, the Binary File Descriptor library.
 #include "elf/ppc.h"
 #include "elf32-ppc.h"
 #include "elf-vxworks.h"
+#include "dwarf2.h"
+#include "elf-linux-psinfo.h"
+
+typedef enum split16_format_type
+{
+  split16a_type = 0,
+  split16d_type
+}
+split16_format_type;
 
 /* RELA relocations are used here.  */
 
@@ -139,6 +147,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
@@ -178,11 +187,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 */
@@ -197,7 +206,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 */
@@ -213,7 +222,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 */
@@ -290,7 +299,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 */
@@ -307,7 +316,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 */
@@ -324,7 +333,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 */
@@ -436,7 +445,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* 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_GOT16_HI",      /* name */
         FALSE,                 /* partial_inplace */
@@ -452,7 +461,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         ppc_elf_addr16_ha_reloc, /* special_function */
         "R_PPC_GOT16_HA",      /* name */
         FALSE,                 /* partial_inplace */
@@ -487,7 +496,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_COPY",          /* name */
         FALSE,                 /* partial_inplace */
@@ -503,7 +512,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_GLOB_DAT",      /* name */
         FALSE,                 /* partial_inplace */
@@ -518,7 +527,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_JMP_SLOT",      /* name */
         FALSE,                 /* partial_inplace */
@@ -535,7 +544,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 */
@@ -567,7 +576,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 */
@@ -597,7 +606,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 */
@@ -613,7 +622,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_PLT32",         /* name */
         FALSE,                 /* partial_inplace */
@@ -629,7 +638,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_PLTREL32",      /* name */
         FALSE,                 /* partial_inplace */
@@ -661,7 +670,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* 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_PLT16_HI",      /* name */
         FALSE,                 /* partial_inplace */
@@ -677,7 +686,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         ppc_elf_addr16_ha_reloc, /* special_function */
         "R_PPC_PLT16_HA",      /* name */
         FALSE,                 /* partial_inplace */
@@ -708,7 +717,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_SECTOFF",       /* name */
         FALSE,                 /* partial_inplace */
@@ -738,7 +747,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* 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_SECTOFF_HI",    /* name */
         FALSE,                 /* partial_inplace */
@@ -753,7 +762,7 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         16,                    /* bitsize */
         FALSE,                 /* pc_relative */
         0,                     /* bitpos */
-        complain_overflow_bitfield, /* complain_on_overflow */
+        complain_overflow_dont, /* complain_on_overflow */
         ppc_elf_addr16_ha_reloc, /* special_function */
         "R_PPC_SECTOFF_HA",    /* name */
         FALSE,                 /* partial_inplace */
@@ -1229,7 +1238,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_EMB_NADDR32",   /* name */
         FALSE,                 /* partial_inplace */
@@ -1244,7 +1253,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_EMB_NADDR16",   /* name */
         FALSE,                 /* partial_inplace */
@@ -1390,13 +1399,270 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 
+  /* A relative 8 bit branch.  */
+  HOWTO (R_PPC_VLE_REL8,       /* type */
+        1,                     /* rightshift */
+        1,                     /* size (0 = byte, 1 = short, 2 = long) */
+        8,                     /* bitsize */
+        TRUE,                  /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC_VLE_REL8",      /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0xff,                  /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* A relative 15 bit branch.  */
+  HOWTO (R_PPC_VLE_REL15,      /* type */
+        1,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        15,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        1,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC_VLE_REL15",     /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0xfe,                  /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* A relative 24 bit branch.  */
+  HOWTO (R_PPC_VLE_REL24,      /* type */
+        1,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        24,                    /* bitsize */
+        TRUE,                  /* pc_relative */
+        1,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC_VLE_REL24",     /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1fffffe,             /* dst_mask */
+        TRUE),                 /* pcrel_offset */
+
+  /* The 16 LSBS in split16a format.  */
+  HOWTO (R_PPC_VLE_LO16A,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_VLE_LO16A",     /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1f007ff,             /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* The 16 LSBS in split16d format.  */
+  HOWTO (R_PPC_VLE_LO16D,      /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_VLE_LO16D",             /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1f07ff,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* Bits 16-31 split16a format.  */
+  HOWTO (R_PPC_VLE_HI16A,      /* type */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_VLE_HI16A",             /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1f007ff,             /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* Bits 16-31 split16d format.  */
+  HOWTO (R_PPC_VLE_HI16D,      /* type */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_VLE_HI16D",             /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1f07ff,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* Bits 16-31 (High Adjusted) in split16a format.  */
+  HOWTO (R_PPC_VLE_HA16A,      /* type */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_VLE_HA16A",             /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1f007ff,             /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* Bits 16-31 (High Adjusted) in split16d format.  */
+  HOWTO (R_PPC_VLE_HA16D,      /* type */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_VLE_HA16D",             /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1f07ff,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* 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) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_signed, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC_VLE_SDA21",             /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0xffff,                /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
+  /* 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) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont, /* complain_on_overflow */
+        bfd_elf_generic_reloc, /* special_function */
+        "R_PPC_VLE_SDA21_LO",  /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_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) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_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.  */
+  HOWTO (R_PPC_VLE_SDAREL_LO16D, /* type */
+        0,                     /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_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 */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_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 */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_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 */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_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 */
+        16,                    /* rightshift */
+        2,                     /* size (0 = byte, 1 = short, 2 = long) */
+        16,                    /* bitsize */
+        FALSE,                 /* pc_relative */
+        0,                     /* bitpos */
+        complain_overflow_dont,        /* complain_on_overflow */
+        bfd_elf_generic_reloc,  /* special_function */
+        "R_PPC_VLE_SDAREL_HA16D",      /* name */
+        FALSE,                 /* partial_inplace */
+        0,                     /* src_mask */
+        0x1f07ff,              /* dst_mask */
+        FALSE),                /* pcrel_offset */
+
   HOWTO (R_PPC_IRELATIVE,      /* type */
         0,                     /* rightshift */
         2,                     /* size (0 = byte, 1 = short, 2 = long) */
         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_IRELATIVE",     /* name */
         FALSE,                 /* partial_inplace */
@@ -1411,7 +1677,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 */
@@ -1510,6 +1776,58 @@ static reloc_howto_type ppc_elf_howto_raw[] = {
         0xffff,                /* dst_mask */
         FALSE),                /* pcrel_offset */
 };
+
+/* External 32-bit PPC structure for PRPSINFO.  This structure is
+   ABI-defined, thus we choose to use char arrays here in order to
+   avoid dealing with different types in different architectures.
+
+   The PPC 32-bit structure uses int for `pr_uid' and `pr_gid' while
+   most non-PPC architectures use `short int'.
+
+   This structure will ultimately be written in the corefile's note
+   section, as the PRPSINFO.  */
+
+struct elf_external_ppc_linux_prpsinfo32
+  {
+    char pr_state;                     /* Numeric process state.  */
+    char pr_sname;                     /* Char for pr_state.  */
+    char pr_zomb;                      /* Zombie.  */
+    char pr_nice;                      /* Nice val.  */
+    char pr_flag[4];                   /* Flags.  */
+    char pr_uid[4];
+    char pr_gid[4];
+    char pr_pid[4];
+    char pr_ppid[4];
+    char pr_pgrp[4];
+    char pr_sid[4];
+    char pr_fname[16];                 /* Filename of executable.  */
+    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)
+
 \f
 /* Initialize the ppc_elf_howto_table, so that linear accesses can be done.  */
 
@@ -1548,7 +1866,9 @@ ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     case BFD_RELOC_NONE:               r = R_PPC_NONE;                 break;
     case BFD_RELOC_32:                 r = R_PPC_ADDR32;               break;
     case BFD_RELOC_PPC_BA26:           r = R_PPC_ADDR24;               break;
+    case BFD_RELOC_PPC64_ADDR16_DS:
     case BFD_RELOC_16:                 r = R_PPC_ADDR16;               break;
+    case BFD_RELOC_PPC64_ADDR16_LO_DS:
     case BFD_RELOC_LO16:               r = R_PPC_ADDR16_LO;            break;
     case BFD_RELOC_HI16:               r = R_PPC_ADDR16_HI;            break;
     case BFD_RELOC_HI16_S:             r = R_PPC_ADDR16_HA;            break;
@@ -1559,7 +1879,9 @@ ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     case BFD_RELOC_PPC_B16:            r = R_PPC_REL14;                break;
     case BFD_RELOC_PPC_B16_BRTAKEN:    r = R_PPC_REL14_BRTAKEN;        break;
     case BFD_RELOC_PPC_B16_BRNTAKEN:   r = R_PPC_REL14_BRNTAKEN;       break;
+    case BFD_RELOC_PPC64_GOT16_DS:
     case BFD_RELOC_16_GOTOFF:          r = R_PPC_GOT16;                break;
+    case BFD_RELOC_PPC64_GOT16_LO_DS:
     case BFD_RELOC_LO16_GOTOFF:                r = R_PPC_GOT16_LO;             break;
     case BFD_RELOC_HI16_GOTOFF:                r = R_PPC_GOT16_HI;             break;
     case BFD_RELOC_HI16_S_GOTOFF:      r = R_PPC_GOT16_HA;             break;
@@ -1570,26 +1892,34 @@ ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     case BFD_RELOC_32_PCREL:           r = R_PPC_REL32;                break;
     case BFD_RELOC_32_PLTOFF:          r = R_PPC_PLT32;                break;
     case BFD_RELOC_32_PLT_PCREL:       r = R_PPC_PLTREL32;             break;
+    case BFD_RELOC_PPC64_PLT16_LO_DS:
     case BFD_RELOC_LO16_PLTOFF:                r = R_PPC_PLT16_LO;             break;
     case BFD_RELOC_HI16_PLTOFF:                r = R_PPC_PLT16_HI;             break;
     case BFD_RELOC_HI16_S_PLTOFF:      r = R_PPC_PLT16_HA;             break;
     case BFD_RELOC_GPREL16:            r = R_PPC_SDAREL16;             break;
+    case BFD_RELOC_PPC64_SECTOFF_DS:
     case BFD_RELOC_16_BASEREL:         r = R_PPC_SECTOFF;              break;
+    case BFD_RELOC_PPC64_SECTOFF_LO_DS:
     case BFD_RELOC_LO16_BASEREL:       r = R_PPC_SECTOFF_LO;           break;
     case BFD_RELOC_HI16_BASEREL:       r = R_PPC_SECTOFF_HI;           break;
     case BFD_RELOC_HI16_S_BASEREL:     r = R_PPC_SECTOFF_HA;           break;
     case BFD_RELOC_CTOR:               r = R_PPC_ADDR32;               break;
+    case BFD_RELOC_PPC64_TOC16_DS:
     case BFD_RELOC_PPC_TOC16:          r = R_PPC_TOC16;                break;
     case BFD_RELOC_PPC_TLS:            r = R_PPC_TLS;                  break;
     case BFD_RELOC_PPC_TLSGD:          r = R_PPC_TLSGD;                break;
     case BFD_RELOC_PPC_TLSLD:          r = R_PPC_TLSLD;                break;
     case BFD_RELOC_PPC_DTPMOD:         r = R_PPC_DTPMOD32;             break;
+    case BFD_RELOC_PPC64_TPREL16_DS:
     case BFD_RELOC_PPC_TPREL16:                r = R_PPC_TPREL16;              break;
+    case BFD_RELOC_PPC64_TPREL16_LO_DS:
     case BFD_RELOC_PPC_TPREL16_LO:     r = R_PPC_TPREL16_LO;           break;
     case BFD_RELOC_PPC_TPREL16_HI:     r = R_PPC_TPREL16_HI;           break;
     case BFD_RELOC_PPC_TPREL16_HA:     r = R_PPC_TPREL16_HA;           break;
     case BFD_RELOC_PPC_TPREL:          r = R_PPC_TPREL32;              break;
+    case BFD_RELOC_PPC64_DTPREL16_DS:
     case BFD_RELOC_PPC_DTPREL16:       r = R_PPC_DTPREL16;             break;
+    case BFD_RELOC_PPC64_DTPREL16_LO_DS:
     case BFD_RELOC_PPC_DTPREL16_LO:    r = R_PPC_DTPREL16_LO;          break;
     case BFD_RELOC_PPC_DTPREL16_HI:    r = R_PPC_DTPREL16_HI;          break;
     case BFD_RELOC_PPC_DTPREL16_HA:    r = R_PPC_DTPREL16_HA;          break;
@@ -1626,6 +1956,35 @@ ppc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED,
     case BFD_RELOC_PPC_EMB_RELST_HA:   r = R_PPC_EMB_RELST_HA;         break;
     case BFD_RELOC_PPC_EMB_BIT_FLD:    r = R_PPC_EMB_BIT_FLD;          break;
     case BFD_RELOC_PPC_EMB_RELSDA:     r = R_PPC_EMB_RELSDA;           break;
+    case BFD_RELOC_PPC_VLE_REL8:       r = R_PPC_VLE_REL8;             break;
+    case BFD_RELOC_PPC_VLE_REL15:      r = R_PPC_VLE_REL15;            break;
+    case BFD_RELOC_PPC_VLE_REL24:      r = R_PPC_VLE_REL24;            break;
+    case BFD_RELOC_PPC_VLE_LO16A:      r = R_PPC_VLE_LO16A;            break;
+    case BFD_RELOC_PPC_VLE_LO16D:      r = R_PPC_VLE_LO16D;            break;
+    case BFD_RELOC_PPC_VLE_HI16A:      r = R_PPC_VLE_HI16A;            break;
+    case BFD_RELOC_PPC_VLE_HI16D:      r = R_PPC_VLE_HI16D;            break;
+    case BFD_RELOC_PPC_VLE_HA16A:      r = R_PPC_VLE_HA16A;            break;
+    case BFD_RELOC_PPC_VLE_HA16D:      r = R_PPC_VLE_HA16D;            break;
+    case BFD_RELOC_PPC_VLE_SDA21:      r = R_PPC_VLE_SDA21;            break;
+    case BFD_RELOC_PPC_VLE_SDA21_LO:   r = R_PPC_VLE_SDA21_LO;         break;
+    case BFD_RELOC_PPC_VLE_SDAREL_LO16A:
+      r = R_PPC_VLE_SDAREL_LO16A;
+      break;
+    case BFD_RELOC_PPC_VLE_SDAREL_LO16D:
+      r = R_PPC_VLE_SDAREL_LO16D;
+      break;
+    case BFD_RELOC_PPC_VLE_SDAREL_HI16A:
+      r = R_PPC_VLE_SDAREL_HI16A;
+      break;
+    case BFD_RELOC_PPC_VLE_SDAREL_HI16D:
+      r = R_PPC_VLE_SDAREL_HI16D;
+      break;
+    case BFD_RELOC_PPC_VLE_SDAREL_HA16A:
+      r = R_PPC_VLE_SDAREL_HA16A;
+      break;
+    case BFD_RELOC_PPC_VLE_SDAREL_HA16D:
+      r = R_PPC_VLE_SDAREL_HA16D;
+      break;
     case BFD_RELOC_16_PCREL:           r = R_PPC_REL16;                break;
     case BFD_RELOC_LO16_PCREL:         r = R_PPC_REL16_LO;             break;
     case BFD_RELOC_HI16_PCREL:         r = R_PPC_REL16_HI;             break;
@@ -1660,19 +2019,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));
+                             abfd, r_type);
       bfd_set_error (bfd_error_bad_value);
 
       cache_ptr->howto = ppc_elf_howto_table[R_PPC_NONE];
@@ -1698,9 +2066,6 @@ 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
@@ -1855,10 +2220,10 @@ ppc_elf_grok_prstatus (bfd *abfd, Elf_Internal_Note *note)
 
     case 268:          /* Linux/PPC.  */
       /* pr_cursig */
-      elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12);
+      elf_tdata (abfd)->core->signal = bfd_get_16 (abfd, note->descdata + 12);
 
       /* pr_pid */
-      elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24);
+      elf_tdata (abfd)->core->lwpid = bfd_get_32 (abfd, note->descdata + 24);
 
       /* pr_reg */
       offset = 72;
@@ -1881,9 +2246,11 @@ ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
       return FALSE;
 
     case 128:          /* Linux/PPC elf_prpsinfo.  */
-      elf_tdata (abfd)->core_program
+      elf_tdata (abfd)->core->pid
+       = bfd_get_32 (abfd, note->descdata + 16);
+      elf_tdata (abfd)->core->program
        = _bfd_elfcore_strndup (abfd, note->descdata + 32, 16);
-      elf_tdata (abfd)->core_command
+      elf_tdata (abfd)->core->command
        = _bfd_elfcore_strndup (abfd, note->descdata + 48, 80);
     }
 
@@ -1892,7 +2259,7 @@ ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
      implementations, so strip it off if it exists.  */
 
   {
-    char *command = elf_tdata (abfd)->core_command;
+    char *command = elf_tdata (abfd)->core->command;
     int n = strlen (command);
 
     if (0 < n && command[n - 1] == ' ')
@@ -1902,6 +2269,19 @@ ppc_elf_grok_psinfo (bfd *abfd, Elf_Internal_Note *note)
   return TRUE;
 }
 
+char *
+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);
+
+  return elfcore_write_note (abfd, buf, bufsiz,
+                            "CORE", NT_PRPSINFO, &data, sizeof (data));
+}
+
 static char *
 ppc_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, ...)
 {
@@ -1916,7 +2296,7 @@ ppc_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, ...)
        va_list ap;
 
        va_start (ap, note_type);
-       memset (data, 0, 32);
+       memset (data, 0, sizeof (data));
        strncpy (data + 32, va_arg (ap, const char *), 16);
        strncpy (data + 48, va_arg (ap, const char *), 80);
        va_end (ap);
@@ -1948,7 +2328,29 @@ ppc_elf_write_core_note (bfd *abfd, char *buf, int *bufsiz, int note_type, ...)
     }
 }
 
-/* Return address for Ith PLT stub in section PLT, for relocation REL
+static flagword
+ppc_elf_lookup_section_flags (char *flag_name)
+{
+
+  if (!strcmp (flag_name, "SHF_PPC_VLE"))
+    return SHF_PPC_VLE;
+
+  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.  */
 
 static bfd_vma
@@ -2021,6 +2423,70 @@ ppc_elf_additional_program_headers (bfd *abfd,
   return ret;
 }
 
+/* Modify the segment map for VLE executables.  */
+
+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;
+
+  /* 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
+     there is no mixing of VLE & non-VLE sections in a text segment.
+     If we find that case, we split the segment.
+     We maintain the original output section order.  */
+
+  for (m = elf_seg_map (abfd); m != NULL; m = m->next)
+    {
+      if (m->count == 0)
+       continue;
+
+      sect0_vle = (elf_section_flags (m->sections[0]) & SHF_PPC_VLE) != 0;
+      for (j = 1; j < m->count; ++j)
+       {
+         sectj_vle = (elf_section_flags (m->sections[j]) & SHF_PPC_VLE) != 0;
+
+         if (sectj_vle != sect0_vle)
+           break;
+        }
+      if (j >= m->count)
+       continue;
+
+      /* 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);
+      if (n == NULL)
+        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->next = m->next;
+      m->next = n;
+
+      /* Fix the current segment  */
+      m->count = j;
+    }
+
+  return TRUE;
+}
+
 /* Add extra PPC sections -- Note, for now, make .sbss2 and
    .PPC.EMB.sbss0 a normal section, and not a bss section so
    that the linker doesn't crater when trying to make more than
@@ -2169,7 +2635,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;
 
@@ -2459,7 +2925,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
@@ -2490,9 +2955,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;
 
@@ -2503,6 +2969,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;
@@ -2519,7 +2988,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.  */
@@ -2559,26 +3028,6 @@ ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
    ppc_elf_finish_dynamic_sections is one of the last functions
    called.  */
 
-/* The PPC linker needs to keep track of the number of relocs that it
-   decides to copy as dynamic relocs in check_relocs for each symbol.
-   This is so that it can later discard them if they are found to be
-   unnecessary.  We store the information in a field extending the
-   regular ELF linker hash table.  */
-
-struct ppc_elf_dyn_relocs
-{
-  struct ppc_elf_dyn_relocs *next;
-
-  /* The input section of the reloc.  */
-  asection *sec;
-
-  /* Total number of relocs copied for the input section.  */
-  bfd_size_type count;
-
-  /* Number of pc-relative relocs copied for the input section.  */
-  bfd_size_type pc_count;
-};
-
 /* Track PLT entries needed for a given symbol.  We might need more
    than one glink entry per symbol when generating a pic binary.  */
 struct plt_entry
@@ -2641,6 +3090,21 @@ must_be_dyn_reloc (struct bfd_link_info *info,
    shared lib.  */
 #define ELIMINATE_COPY_RELOCS 1
 
+/* Used to track dynamic relocations for local symbols.  */
+struct ppc_dyn_relocs
+{
+  struct ppc_dyn_relocs *next;
+
+  /* The input section of the reloc.  */
+  asection *sec;
+
+  /* Total number of relocs copied for the input section.  */
+  unsigned int count : 31;
+
+  /* Whether this entry is for STT_GNU_IFUNC symbols.  */
+  unsigned int ifunc : 1;
+};
+
 /* PPC ELF linker hash entry.  */
 
 struct ppc_elf_link_hash_entry
@@ -2653,7 +3117,7 @@ struct ppc_elf_link_hash_entry
   elf_linker_section_pointers_t *linker_section_pointer;
 
   /* Track dynamic relocs copied for this symbol.  */
-  struct ppc_elf_dyn_relocs *dyn_relocs;
+  struct elf_dyn_relocs *dyn_relocs;
 
   /* Contexts in which symbol is used in the GOT (or TOC).
      TLS_GD .. TLS_TLS bits are or'd into the mask as the
@@ -2682,6 +3146,9 @@ struct ppc_elf_link_hash_table
 {
   struct elf_link_hash_table elf;
 
+  /* Various options passed from the linker.  */
+  struct ppc_elf_params *params;
+
   /* Short-cuts to get to dynamic linker sections.  */
   asection *got;
   asection *relgot;
@@ -2696,6 +3163,7 @@ struct ppc_elf_link_hash_table
   asection *relsbss;
   elf_linker_section_t sdata[2];
   asection *sbss;
+  asection *glink_eh_frame;
 
   /* The (unloaded but important) .rela.plt.unloaded on VxWorks.  */
   asection *srelplt2;
@@ -2726,12 +3194,6 @@ struct ppc_elf_link_hash_table
   /* The type of PLT we have chosen to use.  */
   enum ppc_elf_plt_type plt_type;
 
-  /* Set if we should emit symbols for stubs.  */
-  unsigned int emit_stub_syms:1;
-
-  /* Set if __tls_get_addr optimization should not be done.  */
-  unsigned int no_tls_get_addr_opt:1;
-
   /* True if the target system is VxWorks.  */
   unsigned int is_vxworks:1;
 
@@ -2747,7 +3209,7 @@ struct ppc_elf_link_hash_table
 };
 
 /* Rename some of the generic section flags to better document how they
-   are used here.  */
+   are used for ppc32.  The flags are only valid for ppc32 elf objects.  */
 
 /* Nonzero if this section has TLS related relocations.  */
 #define has_tls_reloc sec_flg0
@@ -2785,6 +3247,7 @@ ppc_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
       ppc_elf_hash_entry (entry)->linker_section_pointer = NULL;
       ppc_elf_hash_entry (entry)->dyn_relocs = NULL;
       ppc_elf_hash_entry (entry)->tls_mask = 0;
+      ppc_elf_hash_entry (entry)->has_sda_refs = 0;
     }
 
   return entry;
@@ -2796,6 +3259,7 @@ 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, 12 };
 
   ret = bfd_zmalloc (sizeof (struct ppc_elf_link_hash_table));
   if (ret == NULL)
@@ -2815,6 +3279,8 @@ ppc_elf_link_hash_table_create (bfd *abfd)
   ret->elf.init_plt_offset.offset = 0;
   ret->elf.init_plt_offset.glist = NULL;
 
+  ret->params = &default_params;
+
   ret->sdata[0].name = ".sdata";
   ret->sdata[0].sym_name = "_SDA_BASE_";
   ret->sdata[0].bss_name = ".sbss";
@@ -2830,6 +3296,17 @@ ppc_elf_link_hash_table_create (bfd *abfd)
   return &ret->elf.root;
 }
 
+/* Hook linker params into hash table.  */
+
+void
+ppc_elf_link_params (struct bfd_link_info *info, struct ppc_elf_params *params)
+{
+  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+  if (htab)
+    htab->params = params;
+}
+
 /* Create .got and the related sections.  */
 
 static bfd_boolean
@@ -2843,13 +3320,13 @@ ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
     return FALSE;
 
   htab = ppc_elf_hash_table (info);
-  htab->got = s = bfd_get_section_by_name (abfd, ".got");
+  htab->got = s = bfd_get_linker_section (abfd, ".got");
   if (s == NULL)
     abort ();
 
   if (htab->is_vxworks)
     {
-      htab->sgotplt = bfd_get_section_by_name (abfd, ".got.plt");
+      htab->sgotplt = bfd_get_linker_section (abfd, ".got.plt");
       if (!htab->sgotplt)
        abort ();
     }
@@ -2863,13 +3340,43 @@ ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
        return FALSE;
     }
 
-  htab->relgot = bfd_get_section_by_name (abfd, ".rela.got");
+  htab->relgot = bfd_get_linker_section (abfd, ".rela.got");
   if (!htab->relgot)
     abort ();
 
   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)
 {
@@ -2882,9 +3389,21 @@ ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
   s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags);
   htab->glink = s;
   if (s == NULL
-      || !bfd_set_section_alignment (abfd, s, 4))
+      || !bfd_set_section_alignment (abfd, s,
+                                    htab->params->ppc476_workaround ? 6 : 4))
     return FALSE;
 
+  if (!info->no_ld_generated_unwind_info)
+    {
+      flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+              | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+      s = bfd_make_section_anyway_with_flags (abfd, ".eh_frame", flags);
+      htab->glink_eh_frame = s;
+      if (s == NULL
+         || !bfd_set_section_alignment (abfd, s, 2))
+       return FALSE;
+    }
+
   flags = SEC_ALLOC | SEC_LINKER_CREATED;
   s = bfd_make_section_anyway_with_flags (abfd, ".iplt", flags);
   htab->iplt = s;
@@ -2894,11 +3413,20 @@ ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info)
 
   flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
           | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-  s = bfd_make_section_with_flags (abfd, ".rela.iplt", flags);
+  s = bfd_make_section_anyway_with_flags (abfd, ".rela.iplt", flags);
   htab->reliplt = s;
   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;
 }
 
@@ -2926,19 +3454,19 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
       && !ppc_elf_create_glink (abfd, info))
     return FALSE;
 
-  htab->dynbss = bfd_get_section_by_name (abfd, ".dynbss");
-  s = bfd_make_section_with_flags (abfd, ".dynsbss",
-                                  SEC_ALLOC | SEC_LINKER_CREATED);
+  htab->dynbss = bfd_get_linker_section (abfd, ".dynbss");
+  s = bfd_make_section_anyway_with_flags (abfd, ".dynsbss",
+                                         SEC_ALLOC | SEC_LINKER_CREATED);
   htab->dynsbss = s;
   if (s == NULL)
     return FALSE;
 
   if (! info->shared)
     {
-      htab->relbss = bfd_get_section_by_name (abfd, ".rela.bss");
+      htab->relbss = bfd_get_linker_section (abfd, ".rela.bss");
       flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
               | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-      s = bfd_make_section_with_flags (abfd, ".rela.sbss", flags);
+      s = bfd_make_section_anyway_with_flags (abfd, ".rela.sbss", flags);
       htab->relsbss = s;
       if (s == NULL
          || ! bfd_set_section_alignment (abfd, s, 2))
@@ -2949,8 +3477,8 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
       && !elf_vxworks_create_dynamic_sections (abfd, info, &htab->srelplt2))
     return FALSE;
 
-  htab->relplt = bfd_get_section_by_name (abfd, ".rela.plt");
-  htab->plt = s = bfd_get_section_by_name (abfd, ".plt");
+  htab->relplt = bfd_get_linker_section (abfd, ".rela.plt");
+  htab->plt = s = bfd_get_linker_section (abfd, ".plt");
   if (s == NULL)
     abort ();
 
@@ -2973,18 +3501,35 @@ ppc_elf_copy_indirect_symbol (struct bfd_link_info *info,
   edir = (struct ppc_elf_link_hash_entry *) dir;
   eind = (struct ppc_elf_link_hash_entry *) ind;
 
+  edir->tls_mask |= eind->tls_mask;
+  edir->has_sda_refs |= eind->has_sda_refs;
+
+  /* If called to transfer flags for a weakdef during processing
+     of elf_adjust_dynamic_symbol, don't copy non_got_ref.
+     We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
+  if (!(ELIMINATE_COPY_RELOCS
+       && eind->elf.root.type != bfd_link_hash_indirect
+       && edir->elf.dynamic_adjusted))
+    edir->elf.non_got_ref |= eind->elf.non_got_ref;
+
+  edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
+  edir->elf.ref_regular |= eind->elf.ref_regular;
+  edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
+  edir->elf.needs_plt |= eind->elf.needs_plt;
+  edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
+
   if (eind->dyn_relocs != NULL)
     {
       if (edir->dyn_relocs != NULL)
        {
-         struct ppc_elf_dyn_relocs **pp;
-         struct ppc_elf_dyn_relocs *p;
+         struct elf_dyn_relocs **pp;
+         struct elf_dyn_relocs *p;
 
          /* Add reloc counts against the indirect sym to the direct sym
             list.  Merge any entries against the same section.  */
          for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
            {
-             struct ppc_elf_dyn_relocs *q;
+             struct elf_dyn_relocs *q;
 
              for (q = edir->dyn_relocs; q != NULL; q = q->next)
                if (q->sec == p->sec)
@@ -3004,24 +3549,13 @@ ppc_elf_copy_indirect_symbol (struct bfd_link_info *info,
       eind->dyn_relocs = NULL;
     }
 
-  edir->tls_mask |= eind->tls_mask;
-  edir->has_sda_refs |= eind->has_sda_refs;
-
-  /* If called to transfer flags for a weakdef during processing
-     of elf_adjust_dynamic_symbol, don't copy non_got_ref.
-     We clear it ourselves for ELIMINATE_COPY_RELOCS.  */
-  if (!(ELIMINATE_COPY_RELOCS
-       && eind->elf.root.type != bfd_link_hash_indirect
-       && edir->elf.dynamic_adjusted))
-    edir->elf.non_got_ref |= eind->elf.non_got_ref;
-
-  edir->elf.ref_dynamic |= eind->elf.ref_dynamic;
-  edir->elf.ref_regular |= eind->elf.ref_regular;
-  edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak;
-  edir->elf.needs_plt |= eind->elf.needs_plt;
-  edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed;
-
-  /* If we were called to copy over info for a weak sym, that's all.  */
+  /* If we were called to copy over info for a weak sym, that's all.
+     You might think dyn_relocs need not be copied over;  After all,
+     both syms will be dynamic or both non-dynamic so we're just
+     moving reloc accounting around.  However, ELIMINATE_COPY_RELOCS
+     code in ppc_elf_adjust_dynamic_symbol needs to check for
+     dyn_relocs in read-only sections, and it does so on what is the
+     DIR sym here.  */
   if (eind->elf.root.type != bfd_link_hash_indirect)
     return;
 
@@ -3111,58 +3645,15 @@ 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_tdata (info->output_bfd)->has_ifunc_symbols = TRUE;
+  if ((ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC
+       || ELF_ST_BIND (sym->st_info) == STB_GNU_UNIQUE)
+      && (abfd->flags & DYNAMIC) == 0
+      && bfd_get_flavour (info->output_bfd) == bfd_target_elf_flavour)
+    elf_tdata (info->output_bfd)->has_gnu_symbols = TRUE;
 
   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 *
@@ -3181,10 +3672,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;
@@ -3252,6 +3743,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;
 
@@ -3434,6 +3927,10 @@ ppc_elf_check_relocs (bfd *abfd,
          while (h->root.type == bfd_link_hash_indirect
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         /* PR15323, ref flags aren't set for references in the same
+            object.  */
+         h->root.non_ir_ref = 1;
        }
 
       /* If a relocation refers to _GLOBAL_OFFSET_TABLE_, create the .got.
@@ -3459,13 +3956,11 @@ ppc_elf_check_relocs (bfd *abfd,
          if (isym == NULL)
            return FALSE;
 
-         if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC
-             && (!info->shared
-                 || is_branch_reloc (r_type)))
+         if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
            {
              struct plt_entry **ifunc;
-             bfd_vma addend;
 
+             /* Set PLT_IFUNC flag for this sym, no GOT entry yet.  */
              ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx,
                                             PLT_IFUNC);
              if (ifunc == NULL)
@@ -3474,15 +3969,19 @@ 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.  */
-             addend = 0;
-             if (r_type == R_PPC_PLTREL24)
+             if (!info->shared
+                 || is_branch_reloc (r_type))
                {
-                 ppc_elf_tdata (abfd)->makes_plt_call = 1;
-                 if (info->shared)
-                   addend = rel->r_addend;
+                 bfd_vma addend = 0;
+                 if (r_type == R_PPC_PLTREL24)
+                   {
+                     ppc_elf_tdata (abfd)->makes_plt_call = 1;
+                     if (info->shared)
+                       addend = rel->r_addend;
+                   }
+                 if (!update_plt_info (abfd, ifunc, got2, addend))
+                   return FALSE;
                }
-             if (!update_plt_info (abfd, ifunc, got2, addend))
-               return FALSE;
            }
        }
 
@@ -3528,7 +4027,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 (info->shared)
            info->flags |= DF_STATIC_TLS;
          tls_type = TLS_TLS | TLS_TPREL;
          goto dogottls;
@@ -3581,12 +4080,9 @@ ppc_elf_check_relocs (bfd *abfd,
              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)
            {
@@ -3602,12 +4098,9 @@ ppc_elf_check_relocs (bfd *abfd,
              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)
            {
@@ -3617,9 +4110,15 @@ ppc_elf_check_relocs (bfd *abfd,
          break;
 
        case R_PPC_SDAREL16:
-         if (htab->sdata[0].sym == NULL
-             && !create_sdata_sym (info, &htab->sdata[0]))
-           return FALSE;
+         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:
          if (h != NULL)
            {
              ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
@@ -3627,15 +4126,24 @@ ppc_elf_check_relocs (bfd *abfd,
            }
          break;
 
+       case R_PPC_VLE_REL8:
+       case R_PPC_VLE_REL15:
+       case R_PPC_VLE_REL24:
+       case R_PPC_VLE_LO16A:
+       case R_PPC_VLE_LO16D:
+       case R_PPC_VLE_HI16A:
+       case R_PPC_VLE_HI16D:
+       case R_PPC_VLE_HA16A:
+       case R_PPC_VLE_HA16D:
+         break;
+
        case R_PPC_EMB_SDA2REL:
          if (info->shared)
            {
              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;
@@ -3643,6 +4151,8 @@ ppc_elf_check_relocs (bfd *abfd,
            }
          break;
 
+       case R_PPC_VLE_SDA21_LO:
+       case R_PPC_VLE_SDA21:
        case R_PPC_EMB_SDA21:
        case R_PPC_EMB_RELSDA:
          if (info->shared)
@@ -3650,12 +4160,6 @@ ppc_elf_check_relocs (bfd *abfd,
              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;
@@ -3699,12 +4203,9 @@ ppc_elf_check_relocs (bfd *abfd,
            {
              /* It does not make sense to have a procedure linkage
                 table entry for a local symbol.  */
-             (*_bfd_error_handler) (_("%B(%A+0x%lx): %s reloc against "
-                                      "local symbol"),
-                                    abfd,
-                                    sec,
-                                    (long) rel->r_offset,
-                                    ppc_elf_howto_table[r_type]->name);
+             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;
            }
@@ -3779,6 +4280,20 @@ ppc_elf_check_relocs (bfd *abfd,
              htab->plt_type = PLT_OLD;
              htab->old_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;
+           }
          break;
 
          /* This relocation describes the C++ object vtable hierarchy.
@@ -3803,7 +4318,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 (info->shared)
            info->flags |= DF_STATIC_TLS;
          goto dodyn;
 
@@ -3920,7 +4435,7 @@ ppc_elf_check_relocs (bfd *abfd,
          if ((info->shared
               && (must_be_dyn_reloc (info, r_type)
                   || (h != NULL
-                      && (! info->symbolic
+                      && (!SYMBOLIC_BIND (info, h)
                           || h->root.type == bfd_link_hash_defweak
                           || !h->def_regular))))
              || (ELIMINATE_COPY_RELOCS
@@ -3929,9 +4444,6 @@ ppc_elf_check_relocs (bfd *abfd,
                  && (h->root.type == bfd_link_hash_defweak
                      || !h->def_regular)))
            {
-             struct ppc_elf_dyn_relocs *p;
-             struct ppc_elf_dyn_relocs **rel_head;
-
 #ifdef DEBUG
              fprintf (stderr,
                       "ppc_elf_check_relocs needs to "
@@ -3955,13 +4467,34 @@ ppc_elf_check_relocs (bfd *abfd,
                 relocations we need for this symbol.  */
              if (h != NULL)
                {
+                 struct elf_dyn_relocs *p;
+                 struct elf_dyn_relocs **rel_head;
+
                  rel_head = &ppc_elf_hash_entry (h)->dyn_relocs;
+                 p = *rel_head;
+                 if (p == NULL || p->sec != sec)
+                   {
+                     p = bfd_alloc (htab->elf.dynobj, sizeof *p);
+                     if (p == NULL)
+                       return FALSE;
+                     p->next = *rel_head;
+                     *rel_head = p;
+                     p->sec = sec;
+                     p->count = 0;
+                     p->pc_count = 0;
+                   }
+                 p->count += 1;
+                 if (!must_be_dyn_reloc (info, r_type))
+                   p->pc_count += 1;
                }
              else
                {
                  /* Track dynamic relocs needed for local syms too.
                     We really need local syms available to do this
                     easily.  Oh well.  */
+                 struct ppc_dyn_relocs *p;
+                 struct ppc_dyn_relocs **rel_head;
+                 bfd_boolean is_ifunc;
                  asection *s;
                  void *vpp;
                  Elf_Internal_Sym *isym;
@@ -3976,25 +4509,24 @@ ppc_elf_check_relocs (bfd *abfd,
                    s = sec;
 
                  vpp = &elf_section_data (s)->local_dynrel;
-                 rel_head = (struct ppc_elf_dyn_relocs **) vpp;
-               }
-
-             p = *rel_head;
-             if (p == NULL || p->sec != sec)
-               {
-                 p = bfd_alloc (htab->elf.dynobj, sizeof *p);
-                 if (p == NULL)
-                   return FALSE;
-                 p->next = *rel_head;
-                 *rel_head = p;
-                 p->sec = sec;
-                 p->count = 0;
-                 p->pc_count = 0;
+                 rel_head = (struct ppc_dyn_relocs **) vpp;
+                 is_ifunc = ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC;
+                 p = *rel_head;
+                 if (p != NULL && p->sec == sec && p->ifunc != is_ifunc)
+                   p = p->next;
+                 if (p == NULL || p->sec != sec || p->ifunc != is_ifunc)
+                   {
+                     p = bfd_alloc (htab->elf.dynobj, sizeof *p);
+                     if (p == NULL)
+                       return FALSE;
+                     p->next = *rel_head;
+                     *rel_head = p;
+                     p->sec = sec;
+                     p->ifunc = is_ifunc;
+                     p->count = 0;
+                   }
+                 p->count += 1;
                }
-
-             p->count += 1;
-             if (!must_be_dyn_reloc (info, r_type))
-               p->pc_count += 1;
            }
 
          break;
@@ -4163,7 +4695,7 @@ ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
   if (!is_ppc_elf (ibfd) || !is_ppc_elf (obfd))
     return TRUE;
 
-  /* Check if we have the same endianess.  */
+  /* Check if we have the same endianness.  */
   if (! _bfd_generic_verify_endian_match (ibfd, obfd))
     return FALSE;
 
@@ -4243,30 +4775,62 @@ ppc_elf_merge_private_bfd_data (bfd *ibfd, bfd *obfd)
 
   return TRUE;
 }
+
+static void
+ppc_elf_vle_split16 (bfd *output_bfd, bfd_byte *loc,
+                    bfd_vma value,
+                    split16_format_type split16_format)
+
+{
+  unsigned int insn, top5;
+
+  insn = bfd_get_32 (output_bfd, loc);
+  top5 = value & 0xf800;
+  top5 = top5 << (split16_format == split16a_type ? 9 : 5);
+  insn |= top5;
+  insn |= value & 0x7ff;
+  bfd_put_32 (output_bfd, insn, loc);
+}
+
 \f
 /* Choose which PLT scheme to use, and set .plt flags appropriately.
    Returns -1 on error, 0 for old PLT, 1 for new PLT.  */
 int
 ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
-                          struct bfd_link_info *info,
-                          enum ppc_elf_plt_type plt_style,
-                          int emit_stub_syms)
+                          struct bfd_link_info *info)
 {
   struct ppc_elf_link_hash_table *htab;
   flagword flags;
 
   htab = ppc_elf_hash_table (info);
 
-  htab->emit_stub_syms = emit_stub_syms;
-
   if (htab->plt_type == PLT_UNSET)
     {
-      if (plt_style == PLT_OLD)
+      struct elf_link_hash_entry *h;
+
+      if (htab->params->plt_style == PLT_OLD)
        htab->plt_type = PLT_OLD;
+      else if (info->shared
+              && htab->elf.dynamic_sections_created
+              && (h = elf_link_hash_lookup (&htab->elf, "_mcount",
+                                            FALSE, FALSE, TRUE)) != NULL
+              && (h->type == STT_FUNC
+                  || h->needs_plt)
+              && h->ref_regular
+              && !(SYMBOL_CALLS_LOCAL (info, h)
+                   || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+                       && h->root.type == bfd_link_hash_undefweak)))
+       {
+         /* Profiling of shared libs (and pies) is not supported with
+            secure plt, because ppc32 does profiling before a
+            function prologue and a secure plt pic call stubs needs
+            r30 to be set up.  */
+         htab->plt_type = PLT_OLD;
+       }
       else
        {
          bfd *ibfd;
-         enum ppc_elf_plt_type plt_type = plt_style;
+         enum ppc_elf_plt_type plt_type = htab->params->plt_style;
 
          /* Look through the reloc flags left by ppc_elf_check_relocs.
             Use the old style bss plt if a file makes plt calls
@@ -4274,7 +4838,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)
@@ -4289,8 +4853,14 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
          htab->plt_type = plt_type;
        }
     }
-  if (htab->plt_type == PLT_OLD && plt_style == PLT_NEW)
-    info->callbacks->info (_("Using bss-plt due to %B"), htab->old_bfd);
+  if (htab->plt_type == PLT_OLD && htab->params->plt_style == PLT_NEW)
+    {
+      if (htab->old_bfd != NULL)
+       info->callbacks->einfo (_("%P: bss-plt forced due to %B\n"),
+                               htab->old_bfd);
+      else
+       info->callbacks->einfo (_("%P: bss-plt forced by profiling\n"));
+    }
 
   BFD_ASSERT (htab->plt_type != PLT_VXWORKS);
 
@@ -4380,7 +4950,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
       r_symndx = ELF32_R_SYM (rel->r_info);
       if (r_symndx >= symtab_hdr->sh_info)
        {
-         struct ppc_elf_dyn_relocs **pp, *p;
+         struct elf_dyn_relocs **pp, *p;
          struct ppc_elf_link_hash_entry *eh;
 
          h = sym_hashes[r_symndx - symtab_hdr->sh_info];
@@ -4519,16 +5089,17 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
    generic ELF tls_setup function.  */
 
 asection *
-ppc_elf_tls_setup (bfd *obfd,
-                  struct bfd_link_info *info,
-                  int no_tls_get_addr_opt)
+ppc_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
 {
   struct ppc_elf_link_hash_table *htab;
 
   htab = ppc_elf_hash_table (info);
   htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
                                             FALSE, FALSE, TRUE);
-  if (!no_tls_get_addr_opt)
+  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;
       opt = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_opt",
@@ -4573,9 +5144,8 @@ ppc_elf_tls_setup (bfd *obfd,
            }
        }
       else
-       no_tls_get_addr_opt = TRUE;
+       htab->params->no_tls_get_addr_opt = TRUE;
     }
-  htab->no_tls_get_addr_opt = no_tls_get_addr_opt;
   if (htab->plt_type == PLT_NEW
       && htab->plt != NULL
       && htab->plt->output_section != NULL)
@@ -4630,12 +5200,17 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
     return TRUE;
 
   htab = ppc_elf_hash_table (info);
+  if (htab == NULL)
+    return FALSE;
+
   /* Make two passes through the relocs.  First time check that tls
      relocs involved in setting up a tls_get_addr call are indeed
-     followed by such a call.  If they are not, exclude them from
-     the optimizations done on the second pass.  */
+     followed by such a call.  If they are not, don't do any tls
+     optimization.  On the second pass twiddle tls_mask flags to
+     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);
@@ -4645,6 +5220,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
          if (sec->has_tls_reloc && !bfd_is_abs_section (sec->output_section))
            {
              Elf_Internal_Rela *relstart, *rel, *relend;
+             int expecting_tls_get_addr = 0;
 
              /* Read the relocations.  */
              relstart = _bfd_elf_link_read_relocs (ibfd, sec, NULL, NULL,
@@ -4661,7 +5237,6 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                  char *tls_mask;
                  char tls_set, tls_clear;
                  bfd_boolean is_local;
-                 int expecting_tls_get_addr;
                  bfd_signed_vma *got_count;
 
                  r_symndx = ELF32_R_SYM (rel->r_info);
@@ -4676,13 +5251,34 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                        h = (struct elf_link_hash_entry *) h->root.u.i.link;
                    }
 
-                 expecting_tls_get_addr = 0;
                  is_local = FALSE;
                  if (h == NULL
                      || !h->def_dynamic)
                    is_local = TRUE;
 
                  r_type = ELF32_R_TYPE (rel->r_info);
+                 /* If this section has old-style __tls_get_addr calls
+                    without marker relocs, then check that each
+                    __tls_get_addr call reloc is preceded by a reloc
+                    that conceivably belongs to the __tls_get_addr arg
+                    setup insn.  If we don't find matching arg setup
+                    relocs, don't do any tls optimization.  */
+                 if (pass == 0
+                     && sec->has_tls_get_addr_call
+                     && h != NULL
+                     && h == htab->tls_get_addr
+                     && !expecting_tls_get_addr
+                     && is_branch_reloc (r_type))
+                   {
+                     info->callbacks->minfo ("%H __tls_get_addr lost arg, "
+                                             "TLS optimization disabled\n",
+                                             ibfd, sec, rel->r_offset);
+                     if (elf_section_data (sec)->relocs != relstart)
+                       free (relstart);
+                     return TRUE;
+                   }
+
+                 expecting_tls_get_addr = 0;
                  switch (r_type)
                    {
                    case R_PPC_GOT_TLSLD16:
@@ -4759,9 +5355,13 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                      /* Uh oh, we didn't find the expected call.  We
                         could just mark this symbol to exclude it
                         from tls optimization but it's safer to skip
-                        the entire section.  */
-                     sec->has_tls_reloc = 0;
-                     break;
+                        the entire optimization.  */
+                     info->callbacks->minfo (_("%H arg lost __tls_get_addr, "
+                                               "TLS optimization disabled\n"),
+                                             ibfd, sec, rel->r_offset);
+                     if (elf_section_data (sec)->relocs != relstart)
+                       free (relstart);
+                     return TRUE;
                    }
 
                  if (expecting_tls_get_addr)
@@ -4848,7 +5448,7 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
 static bfd_boolean
 readonly_dynrelocs (struct elf_link_hash_entry *h)
 {
-  struct ppc_elf_dyn_relocs *p;
+  struct elf_dyn_relocs *p;
 
   for (p = ppc_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
     {
@@ -4919,9 +5519,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
@@ -4931,12 +5546,12 @@ 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;
        }
       return TRUE;
@@ -4973,6 +5588,13 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   if (!h->non_got_ref)
     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.
       We can't do this if there are any small data relocations.  This
@@ -4989,10 +5611,13 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
       return TRUE;
     }
 
-  if (h->size == 0)
+  /* 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.  Text relocations are preferable
+     to an incorrect program.  */
+  if (h->protected_def)
     {
-      (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"),
-                            h->root.root.string);
+      h->non_got_ref = 0;
       return TRUE;
     }
 
@@ -5019,7 +5644,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      copy the initial value out of the dynamic object and into the
      runtime process image.  We need to remember the offset into the
      .rela.bss section we are going to use.  */
-  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0)
+  if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
     {
       asection *srel;
 
@@ -5032,7 +5657,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
@@ -5083,6 +5708,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;
 }
@@ -5132,17 +5758,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   struct bfd_link_info *info = inf;
   struct ppc_elf_link_hash_entry *eh;
   struct ppc_elf_link_hash_table *htab;
-  struct ppc_elf_dyn_relocs *p;
+  struct elf_dyn_relocs *p;
 
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
-  if (h->root.type == bfd_link_hash_warning)
-    /* When warning symbols are created, they **replace** the "real"
-       entry in the hash table, thus we never get to see the real
-       symbol in a hash traversal.  So look at it now.  */
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
   htab = ppc_elf_hash_table (info);
   if (htab->elf.dynamic_sections_created
       || h->type == STT_GNU_IFUNC)
@@ -5189,7 +5809,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                        glink_offset = s->size;
                        s->size += GLINK_ENTRY_SIZE;
                        if (h == htab->tls_get_addr
-                           && !htab->no_tls_get_addr_opt)
+                           && !htab->params->no_tls_get_addr_opt)
                          s->size += TLS_GET_ADDR_GLINK_SIZE - GLINK_ENTRY_SIZE;
                      }
                    if (!doneone
@@ -5202,7 +5822,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                      }
                    ent->glink_offset = glink_offset;
 
-                   if (htab->emit_stub_syms
+                   if (htab->params->emit_stub_syms
                        && !add_stub_sym (ent, h, info))
                      return FALSE;
                  }
@@ -5358,6 +5978,9 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
                  || eh->elf.root.type != bfd_link_hash_undefweak))
            {
              asection *rsec = htab->relgot;
+
+             if (eh->elf.type == STT_GNU_IFUNC)
+               rsec = htab->reliplt;
              /* All the entries we allocated need relocs.
                 Except LD only needs one.  */
              if ((eh->tls_mask & TLS_LD) != 0
@@ -5390,7 +6013,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
         then they should avoid writing weird assembly.  */
       if (SYMBOL_CALLS_LOCAL (info, h))
        {
-         struct ppc_elf_dyn_relocs **pp;
+         struct elf_dyn_relocs **pp;
 
          for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
            {
@@ -5405,7 +6028,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
 
       if (htab->is_vxworks)
        {
-         struct ppc_elf_dyn_relocs **pp;
+         struct elf_dyn_relocs **pp;
 
          for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
            {
@@ -5475,7 +6098,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
   for (p = eh->dyn_relocs; p != NULL; p = p->next)
     {
       asection *sreloc = elf_section_data (p->sec)->sreloc;
-      if (!htab->elf.dynamic_sections_created)
+      if (eh->elf.type == STT_GNU_IFUNC)
        sreloc = htab->reliplt;
       sreloc->size += p->count * sizeof (Elf32_External_Rela);
     }
@@ -5492,9 +6115,6 @@ maybe_set_textrel (struct elf_link_hash_entry *h, void *info)
   if (h->root.type == bfd_link_hash_indirect)
     return TRUE;
 
-  if (h->root.type == bfd_link_hash_warning)
-    h = (struct elf_link_hash_entry *) h->root.u.i.link;
-
   if (readonly_dynrelocs (h))
     {
       ((struct bfd_link_info *) info)->flags |= DF_TEXTREL;
@@ -5505,10 +6125,24 @@ maybe_set_textrel (struct elf_link_hash_entry *h, void *info)
   return TRUE;
 }
 
+static const unsigned char glink_eh_frame_cie[] =
+{
+  0, 0, 0, 16,                         /* length.  */
+  0, 0, 0, 0,                          /* id.  */
+  1,                                   /* CIE version.  */
+  'z', 'R', 0,                         /* Augmentation string.  */
+  4,                                   /* Code alignment.  */
+  0x7c,                                        /* Data alignment.  */
+  65,                                  /* RA reg.  */
+  1,                                   /* Augmentation size.  */
+  DW_EH_PE_pcrel | DW_EH_PE_sdata4,    /* FDE encoding.  */
+  DW_CFA_def_cfa, 1, 0                 /* def_cfa: r1 offset 0.  */
+};
+
 /* 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;
@@ -5528,7 +6162,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       /* Set the contents of the .interp section to the interpreter.  */
       if (info->executable)
        {
-         s = bfd_get_section_by_name (htab->elf.dynobj, ".interp");
+         s = bfd_get_linker_section (htab->elf.dynobj, ".interp");
          BFD_ASSERT (s != NULL);
          s->size = sizeof ELF_DYNAMIC_INTERPRETER;
          s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER;
@@ -5542,7 +6176,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;
@@ -5557,9 +6191,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
 
       for (s = ibfd->sections; s != NULL; s = s->next)
        {
-         struct ppc_elf_dyn_relocs *p;
+         struct ppc_dyn_relocs *p;
 
-         for (p = ((struct ppc_elf_dyn_relocs *)
+         for (p = ((struct ppc_dyn_relocs *)
                    elf_section_data (s)->local_dynrel);
               p != NULL;
               p = p->next)
@@ -5582,7 +6216,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
              else if (p->count != 0)
                {
                  asection *sreloc = elf_section_data (p->sec)->sreloc;
-                 if (!htab->elf.dynamic_sections_created)
+                 if (p->ifunc)
                    sreloc = htab->reliplt;
                  sreloc->size += p->count * sizeof (Elf32_External_Rela);
                  if ((p->sec->output_section->flags
@@ -5627,8 +6261,12 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
              {
                *local_got = allocate_got (htab, need);
                if (info->shared)
-                 htab->relgot->size += (need
-                                        * (sizeof (Elf32_External_Rela) / 4));
+                 {
+                   asection *srel = htab->relgot;
+                   if ((*lgot_masks & PLT_IFUNC) != 0)
+                     srel = htab->reliplt;
+                   srel->size += need * (sizeof (Elf32_External_Rela) / 4);
+                 }
              }
          }
        else
@@ -5708,14 +6346,19 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   if (info->shared)
     {
       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
@@ -5726,10 +6369,11 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
       /* Space for the branch table.  */
       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 & 15;
+      htab->glink->size += -htab->glink->size & (htab->params->ppc476_workaround
+                                                ? 63 : 15);
       htab->glink->size += GLINK_PLTRESOLVE;
 
-      if (htab->emit_stub_syms)
+      if (htab->params->emit_stub_syms)
        {
          struct elf_link_hash_entry *sh;
          sh = elf_link_hash_lookup (&htab->elf, "__glink",
@@ -5746,6 +6390,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);
@@ -5761,10 +6406,27 @@ 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;
            }
        }
     }
 
+  if (htab->glink != NULL
+      && htab->glink->size != 0
+      && htab->glink_eh_frame != NULL
+      && !bfd_is_abs_section (htab->glink_eh_frame->output_section)
+      && _bfd_elf_eh_frame_present (info))
+    {
+      s = htab->glink_eh_frame;
+      s->size = sizeof (glink_eh_frame_cie) + 20;
+      if (info->shared)
+       {
+         s->size += 4;
+         if (htab->glink->size - GLINK_PLTRESOLVE + 8 >= 256)
+           s->size += 4;
+       }
+    }
+
   /* We've now determined the sizes of the various dynamic sections.
      Allocate memory for them.  */
   relocs = FALSE;
@@ -5788,16 +6450,21 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
        }
       else if (s == htab->iplt
               || s == htab->glink
+              || s == htab->glink_eh_frame
               || 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 (CONST_STRNEQ (bfd_get_section_name (dynobj, s), ".rela"))
+      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"))
        {
          if (s->size != 0)
            {
@@ -5864,14 +6531,16 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
            return FALSE;
        }
 
-      if (htab->glink != NULL && htab->glink->size != 0)
+      if (htab->plt_type == PLT_NEW
+         && htab->glink != NULL
+         && htab->glink->size != 0)
        {
          if (!add_dynamic_entry (DT_PPC_GOT, 0))
            return FALSE;
-         if (!htab->no_tls_get_addr_opt
+         if (!htab->params->no_tls_get_addr_opt
              && htab->tls_get_addr != NULL
              && htab->tls_get_addr->plt.plist != NULL
-             && !add_dynamic_entry (DT_PPC_TLSOPT, 0))
+             && !add_dynamic_entry (DT_PPC_OPT, PPC_OPT_TLS))
            return FALSE;
        }
 
@@ -5900,9 +6569,109 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
    }
 #undef add_dynamic_entry
 
+  if (htab->glink_eh_frame != NULL
+      && htab->glink_eh_frame->contents != NULL)
+    {
+      unsigned char *p = htab->glink_eh_frame->contents;
+      bfd_vma val;
+
+      memcpy (p, glink_eh_frame_cie, sizeof (glink_eh_frame_cie));
+      /* CIE length (rewrite in case little-endian).  */
+      bfd_put_32 (htab->elf.dynobj, sizeof (glink_eh_frame_cie) - 4, p);
+      p += sizeof (glink_eh_frame_cie);
+      /* FDE length.  */
+      val = htab->glink_eh_frame->size - 4 - sizeof (glink_eh_frame_cie);
+      bfd_put_32 (htab->elf.dynobj, val, p);
+      p += 4;
+      /* CIE pointer.  */
+      val = p - htab->glink_eh_frame->contents;
+      bfd_put_32 (htab->elf.dynobj, val, p);
+      p += 4;
+      /* Offset to .glink.  Set later.  */
+      p += 4;
+      /* .glink size.  */
+      bfd_put_32 (htab->elf.dynobj, htab->glink->size, p);
+      p += 4;
+      /* Augmentation.  */
+      p += 1;
+
+      if (info->shared
+         && htab->elf.dynamic_sections_created)
+       {
+         bfd_vma adv = (htab->glink->size - GLINK_PLTRESOLVE + 8) >> 2;
+         if (adv < 64)
+           *p++ = DW_CFA_advance_loc + adv;
+         else if (adv < 256)
+           {
+             *p++ = DW_CFA_advance_loc1;
+             *p++ = adv;
+           }
+         else if (adv < 65536)
+           {
+             *p++ = DW_CFA_advance_loc2;
+             bfd_put_16 (htab->elf.dynobj, adv, p);
+             p += 2;
+           }
+         else
+           {
+             *p++ = DW_CFA_advance_loc4;
+             bfd_put_32 (htab->elf.dynobj, adv, p);
+             p += 4;
+           }
+         *p++ = DW_CFA_register;
+         *p++ = 65;
+         p++;
+         *p++ = DW_CFA_advance_loc + 4;
+         *p++ = DW_CFA_restore_extended;
+         *p++ = 65;
+       }
+      BFD_ASSERT ((bfd_vma) ((p + 3 - htab->glink_eh_frame->contents) & -4)
+                 == htab->glink_eh_frame->size);
+    }
+
   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
@@ -5927,7 +6696,7 @@ static const int shared_stub_entry[] =
     0x429f0005, /* bcl 20, 31, .Lxxx */
     0x7d8802a6, /* mflr 12 */
     0x3d8c0000, /* addis 12, 12, (xxx-.Lxxx)@ha */
-    0x398c0008, /* addi 12, 12, (xxx-.Lxxx)@l */
+    0x398c0000, /* addi 12, 12, (xxx-.Lxxx)@l */
     0x7c0803a6, /* mtlr 0 */
     0x7d8903a6, /* mtctr 12 */
     0x4e800420, /* bctr */
@@ -5941,6 +6710,16 @@ static const int stub_entry[] =
     0x4e800420, /* bctr */
   };
 
+struct ppc_elf_relax_info
+{
+  unsigned int workaround_size;
+};
+
+/* This function implements long branch trampolines, and the ppc476
+   icache bug workaround.  Any section needing trampolines or patch
+   space for the workaround has its size extended so that we can
+   add trampolines at the end of the section.  */
+
 static bfd_boolean
 ppc_elf_relax_section (bfd *abfd,
                       asection *isec,
@@ -5961,402 +6740,488 @@ ppc_elf_relax_section (bfd *abfd,
   bfd_byte *contents = NULL;
   Elf_Internal_Sym *isymbuf = NULL;
   Elf_Internal_Rela *internal_relocs = NULL;
-  Elf_Internal_Rela *irel, *irelend;
+  Elf_Internal_Rela *irel, *irelend = NULL;
   struct one_fixup *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 trampoff;
+  bfd_size_type trampbase, trampoff, newsize;
   asection *got2;
+  bfd_boolean maybe_pasted;
 
   *again = FALSE;
 
-  /* Nothing to do if there are no relocations, and no need to do
-     anything with non-alloc or non-code sections.  */
+  /* No need to do anything with non-alloc or non-code sections.  */
   if ((isec->flags & SEC_ALLOC) == 0
       || (isec->flags & SEC_CODE) == 0
-      || (isec->flags & SEC_RELOC) == 0
-      || isec->reloc_count == 0)
+      || (isec->flags & SEC_LINKER_CREATED) != 0
+      || isec->size < 4)
     return TRUE;
 
   /* 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)
-     return TRUE;
-
-  trampoff = (isec->size + 3) & (bfd_vma) -4;
-  /* Space for a branch around any trampolines.  */
-  trampoff += 4;
+    return TRUE;
 
-  symtab_hdr = &elf_symtab_hdr (abfd);
+  htab = ppc_elf_hash_table (link_info);
+  if (htab == NULL)
+    return TRUE;
 
-  /* Get a copy of the native relocations.  */
-  internal_relocs = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL,
-                                              link_info->keep_memory);
-  if (internal_relocs == NULL)
-    goto error_return;
+  isec->size = (isec->size + 3) & -4;
+  if (isec->rawsize == 0)
+    isec->rawsize = isec->size;
+  trampbase = isec->size;
 
-  htab = ppc_elf_hash_table (link_info);
-  got2 = bfd_get_section_by_name (abfd, ".got2");
+  BFD_ASSERT (isec->sec_info_type == SEC_INFO_TYPE_NONE
+             || isec->sec_info_type == SEC_INFO_TYPE_TARGET);
+  isec->sec_info_type = SEC_INFO_TYPE_TARGET;
 
-  irelend = internal_relocs + isec->reloc_count;
-  for (irel = internal_relocs; irel < irelend; irel++)
+  if (htab->params->ppc476_workaround)
     {
-      unsigned long r_type = ELF32_R_TYPE (irel->r_info);
-      bfd_vma toff, roff;
-      asection *tsec;
-      struct one_fixup *f;
-      size_t insn_offset = 0;
-      bfd_vma max_branch_offset, val;
-      bfd_byte *hit_addr;
-      unsigned long t0;
-      struct elf_link_hash_entry *h;
-      struct plt_entry **plist;
-      unsigned char sym_type;
-
-      switch (r_type)
+      if (elf_section_data (isec)->sec_info == NULL)
        {
-       case R_PPC_REL24:
-       case R_PPC_LOCAL24PC:
-       case R_PPC_PLTREL24:
-         max_branch_offset = 1 << 25;
-         break;
+         elf_section_data (isec)->sec_info
+           = bfd_zalloc (abfd, sizeof (struct ppc_elf_relax_info));
+         if (elf_section_data (isec)->sec_info == NULL)
+           return FALSE;
+       }
+      relax_info = elf_section_data (isec)->sec_info;
+      trampbase -= relax_info->workaround_size;
+    }
 
-       case R_PPC_REL14:
-       case R_PPC_REL14_BRTAKEN:
-       case R_PPC_REL14_BRNTAKEN:
-         max_branch_offset = 1 << 15;
-         break;
+  maybe_pasted = (strcmp (isec->output_section->name, ".init") == 0
+                 || strcmp (isec->output_section->name, ".fini") == 0);
+  /* Space for a branch around any trampolines.  */
+  trampoff = trampbase;
+  if (maybe_pasted && trampbase == isec->rawsize)
+    trampoff += 4;
 
-       default:
-         continue;
-       }
+  symtab_hdr = &elf_symtab_hdr (abfd);
 
-      /* Get the value of the symbol referred to by the reloc.  */
-      h = NULL;
-      if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+  if (htab->params->branch_trampolines)
+    {
+      /* Get a copy of the native relocations.  */
+      if (isec->reloc_count != 0)
        {
-         /* A local symbol.  */
-         Elf_Internal_Sym *isym;
+         internal_relocs = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL,
+                                                      link_info->keep_memory);
+         if (internal_relocs == NULL)
+           goto error_return;
+       }
 
-         /* Read this BFD's local symbols.  */
-         if (isymbuf == NULL)
-           {
-             isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
-             if (isymbuf == NULL)
-               isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
-                                               symtab_hdr->sh_info, 0,
-                                               NULL, NULL, NULL);
-             if (isymbuf == 0)
-               goto error_return;
-           }
-         isym = isymbuf + ELF32_R_SYM (irel->r_info);
-         if (isym->st_shndx == SHN_UNDEF)
-           tsec = bfd_und_section_ptr;
-         else if (isym->st_shndx == SHN_ABS)
-           tsec = bfd_abs_section_ptr;
-         else if (isym->st_shndx == SHN_COMMON)
-           tsec = bfd_com_section_ptr;
-         else
-           tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+      got2 = bfd_get_section_by_name (abfd, ".got2");
 
-         toff = isym->st_value;
-         sym_type = ELF_ST_TYPE (isym->st_info);
-       }
-      else
+      irelend = internal_relocs + isec->reloc_count;
+      for (irel = internal_relocs; irel < irelend; irel++)
        {
-         /* Global symbol handling.  */
-         unsigned long indx;
-
-         indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
-         h = elf_sym_hashes (abfd)[indx];
+         unsigned long r_type = ELF32_R_TYPE (irel->r_info);
+         bfd_vma toff, roff;
+         asection *tsec;
+         struct one_fixup *f;
+         size_t insn_offset = 0;
+         bfd_vma max_branch_offset, val;
+         bfd_byte *hit_addr;
+         unsigned long t0;
+         struct elf_link_hash_entry *h;
+         struct plt_entry **plist;
+         unsigned char sym_type;
+
+         switch (r_type)
+           {
+           case R_PPC_REL24:
+           case R_PPC_LOCAL24PC:
+           case R_PPC_PLTREL24:
+             max_branch_offset = 1 << 25;
+             break;
 
-         while (h->root.type == bfd_link_hash_indirect
-                || h->root.type == bfd_link_hash_warning)
-           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+           case R_PPC_REL14:
+           case R_PPC_REL14_BRTAKEN:
+           case R_PPC_REL14_BRNTAKEN:
+             max_branch_offset = 1 << 15;
+             break;
 
-         if (h->root.type == bfd_link_hash_defined
-             || h->root.type == bfd_link_hash_defweak)
-           {
-             tsec = h->root.u.def.section;
-             toff = h->root.u.def.value;
+           default:
+             continue;
            }
-         else if (h->root.type == bfd_link_hash_undefined
-                  || h->root.type == bfd_link_hash_undefweak)
+
+         /* Get the value of the symbol referred to by the reloc.  */
+         h = NULL;
+         if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
            {
-             tsec = bfd_und_section_ptr;
-             toff = link_info->relocatable ? indx : 0;
+             /* A local symbol.  */
+             Elf_Internal_Sym *isym;
+
+             /* Read this BFD's local symbols.  */
+             if (isymbuf == NULL)
+               {
+                 isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+                 if (isymbuf == NULL)
+                   isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+                                                   symtab_hdr->sh_info, 0,
+                                                   NULL, NULL, NULL);
+                 if (isymbuf == 0)
+                   goto error_return;
+               }
+             isym = isymbuf + ELF32_R_SYM (irel->r_info);
+             if (isym->st_shndx == SHN_UNDEF)
+               tsec = bfd_und_section_ptr;
+             else if (isym->st_shndx == SHN_ABS)
+               tsec = bfd_abs_section_ptr;
+             else if (isym->st_shndx == SHN_COMMON)
+               tsec = bfd_com_section_ptr;
+             else
+               tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+
+             toff = isym->st_value;
+             sym_type = ELF_ST_TYPE (isym->st_info);
            }
          else
-           continue;
+           {
+             /* Global symbol handling.  */
+             unsigned long indx;
 
-         sym_type = h->type;
-       }
+             indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+             h = elf_sym_hashes (abfd)[indx];
 
-      /* 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
-        destination used here may be incorrect.  */
-      plist = NULL;
-      if (h != NULL)
-       {
-         /* We know is_branch_reloc (r_type) is true.  */
-         if (h->type == STT_GNU_IFUNC
-             || r_type == R_PPC_PLTREL24)
-           plist = &h->plt.plist;
-       }
-      else if (sym_type == STT_GNU_IFUNC
-              && elf_local_got_offsets (abfd) != NULL)
-       {
-         bfd_vma *local_got_offsets = elf_local_got_offsets (abfd);
-         struct plt_entry **local_plt = (struct plt_entry **)
-           (local_got_offsets + symtab_hdr->sh_info);
-         plist = local_plt + ELF32_R_SYM (irel->r_info);
-       }
-      if (plist != NULL)
-       {
-         bfd_vma addend = 0;
-         struct plt_entry *ent;
+             while (h->root.type == bfd_link_hash_indirect
+                    || h->root.type == bfd_link_hash_warning)
+               h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
-         if (r_type == R_PPC_PLTREL24 && link_info->shared)
-           addend = irel->r_addend;
-         ent = find_plt_ent (plist, got2, addend);
-         if (ent != NULL)
-           {
-             if (htab->plt_type == PLT_NEW
-                 || h == NULL
-                 || !htab->elf.dynamic_sections_created
-                 || h->dynindx == -1)
+             if (h->root.type == bfd_link_hash_defined
+                 || h->root.type == bfd_link_hash_defweak)
                {
-                 tsec = htab->glink;
-                 toff = ent->glink_offset;
+                 tsec = h->root.u.def.section;
+                 toff = h->root.u.def.value;
                }
-             else
+             else if (h->root.type == bfd_link_hash_undefined
+                      || h->root.type == bfd_link_hash_undefweak)
                {
-                 tsec = htab->plt;
-                 toff = ent->plt.offset;
+                 tsec = bfd_und_section_ptr;
+                 toff = link_info->relocatable ? indx : 0;
                }
-           }
-       }
-
-      /* If the branch and target are in the same section, you have
-        no hope of adding stubs.  We'll error out later should the
-        branch overflow.  */
-      if (tsec == isec)
-       continue;
-
-      /* There probably isn't any reason to handle symbols in
-        SEC_MERGE sections;  SEC_MERGE doesn't seem a likely
-        attribute for a code section, and we are only looking at
-        branches.  However, implement it correctly here as a
-        reference for other target relax_section functions.  */
-      if (0 && tsec->sec_info_type == ELF_INFO_TYPE_MERGE)
-       {
-         /* At this stage in linking, no SEC_MERGE symbol has been
-            adjusted, so all references to such symbols need to be
-            passed through _bfd_merged_section_offset.  (Later, in
-            relocate_section, all SEC_MERGE symbols *except* for
-            section symbols have been adjusted.)
-
-            gas may reduce relocations against symbols in SEC_MERGE
-            sections to a relocation against the section symbol when
-            the original addend was zero.  When the reloc is against
-            a section symbol we should include the addend in the
-            offset passed to _bfd_merged_section_offset, since the
-            location of interest is the original symbol.  On the
-            other hand, an access to "sym+addend" where "sym" is not
-            a section symbol should not include the addend;  Such an
-            access is presumed to be an offset from "sym";  The
-            location of interest is just "sym".  */
-         if (sym_type == STT_SECTION)
-           toff += irel->r_addend;
+             else
+               continue;
 
-         toff = _bfd_merged_section_offset (abfd, &tsec,
-                                            elf_section_data (tsec)->sec_info,
-                                            toff);
+             /* 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
+                 && h == htab->tls_get_addr
+                 && irel != internal_relocs)
+               {
+                 unsigned long t_symndx = ELF32_R_SYM (irel[-1].r_info);
+                 unsigned long t_rtype = ELF32_R_TYPE (irel[-1].r_info);
+                 unsigned int tls_mask = 0;
+
+                 /* The previous reloc should be one of R_PPC_TLSGD or
+                    R_PPC_TLSLD, or for older object files, a reloc
+                    on the __tls_get_addr arg setup insn.  Get tls
+                    mask bits from the symbol on that reloc.  */
+                 if (t_symndx < symtab_hdr->sh_info)
+                   {
+                     bfd_vma *local_got_offsets = elf_local_got_offsets (abfd);
 
-         if (sym_type != STT_SECTION)
-           toff += irel->r_addend;
-       }
-      /* PLTREL24 addends are special.  */
-      else if (r_type != R_PPC_PLTREL24)
-       toff += irel->r_addend;
+                     if (local_got_offsets != NULL)
+                       {
+                         struct plt_entry **local_plt = (struct plt_entry **)
+                           (local_got_offsets + symtab_hdr->sh_info);
+                         char *lgot_masks = (char *)
+                           (local_plt + symtab_hdr->sh_info);
+                         tls_mask = lgot_masks[t_symndx];
+                       }
+                   }
+                 else
+                   {
+                     struct elf_link_hash_entry *th
+                       = elf_sym_hashes (abfd)[t_symndx - symtab_hdr->sh_info];
 
-      /* Attempted -shared link of non-pic code loses.  */
-      if (tsec->output_section == NULL)
-       continue;
+                     while (th->root.type == bfd_link_hash_indirect
+                            || th->root.type == bfd_link_hash_warning)
+                       th = (struct elf_link_hash_entry *) th->root.u.i.link;
 
-      roff = irel->r_offset;
+                     tls_mask
+                       = ((struct ppc_elf_link_hash_entry *) th)->tls_mask;
+                   }
 
-      /* If the branch is in range, no need to do anything.  */
-      if (tsec != bfd_und_section_ptr
-         && (!link_info->relocatable
-             /* A relocatable link may have sections moved during
-                final link, so do not presume they remain in range.  */
-             || tsec->output_section == isec->output_section))
-       {
-         bfd_vma symaddr, reladdr;
+                 /* The mask bits tell us if the call will be
+                    optimised away.  */
+                 if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_GD) == 0
+                     && (t_rtype == R_PPC_TLSGD
+                         || t_rtype == R_PPC_GOT_TLSGD16
+                         || t_rtype == R_PPC_GOT_TLSGD16_LO))
+                   continue;
+                 if ((tls_mask & TLS_TLS) != 0 && (tls_mask & TLS_LD) == 0
+                     && (t_rtype == R_PPC_TLSLD
+                         || t_rtype == R_PPC_GOT_TLSLD16
+                         || t_rtype == R_PPC_GOT_TLSLD16_LO))
+                   continue;
+               }
 
-         symaddr = tsec->output_section->vma + tsec->output_offset + toff;
-         reladdr = isec->output_section->vma + isec->output_offset + roff;
-         if (symaddr - reladdr + max_branch_offset < 2 * max_branch_offset)
-           continue;
-       }
+             sym_type = h->type;
+           }
 
-      /* Look for an existing fixup to this address.  */
-      for (f = fixups; f ; f = f->next)
-       if (f->tsec == tsec && f->toff == toff)
-         break;
+         /* 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
+            destination used here may be incorrect.  */
+         plist = NULL;
+         if (h != NULL)
+           {
+             /* We know is_branch_reloc (r_type) is true.  */
+             if (h->type == STT_GNU_IFUNC
+                 || r_type == R_PPC_PLTREL24)
+               plist = &h->plt.plist;
+           }
+         else if (sym_type == STT_GNU_IFUNC
+                  && elf_local_got_offsets (abfd) != NULL)
+           {
+             bfd_vma *local_got_offsets = elf_local_got_offsets (abfd);
+             struct plt_entry **local_plt = (struct plt_entry **)
+               (local_got_offsets + symtab_hdr->sh_info);
+             plist = local_plt + ELF32_R_SYM (irel->r_info);
+           }
+         if (plist != NULL)
+           {
+             bfd_vma addend = 0;
+             struct plt_entry *ent;
 
-      if (f == NULL)
-       {
-         size_t size;
-         unsigned long stub_rtype;
+             if (r_type == R_PPC_PLTREL24 && link_info->shared)
+               addend = irel->r_addend;
+             ent = find_plt_ent (plist, got2, addend);
+             if (ent != NULL)
+               {
+                 if (htab->plt_type == PLT_NEW
+                     || h == NULL
+                     || !htab->elf.dynamic_sections_created
+                     || h->dynindx == -1)
+                   {
+                     tsec = htab->glink;
+                     toff = ent->glink_offset;
+                   }
+                 else
+                   {
+                     tsec = htab->plt;
+                     toff = ent->plt.offset;
+                   }
+               }
+           }
 
-         val = trampoff - roff;
-         if (val >= max_branch_offset)
-           /* Oh dear, we can't reach a trampoline.  Don't try to add
-              one.  We'll report an error later.  */
+         /* If the branch and target are in the same section, you have
+            no hope of adding stubs.  We'll error out later should the
+            branch overflow.  */
+         if (tsec == isec)
            continue;
 
-         if (link_info->shared)
+         /* There probably isn't any reason to handle symbols in
+            SEC_MERGE sections;  SEC_MERGE doesn't seem a likely
+            attribute for a code section, and we are only looking at
+            branches.  However, implement it correctly here as a
+            reference for other target relax_section functions.  */
+         if (0 && tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
            {
-             size = 4 * ARRAY_SIZE (shared_stub_entry);
-             insn_offset = 12;
+             /* At this stage in linking, no SEC_MERGE symbol has been
+                adjusted, so all references to such symbols need to be
+                passed through _bfd_merged_section_offset.  (Later, in
+                relocate_section, all SEC_MERGE symbols *except* for
+                section symbols have been adjusted.)
+
+                gas may reduce relocations against symbols in SEC_MERGE
+                sections to a relocation against the section symbol when
+                the original addend was zero.  When the reloc is against
+                a section symbol we should include the addend in the
+                offset passed to _bfd_merged_section_offset, since the
+                location of interest is the original symbol.  On the
+                other hand, an access to "sym+addend" where "sym" is not
+                a section symbol should not include the addend;  Such an
+                access is presumed to be an offset from "sym";  The
+                location of interest is just "sym".  */
+             if (sym_type == STT_SECTION)
+               toff += irel->r_addend;
+
+             toff
+               = _bfd_merged_section_offset (abfd, &tsec,
+                                             elf_section_data (tsec)->sec_info,
+                                             toff);
+
+             if (sym_type != STT_SECTION)
+               toff += irel->r_addend;
            }
-         else
+         /* PLTREL24 addends are special.  */
+         else if (r_type != R_PPC_PLTREL24)
+           toff += irel->r_addend;
+
+         /* Attempted -shared link of non-pic code loses.  */
+         if ((!link_info->relocatable
+              && tsec == bfd_und_section_ptr)
+             || tsec->output_section == NULL
+             || (tsec->owner != NULL
+                 && (tsec->owner->flags & BFD_PLUGIN) != 0))
+           continue;
+
+         roff = irel->r_offset;
+
+         /* If the branch is in range, no need to do anything.  */
+         if (tsec != bfd_und_section_ptr
+             && (!link_info->relocatable
+                 /* A relocatable link may have sections moved during
+                    final link, so do not presume they remain in range.  */
+                 || tsec->output_section == isec->output_section))
            {
-             size = 4 * ARRAY_SIZE (stub_entry);
-             insn_offset = 0;
+             bfd_vma symaddr, reladdr;
+
+             symaddr = tsec->output_section->vma + tsec->output_offset + toff;
+             reladdr = isec->output_section->vma + isec->output_offset + roff;
+             if (symaddr - reladdr + max_branch_offset
+                 < 2 * max_branch_offset)
+               continue;
            }
-         stub_rtype = R_PPC_RELAX;
-         if (tsec == htab->plt
-             || tsec == htab->glink)
+
+         /* Look for an existing fixup to this address.  */
+         for (f = fixups; f ; f = f->next)
+           if (f->tsec == tsec && f->toff == toff)
+             break;
+
+         if (f == NULL)
            {
-             stub_rtype = R_PPC_RELAX_PLT;
-             if (r_type == R_PPC_PLTREL24)
-               stub_rtype = R_PPC_RELAX_PLTREL24;
-           }
+             size_t size;
+             unsigned long stub_rtype;
 
-         /* Hijack the old relocation.  Since we need two
-            relocations for this use a "composite" reloc.  */
-         irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
-                                      stub_rtype);
-         irel->r_offset = trampoff + insn_offset;
-         if (r_type == R_PPC_PLTREL24
-             && stub_rtype != R_PPC_RELAX_PLTREL24)
-           irel->r_addend = 0;
-
-         /* Record the fixup so we don't do it again this section.  */
-         f = bfd_malloc (sizeof (*f));
-         f->next = fixups;
-         f->tsec = tsec;
-         f->toff = toff;
-         f->trampoff = trampoff;
-         fixups = f;
-
-         trampoff += size;
-         changes++;
-       }
-      else
-       {
-         val = f->trampoff - roff;
-         if (val >= max_branch_offset)
-           continue;
+             val = trampoff - roff;
+             if (val >= max_branch_offset)
+               /* Oh dear, we can't reach a trampoline.  Don't try to add
+                  one.  We'll report an error later.  */
+               continue;
 
-         /* Nop out the reloc, since we're finalizing things here.  */
-         irel->r_info = ELF32_R_INFO (0, R_PPC_NONE);
-       }
+             if (link_info->shared)
+               {
+                 size = 4 * ARRAY_SIZE (shared_stub_entry);
+                 insn_offset = 12;
+               }
+             else
+               {
+                 size = 4 * ARRAY_SIZE (stub_entry);
+                 insn_offset = 0;
+               }
+             stub_rtype = R_PPC_RELAX;
+             if (tsec == htab->plt
+                 || tsec == htab->glink)
+               {
+                 stub_rtype = R_PPC_RELAX_PLT;
+                 if (r_type == R_PPC_PLTREL24)
+                   stub_rtype = R_PPC_RELAX_PLTREL24;
+               }
 
-      /* Get the section contents.  */
-      if (contents == NULL)
-       {
-         /* Get cached copy if it exists.  */
-         if (elf_section_data (isec)->this_hdr.contents != NULL)
-           contents = elf_section_data (isec)->this_hdr.contents;
+             /* Hijack the old relocation.  Since we need two
+                relocations for this use a "composite" reloc.  */
+             irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+                                          stub_rtype);
+             irel->r_offset = trampoff + insn_offset;
+             if (r_type == R_PPC_PLTREL24
+                 && stub_rtype != R_PPC_RELAX_PLTREL24)
+               irel->r_addend = 0;
+
+             /* Record the fixup so we don't do it again this section.  */
+             f = bfd_malloc (sizeof (*f));
+             f->next = fixups;
+             f->tsec = tsec;
+             f->toff = toff;
+             f->trampoff = trampoff;
+             fixups = f;
+
+             trampoff += size;
+             changes++;
+           }
          else
            {
+             val = f->trampoff - roff;
+             if (val >= max_branch_offset)
+               continue;
+
+             /* Nop out the reloc, since we're finalizing things here.  */
+             irel->r_info = ELF32_R_INFO (0, R_PPC_NONE);
+           }
+
+         /* Get the section contents.  */
+         if (contents == NULL)
+           {
+             /* Get cached copy if it exists.  */
+             if (elf_section_data (isec)->this_hdr.contents != NULL)
+               contents = elf_section_data (isec)->this_hdr.contents;
              /* Go get them off disk.  */
-             if (!bfd_malloc_and_get_section (abfd, isec, &contents))
+             else if (!bfd_malloc_and_get_section (abfd, isec, &contents))
                goto error_return;
            }
-       }
 
-      /* Fix up the existing branch to hit the trampoline.  */
-      hit_addr = contents + roff;
-      switch (r_type)
-       {
-       case R_PPC_REL24:
-       case R_PPC_LOCAL24PC:
-       case R_PPC_PLTREL24:
-         t0 = bfd_get_32 (abfd, hit_addr);
-         t0 &= ~0x3fffffc;
-         t0 |= val & 0x3fffffc;
-         bfd_put_32 (abfd, t0, hit_addr);
-         break;
+         /* Fix up the existing branch to hit the trampoline.  */
+         hit_addr = contents + roff;
+         switch (r_type)
+           {
+           case R_PPC_REL24:
+           case R_PPC_LOCAL24PC:
+           case R_PPC_PLTREL24:
+             t0 = bfd_get_32 (abfd, hit_addr);
+             t0 &= ~0x3fffffc;
+             t0 |= val & 0x3fffffc;
+             bfd_put_32 (abfd, t0, hit_addr);
+             break;
 
-       case R_PPC_REL14:
-       case R_PPC_REL14_BRTAKEN:
-       case R_PPC_REL14_BRNTAKEN:
-         t0 = bfd_get_32 (abfd, hit_addr);
-         t0 &= ~0xfffc;
-         t0 |= val & 0xfffc;
-         bfd_put_32 (abfd, t0, hit_addr);
-         break;
+           case R_PPC_REL14:
+           case R_PPC_REL14_BRTAKEN:
+           case R_PPC_REL14_BRNTAKEN:
+             t0 = bfd_get_32 (abfd, hit_addr);
+             t0 &= ~0xfffc;
+             t0 |= val & 0xfffc;
+             bfd_put_32 (abfd, t0, hit_addr);
+             break;
+           }
        }
-    }
 
-  /* Write out the trampolines.  */
-  if (fixups != NULL)
-    {
-      const int *stub;
-      bfd_byte *dest;
-      bfd_vma val;
-      int i, size;
-
-      do
+      while (fixups != NULL)
        {
          struct one_fixup *f = fixups;
          fixups = fixups->next;
          free (f);
        }
-      while (fixups);
-
-      contents = bfd_realloc_or_free (contents, trampoff);
-      if (contents == NULL)
-       goto error_return;
-
-      isec->size = (isec->size + 3) & (bfd_vma) -4;
-      /* Branch around the trampolines.  */
-      val = B + trampoff - isec->size;
-      dest = contents + isec->size;
-      isec->size = trampoff;
-      bfd_put_32 (abfd, val, dest);
-      dest += 4;
-
-      if (link_info->shared)
-       {
-         stub = shared_stub_entry;
-         size = ARRAY_SIZE (shared_stub_entry);
-       }
-      else
-       {
-         stub = stub_entry;
-         size = ARRAY_SIZE (stub_entry);
-       }
+    }
 
-      i = 0;
-      while (dest < contents + trampoff)
+  workaround_change = FALSE;
+  newsize = trampoff;
+  if (htab->params->ppc476_workaround
+      && (!link_info->relocatable
+         || isec->output_section->alignment_power >= htab->params->pagesize_p2))
+    {
+      bfd_vma addr, end_addr;
+      unsigned int crossings;
+      bfd_vma pagesize = (bfd_vma) 1 << htab->params->pagesize_p2;
+
+      addr = isec->output_section->vma + isec->output_offset;
+      end_addr = addr + trampoff;
+      addr &= -pagesize;
+      crossings = ((end_addr & -pagesize) - addr) >> htab->params->pagesize_p2;
+      if (crossings != 0)
        {
-         bfd_put_32 (abfd, stub[i], dest);
-         i++;
-         if (i == size)
-           i = 0;
-         dest += 4;
+         /* 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 - 1) & 15);
+         newsize += crossings * 16;
+         if (relax_info->workaround_size < newsize)
+           {
+             relax_info->workaround_size = newsize;
+             workaround_change = TRUE;
+           }
+         /* Ensure relocate_section is called.  */
+         isec->flags |= SEC_RELOC;
        }
-      BFD_ASSERT (i == 0);
+      newsize = trampoff + relax_info->workaround_size;
     }
 
+  if (changes || workaround_change)
+    isec->size = newsize;
+
   if (isymbuf != NULL
       && symtab_hdr->contents != (unsigned char *) isymbuf)
     {
@@ -6385,6 +7250,7 @@ ppc_elf_relax_section (bfd *abfd,
     {
       /* Append sufficient NOP relocs so we can write out relocation
         information for the trampolines.  */
+      Elf_Internal_Shdr *rel_hdr;
       Elf_Internal_Rela *new_relocs = bfd_malloc ((changes + isec->reloc_count)
                                                  * sizeof (*new_relocs));
       unsigned ix;
@@ -6403,37 +7269,23 @@ ppc_elf_relax_section (bfd *abfd,
        free (internal_relocs);
       elf_section_data (isec)->relocs = new_relocs;
       isec->reloc_count += changes;
-      elf_section_data (isec)->rel_hdr.sh_size
-       += changes * elf_section_data (isec)->rel_hdr.sh_entsize;
+      rel_hdr = _bfd_elf_single_rel_hdr (isec);
+      rel_hdr->sh_size += changes * rel_hdr->sh_entsize;
     }
-  else if (elf_section_data (isec)->relocs != internal_relocs)
+  else if (internal_relocs != NULL
+          && elf_section_data (isec)->relocs != internal_relocs)
     free (internal_relocs);
 
-  *again = changes != 0;
-  if (!*again && link_info->relocatable)
-    {
-      /* Convert the internal relax relocs to external form.  */
-      for (irel = internal_relocs; irel < irelend; irel++)
-       if (ELF32_R_TYPE (irel->r_info) == R_PPC_RELAX)
-         {
-           unsigned long r_symndx = ELF32_R_SYM (irel->r_info);
-
-           /* Rewrite the reloc and convert one of the trailing nop
-              relocs to describe this relocation.  */
-           BFD_ASSERT (ELF32_R_TYPE (irelend[-1].r_info) == R_PPC_NONE);
-           /* The relocs are at the bottom 2 bytes */
-           irel[0].r_offset += 2;
-           memmove (irel + 1, irel, (irelend - irel - 1) * sizeof (*irel));
-           irel[0].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
-           irel[1].r_offset += 4;
-           irel[1].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
-           irel++;
-         }
-    }
-
+  *again = changes != 0 || workaround_change;
   return TRUE;
 
  error_return:
+  while (fixups != NULL)
+    {
+      struct one_fixup *f = fixups;
+      fixups = fixups->next;
+      free (f);
+    }
   if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents)
     free (isymbuf);
   if (contents != NULL
@@ -6558,7 +7410,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
@@ -6682,6 +7534,21 @@ _bfd_elf_ppc_at_tprel_transform (unsigned int insn, unsigned int reg)
   return insn;
 }
 
+static bfd_boolean
+is_insn_ds_form (unsigned int insn)
+{
+  return ((insn & (0x3f << 26)) == 58u << 26 /* ld,ldu,lwa */
+         || (insn & (0x3f << 26)) == 62u << 26 /* std,stdu,stq */
+         || (insn & (0x3f << 26)) == 57u << 26 /* lfdp */
+         || (insn & (0x3f << 26)) == 61u << 26 /* stfdp */);
+}
+
+static bfd_boolean
+is_insn_dq_form (unsigned int insn)
+{
+  return (insn & (0x3f << 26)) == 56u << 26; /* lq */
+}
+
 /* The RELOCATE_SECTION function is called by the ELF backend linker
    to handle the relocations for a section.
 
@@ -6727,7 +7594,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Rela *rel;
   Elf_Internal_Rela *relend;
   Elf_Internal_Rela outrel;
-  asection *got2, *sreloc = NULL;
+  asection *got2;
   bfd_vma *local_got_offsets;
   bfd_boolean ret = TRUE;
   bfd_vma d_offset = (bfd_big_endian (output_bfd) ? 2 : 0);
@@ -6775,6 +7642,7 @@ 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;
 
       r_type = ELF32_R_TYPE (rel->r_info);
       sym = NULL;
@@ -6794,15 +7662,17 @@ ppc_elf_relocate_section (bfd *output_bfd,
        }
       else
        {
+         bfd_boolean ignored;
+
          RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
                                   r_symndx, symtab_hdr, sym_hashes,
                                   h, sec, relocation,
-                                  unresolved_reloc, warned);
+                                  unresolved_reloc, warned, ignored);
 
          sym_name = h->root.root.string;
        }
 
-      if (sec != NULL && elf_discarded_section (sec))
+      if (sec != NULL && discarded_section (sec))
        {
          /* For relocs against symbols from removed linkonce sections,
             or sections discarded by a linker script, we just want the
@@ -6810,23 +7680,24 @@ ppc_elf_relocate_section (bfd *output_bfd,
          howto = NULL;
          if (r_type < R_PPC_max)
            howto = ppc_elf_howto_table[r_type];
-         _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset);
-         rel->r_info = 0;
-         rel->r_addend = 0;
-         continue;
+         RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
+                                          rel, 1, relend, howto, 0, contents);
        }
 
       if (info->relocatable)
        {
          if (got2 != NULL
              && r_type == R_PPC_PLTREL24
-             && rel->r_addend >= 32768)
+             && rel->r_addend != 0)
            {
              /* R_PPC_PLTREL24 is rather special.  If non-zero, the
                 addend specifies the GOT pointer offset within .got2.  */
              rel->r_addend += got2->output_offset;
            }
-         continue;
+         if (r_type != R_PPC_RELAX_PLT
+             && r_type != R_PPC_RELAX_PLTREL24
+             && r_type != R_PPC_RELAX)
+           continue;
        }
 
       /* TLS optimizations.  Replace instruction sequences and relocs
@@ -6915,8 +7786,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);
@@ -6949,12 +7820,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)
                    {
@@ -6969,7 +7844,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.  */
@@ -6979,9 +7855,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
                        if (local_sections[r_symndx] == sec)
                          break;
                      if (r_symndx >= symtab_hdr->sh_info)
-                       r_symndx = 0;
+                       r_symndx = STN_UNDEF;
                      rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-                     if (r_symndx != 0)
+                     if (r_symndx != STN_UNDEF)
                        rel->r_addend -= (local_syms[r_symndx].st_value
                                          + sec->output_offset
                                          + sec->output_section->vma);
@@ -7047,9 +7923,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
                if (local_sections[r_symndx] == sec)
                  break;
              if (r_symndx >= symtab_hdr->sh_info)
-               r_symndx = 0;
+               r_symndx = STN_UNDEF;
              rel->r_addend = htab->elf.tls_sec->vma + DTP_OFFSET;
-             if (r_symndx != 0)
+             if (r_symndx != STN_UNDEF)
                rel->r_addend -= (local_syms[r_symndx].st_value
                                  + sec->output_offset
                                  + sec->output_section->vma);
@@ -7164,7 +8040,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
              unresolved_reloc = FALSE;
              if (htab->plt_type == PLT_NEW
                  || !htab->elf.dynamic_sections_created
-                 || h == NULL)
+                 || h == NULL
+                 || h->dynindx == -1)
                relocation = (htab->glink->output_section->vma
                              + htab->glink->output_offset
                              + (ent->glink_offset & ~1));
@@ -7183,8 +8060,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
       switch (r_type)
        {
        default:
-         (*_bfd_error_handler)
-           (_("%B: unknown relocation type %d for symbol %s"),
+         info->callbacks->einfo
+           (_("%P: %B: unknown relocation type %d for symbol %s\n"),
             input_bfd, (int) r_type, sym_name);
 
          bfd_set_error (bfd_error_bad_value);
@@ -7267,6 +8144,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  ;
                else
                  {
+                   BFD_ASSERT (h->dynindx != -1);
                    indx = h->dynindx;
                    unresolved_reloc = FALSE;
                  }
@@ -7334,6 +8212,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
                        asection *rsec = htab->relgot;
                        bfd_byte * loc;
 
+                       if (ifunc != NULL)
+                         rsec = htab->reliplt;
                        outrel.r_offset = (htab->got->output_section->vma
                                           + htab->got->output_offset
                                           + off);
@@ -7367,7 +8247,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++
@@ -7385,9 +8270,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))
                              {
@@ -7444,17 +8334,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
               generated by a hash table traversal, the value in the
               got at entry m+n bears little relation to the entry m.  */
            if (addend != 0)
-             (*_bfd_error_handler)
-               (_("%B(%A+0x%lx): non-zero addend on %s reloc against `%s'"),
-                input_bfd,
-                input_section,
-                (long) rel->r_offset,
+             info->callbacks->einfo
+               (_("%P: %H: non-zero addend on %s reloc against `%s'\n"),
+                input_bfd, input_section, rel->r_offset,
                 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.  */
@@ -7475,7 +8363,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
@@ -7499,18 +8388,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:
@@ -7538,6 +8430,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_UADDR16:
          goto dodyn;
 
+       case R_PPC_VLE_REL8:
+       case R_PPC_VLE_REL15:
+       case R_PPC_VLE_REL24:
        case R_PPC_REL24:
        case R_PPC_REL14:
        case R_PPC_REL14_BRTAKEN:
@@ -7579,7 +8474,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
                  && !h->def_regular))
            {
              int skip;
-             bfd_byte * loc;
+             bfd_byte *loc;
+             asection *sreloc;
 #ifdef DEBUG
              fprintf (stderr, "ppc_elf_relocate_section needs to "
                       "create relocation for %s\n",
@@ -7590,14 +8486,11 @@ ppc_elf_relocate_section (bfd *output_bfd,
              /* When generating a shared object, these relocations
                 are copied into the output file to be resolved at run
                 time.  */
+             sreloc = elf_section_data (input_section)->sreloc;
+             if (ifunc)
+               sreloc = htab->reliplt;
              if (sreloc == NULL)
-               {
-                 sreloc = elf_section_data (input_section)->sreloc;
-                 if (!htab->elf.dynamic_sections_created)
-                   sreloc = htab->reliplt;
-                 if (sreloc == NULL)
-                   return FALSE;
-               }
+               return FALSE;
 
              skip = 0;
              outrel.r_offset = _bfd_elf_section_offset (output_bfd, info,
@@ -7616,6 +8509,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
                            || h->root.type == bfd_link_hash_undefweak))
                       || !SYMBOL_REFERENCES_LOCAL (info, h))
                {
+                 BFD_ASSERT (h->dynindx != -1);
                  unresolved_reloc = FALSE;
                  outrel.r_info = ELF32_R_INFO (h->dynindx, r_type);
                  outrel.r_addend = rel->r_addend;
@@ -7642,17 +8536,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
                             non-executable to apply text relocations.
                             So we'll segfault when trying to run the
                             indirection function to resolve the reloc.  */
-                         (*_bfd_error_handler)
-                           (_("%B(%A+0x%lx): relocation %s for indirect "
-                              "function %s unsupported"),
-                            input_bfd,
-                            input_section,
-                            (long) rel->r_offset,
+                         info->callbacks->einfo
+                           (_("%P: %H: relocation %s for indirect "
+                              "function %s unsupported\n"),
+                            input_bfd, input_section, rel->r_offset,
                             howto->name,
                             sym_name);
                          ret = FALSE;
                        }
-                     else if (r_symndx == 0 || bfd_is_abs_section (sec))
+                     else if (r_symndx == STN_UNDEF || bfd_is_abs_section (sec))
                        ;
                      else if (sec == NULL || sec->owner == NULL)
                        {
@@ -7739,36 +8631,59 @@ ppc_elf_relocate_section (bfd *output_bfd,
          /* Fall thru */
 
        case R_PPC_RELAX:
-         if (info->shared)
-           relocation -= (input_section->output_section->vma
-                          + input_section->output_offset
-                          + rel->r_offset - 4);
-
          {
-           unsigned long t0;
-           unsigned long t1;
+           const int *stub;
+           size_t size;
+           size_t insn_offset = rel->r_offset;
+           unsigned int insn;
 
-           t0 = bfd_get_32 (output_bfd, contents + rel->r_offset);
-           t1 = bfd_get_32 (output_bfd, contents + rel->r_offset + 4);
-
-           /* We're clearing the bits for R_PPC_ADDR16_HA
-              and R_PPC_ADDR16_LO here.  */
-           t0 &= ~0xffff;
-           t1 &= ~0xffff;
+           if (info->shared)
+             {
+               relocation -= (input_section->output_section->vma
+                              + input_section->output_offset
+                              + rel->r_offset - 4);
+               stub = shared_stub_entry;
+               bfd_put_32 (output_bfd, stub[0], contents + insn_offset - 12);
+               bfd_put_32 (output_bfd, stub[1], contents + insn_offset - 8);
+               bfd_put_32 (output_bfd, stub[2], contents + insn_offset - 4);
+               stub += 3;
+               size = ARRAY_SIZE (shared_stub_entry) - 3;
+             }
+           else
+             {
+               stub = stub_entry;
+               size = ARRAY_SIZE (stub_entry);
+             }
 
-           /* t0 is HA, t1 is LO */
            relocation += addend;
-           t0 |= ((relocation + 0x8000) >> 16) & 0xffff;
-           t1 |= relocation & 0xffff;
-
-           bfd_put_32 (output_bfd, t0, contents + rel->r_offset);
-           bfd_put_32 (output_bfd, t1, contents + rel->r_offset + 4);
+           if (info->relocatable)
+             relocation = 0;
+
+           /* First insn is HA, second is LO.  */
+           insn = *stub++;
+           insn |= ((relocation + 0x8000) >> 16) & 0xffff;
+           bfd_put_32 (output_bfd, insn, contents + insn_offset);
+           insn_offset += 4;
+
+           insn = *stub++;
+           insn |= relocation & 0xffff;
+           bfd_put_32 (output_bfd, insn, contents + insn_offset);
+           insn_offset += 4;
+           size -= 2;
+
+           while (size != 0)
+             {
+               insn = *stub++;
+               --size;
+               bfd_put_32 (output_bfd, insn, contents + insn_offset);
+               insn_offset += 4;
+             }
 
            /* Rewrite the reloc and convert one of the trailing nop
               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 += 2;
+           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;
@@ -7815,40 +8730,46 @@ ppc_elf_relocate_section (bfd *output_bfd,
              unresolved_reloc = TRUE;
              break;
            }
-         BFD_ASSERT (strcmp (bfd_get_section_name (abfd, sec), ".got") == 0
-                     || strcmp (bfd_get_section_name (abfd, sec), ".cgot") == 0);
+         BFD_ASSERT (strcmp (bfd_get_section_name (sec->owner, sec),
+                             ".got") == 0
+                     || strcmp (bfd_get_section_name (sec->owner, sec),
+                                ".cgot") == 0);
 
          addend -= sec->output_section->vma + sec->output_offset + 0x8000;
          break;
 
        case R_PPC_PLTREL24:
-         if (h == NULL || ifunc != NULL)
-           break;
-         /* Relocation is to the entry for this symbol in the
-            procedure linkage table.  */
-         {
-           struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
-                                                 info->shared ? addend : 0);
-           addend = 0;
-           if (ent == NULL
-               || htab->plt == NULL)
-             {
-               /* We didn't make a PLT entry for this symbol.  This
-                  happens when statically linking PIC code, or when
-                  using -Bsymbolic.  */
-               break;
-             }
+         if (h != NULL && ifunc == NULL)
+           {
+             struct plt_entry *ent = find_plt_ent (&h->plt.plist, got2,
+                                                   info->shared ? addend : 0);
+             if (ent == NULL
+                 || htab->plt == NULL)
+               {
+                 /* We didn't make a PLT entry for this symbol.  This
+                    happens when statically linking PIC code, or when
+                    using -Bsymbolic.  */
+               }
+             else
+               {
+                 /* Relocation is to the entry for this symbol in the
+                    procedure linkage table.  */
+                 unresolved_reloc = FALSE;
+                 if (htab->plt_type == PLT_NEW)
+                   relocation = (htab->glink->output_section->vma
+                                 + htab->glink->output_offset
+                                 + ent->glink_offset);
+                 else
+                   relocation = (htab->plt->output_section->vma
+                                 + htab->plt->output_offset
+                                 + ent->plt.offset);
+               }
+           }
 
-           unresolved_reloc = FALSE;
-           if (htab->plt_type == PLT_NEW)
-             relocation = (htab->glink->output_section->vma
-                           + htab->glink->output_offset
-                           + ent->glink_offset);
-           else
-             relocation = (htab->plt->output_section->vma
-                           + htab->plt->output_offset
-                           + ent->plt.offset);
-         }
+         /* R_PPC_PLTREL24 is rather special.  If non-zero, the
+            addend specifies the GOT pointer offset within .got2.
+            Don't apply it to the relocation field.  */
+         addend = 0;
          break;
 
          /* Relocate against _SDA_BASE_.  */
@@ -7866,15 +8787,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
              }
            addend -= SYM_VAL (sda);
 
-           name = bfd_get_section_name (abfd, sec->output_section);
-           if (! ((CONST_STRNEQ (name, ".sdata")
-                   && (name[6] == 0 || name[6] == '.'))
-                  || (CONST_STRNEQ (name, ".sbss")
-                      && (name[5] == 0 || name[5] == '.'))))
+           name = bfd_get_section_name (output_bfd, sec->output_section);
+           if (!(strcmp (name, ".sdata") == 0
+                 || strcmp (name, ".sbss") == 0))
              {
-               (*_bfd_error_handler)
-                 (_("%B: the target (%s) of a %s relocation is "
-                    "in the wrong output section (%s)"),
+               info->callbacks->einfo
+                 (_("%P: %B: the target (%s) of a %s relocation is "
+                    "in the wrong output section (%s)\n"),
                   input_bfd,
                   sym_name,
                   howto->name,
@@ -7898,13 +8817,13 @@ ppc_elf_relocate_section (bfd *output_bfd,
              }
            addend -= SYM_VAL (sda);
 
-           name = bfd_get_section_name (abfd, sec->output_section);
-           if (! (CONST_STRNEQ (name, ".sdata2")
-                  || CONST_STRNEQ (name, ".sbss2")))
+           name = bfd_get_section_name (output_bfd, sec->output_section);
+           if (!(strcmp (name, ".sdata2") == 0
+                 || strcmp (name, ".sbss2") == 0))
              {
-               (*_bfd_error_handler)
-                 (_("%B: the target (%s) of a %s relocation is "
-                    "in the wrong output section (%s)"),
+               info->callbacks->einfo
+                 (_("%P: %B: the target (%s) of a %s relocation is "
+                    "in the wrong output section (%s)\n"),
                   input_bfd,
                   sym_name,
                   howto->name,
@@ -7913,12 +8832,51 @@ ppc_elf_relocate_section (bfd *output_bfd,
          }
          break;
 
+       case R_PPC_VLE_LO16A:
+         relocation = relocation + addend;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16a_type);
+         continue;
+
+       case R_PPC_VLE_LO16D:
+         relocation = relocation + addend;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16d_type);
+         continue;
+
+       case R_PPC_VLE_HI16A:
+         relocation = (relocation + addend) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16a_type);
+         continue;
+
+       case R_PPC_VLE_HI16D:
+         relocation = (relocation + addend) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16d_type);
+         continue;
+
+       case R_PPC_VLE_HA16A:
+         relocation = (relocation + addend + 0x8000) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16a_type);
+         continue;
+
+       case R_PPC_VLE_HA16D:
+         relocation = (relocation + addend + 0x8000) >> 16;
+         ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                              relocation, split16d_type);
+         continue;
+
          /* Relocate against either _SDA_BASE_, _SDA2_BASE_, or 0.  */
        case R_PPC_EMB_SDA21:
+       case R_PPC_VLE_SDA21:
        case R_PPC_EMB_RELSDA:
+       case R_PPC_VLE_SDA21_LO:
          {
            const char *name;
            int reg;
+           unsigned int insn;
            struct elf_link_hash_entry *sda = NULL;
 
            if (sec == NULL || sec->output_section == NULL)
@@ -7927,17 +8885,15 @@ ppc_elf_relocate_section (bfd *output_bfd,
                break;
              }
 
-           name = bfd_get_section_name (abfd, sec->output_section);
-           if (((CONST_STRNEQ (name, ".sdata")
-                 && (name[6] == 0 || name[6] == '.'))
-                || (CONST_STRNEQ (name, ".sbss")
-                    && (name[5] == 0 || name[5] == '.'))))
+           name = bfd_get_section_name (output_bfd, sec->output_section);
+           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;
@@ -7949,9 +8905,9 @@ ppc_elf_relocate_section (bfd *output_bfd,
              }
            else
              {
-               (*_bfd_error_handler)
-                 (_("%B: the target (%s) of a %s relocation is "
-                    "in the wrong output section (%s)"),
+               info->callbacks->einfo
+                 (_("%P: %B: the target (%s) of a %s relocation is "
+                    "in the wrong output section (%s)\n"),
                   input_bfd,
                   sym_name,
                   howto->name,
@@ -7972,17 +8928,136 @@ ppc_elf_relocate_section (bfd *output_bfd,
                addend -= SYM_VAL (sda);
              }
 
-           if (r_type == R_PPC_EMB_SDA21)
+           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))
              {
-               bfd_vma insn;  /* Fill in register field.  */
+               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;
 
-               insn = bfd_get_32 (output_bfd, contents + rel->r_offset);
-               insn = (insn & ~RA_REGISTER_MASK) | (reg << RA_REGISTER_SHIFT);
                bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
+
+               if (r_type == R_PPC_VLE_SDA21
+                   && ((relocation + 0x80000) & 0xffffffff) > 0x100000)
+                 goto overflow;
+               continue;
+             }
+           else if (r_type == R_PPC_EMB_SDA21
+                    || r_type == R_PPC_VLE_SDA21
+                    || r_type == R_PPC_VLE_SDA21_LO)
+             {
+               /* Fill in register field.  */
+               insn = (insn & ~RA_REGISTER_MASK) | (reg << RA_REGISTER_SHIFT);
              }
+           bfd_put_32 (output_bfd, insn, contents + rel->r_offset);
          }
          break;
 
+       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:
+         {
+           bfd_vma value;
+           const char *name;
+           //int reg;
+           struct elf_link_hash_entry *sda = NULL;
+
+           if (sec == NULL || sec->output_section == NULL)
+             {
+               unresolved_reloc = TRUE;
+               break;
+             }
+
+           name = bfd_get_section_name (output_bfd, sec->output_section);
+           if (strcmp (name, ".sdata") == 0
+               || strcmp (name, ".sbss") == 0)
+             {
+               //reg = 13;
+               sda = htab->sdata[0].sym;
+             }
+           else if (strcmp (name, ".sdata2") == 0
+                    || strcmp (name, ".sbss2") == 0)
+             {
+               //reg = 2;
+               sda = htab->sdata[1].sym;
+             }
+           else
+             {
+               (*_bfd_error_handler)
+                 (_("%B: the target (%s) of a %s relocation is "
+                    "in the wrong output section (%s)"),
+                  input_bfd,
+                  sym_name,
+                  howto->name,
+                  name);
+
+               bfd_set_error (bfd_error_bad_value);
+               ret = FALSE;
+               continue;
+             }
+
+           if (sda != NULL)
+             {
+               if (!is_static_defined (sda))
+                 {
+                   unresolved_reloc = TRUE;
+                   break;
+                 }
+             }
+
+           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 >> 16;
+               ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                    value, split16a_type);
+             }
+           else if (r_type == R_PPC_VLE_SDAREL_HI16D)
+             {
+               value = value >> 16;
+               ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                    value, split16d_type);
+             }
+           else if (r_type == R_PPC_VLE_SDAREL_HA16A)
+             {
+               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_HA16D)
+             {
+               value = (value + 0x8000) >> 16;
+               ppc_elf_vle_split16 (output_bfd, contents + rel->r_offset,
+                                    value, split16d_type);
+             }
+         }
+         continue;
+
          /* Relocate against the beginning of the section.  */
        case R_PPC_SECTOFF:
        case R_PPC_SECTOFF_LO:
@@ -8021,8 +9096,8 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_EMB_RELST_HI:
        case R_PPC_EMB_RELST_HA:
        case R_PPC_EMB_BIT_FLD:
-         (*_bfd_error_handler)
-           (_("%B: relocation %s is not yet supported for symbol %s."),
+         info->callbacks->einfo
+           (_("%P: %B: relocation %s is not yet supported for symbol %s\n"),
             input_bfd,
             howto->name,
             sym_name);
@@ -8063,6 +9138,54 @@ ppc_elf_relocate_section (bfd *output_bfd,
             Bits 0:15 are not used.  */
          addend += 0x8000;
          break;
+
+       case R_PPC_ADDR16:
+       case R_PPC_ADDR16_LO:
+       case R_PPC_GOT16:
+       case R_PPC_GOT16_LO:
+       case R_PPC_SDAREL16:
+       case R_PPC_SECTOFF:
+       case R_PPC_SECTOFF_LO:
+       case R_PPC_DTPREL16:
+       case R_PPC_DTPREL16_LO:
+       case R_PPC_TPREL16:
+       case R_PPC_TPREL16_LO:
+       case R_PPC_GOT_TLSGD16:
+       case R_PPC_GOT_TLSGD16_LO:
+       case R_PPC_GOT_TLSLD16:
+       case R_PPC_GOT_TLSLD16_LO:
+       case R_PPC_GOT_DTPREL16:
+       case R_PPC_GOT_DTPREL16_LO:
+       case R_PPC_GOT_TPREL16:
+       case R_PPC_GOT_TPREL16_LO:
+         {
+           /* The 32-bit ABI lacks proper relocations to deal with
+              certain 64-bit instructions.  Prevent damage to bits
+              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);
+           mask = 0;
+           if (is_insn_ds_form (insn))
+             mask = 3;
+           else if (is_insn_dq_form (insn))
+             mask = 15;
+           else
+             break;
+           lobit = mask & (relocation + addend);
+           if (lobit != 0)
+             {
+               addend -= lobit;
+               info->callbacks->einfo
+                 (_("%P: %H: error: %s against `%s' not a multiple of %u\n"),
+                  input_bfd, input_section, rel->r_offset,
+                  howto->name, sym_name, mask + 1);
+               bfd_set_error (bfd_error_bad_value);
+               ret = FALSE;
+             }
+           addend += insn & mask;
+         }
+         break;
        }
 
 #ifdef DEBUG
@@ -8078,61 +9201,76 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
       if (unresolved_reloc
          && !((input_section->flags & SEC_DEBUGGING) != 0
-              && h->def_dynamic))
+              && h->def_dynamic)
+         && _bfd_elf_section_offset (output_bfd, info, input_section,
+                                     rel->r_offset) != (bfd_vma) -1)
        {
-         (*_bfd_error_handler)
-           (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
-            input_bfd,
-            input_section,
-            (long) rel->r_offset,
+         info->callbacks->einfo
+           (_("%P: %H: unresolvable %s relocation against symbol `%s'\n"),
+            input_bfd, input_section, rel->r_offset,
             howto->name,
             sym_name);
          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;
+           }
+       }
+
+      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)
+           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)))
                {
-                 /* 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;
                }
-
-             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;
            }
          else
            {
-             (*_bfd_error_handler)
-               (_("%B(%A+0x%lx): %s reloc against `%s': error %d"),
-                input_bfd, input_section,
-                (long) rel->r_offset, howto->name, sym_name, (int) r);
+             info->callbacks->einfo
+               (_("%P: %H: %s reloc against `%s': error %d\n"),
+                input_bfd, input_section, rel->r_offset,
+                howto->name, sym_name, (int) r);
              ret = FALSE;
            }
        }
@@ -8142,6 +9280,247 @@ ppc_elf_relocate_section (bfd *output_bfd,
   fprintf (stderr, "\n");
 #endif
 
+  if (input_section->sec_info_type == SEC_INFO_TYPE_TARGET
+      && input_section->size != input_section->rawsize
+      && (strcmp (input_section->output_section->name, ".init") == 0
+         || strcmp (input_section->output_section->name, ".fini") == 0))
+    {
+      /* Branch around the trampolines.  */
+      unsigned int insn = B + input_section->size - input_section->rawsize;
+      bfd_put_32 (input_bfd, insn, contents + input_section->rawsize);
+    }
+
+  if (htab->params->ppc476_workaround
+      && input_section->sec_info_type == SEC_INFO_TYPE_TARGET
+      && (!info->relocatable
+         || (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)
+       {
+         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
+        branch back to the next page.  Complicated a little by
+        needing to handle moved conditional branches, and by not
+        wanting to touch data-in-text.  */
+
+      start_addr = (input_section->output_section->vma
+                   + input_section->output_offset);
+      end_addr = (start_addr + input_section->size
+                 - relax_info->workaround_size);
+      for (addr = ((start_addr & -pagesize) + pagesize - 4);
+          addr < end_addr;
+          addr += pagesize)
+       {
+         bfd_vma offset = addr - start_addr;
+         Elf_Internal_Rela *lo, *hi;
+         bfd_boolean is_data;
+         bfd_vma patch_off, patch_addr;
+         unsigned int insn;
+
+         /* Do we have a data reloc at this offset?  If so, leave
+            the word alone.  */
+         is_data = FALSE;
+         lo = relocs;
+         hi = relend;
+         rel = NULL;
+         while (lo < hi)
+           {
+             rel = lo + (hi - lo) / 2;
+             if (rel->r_offset < offset)
+               lo = rel + 1;
+             else if (rel->r_offset > offset + 3)
+               hi = rel;
+             else
+               {
+                 switch (ELF32_R_TYPE (rel->r_info))
+                   {
+                   case R_PPC_ADDR32:
+                   case R_PPC_UADDR32:
+                   case R_PPC_REL32:
+                   case R_PPC_ADDR30:
+                     is_data = TRUE;
+                     break;
+                   default:
+                     break;
+                   }
+                 break;
+               }
+           }
+         if (is_data)
+           continue;
+
+         /* 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 & (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)) == (16u << 1)    /* bclr,bclrl */
+                 && (insn & (0x14 << 21)) == (0x14 << 21)))/*   with BO=0x14 */
+           continue;
+
+         patch_addr = (start_addr + input_section->size
+                       - relax_info->workaround_size);
+         patch_addr = (patch_addr + 15) & -16;
+         patch_off = patch_addr - start_addr;
+         bfd_put_32 (input_bfd, B + patch_off - offset, contents + offset);
+
+         if (rel != NULL
+             && rel->r_offset >= offset
+             && rel->r_offset < offset + 4)
+           {
+             /* 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.  */
+             if (rel + 1 != relend)
+               {
+                 Elf_Internal_Rela tmp = *rel;
+
+                 /* Keep the relocs sorted by r_offset.  */
+                 memmove (rel, rel + 1, (relend - (rel + 1)) * sizeof (*rel));
+                 relend[-1] = tmp;
+               }
+             relend[-1].r_offset += patch_off - offset;
+           }
+         else
+           rel = NULL;
+
+         if ((insn & (0x3f << 26)) == (16u << 26) /* bc */
+             && (insn & 2) == 0 /* relative */)
+           {
+             bfd_vma delta = ((insn & 0xfffc) ^ 0x8000) - 0x8000;
+
+             delta += offset - patch_off;
+             if (info->relocatable && rel != NULL)
+               delta = 0;
+             if (!info->relocatable && rel != NULL)
+               {
+                 enum elf_ppc_reloc_type r_type;
+
+                 r_type = ELF32_R_TYPE (relend[-1].r_info);
+                 if (r_type == R_PPC_REL14_BRTAKEN)
+                   insn |= BRANCH_PREDICT_BIT;
+                 else if (r_type == R_PPC_REL14_BRNTAKEN)
+                   insn &= ~BRANCH_PREDICT_BIT;
+                 else
+                   BFD_ASSERT (r_type == R_PPC_REL14);
+
+                 if ((r_type == R_PPC_REL14_BRTAKEN
+                      || r_type == R_PPC_REL14_BRNTAKEN)
+                     && delta + 0x8000 < 0x10000
+                     && (bfd_signed_vma) delta < 0)
+                   insn ^= BRANCH_PREDICT_BIT;
+               }
+             if (delta + 0x8000 < 0x10000)
+               {
+                 bfd_put_32 (input_bfd,
+                             (insn & ~0xfffc) | (delta & 0xfffc),
+                             contents + patch_off);
+                 patch_off += 4;
+                 bfd_put_32 (input_bfd,
+                             B | ((offset + 4 - patch_off) & 0x3fffffc),
+                             contents + patch_off);
+                 patch_off += 4;
+               }
+             else
+               {
+                 if (rel != NULL)
+                   {
+                     unsigned int r_sym = ELF32_R_SYM (relend[-1].r_info);
+
+                     relend[-1].r_offset += 8;
+                     relend[-1].r_info = ELF32_R_INFO (r_sym, R_PPC_REL24);
+                   }
+                 bfd_put_32 (input_bfd,
+                             (insn & ~0xfffc) | 8,
+                             contents + patch_off);
+                 patch_off += 4;
+                 bfd_put_32 (input_bfd,
+                             B | ((offset + 4 - patch_off) & 0x3fffffc),
+                             contents + patch_off);
+                 patch_off += 4;
+                 bfd_put_32 (input_bfd,
+                             B | ((delta - 8) & 0x3fffffc),
+                             contents + patch_off);
+                 patch_off += 4;
+               }
+           }
+         else
+           {
+             bfd_put_32 (input_bfd, insn, contents + patch_off);
+             patch_off += 4;
+             bfd_put_32 (input_bfd,
+                         B | ((offset + 4 - patch_off) & 0x3fffffc),
+                         contents + patch_off);
+             patch_off += 4;
+           }
+         BFD_ASSERT (patch_off <= input_section->size);
+         relax_info->workaround_size = input_section->size - patch_off;
+       }
+    }
+
   return ret;
 }
 \f
@@ -8257,7 +9636,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
                            htab->plt->contents + ent->plt.offset + 28);
 
                /* Fill in the GOT entry corresponding to this PLT slot with
-                  the address immediately after the the "bctr" instruction
+                  the address immediately after the "bctr" instruction
                   in this PLT entry.  */
                bfd_put_32 (output_bfd, (htab->plt->output_section->vma
                                         + htab->plt->output_offset
@@ -8416,7 +9795,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
            p = (unsigned char *) htab->glink->contents + ent->glink_offset;
 
-           if (h == htab->tls_get_addr && !htab->no_tls_get_addr_opt)
+           if (h == htab->tls_get_addr && !htab->params->no_tls_get_addr_opt)
              {
                bfd_put_32 (output_bfd, LWZ_11_3, p);
                p += 4;
@@ -8477,26 +9856,23 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
   fprintf (stderr, "\n");
 #endif
 
-  /* Mark some specially defined symbols as absolute.  */
-  if (strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || (!htab->is_vxworks
-         && (h == htab->elf.hgot
-             || strcmp (h->root.root.string,
-                        "_PROCEDURE_LINKAGE_TABLE_") == 0)))
-    sym->st_shndx = SHN_ABS;
-
   return TRUE;
 }
 \f
 static enum elf_reloc_type_class
-ppc_elf_reloc_type_class (const Elf_Internal_Rela *rela)
+ppc_elf_reloc_type_class (const struct bfd_link_info *info,
+                         const asection *rel_sec,
+                         const Elf_Internal_Rela *rela)
 {
+  struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+  if (rel_sec == htab->reliplt)
+    return reloc_class_ifunc;
+
   switch (ELF32_R_TYPE (rela->r_info))
     {
     case R_PPC_RELATIVE:
       return reloc_class_relative;
-    case R_PPC_REL24:
-    case R_PPC_ADDR24:
     case R_PPC_JMP_SLOT:
       return reloc_class_plt;
     case R_PPC_COPY:
@@ -8525,9 +9901,9 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 
   htab = ppc_elf_hash_table (info);
   dynobj = elf_hash_table (info)->dynobj;
-  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+  sdyn = bfd_get_linker_section (dynobj, ".dynamic");
   if (htab->is_vxworks)
-    splt = bfd_get_section_by_name (dynobj, ".plt");
+    splt = bfd_get_linker_section (dynobj, ".plt");
   else
     splt = NULL;
 
@@ -8621,10 +9997,10 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
        }
       else
        {
-         (*_bfd_error_handler) (_("%s not defined in linker created %s"),
-                                htab->elf.hgot->root.root.string,
-                                (htab->sgotplt != NULL
-                                 ? htab->sgotplt->name : htab->got->name));
+         info->callbacks->einfo (_("%P: %s not defined in linker created %s\n"),
+                                 htab->elf.hgot->root.root.string,
+                                 (htab->sgotplt != NULL
+                                  ? htab->sgotplt->name : htab->got->name));
          bfd_set_error (bfd_error_bad_value);
          ret = FALSE;
        }
@@ -8829,7 +10205,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;
@@ -8844,6 +10220,39 @@ 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)
        {
@@ -8851,7 +10260,11 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 
          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);
@@ -8885,7 +10298,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);
@@ -8913,22 +10330,50 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
        }
     }
 
+  if (htab->glink_eh_frame != NULL
+      && htab->glink_eh_frame->contents != NULL)
+    {
+      unsigned char *p = htab->glink_eh_frame->contents;
+      bfd_vma val;
+
+      p += sizeof (glink_eh_frame_cie);
+      /* FDE length.  */
+      p += 4;
+      /* CIE pointer.  */
+      p += 4;
+      /* Offset to .glink.  */
+      val = (htab->glink->output_section->vma
+            + htab->glink->output_offset);
+      val -= (htab->glink_eh_frame->output_section->vma
+             + htab->glink_eh_frame->output_offset);
+      val -= p - htab->glink_eh_frame->contents;
+      bfd_put_32 (htab->elf.dynobj, val, p);
+
+      if (htab->glink_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME
+         && !_bfd_elf_write_section_eh_frame (output_bfd, info,
+                                              htab->glink_eh_frame,
+                                              htab->glink_eh_frame->contents))
+       return FALSE;
+    }
+
   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
@@ -8943,12 +10388,13 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #define elf_backend_can_gc_sections    1
 #define elf_backend_can_refcount       1
 #define elf_backend_rela_normal                1
+#define elf_backend_caches_rawsize     1
 
 #define bfd_elf32_mkobject                     ppc_elf_mkobject
 #define bfd_elf32_bfd_merge_private_bfd_data   ppc_elf_merge_private_bfd_data
 #define bfd_elf32_bfd_relax_section            ppc_elf_relax_section
 #define bfd_elf32_bfd_reloc_type_lookup                ppc_elf_reloc_type_lookup
-#define bfd_elf32_bfd_reloc_name_lookup        ppc_elf_reloc_name_lookup
+#define bfd_elf32_bfd_reloc_name_lookup                ppc_elf_reloc_name_lookup
 #define bfd_elf32_bfd_set_private_flags                ppc_elf_set_private_flags
 #define bfd_elf32_bfd_link_hash_table_create   ppc_elf_link_hash_table_create
 #define bfd_elf32_get_synthetic_symtab         ppc_elf_get_synthetic_symtab
@@ -8969,6 +10415,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #define elf_backend_finish_dynamic_sections    ppc_elf_finish_dynamic_sections
 #define elf_backend_fake_sections              ppc_elf_fake_sections
 #define elf_backend_additional_program_headers ppc_elf_additional_program_headers
+#define elf_backend_modify_segment_map         ppc_elf_modify_segment_map
 #define elf_backend_grok_prstatus              ppc_elf_grok_prstatus
 #define elf_backend_grok_psinfo                        ppc_elf_grok_psinfo
 #define elf_backend_write_core_note            ppc_elf_write_core_note
@@ -8980,7 +10427,26 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #define elf_backend_plt_sym_val                        ppc_elf_plt_sym_val
 #define elf_backend_action_discarded           ppc_elf_action_discarded
 #define elf_backend_init_index_section         _bfd_elf_init_1_index_section
-#define elf_backend_post_process_headers       _bfd_elf_set_osabi
+#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"
+
+/* FreeBSD Target */
+
+#undef  TARGET_LITTLE_SYM
+#undef  TARGET_LITTLE_NAME
+
+#undef  TARGET_BIG_SYM
+#define TARGET_BIG_SYM  powerpc_elf32_fbsd_vec
+#undef  TARGET_BIG_NAME
+#define TARGET_BIG_NAME "elf32-powerpc-freebsd"
+
+#undef  ELF_OSABI
+#define ELF_OSABI      ELFOSABI_FREEBSD
+
+#undef  elf32_bed
+#define elf32_bed      elf32_powerpc_fbsd_bed
 
 #include "elf32-target.h"
 
@@ -8990,10 +10456,12 @@ 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"
 
+#undef  ELF_OSABI
+
 /* VxWorks uses the elf default section flags for .plt.  */
 static const struct bfd_elf_special_section *
 ppc_elf_vxworks_get_sec_type_attr (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
This page took 0.086805 seconds and 4 git commands to generate.