2005-05-07 Paul Brook <paul@codesourcery.com>
[deliverable/binutils-gdb.git] / bfd / elf32-ppc.c
index 67e7677c75f27f6be30ff760bd5f824586d18356..451e89f9e5769d57de1deed1c7876e16bdc23a6a 100644 (file)
@@ -32,6 +32,7 @@
 #include "elf-bfd.h"
 #include "elf/ppc.h"
 #include "elf32-ppc.h"
+#include "elf-vxworks.h"
 
 /* RELA relocations are used here.  */
 
@@ -52,35 +53,104 @@ static bfd_reloc_status_type ppc_elf_unhandled_reloc
 #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1"
 
 /* For old-style PLT.  */
-/* The size in bytes of an entry in the procedure linkage table.  */
-#define PLT_ENTRY_SIZE 12
-/* The initial size of the plt reserved for the dynamic linker.  */
-#define PLT_INITIAL_ENTRY_SIZE 72
-/* The size of the gap between entries in the PLT.  */
-#define PLT_SLOT_SIZE 8
 /* The number of single-slot PLT entries (the rest use two slots).  */
 #define PLT_NUM_SINGLE_ENTRIES 8192
 
 /* For new-style .glink and .plt.  */
-#define GLINK_PLTRESOLVE 12*4
+#define GLINK_PLTRESOLVE 16*4
 #define GLINK_ENTRY_SIZE 4*4
 
+/* VxWorks uses its own plt layout, filled in by the static linker.  */
+
+/* The standard VxWorks PLT entry.  */
+#define VXWORKS_PLT_ENTRY_SIZE 32
+static const bfd_vma ppc_elf_vxworks_plt_entry
+    [VXWORKS_PLT_ENTRY_SIZE / 4] =
+  {
+    0x3d800000, /* lis     r12,0                 */
+    0x818c0000, /* lwz     r12,0(r12)            */
+    0x7d8903a6, /* mtctr   r12                   */
+    0x4e800420, /* bctr                          */
+    0x39600000, /* li      r11,0                 */
+    0x48000000, /* b       14 <.PLT0resolve+0x4> */
+    0x60000000, /* nop                           */
+    0x60000000, /* nop                           */
+  };
+static const bfd_vma ppc_elf_vxworks_pic_plt_entry
+    [VXWORKS_PLT_ENTRY_SIZE / 4] =
+  {
+    0x3d9e0000, /* addis r12,r30,0 */
+    0x818c0000, /* lwz  r12,0(r12) */
+    0x7d8903a6, /* mtctr r12 */
+    0x4e800420, /* bctr */
+    0x39600000, /* li   r11,0 */
+    0x48000000, /* b    14 <.PLT0resolve+0x4> 14: R_PPC_REL24 .PLTresolve */
+    0x60000000, /* nop */
+    0x60000000, /* nop */
+  };
+
+/* The initial VxWorks PLT entry.  */
+#define VXWORKS_PLT_INITIAL_ENTRY_SIZE 32
+static const bfd_vma ppc_elf_vxworks_plt0_entry
+    [VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] =
+  {
+    0x3d800000, /* lis     r12,0        */
+    0x398c0000, /* addi    r12,r12,0    */
+    0x800c0008, /* lwz     r0,8(r12)    */
+    0x7c0903a6, /* mtctr   r0           */
+    0x818c0004, /* lwz     r12,4(r12)   */
+    0x4e800420, /* bctr                 */
+    0x60000000, /* nop                  */
+    0x60000000, /* nop                  */
+  };
+static const bfd_vma ppc_elf_vxworks_pic_plt0_entry
+    [VXWORKS_PLT_INITIAL_ENTRY_SIZE / 4] =
+  {
+    0x819e0008, /* lwz  r12,8(r30) */
+    0x7d8903a6, /* mtctr r12        */
+    0x819e0004, /* lwz  r12,4(r30) */
+    0x4e800420, /* bctr             */
+    0x60000000, /* nop              */
+    0x60000000, /* nop              */
+    0x60000000, /* nop              */
+    0x60000000, /* nop              */
+  };
+
+/* For executables, we have some additional relocations in
+   .rela.plt.unloaded, for the kernel loader.  */
+
+/* The number of non-JMP_SLOT relocations per PLT0 slot. */
+#define VXWORKS_PLT_NON_JMP_SLOT_RELOCS 3
+/* The number of relocations in the PLTResolve slot. */
+#define VXWORKS_PLTRESOLVE_RELOCS 2
+/* The number of relocations in the PLTResolve slot when when creating
+   a shared library. */
+#define VXWORKS_PLTRESOLVE_RELOCS_SHLIB 0
+
 /* Some instructions.  */
-#define NOP            0x60000000
-#define B              0x48000000
 #define ADDIS_11_11    0x3d6b0000
+#define ADDIS_11_30    0x3d7e0000
+#define ADDIS_12_12    0x3d8c0000
 #define ADDI_11_11     0x396b0000
-#define SUB_11_11_30   0x7d7e5850
 #define ADD_0_11_11    0x7c0b5a14
 #define ADD_11_0_11    0x7d605a14
-#define LWZ_0_4_30     0x801e0004
-#define MTCTR_0                0x7c0903a6
-#define LWZ_12_8_30    0x819e0008
+#define B              0x48000000
+#define BCL_20_31      0x429f0005
 #define BCTR           0x4e800420
-#define ADDIS_11_30    0x3d7e0000
-#define LWZ_11_X_11    0x816b0000
-#define LWZ_11_X_30    0x817e0000
+#define LIS_11         0x3d600000
+#define LIS_12         0x3d800000
+#define LWZU_0_12      0x840c0000
+#define LWZ_0_12       0x800c0000
+#define LWZ_11_11      0x816b0000
+#define LWZ_11_30      0x817e0000
+#define LWZ_12_12      0x818c0000
+#define MFLR_0         0x7c0802a6
+#define MFLR_12                0x7d8802a6
+#define MTCTR_0                0x7c0903a6
 #define MTCTR_11       0x7d6903a6
+#define MTLR_0         0x7c0803a6
+#define NOP            0x60000000
+#define SUB_11_11_12   0x7d6c5850
 
 /* Offset of tp and dtp pointers from start of TLS block.  */
 #define TP_OFFSET      0x7000
@@ -1820,70 +1890,44 @@ ppc_elf_additional_program_headers (bfd *abfd)
    that the linker doesn't crater when trying to make more than
    2 sections.  */
 
-static struct bfd_elf_special_section const
-  ppc_special_sections_p[] =
+static const struct bfd_elf_special_section ppc_elf_special_sections[] =
 {
   { ".plt",              4,  0, SHT_NOBITS,   SHF_ALLOC + SHF_EXECINSTR },
-  { NULL,                0,  0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  ppc_special_sections_s[] =
-{
-  { ".sdata",            6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
   { ".sbss",             5, -2, SHT_NOBITS,   SHF_ALLOC + SHF_WRITE },
-  { ".sdata2",           7, -2, SHT_PROGBITS, SHF_ALLOC },
   { ".sbss2",            6, -2, SHT_PROGBITS, SHF_ALLOC },
-  { NULL,        0, 0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  ppc_special_sections_t[] =
-{
+  { ".sdata",            6, -2, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+  { ".sdata2",           7, -2, SHT_PROGBITS, SHF_ALLOC },
   { ".tags",             5,  0, SHT_ORDERED,  SHF_ALLOC },
-  { NULL,        0, 0, 0,            0 }
-};
-
-static struct bfd_elf_special_section const
-  ppc_special_sections_other[]=
-{
   { ".PPC.EMB.apuinfo", 16,  0, SHT_NOTE,     0 },
-  { ".PPC.EMB.sdata0",  15,  0, SHT_PROGBITS, SHF_ALLOC },
   { ".PPC.EMB.sbss0",   14,  0, SHT_PROGBITS, SHF_ALLOC },
-  { NULL,        0, 0, 0,            0 }
+  { ".PPC.EMB.sdata0",  15,  0, SHT_PROGBITS, SHF_ALLOC },
+  { NULL,                0,  0, 0,            0 }
 };
 
-static struct bfd_elf_special_section const *
-  ppc_elf_special_sections[27]=
+/* This is what we want for new plt/got.  */
+static struct bfd_elf_special_section ppc_alt_plt =
+  { ".plt",              4,  0, SHT_PROGBITS, SHF_ALLOC };
+
+static const struct bfd_elf_special_section *
+ppc_elf_get_sec_type_attr (bfd *abfd ATTRIBUTE_UNUSED, asection *sec)
 {
-  NULL,                                /* 'a' */
-  NULL,                                /* 'b' */
-  NULL,                                /* 'c' */
-  NULL,                                /* 'd' */
-  NULL,                                /* 'e' */
-  NULL,                                /* 'f' */
-  NULL,                                /* 'g' */
-  NULL,                                /* 'h' */
-  NULL,                                /* 'i' */
-  NULL,                                /* 'j' */
-  NULL,                                /* 'k' */
-  NULL,                                /* 'l' */
-  NULL,                                /* 'm' */
-  NULL,                                /* 'n' */
-  NULL,                                /* 'o' */
-  ppc_special_sections_p,      /* 'p' */
-  NULL,                                /* 'q' */
-  NULL,                                /* 'r' */
-  ppc_special_sections_s,      /* 's' */
-  ppc_special_sections_t,      /* 's' */
-  NULL,                                /* 'u' */
-  NULL,                                /* 'v' */
-  NULL,                                /* 'w' */
-  NULL,                                /* 'x' */
-  NULL,                                /* 'y' */
-  NULL,                                /* 'z' */
-  ppc_special_sections_other,  /* other */
-};
+  const struct bfd_elf_special_section *ssect;
+
+  /* See if this is one of the special sections.  */
+  if (sec->name == NULL)
+    return NULL;
+
+  ssect = _bfd_elf_get_special_section (sec->name, ppc_elf_special_sections,
+                                       sec->use_rela_p);
+  if (ssect != NULL)
+    {
+      if (ssect == ppc_elf_special_sections && (sec->flags & SEC_LOAD) != 0)
+       ssect = &ppc_alt_plt;
+      return ssect;
+    }
+
+  return _bfd_elf_get_sec_type_attr (abfd, sec);
+}
 \f
 /* Very simple linked list structure for recording apuinfo values.  */
 typedef struct apuinfo_list
@@ -2192,6 +2236,32 @@ struct ppc_elf_dyn_relocs
   bfd_size_type pc_count;
 };
 
+/* Track PLT entries needed for a given symbol.  We might need more
+   than one glink entry per symbol.  */
+struct plt_entry
+{
+  struct plt_entry *next;
+
+  /* -fPIC uses multiple GOT sections, one per file, called ".got2".
+     This field stores the offset into .got2 used to initialise the
+     GOT pointer reg.  It will always be at least 32768 (and for
+     current gcc this is the only offset used).  */
+  bfd_vma addend;
+
+  /* The .got2 section.  */
+  asection *sec;
+
+  /* PLT refcount or offset.  */
+  union
+    {
+      bfd_signed_vma refcount;
+      bfd_vma offset;
+    } plt;
+
+  /* .glink stub offset.  */
+  bfd_vma glink_offset;
+};
+
 /* Of those relocs that might be copied as dynamic relocs, this macro
    selects those that must be copied when linking a shared library,
    even when the symbol is local.  */
@@ -2235,6 +2305,10 @@ struct ppc_elf_link_hash_entry
 #define TLS_TLS                16      /* Any TLS reloc.  */
 #define TLS_TPRELGD    32      /* TPREL reloc resulting from GD->IE. */
   char tls_mask;
+
+  /* Nonzero if we have seen a small data relocation referring to this
+     symbol.  */
+  unsigned char has_sda_refs;
 };
 
 #define ppc_elf_hash_entry(ent) ((struct ppc_elf_link_hash_entry *) (ent))
@@ -2281,6 +2355,25 @@ struct ppc_elf_link_hash_table
 
   /* Small local sym to section mapping cache.  */
   struct sym_sec_cache sym_sec;
+
+  /* The (unloaded but important) .rela.plt.unloaded on VxWorks.  */
+  asection *srelplt2;
+
+  /* The .got.plt section (VxWorks only)*/
+  asection *sgotplt;
+
+  /* Short-cuts to frequently used symbols on VxWorks targets.  */
+  struct elf_link_hash_entry *hgot, *hplt;
+
+  /* True if the target system is VxWorks.  */
+  int is_vxworks;
+
+  /* The size of PLT entries.  */
+  int plt_entry_size;
+  /* The distance between adjacent PLT slots.  */
+  int plt_slot_size;
+  /* The size of the first PLT entry.  */
+  int plt_initial_entry_size;
 };
 
 /* Get the PPC ELF linker hash table from a link_info structure.  */
@@ -2335,6 +2428,11 @@ ppc_elf_link_hash_table_create (bfd *abfd)
       return NULL;
     }
 
+  ret->elf.init_plt_refcount.refcount = 0;
+  ret->elf.init_plt_refcount.glist = NULL;
+  ret->elf.init_plt_offset.offset = 0;
+  ret->elf.init_plt_offset.glist = NULL;
+
   ret->sdata[0].name = ".sdata";
   ret->sdata[0].sym_name = "_SDA_BASE_";
   ret->sdata[0].bss_name = ".sbss";
@@ -2343,10 +2441,16 @@ ppc_elf_link_hash_table_create (bfd *abfd)
   ret->sdata[1].sym_name = "_SDA2_BASE_";
   ret->sdata[1].bss_name = ".sbss2";
 
+  ret->plt_entry_size = 12;
+  ret->plt_slot_size = 8;
+  ret->plt_initial_entry_size = 72;
+  
+  ret->is_vxworks = 0;
+
   return &ret->elf.root;
 }
 
