* elf-bfd.h (_bfd_elf_section_from_bfd_section): Update prototype.
[deliverable/binutils-gdb.git] / bfd / elf32-spu.c
index 9073b634f434e72e3a6bcb96edd58da3ec160835..93fe0a4f94e1c2a436381c4f96770743b1162b70 100644 (file)
@@ -1,6 +1,6 @@
 /* SPU specific support for 32-bit ELF
 
-   Copyright 2006, 2007 Free Software Foundation, Inc.
+   Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -257,22 +257,20 @@ struct spu_link_hash_table
 {
   struct elf_link_hash_table elf;
 
-  /* The stub hash table.  */
-  struct bfd_hash_table stub_hash_table;
-
   /* Shortcuts to overlay sections.  */
-  asection *stub;
   asection *ovtab;
+  asection *toe;
+  asection **ovl_sec;
 
-  struct elf_link_hash_entry *ovly_load;
+  /* Count of stubs in each overlay section.  */
+  unsigned int *stub_count;
 
-  /* An array of two output sections per overlay region, chosen such that
-     the first section vma is the overlay buffer vma (ie. the section has
-     the lowest vma in the group that occupy the region), and the second
-     section vma+size specifies the end of the region.  We keep pointers
-     to sections like this because section vmas may change when laying
-     them out.  */
-  asection **ovl_region;
+  /* The stub section for each overlay section.  */
+  asection **stub_sec;
+
+  struct elf_link_hash_entry *ovly_load;
+  struct elf_link_hash_entry *ovly_return;
+  unsigned long ovly_load_r_symndx;
 
   /* Number of overlay buffers.  */
   unsigned int num_buf;
@@ -288,7 +286,7 @@ struct spu_link_hash_table
   unsigned int non_overlay_stubs : 1;
 
   /* Set on error.  */
-  unsigned int stub_overflow : 1;
+  unsigned int stub_err : 1;
 
   /* Set if stack size analysis should be done.  */
   unsigned int stack_analysis : 1;
@@ -297,54 +295,18 @@ struct spu_link_hash_table
   unsigned int emit_stack_syms : 1;
 };
 
-#define spu_hash_table(p) \
-  ((struct spu_link_hash_table *) ((p)->hash))
+/* Hijack the generic got fields for overlay stub accounting.  */
 
-struct spu_stub_hash_entry
+struct got_entry
 {
-  struct bfd_hash_entry root;
-
-  /* Destination of this stub.  */
-  asection *target_section;
-  bfd_vma target_off;
-
-  /* Offset of entry in stub section.  */
-  bfd_vma off;
-
-  /* Offset from this stub to stub that loads the overlay index.  */
-  bfd_vma delta;
+  struct got_entry *next;
+  unsigned int ovl;
+  bfd_vma addend;
+  bfd_vma stub_addr;
 };
 
-/* Create an entry in a spu stub hash table.  */
-
-static struct bfd_hash_entry *
-stub_hash_newfunc (struct bfd_hash_entry *entry,
-                  struct bfd_hash_table *table,
-                  const char *string)
-{
-  /* Allocate the structure if it has not already been allocated by a
-     subclass.  */
-  if (entry == NULL)
-    {
-      entry = bfd_hash_allocate (table, sizeof (struct spu_stub_hash_entry));
-      if (entry == NULL)
-       return entry;
-    }
-
-  /* Call the allocation method of the superclass.  */
-  entry = bfd_hash_newfunc (entry, table, string);
-  if (entry != NULL)
-    {
-      struct spu_stub_hash_entry *sh = (struct spu_stub_hash_entry *) entry;
-
-      sh->target_section = NULL;
-      sh->target_off = 0;
-      sh->off = 0;
-      sh->delta = 0;
-    }
-
-  return entry;
-}
+#define spu_hash_table(p) \
+  ((struct spu_link_hash_table *) ((p)->hash))
 
 /* Create a spu ELF linker hash table.  */
 
@@ -365,28 +327,16 @@ spu_elf_link_hash_table_create (bfd *abfd)
       return NULL;
     }
 
-  /* Init the stub hash table too.  */
-  if (!bfd_hash_table_init (&htab->stub_hash_table, stub_hash_newfunc,
-                           sizeof (struct spu_stub_hash_entry)))
-    return NULL;
-
-  memset (&htab->stub, 0,
-         sizeof (*htab) - offsetof (struct spu_link_hash_table, stub));
+  memset (&htab->ovtab, 0,
+         sizeof (*htab) - offsetof (struct spu_link_hash_table, ovtab));
 
+  htab->elf.init_got_refcount.refcount = 0;
+  htab->elf.init_got_refcount.glist = NULL;
+  htab->elf.init_got_offset.offset = 0;
+  htab->elf.init_got_offset.glist = NULL;
   return &htab->elf.root;
 }
 
-/* Free the derived linker hash table.  */
-
-static void
-spu_elf_link_hash_table_free (struct bfd_link_hash_table *hash)
-{
-  struct spu_link_hash_table *ret = (struct spu_link_hash_table *) hash;
-
-  bfd_hash_table_free (&ret->stub_hash_table);
-  _bfd_generic_link_hash_table_free (hash);
-}
-
 /* Find the symbol for the given R_SYMNDX in IBFD and set *HP and *SYMP
    to (hash, NULL) for global symbols, and (NULL, sym) for locals.  Set
    *SYMSECP to the symbol's section.  *LOCSYMSP caches local syms.  */
@@ -459,64 +409,12 @@ get_sym_h (struct elf_link_hash_entry **hp,
        *symp = sym;
 
       if (symsecp != NULL)
-       {
-         asection *symsec = NULL;
-         if ((sym->st_shndx != SHN_UNDEF
-              && sym->st_shndx < SHN_LORESERVE)
-             || sym->st_shndx > SHN_HIRESERVE)
-           symsec = bfd_section_from_elf_index (ibfd, sym->st_shndx);
-         *symsecp = symsec;
-       }
+       *symsecp = bfd_section_from_elf_index (ibfd, sym->st_shndx);
     }
 
   return TRUE;
 }
 
-/* Build a name for an entry in the stub hash table.  We can't use a
-   local symbol name because ld -r might generate duplicate local symbols.  */
-
-static char *
-spu_stub_name (const asection *sym_sec,
-              const struct elf_link_hash_entry *h,
-              const Elf_Internal_Rela *rel)
-{
-  char *stub_name;
-  bfd_size_type len;
-
-  if (h)
-    {
-      len = strlen (h->root.root.string) + 1 + 8 + 1;
-      stub_name = bfd_malloc (len);
-      if (stub_name == NULL)
-       return stub_name;
-
-      sprintf (stub_name, "%s+%x",
-              h->root.root.string,
-              (int) rel->r_addend & 0xffffffff);
-      len -= 8;
-    }
-  else
-    {
-      len = 8 + 1 + 8 + 1 + 8 + 1;
-      stub_name = bfd_malloc (len);
-      if (stub_name == NULL)
-       return stub_name;
-
-      sprintf (stub_name, "%x:%x+%x",
-              sym_sec->id & 0xffffffff,
-              (int) ELF32_R_SYM (rel->r_info) & 0xffffffff,
-              (int) rel->r_addend & 0xffffffff);
-      len = strlen (stub_name);
-    }
-
-  if (stub_name[len - 2] == '+'
-      && stub_name[len - 1] == '0'
-      && stub_name[len] == 0)
-    stub_name[len - 2] = 0;
-
-  return stub_name;
-}
-
 /* Create the note section if not already present.  This is done early so
    that the linker maps the sections to the right place in the output.  */
 
