public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* powerpc new PLT and GOT
@ 2005-05-11 14:22 Alan Modra
  2005-05-11 14:29 ` Daniel Jacobowitz
                   ` (2 more replies)
  0 siblings, 3 replies; 17+ messages in thread
From: Alan Modra @ 2005-05-11 14:22 UTC (permalink / raw)
  To: binutils

[-- Attachment #1: Type: text/plain, Size: 39670 bytes --]

The current PowerPC ABI requires PLT and GOT to be both writable and
executable, which is a security concern.  It's worse than that actually,
since the entire data segment ends up executable.

This patch implements binutils support for a new PLT and GOT on PowerPC,
details of which are in the attached ABI proposal.  Note that the
proposal is by no means final;  There's likely to be some change in
.glink to do without the lwzu instructions, but I think it's solid
enough to throw some code together.  Also, the ld support doesn't allow
registers other than r30 as a GOT pointer.  We'll worry about that if
and when gcc generates code using a different register.

From a user perspective, this ld change won't do anything until gcc
starts generating code for the new PLT and GOT.  ie. ld will continue to
link using the old PLT scheme.

bfd/
	* reloc.c (BFD_RELOC_HI16_PCREL): Define.
	(BFD_RELOC_HI16_S_PCREL, BFD_RELOC_LO16_PCREL): Define.
	* elf32-ppc.c (GLINK_PLTRESOLVE, GLINK_ENTRY_SIZE): Define.
	(CROR_151515, CROR_313131): Delete.
	(ADDIS_11_11, ADDI_11_11, SUB_11_11_30, ADD_0_11_11, ADD_11_0_11,
	LWZ_0_4_30, MTCTR_0, LWZ_12_8_30, BCTR, ADDIS_11_30,
	LWZU_0_X_11): Define.
	(ppc_elf_howto_raw): Add R_PPC_REL16, R_PPC_REL16_LO, R_PPC_REL16_HI
	and R_PPC_REL16_HA entries.
	(ppc_elf_reloc_type_lookup): Convert new bfd reloc types.
	(ppc_elf_addr16_ha_reloc): Also handle R_PPC_REL16_HA.
	(struct ppc_elf_link_hash_table): Add glink, glink_pltresolve,
	new_plt, and old_plt.
	(ppc_elf_create_dynamic_sections): Create .glink section.
	(ppc_elf_check_relocs): Set new_plt and old_plt.
	(ppc_elf_select_plt_layout): New function.
	(ppc_elf_tls_setup): Set plt output section elf type and flags.
	(allocate_got): Handle differences between old and new got layout.
	(allocate_dynrelocs): Likewise for plt.
	(ppc_elf_size_dynamic_sections): Likewise.  Allocate memory for
	.glink.  Don't allocate memory for old bss .plt.  Emit DT_PPC_GLINK.
	(ppc_elf_relax_section): Rename ppc_info to htab.  Handle .glink
	destination of R_PPC_PLTREL24 relocs.
	(ppc_elf_relocate_section): Handle new relocs and changed destination
	of R_PPC_PLTREL24.
	(ppc_elf_finish_dynamic_symbol): Init new style plt and handle
	differences in layout.
	(ppc_elf_finish_dynamic_sections): Set DT_PPC_GLINK value.  Don't
	put a blrl in new got.  Write glink contents.
	* elf32-ppc.h (ppc_elf_select_plt_layout): Declare.
	* libbfd.h: Regenerate.
	* bfd-in2.h: Regenerate.

binutils/
	* readelf.c (get_ppc_dynamic_type): New function for DT_PPC_GLINK.
	(get_dynamic_type): Call the above.

gas/
	* config/tc-ppc.c (md_apply_fix3): Allow pcrel forms of BFD_RELOC_16,
	BFD_RELOC_LO16, BFD_RELOC_HI16 and BFD_RELOC_HI16_S.

include/elf/
	* ppc.h (R_PPC_RELAX32, R_PPC_RELAX32PC, R_PPC_RELAX32_PLT,
	R_PPC_RELAX32PC_PLT) Adjust.
	(R_PPC_REL16, R_PPC_REL16_LO, R_PPC_REL16_HI, R_PPC_REL16_HA): Define.
	(DT_PPC_GLINK): Define.

ld/
	* ldgram.y: Add SPECIAL token.
	(sect_constraint): Handle SPECIAL.
	* ldlang.c (lang_output_section_find_1): Don't match SPECIAL.
	(map_input_to_output_sections): Likewise.
	* ldlex.l (SPECIAL): Define.
	* emulparams/elf32ppc.sh (DATA_GOT, SDATA_GOT, SEPARATE_GOTPLT,
	GOT, PLT, GOTPLT): Define.
	* emultempl/ppc32elf.em (old_plt, old_got): New static vars.
	(ppc_after_open): New function.
	(PARSE_AND_LIST_PROLOGUE): Define OPTION_OLD_LPT and OPTION_OLD_GOT.
	(PARSE_AND_LIST_LONGOPTS): Add "bss-plt" and "sdata-got".
	(PARSE_AND_LIST_OPTIONS): Document them.
	(PARSE_AND_LIST_ARGS_CASES): Handle them.
	(LDEMUL_AFTER_OPEN): Define.
	* scripttempl/elf.sc (PLT): Don't override existing define.
	(DATA_GOT, SDATA_GOT): Define and use to enable alternate got
	placement rather than using NO_SMALL_DATA.  Emit GOTPLT for RELRO_NOW.

Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.126
diff -u -p -r1.126 reloc.c
--- bfd/reloc.c	4 May 2005 15:53:37 -0000	1.126
+++ bfd/reloc.c	11 May 2005 08:57:41 -0000
@@ -2063,6 +2063,19 @@ ENUMDOC
   Low 16 bits.
 
 ENUM
+  BFD_RELOC_HI16_PCREL
+ENUMDOC
+  High 16 bits of 32-bit pc-relative value
+ENUM
+  BFD_RELOC_HI16_S_PCREL
+ENUMDOC
+  High 16 bits of 32-bit pc-relative value, adjusted
+ENUM
+  BFD_RELOC_LO16_PCREL
+ENUMDOC
+  Low 16 bits of pc-relative value
+
+ENUM
   BFD_RELOC_MIPS16_HI16
 ENUMDOC
   MIPS16 high 16 bits of 32-bit value.
Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.155
diff -u -p -r1.155 elf32-ppc.c
--- bfd/elf32-ppc.c	7 May 2005 13:22:49 -0000	1.155
+++ bfd/elf32-ppc.c	11 May 2005 13:23:05 -0000
@@ -51,6 +51,7 @@ static bfd_reloc_status_type ppc_elf_unh
    section.  */
 #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.  */
@@ -60,10 +61,23 @@ static bfd_reloc_status_type ppc_elf_unh
 /* The number of single-slot PLT entries (the rest use two slots).  */
 #define PLT_NUM_SINGLE_ENTRIES 8192
 
-/* Some nop instructions.  */
+/* For new-style .glink and .plt.  */
+#define GLINK_PLTRESOLVE 12*4
+#define GLINK_ENTRY_SIZE 4*4
+
+/* Some instructions.  */
 #define NOP		0x60000000
-#define CROR_151515	0x4def7b82
-#define CROR_313131	0x4ffffb82
+#define ADDIS_11_11	0x3d6b0000
+#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 BCTR		0x4e800420
+#define ADDIS_11_30	0x3d7e0000
+#define LWZU_0_X_11	0x840b0000
 
 /* Offset of tp and dtp pointers from start of TLS block.  */
 #define TP_OFFSET	0x7000
@@ -1259,6 +1273,67 @@ static reloc_howto_type ppc_elf_howto_ra
 	 0xffff,		/* dst_mask */
 	 FALSE),		/* pcrel_offset */
 
