From 82bd7b59c94c3fbb5ee9186e7c08b350ea073375 Mon Sep 17 00:00:00 2001
From: Alan Modra <amodra@gmail.com>
Date: Mon, 18 Feb 2002 12:40:28 +0000
Subject: [PATCH] 	* elf64-ppc.c (STFD_FR0_0R1, LFD_FR0_0R1, BLR):
 Define. 	(struct ppc_link_hash_table): Add sfpr. 
 (ppc64_elf_link_hash_table_create): Init it. 
 (ppc64_elf_create_dynamic_sections): Split creation of .stub and 	.glink
 out to.. 	(create_linkage_sections): ..here.  Make .sfpr too. 
 (ppc64_elf_check_relocs): Call create_linkage_sections, and set 	dynobj
 early. 	(MIN_SAVE_FPR, MAX_SAVE_FPR): Define. 
 (ppc64_elf_func_desc_adjust): Look for missing ._savef* and 	._restf*
 functions, and create as needed. 	(func_desc_adjust): Only force_local
 for shared libs.

	* emulparams/elf64ppc.sh (OTHER_TEXT_SECTIONS): Define.
---
 bfd/ChangeLog             |  15 ++++
 bfd/elf64-ppc.c           | 182 +++++++++++++++++++++++++++++++-------
 ld/ChangeLog              |   4 +
 ld/emulparams/elf64ppc.sh |   1 +
 4 files changed, 171 insertions(+), 31 deletions(-)

diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 8dfccdd2e0b..ff86ca3e69a 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,3 +1,18 @@
+2002-02-18  Alan Modra  <amodra@bigpond.net.au>
+
+	* elf64-ppc.c (STFD_FR0_0R1, LFD_FR0_0R1, BLR): Define.
+	(struct ppc_link_hash_table): Add sfpr.
+	(ppc64_elf_link_hash_table_create): Init it.
+	(ppc64_elf_create_dynamic_sections): Split creation of .stub and
+	.glink out to..
+	(create_linkage_sections): ..here.  Make .sfpr too.
+	(ppc64_elf_check_relocs): Call create_linkage_sections, and set
+	dynobj early.
+	(MIN_SAVE_FPR, MAX_SAVE_FPR): Define.
+	(ppc64_elf_func_desc_adjust): Look for missing ._savef* and
+	._restf* functions, and create as needed.
+	(func_desc_adjust): Only force_local for shared libs.
+
 2002-02-18  David O'Brien  <obrien@FreeBSD.org>
 
 	* configure.in: Bump version number post 2.12 branching.
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index cca9b7f2bda..08232a304dd 100644
--- a/bfd/elf64-ppc.c
+++ b/bfd/elf64-ppc.c
@@ -51,6 +51,8 @@ static struct bfd_hash_entry *link_hash_newfunc
   PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
 static struct bfd_link_hash_table *ppc64_elf_link_hash_table_create
   PARAMS ((bfd *));
+static boolean create_linkage_sections
+  PARAMS ((bfd *, struct bfd_link_info *));
 static boolean create_got_section
   PARAMS ((bfd *, struct bfd_link_info *));
 static boolean ppc64_elf_create_dynamic_sections
@@ -153,6 +155,11 @@ static boolean ppc64_elf_finish_dynamic_sections
 #define LIS_R0_0	0x3c000000	/* lis   %r0,0		*/
 #define ORI_R0_R0_0	0x60000000	/* ori	 %r0,%r0,0	*/
 
+/* Instructions to save and restore floating point regs.  */
+#define STFD_FR0_0R1	0xd8010000	/* stfd  %fr0,0(%r1)	*/
+#define LFD_FR0_0R1	0xc8010000	/* lfd   %fr0,0(%r1)	*/
+#define BLR		0x4e800020	/* blr			*/
+
 /* Since .opd is an array of descriptors and each entry will end up
    with identical R_PPC64_RELATIVE relocs, there is really no need to
    propagate .opd relocs;  The dynamic linker should be taught to
@@ -1719,6 +1726,7 @@ struct ppc_link_hash_table
   asection *srelbss;
   asection *sstub;
   asection *sglink;
+  asection *sfpr;
 
   /* Set on error.  */
   int plt_overflow;