@@ -626,9 +524,7 @@ spu_elf_find_overlays (bfd *output_bfd, struct bfd_link_info *info)
   qsort (alloc_sec, n, sizeof (*alloc_sec), sort_sections);
 
   /* Look for overlapping vmas.  Any with overlap must be overlays.
-     Count them.  Also count the number of overlay regions and for
-     each region save a section from that region with the lowest vma
-     and another section with the highest end vma.  */
+     Count them.  Also count the number of overlay regions.  */
   ovl_end = alloc_sec[0]->vma + alloc_sec[0]->size;
   for (ovl_index = 0, num_buf = 0, i = 1; i < n; i++)
     {
@@ -637,19 +533,24 @@ spu_elf_find_overlays (bfd *output_bfd, struct bfd_link_info *info)
        {
          asection *s0 = alloc_sec[i - 1];
 
-         if (spu_elf_section_data (s0)->ovl_index == 0)
+         if (spu_elf_section_data (s0)->u.o.ovl_index == 0)
            {
-             spu_elf_section_data (s0)->ovl_index = ++ovl_index;
-             alloc_sec[num_buf * 2] = s0;
-             alloc_sec[num_buf * 2 + 1] = s0;
-             num_buf++;
+             alloc_sec[ovl_index] = s0;
+             spu_elf_section_data (s0)->u.o.ovl_index = ++ovl_index;
+             spu_elf_section_data (s0)->u.o.ovl_buf = ++num_buf;
            }
-         spu_elf_section_data (s)->ovl_index = ++ovl_index;
-         if (ovl_end < s->vma + s->size)
+         alloc_sec[ovl_index] = s;
+         spu_elf_section_data (s)->u.o.ovl_index = ++ovl_index;
+         spu_elf_section_data (s)->u.o.ovl_buf = num_buf;
+         if (s0->vma != s->vma)
            {
-             ovl_end = s->vma + s->size;
-             alloc_sec[num_buf * 2 - 1] = s;
+             info->callbacks->einfo (_("%X%P: overlay sections %A and %A "
+                                       "do not start at the same address.\n"),
+                                     s0, s);
+             return FALSE;
            }
+         if (ovl_end < s->vma + s->size)
+           ovl_end = s->vma + s->size;
        }
       else
        ovl_end = s->vma + s->size;
@@ -657,30 +558,22 @@ spu_elf_find_overlays (bfd *output_bfd, struct bfd_link_info *info)
 
   htab->num_overlays = ovl_index;
   htab->num_buf = num_buf;
-  if (ovl_index == 0)
-    {
-      free (alloc_sec);
-      return FALSE;
-    }
-
-  alloc_sec = bfd_realloc (alloc_sec, num_buf * 2 * sizeof (*alloc_sec));
-  if (alloc_sec == NULL)
-    return FALSE;
-
-  htab->ovl_region = alloc_sec;
-  return TRUE;
+  htab->ovl_sec = alloc_sec;
+  return ovl_index != 0;
 }
 
-/* One of these per stub.  */
-#define SIZEOF_STUB1 8
-#define ILA_79 0x4200004f              /* ila $79,function_address */
-#define BR     0x32000000              /* br stub2 */
-
-/* One of these per overlay.  */
-#define SIZEOF_STUB2 8
-#define ILA_78 0x4200004e              /* ila $78,overlay_number */
-                                       /* br __ovly_load */
+/* Support two sizes of overlay stubs, a slower more compact stub of two
+   intructions, and a faster stub of four instructions.  */
+#ifndef OVL_STUB_SIZE
+/* Default to faster.  */
+#define OVL_STUB_SIZE 16
+/* #define OVL_STUB_SIZE 8 */
+#endif
+#define BRSL   0x33000000
+#define BR     0x32000000
 #define NOP    0x40200000
+#define LNOP   0x00200000
+#define ILA    0x42000000
 
 /* Return true for all relative and absolute branch instructions.
    bra   00110000 0..
@@ -698,6 +591,22 @@ is_branch (const unsigned char *insn)
   return (insn[0] & 0xec) == 0x20 && (insn[1] & 0x80) == 0;
 }
 
+/* Return true for all indirect branch instructions.
+   bi     00110101 000
+   bisl   00110101 001
+   iret   00110101 010
+   bisled 00110101 011
+   biz    00100101 000
+   binz   00100101 001
+   bihz   00100101 010
+   bihnz  00100101 011  */
+
+static bfd_boolean
+is_indirect_branch (const unsigned char *insn)
+{
+  return (insn[0] & 0xef) == 0x25 && (insn[1] & 0x80) == 0;
+}
+
 /* Return true for branch hint instructions.
    hbra  0001000..
    hbrr  0001001..  */