-/* The powerpc .got has a blrl instruction in it.  Mark it executable.  */
+/* Create .got and the related sections.  */
 
 static bfd_boolean
 ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
@@ -2363,10 +2467,21 @@ ppc_elf_create_got (bfd *abfd, struct bfd_link_info *info)
   if (s == NULL)
     abort ();
 
-  flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-          | SEC_LINKER_CREATED);
-  if (!bfd_set_section_flags (abfd, s, flags))
-    return FALSE;
+  if (htab->is_vxworks)
+    {
+      htab->sgotplt = bfd_get_section_by_name (abfd, ".got.plt");
+      if (!htab->sgotplt)
+       abort ();
+    }
+  else
+    {
+      /* The powerpc .got has a blrl instruction in it.  Mark it
+        executable.  */
+      flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS
+              | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+      if (!bfd_set_section_flags (abfd, s, flags))
+       return FALSE;
+    }
 
   flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
           | SEC_LINKER_CREATED | SEC_READONLY);
@@ -2424,6 +2539,20 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
        return FALSE;
     }
 
+  /* Create the section for VxWorks static plt relocations.  */
+  if (htab->is_vxworks && !info->shared)
+    {
+      s = bfd_make_section (abfd, ".rela.plt.unloaded");
+      flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_READONLY
+              | SEC_LINKER_CREATED);
+      if (s == NULL
+         || ! bfd_set_section_flags (abfd, s, flags)
+         || ! bfd_set_section_alignment (abfd, s,
+                 get_elf_backend_data (abfd)->s->log_file_align))
+       return FALSE;
+      htab->srelplt2 = s;
+    }
+
   htab->relplt = bfd_get_section_by_name (abfd, ".rela.plt");
   htab->plt = s = bfd_get_section_by_name (abfd, ".plt");
   if (s == NULL)
@@ -2436,11 +2565,12 @@ ppc_elf_create_dynamic_sections (bfd *abfd, struct bfd_link_info *info)
 /* Copy the extra info we tack onto an elf_link_hash_entry.  */
 
 static void
-ppc_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
+ppc_elf_copy_indirect_symbol (const struct elf_backend_data *bed ATTRIBUTE_UNUSED,
                              struct elf_link_hash_entry *dir,
                              struct elf_link_hash_entry *ind)
 {
   struct ppc_elf_link_hash_entry *edir, *eind;
+  bfd_signed_vma tmp;
 
   edir = (struct ppc_elf_link_hash_entry *) dir;
   eind = (struct ppc_elf_link_hash_entry *) ind;
@@ -2480,21 +2610,74 @@ ppc_elf_copy_indirect_symbol (const struct elf_backend_data *bed,
     }
 
   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;
+
+  /* If we were called to copy over info for a weak sym, that's all.  */
+  if (eind->elf.root.type != bfd_link_hash_indirect)
+    return;
 
-  if (ELIMINATE_COPY_RELOCS
-      && ind->root.type != bfd_link_hash_indirect
-      && dir->dynamic_adjusted)
+  /* Copy over the GOT refcount entries that we may have already seen to
+     the symbol which just became indirect.  */
+  tmp = edir->elf.got.refcount;
+  if (tmp < 1)
     {
-      /* 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.  */
-      dir->ref_dynamic |= ind->ref_dynamic;
-      dir->ref_regular |= ind->ref_regular;
-      dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
-      dir->needs_plt |= ind->needs_plt;
+      edir->elf.got.refcount = eind->elf.got.refcount;
+      eind->elf.got.refcount = tmp;
     }
   else
-    _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+    BFD_ASSERT (eind->elf.got.refcount < 1);
+
+  /* And plt entries.  */
+  if (eind->elf.plt.plist != NULL)
+    {
+      if (edir->elf.plt.plist != NULL)
+       {
+         struct plt_entry **entp;
+         struct plt_entry *ent;
+
+         for (entp = &eind->elf.plt.plist; (ent = *entp) != NULL; )
+           {
+             struct plt_entry *dent;
+
+             for (dent = edir->elf.plt.plist; dent != NULL; dent = dent->next)
+               if (dent->sec == ent->sec && dent->addend == ent->addend)
+                 {
+                   dent->plt.refcount += ent->plt.refcount;
+                   *entp = ent->next;
+                   break;
+                 }
+             if (dent == NULL)
+               entp = &ent->next;
+           }
+         *entp = edir->elf.plt.plist;
+       }
+
+      edir->elf.plt.plist = eind->elf.plt.plist;
+      eind->elf.plt.plist = NULL;
+    }
+
+  if (edir->elf.dynindx == -1)
+    {
+      edir->elf.dynindx = eind->elf.dynindx;
+      edir->elf.dynstr_index = eind->elf.dynstr_index;
+      eind->elf.dynindx = -1;
+      eind->elf.dynstr_index = 0;
+    }
+  else
+    BFD_ASSERT (eind->elf.dynindx == -1);
 }
 
 /* Return 1 if target is one of ours.  */
@@ -2706,6 +2889,46 @@ update_local_sym_info (bfd *abfd,
   return TRUE;
 }
 
