[rfc] read-only plt format for alpha

Richard Henderson rth@twiddle.net
Sun May 29 02:48:00 GMT 2005


The new format allows the .plt section to be placed into the text section.
While it doesn't require compiler or relocation changes, as the recent ppc
plt change did, it does require ld.so changes.

The new format is also smaller than the old format, and allows 3 times 
more entries before it overflows.  Not that we've ever had a report of
the current plt table format overflowing.

Support for this new format has been checked into glibc cvs, but of course
noone else can handle it yet.  So the question is how to enable it.  My
current solution is a "--enable-secureplt" configure option to set the
default, and --secureplt/--no-secureplt options to ld to force a particular
format.  And while we generally frown on global variables in BFD, I think
in this case it's more effort than it's worth to force the variable into
the bfd_link_info table, or one of its hashes.  And pointless besides,
because there are no bfd users that need to support both formats
simultaneously.

Comments?



r~



bfd/
	* configure.in (--enable-secureplt): New.
	* elf64-alpha.c (INSN_LDA, INSN_LDAH, INSN_LDQ, INSN_BR): New.
	(INSN_SUBQ, INSN_S4SUBQ, INSN_JMP): New.
	(INSN_A, INSN_AB, INSN_ABC, INSN_ABO, INSN_AD): New.
	(elf64_alpha_use_secureplt): New.
	(OLD_PLT_HEADER_SIZE, OLD_PLT_ENTRY_SIZE): New.
	(NEW_PLT_HEADER_SIZE, NEW_PLT_ENTRY_SIZE): New.
	(PLT_HEADER_SIZE, PLT_ENTRY_SIZE): Conditionalize on secureplt.
	(ALPHA_ELF_LINK_HASH_PLT_LOC): Remove.
	(struct alpha_elf_link_hash_entry): Add plt_offset.
	(PLT_HEADER_WORD1, PLT_HEADER_WORD2, PLT_HEADER_WORD3): Remove.
	(PLT_HEADER_WORD4, PLT_ENTRY_WORD1, PLT_ENTRY_WORD2): Remove.
	(PLT_ENTRY_WORD3): Remove.
	(elf64_alpha_create_dynamic_sections): If secureplt, set SEC_READONLY
	on .plt and create .got.plt.
	(elf64_alpha_output_extsym): Remove .plt frobbing for symbol values.
	(get_got_entry): Initialize plt_offset.
	(elf64_alpha_want_plt): New.
	(elf64_alpha_check_relocs): Use it.
	(elf64_alpha_adjust_dynamic_symbol): Likewise.  Don't allocate
	plt entries here.
	(elf64_alpha_calc_got_offsets_for_symbol): Don't report internal
	error as user error.
	(elf64_alpha_size_plt_section_1): Allocate one plt entry per
	got subsection.
	(elf64_alpha_size_plt_section): Size .got.plt section.
	(elf64_alpha_size_rela_got_1): Don't allocate relocations if
	plt entries used.
	(elf64_alpha_size_dynamic_sections): Call elf64_alpha_size_plt_section.
	Add PLTRO DYNAMIC entry.
	(elf64_alpha_finish_dynamic_symbol): Generate secureplt format.
	(elf64_alpha_finish_dynamic_sections): Likewise.

binutils/
	* readelf.c (get_alpha_dynamic_type): New.
	(get_dynamic_type): Call it.

include/elf/
	* alpha.h (DT_ALPHA_PLTRO): New.

ld/
	* emulparams/elf64alpha.sh (PLT): New.
	(TEXT_PLT): New.
	* emultempl/alphaelf.em (disable_relaxation): New.
	(limit_32bit): Rename from elf64alpha_32bit; update all users.
	(elf64_alpha_use_secureplt): Declare.
	(bfd_elf64_alpha_vec, bfd_elf64_alpha_freebsd_vec): Declare.
	(alpha_after_open): New.
	(alpha_before_allocation): New.
	(OPTION_NO_RELAX, OPTION_SECUREPLT, OPTION_NO_SECUREPLT): New.
	(PARSE_AND_LIST_LONGOPTS): Include them.
	(PARSE_AND_LIST_OPTIONS): Likewise.
	(PARSE_AND_LIST_ARGS_CASES): Likewise.
	(LDEMUL_AFTER_OPEN, LDEMUL_BEFORE_ALLOCATION): New.
	* scripttempl/elf.sc (TEXT_PLT): New.
	(PLT): Use it.

Index: bfd/configure.in
===================================================================
RCS file: /cvs/src/src/bfd/configure.in,v
retrieving revision 1.184
diff -u -p -d -r1.184 configure.in
--- bfd/configure.in	28 May 2005 11:44:48 -0000	1.184
+++ bfd/configure.in	28 May 2005 23:59:15 -0000
@@ -51,6 +51,18 @@ AC_ARG_WITH(mmap,
   *)    AC_MSG_ERROR(bad value ${withval} for BFD with-mmap option) ;;
 esac],[want_mmap=false])dnl
 
+AC_ARG_ENABLE(secureplt,
+[  --enable-secureplt      Default to creating read-only plt entries],
+[case "${enableval}" in
+  yes)  use_secureplt=true  ;;
+  no)   use_secureplt=false ;;
+  *)    AC_MSG_ERROR(bad value ${enableval} for secureplt option) ;;
+esac],[use_secureplt=false])dnl
+if test $use_secureplt = true; then
+  AC_DEFINE(USE_SECUREPLT, 1,
+    [Define if we should default to creating read-only plt entries])
+fi
+
 AM_BINUTILS_WARNINGS
 
 AM_CONFIG_HEADER(config.h:config.in)
Index: bfd/elf64-alpha.c
===================================================================
RCS file: /cvs/src/src/bfd/elf64-alpha.c,v
retrieving revision 1.138
diff -u -p -d -r1.138 elf64-alpha.c
--- bfd/elf64-alpha.c	26 May 2005 16:14:33 -0000	1.138
+++ bfd/elf64-alpha.c	28 May 2005 23:59:17 -0000
@@ -48,6 +48,59 @@
 #include "ecoffswap.h"
 
 