@@ -733,14 +642,14 @@ needs_ovl_stub (const char *sym_name,
     return TRUE;
 
   /* Usually, symbols in non-overlay sections don't need stubs.  */
-  if (spu_elf_section_data (sym_sec->output_section)->ovl_index == 0
+  if (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index == 0
       && !htab->non_overlay_stubs)
     return FALSE;
 
   /* A reference from some other section to a symbol in an overlay
      section needs a stub.  */
-  if (spu_elf_section_data (sym_sec->output_section)->ovl_index
-       != spu_elf_section_data (input_section->output_section)->ovl_index)
+  if (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index
+       != spu_elf_section_data (input_section->output_section)->u.o.ovl_index)
     return TRUE;
 
   /* If this insn isn't a branch then we are possibly taking the
@@ -748,128 +657,297 @@ needs_ovl_stub (const char *sym_name,
   return !is_branch;
 }
 
-struct stubarr {
-  struct bfd_hash_table *stub_hash_table;
-  struct spu_stub_hash_entry **sh;
-  unsigned int count;
-  int err;
-};
-
-/* Called via elf_link_hash_traverse to allocate stubs for any _SPUEAR_
-   symbols.  */
+enum _insn_type { non_branch, branch, call };
 
 static bfd_boolean
-allocate_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
+count_stub (struct spu_link_hash_table *htab,
+           bfd *ibfd,
+           asection *isec,
+           enum _insn_type insn_type,
+           struct elf_link_hash_entry *h,
+           const Elf_Internal_Rela *irela)
 {
-  /* Symbols starting with _SPUEAR_ need a stub because they may be
-     invoked by the PPU.  */
-  if ((h->root.type == bfd_link_hash_defined
-       || h->root.type == bfd_link_hash_defweak)
-      && h->def_regular
-      && strncmp (h->root.root.string, "_SPUEAR_", 8) == 0)
+  unsigned int ovl = 0;
+  struct got_entry *g, **head;
+  bfd_vma addend;
+
+  /* If this instruction is a branch or call, we need a stub
+     for it.  One stub per function per overlay.
+     If it isn't a branch, then we are taking the address of
+     this function so need a stub in the non-overlay area
+     for it.  One stub per function.  */
+  if (insn_type != non_branch)
+    ovl = spu_elf_section_data (isec->output_section)->u.o.ovl_index;
+
+  if (h != NULL)
+    head = &h->got.glist;
+  else
     {
-      struct stubarr *stubs = inf;
-      static Elf_Internal_Rela zero_rel;
-      char *stub_name = spu_stub_name (h->root.u.def.section, h, &zero_rel);
-      struct spu_stub_hash_entry *sh;
-
-      if (stub_name == NULL)
+      if (elf_local_got_ents (ibfd) == NULL)
        {
-         stubs->err = 1;
-         return FALSE;
+         bfd_size_type amt = (elf_tdata (ibfd)->symtab_hdr.sh_info
+                              * sizeof (*elf_local_got_ents (ibfd)));
+         elf_local_got_ents (ibfd) = bfd_zmalloc (amt);
+         if (elf_local_got_ents (ibfd) == NULL)
+           return FALSE;
        }
+      head = elf_local_got_ents (ibfd) + ELF32_R_SYM (irela->r_info);
+    }
 
-      sh = (struct spu_stub_hash_entry *)
-       bfd_hash_lookup (stubs->stub_hash_table, stub_name, TRUE, FALSE);
-      if (sh == NULL)
-       {
-         free (stub_name);
-         return FALSE;
-       }
+  addend = 0;
+  if (irela != NULL)
+    addend = irela->r_addend;
+
+  if (ovl == 0)
+    {
+      struct got_entry *gnext;
+
+      for (g = *head; g != NULL; g = g->next)
+       if (g->addend == addend && g->ovl == 0)
+         break;
 
-      /* If this entry isn't new, we already have a stub.  */
-      if (sh->target_section != NULL)
+      if (g == NULL)
        {
-         free (stub_name);
-         return TRUE;
+         /* Need a new non-overlay area stub.  Zap other stubs.  */
+         for (g = *head; g != NULL; g = gnext)
+           {
+             gnext = g->next;
+             if (g->addend == addend)
+               {
+                 htab->stub_count[g->ovl] -= 1;
+                 free (g);
+               }
+           }
        }
+    }
+  else
+    {
+      for (g = *head; g != NULL; g = g->next)
+       if (g->addend == addend && (g->ovl == ovl || g->ovl == 0))
+         break;
+    }
+
+  if (g == NULL)
+    {
+      g = bfd_malloc (sizeof *g);
+      if (g == NULL)
+       return FALSE;
+      g->ovl = ovl;
+      g->addend = addend;
+      g->stub_addr = (bfd_vma) -1;
+      g->next = *head;
+      *head = g;
 
-      sh->target_section = h->root.u.def.section;
-      sh->target_off = h->root.u.def.value;
-      stubs->count += 1;
+      htab->stub_count[ovl] += 1;
     }
-  
+
   return TRUE;
 }
 
-/* Called via bfd_hash_traverse to set up pointers to all symbols
-   in the stub hash table.  */
+/* Two instruction overlay stubs look like:
+
+   brsl $75,__ovly_load
+   .word target_ovl_and_address
+
+   ovl_and_address is a word with the overlay number in the top 14 bits
+   and local store address in the bottom 18 bits.
+
+   Four instruction overlay stubs look like:
+
+   ila $78,ovl_number
+   lnop
+   ila $79,target_address
+   br __ovly_load  */
 
 static bfd_boolean
-populate_stubs (struct bfd_hash_entry *bh, void *inf)
+build_stub (struct spu_link_hash_table *htab,
+           bfd *ibfd,
+           asection *isec,
+           enum _insn_type insn_type,
+           struct elf_link_hash_entry *h,
+           const Elf_Internal_Rela *irela,
+           bfd_vma dest,
+           asection *dest_sec)
 {
-  struct stubarr *stubs = inf;
+  unsigned int ovl;
+  struct got_entry *g, **head;
+  asection *sec;
+  bfd_vma addend, val, from, to;
+
+  ovl = 0;
+  if (insn_type != non_branch)
+    ovl = spu_elf_section_data (isec->output_section)->u.o.ovl_index;
+
+  if (h != NULL)
+    head = &h->got.glist;
+  else
+    head = elf_local_got_ents (ibfd) + ELF32_R_SYM (irela->r_info);
+
+  addend = 0;
+  if (irela != NULL)
+    addend = irela->r_addend;
+
+  for (g = *head; g != NULL; g = g->next)
+    if (g->addend == addend && (g->ovl == ovl || g->ovl == 0))
+      break;
+  if (g == NULL)
+    abort ();
+
+  if (g->ovl == 0 && ovl != 0)
+    return TRUE;
+
+  if (g->stub_addr != (bfd_vma) -1)
+    return TRUE;
+
+  sec = htab->stub_sec[ovl];
+  dest += dest_sec->output_offset + dest_sec->output_section->vma;
+  from = sec->size + sec->output_offset + sec->output_section->vma;
+  g->stub_addr = from;
+  to = (htab->ovly_load->root.u.def.value
+       + htab->ovly_load->root.u.def.section->output_offset
+       + htab->ovly_load->root.u.def.section->output_section->vma);
+  val = to - from;
+  if (OVL_STUB_SIZE == 16)
+    val -= 12;
+  if (((dest | to | from) & 3) != 0
+      || val + 0x20000 >= 0x40000)
+    {
+      htab->stub_err = 1;
+      return FALSE;
+    }
+  ovl = spu_elf_section_data (dest_sec->output_section)->u.o.ovl_index;
+
+  if (OVL_STUB_SIZE == 16)
+    {
+      bfd_put_32 (sec->owner, ILA + ((ovl << 7) & 0x01ffff80) + 78,
+                 sec->contents + sec->size);
+      bfd_put_32 (sec->owner, LNOP,
+                 sec->contents + sec->size + 4);
+      bfd_put_32 (sec->owner, ILA + ((dest << 7) & 0x01ffff80) + 79,
+                 sec->contents + sec->size + 8);
+      bfd_put_32 (sec->owner, BR + ((val << 5) & 0x007fff80),
+                 sec->contents + sec->size + 12);
+    }
+  else if (OVL_STUB_SIZE == 8)
+    {
+      bfd_put_32 (sec->owner, BRSL + ((val << 5) & 0x007fff80) + 75,
+                 sec->contents + sec->size);
+
+      val = (dest & 0x3ffff) | (ovl << 14);
+      bfd_put_32 (sec->owner, val,
+                 sec->contents + sec->size + 4);
+    }
+  else
+    abort ();
+  sec->size += OVL_STUB_SIZE;
+
+  if (htab->emit_stub_syms)
+    {
+      size_t len;
+      char *name;
+      int add;
+
+      len = 8 + sizeof (".ovl_call.") - 1;
+      if (h != NULL)
+       len += strlen (h->root.root.string);
+      else
+       len += 8 + 1 + 8;
+      add = 0;
+      if (irela != NULL)
+       add = (int) irela->r_addend & 0xffffffff;
+      if (add != 0)
+       len += 1 + 8;
+      name = bfd_malloc (len);
+      if (name == NULL)
+       return FALSE;
+
+      sprintf (name, "%08x.ovl_call.", g->ovl);
+      if (h != NULL)
+       strcpy (name + 8 + sizeof (".ovl_call.") - 1, h->root.root.string);
+      else
+       sprintf (name + 8 + sizeof (".ovl_call.") - 1, "%x:%x",
+                dest_sec->id & 0xffffffff,
+                (int) ELF32_R_SYM (irela->r_info) & 0xffffffff);
+      if (add != 0)
+       sprintf (name + len - 9, "+%x", add);
+
+      h = elf_link_hash_lookup (&htab->elf, name, TRUE, TRUE, FALSE);
+      free (name);
+      if (h == NULL)
+       return FALSE;
+      if (h->root.type == bfd_link_hash_new)
+       {
+         h->root.type = bfd_link_hash_defined;
+         h->root.u.def.section = sec;
+         h->root.u.def.value = sec->size - OVL_STUB_SIZE;
+         h->size = OVL_STUB_SIZE;
+         h->type = STT_FUNC;
+         h->ref_regular = 1;
+         h->def_regular = 1;
+         h->ref_regular_nonweak = 1;
+         h->forced_local = 1;
+         h->non_elf = 0;
+       }
+    }
 
-  stubs->sh[--stubs->count] = (struct spu_stub_hash_entry *) bh;
   return TRUE;
 }
 
-/* qsort predicate to sort stubs by overlay number.  */
+/* Called via elf_link_hash_traverse to allocate stubs for any _SPUEAR_
+   symbols.  */
 
-static int
-sort_stubs (const void *a, const void *b)
+static bfd_boolean
+allocate_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
 {
-  const struct spu_stub_hash_entry *const *sa = a;
-  const struct spu_stub_hash_entry *const *sb = b;
-  int i;
-  bfd_signed_vma d;
-
-  i = spu_elf_section_data ((*sa)->target_section->output_section)->ovl_index;
-  i -= spu_elf_section_data ((*sb)->target_section->output_section)->ovl_index;
-  if (i != 0)
-    return i;
-
-  d = ((*sa)->target_section->output_section->vma
-       + (*sa)->target_section->output_offset
-       + (*sa)->target_off
-       - (*sb)->target_section->output_section->vma
-       - (*sb)->target_section->output_offset
-       - (*sb)->target_off);
-  if (d != 0)
-    return d < 0 ? -1 : 1;
-
-  /* Two functions at the same address.  Aliases perhaps.  */
-  i = strcmp ((*sb)->root.string, (*sa)->root.string);
-  BFD_ASSERT (i != 0);
-  return i;
+  /* Symbols starting with _SPUEAR_ need a stub because they may be
+     invoked by the PPU.  */
+  if ((h->root.type == bfd_link_hash_defined
+       || h->root.type == bfd_link_hash_defweak)
+      && h->def_regular
+      && strncmp (h->root.root.string, "_SPUEAR_", 8) == 0)
+    {
+      struct spu_link_hash_table *htab = inf;
+
+      count_stub (htab, NULL, NULL, non_branch, h, NULL);
+    }
+  
+  return TRUE;
 }
 
-/* Allocate space for overlay call and return stubs.  */
+static bfd_boolean
+build_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
+{
+  /* Symbols starting with _SPUEAR_ need a stub because they may be
+     invoked by the PPU.  */
+  if ((h->root.type == bfd_link_hash_defined
+       || h->root.type == bfd_link_hash_defweak)
+      && h->def_regular
+      && strncmp (h->root.root.string, "_SPUEAR_", 8) == 0)
+    {
+      struct spu_link_hash_table *htab = inf;
 
-bfd_boolean
-spu_elf_size_stubs (bfd *output_bfd,
-                   struct bfd_link_info *info,
-                   int non_overlay_stubs,
-                   int stack_analysis,
-                   asection **stub,
-                   asection **ovtab,
-                   asection **toe)
+      build_stub (htab, NULL, NULL, non_branch, h, NULL,
+                 h->root.u.def.value, h->root.u.def.section);
+    }
+  
+  return TRUE;
+}
+
+/* Size or build stubs.  */
+
+static bfd_boolean
+process_stubs (bfd *output_bfd,
+              struct bfd_link_info *info,
+              bfd_boolean build)
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
   bfd *ibfd;
-  struct stubarr stubs;
-  unsigned i, group;
-  flagword flags;
 
-  htab->non_overlay_stubs = non_overlay_stubs;
-  stubs.stub_hash_table = &htab->stub_hash_table;
-  stubs.count = 0;
-  stubs.err = 0;
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
       extern const bfd_target bfd_elf32_spu_vec;
       Elf_Internal_Shdr *symtab_hdr;
-      asection *section;
+      asection *isec;
       Elf_Internal_Sym *local_syms = NULL;
       void *psyms;
 
@@ -883,37 +961,36 @@ spu_elf_size_stubs (bfd *output_bfd,
 
       /* Arrange to read and keep global syms for later stack analysis.  */
       psyms = &local_syms;
-      if (stack_analysis)
+      if (htab->stack_analysis)
        psyms = &symtab_hdr->contents;
 
       /* Walk over each section attached to the input bfd.  */
-      for (section = ibfd->sections; section != NULL; section = section->next)
+      for (isec = ibfd->sections; isec != NULL; isec = isec->next)
        {
          Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
 
          /* If there aren't any relocs, then there's nothing more to do.  */
-         if ((section->flags & SEC_RELOC) == 0
-             || (section->flags & SEC_ALLOC) == 0
-             || (section->flags & SEC_LOAD) == 0
-             || section->reloc_count == 0)
+         if ((isec->flags & SEC_RELOC) == 0
+             || (isec->flags & SEC_ALLOC) == 0
+             || (isec->flags & SEC_LOAD) == 0
+             || isec->reloc_count == 0)
            continue;
 
          /* If this section is a link-once section that will be
             discarded, then don't create any stubs.  */
-         if (section->output_section == NULL
-             || section->output_section->owner != output_bfd)
+         if (isec->output_section == NULL
+             || isec->output_section->owner != output_bfd)
            continue;
 
          /* Get the relocs.  */
-         internal_relocs
-           = _bfd_elf_link_read_relocs (ibfd, section, NULL, NULL,
-                                        info->keep_memory);
+         internal_relocs = _bfd_elf_link_read_relocs (ibfd, isec, NULL, NULL,
+                                                      info->keep_memory);
          if (internal_relocs == NULL)
            goto error_ret_free_local;
 
          /* Now examine each relocation.  */
          irela = internal_relocs;
-         irelaend = irela + section->reloc_count;
+         irelaend = irela + isec->reloc_count;
          for (; irela < irelaend; irela++)
            {
              enum elf_spu_reloc_type r_type;
@@ -922,10 +999,8 @@ spu_elf_size_stubs (bfd *output_bfd,
              Elf_Internal_Sym *sym;
              struct elf_link_hash_entry *h;
              const char *sym_name;
-             char *stub_name;
-             struct spu_stub_hash_entry *sh;
              unsigned int sym_type;
-             enum _insn_type { non_branch, branch, call } insn_type;
+             enum _insn_type insn_type;
 
              r_type = ELF32_R_TYPE (irela->r_info);
              r_indx = ELF32_R_SYM (irela->r_info);
@@ -933,7 +1008,15 @@ spu_elf_size_stubs (bfd *output_bfd,
              if (r_type >= R_SPU_max)
                {
                  bfd_set_error (bfd_error_bad_value);
-                 goto error_ret_free_internal;
+               error_ret_free_internal:
+                 if (elf_section_data (isec)->relocs != internal_relocs)
+                   free (internal_relocs);
+               error_ret_free_local:
+                 if (local_syms != NULL
+                     && (symtab_hdr->contents
+                         != (unsigned char *) local_syms))
+                   free (local_syms);
+                 return FALSE;
                }
 
              /* Determine the reloc target section.  */
@@ -957,7 +1040,7 @@ spu_elf_size_stubs (bfd *output_bfd,
                {
                  unsigned char insn[4];
 
-                 if (!bfd_get_section_contents (ibfd, section, insn,
+                 if (!bfd_get_section_contents (ibfd, isec, insn,
                                                 irela->r_offset, 4))
                    goto error_ret_free_internal;
 
@@ -983,6 +1066,7 @@ spu_elf_size_stubs (bfd *output_bfd,
                                               sym,
                                               sym_sec);
                }
+
              if (sym_type != STT_FUNC)
                {
                  /* It's common for people to write assembly and forget
@@ -995,54 +1079,45 @@ spu_elf_size_stubs (bfd *output_bfd,
                    (*_bfd_error_handler) (_("warning: call to non-function"
                                             " symbol %s defined in %B"),
                                           sym_sec->owner, sym_name);
-                 else
+                 else if (insn_type == non_branch)
                    continue;
                }
 
-             if (!needs_ovl_stub (sym_name, sym_sec, section, htab,
+             if (!needs_ovl_stub (sym_name, sym_sec, isec, htab,
                                   insn_type != non_branch))
                continue;
 
-             stub_name = spu_stub_name (sym_sec, h, irela);
-             if (stub_name == NULL)
-               goto error_ret_free_internal;
-
-             sh = (struct spu_stub_hash_entry *)
-               bfd_hash_lookup (&htab->stub_hash_table, stub_name,
-                                TRUE, FALSE);
-             if (sh == NULL)
+             if (htab->stub_count == NULL)
                {
-                 free (stub_name);
-               error_ret_free_internal:
-                 if (elf_section_data (section)->relocs != internal_relocs)
-                   free (internal_relocs);
-               error_ret_free_local:
-                 if (local_syms != NULL
-                     && (symtab_hdr->contents
-                         != (unsigned char *) local_syms))
-                   free (local_syms);
-                 return FALSE;
+                 bfd_size_type amt;
+                 amt = (htab->num_overlays + 1) * sizeof (*htab->stub_count);
+                 htab->stub_count = bfd_zmalloc (amt);
+                 if (htab->stub_count == NULL)
+                   goto error_ret_free_internal;
                }
 
-             /* If this entry isn't new, we already have a stub.  */
-             if (sh->target_section != NULL)
+             if (!build)
                {
-                 free (stub_name);
-                 continue;
+                 if (!count_stub (htab, ibfd, isec, insn_type, h, irela))
+                   goto error_ret_free_internal;
                }
-
-             sh->target_section = sym_sec;
-             if (h != NULL)
-               sh->target_off = h->root.u.def.value;
              else
-               sh->target_off = sym->st_value;
-             sh->target_off += irela->r_addend;
+               {
+                 bfd_vma dest;
 
-             stubs.count += 1;
+                 if (h != NULL)
+                   dest = h->root.u.def.value;
+                 else
+                   dest = sym->st_value;
+                 dest += irela->r_addend;
+                 if (!build_stub (htab, ibfd, isec, insn_type, h, irela,
+                                  dest, sym_sec))
+                   goto error_ret_free_internal;
+               }
            }
 
          /* We're done with the internal relocs, free them.  */
-         if (elf_section_data (section)->relocs != internal_relocs)
+         if (elf_section_data (isec)->relocs != internal_relocs)
            free (internal_relocs);
        }
 
@@ -1056,93 +1131,64 @@ spu_elf_size_stubs (bfd *output_bfd,
        }
     }
 
-  elf_link_hash_traverse (&htab->elf, allocate_spuear_stubs, &stubs);
-  if (stubs.err)
-    return FALSE;
+  return TRUE;
+}
 
-  *stub = NULL;
-  if (stubs.count == 0)
-    return TRUE;
+/* Allocate space for overlay call and return stubs.  */
+
+int
+spu_elf_size_stubs (bfd *output_bfd,
+                   struct bfd_link_info *info,
+                   void (*place_spu_section) (asection *, asection *,
+                                              const char *),
+                   int non_overlay_stubs)
+{
+  struct spu_link_hash_table *htab = spu_hash_table (info);
+  bfd *ibfd;
+  bfd_size_type amt;
+  flagword flags;
+  unsigned int i;
+  asection *stub;
+
+  htab->non_overlay_stubs = non_overlay_stubs;
+  if (!process_stubs (output_bfd, info, FALSE))
+    return 0;
+
+  elf_link_hash_traverse (&htab->elf, allocate_spuear_stubs, htab);
+  if (htab->stub_err)
+    return 0;
+
+  if (htab->stub_count == NULL)
+    return 1;
 
   ibfd = info->input_bfds;
-  flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
-          | SEC_HAS_CONTENTS | SEC_IN_MEMORY);
-  htab->stub = bfd_make_section_anyway_with_flags (ibfd, ".stub", flags);
-  *stub = htab->stub;
-  if (htab->stub == NULL
-      || !bfd_set_section_alignment (ibfd, htab->stub, 2))
-    return FALSE;
+  amt = (htab->num_overlays + 1) * sizeof (*htab->stub_sec);
+  htab->stub_sec = bfd_zmalloc (amt);
+  if (htab->stub_sec == NULL)
+    return 0;
 
-  flags = (SEC_ALLOC | SEC_LOAD
+  flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY
           | SEC_HAS_CONTENTS | SEC_IN_MEMORY);
-  htab->ovtab = bfd_make_section_anyway_with_flags (ibfd, ".ovtab", flags);
-  *ovtab = htab->ovtab;
-  if (htab->ovtab == NULL
-      || !bfd_set_section_alignment (ibfd, htab->stub, 4))
-    return FALSE;
-
-  *toe = bfd_make_section_anyway_with_flags (ibfd, ".toe", SEC_ALLOC);
-  if (*toe == NULL
-      || !bfd_set_section_alignment (ibfd, *toe, 4))
-    return FALSE;
-  (*toe)->size = 16;
+  stub = bfd_make_section_anyway_with_flags (ibfd, ".stub", flags);
+  htab->stub_sec[0] = stub;
+  if (stub == NULL
+      || !bfd_set_section_alignment (ibfd, stub, 3 + (OVL_STUB_SIZE > 8)))
+    return 0;
+  stub->size = htab->stub_count[0] * OVL_STUB_SIZE;
+  (*place_spu_section) (stub, NULL, ".text");
 
-  /* Retrieve all the stubs and sort.  */
-  stubs.sh = bfd_malloc (stubs.count * sizeof (*stubs.sh));
-  if (stubs.sh == NULL)
-    return FALSE;
-  i = stubs.count;
-  bfd_hash_traverse (&htab->stub_hash_table, populate_stubs, &stubs);
-  BFD_ASSERT (stubs.count == 0);
-
-  stubs.count = i;
-  qsort (stubs.sh, stubs.count, sizeof (*stubs.sh), sort_stubs);
-
-  /* Now that the stubs are sorted, place them in the stub section.
-     Stubs are grouped per overlay
-     .     ila $79,func1
-     .     br 1f
-     .     ila $79,func2
-     .     br 1f
-     .
-     .
-     .     ila $79,funcn
-     .     nop
-     . 1:
-     .     ila $78,ovl_index
-     .     br __ovly_load  */
-
-  group = 0;
-  for (i = 0; i < stubs.count; i++)
+  for (i = 0; i < htab->num_overlays; ++i)
     {
-      if (spu_elf_section_data (stubs.sh[group]->target_section
-                               ->output_section)->ovl_index
-         != spu_elf_section_data (stubs.sh[i]->target_section
-                                  ->output_section)->ovl_index)
-       {
-         htab->stub->size += SIZEOF_STUB2;
-         for (; group != i; group++)
-           stubs.sh[group]->delta
-             = stubs.sh[i - 1]->off - stubs.sh[group]->off;
-       }
-      if (group == i
-         || ((stubs.sh[i - 1]->target_section->output_section->vma
-              + stubs.sh[i - 1]->target_section->output_offset
-              + stubs.sh[i - 1]->target_off)
-             != (stubs.sh[i]->target_section->output_section->vma
-                 + stubs.sh[i]->target_section->output_offset
-                 + stubs.sh[i]->target_off)))
-       {
-         stubs.sh[i]->off = htab->stub->size;
-         htab->stub->size += SIZEOF_STUB1;
-       }
-      else
-       stubs.sh[i]->off = stubs.sh[i - 1]->off;
+      asection *osec = htab->ovl_sec[i];
+      unsigned int ovl = spu_elf_section_data (osec)->u.o.ovl_index;
+      stub = bfd_make_section_anyway_with_flags (ibfd, ".stub", flags);
+      htab->stub_sec[ovl] = stub;
+      if (stub == NULL
+         || !bfd_set_section_alignment (ibfd, stub, 3 + (OVL_STUB_SIZE > 8)))
+       return 0;
+      stub->size = htab->stub_count[ovl] * OVL_STUB_SIZE;
+      (*place_spu_section) (stub, osec, NULL);
     }
-  if (group != i)
-    htab->stub->size += SIZEOF_STUB2;
-  for (; group != i; group++)
-    stubs.sh[group]->delta = stubs.sh[i - 1]->off - stubs.sh[group]->off;
 
  /* htab->ovtab consists of two arrays.
     .  struct {
@@ -1154,12 +1200,27 @@ spu_elf_size_stubs (bfd *output_bfd,
     .
     .  struct {
     .    u32 mapped;
-    .  } _ovly_buf_table[];  */
+    .  } _ovly_buf_table[];
+    .  */
 
-  htab->ovtab->alignment_power = 4;
-  htab->ovtab->size = htab->num_overlays * 16 + htab->num_buf * 4;
+  flags = (SEC_ALLOC | SEC_LOAD
+          | SEC_HAS_CONTENTS | SEC_IN_MEMORY);
+  htab->ovtab = bfd_make_section_anyway_with_flags (ibfd, ".ovtab", flags);
+  if (htab->ovtab == NULL
+      || !bfd_set_section_alignment (ibfd, htab->ovtab, 4))
+    return 0;
 
-  return TRUE;
+  htab->ovtab->size = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
+  (*place_spu_section) (htab->ovtab, NULL, ".data");
+
+  htab->toe = bfd_make_section_anyway_with_flags (ibfd, ".toe", SEC_ALLOC);
+  if (htab->toe == NULL
+      || !bfd_set_section_alignment (ibfd, htab->toe, 4))
+    return 0;
+  htab->toe->size = 16;
+  (*place_spu_section) (htab->toe, NULL, ".toe");
+
+  return 2;
 }
 
 /* Functions to handle embedded spu_ovl.o object.  */
@@ -1208,86 +1269,6 @@ spu_elf_open_builtin_lib (bfd **ovl_bfd, const struct _ovl_stream *stream)
   return *ovl_bfd != NULL;
 }
 
-/* Fill in the ila and br for a stub.  On the last stub for a group,
-   write the stub that sets the overlay number too.  */
-
-static bfd_boolean
-write_one_stub (struct bfd_hash_entry *bh, void *inf)
-{
-  struct spu_stub_hash_entry *ent = (struct spu_stub_hash_entry *) bh;
-  struct spu_link_hash_table *htab = inf;
-  asection *sec = htab->stub;
-  asection *s = ent->target_section;
-  unsigned int ovl;
-  bfd_vma val;
-
-  val = ent->target_off + s->output_offset + s->output_section->vma;
-  bfd_put_32 (sec->owner, ILA_79 + ((val << 7) & 0x01ffff80),
-             sec->contents + ent->off);
-  val = ent->delta + 4;
-  bfd_put_32 (sec->owner, BR + ((val << 5) & 0x007fff80),
-             sec->contents + ent->off + 4);
-
-  /* If this is the last stub of this group, write stub2.  */
-  if (ent->delta == 0)
-    {
-      bfd_put_32 (sec->owner, NOP,
-                 sec->contents + ent->off + 4);
-
-      ovl = spu_elf_section_data (s->output_section)->ovl_index;
-      bfd_put_32 (sec->owner, ILA_78 + ((ovl << 7) & 0x01ffff80),
-                 sec->contents + ent->off + 8);
-
-      val = (htab->ovly_load->root.u.def.section->output_section->vma
-            + htab->ovly_load->root.u.def.section->output_offset
-            + htab->ovly_load->root.u.def.value
-            - (sec->output_section->vma
-               + sec->output_offset
-               + ent->off + 12));
-
-      if (val + 0x20000 >= 0x40000)
-       htab->stub_overflow = TRUE;
-
-      bfd_put_32 (sec->owner, BR + ((val << 5) & 0x007fff80),
-                 sec->contents + ent->off + 12);
-    }
-
-  if (htab->emit_stub_syms)
-    {
-      struct elf_link_hash_entry *h;
-      size_t len1, len2;
-      char *name;
-
-      len1 = sizeof ("00000000.ovl_call.") - 1;
-      len2 = strlen (ent->root.string);
-      name = bfd_malloc (len1 + len2 + 1);
-      if (name == NULL)
-       return FALSE;
-      memcpy (name, "00000000.ovl_call.", len1);
-      memcpy (name + len1, ent->root.string, len2 + 1);
-      h = elf_link_hash_lookup (&htab->elf, name, TRUE, TRUE, FALSE);
-      free (name);
-      if (h == NULL)
-       return FALSE;
-      if (h->root.type == bfd_link_hash_new)
-       {
-         h->root.type = bfd_link_hash_defined;
-         h->root.u.def.section = sec;
-         h->root.u.def.value = ent->off;
-         h->size = (ent->delta == 0
-                    ? SIZEOF_STUB1 + SIZEOF_STUB2 : SIZEOF_STUB1);
-         h->type = STT_FUNC;
-         h->ref_regular = 1;
-         h->def_regular = 1;
-         h->ref_regular_nonweak = 1;
-         h->forced_local = 1;
-         h->non_elf = 0;
-       }
-    }
-
-  return TRUE;
-}
-
 /* Define an STT_OBJECT symbol.  */
 
 static struct elf_link_hash_entry *
@@ -1325,7 +1306,7 @@ define_ovtab_symbol (struct spu_link_hash_table *htab, const char *name)
 /* Fill in all stubs and the overlay tables.  */
 
 bfd_boolean
-spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms, asection *toe)
+spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms)
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
   struct elf_link_hash_entry *h;
@@ -1335,9 +1316,19 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms, asection *toe)
   unsigned int i;
 
   htab->emit_stub_syms = emit_syms;
-  htab->stub->contents = bfd_zalloc (htab->stub->owner, htab->stub->size);
-  if (htab->stub->contents == NULL)
-    return FALSE;
+  if (htab->stub_count == NULL)
+    return TRUE;
+
+  for (i = 0; i <= htab->num_overlays; i++)
+    if (htab->stub_sec[i]->size != 0)
+      {
+       htab->stub_sec[i]->contents = bfd_zalloc (htab->stub_sec[i]->owner,
+                                                 htab->stub_sec[i]->size);
+       if (htab->stub_sec[i]->contents == NULL)
+         return FALSE;
+       htab->stub_sec[i]->rawsize = htab->stub_sec[i]->size;
+       htab->stub_sec[i]->size = 0;
+      }
 
   h = elf_link_hash_lookup (&htab->elf, "__ovly_load", FALSE, FALSE, FALSE);
   htab->ovly_load = h;
@@ -1347,7 +1338,7 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms, asection *toe)
              && h->def_regular);
 
   s = h->root.u.def.section->output_section;