@@ -1790,12 +1798,49 @@ ppc64_elf_link_hash_table_create (abfd)
   htab->srelbss = NULL;
   htab->sstub = NULL;
   htab->sglink = NULL;
+  htab->sfpr = NULL;
   htab->plt_overflow = 0;
   htab->sym_sec.abfd = NULL;
 
   return &htab->elf.root;
 }
 
+/* Create sections for linker generated code.  */
+
+static boolean
+create_linkage_sections (dynobj, info)
+     bfd *dynobj;
+     struct bfd_link_info *info;
+{
+  struct ppc_link_hash_table *htab;
+  flagword flags;
+
+  htab = ppc_hash_table (info);
+
+  /* Create .sfpr for code to save and restore fp regs.  */
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
+	   | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
+  htab->sfpr = bfd_make_section (dynobj, ".sfpr");
+  if (htab->sfpr == NULL
+      || ! bfd_set_section_flags (dynobj, htab->sfpr, flags)
+      || ! bfd_set_section_alignment (dynobj, htab->sfpr, 2))
+    return false;
+
+  /* Create .stub and .glink for global linkage functions.  */
+  htab->sstub = bfd_make_section (dynobj, ".stub");
+  if (htab->sstub == NULL
+      || ! bfd_set_section_flags (dynobj, htab->sstub, flags)
+      || ! bfd_set_section_alignment (dynobj, htab->sstub, 2))
+    return false;
+  htab->sglink = bfd_make_section (dynobj, ".glink");
+  if (htab->sglink == NULL
+      || ! bfd_set_section_flags (dynobj, htab->sglink, flags)
+      || ! bfd_set_section_alignment (dynobj, htab->sglink, 2))
+    return false;
+
+  return true;
+}
+
 /* Create .got and .rela.got sections in DYNOBJ, and set up
    shortcuts to them in our hash table.  */
 
@@ -1825,8 +1870,7 @@ create_got_section (dynobj, info)
   return true;
 }
 
-/* Create the .stub and .glink sections as well as the ordinary
-   dynamic sections.  */
+/* Create the dynamic sections, and set up shortcuts.  */
 
 static boolean
 ppc64_elf_create_dynamic_sections (dynobj, info)
@@ -1834,7 +1878,6 @@ ppc64_elf_create_dynamic_sections (dynobj, info)
      struct bfd_link_info *info;
 {
   struct ppc_link_hash_table *htab;
-  flagword flags;
 
   htab = ppc_hash_table (info);
   if (!htab->sgot && !create_got_section (dynobj, info))
@@ -1853,20 +1896,6 @@ ppc64_elf_create_dynamic_sections (dynobj, info)
       || (!info->shared && !htab->srelbss))
     abort ();
 
-  /* Create .stub and .glink for global linkage functions.  */
-  flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
-	   | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED);
-  htab->sstub = bfd_make_section (dynobj, ".stub");
-  if (htab->sstub == NULL
-      || ! bfd_set_section_flags (dynobj, htab->sstub, flags)
-      || ! bfd_set_section_alignment (dynobj, htab->sstub, 2))
-    return false;
-  htab->sglink = bfd_make_section (dynobj, ".glink");
-  if (htab->sglink == NULL
-      || ! bfd_set_section_flags (dynobj, htab->sglink, flags)
-      || ! bfd_set_section_alignment (dynobj, htab->sglink, 3))
-    return false;
-
   return true;
 }
 
@@ -1955,6 +1984,12 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
   sreloc = NULL;
   is_opd = strcmp (bfd_get_section_name (abfd, sec), ".opd") == 0;
 