+/* Instruction data for plt generation and relaxation.  */
+
+#define OP_LDA		0x08
+#define OP_LDAH		0x09
+#define OP_LDQ		0x29
+#define OP_BR		0x30
+#define OP_BSR		0x34
+
+#define INSN_LDA	(OP_LDA << 26)
+#define INSN_LDAH	(OP_LDAH << 26)
+#define INSN_LDQ	(OP_LDQ << 26)
+#define INSN_BR		(OP_BR << 26)
+
+#define INSN_ADDQ	0x40000400
+#define INSN_RDUNIQ	0x0000009e
+#define INSN_SUBQ	0x40000520
+#define INSN_S4SUBQ	0x40000560
+#define INSN_UNOP	0x2ffe0000
+
+#define INSN_JSR	0x68004000
+#define INSN_JMP	0x68000000
+#define INSN_JSR_MASK	0xfc00c000
+
+#define INSN_A(I,A)		(I | (A << 21))
+#define INSN_AB(I,A,B)		(I | (A << 21) | (B << 16))
+#define INSN_ABC(I,A,B,C)	(I | (A << 21) | (B << 16) | C)
+#define INSN_ABO(I,A,B,O)	(I | (A << 21) | (B << 16) | ((O) & 0xffff))
+#define INSN_AD(I,A,D)		(I | (A << 21) | (((D) >> 2) & 0x1fffff))
+
+/* PLT/GOT Stuff */
+
+/* Set by ld emulation.  Putting this into the link_info or hash structure
+   is simply working too hard.  */
+#ifdef USE_SECUREPLT
+bfd_boolean elf64_alpha_use_secureplt = TRUE;
+#else
+bfd_boolean elf64_alpha_use_secureplt = FALSE;
+#endif
+
+#define OLD_PLT_HEADER_SIZE	32
+#define OLD_PLT_ENTRY_SIZE	12
+#define NEW_PLT_HEADER_SIZE	36
+#define NEW_PLT_ENTRY_SIZE	4
+
+#define PLT_HEADER_SIZE \
+  (elf64_alpha_use_secureplt ? NEW_PLT_HEADER_SIZE : OLD_PLT_HEADER_SIZE)
+#define PLT_ENTRY_SIZE \
+  (elf64_alpha_use_secureplt ? NEW_PLT_ENTRY_SIZE : OLD_PLT_ENTRY_SIZE)
+
+#define MAX_GOT_SIZE		(64*1024)
+
+#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so"
+
 struct alpha_elf_link_hash_entry
 {
   struct elf_link_hash_entry root;
@@ -67,7 +120,6 @@ struct alpha_elf_link_hash_entry
 #define ALPHA_ELF_LINK_HASH_LU_TLSLDM	0x20
 #define ALPHA_ELF_LINK_HASH_LU_FUNC	0x38
 #define ALPHA_ELF_LINK_HASH_TLS_IE	0x40
-#define ALPHA_ELF_LINK_HASH_PLT_LOC	0x80
 
   /* Used to implement multiple .got subsections.  */
   struct alpha_elf_got_entry
@@ -83,6 +135,9 @@ struct alpha_elf_link_hash_entry
     /* The .got offset for this entry.  */
     int got_offset;
 
+    /* The .plt offset for this entry.  */
+    int plt_offset;
+
     /* How many references to this entry?  */
     int use_count;
 
@@ -1021,22 +1076,6 @@ elf64_alpha_info_to_howto (bfd *abfd ATT
    - align_power ((bfd_vma) 16,						\
 		  elf_hash_table (info)->tls_sec->alignment_power))
 
-/* PLT/GOT Stuff */
-#define PLT_HEADER_SIZE 32
-#define PLT_HEADER_WORD1	(bfd_vma) 0xc3600000	/* br   $27,.+4     */
-#define PLT_HEADER_WORD2	(bfd_vma) 0xa77b000c	/* ldq  $27,12($27) */
-#define PLT_HEADER_WORD3	(bfd_vma) 0x47ff041f	/* nop              */
-#define PLT_HEADER_WORD4	(bfd_vma) 0x6b7b0000	/* jmp  $27,($27)   */
-
-#define PLT_ENTRY_SIZE 12
-#define PLT_ENTRY_WORD1		0xc3800000	/* br   $28, plt0   */
-#define PLT_ENTRY_WORD2		0
-#define PLT_ENTRY_WORD3		0
-
-#define MAX_GOT_SIZE		(64*1024)
-
-#define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so"
-
 /* Handle an Alpha specific section when reading an object file.  This
    is called when bfd_section_from_shdr finds a section with an unknown
    type.
@@ -1199,13 +1238,13 @@ elf64_alpha_create_dynamic_sections (bfd
   /* We need to create .plt, .rela.plt, .got, and .rela.got sections.  */
 
   s = bfd_make_section_with_flags (abfd, ".plt",
-				   (SEC_ALLOC | SEC_LOAD
+				   (SEC_ALLOC | SEC_LOAD | SEC_CODE
 				    | SEC_HAS_CONTENTS
 				    | SEC_IN_MEMORY
 				    | SEC_LINKER_CREATED
-				    | SEC_CODE));
-  if (s == NULL
-      || ! bfd_set_section_alignment (abfd, s, 3))
+				    | (elf64_alpha_use_secureplt
+				       ? SEC_READONLY : 0)));
+  if (s == NULL || ! bfd_set_section_alignment (abfd, s, 4))
     return FALSE;
 
   /* Define the symbol _PROCEDURE_LINKAGE_TABLE_ at the start of the
@@ -1220,8 +1259,7 @@ elf64_alpha_create_dynamic_sections (bfd
   h->def_regular = 1;
   h->type = STT_OBJECT;
 
-  if (info->shared
-      && ! bfd_elf_link_record_dynamic_symbol (info, h))
+  if (info->shared && ! bfd_elf_link_record_dynamic_symbol (info, h))
     return FALSE;
 
   s = bfd_make_section_with_flags (abfd, ".rela.plt",
@@ -1230,10 +1268,17 @@ elf64_alpha_create_dynamic_sections (bfd
 				    | SEC_IN_MEMORY
 				    | SEC_LINKER_CREATED
 				    | SEC_READONLY));
-  if (s == NULL
-      || ! bfd_set_section_alignment (abfd, s, 3))
+  if (s == NULL || ! bfd_set_section_alignment (abfd, s, 3))
     return FALSE;
 
+  if (elf64_alpha_use_secureplt)
+    {
+      s = bfd_make_section_with_flags (abfd, ".got.plt",
+				       SEC_ALLOC | SEC_LINKER_CREATED);
+      if (s == NULL || ! bfd_set_section_alignment (abfd, s, 3))
+	return FALSE;
+    }
+
   /* We may or may not have created a .got section for this object, but
      we definitely havn't done the rest of the work.  */
 