-  if (spu_elf_section_data (s)->ovl_index)
+  if (spu_elf_section_data (s)->u.o.ovl_index)
     {
       (*_bfd_error_handler) (_("%s in overlay section"),
                             h->root.u.def.section->owner);
@@ -1355,10 +1346,29 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms, asection *toe)
       return FALSE;
     }
 
+  h = elf_link_hash_lookup (&htab->elf, "__ovly_return", FALSE, FALSE, FALSE);
+  htab->ovly_return = h;
+
   /* Write out all the stubs.  */
-  bfd_hash_traverse (&htab->stub_hash_table, write_one_stub, htab);
+  obfd = htab->ovtab->output_section->owner;
+  process_stubs (obfd, info, TRUE);
+
+  elf_link_hash_traverse (&htab->elf, build_spuear_stubs, htab);
+  if (htab->stub_err)
+    return FALSE;
 
-  if (htab->stub_overflow)
+  for (i = 0; i <= htab->num_overlays; i++)
+    {
+      if (htab->stub_sec[i]->size != htab->stub_sec[i]->rawsize)
+       {
+         (*_bfd_error_handler)  (_("stubs don't match calculated size"));
+         bfd_set_error (bfd_error_bad_value);
+         return FALSE;
+       }
+      htab->stub_sec[i]->rawsize = 0;
+    }
+
+  if (htab->stub_err)
     {
       (*_bfd_error_handler) (_("overlay stub relocation overflow"));
       bfd_set_error (bfd_error_bad_value);
@@ -1371,75 +1381,52 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms, asection *toe)
 
   /* Write out _ovly_table.  */
   p = htab->ovtab->contents;