+  /* A 16 bit relative relocation.  */
+  HOWTO (R_PPC_REL16,		/* type */
+	 0,			/* rightshift */
+	 1,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 TRUE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_bitfield, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_PPC_REL16",		/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffff,		/* dst_mask */
+	 TRUE),			/* pcrel_offset */
+
+  /* A 16 bit relative relocation without overflow.  */
+  HOWTO (R_PPC_REL16_LO,	/* type */
+	 0,			/* rightshift */
+	 1,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 TRUE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont,/* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_PPC_REL16_LO",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffff,		/* dst_mask */
+	 TRUE),			/* pcrel_offset */
+
+  /* The high order 16 bits of a relative address.  */
+  HOWTO (R_PPC_REL16_HI,	/* type */
+	 16,			/* rightshift */
+	 1,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 TRUE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 bfd_elf_generic_reloc,	/* special_function */
+	 "R_PPC_REL16_HI",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffff,		/* dst_mask */
+	 TRUE),			/* pcrel_offset */
+
+  /* The high order 16 bits of a relative address, plus 1 if the contents of
+     the low 16 bits, treated as a signed number, is negative.  */
+  HOWTO (R_PPC_REL16_HA,	/* type */
+	 16,			/* rightshift */
+	 1,			/* size (0 = byte, 1 = short, 2 = long) */
+	 16,			/* bitsize */
+	 TRUE,			/* pc_relative */
+	 0,			/* bitpos */
+	 complain_overflow_dont, /* complain_on_overflow */
+	 ppc_elf_addr16_ha_reloc, /* special_function */
+	 "R_PPC_REL16_HA",	/* name */
+	 FALSE,			/* partial_inplace */
+	 0,			/* src_mask */
+	 0xffff,		/* dst_mask */
+	 TRUE),			/* pcrel_offset */
+
   /* GNU extension to record C++ vtable hierarchy.  */
   HOWTO (R_PPC_GNU_VTINHERIT,	/* type */
 	 0,			/* rightshift */
@@ -1418,6 +1493,10 @@ ppc_elf_reloc_type_lookup (bfd *abfd ATT
     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_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;
+    case BFD_RELOC_HI16_S_PCREL:	r = R_PPC_REL16_HA;		break;
     case BFD_RELOC_VTABLE_INHERIT:	r = R_PPC_GNU_VTINHERIT;	break;
     case BFD_RELOC_VTABLE_ENTRY:	r = R_PPC_GNU_VTENTRY;		break;
     }
@@ -1440,7 +1519,7 @@ ppc_elf_info_to_howto (bfd *abfd ATTRIBU
   cache_ptr->howto = ppc_elf_howto_table[ELF32_R_TYPE (dst->r_info)];
 }
 
-/* Handle the R_PPC_ADDR16_HA reloc.  */
+/* Handle the R_PPC_ADDR16_HA and R_PPC_REL16_HA relocs.  */
 
 static bfd_reloc_status_type
 ppc_elf_addr16_ha_reloc (bfd *abfd ATTRIBUTE_UNUSED,
@@ -1470,6 +1549,8 @@ ppc_elf_addr16_ha_reloc (bfd *abfd ATTRI
   relocation += symbol->section->output_section->vma;
   relocation += symbol->section->output_offset;
   relocation += reloc_entry->addend;
+  if (reloc_entry->howto->pc_relative)
+    relocation -= reloc_entry->address;
 
   reloc_entry->addend += (relocation & 0x8000) << 1;
 
@@ -2164,6 +2245,7 @@ struct ppc_elf_link_hash_table
   /* Short-cuts to get to dynamic linker sections.  */
   asection *got;
   asection *relgot;
+  asection *glink;
   asection *plt;
   asection *relplt;
   asection *dynbss;
@@ -2182,11 +2264,18 @@ struct ppc_elf_link_hash_table
     bfd_vma offset;
   } tlsld_got;
 
+  /* Offset of PltResolve function in glink.  */
+  bfd_vma glink_pltresolve;
+
   /* Size of reserved GOT entries.  */
   unsigned int got_header_size;
   /* Non-zero if allocating the header left a gap.  */
   unsigned int got_gap;
 
+  /* Whether to use new plt/got layout or not.  */
+  unsigned int new_plt:1;
+  unsigned int old_plt:1;
+
   /* Small local sym to section mapping cache.  */
   struct sym_sec_cache sym_sec;
 };
@@ -2306,8 +2395,14 @@ ppc_elf_create_dynamic_sections (bfd *ab
   if (!_bfd_elf_create_dynamic_sections (abfd, info))
     return FALSE;
 
-  flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY
-	   | SEC_LINKER_CREATED);
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_HAS_CONTENTS
+	   | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+
+  s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags | SEC_CODE);
+  htab->glink = s;
+  if (s == NULL
+      || !bfd_set_section_alignment (abfd, s, 4))
+    return FALSE;
 
   htab->dynbss = bfd_get_section_by_name (abfd, ".dynbss");
   s = bfd_make_section_with_flags (abfd, ".dynsbss",
@@ -2319,8 +2414,7 @@ ppc_elf_create_dynamic_sections (bfd *ab
   if (! info->shared)
     {
       htab->relbss = bfd_get_section_by_name (abfd, ".rela.bss");
-      s = bfd_make_section_with_flags (abfd, ".rela.sbss",
-				       flags | SEC_READONLY);
+      s = bfd_make_section_with_flags (abfd, ".rela.sbss", flags);
       htab->relsbss = s;
       if (s == NULL
 	  || ! bfd_set_section_alignment (abfd, s, 2))
@@ -2845,6 +2939,13 @@ ppc_elf_check_relocs (bfd *abfd,
 	case R_PPC_TOC16:
 	  break;
 
+	case R_PPC_REL16:
+	case R_PPC_REL16_LO:
+	case R_PPC_REL16_HI:
+	case R_PPC_REL16_HA:
+	  htab->new_plt = 1;
+	  break;
+
 	  /* This are just markers.  */
 	case R_PPC_TLS:
 	case R_PPC_EMB_MRKREF:
@@ -2870,6 +2971,8 @@ ppc_elf_check_relocs (bfd *abfd,
 
 	  /* This refers only to functions defined in the shared library.  */
 	case R_PPC_LOCAL24PC:
+	  if (h && h == htab->elf.hgot)
+	    htab->old_plt = 1;
 	  break;
 
 	  /* This relocation describes the C++ object vtable hierarchy.
@@ -2913,8 +3016,13 @@ ppc_elf_check_relocs (bfd *abfd,
 	case R_PPC_REL14_BRTAKEN:
 	case R_PPC_REL14_BRNTAKEN:
 	case R_PPC_REL32:
-	  if (h == NULL || h == htab->elf.hgot)
+	  if (h == NULL)
 	    break;
+	  if (h == htab->elf.hgot)
+	    {
+	      htab->old_plt = 1;
+	      break;
+	    }
 	  /* fall through */
 
 	case R_PPC_ADDR32:
@@ -3157,6 +3265,43 @@ ppc_elf_merge_private_bfd_data (bfd *ibf
   return TRUE;
 }
 \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,
+			   int force_old_plt)
+{
+  struct ppc_elf_link_hash_table *htab;
+
+  htab = ppc_elf_hash_table (info);
+  if (force_old_plt || !htab->new_plt)
+    htab->old_plt = 1;
+
+  if (!htab->old_plt)
+    {
+      /* The new PLT is a loaded section.  Fix its flags.  */
+      if (htab->plt != NULL)
+	{
+	  flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+			    | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+
+	  if (!bfd_set_section_flags (htab->elf.dynobj, htab->plt, flags))
+	    return -1;
+	}
+    }
+  else
+    {
+      /* Stop an unused .glink section from affecting .text alignment.  */
+      if (htab->glink != NULL)
+	{
+	  if (!bfd_set_section_alignment (htab->elf.dynobj, htab->glink, 0))
+	    return -1;
+	}
+    }
+  return !htab->old_plt;
+}
+\f
 /* Return the section that should be marked against GC for a given
    relocation.  */
 
@@ -3334,9 +3479,16 @@ ppc_elf_tls_setup (bfd *obfd, struct bfd
   struct ppc_elf_link_hash_table *htab;
 
   htab = ppc_elf_hash_table (info);
+  if (!htab->old_plt
+      && htab->plt != NULL
+      && htab->plt->output_section != NULL)
+    {
+      elf_section_type (htab->plt->output_section) = SHT_PROGBITS;
+      elf_section_flags (htab->plt->output_section) = SHF_ALLOC + SHF_WRITE;
+    }
+
   htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
 					     FALSE, FALSE, TRUE);
-
   return _bfd_elf_tls_setup (obfd, info);
 }
 
@@ -3706,7 +3858,10 @@ static bfd_vma
 allocate_got (struct ppc_elf_link_hash_table *htab, unsigned int need)
 {
   bfd_vma where;
-  unsigned int max_before_header = 32764;
+  unsigned int max_before_header = 32768;
+
+  if (htab->old_plt)
+    max_before_header = 32764;
 
   if (need <= htab->got_gap)
     {
@@ -3763,37 +3918,54 @@ allocate_dynrelocs (struct elf_link_hash
 	{
 	  asection *s = htab->plt;
 
-	  /* 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)
+	  if (!htab->old_plt)
 	    {
-	      h->root.u.def.section = s;
-	      h->root.u.def.value = h->plt.offset;
+	      h->plt.offset = s->size;
+	      s->size += 4;
+
+	      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;
+		}
 
-	  /* 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;
+	      /* 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;
+	    }
 
 	  /* We also need to make an entry in the .rela.plt section.  */
 	  htab->relplt->size += sizeof (Elf32_External_Rela);
@@ -4010,7 +4182,10 @@ ppc_elf_size_dynamic_sections (bfd *outp
 	}
     }
 
-  htab->got_header_size = 16;
+  if (htab->old_plt)
+    htab->got_header_size = 16;
+  else
+    htab->got_header_size = 12;
 
   /* Set up .got offsets for local syms, and space for local dynamic
      relocs.  */
@@ -4118,11 +4293,18 @@ ppc_elf_size_dynamic_sections (bfd *outp
 	  g_o_t = htab->got->size;
 	  htab->got->size += htab->got_header_size;
 	}
-      g_o_t += 4;
+      if (htab->old_plt)
+	g_o_t += 4;
 
       htab->elf.hgot->root.u.def.value = g_o_t;
     }
 
+  if (htab->glink != NULL && htab->glink->size != 0)
+    {
+      htab->glink_pltresolve = htab->glink->size;
+      htab->glink->size += GLINK_PLTRESOLVE;
+    }
+
   /* We've now determined the sizes of the various dynamic sections.
      Allocate memory for them.  */
   relocs = FALSE;
@@ -4132,6 +4314,7 @@ ppc_elf_size_dynamic_sections (bfd *outp
 	continue;
 
       if (s == htab->plt
+	  || s == htab->glink
 	  || s == htab->got
 	  || s == htab->sbss)
 	{
@@ -4179,7 +4362,7 @@ ppc_elf_size_dynamic_sections (bfd *outp
 	  continue;
 	}
 
-      if (s == htab->sbss)
+      if ((s->flags & SEC_HAS_CONTENTS) == 0)
 	continue;
 
       /* Allocate memory for the section contents.  */
@@ -4213,6 +4396,12 @@ ppc_elf_size_dynamic_sections (bfd *outp
 	    return FALSE;
 	}
 
+      if (htab->glink != NULL && htab->glink->size != 0)
+	{
+	  if (!add_dynamic_entry (DT_PPC_GLINK, 0))
+	    return FALSE;
+	}
+
       if (relocs)
 	{
 	  if (!add_dynamic_entry (DT_RELA, 0)
@@ -4281,7 +4470,7 @@ ppc_elf_relax_section (bfd *abfd,
   Elf_Internal_Rela *irel, *irelend;
   struct one_fixup *fixups = NULL;
   bfd_boolean changed;
-  struct ppc_elf_link_hash_table *ppc_info;
+  struct ppc_elf_link_hash_table *htab;
   bfd_size_type trampoff;
 
   *again = FALSE;
@@ -4305,7 +4494,7 @@ ppc_elf_relax_section (bfd *abfd,
   if (internal_relocs == NULL)
     goto error_return;
 
-  ppc_info = ppc_elf_hash_table (link_info);
+  htab = ppc_elf_hash_table (link_info);
   irelend = internal_relocs + isec->reloc_count;
 
   for (irel = internal_relocs; irel < irelend; irel++)
@@ -4382,11 +4571,19 @@ ppc_elf_relax_section (bfd *abfd,
 	    h = (struct elf_link_hash_entry *) h->root.u.i.link;
 
 	  if (r_type == R_PPC_PLTREL24
-	      && ppc_info->plt != NULL
+	      && htab->plt != NULL
 	      && h->plt.offset != (bfd_vma) -1)
 	    {
-	      tsec = ppc_info->plt;
-	      toff = h->plt.offset;
+	      if (!htab->old_plt)
+		{
+		  tsec = htab->glink;
+		  toff = h->plt.offset * (GLINK_ENTRY_SIZE / 4);
+		}
+	      else
+		{
+		  tsec = htab->plt;
+		  toff = h->plt.offset;
+		}
 	    }
 	  else if (h->root.type == bfd_link_hash_defined
 		   || h->root.type == bfd_link_hash_defweak)
@@ -4483,7 +4680,8 @@ ppc_elf_relax_section (bfd *abfd,
 	  if (R_PPC_RELAX32_PLT - R_PPC_RELAX32
 	      != R_PPC_RELAX32PC_PLT - R_PPC_RELAX32PC)
 	    abort ();
-	  if (tsec == ppc_info->plt)
+	  if (tsec == htab->plt
+	      || tsec == htab->glink)
 	    stub_rtype += R_PPC_RELAX32_PLT - R_PPC_RELAX32;
 
 	  /* Hijack the old relocation.  Since we need two
@@ -5426,6 +5624,12 @@ ppc_elf_relocate_section (bfd *output_bf
 	  addend = 0;
 	  goto dodyn;
 
+	case R_PPC_REL16:
+	case R_PPC_REL16_LO:
+	case R_PPC_REL16_HI:
+	case R_PPC_REL16_HA:
+	  break;
+
 	case R_PPC_REL24:
 	case R_PPC_REL32:
 	case R_PPC_REL14:
@@ -5595,9 +5799,14 @@ ppc_elf_relocate_section (bfd *output_bf
 		      && h->plt.offset != (bfd_vma) -1
 		      && htab->plt != NULL);
 
-	  relocation = (htab->plt->output_section->vma
-			+ htab->plt->output_offset
-			+ h->plt.offset);
+	  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);
 	  if (r_type == R_PPC_RELAX32_PLT)
 	    goto relax32;
 	  /* Fall thru */
@@ -5676,9 +5885,14 @@ ppc_elf_relocate_section (bfd *output_bf
 	    }
 
 	  unresolved_reloc = FALSE;
-	  relocation = (htab->plt->output_section->vma
-			+ htab->plt->output_offset
-			+ h->plt.offset);
+	  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);
 	  break;
 
 	  /* Relocate against _SDA_BASE_.  */
@@ -5837,6 +6051,7 @@ ppc_elf_relocate_section (bfd *output_bf
 	  break;
 
 	case R_PPC_ADDR16_HA:
+	case R_PPC_REL16_HA:
 	case R_PPC_GOT16_HA:
 	case R_PPC_PLT16_HA:
 	case R_PPC_SECTOFF_HA:
@@ -5974,8 +6189,18 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
       BFD_ASSERT (h->dynindx != -1);
       BFD_ASSERT (htab->plt != NULL && htab->relplt != NULL);
 
-      /* We don't need to fill in the .plt.  The ppc dynamic linker
-	 will fill it in.  */
+      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
+			 + htab->glink->output_section->vma
+			 + htab->glink->output_offset);
+	  bfd_put_32 (output_bfd, val, htab->plt->contents + h->plt.offset);
+	}
 
       /* Fill in the entry in the .rela.plt section.  */
       rela.r_offset = (htab->plt->output_section->vma
@@ -5984,9 +6209,15 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
       rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
       rela.r_addend = 0;
 
-      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;
+      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);
@@ -6112,6 +6343,12 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 	      dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
 	      break;
 
+	    case DT_PPC_GLINK:
+	      s = htab->glink;
+	      dyn.d_un.d_ptr = (htab->glink_pltresolve
+				+ s->output_section->vma + s->output_offset);
+	      break;
+
 	    default:
 	      continue;
 	    }
@@ -6128,7 +6365,8 @@ ppc_elf_finish_dynamic_sections (bfd *ou
       bfd_vma val;
 
       p += elf_hash_table (info)->hgot->root.u.def.value;
-      bfd_put_32 (output_bfd, 0x4e800021 /* blrl */, p - 4);
+      if (htab->old_plt)
+	bfd_put_32 (output_bfd, 0x4e800021 /* blrl */, p - 4);
 
       val = 0;
       if (sdyn != NULL)
@@ -6138,6 +6376,67 @@ ppc_elf_finish_dynamic_sections (bfd *ou
       elf_section_data (htab->got->output_section)->this_hdr.sh_entsize = 4;
     }
 
+  if (htab->glink != NULL && htab->glink->contents != NULL)
+    {
+      unsigned char *p;
+      unsigned char *endp;
+      bfd_vma pltgot;
+      unsigned int i;
+      static const unsigned int plt_resolve[] =
+	{
+	  SUB_11_11_30,
+	  ADD_0_11_11,
+	  ADD_11_0_11,
+	  LWZ_0_4_30,
+	  MTCTR_0,
+	  LWZ_12_8_30,
+	  BCTR,
+	  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
+		- 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);
+
+      p = htab->glink->contents;
+      p += htab->glink_pltresolve;
+      bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (-pltgot), p);
+      p += 4;
+      bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (-pltgot), p);
+      p += 4;
+
+      for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
+	{
+	  bfd_put_32 (output_bfd, plt_resolve[i], p);
+	  p += 4;
+	}
+      if (ARRAY_SIZE (plt_resolve) + 2 != GLINK_PLTRESOLVE / 4)
+	abort ();
+
+      p = htab->glink->contents;
+      endp = p + htab->glink_pltresolve;
+      while (p < endp)
+	{
+	  bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (pltgot), p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, LWZU_0_X_11 + PPC_LO (pltgot), p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, MTCTR_0, p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, BCTR, p);
+	  p += 4;
+	  pltgot += 4;
+	}
+    }
+
   return TRUE;
 }
 \f
Index: bfd/elf32-ppc.h
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.h,v
retrieving revision 1.6
diff -u -p -r1.6 elf32-ppc.h
--- bfd/elf32-ppc.h	4 May 2005 15:53:19 -0000	1.6
+++ bfd/elf32-ppc.h	11 May 2005 08:57:36 -0000
@@ -17,6 +17,7 @@ You should have received a copy of the G
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
 
+int ppc_elf_select_plt_layout (bfd *, struct bfd_link_info *, int);
 asection *ppc_elf_tls_setup (bfd *, struct bfd_link_info *);
 bfd_boolean ppc_elf_tls_optimize (bfd *, struct bfd_link_info *);
 bfd_boolean ppc_elf_set_sdata_syms (bfd *, struct bfd_link_info *);
Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.288
diff -u -p -r1.288 readelf.c
--- binutils/readelf.c	8 May 2005 14:17:39 -0000	1.288
+++ binutils/readelf.c	11 May 2005 08:57:48 -0000
@@ -1413,6 +1413,17 @@ get_sparc64_dynamic_type (unsigned long 
 }
 
 static const char *
+get_ppc_dynamic_type (unsigned long type)
+{
+  switch (type)
+    {
+    case DT_PPC_GLINK: return "PPC_GLINK";
+    default:
+      return NULL;
+    }
+}
+
+static const char *
 get_ppc64_dynamic_type (unsigned long type)
 {
   switch (type)
@@ -1552,6 +1563,9 @@ get_dynamic_type (unsigned long type)
 	    case EM_SPARCV9:
 	      result = get_sparc64_dynamic_type (type);
 	      break;
+	    case EM_PPC:
+	      result = get_ppc_dynamic_type (type);
+	      break;
 	    case EM_PPC64:
 	      result = get_ppc64_dynamic_type (type);
 	      break;
Index: gas/config/tc-ppc.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-ppc.c,v
retrieving revision 1.96
diff -u -p -r1.96 tc-ppc.c
--- gas/config/tc-ppc.c	5 May 2005 09:13:02 -0000	1.96
+++ gas/config/tc-ppc.c	11 May 2005 08:58:14 -0000
@@ -5707,8 +5719,6 @@ md_apply_fix3 (fixP, valP, seg)
 			      value, 8);
 	  break;
 
-	case BFD_RELOC_LO16:
-	case BFD_RELOC_16:
 	case BFD_RELOC_GPREL16:
 	case BFD_RELOC_16_GOT_PCREL:
 	case BFD_RELOC_16_GOTOFF:
@@ -5754,19 +5764,45 @@ md_apply_fix3 (fixP, valP, seg)
 			      value, 2);
 	  break;
 
+	case BFD_RELOC_16:
+	  if (fixP->fx_pcrel)
+	    fixP->fx_r_type = BFD_RELOC_16_PCREL;
+	  /* fall through */
+
+	case BFD_RELOC_16_PCREL:
+	  md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+			      value, 2);
+	  break;
+
+	case BFD_RELOC_LO16:
+	  if (fixP->fx_pcrel)
+	    fixP->fx_r_type = BFD_RELOC_LO16_PCREL;
+	  /* fall through */
+
+	case BFD_RELOC_LO16_PCREL:
+	  md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
+			      value, 2);
+	  break;
+
 	  /* This case happens when you write, for example,
 	     lis %r3,(L1-L2)@ha
 	     where L1 and L2 are defined later.  */
 	case BFD_RELOC_HI16:
 	  if (fixP->fx_pcrel)
-	    abort ();
+	    fixP->fx_r_type = BFD_RELOC_HI16_PCREL;
+	  /* fall through */
+
+	case BFD_RELOC_HI16_PCREL:
 	  md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
 			      PPC_HI (value), 2);
 	  break;
 
 	case BFD_RELOC_HI16_S:
 	  if (fixP->fx_pcrel)
-	    abort ();
+	    fixP->fx_r_type = BFD_RELOC_HI16_S_PCREL;
+	  /* fall through */
+
+	case BFD_RELOC_HI16_S_PCREL:
 	  md_number_to_chars (fixP->fx_frag->fr_literal + fixP->fx_where,
 			      PPC_HA (value), 2);
 	  break;
Index: include/elf/ppc.h
===================================================================
RCS file: /cvs/src/src/include/elf/ppc.h,v
retrieving revision 1.16
diff -u -p -r1.16 ppc.h
--- include/elf/ppc.h	10 May 2005 10:21:10 -0000	1.16
+++ include/elf/ppc.h	11 May 2005 08:58:25 -0000
@@ -120,12 +120,17 @@ START_RELOC_NUMBERS (elf_ppc_reloc_type)
   RELOC_NUMBER (R_PPC_EMB_BIT_FLD,	115)
   RELOC_NUMBER (R_PPC_EMB_RELSDA,	116)
 
-/* Fake relocations for branch stubs. This will keep them
-   together.  */
-#define R_PPC_RELAX32 249
-#define R_PPC_RELAX32PC 250
-#define R_PPC_RELAX32_PLT 251
-#define R_PPC_RELAX32PC_PLT 252
+/* Fake relocations for branch stubs, only used internally by ld.  */
+#define R_PPC_RELAX32 245
+#define R_PPC_RELAX32PC 246
+#define R_PPC_RELAX32_PLT 247
+#define R_PPC_RELAX32PC_PLT 248
+
+/* These are GNU extensions used in PIC code sequences.  */
+  RELOC_NUMBER (R_PPC_REL16,		249)
+  RELOC_NUMBER (R_PPC_REL16_LO,		250)
+  RELOC_NUMBER (R_PPC_REL16_HI,		251)
+  RELOC_NUMBER (R_PPC_REL16_HA,		252)
 
 /* These are GNU extensions to enable C++ vtable garbage collection.  */
   RELOC_NUMBER (R_PPC_GNU_VTINHERIT,	253)
@@ -140,6 +145,9 @@ END_RELOC_NUMBERS (R_PPC_max)
 #define IS_PPC_TLS_RELOC(R) \
   ((R) >= R_PPC_TLS && (R) <= R_PPC_GOT_DTPREL16_HA)
 
+/* Specify the start of the .glink section.  */
+#define DT_PPC_GLINK		DT_LOPROC
+
 /* Processor specific flags for the ELF header e_flags field.  */
 
 #define	EF_PPC_EMB		0x80000000	/* PowerPC embedded flag.  */
Index: ld/ldgram.y
===================================================================
RCS file: /cvs/src/src/ld/ldgram.y,v
retrieving revision 1.40
diff -u -p -r1.40 ldgram.y
--- ld/ldgram.y	28 Apr 2005 23:54:32 -0000	1.40
+++ ld/ldgram.y	11 May 2005 08:58:27 -0000
@@ -151,7 +151,7 @@ static int error_index;
 %token INPUT_SCRIPT INPUT_MRI_SCRIPT INPUT_DEFSYM CASE EXTERN START
 %token <name> VERS_TAG VERS_IDENTIFIER
 %token GLOBAL LOCAL VERSIONK INPUT_VERSION_SCRIPT
-%token KEEP ONLY_IF_RO ONLY_IF_RW
+%token KEEP ONLY_IF_RO ONLY_IF_RW SPECIAL
 %token EXCLUDE_FILE
 %type <versyms> vers_defns
 %type <versnode> vers_tag
@@ -899,6 +899,7 @@ opt_subalign:
 sect_constraint:
 		ONLY_IF_RO { $$ = ONLY_IF_RO; }
 	|	ONLY_IF_RW { $$ = ONLY_IF_RW; }
+	|	SPECIAL { $$ = SPECIAL; }
 	|	{ $$ = 0; }
 	;
 
Index: ld/ldlang.c
===================================================================
RCS file: /cvs/src/src/ld/ldlang.c,v
retrieving revision 1.180
diff -u -p -r1.180 ldlang.c
--- ld/ldlang.c	4 May 2005 11:00:26 -0000	1.180
+++ ld/ldlang.c	11 May 2005 08:58:30 -0000
@@ -991,7 +991,9 @@ lang_output_section_find_1 (const char *
     {
       if (strcmp (name, lookup->name) == 0
 	  && lookup->constraint != -1
-	  && (constraint == 0 || constraint == lookup->constraint))
+	  && (constraint == 0
+	      || (constraint == lookup->constraint
+		  && constraint != SPECIAL)))
 	return lookup;
     }
   return NULL;
@@ -2951,7 +2953,8 @@ map_input_to_output_sections
 	case lang_output_section_statement_enum:
 	  if (s->output_section_statement.constraint)
 	    {
-	      if (s->output_section_statement.constraint == -1)
+	      if (s->output_section_statement.constraint != ONLY_IF_RW
+		  && s->output_section_statement.constraint != ONLY_IF_RO)
 		break;
 	      s->output_section_statement.all_input_readonly = TRUE;
 	      check_input_sections (s->output_section_statement.children.head,
Index: ld/ldlex.l
===================================================================
RCS file: /cvs/src/src/ld/ldlex.l,v
retrieving revision 1.30
diff -u -p -r1.30 ldlex.l
--- ld/ldlex.l	15 Feb 2005 14:36:19 -0000	1.30
+++ ld/ldlex.l	11 May 2005 08:58:31 -0000
@@ -303,6 +303,7 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([
 <EXPRESSION,BOTH,SCRIPT>"OVERLAY"	{ RTOKEN(OVERLAY);}
 <EXPRESSION,BOTH,SCRIPT>"ONLY_IF_RO"	{ RTOKEN(ONLY_IF_RO); }
 <EXPRESSION,BOTH,SCRIPT>"ONLY_IF_RW"	{ RTOKEN(ONLY_IF_RW); }
+<EXPRESSION,BOTH,SCRIPT>"SPECIAL"	{ RTOKEN(SPECIAL); }
 <BOTH,SCRIPT>"o"			{ RTOKEN(ORIGIN);}
 <BOTH,SCRIPT>"org"			{ RTOKEN(ORIGIN);}
 <BOTH,SCRIPT>"l"			{ RTOKEN( LENGTH);}
Index: ld/emulparams/elf32ppc.sh
===================================================================
RCS file: /cvs/src/src/ld/emulparams/elf32ppc.sh,v
retrieving revision 1.14
diff -u -p -r1.14 elf32ppc.sh
--- ld/emulparams/elf32ppc.sh	11 May 2004 17:08:33 -0000	1.14
+++ ld/emulparams/elf32ppc.sh	11 May 2005 08:58:31 -0000
@@ -12,7 +12,16 @@ MAXPAGESIZE=0x10000
 COMMONPAGESIZE=0x1000
 ARCH=powerpc:common
 MACHINE=
+# Yes, we want duplicate .got and .plt sections.  The linker chooses the
+# appropriate one magically in ppc_after_open
+DATA_GOT=
+SDATA_GOT=
+SEPARATE_GOTPLT=0
 BSS_PLT=
+GOT=".got          ${RELOCATING-0} : SPECIAL { *(.got) }"
+PLT=".plt          ${RELOCATING-0} : SPECIAL { *(.plt) }"
+GOTPLT="${PLT}"
+OTHER_TEXT_SECTIONS="*(.glink)"
 EXECUTABLE_SYMBOLS='PROVIDE (__stack = 0); PROVIDE (___stack = 0);'
 OTHER_BSS_END_SYMBOLS='__end = .;'
 OTHER_RELRO_SECTIONS="
Index: ld/emultempl/ppc32elf.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/ppc32elf.em,v
retrieving revision 1.4
diff -u -p -r1.4 ppc32elf.em
--- ld/emultempl/ppc32elf.em	21 Mar 2005 13:23:15 -0000	1.4
+++ ld/emultempl/ppc32elf.em	11 May 2005 08:58:31 -0000
@@ -32,6 +32,66 @@ extern const bfd_target bfd_elf32_powerp
 /* Whether to run tls optimization.  */
 static int notlsopt = 0;
 
+/* Chooses the correct place for .plt and .got.  */
+static int old_plt = 0;
+static int old_got = 0;
+
+static void
+ppc_after_open (void)
+{
+  if (link_info.hash->creator == &bfd_elf32_powerpc_vec
+      || link_info.hash->creator == &bfd_elf32_powerpcle_vec)
+    {
+      int new_plt;
+      int keep_new;
+      unsigned int num_plt;
+      unsigned int num_got;
+      lang_output_section_statement_type *os;
+      lang_output_section_statement_type *plt_os[2];
+      lang_output_section_statement_type *got_os[2];
+
+      new_plt = ppc_elf_select_plt_layout (output_bfd, &link_info, old_plt);
+      if (new_plt < 0)
+	einfo ("%X%P: select_plt_layout problem %E\n");
+
+      num_got = 0;
+      num_plt = 0;
+      for (os = &lang_output_section_statement.head->output_section_statement;
+	   os != NULL;
+	   os = os->next)
+	{
+	  if (os->constraint == SPECIAL && strcmp (os->name, ".plt") == 0)
+	    {
+	      if (num_plt < 2)
+		plt_os[num_plt] = os;
+	      ++num_plt;
+	    }
+	  if (os->constraint == SPECIAL && strcmp (os->name, ".got") == 0)
+	    {
+	      if (num_got < 2)
+		got_os[num_got] = os;
+	      ++num_got;
+	    }
+	}
+
+      keep_new = new_plt == 1 ? 0 : -1;
+      if (num_plt == 2)
+	{
+	  plt_os[0]->constraint = keep_new;
+	  plt_os[1]->constraint = ~keep_new;
+	}
+      if (num_got == 2)
+	{
+	  if (old_got)
+	    keep_new = -1;
+	  got_os[0]->constraint = keep_new;
+	  got_os[1]->constraint = ~keep_new;
+	}
+    }
+
+  gld${EMULATION_NAME}_after_open ();
+}
+
 static void
 ppc_before_allocation (void)
 {
@@ -68,15 +128,21 @@ EOF
 #
 PARSE_AND_LIST_PROLOGUE='
 #define OPTION_NO_TLS_OPT		301
+#define OPTION_OLD_PLT			302
+#define OPTION_OLD_GOT			303
 '
 
 PARSE_AND_LIST_LONGOPTS='
   { "no-tls-optimize", no_argument, NULL, OPTION_NO_TLS_OPT },
+  { "bss-plt", no_argument, NULL, OPTION_OLD_PLT },
+  { "sdata-got", no_argument, NULL, OPTION_OLD_GOT },
 '
 
 PARSE_AND_LIST_OPTIONS='
   fprintf (file, _("\
-  --no-tls-optimize     Don'\''t try to optimize TLS accesses.\n"
+  --no-tls-optimize     Don'\''t try to optimize TLS accesses.\n\
+  --bss-plt             Force old-style BSS PLT.\n\
+  --sdata-got           Force GOT location just before .sdata.\n"
 		   ));
 '
 
@@ -84,9 +150,18 @@ PARSE_AND_LIST_ARGS_CASES='
     case OPTION_NO_TLS_OPT:
       notlsopt = 1;
       break;
+
+    case OPTION_OLD_PLT:
+      old_plt = 1;
+      break;
+
+    case OPTION_OLD_GOT:
+      old_got = 1;
+      break;
 '
 
 # Put these extra ppc32elf routines in ld_${EMULATION_NAME}_emulation
 #
+LDEMUL_AFTER_OPEN=ppc_after_open
 LDEMUL_BEFORE_ALLOCATION=ppc_before_allocation
 LDEMUL_AFTER_ALLOCATION=gld${EMULATION_NAME}_after_allocation
Index: ld/scripttempl/elf.sc
===================================================================
RCS file: /cvs/src/src/ld/scripttempl/elf.sc,v
retrieving revision 1.56
diff -u -p -r1.56 elf.sc
--- ld/scripttempl/elf.sc	10 May 2005 02:27:38 -0000	1.56
+++ ld/scripttempl/elf.sc	11 May 2005 08:58:32 -0000
@@ -101,7 +101,9 @@ if test -n "${COMMONPAGESIZE}"; then
   DATA_SEGMENT_RELRO_END=". = DATA_SEGMENT_RELRO_END (${SEPARATE_GOTPLT-0}, .);"
 fi
 INTERP=".interp       ${RELOCATING-0} : { *(.interp) }"
-PLT=".plt          ${RELOCATING-0} : { *(.plt) }"
+if test -z "$PLT"; then
+  PLT=".plt          ${RELOCATING-0} : { *(.plt) }"
+fi
 if test -z "$GOT"; then
   if test -z "$SEPARATE_GOTPLT"; then
     GOT=".got          ${RELOCATING-0} : { *(.got.plt) *(.got) }"
@@ -144,6 +146,16 @@ if test -z "${NO_SMALL_DATA}"; then
 else
   NO_SMALL_DATA=" "
 fi
+if test -z "${DATA_GOT}"; then
+  if test -n "${NO_SMALL_DATA}"; then
+    DATA_GOT=" "
+  fi
+fi
+if test -z "${SDATA_GOT}"; then
+  if test -z "${NO_SMALL_DATA}"; then
+    SDATA_GOT=" "
+  fi
+fi
 test -n "$SEPARATE_GOTPLT" && SEPARATE_GOTPLT=" "
 CTOR=".ctors        ${CONSTRUCTING-0} : 
   {
@@ -343,11 +355,12 @@ cat <<EOF
   ${RELOCATING+${DATARELRO}}
   ${OTHER_RELRO_SECTIONS}
   ${TEXT_DYNAMIC-${DYNAMIC}}
-  ${NO_SMALL_DATA+${RELRO_NOW+${GOT}}}
-  ${NO_SMALL_DATA+${RELRO_NOW-${SEPARATE_GOTPLT+${GOT}}}}
+  ${DATA_GOT+${RELRO_NOW+${GOT}}}
+  ${DATA_GOT+${RELRO_NOW+${GOTPLT}}}
+  ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT+${GOT}}}}
   ${RELOCATING+${DATA_SEGMENT_RELRO_END}}
-  ${NO_SMALL_DATA+${RELRO_NOW-${SEPARATE_GOTPLT+${GOTPLT}}}}
-  ${NO_SMALL_DATA+${RELRO_NOW-${SEPARATE_GOTPLT-${GOT}}}}
+  ${DATA_GOT+${RELRO_NOW-${SEPARATE_GOTPLT-${GOT}}}}
+  ${DATA_GOT+${RELRO_NOW-${GOTPLT}}}
 
   ${DATA_PLT+${PLT_BEFORE_GOT-${PLT}}}
 
@@ -364,9 +377,9 @@ cat <<EOF
   ${SMALL_DATA_CTOR+${RELOCATING+${CTOR}}}
   ${SMALL_DATA_DTOR+${RELOCATING+${DTOR}}}
   ${DATA_PLT+${PLT_BEFORE_GOT+${PLT}}}
-  ${RELOCATING+${OTHER_GOT_SYMBOLS}}
-  ${NO_SMALL_DATA-${GOT}}
-  ${OTHER_GOT_SECTIONS}
+  ${SDATA_GOT+${RELOCATING+${OTHER_GOT_SYMBOLS}}}
+  ${SDATA_GOT+${GOT}}
+  ${SDATA_GOT+${OTHER_GOT_SECTIONS}}
   ${SDATA}
   ${OTHER_SDATA_SECTIONS}
   ${RELOCATING+${DATA_END_SYMBOLS-_edata = .; PROVIDE (edata = .);}}

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

[-- Attachment #2: ppc32abi-2 --]
[-- Type: text/plain, Size: 4202 bytes --]

This is the minimum set of required changes to support a non-executable
GOT and PLT segment and SELinux restrictions on never allowing writable
areas to become executable nor executable areas to become writable.  It
leaves us backward compatible with old shared libraries.

o GOT needs to be made non-exec.  A non-exec GOT means we can't execute
  the blrl insn in the GOT to load the GOT pointer register for -fpic
  code.  Instead of using "bl got-4; mflr 30", use
	bcl 20,31,1f
     1: mflr 30
	addis 30,30,(got-1b)@ha
	addi 30,30,(got-1b)@l
  This example assumes r30 is being used as the GOT pointer. "got" is
  shorthand for the value of the symbol _GLOBAL_OFFSET_TABLE_.

  This will require addition of R_PPC_REL16_HA and R_PPC_REL16_LO
  relocs, support for them in ld, and of course gcc changes to generate
  the new code to load the GOT register.  -fPIC GOT pointer code can
  also benefit from the new relocs.
  The .got section will be placed before .data so that it may be made
  read-only after relocatation.  This placement is incompatible with the
  use of the GOT pointer to address .sdata using short offsets in shared
  libraries, so this feature of the SYSV ABI is no longer supported.
  With the new GOT layout, three locations are reserved.  got[0] will be
  initialised to the link-time address of .dynamic by ld, got[1] will be
  initialised to the address of dl_runtime_resolve by ld.so, and got[2]
  will be initialised to the map address by ld.so.  got[-1] will no
  longer be reserved.

o PLT should be split into two sections, one containing code which we'll
  call .glink, the other containing just data, which we'll call .plt.
  .glink will be generated by ld somewhere in the text segment, and
  contains a special symbol resolver stub, and a number of PLT call code
  stubs.  The symbol resolver stub will call the dl_runtime_resolve
  function specified by got[1] with r11 set to the plt reloc offset, and
  r12 set to the value of got[2].  These stubs need not be adjacent to
  one another or unique, and can be scattered throughout the text
  segment so as to be reachable with a branch and link instruction.
  A possible implementation is:

  # Enter with .plt entry address in r11, got pointer in r30
  PLTresolve:
	addis 11,11,(got-plt)@ha
	addi 11,11,(got-plt)@l
	sub 11,11,30			# r11 = index * 4
	add 0,11,11
	add 11,0,11			# r11 = index * 12 = reloc offset.
	lwz 0,4(30)			# got[1] address of dl_runtime_resolve
	mtctr 0
	lwz 12,8(30)			# got[2] contains the map address
	bctr

  # ith PLT code stub.
	addis 11,30,(plt+(i-1)*4-got)@ha
	lwzu 0,(plt+(i-1)*4-got)@l(11)
	mtctr 0
	bctr

  .plt will be a loaded section located after .got, and consists of an
  array of addresses.  There will also be an array of R_PPC_JMP_SLOT
  relocs in .rela.plt, with a one-one correspondence between elements of
  each array.  Each R_PPC_JMP_SLOT reloc will have r_offset pointing at
  the .plt word it relocates.  To support lazy linking, ld will set each
  .plt word to point to the symbol resolver stub in .glink.  On loading
  a shared library, ld.so should relocate the contents of .plt by adding
  the load address to each word in .plt.
  Note that this ABI does not specify a fixed GOT register, or even one
  register used throughout a binary.  If a function uses a register
  other than r30 for the GOT register, it will be specified as
  r_addend = reg*4+2 on each R_PPC_PLTREL24 reloc used by calls.  This
  of course will require the linker to generate different .glink code
  than the example implementation given here.
  To allow ld.so to support old shared libs, we need to flag a new got
  and plt layout, so we'll define a new dynamic tag, DT_PPC_GLINK which
  will be set to the link-time address of PLTresolve.

  The PLT change requires support in ld and ld.so, and gcc changes to
  ensure that the got pointer register is set properly whenever a call
  via the PLT may be necessary.  The linker will detect the difference
  between old and new object files by looking at relocs.  A new object
  file will always have R_PPC_REL16* relocs if it uses the GOT or makes
  calls that might need to go via the plt.  An old file won't have these
  (new) relocs.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 14:22 powerpc new PLT and GOT Alan Modra
@ 2005-05-11 14:29 ` Daniel Jacobowitz
  2005-05-11 14:59   ` Alan Modra
  2005-05-11 14:45 ` Andreas Schwab
  2005-05-12  6:08 ` Richard Henderson
  2 siblings, 1 reply; 17+ messages in thread
From: Daniel Jacobowitz @ 2005-05-11 14:29 UTC (permalink / raw)
  To: binutils

On Wed, May 11, 2005 at 11:42:50PM +0930, Alan Modra wrote:
> The current PowerPC ABI requires PLT and GOT to be both writable and
> executable, which is a security concern.  It's worse than that actually,
> since the entire data segment ends up executable.
> 
> This patch implements binutils support for a new PLT and GOT on PowerPC,
> details of which are in the attached ABI proposal.  Note that the
> proposal is by no means final;  There's likely to be some change in
> .glink to do without the lwzu instructions, but I think it's solid
> enough to throw some code together.  Also, the ld support doesn't allow
> registers other than r30 as a GOT pointer.  We'll worry about that if
> and when gcc generates code using a different register.
> 
> From a user perspective, this ld change won't do anything until gcc
> starts generating code for the new PLT and GOT.  ie. ld will continue to
> link using the old PLT scheme.

I'm not oing to look at the code, but I have one comment on the ABI.
You've got a data section named .plt and a code section named .glink
(well, you note that the text stubs can be anywhere in the text
segment, but that's true on most architectures; I imagine that for now
they're all going in a single .glink section).

Everyone else seems to call the data section ".got.plt" and the text
section ".plt".  GDB even knows about this; it uses the name ".plt" to
help in skipping dynamic linker code.  Is there a reason why you can't
use the traditional names?

-- 
Daniel Jacobowitz
CodeSourcery, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 14:22 powerpc new PLT and GOT Alan Modra
  2005-05-11 14:29 ` Daniel Jacobowitz
@ 2005-05-11 14:45 ` Andreas Schwab
  2005-05-12  6:08 ` Richard Henderson
  2 siblings, 0 replies; 17+ messages in thread
From: Andreas Schwab @ 2005-05-11 14:45 UTC (permalink / raw)
  To: binutils

Alan Modra <amodra@bigpond.net.au> writes:

> o PLT should be split into two sections, one containing code which we'll
>   call .glink, the other containing just data, which we'll call .plt.
>   .glink will be generated by ld somewhere in the text segment, and
>   contains a special symbol resolver stub, and a number of PLT call code
>   stubs.

Would it make sense to name them .plt and .got.plt instead of .glink and
.plt?  That would make them closer to the traditional use on other
architectures.

Andreas.

-- 
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 14:29 ` Daniel Jacobowitz
@ 2005-05-11 14:59   ` Alan Modra
  2005-05-11 15:10     ` Andreas Schwab
  2005-05-11 15:16     ` Daniel Jacobowitz
  0 siblings, 2 replies; 17+ messages in thread
From: Alan Modra @ 2005-05-11 14:59 UTC (permalink / raw)
  To: binutils

On Wed, May 11, 2005 at 10:22:09AM -0400, Daniel Jacobowitz wrote:
> I'm not oing to look at the code, but I have one comment on the ABI.
> You've got a data section named .plt and a code section named .glink
> (well, you note that the text stubs can be anywhere in the text
> segment, but that's true on most architectures; I imagine that for now
> they're all going in a single .glink section).

Yes, they go in a single linker created .glink section, but this section
doesn't have it's own output section.  Instead, it contributes to .text.

> Everyone else seems to call the data section ".got.plt" and the text
> section ".plt".  GDB even knows about this; it uses the name ".plt" to
> help in skipping dynamic linker code.  Is there a reason why you can't
> use the traditional names?

I could, but .plt is traditionally a (somewhat) regular array.  .glink
doesn't really fit this model since it could be dispersed throughout the
text segment, something you might want to do in large programs where the
24-bit powerpc branch offset is limiting.  I know I don't support such
a .glink at the moment on powerpc (as we do on powerpc64), but I might
in the future.  In that case you couldn't output .glink to its own
output section, so gdb could not use the section name to skip plt call
stubs.

Then, given that .glink doesn't really fit the traditional .plt, I don't
want to use .got.plt because I feel some section ought to be called
.plt, simply because that's the traditional name for sections associated
with dynamic function linkage.  Powerpc64 also has a .plt that just
consists of data.

You're not the first to suggest .got.plt though.  :)

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 14:59   ` Alan Modra
@ 2005-05-11 15:10     ` Andreas Schwab
  2005-05-11 15:39       ` Alan Modra
  2005-05-11 15:16     ` Daniel Jacobowitz
  1 sibling, 1 reply; 17+ messages in thread
From: Andreas Schwab @ 2005-05-11 15:10 UTC (permalink / raw)
  To: binutils

Alan Modra <amodra@bigpond.net.au> writes:

> Then, given that .glink doesn't really fit the traditional .plt, I don't
> want to use .got.plt because I feel some section ought to be called
> .plt, simply because that's the traditional name for sections associated
> with dynamic function linkage.  Powerpc64 also has a .plt that just
> consists of data.

How about .got.glink then?  Calling a section that has GOT nature .plt can
be confusing.

Andreas.

-- 
Andreas Schwab, SuSE Labs, schwab@suse.de
SuSE Linux Products GmbH, Maxfeldstraße 5, 90409 Nürnberg, Germany
Key fingerprint = 58CA 54C7 6D53 942B 1756  01D3 44D5 214B 8276 4ED5
"And now for something completely different."

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 14:59   ` Alan Modra
  2005-05-11 15:10     ` Andreas Schwab
@ 2005-05-11 15:16     ` Daniel Jacobowitz
  2005-05-11 16:37       ` Richard Earnshaw
  1 sibling, 1 reply; 17+ messages in thread
From: Daniel Jacobowitz @ 2005-05-11 15:16 UTC (permalink / raw)
  To: binutils

On Thu, May 12, 2005 at 12:14:58AM +0930, Alan Modra wrote:
> I could, but .plt is traditionally a (somewhat) regular array.  .glink
> doesn't really fit this model since it could be dispersed throughout the
> text segment, something you might want to do in large programs where the
> 24-bit powerpc branch offset is limiting.  I know I don't support such

This is not exclusive to PowerPC.  For instance, the ARM PLT could be
distributed this way (and that would be a useful thing to support for
sufficiently large applications - but ARM doesn't have a ton of those).
And I've implemented irregularly sized entries in ARM's .plt.

> a .glink at the moment on powerpc (as we do on powerpc64), but I might
> in the future.  In that case you couldn't output .glink to its own
> output section, so gdb could not use the section name to skip plt call
> stubs.

Sure, it's not perfect - but it's more traditional to use the name when
it's available.  Once dispersed, of course, GDB needs another solution.

-- 
Daniel Jacobowitz
CodeSourcery, LLC

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 15:10     ` Andreas Schwab
@ 2005-05-11 15:39       ` Alan Modra
  0 siblings, 0 replies; 17+ messages in thread
From: Alan Modra @ 2005-05-11 15:39 UTC (permalink / raw)
  To: Andreas Schwab; +Cc: binutils

On Wed, May 11, 2005 at 04:59:26PM +0200, Andreas Schwab wrote:
> Alan Modra <amodra@bigpond.net.au> writes:
> 
> > Then, given that .glink doesn't really fit the traditional .plt, I don't
> > want to use .got.plt because I feel some section ought to be called
> > .plt, simply because that's the traditional name for sections associated
> > with dynamic function linkage.  Powerpc64 also has a .plt that just
> > consists of data.
> 
> How about .got.glink then?  Calling a section that has GOT nature .plt can
> be confusing.

I want to call _something_ .plt, that's my main reason for not using
.got.plt.  It's not that I dislike the name .got.plt.

The other reason for choosing .glink and .plt is that these are the
sections used by PowerPC64 Linux, with ppc64 .glink being a code section
and ppc64 .plt a (BSS) data section.

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 15:16     ` Daniel Jacobowitz
@ 2005-05-11 16:37       ` Richard Earnshaw
  0 siblings, 0 replies; 17+ messages in thread
From: Richard Earnshaw @ 2005-05-11 16:37 UTC (permalink / raw)
  To: Daniel Jacobowitz; +Cc: binutils

On Wed, 2005-05-11 at 16:10, Daniel Jacobowitz wrote:
> On Thu, May 12, 2005 at 12:14:58AM +0930, Alan Modra wrote:
> > I could, but .plt is traditionally a (somewhat) regular array.  .glink
> > doesn't really fit this model since it could be dispersed throughout the
> > text segment, something you might want to do in large programs where the
> > 24-bit powerpc branch offset is limiting.  I know I don't support such
> 
> This is not exclusive to PowerPC.  For instance, the ARM PLT could be
> distributed this way (and that would be a useful thing to support for
> sufficiently large applications - but ARM doesn't have a ton of those).
> And I've implemented irregularly sized entries in ARM's .plt.
> 

It's more of a problem in Thumb than in ARM.  In Thumb the addressing
range is limited to +/- 4M, which, given that the plt is always at one
end of the shared library (well, in the GNU linker, anyway) that
effectively limits your shared library size to 4Mb of text segment which
IS a significant limitation (libgcj, for example, is substantially
larger than that ;-).  It is possible (with a sufficiently smart linker)
to put trampolines in to enable you to span more address space than
that, but when the target is then a plt trampoline it would be much more
efficient to put in multiple plt fragments and then bind a call to the
nearest one.


R.

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-11 14:22 powerpc new PLT and GOT Alan Modra
  2005-05-11 14:29 ` Daniel Jacobowitz
  2005-05-11 14:45 ` Andreas Schwab
@ 2005-05-12  6:08 ` Richard Henderson
  2005-05-12  6:13   ` Alan Modra
  2 siblings, 1 reply; 17+ messages in thread
From: Richard Henderson @ 2005-05-12  6:08 UTC (permalink / raw)
  To: binutils

On Wed, May 11, 2005 at 11:42:50PM +0930, Alan Modra wrote:
>   Note that this ABI does not specify a fixed GOT register, or even one
>   register used throughout a binary.  If a function uses a register
>   other than r30 for the GOT register, it will be specified as
>   r_addend = reg*4+2 on each R_PPC_PLTREL24 reloc used by calls.  This
>   of course will require the linker to generate different .glink code
>   than the example implementation given here.
>   To allow ld.so to support old shared libs, we need to flag a new got
>   and plt layout, so we'll define a new dynamic tag, DT_PPC_GLINK which
>   will be set to the link-time address of PLTresolve.

If the program does in fact use different got registers, then there
will have to be more than one PLTresolve.  At which point it doesn't
make sense to have DT_PPC_GLINK pick out any one of them.  I suggest
simply setting this entry to 1, as with DT_TEXTREL.



r~

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-12  6:08 ` Richard Henderson
@ 2005-05-12  6:13   ` Alan Modra
  2005-05-12  7:47     ` Richard Henderson
  0 siblings, 1 reply; 17+ messages in thread
From: Alan Modra @ 2005-05-12  6:13 UTC (permalink / raw)
  To: Richard Henderson; +Cc: binutils

On Wed, May 11, 2005 at 10:37:47PM -0700, Richard Henderson wrote:
> If the program does in fact use different got registers, then there
> will have to be more than one PLTresolve.

Not if the plt call stubs are modified to copy their got pointer to,
say, r12.  Or better, if PLTresolve loads its own got pointer, like
this:

  PLTresolve:
	addis 11,11,(got-plt)@ha
	addi 11,11,(got-plt)@l
	mflr 0
	bcl 20,31,1f
     1: mflr 12
	addis 12,12,(got-1b)@ha
	addi 12,12,(got-1b)@l		# r12 = _GLOBAL_OFFSET_TABLE_
	mtlr 0
	sub 11,11,12			# r11 = index * 4
	add 0,11,11
	add 11,0,11			# r11 = index * 12 = reloc offset.
	lwz 0,4(12)			# got[1] address of dl_runtime_resolve
	mtctr 0
	lwz 12,8(12)			# got[2] contains the map address
	bctr

>  At which point it doesn't
> make sense to have DT_PPC_GLINK pick out any one of them.  I suggest
> simply setting this entry to 1, as with DT_TEXTREL.

I agree however that the dynamic linker doesn't need to know where
Pltresolve is, so any non-zero value in DT_PPC_GLINK will do.  (It did
need to know when I was still considering a BSS PLT.)

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-12  6:13   ` Alan Modra
@ 2005-05-12  7:47     ` Richard Henderson
  2005-05-12  9:02       ` Alan Modra
  0 siblings, 1 reply; 17+ messages in thread
From: Richard Henderson @ 2005-05-12  7:47 UTC (permalink / raw)
  To: binutils

On Thu, May 12, 2005 at 03:38:14PM +0930, Alan Modra wrote:
> Not if the plt call stubs are modified to copy their got pointer to,
> say, r12.  Or better, if PLTresolve loads its own got pointer, like
> this:

Or better, ensure that r0 (or r12 if r0 can't be used in the appropriate
addressing modes) still contains a copy of ctr, which means that it
contains a copy of PLTresolve.  Then use pc-relative references to your
got entries instead of got-relative.


r~

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-12  7:47     ` Richard Henderson
@ 2005-05-12  9:02       ` Alan Modra
  2005-05-12 16:10         ` Alan Modra
  2005-05-12 18:07         ` Richard Henderson
  0 siblings, 2 replies; 17+ messages in thread
From: Alan Modra @ 2005-05-12  9:02 UTC (permalink / raw)
  To: Richard Henderson; +Cc: binutils, Steve Munroe, Anton Blanchard, Paul Mackerras

On Wed, May 11, 2005 at 11:37:29PM -0700, Richard Henderson wrote:
> On Thu, May 12, 2005 at 03:38:14PM +0930, Alan Modra wrote:
> > Not if the plt call stubs are modified to copy their got pointer to,
> > say, r12.  Or better, if PLTresolve loads its own got pointer, like
> > this:
> 
> Or better, ensure that r0 (or r12 if r0 can't be used in the appropriate
> addressing modes) still contains a copy of ctr, which means that it
> contains a copy of PLTresolve.  Then use pc-relative references to your
> got entries instead of got-relative.

Yes, that would be even nicer.  Thanks for checking over the ABI
proposal.  Hmm, your idea about ctr triggered some more ideas..


The one thing that I'm a little unhappy about with the new plt call
scheme is that

  # ith PLT code stub.
	addis 11,30,(plt+(i-1)*4-got)@ha
	lwzu 0,(plt+(i-1)*4-got)@l(11)
	mtctr 0
	bctr

is slower than the old plt call scheme, which allowed ld.so to optimise
.plt to simple branches.  Steve Munroe improved it a little by
suggesting that when plt and got are close enough we could reduce it to

	lwz 0,(plt+(i-1)*4-got)(30)
	mtctr 0
	bctr

but that loses r11 as an index into the plt.  So each plt call stub
needs a different entry into PLTresolve in order to differentiate plt
entries.  Steve suggested that each entry would load r11, using
"li 11,(i-1)*4; b PLTresolve" as is done with the PowerPC64 .glink.
Combining Steve's idea with yours about ctr gets me to


# 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
# or, if plt+(i-1)*4-got is less than 32k
	lwz 11,(plt+(i-1)*4-got)(30)
	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:
	mflr 0
	bcl 20,31,1f
     1: mflr 12
	addis 11,11,(1b-res_0)@ha
	addi 11,11,(1b-res_0)@l
	sub 11,11,12			# r11 = index * 4
	addis 12,12,(got-1b)@ha
	addi 12,12,(got-1b)@l		# r12 = _GLOBAL_OFFSET_TABLE_
	mtlr 0
	add 0,11,11
	add 11,0,11			# r11 = index * 12 = reloc offset.
	lwz 0,4(12)			# got[1] address of dl_runtime_resolve
	mtctr 0
	lwz 12,8(12)			# got[2] contains the map address
	bctr


Of course, if we want to make the normal plt call path go fast, then the
thing to do is have gcc generate the plt call stubs so that they can be
scheduled.  So gcc generates

 addis 11,30,foo@gotplt@ha
 lwz 11,foo@gotplt@l(11)
 mtctr 11,foo@gotplt_marker
 bctr foo@gotplt_marker

hopefully with other instructions scheduled in the sequence.  The funny
looking gotplt_marker relocs are because ld might resolve "foo" to a
local function, and would then turn the sequence into

 nop
 nop
 nop
 bl foo


-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-12  9:02       ` Alan Modra
@ 2005-05-12 16:10         ` Alan Modra
  2005-05-12 18:07         ` Richard Henderson
  1 sibling, 0 replies; 17+ messages in thread
From: Alan Modra @ 2005-05-12 16:10 UTC (permalink / raw)
  To: binutils

A bug fix for new .got section flags, and the smaller plt call stubs
without a load-with-update instruction.

	* elf32-ppc.c (LWZU_0_X_11): Delete.
	(B, LWZ_11_X_11, LWZ_11_X_30, MTCTR_11): Define.
	(ppc_elf_select_plt_layout): Set .got flags too.  Formatting.
	(ppc_elf_size_dynamic_sections): Allocate space for .glink branch
	table.
	(ppc_elf_finish_dynamic_symbol): Point .plt entries into the branch
	table.
	(ppc_elf_finish_dynamic_sections): Adjust DT_PPC_GLINK value.
	Generate .glink branch table and updated stubs.

Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.156
diff -u -p -r1.156 elf32-ppc.c
--- bfd/elf32-ppc.c	11 May 2005 14:09:42 -0000	1.156
+++ bfd/elf32-ppc.c	12 May 2005 14:51:46 -0000
@@ -67,6 +67,7 @@ static bfd_reloc_status_type ppc_elf_unh
 
 /* Some instructions.  */
 #define NOP		0x60000000
+#define B		0x48000000
 #define ADDIS_11_11	0x3d6b0000
 #define ADDI_11_11	0x396b0000
 #define SUB_11_11_30	0x7d7e5850
@@ -77,7 +78,9 @@ static bfd_reloc_status_type ppc_elf_unh
 #define LWZ_12_8_30	0x819e0008
 #define BCTR		0x4e800420
 #define ADDIS_11_30	0x3d7e0000
-#define LWZU_0_X_11	0x840b0000
+#define LWZ_11_X_11	0x816b0000
+#define LWZ_11_X_30	0x817e0000
+#define MTCTR_11	0x7d6903a6
 
 /* Offset of tp and dtp pointers from start of TLS block.  */
 #define TP_OFFSET	0x7000
@@ -3280,24 +3283,25 @@ ppc_elf_select_plt_layout (bfd *output_b
 
   if (!htab->old_plt)
     {
-      /* The new PLT is a loaded section.  Fix its flags.  */
-      if (htab->plt != NULL)
-	{
-	  flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
-			    | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+      flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS
+			| SEC_IN_MEMORY | SEC_LINKER_CREATED);
 
-	  if (!bfd_set_section_flags (htab->elf.dynobj, htab->plt, flags))
-	    return -1;
-	}
+      /* The new PLT is a loaded section.  */
+      if (htab->plt != NULL
+	  && !bfd_set_section_flags (htab->elf.dynobj, htab->plt, flags))
+	return -1;
+
+      /* The new GOT is not executable.  */
+      if (htab->got != NULL
+	  && !bfd_set_section_flags (htab->elf.dynobj, htab->got, flags))
+	return -1;
     }
   else
     {
       /* Stop an unused .glink section from affecting .text alignment.  */
-      if (htab->glink != NULL)
-	{
-	  if (!bfd_set_section_alignment (htab->elf.dynobj, htab->glink, 0))
-	    return -1;
-	}
+      if (htab->glink != NULL
+	  && !bfd_set_section_alignment (htab->elf.dynobj, htab->glink, 0))
+	return -1;
     }
   return !htab->old_plt;
 }
@@ -4302,6 +4306,10 @@ ppc_elf_size_dynamic_sections (bfd *outp
   if (htab->glink != NULL && htab->glink->size != 0)
     {
       htab->glink_pltresolve = htab->glink->size;
+      /* 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 += GLINK_PLTRESOLVE;
     }
 
@@ -6197,6 +6205,7 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
       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);
@@ -6345,7 +6354,7 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 
 	    case DT_PPC_GLINK:
 	      s = htab->glink;
-	      dyn.d_un.d_ptr = (htab->glink_pltresolve
+	      dyn.d_un.d_ptr = (s->size - GLINK_PLTRESOLVE
 				+ s->output_section->vma + s->output_offset);
 	      break;
 
@@ -6380,7 +6389,7 @@ ppc_elf_finish_dynamic_sections (bfd *ou
     {
       unsigned char *p;
       unsigned char *endp;
-      bfd_vma pltgot;
+      bfd_vma got, pltgot;
       unsigned int i;
       static const unsigned int plt_resolve[] =
 	{
@@ -6400,17 +6409,67 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 #define PPC_HI(v) (((v) >> 16) & 0xffff)
 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
 
+      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);
+
       pltgot = (htab->plt->output_section->vma
 		+ htab->plt->output_offset
-		- 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);
+		- got);
 
+      /* Write the plt call stubs.  */
       p = htab->glink->contents;
-      p += htab->glink_pltresolve;
-      bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (-pltgot), p);
+      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;
+	}
+
+      /* Now build the branch table, one for each plt entry (less one),
+	 and perhaps some padding.  */
+      endp = htab->glink->contents;
+      endp += htab->glink->size - GLINK_PLTRESOLVE;
+      while (p < endp - 8 * 4)
+	{
+	  bfd_put_32 (output_bfd, B + endp - p, p);
+	  p += 4;
+	}
+      while (p < endp)
+	{
+	  bfd_put_32 (output_bfd, NOP, p);
+	  p += 4;
+	}
+
+      got -= (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 (-pltgot), p);
+      bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (got), p);
       p += 4;
 
       for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
@@ -6420,21 +6479,6 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 	}
       if (ARRAY_SIZE (plt_resolve) + 2 != GLINK_PLTRESOLVE / 4)
 	abort ();
-
-      p = htab->glink->contents;
-      endp = p + htab->glink_pltresolve;
-      while (p < endp)
-	{
-	  bfd_put_32 (output_bfd, ADDIS_11_30 + PPC_HA (pltgot), p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, LWZU_0_X_11 + PPC_LO (pltgot), p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, MTCTR_0, p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, BCTR, p);
-	  p += 4;
-	  pltgot += 4;
-	}
     }
 
   return TRUE;

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-12  9:02       ` Alan Modra
  2005-05-12 16:10         ` Alan Modra
@ 2005-05-12 18:07         ` Richard Henderson
  2005-05-14  5:57           ` Alan Modra
  1 sibling, 1 reply; 17+ messages in thread
From: Richard Henderson @ 2005-05-12 18:07 UTC (permalink / raw)
  To: binutils, Steve Munroe, Anton Blanchard, Paul Mackerras

On Thu, May 12, 2005 at 06:06:50PM +0930, Alan Modra wrote:
> # 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
> # or, if plt+(i-1)*4-got is less than 32k
> 	lwz 11,(plt+(i-1)*4-got)(30)
> 	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:

I'm not thrilled, but I suppose in the normal case we'll still be
consuming only 4 words per plt.  And update forms are slow, and to
be avoided if possible.

> 	addis 11,11,(1b-res_0)@ha
> 	addi 11,11,(1b-res_0)@l
> 	sub 11,11,12			# r11 = index * 4

Reverse the res entries and you can compute

	sub 11,12,11
	subi 11,11,(1b-res_0)

> Of course, if we want to make the normal plt call path go fast, then the
> thing to do is have gcc generate the plt call stubs so that they can be
> scheduled.  So gcc generates
> 
>  addis 11,30,foo@gotplt@ha
>  lwz 11,foo@gotplt@l(11)
>  mtctr 11,foo@gotplt_marker
>  bctr foo@gotplt_marker
> 
> hopefully with other instructions scheduled in the sequence.  The funny
> looking gotplt_marker relocs are because ld might resolve "foo" to a
> local function, and would then turn the sequence into
> 
>  nop
>  nop
>  nop
>  bl foo

I don't think this is worthwhile.  IF we ever make gcc inline plt entries,
we should only do so when we have a good idea the target isn't local.
Relocations that get gotplt entries can be useful though.



r~

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-12 18:07         ` Richard Henderson
@ 2005-05-14  5:57           ` Alan Modra
  2005-05-17 14:16             ` Alan Modra
  0 siblings, 1 reply; 17+ messages in thread
From: Alan Modra @ 2005-05-14  5:57 UTC (permalink / raw)
  To: binutils

It turns out that it's useful to have the value of _GLOBAL_OFFSET_TABLE_
available easily in ld.so, and since ld.so doesn't need a pointer to
.glink I've renamed the tag that indicates a new PLT/GOT accordingly.

include/elf/
	* ppc.h (DT_PPC_GOT): Rename from DT_PPC_GLINK.
bfd/
	* elf32-ppc.c (ppc_elf_size_dynamic_sections): Set DT_PPC_GOT,
	not DT_PPC_GLINK.
	(ppc_elf_finish_dynamic_sections): Likewise.
binutils/
	* readelf.c (get_ppc_dynamic_type): Display DT_PPC_GOT, not
	DT_PPC_GLINK.

Index: include/elf/ppc.h
===================================================================
RCS file: /cvs/src/src/include/elf/ppc.h,v
retrieving revision 1.17
diff -u -p -r1.17 ppc.h
--- include/elf/ppc.h	11 May 2005 14:08:45 -0000	1.17
+++ include/elf/ppc.h	14 May 2005 04:28:53 -0000
@@ -145,8 +145,8 @@ END_RELOC_NUMBERS (R_PPC_max)
 #define IS_PPC_TLS_RELOC(R) \
   ((R) >= R_PPC_TLS && (R) <= R_PPC_GOT_DTPREL16_HA)
 
-/* Specify the start of the .glink section.  */
-#define DT_PPC_GLINK		DT_LOPROC
+/* Specify the value of _GLOBAL_OFFSET_TABLE_.  */
+#define DT_PPC_GOT		DT_LOPROC
 
 /* Processor specific flags for the ELF header e_flags field.  */
 
Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.157
diff -u -p -r1.157 elf32-ppc.c
--- bfd/elf32-ppc.c	12 May 2005 15:24:51 -0000	1.157
+++ bfd/elf32-ppc.c	14 May 2005 04:28:57 -0000
@@ -4406,7 +4406,7 @@ ppc_elf_size_dynamic_sections (bfd *outp
 
       if (htab->glink != NULL && htab->glink->size != 0)
 	{
-	  if (!add_dynamic_entry (DT_PPC_GLINK, 0))
+	  if (!add_dynamic_entry (DT_PPC_GOT, 0))
 	    return FALSE;
 	}
 
@@ -6313,6 +6313,7 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 {
   asection *sdyn;
   struct ppc_elf_link_hash_table *htab;
+  bfd_vma got;
 
 #ifdef DEBUG
   fprintf (stderr, "ppc_elf_finish_dynamic_sections called\n");
@@ -6321,6 +6322,12 @@ ppc_elf_finish_dynamic_sections (bfd *ou
   htab = ppc_elf_hash_table (info);
   sdyn = bfd_get_section_by_name (htab->elf.dynobj, ".dynamic");
 
+  got = 0;
+  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);
+
   if (htab->elf.dynamic_sections_created)
     {
       Elf32_External_Dyn *dyncon, *dynconend;
@@ -6352,10 +6359,8 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 	      dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
 	      break;
 
-	    case DT_PPC_GLINK:
-	      s = htab->glink;
-	      dyn.d_un.d_ptr = (s->size - GLINK_PLTRESOLVE
-				+ s->output_section->vma + s->output_offset);
+	    case DT_PPC_GOT:
+	      dyn.d_un.d_ptr = got;
 	      break;
 
 	    default:
@@ -6389,7 +6394,7 @@ ppc_elf_finish_dynamic_sections (bfd *ou
     {
       unsigned char *p;
       unsigned char *endp;
-      bfd_vma got, pltgot;
+      bfd_vma pltgot;
       unsigned int i;
       static const unsigned int plt_resolve[] =
 	{
@@ -6409,10 +6414,6 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 #define PPC_HI(v) (((v) >> 16) & 0xffff)
 #define PPC_HA(v) PPC_HI ((v) + 0x8000)
 
-      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);
-
       pltgot = (htab->plt->output_section->vma
 		+ htab->plt->output_offset
 		- got);
Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.293
diff -u -p -r1.293 readelf.c
--- binutils/readelf.c	14 May 2005 01:38:14 -0000	1.293
+++ binutils/readelf.c	14 May 2005 04:29:06 -0000
@@ -1417,7 +1417,7 @@ get_ppc_dynamic_type (unsigned long type
 {
   switch (type)
     {
-    case DT_PPC_GLINK: return "PPC_GLINK";
+    case DT_PPC_GOT: return "PPC_GOT";
     default:
       return NULL;
     }

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-14  5:57           ` Alan Modra
@ 2005-05-17 14:16             ` Alan Modra
  2005-05-19  8:32               ` Alan Modra
  0 siblings, 1 reply; 17+ messages in thread
From: Alan Modra @ 2005-05-17 14:16 UTC (permalink / raw)
  To: binutils

Fixes a really dumb oversight in the new plt call stubs:  When building
non-pic executables there is no GOT pointer, so we need to build non-pic
call stubs.  With this plus gcc support and a rather large glibc patch,
I have a working ppc32 toolchain for the new non-exec PLT/GOT.  ppc32
-fPIC has a similar problem to non-PIC in that the GOT is per-function,
so there's no global GOT pointer we can use to access the PLT
addresses.  I haven't fixed -fPIC yet.

	* elf32-ppc.c (LIS_11. LIS_12): Define.
	(LWZU_0_X_12, LWZ_0_X_12, LWZ_12_4_12, LWZ_12_X_12): Define.
	(ppc_elf_finish_dynamic_sections): Provide non-pic plt call stub
	for --data-plt when building non-pie executables.

Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.158
diff -u -p -r1.158 elf32-ppc.c
--- bfd/elf32-ppc.c	14 May 2005 05:07:18 -0000	1.158
+++ bfd/elf32-ppc.c	17 May 2005 11:41:07 -0000
@@ -81,6 +81,12 @@ static bfd_reloc_status_type ppc_elf_unh
 #define LWZ_11_X_11	0x816b0000
 #define LWZ_11_X_30	0x817e0000
 #define MTCTR_11	0x7d6903a6
+#define LIS_11		0x3d600000
+#define LIS_12		0x3d800000
+#define LWZU_0_X_12	0x840c0000
+#define LWZ_0_X_12	0x800c0000
+#define LWZ_12_4_12	0x818c0004
+#define LWZ_12_X_12	0x818c0000
 
 /* Offset of tp and dtp pointers from start of TLS block.  */
 #define TP_OFFSET	0x7000
@@ -6394,59 +6400,78 @@ ppc_elf_finish_dynamic_sections (bfd *ou
     {
       unsigned char *p;
       unsigned char *endp;
-      bfd_vma pltgot;
+      bfd_vma pltgot, res0;
       unsigned int i;
       static const unsigned int plt_resolve[] =
 	{
-	  SUB_11_11_30,
-	  ADD_0_11_11,
-	  ADD_11_0_11,
 	  LWZ_0_4_30,
+	  SUB_11_11_30,
 	  MTCTR_0,
+	  ADD_0_11_11,
 	  LWZ_12_8_30,
+	  ADD_11_0_11,
 	  BCTR,
 	  NOP,
 	  NOP,
 	  NOP
 	};
 
+      if (ARRAY_SIZE (plt_resolve) + 2 != GLINK_PLTRESOLVE / 4)
+	abort ();
+
 #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);
+      pltgot = htab->plt->output_section->vma + htab->plt->output_offset;
 
       /* Write the plt call stubs.  */
       p = htab->glink->contents;
       endp = p + htab->glink_pltresolve;
-      while (p < endp)
+      if (info->shared || info->pie)
 	{
-	  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
+	  pltgot -= got;
+
+	  while (p < endp)
 	    {
-	      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;
+	      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;
 	    }
-	  pltgot += 4;
 	}
+      else
+	while (p < endp)
+	  {
+	    bfd_put_32 (output_bfd, LIS_11 + 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;
+	  }
 
       /* Now build the branch table, one for each plt entry (less one),
 	 and perhaps some padding.  */
@@ -6463,23 +6488,53 @@ ppc_elf_finish_dynamic_sections (bfd *ou
 	  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_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (got - res0), p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (got - res0), p);
+	  p += 4;
 
-      for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
+	  for (i = 0; i < ARRAY_SIZE (plt_resolve); i++)
+	    {
+	      bfd_put_32 (output_bfd, plt_resolve[i], p);
+	      p += 4;
+	    }
+	}
+      else
 	{
-	  bfd_put_32 (output_bfd, plt_resolve[i], p);
+	  bfd_put_32 (output_bfd, LIS_12 + PPC_HA (got + 4), p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (-res0), p);
 	  p += 4;
+	  if (PPC_HA (got + 4) != PPC_HA (got + 8))
+	    bfd_put_32 (output_bfd, LWZU_0_X_12 + PPC_LO (got + 4), p);
+	  else
+	    bfd_put_32 (output_bfd, LWZ_0_X_12 + PPC_LO (got + 4), p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (-res0), p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, MTCTR_0, p);
+	  p += 4;
+	  bfd_put_32 (output_bfd, ADD_0_11_11, p);
+	  p += 4;
+	  if (PPC_HA (got + 4) != PPC_HA (got + 8))
+	    bfd_put_32 (output_bfd, LWZ_12_4_12, p);
+	  else
+	    bfd_put_32 (output_bfd, LWZ_12_X_12 + PPC_LO (got + 8), p);
+	  p += 4;
+
+	  for (i = 5; i < ARRAY_SIZE (plt_resolve); i++)
+	    {
+	      bfd_put_32 (output_bfd, plt_resolve[i], p);
+	      p += 4;
+	    }
 	}
-      if (ARRAY_SIZE (plt_resolve) + 2 != GLINK_PLTRESOLVE / 4)
-	abort ();
     }
 
   return TRUE;

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

* Re: powerpc new PLT and GOT
  2005-05-17 14:16             ` Alan Modra
@ 2005-05-19  8:32               ` Alan Modra
  0 siblings, 0 replies; 17+ messages in thread
From: Alan Modra @ 2005-05-19  8:32 UTC (permalink / raw)
  To: binutils

This patch adds support for -fPIC using the new ppc32 PLT/GOT layout.
-fPIC as implemented by gcc uses a separate GOT section per input file,
called .got2.  Functions that use the GOT load r30 to point 32768 bytes
past the start of this section, which means that each input file will
have a different GOT pointer value.  That implies a need for multiple
plt call stubs, eg. printf called in one file will need a different
stub than printf in another file, because the stub uses the GOT pointer
to access .plt.  Supporting this is quite straight-forward using the
ppc64 multiple stub infrastructure, with one small complication:  ppc32
wants to use plt.plist but continue using got.refcount, so we need to
separate out initial values held in struct elf_link_hash_table.

Multple GOT pointer values also mean that the plt resolver stub must
calculate its own GOT pointer.

Then there's the problem of how the linker should deduce that multiple
GOT sections are in use, and how it should find the GOT pointer values.
It's not possible to use .got2 + 32768 if .got2 exists, because ld -r
might have combined a number of input files, merging their .got2
sections.  ld -r might also merge -fPIC code with -fpic code.  So some
scheme using relocations on call instructions is needed.  Now it so
happens that the R_PPC_PLTREL24 reloc on plt calls always has r_addend
zero.  (Indeed, non-zero values make no sense since the addend applies
to the plt slot.)  The unused addend field can therefore be used to
specify the .got2 offset, rather than using an additional reloc.  This
does make R_PPC_PLTREL24 a rather unusual reloc, and
"bl printf+32768@plt" looks weird too, but I think it's better than
inventing a new reloc type.  Besides, this makes the necessary gas
change trivial.


bfd/
	* elf-bfd.h (struct elf_link_hash_table): Delete init_refcount and
	init_offset.  Add init_got_refcount, init_plt_refcount,
	init_got_offset and init_plt_offset.
	* elf.c (_bfd_elf_link_hash_newfunc): Adjust for above change.
	(_bfd_elf_link_hash_hide_symbol): Likewise.
	(_bfd_elf_link_hash_table_init): Likewise.
	* elf32-hppa.c (elf32_hppa_hide_symbol): Likewise.
	* elf64-ppc.c (ppc64_elf_link_hash_table_create): Likewise.
	* elflink.c (_bfd_elf_adjust_dynamic_symbol): Likewise.
	(bfd_elf_size_dynamic_sections): Likewise.
	* elf32-ppc.c (GLINK_PLTRESOLVE): Now 16 insns.
	(LWZU_0_X_12, LWZ_0_4_30, LWZ_0_X_12, LWZ_11_X_11, LWZ_11_X_30,
	LWZ_12_4_12, LWZ_12_8_30, LWZ_12_X_12, SUB_11_11_30): Delete.
	(ADDIS_12_12, BCL_20_31, LWZU_0_12, LWZ_0_12, LWZ_11_11, LWZ_11_30,
	LWZ_12_12, MFLR_0, MFLR_12, MTLR_0, SUB_11_11_12): Define.
	(struct plt_entry): New.
	(ppc_elf_link_hash_table_create): Set new init_plt fields.
	(ppc_elf_copy_indirect_symbol): Handle merge of plt plist.  Don't
	use _bfd_elf_link_hash_copy_indirect.
	(update_plt_info, find_plt_ent): New functions.
	(ppc_elf_check_relocs): Handle R_PPC_PLTREL24 with non-zero addend
	and adjust for use of plt list rather than refcount.
	(ppc_elf_gc_sweep_hook): Likewise.
	(ppc_elf_tls_optimize): Likewise.
	(ppc_elf_adjust_dynamic_symbol): Likewise.
	(allocate_dynrelocs): Likewise.
	(ppc_elf_relax_section): Likewise.
	(ppc_elf_relocate_section): Likewise.  Adjust R_PPC_PLTREL24 addends
	when performing a relocatable link.
	(ppc_elf_finish_dynamic_symbol): Likewise.  Write .glink stubs here..
	(ppc_elf_finish_dynamic_sections): ..rather than here.  Use new
	pic resolver stub.

gas/
	* config/tc-ppc.c (ppc_force_relocation): Add BFD_RELOC_24_PLT_PCREL.

Index: bfd/elf-bfd.h
===================================================================
RCS file: /cvs/src/src/bfd/elf-bfd.h,v
retrieving revision 1.186
diff -u -p -r1.186 elf-bfd.h
--- bfd/elf-bfd.h	7 May 2005 13:22:44 -0000	1.186
+++ bfd/elf-bfd.h	19 May 2005 05:34:21 -0000
@@ -349,13 +349,15 @@ struct elf_link_hash_table
 
   /* The value to use when initialising got.refcount/offset and
      plt.refcount/offset in an elf_link_hash_entry.  Set to zero when
-     the values are refcounts.  Set to init_offset in
-     size_dynamic_sections when the values may be offsets.  */
-  union gotplt_union init_refcount;
+     the values are refcounts.  Set to init_got_offset/init_plt_offset
+     in size_dynamic_sections when the values may be offsets.  */
+  union gotplt_union init_got_refcount;
+  union gotplt_union init_plt_refcount;
 
   /* The value to use for got.refcount/offset and plt.refcount/offset
      when the values may be offsets.  Normally (bfd_vma) -1.  */
-  union gotplt_union init_offset;
+  union gotplt_union init_got_offset;
+  union gotplt_union init_plt_offset;
 
   /* The number of symbols found in the link which must be put into
      the .dynsym section.  */
Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.296
diff -u -p -r1.296 elf.c
--- bfd/elf.c	18 May 2005 13:41:59 -0000	1.296
+++ bfd/elf.c	19 May 2005 05:34:28 -0000
@@ -1414,7 +1414,8 @@ _bfd_elf_link_hash_newfunc (struct bfd_h
       /* Set local fields.  */
       ret->indx = -1;
       ret->dynindx = -1;
-      ret->got = ret->plt = htab->init_refcount;
+      ret->got = htab->init_got_refcount;
+      ret->plt = htab->init_plt_refcount;
       memset (&ret->size, 0, (sizeof (struct elf_link_hash_entry)
 			      - offsetof (struct elf_link_hash_entry, size)));
       /* Assume that we have been called by a non-ELF symbol reader.
@@ -1487,7 +1488,7 @@ _bfd_elf_link_hash_hide_symbol (struct b
 				struct elf_link_hash_entry *h,
 				bfd_boolean force_local)
 {
-  h->plt = elf_hash_table (info)->init_offset;
+  h->plt = elf_hash_table (info)->init_plt_offset;
   h->needs_plt = 0;
   if (force_local)
     {
@@ -1512,14 +1513,14 @@ _bfd_elf_link_hash_table_init
 				      const char *))
 {
   bfd_boolean ret;
+  int can_refcount = get_elf_backend_data (abfd)->can_refcount;
 
   table->dynamic_sections_created = FALSE;
   table->dynobj = NULL;
-  /* Make sure can_refcount is extended to the width and signedness of
-     init_refcount before we subtract one from it.  */
-  table->init_refcount.refcount = get_elf_backend_data (abfd)->can_refcount;
-  table->init_refcount.refcount -= 1;
-  table->init_offset.offset = -(bfd_vma) 1;
+  table->init_got_refcount.refcount = can_refcount - 1;
+  table->init_plt_refcount.refcount = can_refcount - 1;
+  table->init_got_offset.offset = -(bfd_vma) 1;
+  table->init_plt_offset.offset = -(bfd_vma) 1;
   /* The first dynamic symbol is a dummy.  */
   table->dynsymcount = 1;
   table->dynstr = NULL;
Index: bfd/elf32-hppa.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-hppa.c,v
retrieving revision 1.131
diff -u -p -r1.131 elf32-hppa.c
--- bfd/elf32-hppa.c	5 May 2005 14:33:47 -0000	1.131
+++ bfd/elf32-hppa.c	19 May 2005 05:34:29 -0000
@@ -1678,7 +1678,7 @@ elf32_hppa_hide_symbol (struct bfd_link_
   if (! ((struct elf32_hppa_link_hash_entry *) h)->plabel)
     {
       h->needs_plt = 0;
-      h->plt = elf_hash_table (info)->init_refcount;
+      h->plt = elf_hash_table (info)->init_plt_refcount;
     }
 }
 
Index: bfd/elf64-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf64-ppc.c,v
retrieving revision 1.206
diff -u -p -r1.206 elf64-ppc.c
--- bfd/elf64-ppc.c	9 May 2005 10:30:40 -0000	1.206
+++ bfd/elf64-ppc.c	19 May 2005 05:34:36 -0000
@@ -3520,10 +3520,14 @@ ppc64_elf_link_hash_table_create (bfd *a
      only care about glist, but when compiled on a 32-bit host the
      bfd_vma fields are larger.  Setting the bfd_vma to zero makes
      debugger inspection of these fields look nicer.  */
-  htab->elf.init_refcount.refcount = 0;
-  htab->elf.init_refcount.glist = NULL;
-  htab->elf.init_offset.offset = 0;
-  htab->elf.init_offset.glist = NULL;
+  htab->elf.init_got_refcount.refcount = 0;
+  htab->elf.init_got_refcount.glist = NULL;
+  htab->elf.init_plt_refcount.refcount = 0;
+  htab->elf.init_plt_refcount.glist = NULL;
+  htab->elf.init_got_offset.offset = 0;
+  htab->elf.init_got_offset.glist = NULL;
+  htab->elf.init_plt_offset.offset = 0;
+  htab->elf.init_plt_offset.glist = NULL;
 
   return &htab->elf.root;
 }
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.164
diff -u -p -r1.164 elflink.c
--- bfd/elflink.c	14 May 2005 13:49:42 -0000	1.164
+++ bfd/elflink.c	19 May 2005 05:34:42 -0000
@@ -2362,8 +2362,8 @@ _bfd_elf_adjust_dynamic_symbol (struct e
 
   if (h->root.type == bfd_link_hash_warning)
     {
-      h->plt = elf_hash_table (eif->info)->init_offset;
-      h->got = elf_hash_table (eif->info)->init_offset;
+      h->got = elf_hash_table (eif->info)->init_got_offset;
+      h->plt = elf_hash_table (eif->info)->init_plt_offset;
 
       /* When warning symbols are created, they **replace** the "real"
 	 entry in the hash table, thus we never get to see the real
@@ -2392,7 +2392,7 @@ _bfd_elf_adjust_dynamic_symbol (struct e
 	  || (!h->ref_regular
 	      && (h->u.weakdef == NULL || h->u.weakdef->dynindx == -1))))
     {
-      h->plt = elf_hash_table (eif->info)->init_offset;
+      h->plt = elf_hash_table (eif->info)->init_plt_offset;
       return TRUE;
     }
 
@@ -4986,7 +4986,10 @@ bfd_elf_size_dynamic_sections (bfd *outp
 
   /* Any syms created from now on start with -1 in
      got.refcount/offset and plt.refcount/offset.  */
-  elf_hash_table (info)->init_refcount = elf_hash_table (info)->init_offset;
+  elf_hash_table (info)->init_got_refcount
+    = elf_hash_table (info)->init_got_offset;
+  elf_hash_table (info)->init_plt_refcount
+    = elf_hash_table (info)->init_plt_offset;
 
   /* The backend may have to create some sections regardless of whether
      we're dynamic or not.  */
Index: bfd/elf32-ppc.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-ppc.c,v
retrieving revision 1.159
diff -u -p -r1.159 elf32-ppc.c
--- bfd/elf32-ppc.c	17 May 2005 13:55:02 -0000	1.159
+++ bfd/elf32-ppc.c	19 May 2005 07:46:23 -0000
@@ -62,31 +62,33 @@ static bfd_reloc_status_type ppc_elf_unh
 #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
 
 /* 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 MTCTR_11	0x7d6903a6
 #define LIS_11		0x3d600000
 #define LIS_12		0x3d800000
-#define LWZU_0_X_12	0x840c0000
-#define LWZ_0_X_12	0x800c0000
-#define LWZ_12_4_12	0x818c0004
-#define LWZ_12_X_12	0x818c0000
+#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
@@ -2198,6 +2200,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.  */
@@ -2341,6 +2369,11 @@ ppc_elf_link_hash_table_create (bfd *abf
       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";
@@ -2442,11 +2475,12 @@ ppc_elf_create_dynamic_sections (bfd *ab
 /* 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;
@@ -2487,20 +2521,72 @@ ppc_elf_copy_indirect_symbol (const stru
 
   edir->tls_mask |= eind->tls_mask;
 
-  if (ELIMINATE_COPY_RELOCS
-      && ind->root.type != bfd_link_hash_indirect
-      && dir->dynamic_adjusted)
-    {
-      /* 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;
+  /* 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;
+
+  /* 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)
+    {
+      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.  */
@@ -2712,6 +2798,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)
 {
@@ -2737,7 +2863,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;
@@ -2763,6 +2889,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;
@@ -2929,9 +3056,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
@@ -2955,7 +3087,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:
@@ -3049,7 +3181,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;
@@ -3365,6 +3498,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;
@@ -3375,6 +3509,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++)
@@ -3469,8 +3604,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;
 
@@ -3620,8 +3757,9 @@ ppc_elf_tls_optimize (bfd *obfd ATTRIBUT
 			&& 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;
@@ -3729,7 +3867,11 @@ ppc_elf_adjust_dynamic_symbol (struct bf
     {
       /* 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))
@@ -3744,13 +3886,13 @@ ppc_elf_adjust_dynamic_symbol (struct bf
 
 	     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
@@ -3912,83 +4054,100 @@ allocate_dynrelocs (struct elf_link_hash
     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;
+      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 (info->shared
-	  || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
-	{
-	  asection *s = htab->plt;
+	    if (info->shared
+		|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
+	      {
+		asection *s = htab->plt;
 
-	  if (!htab->old_plt)
-	    {
-	      h->plt.offset = s->size;
-	      s->size += 4;
+		if (!htab->old_plt)
+		  {
+		    ent->plt.offset = s->size;
+		    if (!doneone)
+		      s->size += 4;
+
+		    s = htab->glink;
+		    if (!info->shared
+			&& !h->def_regular)
+		      {
+			h->root.u.def.section = s;
+			h->root.u.def.value = s->size;
+		      }
+		    ent->glink_offset = 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.  */
+		    ent->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 = ent->plt.offset;
+		      }
 
-	      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;
-		}
+		    /* Make room for this entry.  After the 8192nd entry, room
+		       for two entries is allocated.  */
+		    if (!doneone)
+		      {
+			s->size += PLT_ENTRY_SIZE;
+			if ((s->size - PLT_INITIAL_ENTRY_SIZE) / PLT_ENTRY_SIZE
+			    > PLT_NUM_SINGLE_ENTRIES)
+			  s->size += PLT_ENTRY_SIZE;
+		      }
+		  }
 
-	      /* 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;
-	    }
+		/* We also need to make an entry in the .rela.plt section.  */
+		if (!doneone)
+		  {
+		    htab->relplt->size += sizeof (Elf32_External_Rela);
+		    doneone = TRUE;
+		  }
+	      }
+	    else
+	      ent->plt.offset = (bfd_vma) -1;
 
-	  /* 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;
-	}
+	    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;
     }
 
@@ -4486,6 +4645,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;
 
@@ -4509,8 +4669,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);
@@ -4584,21 +4745,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)
 	    {
@@ -4650,7 +4819,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;
@@ -5018,7 +5188,7 @@ ppc_elf_relocate_section (bfd *output_bf
   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;
 
@@ -5030,8 +5200,30 @@ ppc_elf_relocate_section (bfd *output_bf
 		      (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])
@@ -5809,18 +6001,19 @@ ppc_elf_relocate_section (bfd *output_bf
 
 	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 */
@@ -5887,26 +6080,29 @@ ppc_elf_relocate_section (bfd *output_bf
 	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_.  */
@@ -6168,6 +6364,10 @@ ppc_elf_relocate_section (bfd *output_bf
   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.  */
 
@@ -6178,6 +6378,8 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
 			       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",
@@ -6187,69 +6389,130 @@ ppc_elf_finish_dynamic_symbol (bfd *outp
   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;
+
+	    /* This symbol has an entry in the procedure linkage table.
+	       Set it up.  */
+	    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);
+	      }
 
-#ifdef DEBUG
-      fprintf (stderr, ", plt_offset = %d", h->plt.offset);
-#endif
+	    /* Fill in the entry in the .rela.plt section.  */
+	    rela.r_offset = (htab->plt->output_section->vma
+			     + htab->plt->output_offset
+			     + ent->plt.offset);
+	    rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT);
+	    rela.r_addend = 0;
 
-      /* This symbol has an entry in the procedure linkage table.  Set
-	 it up.  */
+	    if (!htab->old_plt)
+	      reloc_index = ent->plt.offset / 4;
+	    else
+	      {
+		reloc_index = ((ent->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);
 
-      BFD_ASSERT (h->dynindx != -1);
-      BFD_ASSERT (htab->plt != NULL && htab->relplt != NULL);
+	    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)
-	{
-	  /* 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);
-	}
+	if (!htab->old_plt)
+	  {
+	    bfd_vma plt;
+	    unsigned char *p;
 
-      /* 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;
+	    plt = (ent->plt.offset
+		   + htab->plt->output_section->vma
+		   + htab->plt->output_offset);
+	    p = (unsigned char *) htab->glink->contents + ent->glink_offset;
 
-      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);
+	    if (info->shared || info->pie)
+	      {
+		bfd_vma got = 0;
 
-      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;
-	}
-    }
+		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;
+	      }
+	  }
+	else
+	  break;
+      }
 
   if (h->needs_copy)
     {
@@ -6400,81 +6663,94 @@ ppc_elf_finish_dynamic_sections (bfd *ou
     {
       unsigned char *p;
       unsigned char *endp;
-      bfd_vma pltgot, res0;
+      bfd_vma res0;
       unsigned int i;
+
+      /*
+       * 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[] =
+	{
+	  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,
+	  BCTR,
+	  NOP,
+	  NOP
+	};
+
       static const unsigned int plt_resolve[] =
 	{
-	  LWZ_0_4_30,
-	  SUB_11_11_30,
+	  LIS_12,
+	  ADDIS_11_11,
+	  LWZ_0_12,
+	  ADDI_11_11,
 	  MTCTR_0,
 	  ADD_0_11_11,
-	  LWZ_12_8_30,
+	  LWZ_12_12,
 	  ADD_11_0_11,
 	  BCTR,
 	  NOP,
 	  NOP,
+	  NOP,
+	  NOP,
+	  NOP,
+	  NOP,
 	  NOP
 	};
 
-      if (ARRAY_SIZE (plt_resolve) + 2 != GLINK_PLTRESOLVE / 4)
+      if (ARRAY_SIZE (pic_plt_resolve) != GLINK_PLTRESOLVE / 4)
+	abort ();
+      if (ARRAY_SIZE (plt_resolve) != GLINK_PLTRESOLVE / 4)
 	abort ();
 
-#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;
-
-      /* Write the plt call stubs.  */
-      p = htab->glink->contents;
-      endp = p + htab->glink_pltresolve;
-      if (info->shared || info->pie)
-	{
-	  pltgot -= got;
-
-	  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;
-	    }
-	}
-      else
-	while (p < endp)
-	  {
-	    bfd_put_32 (output_bfd, LIS_11 + 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;
-	  }
-
-      /* 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)
@@ -6495,45 +6771,69 @@ ppc_elf_finish_dynamic_sections (bfd *ou
       /* Last comes the PLTresolve stub.  */
       if (info->shared || info->pie)
 	{
-	  bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (got - res0), p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (got - res0), p);
-	  p += 4;
+	  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, plt_resolve[i], p);
+	      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, LIS_12 + PPC_HA (got + 4), p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, ADDIS_11_11 + PPC_HA (-res0), p);
-	  p += 4;
-	  if (PPC_HA (got + 4) != PPC_HA (got + 8))
-	    bfd_put_32 (output_bfd, LWZU_0_X_12 + PPC_LO (got + 4), p);
-	  else
-	    bfd_put_32 (output_bfd, LWZ_0_X_12 + PPC_LO (got + 4), p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, ADDI_11_11 + PPC_LO (-res0), p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, MTCTR_0, p);
-	  p += 4;
-	  bfd_put_32 (output_bfd, ADD_0_11_11, p);
-	  p += 4;
-	  if (PPC_HA (got + 4) != PPC_HA (got + 8))
-	    bfd_put_32 (output_bfd, LWZ_12_4_12, p);
-	  else
-	    bfd_put_32 (output_bfd, LWZ_12_X_12 + PPC_LO (got + 8), p);
-	  p += 4;
-
-	  for (i = 5; i < ARRAY_SIZE (plt_resolve); i++)
+	  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);
+	    }
 	}
     }
 
Index: gas/config/tc-ppc.c
===================================================================
RCS file: /cvs/src/src/gas/config/tc-ppc.c,v
retrieving revision 1.97
diff -u -p -r1.97 tc-ppc.c
--- gas/config/tc-ppc.c	11 May 2005 14:10:37 -0000	1.97
+++ gas/config/tc-ppc.c	19 May 2005 05:35:11 -0000
@@ -5460,6 +5472,7 @@ ppc_force_relocation (fix)
     case BFD_RELOC_PPC_B16_BRNTAKEN:
     case BFD_RELOC_PPC_BA16_BRTAKEN:
     case BFD_RELOC_PPC_BA16_BRNTAKEN:
+    case BFD_RELOC_24_PLT_PCREL:
     case BFD_RELOC_PPC64_TOC:
       return 1;
     default:

-- 
Alan Modra
IBM OzLabs - Linux Technology Centre

^ permalink raw reply	[flat|nested] 17+ messages in thread

end of thread, other threads:[~2005-05-19  8:26 UTC | newest]

Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-05-11 14:22 powerpc new PLT and GOT Alan Modra
2005-05-11 14:29 ` Daniel Jacobowitz
2005-05-11 14:59   ` Alan Modra
2005-05-11 15:10     ` Andreas Schwab
2005-05-11 15:39       ` Alan Modra
2005-05-11 15:16     ` Daniel Jacobowitz
2005-05-11 16:37       ` Richard Earnshaw
2005-05-11 14:45 ` Andreas Schwab
2005-05-12  6:08 ` Richard Henderson
2005-05-12  6:13   ` Alan Modra
2005-05-12  7:47     ` Richard Henderson
2005-05-12  9:02       ` Alan Modra
2005-05-12 16:10         ` Alan Modra
2005-05-12 18:07         ` Richard Henderson
2005-05-14  5:57           ` Alan Modra
2005-05-17 14:16             ` Alan Modra
2005-05-19  8:32               ` Alan Modra

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).