+static bfd_boolean
+update_plt_info (bfd *abfd, struct elf_link_hash_entry *h,
+                asection *sec, bfd_vma addend)
+{
+  struct plt_entry *ent;
+
+  if (addend < 32768)
+    sec = NULL;
+  for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+    if (ent->sec == sec && ent->addend == addend)
+      break;
+  if (ent == NULL)
+    {
+      bfd_size_type amt = sizeof (*ent);
+      ent = bfd_alloc (abfd, amt);
+      if (ent == NULL)
+       return FALSE;
+      ent->next = h->plt.plist;
+      ent->sec = sec;
+      ent->addend = addend;
+      ent->plt.refcount = 0;
+      h->plt.plist = ent;
+    }
+  ent->plt.refcount += 1;
+  return TRUE;
+}
+
+static struct plt_entry *
+find_plt_ent (struct elf_link_hash_entry *h, asection *sec, bfd_vma addend)
+{
+  struct plt_entry *ent;
+
+  if (addend < 32768)
+    sec = NULL;
+  for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+    if (ent->sec == sec && ent->addend == addend)
+      break;
+  return ent;
+}
+
 static void
 bad_shared_reloc (bfd *abfd, enum elf_ppc_reloc_type r_type)
 {
@@ -2731,7 +2954,7 @@ ppc_elf_check_relocs (bfd *abfd,
   struct elf_link_hash_entry **sym_hashes;
   const Elf_Internal_Rela *rel;
   const Elf_Internal_Rela *rel_end;
-  asection *sreloc;
+  asection *got2, *sreloc;
 
   if (info->relocatable)
     return TRUE;
@@ -2757,6 +2980,7 @@ ppc_elf_check_relocs (bfd *abfd,
   htab = ppc_elf_hash_table (info);
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
+  got2 = bfd_get_section_by_name (abfd, ".got2");
   sreloc = NULL;
 
   rel_end = relocs + sec->reloc_count;
@@ -2771,7 +2995,12 @@ ppc_elf_check_relocs (bfd *abfd,
       if (r_symndx < symtab_hdr->sh_info)
        h = NULL;
       else
-       h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+       {
+         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+         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 a relocation refers to _GLOBAL_OFFSET_TABLE_, create the .got.
         This shows up in particular in an R_PPC_ADDR32 in the eabi
@@ -2893,6 +3122,12 @@ ppc_elf_check_relocs (bfd *abfd,
              bad_shared_reloc (abfd, r_type);
              return FALSE;
            }
+         if (h != NULL)
+           {
+             ppc_elf_hash_entry (h)->has_sda_refs = TRUE;
+             /* We may need a copy reloc.  */
+             h->non_got_ref = TRUE;
+           }
          break;
 
        case R_PPC_PLT32:
@@ -2923,9 +3158,14 @@ ppc_elf_check_relocs (bfd *abfd,
              bfd_set_error (bfd_error_bad_value);
              return FALSE;
            }
+         else
+           {
+             bfd_vma addend = r_type == R_PPC_PLTREL24 ? rel->r_addend : 0;
 
-         h->needs_plt = 1;
-         h->plt.refcount++;
+             h->needs_plt = 1;
+             if (!update_plt_info (abfd, h, got2, addend))
+               return FALSE;
+           }
          break;
 
          /* The following relocations don't need to propagate the
@@ -2949,7 +3189,7 @@ ppc_elf_check_relocs (bfd *abfd,
          htab->new_plt = 1;
          break;
 
-         /* This are just markers.  */
+         /* These are just markers.  */
        case R_PPC_TLS:
        case R_PPC_EMB_MRKREF:
        case R_PPC_NONE:
@@ -3011,14 +3251,32 @@ ppc_elf_check_relocs (bfd *abfd,
            info->flags |= DF_STATIC_TLS;
          goto dodyn;
 
-         /* When creating a shared object, we must copy these
-            relocs into the output file.  We create a reloc
-            section in dynobj and make room for the reloc.  */
+       case R_PPC_REL32:
+         if (h == NULL
+             && got2 != NULL
+             && (sec->flags & SEC_CODE) != 0
+             && (info->shared || info->pie)
+             && !htab->old_plt)
+           {
+             /* Old -fPIC gcc code has .long LCTOC1-LCFx just before
+                the start of a function, which assembles to a REL32
+                reference to .got2.  If we detect one of these, then
+                force the old PLT layout because the linker cannot
+                reliably deduce the GOT pointer value needed for
+                PLT call stubs.  */
+             asection *s;
+
+             s = bfd_section_from_r_symndx (abfd, &htab->sym_sec, sec,
+                                            r_symndx);
+             if (s == got2)
+               htab->old_plt = 1;
+           }
+         /* fall through */
+
        case R_PPC_REL24:
        case R_PPC_REL14:
        case R_PPC_REL14_BRTAKEN:
        case R_PPC_REL14_BRNTAKEN:
-       case R_PPC_REL32:
          if (h == NULL)
            break;
          if (h == htab->elf.hgot)
@@ -3043,7 +3301,8 @@ ppc_elf_check_relocs (bfd *abfd,
            {
              /* We may need a plt entry if the symbol turns out to be
                 a function defined in a dynamic object.  */
-             h->plt.refcount++;
+             if (!update_plt_info (abfd, h, NULL, 0))
+               return FALSE;
 
              /* We may need a copy reloc too.  */
              h->non_got_ref = 1;
@@ -3276,15 +3535,26 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
                           int force_old_plt)
 {
   struct ppc_elf_link_hash_table *htab;
+  flagword flags;
 
   htab = ppc_elf_hash_table (info);
   if (force_old_plt || !htab->new_plt)
     htab->old_plt = 1;
 
-  if (!htab->old_plt)
+  if (htab->is_vxworks)
+    {
+      /* The VxWorks PLT is a loaded section with contents.  */
+      flags = SEC_ALLOC | SEC_CODE | SEC_IN_MEMORY | SEC_LINKER_CREATED
+             | SEC_HAS_CONTENTS | SEC_LOAD | SEC_READONLY;
+
+      if (htab->plt != NULL
+         && !bfd_set_section_flags (htab->elf.dynobj, htab->plt, flags))
+       return -1;
+    }
+  else if (!htab->old_plt)
     {
-      flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
-                       | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+      flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+              | SEC_IN_MEMORY | SEC_LINKER_CREATED);
 
       /* The new PLT is a loaded section.  */
       if (htab->plt != NULL
@@ -3359,6 +3629,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
   struct elf_link_hash_entry **sym_hashes;
   bfd_signed_vma *local_got_refcounts;
   const Elf_Internal_Rela *rel, *relend;
+  asection *got2;
 
   if ((sec->flags & SEC_ALLOC) == 0)
     return TRUE;
@@ -3369,6 +3640,7 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
   symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
   sym_hashes = elf_sym_hashes (abfd);
   local_got_refcounts = elf_local_got_refcounts (abfd);
+  got2 = bfd_get_section_by_name (abfd, ".got2");
 
   relend = relocs + sec->reloc_count;
   for (rel = relocs; rel < relend; rel++)
@@ -3463,8 +3735,10 @@ ppc_elf_gc_sweep_hook (bfd *abfd,
        case R_PPC_PLT16_HA:
          if (h != NULL)
            {
-             if (h->plt.refcount > 0)
-               h->plt.refcount--;
+             bfd_vma addend = r_type == R_PPC_PLTREL24 ? rel->r_addend : 0;
+             struct plt_entry *ent = find_plt_ent (h, got2, addend);
+             if (ent->plt.refcount > 0)
+               ent->plt.refcount -= 1;
            }
          break;
 
@@ -3614,8 +3888,9 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED,
                        && h != NULL
                        && h == htab->tls_get_addr)
                      {
-                       if (h->plt.refcount > 0)
-                         h->plt.refcount -= 1;
+                       struct plt_entry *ent = find_plt_ent (h, NULL, 0);
+                       if (ent != NULL && ent->plt.refcount > 0)
+                         ent->plt.refcount -= 1;
                      }
                    expecting_tls_get_addr = 0;
                    continue;
@@ -3723,7 +3998,11 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
     {
       /* Clear procedure linkage table information for any symbol that
         won't need a .plt entry.  */
-      if (h->plt.refcount <= 0
+      struct plt_entry *ent;
+      for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+       if (ent->plt.refcount > 0)
+         break;
+      if (ent == NULL
          || SYMBOL_CALLS_LOCAL (info, h)
          || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
              && h->root.type == bfd_link_hash_undefweak))
@@ -3738,13 +4017,13 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
 
             3. We know for certain that a call to this symbol
             will go to this object, or will remain undefined.  */
-         h->plt.offset = (bfd_vma) -1;
+         h->plt.plist = NULL;
          h->needs_plt = 0;
        }
       return TRUE;
     }
   else
-    h->plt.offset = (bfd_vma) -1;
+    h->plt.plist = NULL;
 
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
@@ -3763,6 +4042,24 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   /* This is a reference to a symbol defined by a dynamic object which
      is not a function.  */
 
+  /* First, a fudge for old shared libs that export some symbols they
+     should not.  */
+  if (!h->def_regular
+      && (strcmp (h->root.root.string, "_SDA_BASE_") == 0
+         || strcmp (h->root.root.string, "_SDA2_BASE_") == 0))
+    {
+      /* These symbols will be defined later, as if they were defined in
+        a linker script.  We don't want to use a definition in a shared
+        object.  */
+      const struct elf_backend_data *bed;
+
+      bed = get_elf_backend_data (htab->elf.dynobj);
+      (*bed->elf_backend_hide_symbol) (info, h, TRUE);
+      h->root.type = bfd_link_hash_undefined;
+      h->root.u.undef.abfd = htab->elf.dynobj;
+      return TRUE;
+    }
+
   /* If we are creating a shared library, we must presume that the
      only references to the symbol are via the global offset table.
      For such cases we need not do anything here; the relocations will
@@ -3775,7 +4072,11 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
   if (!h->non_got_ref)
     return TRUE;
 
-  if (ELIMINATE_COPY_RELOCS)
+   /* 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.  */
+  if (ELIMINATE_COPY_RELOCS
+      && !ppc_elf_hash_entry (h)->has_sda_refs)
     {
       struct ppc_elf_dyn_relocs *p;
       for (p = ppc_elf_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
@@ -3785,8 +4086,6 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
            break;
        }
 
-      /* 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.  */
       if (p == NULL)
        {
          h->non_got_ref = 0;
@@ -3804,11 +4103,10 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
      both the dynamic object and the regular object will refer to the
      same memory location for the variable.
 
-     Of course, if the symbol is sufficiently small, we must instead
-     allocate it in .sbss.  FIXME: It would be better to do this if and
-     only if there were actually SDAREL relocs for that symbol.  */
+     Of course, if the symbol is referenced using SDAREL relocs, we
+     must instead allocate it in .sbss.  */
 
-  if (h->size <= elf_gp_size (htab->elf.dynobj))
+  if (ppc_elf_hash_entry (h)->has_sda_refs)
     s = htab->dynsbss;
   else
     s = htab->dynbss;
@@ -3822,7 +4120,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
     {
       asection *srel;
 
-      if (h->size <= elf_gp_size (htab->elf.dynobj))
+      if (ppc_elf_hash_entry (h)->has_sda_refs)
        srel = htab->relsbss;
       else
        srel = htab->relbss;
@@ -3867,7 +4165,12 @@ allocate_got (struct ppc_elf_link_hash_table *htab, unsigned int need)
   if (htab->old_plt)
     max_before_header = 32764;
 
-  if (need <= htab->got_gap)
+  if (htab->is_vxworks)
+    {
+      where = htab->got->size;
+      htab->got->size += need;
+    }
+  else if (need <= htab->got_gap)
     {
       where = max_before_header - htab->got_gap;
       htab->got_gap -= need;
@@ -3906,83 +4209,140 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
     h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
   htab = ppc_elf_hash_table (info);
-  if (htab->elf.dynamic_sections_created
-      && h->plt.refcount > 0)
+  if (htab->elf.dynamic_sections_created)
     {
-      /* Make sure this symbol is output as a dynamic symbol.  */
-      if (h->dynindx == -1
-         && !h->forced_local)
-       {
-         if (! bfd_elf_link_record_dynamic_symbol (info, h))
-           return FALSE;
-       }
+      struct plt_entry *ent;
+      bfd_boolean doneone = FALSE;
+      bfd_vma plt_offset = 0, glink_offset = 0;
 
-      if (info->shared
-         || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
-       {
-         asection *s = htab->plt;
+      for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+       if (ent->plt.refcount > 0)
+         {
+           /* Make sure this symbol is output as a dynamic symbol.  */
+           if (h->dynindx == -1
+               && !h->forced_local)
+             {
+               if (! bfd_elf_link_record_dynamic_symbol (info, h))
+                 return FALSE;
+             }
 
-         if (!htab->old_plt)
-           {
-             h->plt.offset = s->size;
-             s->size += 4;
+           if (info->shared
+               || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
+             {
+               asection *s = htab->plt;
 
-             s = htab->glink;
-             if (!info->shared
-                 && !h->def_regular)
-               {
-                 h->root.u.def.section = s;
-                 h->root.u.def.value = s->size;
-               }
-             s->size += GLINK_ENTRY_SIZE;
-           }
-         else
-           {
-             /* If this is the first .plt entry, make room for the
-                special first entry.  */
-             if (s->size == 0)
-               s->size += PLT_INITIAL_ENTRY_SIZE;
-
-             /* The PowerPC PLT is actually composed of two parts, the
-                first part is 2 words (for a load and a jump), and then
-                there is a remaining word available at the end.  */
-             h->plt.offset = (PLT_INITIAL_ENTRY_SIZE
-                              + (PLT_SLOT_SIZE
-                                 * ((s->size - PLT_INITIAL_ENTRY_SIZE)
-                                    / PLT_ENTRY_SIZE)));
-
-             /* If this symbol is not defined in a regular file, and we
-                are not generating a shared library, then set the symbol
-                to this location in the .plt.  This is required to make
-                function pointers compare as equal between the normal
-                executable and the shared library.  */
-             if (! info->shared
-                 && !h->def_regular)
-               {
-                 h->root.u.def.section = s;
-                 h->root.u.def.value = h->plt.offset;
-               }
+               if (!(htab->old_plt || htab->is_vxworks))
+                 {
+                   if (!doneone)
+                     {
+                       plt_offset = s->size;
+                       s->size += 4;
+                     }
+                   ent->plt.offset = plt_offset;
 
-             /* Make room for this entry.  After the 8192nd entry, room
-                for two entries is allocated.  */
-             s->size += PLT_ENTRY_SIZE;
-             if ((s->size - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE
-                 > PLT_NUM_SINGLE_ENTRIES)
-               s->size += PLT_ENTRY_SIZE;
-           }
+                   s = htab->glink;
+                   if (!doneone || info->shared || info->pie)
+                     {
+                       glink_offset = s->size;
+                       s->size += GLINK_ENTRY_SIZE;
+                     }
+                   if (!doneone
+                       && !info->shared
+                       && !h->def_regular)
+                     {
+                       h->root.u.def.section = s;
+                       h->root.u.def.value = glink_offset;
+                     }
+                   ent->glink_offset = glink_offset;
+                 }
+               else
+                 {
+                   if (!doneone)
+                     {
+                       /* If this is the first .plt entry, make room
+                          for the special first entry.  */
+                       if (s->size == 0)
+                         s->size += htab->plt_initial_entry_size;
+
+                       /* The PowerPC PLT is actually composed of two
+                          parts, the first part is 2 words (for a load
+                          and a jump), and then there is a remaining
+                          word available at the end.  */
+                       plt_offset = (htab->plt_initial_entry_size
+                                     + (htab->plt_slot_size
+                                        * ((s->size
+                                            - htab->plt_initial_entry_size)
+                                           / htab->plt_entry_size)));
+
+                       /* If this symbol is not defined in a regular
+                          file, and we are not generating a shared
+                          library, then set the symbol to this location
+                          in the .plt.  This is required to make
+                          function pointers compare as equal between
+                          the normal executable and the shared library.  */
+                       if (! info->shared
+                           && !h->def_regular)
+                         {
+                           h->root.u.def.section = s;
+                           h->root.u.def.value = plt_offset;
+                         }
 
-         /* We also need to make an entry in the .rela.plt section.  */
-         htab->relplt->size += sizeof (Elf32_External_Rela);
-       }
-      else
-       {
-         h->plt.offset = (bfd_vma) -1;
-         h->needs_plt = 0;
-       }
+                       /* Make room for this entry.  */
+                       s->size += htab->plt_entry_size;
+                       /* After the 8192nd entry, room for two entries
+                          is allocated.  */
+                       if (!htab->is_vxworks
+                           && (s->size - htab->plt_initial_entry_size)
+                               / htab->plt_entry_size
+                              > PLT_NUM_SINGLE_ENTRIES)
+                         s->size += htab->plt_entry_size;
+                     }
+                   ent->plt.offset = plt_offset;
+                 }
+
+               /* We also need to make an entry in the .rela.plt section.  */
+               if (!doneone)
+                 {
+                   htab->relplt->size += sizeof (Elf32_External_Rela);
+
+                   if (htab->is_vxworks)
+                     {
+                       /* Allocate space for the unloaded relocations.  */
+                       if (!info->shared)
+                         {
+                           if (ent->plt.offset
+                               == (bfd_vma) htab->plt_initial_entry_size)
+                             {
+                               htab->srelplt2->size
+                                 += sizeof (Elf32_External_Rela)
+                                     * VXWORKS_PLTRESOLVE_RELOCS;
+                             }
+
+                           htab->srelplt2->size
+                             += sizeof (Elf32_External_Rela)
+                                 * VXWORKS_PLT_NON_JMP_SLOT_RELOCS;
+                         }
+
+                       /* Every PLT entry has an associated GOT entry in
+                          .got.plt.  */
+                       htab->sgotplt->size += 4;
+                     }
+                   doneone = TRUE;
+                 }
+             }
+           else
+             ent->plt.offset = (bfd_vma) -1;
+
+           if (!doneone)
+             {
+               h->plt.plist = NULL;
+               h->needs_plt = 0;
+             }
+         }
     }
   else
     {
-      h->plt.offset = (bfd_vma) -1;
+      h->plt.plist = NULL;
       h->needs_plt = 0;
     }
 
@@ -4284,10 +4644,31 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   else
     htab->tlsld_got.offset = (bfd_vma) -1;
 
+  if (htab->is_vxworks)
+    {
+      /* Save the GOT and PLT symbols in the hash table for easy access.
+        Mark them as having relocations; they might not, but we won't
+        know for sure until we build the GOT in finish_dynamic_symbol.  */
+
+      htab->hgot = elf_link_hash_lookup (elf_hash_table (info),
+                                        "_GLOBAL_OFFSET_TABLE_",
+                                        FALSE, FALSE, FALSE);
+      if (htab->hgot)
+       htab->hgot->indx = -2;
+      htab->hplt = elf_link_hash_lookup (elf_hash_table (info),
+                                        "_PROCEDURE_LINKAGE_TABLE_",
+                                        FALSE, FALSE, FALSE);
+      if (htab->hplt)
+       htab->hplt->indx = -2;
+      /* If the PLT is executable then give the symbol function type.  */
+      if (htab->hplt && htab->plt->flags & SEC_CODE)
+       htab->hplt->type = STT_FUNC;
+    }
+
   /* Allocate space for global sym dynamic relocs.  */
   elf_link_hash_traverse (elf_hash_table (info), allocate_dynrelocs, info);
 
-  if (htab->got != NULL)
+  if (htab->got != NULL && !htab->is_vxworks)
     {
       unsigned int g_o_t = 32768;
 
@@ -4297,7 +4678,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
          g_o_t = htab->got->size;
          htab->got->size += htab->got_header_size;
        }
-      if (htab->old_plt)
+      if (htab->old_plt && !htab->is_vxworks)
        g_o_t += 4;
 
       htab->elf.hgot->root.u.def.value = g_o_t;
@@ -4318,14 +4699,22 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
   relocs = FALSE;
   for (s = htab->elf.dynobj->sections; s != NULL; s = s->next)
     {
+      bfd_boolean strip_section = TRUE;
+
       if ((s->flags & SEC_LINKER_CREATED) == 0)
        continue;
 
       if (s == htab->plt
          || s == htab->glink
          || s == htab->got
+         || s == htab->sgotplt
          || s == htab->sbss)
        {
+         /* We'd like to strip these sections if they aren't needed, but if
+            we've exported dynamic symbols from them we must leave them.
+            It's too late to tell BFD to get rid of the symbols.  */
+         if ((s == htab->plt || s == htab->got) && htab->hplt != NULL)
+           strip_section = FALSE;
          /* Strip this section if we don't need it; see the
             comment below.  */
        }
@@ -4364,7 +4753,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED,
          continue;
        }
 
-      if (s->size == 0)
+      if (s->size == 0 && strip_section)
        {
          s->flags |= SEC_EXCLUDE;
          continue;
@@ -4480,6 +4869,7 @@ ppc_elf_relax_section (bfd *abfd,
   bfd_boolean changed;
   struct ppc_elf_link_hash_table *htab;
   bfd_size_type trampoff;
+  asection *got2;
 
   *again = FALSE;
 
@@ -4503,8 +4893,9 @@ ppc_elf_relax_section (bfd *abfd,
     goto error_return;
 
   htab = ppc_elf_hash_table (link_info);
-  irelend = internal_relocs + isec->reloc_count;
+  got2 = bfd_get_section_by_name (abfd, ".got2");
 
+  irelend = internal_relocs + isec->reloc_count;
   for (irel = internal_relocs; irel < irelend; irel++)
     {
       unsigned long r_type = ELF32_R_TYPE (irel->r_info);
@@ -4578,21 +4969,29 @@ ppc_elf_relax_section (bfd *abfd,
                 || h->root.type == bfd_link_hash_warning)
            h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
+         tsec = NULL;
+         toff = 0;
          if (r_type == R_PPC_PLTREL24
-             && htab->plt != NULL
-             && h->plt.offset != (bfd_vma) -1)
+             && htab->plt != NULL)
            {
-             if (!htab->old_plt)
-               {
-                 tsec = htab->glink;
-                 toff = h->plt.offset * (GLINK_ENTRY_SIZE / 4);
-               }
-             else
+             struct plt_entry *ent = find_plt_ent (h, got2, irel->r_addend);
+
+             if (ent != NULL)
                {
-                 tsec = htab->plt;
-                 toff = h->plt.offset;
+                 if (!htab->old_plt)
+                   {
+                     tsec = htab->glink;
+                     toff = ent->glink_offset;
+                   }
+                 else
+                   {
+                     tsec = htab->plt;
+                     toff = ent->plt.offset;
+                   }
                }
            }
+         if (tsec != NULL)
+           ;
          else if (h->root.type == bfd_link_hash_defined
                   || h->root.type == bfd_link_hash_defweak)
            {
@@ -4644,7 +5043,8 @@ ppc_elf_relax_section (bfd *abfd,
          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;
 
       symaddr = tsec->output_section->vma + tsec->output_offset + toff;
@@ -4884,24 +5284,38 @@ ppc_elf_set_sdata_syms (bfd *obfd, struct bfd_link_info *info)
       if (s == NULL)
        s = bfd_get_section_by_name (obfd, lsect->bss_name);
 
-      val = 0;
-      if (s != NULL)
-       val = s->vma + 32768;
-      lsect->sym_val = val;
+      if (s)
+       {
+         /* VxWorks executables are relocatable, so the sdata base symbols
+            must be section-relative.  If the section is zero sized leave
+            them as absolute symbols to avoid creationg an unused
+            output section.  */
+         val = 32768;
+         lsect->sym_val = val + s->vma;
+         if (s->size == 0)
+           {
+             val += s->vma;
+             s = NULL;
+           }
+       }
+      else
+       {
+         val = 0;
+         lsect->sym_val = 0;
+       }
 
-      _bfd_elf_provide_symbol (info, lsect->sym_name, val);
+      _bfd_elf_provide_symbol (info, lsect->sym_name, val, s);
     }
 
   s = bfd_get_section_by_name (obfd, ".sbss");
-  val = 0;
+  _bfd_elf_provide_symbol (info, "__sbss_start", 0, NULL);
+  _bfd_elf_provide_symbol (info, "___sbss_start", 0, NULL);
   if (s != NULL)
-    val = s->vma;
-  _bfd_elf_provide_symbol (info, "__sbss_start", val);
-  _bfd_elf_provide_symbol (info, "___sbss_start", val);
-  if (s != NULL)
-    val += s->size;
-  _bfd_elf_provide_symbol (info, "__sbss_end", val);
-  _bfd_elf_provide_symbol (info, "___sbss_end", val);
+    val = s->size;
+  else
+    val = 0;
+  _bfd_elf_provide_symbol (info, "__sbss_end", val, s);
+  _bfd_elf_provide_symbol (info, "___sbss_end", val, s);
   return TRUE;
 }
 \f
@@ -5012,7 +5426,7 @@ ppc_elf_relocate_section (bfd *output_bfd,
   Elf_Internal_Rela *relend;
   Elf_Internal_Rela outrel;
   bfd_byte *loc;
-  asection *sreloc = NULL;
+  asection *got2, *sreloc = NULL;
   bfd_vma *local_got_offsets;
   bfd_boolean ret = TRUE;
 
@@ -5024,8 +5438,30 @@ ppc_elf_relocate_section (bfd *output_bfd,
                      (info->relocatable) ? " (relocatable)" : "");
 #endif
 
+  got2 = bfd_get_section_by_name (input_bfd, ".got2");
+
   if (info->relocatable)
-    return TRUE;
+    {
+      if (got2 == NULL)
+       return TRUE;
+
+      rel = relocs;
+      relend = relocs + input_section->reloc_count;
+      for (; rel < relend; rel++)
+       {
+         enum elf_ppc_reloc_type r_type;
+
+         r_type = ELF32_R_TYPE (rel->r_info);
+         if (r_type == R_PPC_PLTREL24
+             && rel->r_addend >= 32768)
+           {
+             /* R_PPC_PLTREL24 is rather special.  If non-zero, the
+                addend specifies the GOT pointer offset within .got2.  */
+             rel->r_addend += got2->output_offset;
+           }
+       }
+      return TRUE;
+    }
 
   /* Initialize howto table if not already done.  */
   if (!ppc_elf_howto_table[R_PPC_ADDR32])
@@ -5803,18 +6239,19 @@ ppc_elf_relocate_section (bfd *output_bfd,
 
        case R_PPC_RELAX32PC_PLT:
        case R_PPC_RELAX32_PLT:
-         BFD_ASSERT (h != NULL
-                     && h->plt.offset != (bfd_vma) -1
-                     && htab->plt != NULL);
-
-         if (!htab->old_plt)
-           relocation = (htab->glink->output_section->vma
-                         + htab->glink->output_offset
-                         + h->plt.offset * (GLINK_ENTRY_SIZE / 4));
-         else
-           relocation = (htab->plt->output_section->vma
-                         + htab->plt->output_offset
-                         + h->plt.offset);
+         {
+           struct plt_entry *ent = find_plt_ent (h, got2, addend);
+
+           if (!htab->old_plt)
+             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);
+           addend = 0;
+         }
          if (r_type == R_PPC_RELAX32_PLT)
            goto relax32;
          /* Fall thru */
@@ -5881,26 +6318,29 @@ ppc_elf_relocate_section (bfd *output_bfd,
        case R_PPC_PLTREL24:
          /* Relocation is to the entry for this symbol in the
             procedure linkage table.  */
-         BFD_ASSERT (h != NULL);
+         {
+           struct plt_entry *ent = find_plt_ent (h, got2, addend);
 
-         if (h->plt.offset == (bfd_vma) -1
-             || 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;
-           }
+           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;
+             }
 
-         unresolved_reloc = FALSE;
-         if (!htab->old_plt)
-           relocation = (htab->glink->output_section->vma
-                         + htab->glink->output_offset
-                         + h->plt.offset * (GLINK_ENTRY_SIZE / 4));
-         else
-           relocation = (htab->plt->output_section->vma
-                         + htab->plt->output_offset
-                         + h->plt.offset);
+           unresolved_reloc = FALSE;
+           if (!htab->old_plt)
+             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);
+         }
          break;
 
          /* Relocate against _SDA_BASE_.  */
@@ -6162,6 +6602,10 @@ ppc_elf_relocate_section (bfd *output_bfd,
   return ret;
 }
 \f
+#define PPC_LO(v) ((v) & 0xffff)
+#define PPC_HI(v) (((v) >> 16) & 0xffff)
+#define PPC_HA(v) PPC_HI ((v) + 0x8000)
+
 /* Finish up dynamic symbol handling.  We set the contents of various
    dynamic sections here.  */
 
@@ -6172,6 +6616,8 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
                               Elf_Internal_Sym *sym)
 {
   struct ppc_elf_link_hash_table *htab;
+  struct plt_entry *ent;
+  bfd_boolean doneone;
 
 #ifdef DEBUG
   fprintf (stderr, "ppc_elf_finish_dynamic_symbol called for %s",
@@ -6181,69 +6627,266 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
   htab = ppc_elf_hash_table (info);
   BFD_ASSERT (htab->elf.dynobj != NULL);
 
-  if (h->plt.offset != (bfd_vma) -1)
-    {
-      Elf_Internal_Rela rela;
-      bfd_byte *loc;
-      bfd_vma reloc_index;
+  doneone = FALSE;
+  for (ent = h->plt.plist; ent != NULL; ent = ent->next)
+    if (ent->plt.offset != (bfd_vma) -1)
+      {
+       if (!doneone)
+         {
+           Elf_Internal_Rela rela;
+           bfd_byte *loc;
+           bfd_vma reloc_index;
 
-#ifdef DEBUG
-      fprintf (stderr, ", plt_offset = %d", h->plt.offset);
-#endif
+           if (!(htab->old_plt || htab->is_vxworks))
+             reloc_index = ent->plt.offset / 4;
+           else
+             {
+               reloc_index = ((ent->plt.offset - htab->plt_initial_entry_size)
+                              / htab->plt_slot_size);
+               if (reloc_index > PLT_NUM_SINGLE_ENTRIES
+                   && !htab->is_vxworks)
+                 reloc_index -= (reloc_index - PLT_NUM_SINGLE_ENTRIES) / 2;
+             }
 
-      /* This symbol has an entry in the procedure linkage table.  Set
-        it up.  */
+           /* This symbol has an entry in the procedure linkage table.
+              Set it up.  */
+           if (htab->is_vxworks)
+             {
+               bfd_vma got_offset;
+               const bfd_vma *plt_entry;
+               
+               /* The first three entries in .got.plt are reserved.  */
+               got_offset = (reloc_index + 3) * 4;
 
-      BFD_ASSERT (h->dynindx != -1);
-      BFD_ASSERT (htab->plt != NULL && htab->relplt != NULL);
+               /* Use the right PLT. */
+               plt_entry = info->shared ? ppc_elf_vxworks_pic_plt_entry
+                           : ppc_elf_vxworks_plt_entry;
 
-      if (htab->old_plt)
-       {
-         /* We don't need to fill in the .plt.  The ppc dynamic linker
-            will fill it in.  */
-       }
-      else
-       {
-         bfd_vma val = (htab->glink_pltresolve
-                        + h->plt.offset
-                        + htab->glink->output_section->vma
-                        + htab->glink->output_offset);
-         bfd_put_32 (output_bfd, val, htab->plt->contents + h->plt.offset);
-       }
+               /* Fill in the .plt on VxWorks.  */
+               if (info->shared)
+                 {
+                   bfd_vma got_offset_hi = (got_offset >> 16)
+                                           + ((got_offset & 0x8000) >> 15);
+
+                   bfd_put_32 (output_bfd,
+                               plt_entry[0] | (got_offset_hi & 0xffff),
+                               htab->plt->contents + ent->plt.offset + 0);
+                   bfd_put_32 (output_bfd,
+                               plt_entry[1] | (got_offset & 0xffff),
+                               htab->plt->contents + ent->plt.offset + 4);
+                 }
+               else
+                 {
+                   bfd_vma got_loc = (got_offset
+                       + htab->hgot->root.u.def.value
+                       + htab->hgot->root.u.def.section->output_offset
+                       + htab->hgot->root.u.def.section->output_section->vma);
+                   bfd_vma got_loc_hi = (got_loc >> 16)
+                                        + ((got_loc & 0x8000) >> 15);
+
+                   bfd_put_32 (output_bfd,
+                               plt_entry[0] | (got_loc_hi & 0xffff),
+                               htab->plt->contents + ent->plt.offset + 0);
+                   bfd_put_32 (output_bfd,
+                               plt_entry[1] | (got_loc & 0xffff),
+                               htab->plt->contents + ent->plt.offset + 4);
+                 }
 
-      /* Fill in the entry in the .rela.plt section.  */
-      rela.r_offset = (htab->plt->output_section->vma
-                      + htab->plt->output_offset
-                      + h->plt.offset);
-      rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
-      rela.r_addend = 0;
+               bfd_put_32 (output_bfd, plt_entry[2],
+                           htab->plt->contents + ent->plt.offset + 8);
+               bfd_put_32 (output_bfd, plt_entry[3],
+                           htab->plt->contents + ent->plt.offset + 12);
+
+               /* This instruction is an immediate load.  The value loaded is
+                  the byte offset of the R_PPC_JMP_SLOT relocation from the
+                  start of the .rela.plt section.  The value is stored in the
+                  low-order 16 bits of the load instruction.  */
+               /* NOTE: It appears that this is now an index rather than a
+                  prescaled offset.  */
+               bfd_put_32 (output_bfd, 
+                           plt_entry[4] | reloc_index,
+                           htab->plt->contents + ent->plt.offset + 16);
+               /* This instruction is a PC-relative branch whose target is
+                  the start of the PLT section.  The address of this branch
+                  instruction is 20 bytes beyond the start of this PLT entry.
+                  The address is encoded in bits 6-29, inclusive.  The value
+                  stored is right-shifted by two bits, permitting a 26-bit
+                  offset.  */
+               bfd_put_32 (output_bfd, 
+                           (plt_entry[5] 
+                            | (-(ent->plt.offset + 20) & 0x03fffffc)),
+                           htab->plt->contents + ent->plt.offset + 20);
+               bfd_put_32 (output_bfd, plt_entry[6],
+                           htab->plt->contents + ent->plt.offset + 24);
+               bfd_put_32 (output_bfd, plt_entry[7],
+                           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
+                  in this PLT entry.  */
+               bfd_put_32 (output_bfd, (htab->plt->output_section->vma
+                                        + htab->plt->output_offset
+                                        + ent->plt.offset + 16),
+                           htab->sgotplt->contents + got_offset);
+
+               if (!info->shared)
+                 {
+                   /* Fill in a couple of entries in .rela.plt.unloaded.  */
+                   loc = htab->srelplt2->contents
+                     + ((VXWORKS_PLTRESOLVE_RELOCS + reloc_index
+                         * VXWORKS_PLT_NON_JMP_SLOT_RELOCS)
+                        * sizeof (Elf32_External_Rela));
+
+                   /* Provide the @ha relocation for the first instruction.  */
+                   rela.r_offset = (htab->plt->output_section->vma
+                                    + htab->plt->output_offset
+                                    + ent->plt.offset + 2);
+                   rela.r_info = ELF32_R_INFO (htab->hgot->indx,
+                                               R_PPC_ADDR16_HA);
+                   rela.r_addend = got_offset;
+                   bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+                   loc += sizeof (Elf32_External_Rela);
+
+                   /* Provide the @l relocation for the second instruction.  */
+                   rela.r_offset = (htab->plt->output_section->vma
+                                    + htab->plt->output_offset
+                                    + ent->plt.offset + 6);
+                   rela.r_info = ELF32_R_INFO (htab->hgot->indx,
+                                               R_PPC_ADDR16_LO);
+                   rela.r_addend = got_offset;
+                   bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+                   loc += sizeof (Elf32_External_Rela);
+
+                   /* Provide a relocation for the GOT entry corresponding to this
+                      PLT slot.  Point it at the middle of the .plt entry.  */
+                   rela.r_offset = (htab->sgotplt->output_section->vma
+                                    + htab->sgotplt->output_offset
+                                    + got_offset);
+                   rela.r_info = ELF32_R_INFO (htab->hplt->indx,
+                                               R_PPC_ADDR32);
+                   rela.r_addend = ent->plt.offset + 16;
+                   bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+                 }
 
-      if (!htab->old_plt)
-       reloc_index = h->plt.offset / 4;
-      else
-       {
-         reloc_index = ((h->plt.offset - PLT_INITIAL_ENTRY_SIZE)
-                        / PLT_SLOT_SIZE);
-         if (reloc_index > PLT_NUM_SINGLE_ENTRIES)
-           reloc_index -= (reloc_index - PLT_NUM_SINGLE_ENTRIES) / 2;
-       }
-      loc = (htab->relplt->contents
-            + reloc_index * sizeof (Elf32_External_Rela));
-      bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+               /* VxWorks uses non-standard semantics for R_PPC_JMP_SLOT.
+                  In particular, the offset for the relocation is not the
+                  address of the PLT entry for this function, as specified
+                  by the ABI.  Instead, the offset is set to the address of
+                  the GOT slot for this function.  See EABI 4.4.4.1.  */
+               rela.r_offset = (htab->sgotplt->output_section->vma
+                                + htab->sgotplt->output_offset
+                                + got_offset);
 
-      if (!h->def_regular)
-       {
-         /* Mark the symbol as undefined, rather than as defined in
-            the .plt section.  Leave the value alone.  */
-         sym->st_shndx = SHN_UNDEF;
-         /* If the symbol is weak, we do need to clear the value.
-            Otherwise, the PLT entry would provide a definition for
-            the symbol even if the symbol wasn't defined anywhere,
-            and so the symbol would never be NULL.  */
-         if (!h->ref_regular_nonweak)
-           sym->st_value = 0;
-       }
-    }
+             }
+           else
+             {
+               rela.r_offset = (htab->plt->output_section->vma
+                                + htab->plt->output_offset
+                                + ent->plt.offset);
+               if (htab->old_plt)
+                 {
+                   /* We don't need to fill in the .plt.  The ppc dynamic
+                      linker will fill it in.  */
+                 }
+               else
+                 {
+                   bfd_vma val = (htab->glink_pltresolve + ent->plt.offset
+                                  + htab->glink->output_section->vma
+                                  + htab->glink->output_offset);
+                   bfd_put_32 (output_bfd, val,
+                               htab->plt->contents + ent->plt.offset);
+                 }
+             }
+
+           /* Fill in the entry in the .rela.plt section.  */
+           rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
+           rela.r_addend = 0;
+
+           loc = (htab->relplt->contents
+                  + reloc_index * sizeof (Elf32_External_Rela));
+           bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+
+           if (!h->def_regular)
+             {
+               /* Mark the symbol as undefined, rather than as defined in
+                  the .plt section.  Leave the value alone.  */
+               sym->st_shndx = SHN_UNDEF;
+               /* If the symbol is weak, we do need to clear the value.
+                  Otherwise, the PLT entry would provide a definition for
+                  the symbol even if the symbol wasn't defined anywhere,
+                  and so the symbol would never be NULL.  */
+               if (!h->ref_regular_nonweak)
+                 sym->st_value = 0;
+             }
+           doneone = TRUE;
+         }
+
+       if (!htab->old_plt)
+         {
+           bfd_vma plt;
+           unsigned char *p;
+
+           plt = (ent->plt.offset
+                  + htab->plt->output_section->vma
+                  + htab->plt->output_offset);
+           p = (unsigned char *) htab->glink->contents + ent->glink_offset;
+
+           if (info->shared || info->pie)
+             {
+               bfd_vma got = 0;
+
+               if (ent->addend >= 32768)
+                 got = (ent->addend
+                        + ent->sec->output_section->vma
+                        + ent->sec->output_offset);
+               else if (htab->elf.hgot != NULL)
+                 got = (htab->elf.hgot->root.u.def.value
+                        + htab->elf.hgot->root.u.def.section->output_section->vma
+                        + htab->elf.hgot->root.u.def.section->output_offset);
+
+               plt -= got;
+
+               if (plt + 0x8000 < 0x10000)
+                 {
+                   bfd_put_32 (output_bfd, LWZ_11_30 + PPC_LO (plt), p);
+                   p += 4;
+                   bfd_put_32 (output_bfd, MTCTR_11, p);
+                   p += 4;
+                   bfd_put_32 (output_bfd, BCTR, p);
+                   p += 4;
+                   bfd_put_32 (output_bfd, NOP, p);
+                   p += 4;
+                 }
+               else
+                 {
+                   bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (plt), p);
+                   p += 4;
+                   bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
+                   p += 4;
+                   bfd_put_32 (output_bfd, MTCTR_11, p);
+                   p += 4;
+                   bfd_put_32 (output_bfd, BCTR, p);
+                   p += 4;
+                 }
+             }
+           else
+             {
+               bfd_put_32 (output_bfd, LIS_11 + PPC_HA (plt), p);
+               p += 4;
+               bfd_put_32 (output_bfd, LWZ_11_11 + PPC_LO (plt), p);
+               p += 4;
+               bfd_put_32 (output_bfd, MTCTR_11, p);
+               p += 4;
+               bfd_put_32 (output_bfd, BCTR, p);
+               p += 4;
+
+               /* We only need one non-PIC glink stub.  */
+               break;
+             }
+         }
+       else
+         break;
+      }
 
   if (h->needs_copy)
     {
@@ -6259,7 +6902,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
 
       BFD_ASSERT (h->dynindx != -1);
 
-      if (h->size <= elf_gp_size (htab->elf.dynobj))
+      if (ppc_elf_hash_entry (h)->has_sda_refs)
        s = htab->relsbss;
       else
        s = htab->relbss;
@@ -6279,9 +6922,11 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd,
 #endif
 
   /* Mark some specially defined symbols as absolute.  */
-  if (h == htab->elf.hgot
-      || strcmp (h->root.root.string, "_DYNAMIC") == 0
-      || strcmp (h->root.root.string, "_PROCEDURE_LINKAGE_TABLE_") == 0)
+  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;
@@ -6312,15 +6957,22 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
                                 struct bfd_link_info *info)
 {
   asection *sdyn;
+  asection *splt;
   struct ppc_elf_link_hash_table *htab;
   bfd_vma got;
+  bfd * dynobj;
 
 #ifdef DEBUG
   fprintf (stderr, "ppc_elf_finish_dynamic_sections called\n");
 #endif
 
   htab = ppc_elf_hash_table (info);
-  sdyn = bfd_get_section_by_name (htab->elf.dynobj, ".dynamic");
+  dynobj = elf_hash_table (info)->dynobj;
+  sdyn = bfd_get_section_by_name (dynobj, ".dynamic");
+  if (htab->is_vxworks)
+    splt = bfd_get_section_by_name (dynobj, ".plt");  
+  else
+    splt = NULL;
 
   got = 0;
   if (htab->elf.hgot != NULL)
@@ -6341,12 +6993,15 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
          Elf_Internal_Dyn dyn;
          asection *s;
 
-         bfd_elf32_swap_dyn_in (htab->elf.dynobj, dyncon, &dyn);
+         bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn);
 
          switch (dyn.d_tag)
            {
            case DT_PLTGOT:
-             s = htab->plt;
+             if (htab->is_vxworks)
+               s = htab->sgotplt;
+             else
+               s = htab->plt;
              dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
              break;
 
@@ -6363,6 +7018,15 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
              dyn.d_un.d_ptr = got;
              break;
 
+           case DT_RELASZ:
+             if (htab->is_vxworks)
+               {
+                 if (htab->relplt)
+                   dyn.d_un.d_ptr -= htab->relplt->size;
+                 break;
+               }
+             continue;
+
            default:
              continue;
            }
@@ -6379,7 +7043,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
       bfd_vma val;
 
       p += elf_hash_table (info)->hgot->root.u.def.value;
-      if (htab->old_plt)
+      if (htab->old_plt && !htab->is_vxworks)
        bfd_put_32 (output_bfd, 0x4e800021 /* blrl */, p - 4);
 
       val = 0;
@@ -6390,66 +7054,181 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
       elf_section_data (htab->got->output_section)->this_hdr.sh_entsize = 4;
     }
 
+  /* Fill in the first entry in the VxWorks procedure linkage table.  */
+  if (splt && splt->size > 0)
+    {
+      /* Use the right PLT. */
+      static const bfd_vma *plt_entry = NULL;
+      plt_entry = info->shared ? 
+       ppc_elf_vxworks_pic_plt0_entry : ppc_elf_vxworks_plt0_entry;
+
+      if (!info->shared)
+       {
+         bfd_vma got_value =
+           (htab->hgot->root.u.def.section->output_section->vma
+            + htab->hgot->root.u.def.section->output_offset
+            + htab->hgot->root.u.def.value);
+         bfd_vma got_hi = (got_value >> 16) + ((got_value & 0x8000) >> 15);
+
+         bfd_put_32 (output_bfd, plt_entry[0] | (got_hi & 0xffff),
+                     splt->contents +  0);
+         bfd_put_32 (output_bfd, plt_entry[1] | (got_value & 0xffff),
+                     splt->contents +  4);
+       }
+      else
+       {
+         bfd_put_32 (output_bfd, plt_entry[0], splt->contents +  0);
+         bfd_put_32 (output_bfd, plt_entry[1], splt->contents +  4);
+       }
+      bfd_put_32 (output_bfd, plt_entry[2], splt->contents +  8);
+      bfd_put_32 (output_bfd, plt_entry[3], splt->contents + 12);
+      bfd_put_32 (output_bfd, plt_entry[4], splt->contents + 16);
+      bfd_put_32 (output_bfd, plt_entry[5], splt->contents + 20);
+      bfd_put_32 (output_bfd, plt_entry[6], splt->contents + 24);
+      bfd_put_32 (output_bfd, plt_entry[7], splt->contents + 28);
+
+      if (! info->shared)
+       {
+         Elf_Internal_Rela rela;
+         bfd_byte *loc;
+
+         loc = htab->srelplt2->contents;
+
+         /* Output the @ha relocation for the first instruction.  */
+         rela.r_offset = (htab->plt->output_section->vma
+                          + htab->plt->output_offset
+                          + 2);
+         rela.r_info = ELF32_R_INFO (htab->hgot->indx, R_PPC_ADDR16_HA);
+         rela.r_addend = 0;
+         bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+         loc += sizeof (Elf32_External_Rela);
+         
+         /* Output the @l relocation for the second instruction.  */
+         rela.r_offset = (htab->plt->output_section->vma
+                          + htab->plt->output_offset
+                          + 6);
+         rela.r_info = ELF32_R_INFO (htab->hgot->indx, R_PPC_ADDR16_LO);
+         rela.r_addend = 0;
+         bfd_elf32_swap_reloca_out (output_bfd, &rela, loc);
+         loc += sizeof (Elf32_External_Rela);
+
+         /* Fix up the remaining relocations.  They may have the wrong
+            symbol index for _G_O_T_ or _P_L_T_ depending on the order
+            in which symbols were output.  */
+         while (loc < htab->srelplt2->contents + htab->srelplt2->size)
+           {
+             Elf_Internal_Rela rel;
+
+             bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
+             rel.r_info = ELF32_R_INFO (htab->hgot->indx, R_PPC_ADDR16_HA);
+             bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+             loc += sizeof (Elf32_External_Rela);
+
+             bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
+             rel.r_info = ELF32_R_INFO (htab->hgot->indx, R_PPC_ADDR16_LO);
+             bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+             loc += sizeof (Elf32_External_Rela);
+
+             bfd_elf32_swap_reloc_in (output_bfd, loc, &rel);
+             rel.r_info = ELF32_R_INFO (htab->hplt->indx, R_PPC_ADDR32);
+             bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+             loc += sizeof (Elf32_External_Rela);
+           }
+       }
+    }
+
   if (htab->glink != NULL && htab->glink->contents != NULL)
     {
       unsigned char *p;
       unsigned char *endp;
-      bfd_vma pltgot;
+      bfd_vma res0;
       unsigned int i;
-      static const unsigned int plt_resolve[] =
+
+      /*
+       * PIC glink code is the following:
+       *
+       * # ith PLT code stub.
+       *   addis 11,30,(plt+(i-1)*4-got)@ha
+       *   lwz 11,(plt+(i-1)*4-got)@l(11)
+       *   mtctr 11
+       *   bctr
+       *
+       * # A table of branches, one for each plt entry.
+       * # The idea is that the plt call stub loads ctr (and r11) with these
+       * # addresses, so (r11 - res_0) gives the plt index * 4.
+       * res_0:        b PLTresolve
+       * res_1:        b PLTresolve
+       * .
+       * # Some number of entries towards the end can be nops
+       * res_n_m3: nop
+       * res_n_m2: nop
+       * res_n_m1:
+       *
+       * PLTresolve:
+       *    addis 11,11,(1f-res_0)@ha
+       *    mflr 0
+       *    bcl 20,31,1f
+       * 1: addi 11,11,(1b-res_0)@l
+       *    mflr 12
+       *    mtlr 0
+       *    sub 11,11,12                # r11 = index * 4
+       *    addis 12,12,(got+4-1b)@ha
+       *    lwz 0,(got+4-1b)@l(12)      # got[1] address of dl_runtime_resolve
+       *    lwz 12,(got+8-1b)@l(12)     # got[2] contains the map address
+       *    mtctr 0
+       *    add 0,11,11
+       *    add 11,0,11                 # r11 = index * 12 = reloc offset.
+       *    bctr
+       */
+      static const unsigned int pic_plt_resolve[] =
        {
-         SUB_11_11_30,
+         ADDIS_11_11,
+         MFLR_0,
+         BCL_20_31,
+         ADDI_11_11,
+         MFLR_12,
+         MTLR_0,
+         SUB_11_11_12,
+         ADDIS_12_12,
+         LWZ_0_12,
+         LWZ_12_12,
+         MTCTR_0,
          ADD_0_11_11,
          ADD_11_0_11,
-         LWZ_0_4_30,
+         BCTR,
+         NOP,
+         NOP
+       };
+
+      static const unsigned int plt_resolve[] =
+       {
+         LIS_12,
+         ADDIS_11_11,
+         LWZ_0_12,
+         ADDI_11_11,
          MTCTR_0,
-         LWZ_12_8_30,
+         ADD_0_11_11,
+         LWZ_12_12,
+         ADD_11_0_11,
          BCTR,
          NOP,
          NOP,
+         NOP,
+         NOP,
+         NOP,
+         NOP,
          NOP
        };
 
-#define PPC_LO(v) ((v) & 0xffff)
-#define PPC_HI(v) (((v) >> 16) & 0xffff)
-#define PPC_HA(v) PPC_HI ((v) + 0x8000)
-
-      pltgot = (htab->plt->output_section->vma
-               + htab->plt->output_offset
-               - got);
-
-      /* Write the plt call stubs.  */
-      p = htab->glink->contents;
-      endp = p + htab->glink_pltresolve;
-      while (p < endp)
-       {
-         if (pltgot < 0x8000)
-           {
-             bfd_put_32 (output_bfd, LWZ_11_X_30 + pltgot, p);
-             p += 4;
-             bfd_put_32 (output_bfd, MTCTR_11, p);
-             p += 4;
-             bfd_put_32 (output_bfd, BCTR, p);
-             p += 4;
-             bfd_put_32 (output_bfd, NOP, p);
-             p += 4;
-           }
-         else
-           {
-             bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (pltgot), p);
-             p += 4;
-             bfd_put_32 (output_bfd, LWZ_11_X_11 + PPC_LO (pltgot), p);
-             p += 4;
-             bfd_put_32 (output_bfd, MTCTR_11, p);
-             p += 4;
-             bfd_put_32 (output_bfd, BCTR, p);
-             p += 4;
-           }
-         pltgot += 4;
-       }
+      if (ARRAY_SIZE (pic_plt_resolve) != GLINK_PLTRESOLVE / 4)
+       abort ();
+      if (ARRAY_SIZE (plt_resolve) != GLINK_PLTRESOLVE / 4)
+       abort ();
 
-      /* Now build the branch table, one for each plt entry (less one),
+      /* Build the branch table, one for each plt entry (less one),
         and perhaps some padding.  */
+      p = htab->glink->contents;
+      p += htab->glink_pltresolve;
       endp = htab->glink->contents;
       endp += htab->glink->size - GLINK_PLTRESOLVE;
       while (p < endp - 8 * 4)
@@ -6463,23 +7242,77 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
          p += 4;
        }
 
-      got -= (htab->glink_pltresolve
+      res0 = (htab->glink_pltresolve
              + htab->glink->output_section->vma
              + htab->glink->output_offset);
 
       /* Last comes the PLTresolve stub.  */
-      bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (got), p);
-      p += 4;
-      bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (got), p);
-      p += 4;
+      if (info->shared || info->pie)
+       {
+         bfd_vma bcl;
 
-      for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
+         for (i = 0; i < ARRAY_SIZE (pic_plt_resolve); i++)
+           {
+             bfd_put_32 (output_bfd, pic_plt_resolve[i], p);
+             p += 4;
+           }
+         p -= 4 * ARRAY_SIZE (pic_plt_resolve);
+
+         bcl = (htab->glink->size - GLINK_PLTRESOLVE + 3*4
+                + htab->glink->output_section->vma
+                + htab->glink->output_offset);
+
+         bfd_put_32 (output_bfd,
+                     ADDIS_11_11 + PPC_HA (bcl - res0), p + 0*4);
+         bfd_put_32 (output_bfd,
+                     ADDI_11_11 + PPC_LO (bcl - res0), p + 3*4);
+         bfd_put_32 (output_bfd,
+                     ADDIS_12_12 + PPC_HA (got + 4 - bcl), p + 7*4);
+         if (PPC_HA (got + 4 - bcl) == PPC_HA (got + 8 - bcl))
+           {
+             bfd_put_32 (output_bfd,
+                         LWZ_0_12 + PPC_LO (got + 4 - bcl), p + 8*4);
+             bfd_put_32 (output_bfd,
+                         LWZ_12_12 + PPC_LO (got + 8 - bcl), p + 9*4);
+           }
+         else
+           {
+             bfd_put_32 (output_bfd,
+                         LWZU_0_12 + PPC_LO (got + 4 - bcl), p + 8*4);
+             bfd_put_32 (output_bfd,
+                         LWZ_12_12 + 4, p + 9*4);
+           }
+       }
+      else
        {
-         bfd_put_32 (output_bfd, plt_resolve[i], p);
-         p += 4;
+         for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
+           {
+             bfd_put_32 (output_bfd, plt_resolve[i], p);
+             p += 4;
+           }
+         p -= 4 * ARRAY_SIZE (plt_resolve);
+
+         bfd_put_32 (output_bfd,
+                     LIS_12 + PPC_HA (got + 4), p + 0*4);
+         bfd_put_32 (output_bfd,
+                     ADDIS_11_11 + PPC_HA (-res0), p + 1*4);
+         bfd_put_32 (output_bfd,
+                     ADDI_11_11 + PPC_LO (-res0), p + 3*4);
+         if (PPC_HA (got + 4) == PPC_HA (got + 8))
+           {
+             bfd_put_32 (output_bfd,
+                         LWZ_0_12 + PPC_LO (got + 4), p + 2*4);
+             bfd_put_32 (output_bfd,
+                         LWZ_12_12 + PPC_LO (got + 8), p + 6*4);
+           }
+         else
+           {
+             bfd_put_32 (output_bfd,
+                         LWZU_0_12 + PPC_LO (got + 4), p + 2*4);
+             bfd_put_32 (output_bfd,
+                         LWZ_12_12 + 4, p + 6*4);
+           }
        }
-      if (ARRAY_SIZE (plt_resolve) + 2 != GLINK_PLTRESOLVE / 4)
-       abort ();
     }
 
   return TRUE;
@@ -6540,7 +7373,151 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
 #define elf_backend_begin_write_processing     ppc_elf_begin_write_processing
 #define elf_backend_final_write_processing     ppc_elf_final_write_processing
 #define elf_backend_write_section              ppc_elf_write_section
-#define elf_backend_special_sections           ppc_elf_special_sections
+#define elf_backend_get_sec_type_attr          ppc_elf_get_sec_type_attr
 #define elf_backend_plt_sym_val                        ppc_elf_plt_sym_val
 
 #include "elf32-target.h"
+
+/* VxWorks Target */
+
+#undef TARGET_LITTLE_SYM
+#undef TARGET_LITTLE_NAME
+
+#undef TARGET_BIG_SYM
+#define TARGET_BIG_SYM         bfd_elf32_powerpc_vxworks_vec
+#undef TARGET_BIG_NAME
+#define TARGET_BIG_NAME                "elf32-powerpc-vxworks"
+
+/* This is the same as ppc_elf_special_sections except it does not include
+   the entry for .plt.  */
+static struct bfd_elf_special_section const *
+  ppc_elf_vxworks_special_sections[27]=
+{
+  NULL,                                /* 'a' */
+  NULL,                                /* 'b' */
+  NULL,                                /* 'c' */
+  NULL,                                /* 'd' */
+  NULL,                                /* 'e' */
+  NULL,                                /* 'f' */
+  NULL,                                /* 'g' */
+  NULL,                                /* 'h' */
+  NULL,                                /* 'i' */
+  NULL,                                /* 'j' */
+  NULL,                                /* 'k' */
+  NULL,                                /* 'l' */
+  NULL,                                /* 'm' */
+  NULL,                                /* 'n' */
+  NULL,                                /* 'o' */
+  NULL,                                /* 'p' */
+  NULL,                                /* 'q' */
+  NULL,                                /* 'r' */
+  ppc_special_sections_s,      /* 's' */
+  ppc_special_sections_t,      /* 's' */
+  NULL,                                /* 'u' */
+  NULL,                                /* 'v' */
+  NULL,                                /* 'w' */
+  NULL,                                /* 'x' */
+  NULL,                                /* 'y' */
+  NULL,                                /* 'z' */
+  ppc_special_sections_other,  /* other */
+};
+
+/* Like ppc_elf_link_hash_table_create, but overrides
+   appropriately for VxWorks.  */
+static struct bfd_link_hash_table *
+ppc_elf_vxworks_link_hash_table_create (bfd *abfd)
+{
+  struct bfd_link_hash_table *ret;
+
+  ret = ppc_elf_link_hash_table_create (abfd);
+  if (ret)
+    {
+      struct ppc_elf_link_hash_table *htab
+        = (struct ppc_elf_link_hash_table *)ret;
+      htab->is_vxworks = 1;
+      htab->plt_entry_size = VXWORKS_PLT_ENTRY_SIZE;
+      htab->plt_slot_size = VXWORKS_PLT_ENTRY_SIZE;
+      htab->plt_initial_entry_size = VXWORKS_PLT_INITIAL_ENTRY_SIZE;
+    }
+  return ret;
+}
+
+/* Tweak magic VxWorks symbols as they are loaded.  */
+static bfd_boolean
+ppc_elf_vxworks_add_symbol_hook (bfd *abfd,
+                                struct bfd_link_info *info,
+                                Elf_Internal_Sym *sym,
+                                const char **namep ATTRIBUTE_UNUSED,
+                                flagword *flagsp ATTRIBUTE_UNUSED,
+                                asection **secp,
+                                bfd_vma *valp)
+{
+  if (!elf_vxworks_add_symbol_hook(abfd, info, sym,namep, flagsp, secp,
+                                  valp))
+    return FALSE;
+
+  return ppc_elf_add_symbol_hook(abfd, info, sym,namep, flagsp, secp, valp);
+}
+
+/* Tweak magic VxWorks symbols as they are written to the output file.  */
+static bfd_boolean
+elf_i386_vxworks_link_output_symbol_hook (struct bfd_link_info *info
+                                          ATTRIBUTE_UNUSED,
+                                        const char *name,
+                                        Elf_Internal_Sym *sym,
+                                        asection *input_sec ATTRIBUTE_UNUSED,
+                                        struct elf_link_hash_entry *h
+                                          ATTRIBUTE_UNUSED)
+{
+  /* Ignore the first dummy symbol.  */
+  if (!name)
+    return TRUE;
+
+  return elf_vxworks_link_output_symbol_hook (name, sym);
+}
+
+static void
+ppc_elf_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
+{
+  ppc_elf_final_write_processing(abfd, linker);
+  elf_vxworks_final_write_processing(abfd, linker);
+}
+
+/* On VxWorks, we emit relocations against _PROCEDURE_LINKAGE_TABLE_, so
+   define it.  */
+#undef elf_backend_want_plt_sym
+#define elf_backend_want_plt_sym               1
+#undef elf_backend_want_got_plt
+#define elf_backend_want_got_plt               1
+#undef elf_backend_got_symbol_offset
+#define elf_backend_got_symbol_offset          0
+#undef elf_backend_plt_not_loaded
+#define elf_backend_plt_not_loaded             0
+#undef elf_backend_plt_readonly
+#define elf_backend_plt_readonly               1
+#undef elf_backend_got_header_size
+#define elf_backend_got_header_size            12
+
+#undef bfd_elf32_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_create \
+  ppc_elf_vxworks_link_hash_table_create
+#undef elf_backend_special_sections
+#define elf_backend_special_sections \
+  ppc_elf_vxworks_special_sections
+#undef elf_backend_add_symbol_hook
+#define elf_backend_add_symbol_hook \
+  ppc_elf_vxworks_add_symbol_hook
+#undef elf_backend_link_output_symbol_hook
+#define elf_backend_link_output_symbol_hook \
+  elf_i386_vxworks_link_output_symbol_hook
+#undef elf_backend_final_write_processing
+#define elf_backend_final_write_processing \
+  ppc_elf_vxworks_final_write_processing
+#undef elf_backend_emit_relocs
+#define elf_backend_emit_relocs \
+  elf_vxworks_emit_relocs
+
+#undef elf32_bed
+#define elf32_bed                              ppc_elf_vxworks_bed
+
+#include "elf32-target.h"
This page took 0.049188 seconds and 4 git commands to generate.