-  obfd = htab->ovtab->output_section->owner;
+  /* set low bit of .size to mark non-overlay area as present.  */
+  p[7] = 1;
   for (s = obfd->sections; s != NULL; s = s->next)
     {
-      unsigned int ovl_index = spu_elf_section_data (s)->ovl_index;
+      unsigned int ovl_index = spu_elf_section_data (s)->u.o.ovl_index;
 
       if (ovl_index != 0)
        {
-         unsigned int lo, hi, mid;
-         unsigned long off = (ovl_index - 1) * 16;
+         unsigned long off = ovl_index * 16;
+         unsigned int ovl_buf = spu_elf_section_data (s)->u.o.ovl_buf;
+
          bfd_put_32 (htab->ovtab->owner, s->vma, p + off);
          bfd_put_32 (htab->ovtab->owner, (s->size + 15) & -16, p + off + 4);
          /* file_off written later in spu_elf_modify_program_headers.  */
-
-         lo = 0;
-         hi = htab->num_buf;
-         while (lo < hi)
-           {
-             mid = (lo + hi) >> 1;
-             if (htab->ovl_region[2 * mid + 1]->vma
-                 + htab->ovl_region[2 * mid + 1]->size <= s->vma)
-               lo = mid + 1;
-             else if (htab->ovl_region[2 * mid]->vma > s->vma)
-               hi = mid;
-             else
-               {
-                 bfd_put_32 (htab->ovtab->owner, mid + 1, p + off + 12);
-                 break;
-               }
-           }
-         BFD_ASSERT (lo < hi);
+         bfd_put_32 (htab->ovtab->owner, ovl_buf, p + off + 12);
        }
     }
 