+  if (htab->elf.dynobj == NULL)
+    htab->elf.dynobj = abfd;
+  if (htab->sfpr == NULL
+      && !create_linkage_sections (htab->elf.dynobj, info))
+    return false;
+
   rel_end = relocs + sec->reloc_count;
   for (rel = relocs; rel < rel_end; rel++)
     {
@@ -1980,13 +2015,9 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
 	case R_PPC64_GOT16_LO_DS:
 
 	  /* This symbol requires a global offset table entry.  */
-	  if (htab->sgot == NULL)
-	    {
-	      if (htab->elf.dynobj == NULL)
-		htab->elf.dynobj = abfd;
-	      if (!create_got_section (htab->elf.dynobj, info))
-		return false;
-	    }
+	  if (htab->sgot == NULL
+	      && !create_got_section (htab->elf.dynobj, info))
+	    return false;
 
 	  if (h != NULL)
 	    {
@@ -2197,9 +2228,6 @@ ppc64_elf_check_relocs (abfd, info, sec, relocs)
 		      bfd_set_error (bfd_error_bad_value);
 		    }
 
-		  if (htab->elf.dynobj == NULL)
-		    htab->elf.dynobj = abfd;
-
 		  dynobj = htab->elf.dynobj;
 		  sreloc = bfd_get_section_by_name (dynobj, name);
 		  if (sreloc == NULL)
@@ -2554,25 +2582,117 @@ func_desc_adjust (h, inf)
 	 been imported from another library.  Function code syms that
 	 are really in the library we must leave global to prevent the
 	 linker dragging in a definition from a static library.  */
-      force_local = (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0;
+      force_local = ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0
+		     && info->shared);
       _bfd_elf_link_hash_hide_symbol (info, h, force_local);
     }
 
   return true;
 }
 