@@ -1588,24 +1633,6 @@ elf64_alpha_output_extsym (struct alpha_
       else
 	h->esym.asym.value = 0;
     }
-  else if (h->root.needs_plt)
-    {
-      /* Set type and value for a symbol with a function stub.  */
-      h->esym.asym.st = stProc;
-      sec = bfd_get_section_by_name (einfo->abfd, ".plt");
-      if (sec == NULL)
-	h->esym.asym.value = 0;
-      else
-	{
-	  output_section = sec->output_section;
-	  if (output_section != NULL)
-	    h->esym.asym.value = (h->root.plt.offset
-				  + sec->output_offset
-				  + output_section->vma);
-	  else
-	    h->esym.asym.value = 0;
-	}
-    }
 
   if (! bfd_ecoff_debug_one_external (einfo->abfd, einfo->debug, einfo->swap,
 				      h->root.root.root.string,
@@ -1676,6 +1703,7 @@ get_got_entry (bfd *abfd, struct alpha_e
       gotent->gotobj = abfd;
       gotent->addend = r_addend;
       gotent->got_offset = -1;
+      gotent->plt_offset = -1;
       gotent->use_count = 1;
       gotent->reloc_type = r_type;
       gotent->reloc_done = 0;
@@ -1695,6 +1723,16 @@ get_got_entry (bfd *abfd, struct alpha_e
   return gotent;
 }
 
+static bfd_boolean
+elf64_alpha_want_plt (struct alpha_elf_link_hash_entry *ah)
+{
+  return ((ah->root.type == STT_FUNC
+	  || ah->root.root.type == bfd_link_hash_undefweak
+	  || ah->root.root.type == bfd_link_hash_undefined)
+	  && (ah->flags & ALPHA_ELF_LINK_HASH_LU_FUNC) != 0
+	  && (ah->flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC) == 0);
+}
+
 /* Handle dynamic relocations when doing an Alpha ELF link.  */
 
 static bfd_boolean
@@ -1874,12 +1912,13 @@ elf64_alpha_check_relocs (bfd *abfd, str
 		  h->flags = gotent_flags;
 
 		  /* Make a guess as to whether a .plt entry is needed.  */
-		  if ((gotent_flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
-		      && !(gotent_flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC))
-		    h->root.needs_plt = 1;
-		  else
-		    h->root.needs_plt = 0;
-	        }
+		  /* ??? It appears that we won't make it into
+		     adjust_dynamic_symbol for symbols that remain
+		     totally undefined.  Copying this check here means
+		     we can create a plt entry for them too.  */
+		  h->root.needs_plt
+		    = (maybe_dynamic && elf64_alpha_want_plt (h));
+		}
 	    }
 	}
 
@@ -1986,42 +2025,26 @@ elf64_alpha_adjust_dynamic_symbol (struc
   ah = (struct alpha_elf_link_hash_entry *)h;
 
   /* Now that we've seen all of the input symbols, finalize our decision
-     about whether this symbol should get a .plt entry.  */
-
-  if (alpha_elf_dynamic_symbol_p (h, info)
-      && ((h->type == STT_FUNC
-	   && !(ah->flags & ALPHA_ELF_LINK_HASH_LU_ADDR))
-	  || (h->type == STT_NOTYPE
-	      && (ah->flags & ALPHA_ELF_LINK_HASH_LU_FUNC)
-	      && !(ah->flags & ~ALPHA_ELF_LINK_HASH_LU_FUNC)))
-      /* Don't prevent otherwise valid programs from linking by attempting
-	 to create a new .got entry somewhere.  A Correct Solution would be
-	 to add a new .got section to a new object file and let it be merged
-	 somewhere later.  But for now don't bother.  */
-      && ah->got_entries)
+     about whether this symbol should get a .plt entry.  Irritatingly, it
+     is common for folk to leave undefined symbols in shared libraries,
+     and they still expect lazy binding; accept undefined symbols in lieu
+     of STT_FUNC.  */
+  if (alpha_elf_dynamic_symbol_p (h, info) && elf64_alpha_want_plt (ah))
     {
-      h->needs_plt = 1;
+      h->needs_plt = TRUE;
 
       s = bfd_get_section_by_name(dynobj, ".plt");
       if (!s && !elf64_alpha_create_dynamic_sections (dynobj, info))
 	return FALSE;
 
-      /* The first bit of the .plt is reserved.  */
-      if (s->size == 0)
-	s->size = PLT_HEADER_SIZE;
-
-      h->plt.offset = s->size;
-      s->size += PLT_ENTRY_SIZE;
-
-      /* We also need a JMP_SLOT entry in the .rela.plt section.  */
-      s = bfd_get_section_by_name (dynobj, ".rela.plt");
-      BFD_ASSERT (s != NULL);
-      s->size += sizeof (Elf64_External_Rela);
+      /* We need one plt entry per got subsection.  Delay allocation of
+	 the actual plt entries until size_plt_section, called from
+	 size_dynamic_sections or during relaxation.  */
 
       return TRUE;
     }
   else
-    h->needs_plt = 0;
+    h->needs_plt = FALSE;
 
   /* If this is a weak symbol, and there is a real definition, the
      processor independent code will have arranged for us to see the
@@ -2284,7 +2307,6 @@ static bfd_boolean
 elf64_alpha_calc_got_offsets_for_symbol (struct alpha_elf_link_hash_entry *h,
 					 PTR arg ATTRIBUTE_UNUSED)
 {
-  bfd_boolean result = TRUE;
   struct alpha_elf_got_entry *gotent;
 
   if (h->root.root.type == bfd_link_hash_warning)
@@ -2297,19 +2319,12 @@ elf64_alpha_calc_got_offsets_for_symbol 
 	bfd_size_type *plge;
 
 	td = alpha_elf_tdata (gotent->gotobj);
-	if (td == NULL)
-	  {
-	    _bfd_error_handler (_("Symbol %s has no GOT subsection for offset 0x%x"),
-				h->root.root.root.string, gotent->got_offset);
-	    result = FALSE;
-	    continue;
-	  }
 	plge = &td->got->size;
 	gotent->got_offset = *plge;
 	*plge += alpha_got_entry_size (gotent->reloc_type);
       }
 
-  return result;
+  return TRUE;
 }
 
 static void
@@ -2438,31 +2453,27 @@ elf64_alpha_size_plt_section_1 (struct a
 {
   asection *splt = (asection *) data;
   struct alpha_elf_got_entry *gotent;
+  bfd_boolean saw_one = FALSE;
 
   /* If we didn't need an entry before, we still don't.  */
   if (!h->root.needs_plt)
     return TRUE;
 
-  /* There must still be a LITERAL got entry for the function.  */
+  /* For each LITERAL got entry still in use, allocate a plt entry.  */
   for (gotent = h->got_entries; gotent ; gotent = gotent->next)
     if (gotent->reloc_type == R_ALPHA_LITERAL
 	&& gotent->use_count > 0)
-      break;
+      {
+	if (splt->size == 0)
+	  splt->size = PLT_HEADER_SIZE;
+	gotent->plt_offset = splt->size;
+	splt->size += PLT_ENTRY_SIZE;
+	saw_one = TRUE;
+      }
 
-  /* If there is, reset the PLT offset.  If not, there's no longer
-     a need for the PLT entry.  */
-  if (gotent)
-    {
-      if (splt->size == 0)
-	splt->size = PLT_HEADER_SIZE;
-      h->root.plt.offset = splt->size;
-      splt->size += PLT_ENTRY_SIZE;
-    }
-  else
-    {
-      h->root.needs_plt = 0;
-      h->root.plt.offset = -1;
-    }
+  /* If there weren't any, there's no longer a need for the PLT entry.  */
+  if (!saw_one)
+    h->root.needs_plt = FALSE;
 
   return TRUE;
 }
@@ -2473,12 +2484,12 @@ elf64_alpha_size_plt_section_1 (struct a
 static bfd_boolean
 elf64_alpha_size_plt_section (struct bfd_link_info *info)
 {
-  asection *splt, *spltrel;
+  asection *splt, *spltrel, *sgotplt;
   unsigned long entries;
   bfd *dynobj;
 
   dynobj = elf_hash_table(info)->dynobj;
-  splt = bfd_get_section_by_name(dynobj, ".plt");
+  splt = bfd_get_section_by_name (dynobj, ".plt");
   if (splt == NULL)
     return TRUE;
 
@@ -2490,11 +2501,25 @@ elf64_alpha_size_plt_section (struct bfd
   /* Every plt entry requires a JMP_SLOT relocation.  */
   spltrel = bfd_get_section_by_name (dynobj, ".rela.plt");
   if (splt->size)
-    entries = (splt->size - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
+    {
+      if (elf64_alpha_use_secureplt)
+	entries = (splt->size - NEW_PLT_HEADER_SIZE) / NEW_PLT_ENTRY_SIZE;
+      else
+	entries = (splt->size - OLD_PLT_HEADER_SIZE) / OLD_PLT_ENTRY_SIZE;
+    }
   else
     entries = 0;
   spltrel->size = entries * sizeof (Elf64_External_Rela);
 
+  /* When using the secureplt, we need two words somewhere in the data
+     segment for the dynamic linker to tell us where to go.  This is the
+     entire contents of the .got.plt section.  */
+  if (elf64_alpha_use_secureplt)
+    {
+      sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
+      sgotplt->size = entries ? 16 : 0;
+    }
+
   return TRUE;
 }
 
@@ -2631,6 +2656,11 @@ elf64_alpha_size_rela_got_1 (struct alph
   if (h->root.root.type == bfd_link_hash_warning)
     h = (struct alpha_elf_link_hash_entry *) h->root.root.u.i.link;
 
+  /* If we're using a plt for this symbol, then all of its relocations
+     for its got entries go into .rela.plt.  */
+  if (h->root.needs_plt)
+    return TRUE;
+
   /* If the symbol is dynamic, we'll need all the relocations in their
      natural form.  If this is a shared object, and it has been forced
      local, we'll need the same number of RELATIVE relocations.  */
@@ -2648,11 +2678,6 @@ elf64_alpha_size_rela_got_1 (struct alph
       entries += alpha_dynamic_entries_for_reloc (gotent->reloc_type,
 						  dynamic, info->shared);
 
-  /* If we are using a .plt entry, subtract one, as the first
-     reference uses a .rela.plt entry instead.  */
-  if (h->root.plt.offset != MINUS_ONE)
-    entries--;
-
   if (entries > 0)
     {
       bfd *dynobj = elf_hash_table(info)->dynobj;
@@ -2748,6 +2773,7 @@ elf64_alpha_size_dynamic_sections (bfd *
 				    elf64_alpha_calc_dynrel_sizes, info);
 
       elf64_alpha_size_rela_got_section (info);
+      elf64_alpha_size_plt_section (info);
     }
   /* else we're not dynamic and by definition we don't need such things.  */
 
@@ -2831,6 +2857,10 @@ elf64_alpha_size_dynamic_sections (bfd *
 	      || !add_dynamic_entry (DT_PLTREL, DT_RELA)
 	      || !add_dynamic_entry (DT_JMPREL, 0))
 	    return FALSE;
+
+	  if (elf64_alpha_use_secureplt
+	      && !add_dynamic_entry (DT_ALPHA_PLTRO, 1))
+	    return FALSE;
 	}
 
       if (!add_dynamic_entry (DT_RELA, 0)
@@ -2862,17 +2892,6 @@ elf64_alpha_size_dynamic_sections (bfd *
    related to Alpha in particular.  They are by David Wall, then of
    DEC WRL.  */
 
-#define OP_LDA		0x08
-#define OP_LDAH		0x09
-#define INSN_JSR	0x68004000
-#define INSN_JSR_MASK	0xfc00c000
-#define OP_LDQ		0x29
-#define OP_BR		0x30
-#define OP_BSR		0x34
-#define INSN_UNOP	0x2ffe0000
-#define INSN_ADDQ	0x40000400
-#define INSN_RDUNIQ	0x0000009e
-
 struct alpha_relax_info
 {
   bfd *abfd;
@@ -4599,9 +4618,10 @@ elf64_alpha_finish_dynamic_symbol (bfd *
 				   struct elf_link_hash_entry *h,
 				   Elf_Internal_Sym *sym)
 {
+  struct alpha_elf_link_hash_entry *ah = (struct alpha_elf_link_hash_entry *)h;
   bfd *dynobj = elf_hash_table(info)->dynobj;
 
-  if (h->plt.offset != MINUS_ONE)
+  if (h->needs_plt)
     {
       /* Fill in the .plt entry for this symbol.  */
       asection *splt, *sgot, *srel;
@@ -4613,83 +4633,71 @@ elf64_alpha_finish_dynamic_symbol (bfd *
 
       BFD_ASSERT (h->dynindx != -1);
 
-      /* The first .got entry will be updated by the .plt with the
-	 address of the target function.  */
-      gotent = ((struct alpha_elf_link_hash_entry *) h)->got_entries;
-      BFD_ASSERT (gotent && gotent->addend == 0);
-
       splt = bfd_get_section_by_name (dynobj, ".plt");
       BFD_ASSERT (splt != NULL);
       srel = bfd_get_section_by_name (dynobj, ".rela.plt");
       BFD_ASSERT (srel != NULL);
-      sgot = alpha_elf_tdata (gotent->gotobj)->got;
-      BFD_ASSERT (sgot != NULL);
-
-      got_addr = (sgot->output_section->vma
-		  + sgot->output_offset
-		  + gotent->got_offset);
-      plt_addr = (splt->output_section->vma
-		  + splt->output_offset
-		  + h->plt.offset);
-
-      plt_index = (h->plt.offset - PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
 
-      /* Fill in the entry in the procedure linkage table.  */
-      {
-	bfd_vma insn1, insn2, insn3;
-
-	insn1 = PLT_ENTRY_WORD1 | ((-(h->plt.offset + 4) >> 2) & 0x1fffff);
-	insn2 = PLT_ENTRY_WORD2;
-	insn3 = PLT_ENTRY_WORD3;
+      for (gotent = ah->got_entries; gotent ; gotent = gotent->next)
+	if (gotent->reloc_type == R_ALPHA_LITERAL
+	    && gotent->use_count > 0)
+	  {
+	    unsigned int insn;
+	    int disp;
 
-	bfd_put_32 (output_bfd, insn1, splt->contents + h->plt.offset);
-	bfd_put_32 (output_bfd, insn2, splt->contents + h->plt.offset + 4);
-	bfd_put_32 (output_bfd, insn3, splt->contents + h->plt.offset + 8);
-      }
+	    sgot = alpha_elf_tdata (gotent->gotobj)->got;
+	    BFD_ASSERT (sgot != NULL);
 
-      /* Fill in the entry in the .rela.plt section.  */
-      outrel.r_offset = got_addr;
-      outrel.r_info = ELF64_R_INFO(h->dynindx, R_ALPHA_JMP_SLOT);
-      outrel.r_addend = 0;
+	    BFD_ASSERT (gotent->got_offset != -1);
+	    BFD_ASSERT (gotent->plt_offset != -1);
 
-      loc = srel->contents + plt_index * sizeof (Elf64_External_Rela);
-      bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+	    got_addr = (sgot->output_section->vma
+			+ sgot->output_offset
+			+ gotent->got_offset);
+	    plt_addr = (splt->output_section->vma
+			+ splt->output_offset
+			+ gotent->plt_offset);
 
-      if (!h->def_regular)
-	{
-	  /* Mark the symbol as undefined, rather than as defined in the
-	     .plt section.  Leave the value alone.  */
-	  sym->st_shndx = SHN_UNDEF;
-	}
+	    plt_index = (gotent->plt_offset-PLT_HEADER_SIZE) / PLT_ENTRY_SIZE;
 
-      /* Fill in the entries in the .got.  */
-      bfd_put_64 (output_bfd, plt_addr, sgot->contents + gotent->got_offset);
+	    /* Fill in the entry in the procedure linkage table.  */
+	    if (elf64_alpha_use_secureplt)
+	      {
+		disp = (PLT_HEADER_SIZE - 4) - (gotent->plt_offset + 4);
+		insn = INSN_AD (INSN_BR, 31, disp);
+		bfd_put_32 (output_bfd, insn,
+			    splt->contents + gotent->plt_offset);
 
-      /* Subsequent .got entries will continue to bounce through the .plt.  */
-      if (gotent->next)
-	{
-	  srel = bfd_get_section_by_name (dynobj, ".rela.got");
-	  BFD_ASSERT (! info->shared || srel != NULL);
+		plt_index = ((gotent->plt_offset - NEW_PLT_HEADER_SIZE)
+			     / NEW_PLT_ENTRY_SIZE);
+	      }
+	    else
+	      {
+		disp = -(h->plt.offset + 4);
+		insn = INSN_AD (INSN_BR, 28, disp);
+		bfd_put_32 (output_bfd, insn,
+			    splt->contents + gotent->plt_offset);
+		bfd_put_32 (output_bfd, INSN_UNOP,
+			    splt->contents + gotent->plt_offset + 4);
+		bfd_put_32 (output_bfd, INSN_UNOP,
+			    splt->contents + gotent->plt_offset + 8);
 
-	  gotent = gotent->next;
-	  do
-	    {
-	      sgot = alpha_elf_tdata(gotent->gotobj)->got;
-	      BFD_ASSERT(sgot != NULL);
-	      BFD_ASSERT(gotent->addend == 0);
+		plt_index = ((gotent->plt_offset - OLD_PLT_HEADER_SIZE)
+			     / OLD_PLT_ENTRY_SIZE);
+	      }
 
-	      bfd_put_64 (output_bfd, plt_addr,
-		          sgot->contents + gotent->got_offset);
+	    /* Fill in the entry in the .rela.plt section.  */
+	    outrel.r_offset = got_addr;
+	    outrel.r_info = ELF64_R_INFO(h->dynindx, R_ALPHA_JMP_SLOT);
+	    outrel.r_addend = 0;
 
-	      if (info->shared)
-		elf64_alpha_emit_dynrel (output_bfd, info, sgot, srel,
-					 gotent->got_offset, 0,
-					 R_ALPHA_RELATIVE, plt_addr);
+	    loc = srel->contents + plt_index * sizeof (Elf64_External_Rela);
+	    bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
 
-	      gotent = gotent->next;
-	    }
-          while (gotent != NULL);
-	}
+	    /* Fill in the entry in the .got.  */
+	    bfd_put_64 (output_bfd, plt_addr,
+			sgot->contents + gotent->got_offset);
+	  }
     }
   else if (alpha_elf_dynamic_symbol_p (h, info))
     {
@@ -4766,33 +4774,45 @@ elf64_alpha_finish_dynamic_sections (bfd
 
   if (elf_hash_table (info)->dynamic_sections_created)
     {
-      asection *splt;
+      asection *splt, *sgotplt, *srelaplt;
       Elf64_External_Dyn *dyncon, *dynconend;
+      bfd_vma plt_vma, gotplt_vma;
 
       splt = bfd_get_section_by_name (dynobj, ".plt");
+      srelaplt = bfd_get_section_by_name (output_bfd, ".rela.plt");
       BFD_ASSERT (splt != NULL && sdyn != NULL);
 
+      plt_vma = splt->output_section->vma + splt->output_offset;
+
+      gotplt_vma = 0;
+      if (elf64_alpha_use_secureplt)
+	{
+	  sgotplt = bfd_get_section_by_name (dynobj, ".got.plt");
+	  BFD_ASSERT (sgotplt != NULL);
+	  if (sgotplt->size > 0)
+	    gotplt_vma = sgotplt->output_section->vma + sgotplt->output_offset;
+	}
+
       dyncon = (Elf64_External_Dyn *) sdyn->contents;
       dynconend = (Elf64_External_Dyn *) (sdyn->contents + sdyn->size);
       for (; dyncon < dynconend; dyncon++)
 	{
 	  Elf_Internal_Dyn dyn;
-	  const char *name;
-	  asection *s;
 
 	  bfd_elf64_swap_dyn_in (dynobj, dyncon, &dyn);
 
 	  switch (dyn.d_tag)
 	    {
 	    case DT_PLTGOT:
-	      name = ".plt";
-	      goto get_vma;
+	      dyn.d_un.d_ptr
+		= elf64_alpha_use_secureplt ? gotplt_vma : plt_vma;
+	      break;
 	    case DT_PLTRELSZ:
-	      name = ".rela.plt";
-	      goto get_size;
+	      dyn.d_un.d_val = srelaplt ? srelaplt->size : 0;
+	      break;
 	    case DT_JMPREL:
-	      name = ".rela.plt";
-	      goto get_vma;
+	      dyn.d_un.d_ptr = srelaplt ? srelaplt->vma : 0;
+	      break;
 
 	    case DT_RELASZ:
 	      /* My interpretation of the TIS v1.1 ELF document indicates
@@ -4800,36 +4820,69 @@ elf64_alpha_finish_dynamic_sections (bfd
 		 the rest of the BFD does.  It is, however, what the
 		 glibc ld.so wants.  Do this fixup here until we found
 		 out who is right.  */
-	      s = bfd_get_section_by_name (output_bfd, ".rela.plt");
-	      if (s)
-		dyn.d_un.d_val -= s->size;
-	      break;
-
-	    get_vma:
-	      s = bfd_get_section_by_name (output_bfd, name);
-	      dyn.d_un.d_ptr = (s ? s->vma : 0);
-	      break;
-
-	    get_size:
-	      s = bfd_get_section_by_name (output_bfd, name);
-	      dyn.d_un.d_val = s->size;
+	      if (srelaplt)
+		dyn.d_un.d_val -= srelaplt->size;
 	      break;
 	    }
 
 	  bfd_elf64_swap_dyn_out (output_bfd, &dyn, dyncon);
 	}
 
-      /* Initialize the PLT0 entry.  */
+      /* Initialize the plt header.  */
       if (splt->size > 0)
 	{
-	  bfd_put_32 (output_bfd, PLT_HEADER_WORD1, splt->contents);
-	  bfd_put_32 (output_bfd, PLT_HEADER_WORD2, splt->contents + 4);
-	  bfd_put_32 (output_bfd, PLT_HEADER_WORD3, splt->contents + 8);
-	  bfd_put_32 (output_bfd, PLT_HEADER_WORD4, splt->contents + 12);
+	  unsigned int insn;
+	  int ofs;
 
-	  /* The next two words will be filled in by ld.so */
-	  bfd_put_64 (output_bfd, (bfd_vma) 0, splt->contents + 16);
-	  bfd_put_64 (output_bfd, (bfd_vma) 0, splt->contents + 24);
+	  if (elf64_alpha_use_secureplt)
+	    {
+	      ofs = gotplt_vma - (plt_vma + PLT_HEADER_SIZE);
+
+	      insn = INSN_ABC (INSN_SUBQ, 27, 28, 25);
+	      bfd_put_32 (output_bfd, insn, splt->contents);
+
+	      insn = INSN_ABO (INSN_LDAH, 28, 28, (ofs + 0x8000) >> 16);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 4);
+
+	      insn = INSN_ABC (INSN_S4SUBQ, 25, 25, 25);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 8);
+
+	      insn = INSN_ABO (INSN_LDA, 28, 28, ofs);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 12);
+
+	      insn = INSN_ABO (INSN_LDQ, 27, 28, 0);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 16);
+
+	      insn = INSN_ABC (INSN_ADDQ, 25, 25, 25);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 20);
+
+	      insn = INSN_ABO (INSN_LDQ, 28, 28, 8);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 24);
+
+	      insn = INSN_AB (INSN_JMP, 31, 27);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 28);
+
+	      insn = INSN_AD (INSN_BR, 28, -PLT_HEADER_SIZE);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 32);
+	    }
+	  else
+	    {
+	      insn = INSN_AD (INSN_BR, 27, 0);	/* br $27, .+4 */
+	      bfd_put_32 (output_bfd, insn, splt->contents);
+
+	      insn = INSN_ABO (INSN_LDQ, 27, 27, 12);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 4);
+
+	      insn = INSN_UNOP;
+	      bfd_put_32 (output_bfd, insn, splt->contents + 8);
+
+	      insn = INSN_AB (INSN_JMP, 27, 27);
+	      bfd_put_32 (output_bfd, insn, splt->contents + 12);
+
+	      /* The next two words will be filled in by ld.so.  */
+	      bfd_put_64 (output_bfd, 0, splt->contents + 16);
+	      bfd_put_64 (output_bfd, 0, splt->contents + 24);
+	    }
 
 	  elf_section_data (splt->output_section)->this_hdr.sh_entsize = 0;
 	}
Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.297
diff -u -p -d -r1.297 readelf.c
--- binutils/readelf.c	24 May 2005 13:53:30 -0000	1.297
+++ binutils/readelf.c	28 May 2005 23:59:24 -0000
@@ -1471,6 +1471,17 @@ get_ia64_dynamic_type (unsigned long typ
 }
 
 static const char *
+get_alpha_dynamic_type (unsigned long type)
+{
+  switch (type)
+    {
+    case DT_ALPHA_PLTRO: return "ALPHA_PLTRO";
+    default:
+      return NULL;
+    }
+}
+
+static const char *
 get_dynamic_type (unsigned long type)
 {
   static char buff[64];
@@ -1572,6 +1583,9 @@ get_dynamic_type (unsigned long type)
 	    case EM_IA_64:
 	      result = get_ia64_dynamic_type (type);
 	      break;
+	    case EM_ALPHA:
+	      result = get_alpha_dynamic_type (type);
+	      break;
 	    default:
 	      result = NULL;
 	      break;
Index: include/elf/alpha.h
===================================================================
RCS file: /cvs/src/src/include/elf/alpha.h,v
retrieving revision 1.10
diff -u -p -d -r1.10 alpha.h
--- include/elf/alpha.h	10 May 2005 10:21:10 -0000	1.10
+++ include/elf/alpha.h	28 May 2005 23:59:24 -0000
@@ -63,6 +63,9 @@ typedef struct
 #define STO_ALPHA_NOPV		0x80
 #define STO_ALPHA_STD_GPLOAD	0x88
 
+/* Special values for Elf64_Dyn tag.  */
+#define DT_ALPHA_PLTRO		DT_LOPROC
+
 #include "elf/reloc-macros.h"
 
 /* Alpha relocs.  */
Index: ld/emulparams/elf64alpha.sh
===================================================================
RCS file: /cvs/src/src/ld/emulparams/elf64alpha.sh,v
retrieving revision 1.12
diff -u -p -d -r1.12 elf64alpha.sh
--- ld/emulparams/elf64alpha.sh	30 May 2003 15:50:11 -0000	1.12
+++ ld/emulparams/elf64alpha.sh	28 May 2005 23:59:24 -0000
@@ -12,7 +12,13 @@ ARCH=alpha
 MACHINE=
 GENERATE_SHLIB_SCRIPT=yes
 GENERATE_PIE_SCRIPT=yes
-DATA_PLT=
+
+# Yes, we want duplicate .plt sections.  The linker chooses the
+# appropriate one magically in alpha_after_open.
+PLT=".plt          ${RELOCATING-0} : SPECIAL { *(.plt) }"
+DATA_PLT=yes
+TEXT_PLT=yes
+
 # Note that the number is always big-endian, thus we have to 
 # reverse the digit string.
 NOP=0x0000fe2f1f04ff47		# unop; nop
Index: ld/emultempl/alphaelf.em
===================================================================
RCS file: /cvs/src/src/ld/emultempl/alphaelf.em,v
retrieving revision 1.6
diff -u -p -d -r1.6 alphaelf.em
--- ld/emultempl/alphaelf.em	12 May 2005 07:32:03 -0000	1.6
+++ ld/emultempl/alphaelf.em	28 May 2005 23:59:24 -0000
@@ -1,5 +1,5 @@
 # This shell script emits a C file. -*- C -*-
-#   Copyright 2003, 2004 Free Software Foundation, Inc.
+#   Copyright 2003, 2004, 2005 Free Software Foundation, Inc.
 #
 # This file is part of GLD, the Gnu Linker.
 #
@@ -27,15 +27,54 @@ cat >>e${EMULATION_NAME}.c <<EOF
 #include "elf/alpha.h"
 #include "elf-bfd.h"
 
-static int elf64alpha_32bit = 0;
+static bfd_boolean limit_32bit;
+static bfd_boolean disable_relaxation;
+
+extern bfd_boolean elf64_alpha_use_secureplt;
+extern const bfd_target bfd_elf64_alpha_vec;
+extern const bfd_target bfd_elf64_alpha_freebsd_vec;
+
 
 /* Set the start address as in the Tru64 ld.  */
 #define ALPHA_TEXT_START_32BIT 0x12000000
 
 static void
+alpha_after_open (void)
+{
+  if (link_info.hash->creator == &bfd_elf64_alpha_vec
+      || link_info.hash->creator == &bfd_elf64_alpha_freebsd_vec)
+    {
+      unsigned int num_plt;
+      lang_output_section_statement_type *os;
+      lang_output_section_statement_type *plt_os[2];
+
+      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 (num_plt == 2)
+	{
+	  plt_os[0]->constraint = elf64_alpha_use_secureplt ? 0 : -1;
+	  plt_os[1]->constraint = elf64_alpha_use_secureplt ? -1 : 0;
+	}
+    }
+
+  gld${EMULATION_NAME}_after_open ();
+}
+
+static void
 alpha_after_parse (void)
 {
-  if (elf64alpha_32bit && !link_info.shared && !link_info.relocatable)
+  if (limit_32bit && !link_info.shared && !link_info.relocatable)
     lang_section_start (".interp",
 			exp_binop ('+',
 				   exp_intop (ALPHA_TEXT_START_32BIT),
@@ -44,9 +83,20 @@ alpha_after_parse (void)
 }
 
 static void
+alpha_before_allocation (void)
+{
+  /* Call main function; we're just extending it.  */
+  gld${EMULATION_NAME}_before_allocation ();
+
+  /* Add -relax if -O, not -r, and not explicitly disabled.  */
+  if (link_info.optimize && !link_info.relocatable && !disable_relaxation)
+    command_line.relax = TRUE;
+}
+
+static void
 alpha_finish (void)
 {
-  if (elf64alpha_32bit)
+  if (limit_32bit)
     elf_elfheader (output_bfd)->e_flags |= EF_ALPHA_32BIT;
 
   gld${EMULATION_NAME}_finish ();
@@ -57,25 +107,47 @@ EOF
 # parse_args and list_options functions.
 #
 PARSE_AND_LIST_PROLOGUE='
-#define OPTION_TASO            300
+#define OPTION_TASO		300
+#define OPTION_NO_RELAX		(OPTION_TASO + 1)
+#define OPTION_SECUREPLT	(OPTION_NO_RELAX + 1)
+#define OPTION_NO_SECUREPLT	(OPTION_SECUREPLT + 1)
 '
 
 PARSE_AND_LIST_LONGOPTS='
-  {"taso", no_argument, NULL, OPTION_TASO},
+  { "taso", no_argument, NULL, OPTION_TASO },
+  { "no-relax", no_argument, NULL, OPTION_NO_RELAX },
+  { "secureplt", no_argument, NULL, OPTION_SECUREPLT },
+  { "no-secureplt", no_argument, NULL, OPTION_NO_SECUREPLT },
 '
 
 PARSE_AND_LIST_OPTIONS='
-  fprintf (file, _("  -taso\t\t\tLoad executable in the lower 31-bit addressable\n"));
-  fprintf (file, _("\t\t\t  virtual address range\n"));
+  fprintf (file, _("\
+  --taso		Load executable in the lower 31-bit addressable\n\
+			virtual address range.\n\
+  --no-relax		Do not relax call and gp sequences.\n\
+  --secureplt		Force PLT in text segment.\n\
+  --no-secureplt	Force PLT in data segment.\n\
+"));
 '
 
 PARSE_AND_LIST_ARGS_CASES='
     case OPTION_TASO:
-      elf64alpha_32bit = 1;
+      limit_32bit = 1;
+      break;
+    case OPTION_NO_RELAX:
+      disable_relaxation = TRUE;
+      break;
+    case OPTION_SECUREPLT:
+      elf64_alpha_use_secureplt = TRUE;
+      break;
+    case OPTION_NO_SECUREPLT:
+      elf64_alpha_use_secureplt = FALSE;
       break;
 '
 
 # Put these extra alpha routines in ld_${EMULATION_NAME}_emulation
 #
+LDEMUL_AFTER_OPEN=alpha_after_open
 LDEMUL_AFTER_PARSE=alpha_after_parse
+LDEMUL_BEFORE_ALLOCATION=alpha_before_allocation
 LDEMUL_FINISH=alpha_finish
Index: ld/scripttempl/elf.sc
===================================================================
RCS file: /cvs/src/src/ld/scripttempl/elf.sc,v
retrieving revision 1.57
diff -u -p -d -r1.57 elf.sc
--- ld/scripttempl/elf.sc	11 May 2005 14:10:10 -0000	1.57
+++ ld/scripttempl/elf.sc	28 May 2005 23:59:25 -0000
@@ -104,6 +104,7 @@ INTERP=".interp       ${RELOCATING-0} : 
 if test -z "$PLT"; then
   PLT=".plt          ${RELOCATING-0} : { *(.plt) }"
 fi
+test -n "${DATA_PLT-${BSS_PLT-text}}" && TEXT_PLT=yes
 if test -z "$GOT"; then
   if test -z "$SEPARATE_GOTPLT"; then
     GOT=".got          ${RELOCATING-0} : { *(.got.plt) *(.got) }"
@@ -302,7 +303,7 @@ cat <<EOF
     ${RELOCATING+${INIT_END}}
   } =${NOP-0}
 
-  ${DATA_PLT-${BSS_PLT-${PLT}}}
+  ${TEXT_PLT+${PLT}}
   .text         ${RELOCATING-0} :
   {
     ${RELOCATING+${TEXT_START_SYMBOLS}}



More information about the Binutils mailing list