-  /* Write out _ovly_buf_table.  */
-  p = htab->ovtab->contents + htab->num_overlays * 16;
-  for (i = 0; i < htab->num_buf; i++)
-    {
-      bfd_put_32 (htab->ovtab->owner, 0, p);
-      p += 4;
-    }
-
   h = define_ovtab_symbol (htab, "_ovly_table");
   if (h == NULL)
     return FALSE;
-  h->root.u.def.value = 0;
+  h->root.u.def.value = 16;
   h->size = htab->num_overlays * 16;
 
   h = define_ovtab_symbol (htab, "_ovly_table_end");
   if (h == NULL)
     return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16;
+  h->root.u.def.value = htab->num_overlays * 16 + 16;
   h->size = 0;
 
   h = define_ovtab_symbol (htab, "_ovly_buf_table");
   if (h == NULL)
     return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16;
+  h->root.u.def.value = htab->num_overlays * 16 + 16;
   h->size = htab->num_buf * 4;
 
   h = define_ovtab_symbol (htab, "_ovly_buf_table_end");
   if (h == NULL)
     return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16 + htab->num_buf * 4;
+  h->root.u.def.value = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
   h->size = 0;
 
   h = define_ovtab_symbol (htab, "_EAR_");
   if (h == NULL)
     return FALSE;
-  h->root.u.def.section = toe;
+  h->root.u.def.section = htab->toe;
   h->root.u.def.value = 0;
   h->size = 16;
 
@@ -1534,7 +1521,7 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
          reg[rt] = 0;
          continue;
        }
-      else if (is_branch (buf))
+      else if (is_branch (buf) || is_indirect_branch (buf))
        /* If we hit a branch then we must be out of the prologue.  */
        break;
     unknown_insn:
@@ -1632,10 +1619,10 @@ alloc_stack_info (asection *sec, int max_fun)
 
   amt = sizeof (struct spu_elf_stack_info);
   amt += (max_fun - 1) * sizeof (struct function_info);
-  sec_data->stack_info = bfd_zmalloc (amt);
-  if (sec_data->stack_info != NULL)
-    sec_data->stack_info->max_fun = max_fun;
-  return sec_data->stack_info;
+  sec_data->u.i.stack_info = bfd_zmalloc (amt);
+  if (sec_data->u.i.stack_info != NULL)
+    sec_data->u.i.stack_info->max_fun = max_fun;
+  return sec_data->u.i.stack_info;
 }
 
 /* Add a new struct function_info describing a (part of a) function
@@ -1648,7 +1635,7 @@ maybe_insert_function (asection *sec,
                       bfd_boolean is_func)
 {
   struct _spu_elf_section_data *sec_data = spu_elf_section_data (sec);
-  struct spu_elf_stack_info *sinfo = sec_data->stack_info;
+  struct spu_elf_stack_info *sinfo = sec_data->u.i.stack_info;
   int i;
   bfd_vma off, size;
 
@@ -1712,7 +1699,7 @@ maybe_insert_function (asection *sec,
       if (sinfo == NULL)
        return NULL;
       memset ((char *) sinfo + old, 0, amt - old);
-      sec_data->stack_info = sinfo;
+      sec_data->u.i.stack_info = sinfo;
     }
   sinfo->fun[i].is_func = is_func;
   sinfo->fun[i].global = global;
@@ -1803,7 +1790,7 @@ static bfd_boolean
 check_function_ranges (asection *sec, struct bfd_link_info *info)
 {
   struct _spu_elf_section_data *sec_data = spu_elf_section_data (sec);
-  struct spu_elf_stack_info *sinfo = sec_data->stack_info;
+  struct spu_elf_stack_info *sinfo = sec_data->u.i.stack_info;
   int i;
   bfd_boolean gaps = FALSE;
 
@@ -1849,7 +1836,7 @@ static struct function_info *
 find_function (asection *sec, bfd_vma offset, struct bfd_link_info *info)
 {
   struct _spu_elf_section_data *sec_data = spu_elf_section_data (sec);
-  struct spu_elf_stack_info *sinfo = sec_data->stack_info;
+  struct spu_elf_stack_info *sinfo = sec_data->u.i.stack_info;
   int lo, hi, mid;
 
   lo = 0;
@@ -2018,14 +2005,29 @@ mark_functions_via_relocs (asection *sec,
             destination has been called by some other function then
             it is a separate function.  We also assume that functions
             are not split across input files.  */