+#define MIN_SAVE_FPR 14
+#define MAX_SAVE_FPR 31
+
 /* Called near the start of bfd_elf_size_dynamic_sections.  We use
-   this hook to transfer dynamic linking information gathered so far
-   on function code symbol entries, to their corresponding function
-   descriptor symbol entries.  */
+   this hook to a) provide some gcc support functions, and b) transfer
+   dynamic linking information gathered so far on function code symbol
+   entries, to their corresponding function descriptor symbol entries.  */
 static boolean
 ppc64_elf_func_desc_adjust (obfd, info)
      bfd *obfd ATTRIBUTE_UNUSED;
      struct bfd_link_info *info;
 {
   struct ppc_link_hash_table *htab;
+  unsigned int lowest_savef = MAX_SAVE_FPR + 2;
+  unsigned int lowest_restf = MAX_SAVE_FPR + 2;
+  unsigned int i;
+  struct elf_link_hash_entry *h;
+  char sym[10];
 
   htab = ppc_hash_table (info);
+
+  if (htab->sfpr == NULL)
+    /* We don't have any relocs.  */
+    return true;
+
+  /* First provide any missing ._savef* and ._restf* functions.  */
+  memcpy (sym, "._savef14", 10);
+  for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++)
+    {
+      sym[7] = i / 10 + '0';
+      sym[8] = i % 10 + '0';
+      h = elf_link_hash_lookup (&htab->elf, sym, false, false, true);
+      if (h != NULL
+	  && h->root.type == bfd_link_hash_undefined)
+	{
+	  if (lowest_savef > i)
+	    lowest_savef = i;
+	  h->root.type = bfd_link_hash_defined;
+	  h->root.u.def.section = htab->sfpr;
+	  h->root.u.def.value = (i - lowest_savef) * 4;
+	  h->type = STT_FUNC;
+	  h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+	  _bfd_elf_link_hash_hide_symbol (info, h, info->shared);
+	}
+    }
+
+  memcpy (sym, "._restf14", 10);
+  for (i = MIN_SAVE_FPR; i <= MAX_SAVE_FPR; i++)
+    {
+      sym[7] = i / 10 + '0';
+      sym[8] = i % 10 + '0';
+      h = elf_link_hash_lookup (&htab->elf, sym, false, false, true);
+      if (h != NULL
+	  && h->root.type == bfd_link_hash_undefined)
+	{
+	  if (lowest_restf > i)
+	    lowest_restf = i;
+	  h->root.type = bfd_link_hash_defined;
+	  h->root.u.def.section = htab->sfpr;
+	  h->root.u.def.value = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4
+				 + (i - lowest_restf) * 4);
+	  h->type = STT_FUNC;
+	  h->elf_link_hash_flags |= ELF_LINK_HASH_DEF_REGULAR;
+	  _bfd_elf_link_hash_hide_symbol (info, h, info->shared);
+	}
+    }
+
+  htab->sfpr->_raw_size = ((MAX_SAVE_FPR + 2 - lowest_savef) * 4
+			   + (MAX_SAVE_FPR + 2 - lowest_restf) * 4);
+
+  if (htab->sfpr->_raw_size == 0)
+    {
+      _bfd_strip_section_from_output (info, htab->sfpr);
+    }
+  else
+    {
+      bfd_byte *p = (bfd_byte *) bfd_alloc (htab->elf.dynobj,
+					    htab->sfpr->_raw_size);
+      if (p == NULL)
+	return false;
+      htab->sfpr->contents = p;
+
+      for (i = lowest_savef; i <= MAX_SAVE_FPR; i++)
+	{
+	  unsigned int fpr = i << 21;
+	  unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8;
+	  bfd_put_32 (htab->elf.dynobj, STFD_FR0_0R1 + fpr + stackoff, p);
+	  p += 4;
+	}
+      bfd_put_32 (htab->elf.dynobj, BLR, p);
+      p += 4;
+
+      for (i = lowest_restf; i <= MAX_SAVE_FPR; i++)
+	{
+	  unsigned int fpr = i << 21;
+	  unsigned int stackoff = (1 << 16) - (MAX_SAVE_FPR + 1 - i) * 8;
+	  bfd_put_32 (htab->elf.dynobj, LFD_FR0_0R1 + fpr + stackoff, p);
+	  p += 4;
+	}
+      bfd_put_32 (htab->elf.dynobj, BLR, p);
+      p += 4;
+    }
+
   elf_link_hash_traverse (&htab->elf, func_desc_adjust, (PTR) info);
   return true;
 }
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 97ddebbb77c..2d388c48f53 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,7 @@
+2002-02-18  Alan Modra  <amodra@bigpond.net.au>
+
+	* emulparams/elf64ppc.sh (OTHER_TEXT_SECTIONS): Define.
+
 2002-02-18  David O'Brien  <obrien@FreeBSD.org>
 
 	* Makefile.in: Add new files earmelf_fbsd, eelf32ppc_fbsd,
diff --git a/ld/emulparams/elf64ppc.sh b/ld/emulparams/elf64ppc.sh
index 2169862348c..a6b3ec5438d 100644
--- a/ld/emulparams/elf64ppc.sh
+++ b/ld/emulparams/elf64ppc.sh
@@ -15,6 +15,7 @@ CTOR_START='PROVIDE (__CTOR_LIST__ = .); PROVIDE (___CTOR_LIST__ = .);'
 CTOR_END='PROVIDE (__CTOR_END__ = .); PROVIDE (___CTOR_END__ = .);'
 DTOR_START='PROVIDE (__DTOR_LIST__ = .); PROVIDE (___DTOR_LIST__ = .);'
 DTOR_END='PROVIDE (__DTOR_END__ = .); PROVIDE (___DTOR_END__ = .);'
+OTHER_TEXT_SECTIONS="*(.sfpr .glink)"
 BSS_PLT=
 OTHER_BSS_SYMBOLS="
   .tocbss	${RELOCATING-0}${RELOCATING+ALIGN(8)} : { *(.tocbss)}"