-         if (callee->fun->start != NULL
-             || sec->owner != sym_sec->owner)
+         if (sec->owner != sym_sec->owner)
            {
              callee->fun->start = NULL;
              callee->fun->is_func = TRUE;
            }
-         else
+         else if (callee->fun->start == NULL)
            callee->fun->start = caller;
+         else
+           {
+             struct function_info *callee_start;
+             struct function_info *caller_start;
+             callee_start = callee->fun;
+             while (callee_start->start)
+               callee_start = callee_start->start;
+             caller_start = caller;
+             while (caller_start->start)
+               caller_start = caller_start->start;
+             if (caller_start != callee_start)
+               {
+                 callee->fun->start = NULL;
+                 callee->fun->is_func = TRUE;
+               }
+           }
        }
     }
 
@@ -2062,16 +2064,12 @@ pasted_function (asection *sec, struct bfd_link_info *info)
       if (l->u.indirect.section == sec)
        {
          if (fun_start != NULL)
-           {
-             if (fun_start->start)
-               fun_start = fun_start->start;
-             fun->start = fun_start;
-           }
+           fun->start = fun_start;
          return TRUE;
        }
       if (l->type == bfd_indirect_link_order
          && (sec_data = spu_elf_section_data (l->u.indirect.section)) != NULL
-         && (sinfo = sec_data->stack_info) != NULL
+         && (sinfo = sec_data->u.i.stack_info) != NULL
          && sinfo->num_fun != 0)
        fun_start = &sinfo->fun[sinfo->num_fun - 1];
     }
@@ -2080,15 +2078,15 @@ pasted_function (asection *sec, struct bfd_link_info *info)
   return FALSE;
 }
 
-/* We're only interested in code sections.  */
+/* We're only interested in code sections.  Testing SEC_IN_MEMORY excludes
+   overlay stub sections.  */
 
 static bfd_boolean
-interesting_section (asection *s, bfd *obfd, struct spu_link_hash_table *htab)
+interesting_section (asection *s, bfd *obfd)
 {
-  return (s != htab->stub
-         && s->output_section != NULL
+  return (s->output_section != NULL
          && s->output_section->owner == obfd
-         && ((s->flags & (SEC_ALLOC | SEC_LOAD | SEC_CODE))
+         && ((s->flags & (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_IN_MEMORY))
              == (SEC_ALLOC | SEC_LOAD | SEC_CODE))
          && s->size != 0);
 }
@@ -2098,7 +2096,6 @@ interesting_section (asection *s, bfd *obfd, struct spu_link_hash_table *htab)
 static bfd_boolean
 discover_functions (bfd *output_bfd, struct bfd_link_info *info)
 {
-  struct spu_link_hash_table *htab = spu_hash_table (info);
   bfd *ibfd;
   int bfd_idx;
   Elf_Internal_Sym ***psym_arr;
@@ -2163,7 +2160,7 @@ discover_functions (bfd *output_bfd, struct bfd_link_info *info)
            asection *s;
 
            *p = s = bfd_section_from_elf_index (ibfd, sy->st_shndx);
-           if (s != NULL && interesting_section (s, output_bfd, htab))
+           if (s != NULL && interesting_section (s, output_bfd))
              *psy++ = sy;
          }
       symcount = psy - psyms;
@@ -2205,7 +2202,7 @@ discover_functions (bfd *output_bfd, struct bfd_link_info *info)
        }
 
       for (sec = ibfd->sections; sec != NULL && !gaps; sec = sec->next)
-       if (interesting_section (sec, output_bfd, htab))
+       if (interesting_section (sec, output_bfd))
          gaps |= check_function_ranges (sec, info);
     }
 
@@ -2223,7 +2220,7 @@ discover_functions (bfd *output_bfd, struct bfd_link_info *info)
            continue;
 
          for (sec = ibfd->sections; sec != NULL; sec = sec->next)
-           if (interesting_section (sec, output_bfd, htab)
+           if (interesting_section (sec, output_bfd)
                && sec->reloc_count != 0)
              {
                if (!mark_functions_via_relocs (sec, info, FALSE))
@@ -2250,7 +2247,7 @@ discover_functions (bfd *output_bfd, struct bfd_link_info *info)
 
          gaps = FALSE;
          for (sec = ibfd->sections; sec != NULL && !gaps; sec = sec->next)
-           if (interesting_section (sec, output_bfd, htab))
+           if (interesting_section (sec, output_bfd))
              gaps |= check_function_ranges (sec, info);
          if (!gaps)
            continue;
@@ -2276,13 +2273,13 @@ discover_functions (bfd *output_bfd, struct bfd_link_info *info)
             the range of such functions to the beginning of the
             next symbol of interest.  */
          for (sec = ibfd->sections; sec != NULL; sec = sec->next)
-           if (interesting_section (sec, output_bfd, htab))
+           if (interesting_section (sec, output_bfd))
              {
                struct _spu_elf_section_data *sec_data;
                struct spu_elf_stack_info *sinfo;
 
                sec_data = spu_elf_section_data (sec);
-               sinfo = sec_data->stack_info;
+               sinfo = sec_data->u.i.stack_info;
                if (sinfo != NULL)
                  {
                    int fun_idx;
@@ -2371,7 +2368,6 @@ call_graph_traverse (struct function_info *fun, struct bfd_link_info *info)
 static bfd_boolean
 build_call_tree (bfd *output_bfd, struct bfd_link_info *info)
 {
-  struct spu_link_hash_table *htab = spu_hash_table (info);
   bfd *ibfd;
 
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
@@ -2384,7 +2380,7 @@ build_call_tree (bfd *output_bfd, struct bfd_link_info *info)
 
       for (sec = ibfd->sections; sec != NULL; sec = sec->next)
        {
-         if (!interesting_section (sec, output_bfd, htab)
+         if (!interesting_section (sec, output_bfd)
              || sec->reloc_count == 0)
            continue;
 
@@ -2400,19 +2396,24 @@ build_call_tree (bfd *output_bfd, struct bfd_link_info *info)
          struct spu_elf_stack_info *sinfo;
 
          if ((sec_data = spu_elf_section_data (sec)) != NULL
-             && (sinfo = sec_data->stack_info) != NULL)
+             && (sinfo = sec_data->u.i.stack_info) != NULL)
            {
              int i;
              for (i = 0; i < sinfo->num_fun; ++i)
                {
-                 if (sinfo->fun[i].start != NULL)
+                 struct function_info *start = sinfo->fun[i].start;
+
+                 if (start != NULL)
                    {
-                     struct call_info *call = sinfo->fun[i].call_list;
+                     struct call_info *call;
 
+                     while (start->start != NULL)
+                       start = start->start;
+                     call = sinfo->fun[i].call_list;
                      while (call != NULL)
                        {
                          struct call_info *call_next = call->next;
-                         if (!insert_callee (sinfo->fun[i].start, call))
+                         if (!insert_callee (start, call))
                            free (call);
                          call = call_next;
                        }
@@ -2439,7 +2440,7 @@ build_call_tree (bfd *output_bfd, struct bfd_link_info *info)
          struct spu_elf_stack_info *sinfo;
 
          if ((sec_data = spu_elf_section_data (sec)) != NULL
-             && (sinfo = sec_data->stack_info) != NULL)
+             && (sinfo = sec_data->u.i.stack_info) != NULL)
            {
              int i;
              for (i = 0; i < sinfo->num_fun; ++i)
@@ -2465,7 +2466,7 @@ build_call_tree (bfd *output_bfd, struct bfd_link_info *info)
          struct spu_elf_stack_info *sinfo;
 
          if ((sec_data = spu_elf_section_data (sec)) != NULL
-             && (sinfo = sec_data->stack_info) != NULL)
+             && (sinfo = sec_data->u.i.stack_info) != NULL)
            {
              int i;
              for (i = 0; i < sinfo->num_fun; ++i)
@@ -2510,7 +2511,8 @@ sum_stack (struct function_info *fun,
     }
 
   f1 = func_name (fun);
-  info->callbacks->minfo (_("%s: 0x%v 0x%v\n"), f1, fun->stack, max_stack);
+  info->callbacks->minfo (_("%s: 0x%v 0x%v\n"),
+                         f1, (bfd_vma) fun->stack, max_stack);
 
   if (fun->call_list)
     {
@@ -2599,7 +2601,7 @@ spu_elf_stack_analysis (bfd *output_bfd,
          struct spu_elf_stack_info *sinfo;
 
          if ((sec_data = spu_elf_section_data (sec)) != NULL
-             && (sinfo = sec_data->stack_info) != NULL)
+             && (sinfo = sec_data->u.i.stack_info) != NULL)
            {
              int i;
              for (i = 0; i < sinfo->num_fun; ++i)
@@ -2662,7 +2664,7 @@ spu_elf_count_relocs (asection *sec, Elf_Internal_Rela *relocs)
 
 /* Apply RELOCS to CONTENTS of INPUT_SECTION from INPUT_BFD.  */
 
-static bfd_boolean
+static int
 spu_elf_relocate_section (bfd *output_bfd,
                          struct bfd_link_info *info,
                          bfd *input_bfd,
@@ -2676,7 +2678,7 @@ spu_elf_relocate_section (bfd *output_bfd,
   struct elf_link_hash_entry **sym_hashes;
   Elf_Internal_Rela *rel, *relend;
   struct spu_link_hash_table *htab;
-  bfd_boolean ret = TRUE;
+  int ret = TRUE;
   bfd_boolean emit_these_relocs = FALSE;
 
   htab = spu_hash_table (info);
@@ -2699,7 +2701,6 @@ spu_elf_relocate_section (bfd *output_bfd,
       bfd_reloc_status_type r;
       bfd_boolean unresolved_reloc;
       bfd_boolean warned;
-      bfd_boolean branch;
 
       r_symndx = ELF32_R_SYM (rel->r_info);
       r_type = ELF32_R_TYPE (rel->r_info);
@@ -2760,27 +2761,51 @@ spu_elf_relocate_section (bfd *output_bfd,
       /* If this symbol is in an overlay area, we may need to relocate
         to the overlay stub.  */
       addend = rel->r_addend;
-      branch = (is_branch (contents + rel->r_offset)
-               || is_hint (contents + rel->r_offset));
-      if (needs_ovl_stub (sym_name, sec, input_section, htab, branch))
+      if (htab->stub_sec != NULL
+         && sec != NULL
+         && sec->output_section != NULL
+         && sec->output_section->owner == output_bfd
+         && (h == NULL
+             || (h != htab->ovly_load && h != htab->ovly_return)))
        {
-         char *stub_name;
-         struct spu_stub_hash_entry *sh;
+         bfd_boolean branch;
+         unsigned int sym_type;
 
-         stub_name = spu_stub_name (sec, h, rel);
-         if (stub_name == NULL)
-           return FALSE;
+         branch = FALSE;
+         if (r_type == R_SPU_REL16
+             || r_type == R_SPU_ADDR16)
+           branch = (is_branch (contents + rel->r_offset)
+                     || is_hint (contents + rel->r_offset));
+
+         if (h != NULL)
+           sym_type = h->type;
+         else
+           sym_type = ELF_ST_TYPE (sym->st_info);
 
-         sh = (struct spu_stub_hash_entry *)
-           bfd_hash_lookup (&htab->stub_hash_table, stub_name, FALSE, FALSE);
-         if (sh != NULL)
+         if ((sym_type == STT_FUNC || branch)
+             && needs_ovl_stub (sym_name, sec, input_section, htab, branch))
            {
-             relocation = (htab->stub->output_section->vma
-                           + htab->stub->output_offset
-                           + sh->off);
+             unsigned int ovl = 0;
+             struct got_entry *g, **head;
+
+             if (branch)
+               ovl = (spu_elf_section_data (input_section->output_section)
+                      ->u.o.ovl_index);
+
+             if (h != NULL)
+               head = &h->got.glist;
+             else
+               head = elf_local_got_ents (input_bfd) + r_symndx;
+
+             for (g = *head; g != NULL; g = g->next)
+               if (g->addend == addend && (g->ovl == ovl || g->ovl == 0))
+                 break;
+             if (g == NULL)
+               abort ();
+
+             relocation = g->stub_addr;
              addend = 0;
            }
-         free (stub_name);
        }
 
       r = _bfd_final_link_relocate (howto,
@@ -2826,6 +2851,7 @@ spu_elf_relocate_section (bfd *output_bfd,
              /* fall through */
 
            common_error:
+             ret = FALSE;
              if (!((*info->callbacks->warning)
                    (info, msg, sym_name, input_bfd, input_section,
                     rel->r_offset)))
@@ -2875,30 +2901,24 @@ spu_elf_output_symbol_hook (struct bfd_link_info *info,
   struct spu_link_hash_table *htab = spu_hash_table (info);
 
   if (!info->relocatable
-      && htab->num_overlays != 0
+      && htab->stub_sec != NULL
       && h != NULL
       && (h->root.type == bfd_link_hash_defined
          || h->root.type == bfd_link_hash_defweak)
       && h->def_regular
       && strncmp (h->root.root.string, "_SPUEAR_", 8) == 0)
     {
-      static Elf_Internal_Rela zero_rel;
-      char *stub_name = spu_stub_name (h->root.u.def.section, h, &zero_rel);
-      struct spu_stub_hash_entry *sh;
+      struct got_entry *g;
 
-      if (stub_name == NULL)
-       return FALSE;
-      sh = (struct spu_stub_hash_entry *)
-       bfd_hash_lookup (&htab->stub_hash_table, stub_name, FALSE, FALSE);
-      free (stub_name);
-      if (sh == NULL)
-       return TRUE;
-      sym->st_shndx
-       = _bfd_elf_section_from_bfd_section (htab->stub->output_section->owner,
-                                            htab->stub->output_section);
-      sym->st_value = (htab->stub->output_section->vma
-                      + htab->stub->output_offset
-                      + sh->off);
+      for (g = h->got.glist; g != NULL; g = g->next)
+       if (g->addend == 0 && g->ovl == 0)
+         {
+           sym->st_shndx = (_bfd_elf_section_from_bfd_section
+                            (htab->stub_sec[0]->output_section->owner,
+                             htab->stub_sec[0]->output_section));
+           sym->st_value = g->stub_addr;
+           break;
+         }
     }
 
   return TRUE;
@@ -2964,7 +2984,7 @@ spu_elf_modify_segment_map (bfd *abfd, struct bfd_link_info *info)
     if (m->p_type == PT_LOAD && m->count > 1)
       for (i = 0; i < m->count; i++)
        if ((s = m->sections[i]) == toe
-           || spu_elf_section_data (s)->ovl_index != 0)
+           || spu_elf_section_data (s)->u.o.ovl_index != 0)
          {
            struct elf_segment_map *m2;
            bfd_vma amt;
@@ -3063,7 +3083,7 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
 
       for (i = 0, m = elf_tdata (abfd)->segment_map; m; ++i, m = m->next)
        if (m->count != 0
-           && (o = spu_elf_section_data (m->sections[0])->ovl_index) != 0)
+           && (o = spu_elf_section_data (m->sections[0])->u.o.ovl_index) != 0)
          {
            /* Mark this as an overlay header.  */
            phdr[i].p_flags |= PF_OVERLAY;
@@ -3071,7 +3091,7 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
            if (htab->ovtab != NULL && htab->ovtab->size != 0)
              {
                bfd_byte *p = htab->ovtab->contents;
-               unsigned int off = (o - 1) * 16 + 8;
+               unsigned int off = o * 16 + 8;
 
                /* Write file_off into _ovly_table.  */
                bfd_put_32 (htab->ovtab->owner, phdr[i].p_offset, p + off);
@@ -3141,7 +3161,6 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
 #define elf_backend_link_output_symbol_hook    spu_elf_output_symbol_hook
 #define bfd_elf32_new_section_hook             spu_elf_new_section_hook
 #define bfd_elf32_bfd_link_hash_table_create   spu_elf_link_hash_table_create
-#define bfd_elf32_bfd_link_hash_table_free     spu_elf_link_hash_table_free
 
 #define elf_backend_additional_program_headers spu_elf_additional_program_headers
 #define elf_backend_modify_segment_map         spu_elf_modify_segment_map
This page took 0.046427 seconds and 4 git commands to generate.