* elf-bfd.h (struct elf_backend_data
[deliverable/binutils-gdb.git] / bfd / elf32-spu.c
index 00c01db8dd6b9c63b3cfd6acadaba9facf3d8bb8..728cb452fb01973a59671c585ad20e3002d37f7c 100644 (file)
@@ -1,6 +1,6 @@
 /* SPU specific support for 32-bit ELF
 
-   Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+   Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
 
    This file is part of BFD, the Binary File Descriptor library.
 
@@ -297,8 +297,11 @@ struct spu_link_hash_table
 {
   struct elf_link_hash_table elf;
 
+  struct spu_elf_params *params;
+
   /* Shortcuts to overlay sections.  */
   asection *ovtab;
+  asection *init;
   asection *toe;
   asection **ovl_sec;
 
@@ -308,9 +311,7 @@ struct spu_link_hash_table
   /* 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;
+  struct elf_link_hash_entry *ovly_entry[2];
 
   /* Number of overlay buffers.  */
   unsigned int num_buf;
@@ -318,6 +319,11 @@ struct spu_link_hash_table
   /* Total number of overlays.  */
   unsigned int num_overlays;
 
+  /* For soft icache.  */
+  unsigned int line_size_log2;
+  unsigned int num_lines_log2;
+  unsigned int fromelem_size_log2;
+
   /* How much memory we have.  */
   unsigned int local_store;
   /* Local store --auto-overlay should reserve for non-overlay
@@ -325,37 +331,16 @@ struct spu_link_hash_table
   unsigned int overlay_fixed;
   /* Local store --auto-overlay should reserve for stack and heap.  */
   unsigned int reserved;
+  /* If reserved is not specified, stack analysis will calculate a value
+     for the stack.  This parameter adjusts that value to allow for
+     negative sp access (the ABI says 2000 bytes below sp are valid,
+     and the overlay manager uses some of this area).  */
+  int extra_stack_space;
   /* Count of overlay stubs needed in non-overlay area.  */
   unsigned int non_ovly_stub;
 
-  /* Stash various callbacks for --auto-overlay.  */
-  void (*spu_elf_load_ovl_mgr) (void);
-  FILE *(*spu_elf_open_overlay_script) (void);
-  void (*spu_elf_relink) (void);
-
-  /* Bit 0 set if --auto-overlay.
-     Bit 1 set if --auto-relink.
-     Bit 2 set if --overlay-rodata.  */
-  unsigned int auto_overlay : 3;
-#define AUTO_OVERLAY 1
-#define AUTO_RELINK 2
-#define OVERLAY_RODATA 4
-
-  /* Set if we should emit symbols for stubs.  */
-  unsigned int emit_stub_syms:1;
-
-  /* Set if we want stubs on calls out of overlay regions to
-     non-overlay regions.  */
-  unsigned int non_overlay_stubs : 1;
-
   /* Set on error.  */
   unsigned int stub_err : 1;
-
-  /* Set if stack size analysis should be done.  */
-  unsigned int stack_analysis : 1;
-
-  /* Set if __stack_* syms will be emitted.  */
-  unsigned int emit_stack_syms : 1;
 };
 
 /* Hijack the generic got fields for overlay stub accounting.  */
@@ -364,13 +349,87 @@ struct got_entry
 {
   struct got_entry *next;
   unsigned int ovl;
-  bfd_vma addend;
+  union {
+    bfd_vma addend;
+    bfd_vma br_addr;
+  };
   bfd_vma stub_addr;
 };
 
 #define spu_hash_table(p) \
   ((struct spu_link_hash_table *) ((p)->hash))
 
+struct call_info
+{
+  struct function_info *fun;
+  struct call_info *next;
+  unsigned int count;
+  unsigned int max_depth;
+  unsigned int is_tail : 1;
+  unsigned int is_pasted : 1;
+  unsigned int priority : 13;
+};
+
+struct function_info
+{
+  /* List of functions called.  Also branches to hot/cold part of
+     function.  */
+  struct call_info *call_list;
+  /* For hot/cold part of function, point to owner.  */
+  struct function_info *start;
+  /* Symbol at start of function.  */
+  union {
+    Elf_Internal_Sym *sym;
+    struct elf_link_hash_entry *h;
+  } u;
+  /* Function section.  */
+  asection *sec;
+  asection *rodata;
+  /* Where last called from, and number of sections called from.  */
+  asection *last_caller;
+  unsigned int call_count;
+  /* Address range of (this part of) function.  */
+  bfd_vma lo, hi;
+  /* Offset where we found a store of lr, or -1 if none found.  */
+  bfd_vma lr_store;
+  /* Offset where we found the stack adjustment insn.  */
+  bfd_vma sp_adjust;
+  /* Stack usage.  */
+  int stack;
+  /* Distance from root of call tree.  Tail and hot/cold branches
+     count as one deeper.  We aren't counting stack frames here.  */
+  unsigned int depth;
+  /* Set if global symbol.  */
+  unsigned int global : 1;
+  /* Set if known to be start of function (as distinct from a hunk
+     in hot/cold section.  */
+  unsigned int is_func : 1;
+  /* Set if not a root node.  */
+  unsigned int non_root : 1;
+  /* Flags used during call tree traversal.  It's cheaper to replicate
+     the visit flags than have one which needs clearing after a traversal.  */
+  unsigned int visit1 : 1;
+  unsigned int visit2 : 1;
+  unsigned int marking : 1;
+  unsigned int visit3 : 1;
+  unsigned int visit4 : 1;
+  unsigned int visit5 : 1;
+  unsigned int visit6 : 1;
+  unsigned int visit7 : 1;
+};
+
+struct spu_elf_stack_info
+{
+  int num_fun;
+  int max_fun;
+  /* Variable size array describing functions, one per contiguous
+     address range belonging to a function.  */
+  struct function_info fun[1];
+};
+
+static struct function_info *find_function (asection *, bfd_vma,
+                                           struct bfd_link_info *);
+
 /* Create a spu ELF linker hash table.  */
 
 static struct bfd_link_hash_table *
@@ -400,6 +459,23 @@ spu_elf_link_hash_table_create (bfd *abfd)
   return &htab->elf.root;
 }
 
+void
+spu_elf_setup (struct bfd_link_info *info, struct spu_elf_params *params)
+{
+  bfd_vma max_branch_log2;
+
+  struct spu_link_hash_table *htab = spu_hash_table (info);
+  htab->params = params;
+  htab->line_size_log2 = bfd_log2 (htab->params->line_size);
+  htab->num_lines_log2 = bfd_log2 (htab->params->num_lines);
+
+  /* For the software i-cache, we provide a "from" list whose size
+     is a power-of-two number of quadwords, big enough to hold one
+     byte per outgoing branch.  Compute this number here.  */
+  max_branch_log2 = bfd_log2 (htab->params->max_branch);
+  htab->fromelem_size_log2 = max_branch_log2 > 4 ? max_branch_log2 - 4 : 0;
+}
+
 /* 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.  */
@@ -448,17 +524,9 @@ get_sym_h (struct elf_link_hash_entry **hp,
        {
          locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
          if (locsyms == NULL)
-           {
-             size_t symcount = symtab_hdr->sh_info;
-
-             /* If we are reading symbols into the contents, then
-                read the global syms too.  This is done to cache
-                syms for later stack analysis.  */
-             if ((unsigned char **) locsymsp == &symtab_hdr->contents)
-               symcount = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
-             locsyms = bfd_elf_get_elf_syms (ibfd, symtab_hdr, symcount, 0,
-                                             NULL, NULL, NULL);
-           }
+           locsyms = bfd_elf_get_elf_syms (ibfd, symtab_hdr,
+                                           symtab_hdr->sh_info,
+                                           0, NULL, NULL, NULL);
          if (locsyms == NULL)
            return FALSE;
          *locsymsp = locsyms;
@@ -482,16 +550,9 @@ get_sym_h (struct elf_link_hash_entry **hp,
    that the linker maps the sections to the right place in the output.  */
 
 bfd_boolean
-spu_elf_create_sections (struct bfd_link_info *info,
-                        int stack_analysis,
-                        int emit_stack_syms)
+spu_elf_create_sections (struct bfd_link_info *info)
 {
   bfd *ibfd;
-  struct spu_link_hash_table *htab = spu_hash_table (info);
-
-  /* Stash some options away where we can get at them later.  */
-  htab->stack_analysis = stack_analysis;
-  htab->emit_stack_syms = emit_stack_syms;
 
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     if (bfd_get_section_by_name (ibfd, SPU_PTNOTE_SPUNAME) != NULL)
@@ -551,9 +612,10 @@ sort_sections (const void *a, const void *b)
   return (*s1)->index - (*s2)->index;
 }
 
-/* Identify overlays in the output bfd, and number them.  */
+/* Identify overlays in the output bfd, and number them.
+   Returns 0 on error, 1 if no overlays, 2 if overlays.  */
 
-bfd_boolean
+int
 spu_elf_find_overlays (struct bfd_link_info *info)
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
@@ -561,14 +623,18 @@ spu_elf_find_overlays (struct bfd_link_info *info)
   unsigned int i, n, ovl_index, num_buf;
   asection *s;
   bfd_vma ovl_end;
+  static const char *const entry_names[2][2] = {
+    { "__ovly_load", "__icache_br_handler" },
+    { "__ovly_return", "__icache_call_handler" }
+  };
 
   if (info->output_bfd->section_count < 2)
-    return FALSE;
+    return 1;
 
   alloc_sec
     = bfd_malloc (info->output_bfd->section_count * sizeof (*alloc_sec));
   if (alloc_sec == NULL)
-    return FALSE;
+    return 0;
 
   /* Pick out all the alloced sections.  */
   for (n = 0, s = info->output_bfd->sections; s != NULL; s = s->next)
@@ -580,64 +646,178 @@ spu_elf_find_overlays (struct bfd_link_info *info)
   if (n == 0)
     {
       free (alloc_sec);
-      return FALSE;
+      return 1;
     }
 
   /* Sort them by vma.  */
   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.  */
   ovl_end = alloc_sec[0]->vma + alloc_sec[0]->size;
-  for (ovl_index = 0, num_buf = 0, i = 1; i < n; i++)
+  if (htab->params->ovly_flavour == ovly_soft_icache)
     {
-      s = alloc_sec[i];
-      if (s->vma < ovl_end)
+      /* Look for an overlapping vma to find the first overlay section.  */
+      bfd_vma vma_start = 0;
+      bfd_vma lma_start = 0;
+
+      for (i = 1; i < n; i++)
        {
-         asection *s0 = alloc_sec[i - 1];
+         s = alloc_sec[i];
+         if (s->vma < ovl_end)
+           {
+             asection *s0 = alloc_sec[i - 1];
+             vma_start = s0->vma;
+             if (strncmp (s0->name, ".ovl.init", 9) != 0)
+               lma_start = s0->lma;
+             else
+               lma_start = s->lma;
+             ovl_end = (s0->vma
+                        + ((bfd_vma) 1
+                           << (htab->num_lines_log2 + htab->line_size_log2)));
+             --i;
+             break;
+           }
+         else
+           ovl_end = s->vma + s->size;
+       }
+
+      /* Now find any sections within the cache area.  */
+      for (ovl_index = 0, num_buf = 0; i < n; i++)
+       {
+         s = alloc_sec[i];
+         if (s->vma >= ovl_end)
+           break;
 
-         if (spu_elf_section_data (s0)->u.o.ovl_index == 0)
+         /* A section in an overlay area called .ovl.init is not
+            an overlay, in the sense that it might be loaded in
+            by the overlay manager, but rather the initial
+            section contents for the overlay buffer.  */
+         if (strncmp (s->name, ".ovl.init", 9) != 0)
            {
-             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;
+             num_buf = ((s->vma - vma_start) >> htab->line_size_log2) + 1;
+             if (((s->vma - vma_start) & (htab->params->line_size - 1))
+                 || ((s->lma - lma_start) & (htab->params->line_size - 1)))
+               {
+                 info->callbacks->einfo (_("%X%P: overlay section %A "
+                                           "does not start on a cache line.\n"),
+                                         s);
+                 bfd_set_error (bfd_error_bad_value);
+                 return 0;
+               }
+             else if (s->size > htab->params->line_size)
+               {
+                 info->callbacks->einfo (_("%X%P: overlay section %A "
+                                           "is larger than a cache line.\n"),
+                                         s);
+                 bfd_set_error (bfd_error_bad_value);
+                 return 0;
+               }
+
+             alloc_sec[ovl_index++] = s;
+             spu_elf_section_data (s)->u.o.ovl_index
+               = ((s->lma - lma_start) >>  htab->line_size_log2) + 1;
+             spu_elf_section_data (s)->u.o.ovl_buf = num_buf;
            }
-         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)
+       }
+
+      /* Ensure there are no more overlay sections.  */
+      for ( ; i < n; i++)
+       {
+         s = alloc_sec[i];
+         if (s->vma < ovl_end)
            {
-             info->callbacks->einfo (_("%X%P: overlay sections %A and %A "
-                                       "do not start at the same address.\n"),
-                                     s0, s);
-             return FALSE;
+             info->callbacks->einfo (_("%X%P: overlay section %A "
+                                       "is not in cache area.\n"),
+                                     alloc_sec[i-1]);
+             bfd_set_error (bfd_error_bad_value);
+             return 0;
            }
-         if (ovl_end < s->vma + s->size)
+         else
+           ovl_end = s->vma + s->size;
+       }
+    }
+  else
+    {
+      /* Look for overlapping vmas.  Any with overlap must be overlays.
+        Count them.  Also count the number of overlay regions.  */
+      for (ovl_index = 0, num_buf = 0, i = 1; i < n; i++)
+       {
+         s = alloc_sec[i];
+         if (s->vma < ovl_end)
+           {
+             asection *s0 = alloc_sec[i - 1];
+
+             if (spu_elf_section_data (s0)->u.o.ovl_index == 0)
+               {
+                 ++num_buf;
+                 if (strncmp (s0->name, ".ovl.init", 9) != 0)
+                   {
+                     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;
+                   }
+                 else
+                   ovl_end = s->vma + s->size;
+               }
+             if (strncmp (s->name, ".ovl.init", 9) != 0)
+               {
+                 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)
+                   {
+                     info->callbacks->einfo (_("%X%P: overlay sections %A "
+                                               "and %A do not start at the "
+                                               "same address.\n"),
+                                             s0, s);
+                     bfd_set_error (bfd_error_bad_value);
+                     return 0;
+                   }
+                 if (ovl_end < s->vma + s->size)
+                   ovl_end = s->vma + s->size;
+               }
+           }
+         else
            ovl_end = s->vma + s->size;
        }
-      else
-       ovl_end = s->vma + s->size;
     }
 
   htab->num_overlays = ovl_index;
   htab->num_buf = num_buf;
   htab->ovl_sec = alloc_sec;
-  htab->ovly_load = elf_link_hash_lookup (&htab->elf, "__ovly_load",
-                                         FALSE, FALSE, FALSE);
-  htab->ovly_return = elf_link_hash_lookup (&htab->elf, "__ovly_return",
-                                           FALSE, FALSE, FALSE);
-  return ovl_index != 0;
+
+  if (ovl_index == 0)
+    return 1;
+
+  for (i = 0; i < 2; i++)
+    {
+      const char *name;
+      struct elf_link_hash_entry *h;
+
+      name = entry_names[i][htab->params->ovly_flavour];
+      h = elf_link_hash_lookup (&htab->elf, name, TRUE, FALSE, FALSE);
+      if (h == NULL)
+       return 0;
+
+      if (h->root.type == bfd_link_hash_new)
+       {
+         h->root.type = bfd_link_hash_undefined;
+         h->ref_regular = 1;
+         h->ref_regular_nonweak = 1;
+         h->non_elf = 0;
+       }
+      htab->ovly_entry[i] = h;
+    }
+
+  return 2;
 }
 
-/* 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
+/* Non-zero to use bra in overlay stubs rather than br.  */
+#define BRA_STUBS 0
+
+#define BRA    0x30000000
+#define BRASL  0x31000000
 #define BR     0x32000000
+#define BRSL   0x33000000
 #define NOP    0x40200000
 #define LNOP   0x00200000
 #define ILA    0x42000000
@@ -687,15 +867,14 @@ is_hint (const unsigned char *insn)
 /* True if INPUT_SECTION might need overlay stubs.  */
 
 static bfd_boolean
-maybe_needs_stubs (asection *input_section, bfd *output_bfd)
+maybe_needs_stubs (asection *input_section)
 {
   /* No stubs for debug sections and suchlike.  */
   if ((input_section->flags & SEC_ALLOC) == 0)
     return FALSE;
 
   /* No stubs for link-once sections that will be discarded.  */
-  if (input_section->output_section == NULL
-      || input_section->output_section->owner != output_bfd)
+  if (input_section->output_section == bfd_abs_section_ptr)
     return FALSE;
 
   /* Don't create stubs for .eh_frame references.  */
@@ -708,7 +887,15 @@ maybe_needs_stubs (asection *input_section, bfd *output_bfd)
 enum _stub_type
 {
   no_stub,
-  ovl_stub,
+  call_ovl_stub,
+  br000_ovl_stub,
+  br001_ovl_stub,
+  br010_ovl_stub,
+  br011_ovl_stub,
+  br100_ovl_stub,
+  br101_ovl_stub,
+  br110_ovl_stub,
+  br111_ovl_stub,
   nonovl_stub,
   stub_error
 };
@@ -728,19 +915,19 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
   struct spu_link_hash_table *htab = spu_hash_table (info);
   enum elf_spu_reloc_type r_type;
   unsigned int sym_type;
-  bfd_boolean branch;
+  bfd_boolean branch, hint, call;
   enum _stub_type ret = no_stub;
+  bfd_byte insn[4];
 
   if (sym_sec == NULL
-      || sym_sec->output_section == NULL
-      || sym_sec->output_section->owner != info->output_bfd
+      || sym_sec->output_section == bfd_abs_section_ptr
       || spu_elf_section_data (sym_sec->output_section) == NULL)
     return ret;
 
   if (h != NULL)
     {
       /* Ensure no stubs for user supplied overlay manager syms.  */
-      if (h == htab->ovly_load || h == htab->ovly_return)
+      if (h == htab->ovly_entry[0] || h == htab->ovly_entry[1])
        return ret;
 
       /* setjmp always goes via an overlay stub, because then the return
@@ -748,14 +935,9 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
         makes setjmp/longjmp between overlays work.  */
       if (strncmp (h->root.root.string, "setjmp", 6) == 0
          && (h->root.root.string[6] == '\0' || h->root.root.string[6] == '@'))
-       ret = ovl_stub;
+       ret = call_ovl_stub;
     }
 
-  /* Usually, symbols in non-overlay sections don't need stubs.  */
-  if (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index == 0
-      && !htab->non_overlay_stubs)
-    return ret;
-
   if (h != NULL)
     sym_type = h->type;
   else
@@ -763,10 +945,10 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
 
   r_type = ELF32_R_TYPE (irela->r_info);
   branch = FALSE;
+  hint = FALSE;
+  call = FALSE;
   if (r_type == R_SPU_REL16 || r_type == R_SPU_ADDR16)
     {
-      bfd_byte insn[4];
-
       if (contents == NULL)
        {
          contents = insn;
@@ -779,10 +961,12 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
       else
        contents += irela->r_offset;
 
-      if (is_branch (contents) || is_hint (contents))
+      branch = is_branch (contents);
+      hint = is_hint (contents);
+      if (branch || hint)
        {
-         branch = TRUE;
-         if ((contents[0] & 0xfd) == 0x31
+         call = (contents[0] & 0xfd) == 0x31;
+         if (call
              && sym_type != STT_FUNC
              && contents != insn)
            {
@@ -813,20 +997,45 @@ needs_ovl_stub (struct elf_link_hash_entry *h,
        }
     }
 
-  if (sym_type != STT_FUNC
-      && !branch
-      && (sym_sec->flags & SEC_CODE) == 0)
+  if ((!branch && htab->params->ovly_flavour == ovly_soft_icache)
+      || (sym_type != STT_FUNC
+         && !(branch || hint)
+         && (sym_sec->flags & SEC_CODE) == 0))
+    return no_stub;
+
+  /* Usually, symbols in non-overlay sections don't need stubs.  */
+  if (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index == 0
+      && !htab->params->non_overlay_stubs)
     return ret;
 
   /* 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)->u.o.ovl_index
        != spu_elf_section_data (input_section->output_section)->u.o.ovl_index)
-    return ovl_stub;
+    {
+      if (call || sym_type == STT_FUNC)
+       ret = call_ovl_stub;
+      else
+       {
+         ret = br000_ovl_stub;
+
+         if (branch)
+           {
+             unsigned int lrlive = (contents[1] & 0x70) >> 4;
+             ret += lrlive;
+           }
+       }
+    }
 
   /* If this insn isn't a branch then we are possibly taking the
-     address of a function and passing it out somehow.  */
-  return !branch && sym_type == STT_FUNC ? nonovl_stub : ret;
+     address of a function and passing it out somehow.  Soft-icache code
+     always generates inline code to do indirect branches.  */
+  if (!(branch || hint)
+      && sym_type == STT_FUNC
+      && htab->params->ovly_flavour != ovly_soft_icache)
+    ret = nonovl_stub;
+
+  return ret;
 }
 
 static bfd_boolean
@@ -864,6 +1073,12 @@ count_stub (struct spu_link_hash_table *htab,
       head = elf_local_got_ents (ibfd) + ELF32_R_SYM (irela->r_info);
     }
 
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    {
+      htab->stub_count[ovl] += 1;
+      return TRUE;
+    }
+
   addend = 0;
   if (irela != NULL)
     addend = irela->r_addend;
@@ -914,6 +1129,22 @@ count_stub (struct spu_link_hash_table *htab,
   return TRUE;
 }
 
+/* Support two sizes of overlay stubs, a slower more compact stub of two
+   intructions, and a faster stub of four instructions.
+   Soft-icache stubs are four or eight words.  */
+
+static unsigned int
+ovl_stub_size (struct spu_elf_params *params)
+{
+  return 16 << params->ovly_flavour >> params->compact_stub;
+}
+
+static unsigned int
+ovl_stub_size_log2 (struct spu_elf_params *params)
+{
+  return 4 + params->ovly_flavour - params->compact_stub;
+}
+
 /* Two instruction overlay stubs look like:
 
    brsl $75,__ovly_load
@@ -927,10 +1158,19 @@ count_stub (struct spu_link_hash_table *htab,
    ila $78,ovl_number
    lnop
    ila $79,target_address
-   br __ovly_load  */
+   br __ovly_load
+
+   Software icache stubs are:
+
+   .word target_index
+   .word target_ia;
+   .word lrlive_branchlocalstoreaddr;
+   brasl $75,__icache_br_handler
+   .quad xor_pattern
+*/
 
 static bfd_boolean
-build_stub (struct spu_link_hash_table *htab,
+build_stub (struct bfd_link_info *info,
            bfd *ibfd,
            asection *isec,
            enum _stub_type stub_type,
@@ -939,10 +1179,12 @@ build_stub (struct spu_link_hash_table *htab,
            bfd_vma dest,
            asection *dest_sec)
 {
-  unsigned int ovl;
+  struct spu_link_hash_table *htab = spu_hash_table (info);
+  unsigned int ovl, dest_ovl, set_id;
   struct got_entry *g, **head;
   asection *sec;
-  bfd_vma addend, val, from, to;
+  bfd_vma addend, from, to, br_dest, patt;
+  unsigned int lrlive;
 
   ovl = 0;
   if (stub_type != nonovl_stub)
@@ -957,61 +1199,202 @@ build_stub (struct spu_link_hash_table *htab,
   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 (htab->params->ovly_flavour == ovly_soft_icache)
+    {
+      g = bfd_malloc (sizeof *g);
+      if (g == NULL)
+       return FALSE;
+      g->ovl = ovl;
+      g->br_addr = 0;
+      if (irela != NULL)
+       g->br_addr = (irela->r_offset
+                     + isec->output_offset
+                     + isec->output_section->vma);
+      g->next = *head;
+      *head = g;
+    }
+  else
+    {
+      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->ovl == 0 && ovl != 0)
+       return TRUE;
 
-  if (g->stub_addr != (bfd_vma) -1)
-    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)
+  to = (htab->ovly_entry[0]->root.u.def.value
+       + htab->ovly_entry[0]->root.u.def.section->output_offset
+       + htab->ovly_entry[0]->root.u.def.section->output_section->vma);
+
+  if (((dest | to | from) & 3) != 0)
     {
       htab->stub_err = 1;
       return FALSE;
     }
-  ovl = spu_elf_section_data (dest_sec->output_section)->u.o.ovl_index;
+  dest_ovl = spu_elf_section_data (dest_sec->output_section)->u.o.ovl_index;
 
-  if (OVL_STUB_SIZE == 16)
+  if (htab->params->ovly_flavour == ovly_normal
+      && !htab->params->compact_stub)
     {
-      bfd_put_32 (sec->owner, ILA + ((ovl << 7) & 0x01ffff80) + 78,
+      bfd_put_32 (sec->owner, ILA + ((dest_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);
+      if (!BRA_STUBS)
+       bfd_put_32 (sec->owner, BR + (((to - (from + 12)) << 5) & 0x007fff80),
+                   sec->contents + sec->size + 12);
+      else
+       bfd_put_32 (sec->owner, BRA + ((to << 5) & 0x007fff80),
+                   sec->contents + sec->size + 12);
     }
-  else if (OVL_STUB_SIZE == 8)
+  else if (htab->params->ovly_flavour == ovly_normal
+          && htab->params->compact_stub)
     {
-      bfd_put_32 (sec->owner, BRSL + ((val << 5) & 0x007fff80) + 75,
-                 sec->contents + sec->size);
+      if (!BRA_STUBS)
+       bfd_put_32 (sec->owner, BRSL + (((to - from) << 5) & 0x007fff80) + 75,
+                   sec->contents + sec->size);
+      else
+       bfd_put_32 (sec->owner, BRASL + ((to << 5) & 0x007fff80) + 75,
+                   sec->contents + sec->size);
+      bfd_put_32 (sec->owner, (dest & 0x3ffff) | (dest_ovl << 18),
+                 sec->contents + sec->size + 4);
+    }
+  else if (htab->params->ovly_flavour == ovly_soft_icache
+          && htab->params->compact_stub)
+    {
+      lrlive = 0;
+      if (stub_type == nonovl_stub)
+       ;
+      else if (stub_type == call_ovl_stub)
+       /* A brsl makes lr live and *(*sp+16) is live.
+          Tail calls have the same liveness.  */
+       lrlive = 5;
+      else if (!htab->params->lrlive_analysis)
+       /* Assume stack frame and lr save.  */
+       lrlive = 1;
+      else if (irela != NULL)
+       {
+         /* Analyse branch instructions.  */
+         struct function_info *caller;
+         bfd_vma off;
 
-      val = (dest & 0x3ffff) | (ovl << 18);
-      bfd_put_32 (sec->owner, val,
+         caller = find_function (isec, irela->r_offset, info);
+         if (caller->start == NULL)
+           off = irela->r_offset;
+         else
+           {
+             struct function_info *found = NULL;
+
+             /* Find the earliest piece of this function that
+                has frame adjusting instructions.  We might
+                see dynamic frame adjustment (eg. for alloca)
+                in some later piece, but functions using
+                alloca always set up a frame earlier.  Frame
+                setup instructions are always in one piece.  */
+             if (caller->lr_store != (bfd_vma) -1
+                 || caller->sp_adjust != (bfd_vma) -1)
+               found = caller;
+             while (caller->start != NULL)
+               {
+                 caller = caller->start;
+                 if (caller->lr_store != (bfd_vma) -1
+                     || caller->sp_adjust != (bfd_vma) -1)
+                   found = caller;
+               }
+             if (found != NULL)
+               caller = found;
+             off = (bfd_vma) -1;
+           }
+
+         if (off > caller->sp_adjust)
+           {
+             if (off > caller->lr_store)
+               /* Only *(*sp+16) is live.  */
+               lrlive = 1;
+             else
+               /* If no lr save, then we must be in a
+                  leaf function with a frame.
+                  lr is still live.  */
+               lrlive = 4;
+           }
+         else if (off > caller->lr_store)
+           {
+             /* Between lr save and stack adjust.  */
+             lrlive = 3;
+             /* This should never happen since prologues won't
+                be split here.  */
+             BFD_ASSERT (0);
+           }
+         else
+           /* On entry to function.  */
+           lrlive = 5;
+
+         if (stub_type != br000_ovl_stub
+             && lrlive != stub_type - br000_ovl_stub)
+           info->callbacks->einfo (_("%A:0x%v lrlive .brinfo (%u) differs "
+                                     "from analysis (%u)\n"),
+                                   isec, irela->r_offset, lrlive,
+                                   stub_type - br000_ovl_stub);
+       }
+
+      /* If given lrlive info via .brinfo, use it.  */
+      if (stub_type > br000_ovl_stub)
+       lrlive = stub_type - br000_ovl_stub;
+
+      if (ovl == 0)
+       to = (htab->ovly_entry[1]->root.u.def.value
+             + htab->ovly_entry[1]->root.u.def.section->output_offset
+             + htab->ovly_entry[1]->root.u.def.section->output_section->vma);
+
+      /* The branch that uses this stub goes to stub_addr + 4.  We'll
+        set up an xor pattern that can be used by the icache manager
+        to modify this branch to go directly to its destination.  */
+      g->stub_addr += 4;
+      br_dest = g->stub_addr;
+      if (irela == NULL)
+       {
+         /* Except in the case of _SPUEAR_ stubs, the branch in
+            question is the one in the stub itself.  */
+         BFD_ASSERT (stub_type == nonovl_stub);
+         g->br_addr = g->stub_addr;
+         br_dest = to;
+       }
+
+      set_id = ((dest_ovl - 1) >> htab->num_lines_log2) + 1;
+      bfd_put_32 (sec->owner, (set_id << 18) | (dest & 0x3ffff),
+                 sec->contents + sec->size);
+      bfd_put_32 (sec->owner, BRASL + ((to << 5) & 0x007fff80) + 75,
                  sec->contents + sec->size + 4);
+      bfd_put_32 (sec->owner, (lrlive << 29) | (g->br_addr & 0x3ffff),
+                 sec->contents + sec->size + 8);
+      patt = dest ^ br_dest;
+      if (irela != NULL && ELF32_R_TYPE (irela->r_info) == R_SPU_REL16)
+       patt = (dest - g->br_addr) ^ (br_dest - g->br_addr);
+      bfd_put_32 (sec->owner, (patt << 5) & 0x007fff80,
+                 sec->contents + sec->size + 12);
+
+      if (ovl == 0)
+       /* Extra space for linked list entries.  */
+       sec->size += 16;
     }
   else
     abort ();
-  sec->size += OVL_STUB_SIZE;
 
-  if (htab->emit_stub_syms)
+  sec->size += ovl_stub_size (htab->params);
+
+  if (htab->params->emit_stub_syms)
     {
       size_t len;
       char *name;
@@ -1049,8 +1432,8 @@ build_stub (struct spu_link_hash_table *htab,
        {
          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->size = ovl_stub_size (htab->params);
+         h->root.u.def.value = sec->size - h->size;
          h->type = STT_FUNC;
          h->ref_regular = 1;
          h->def_regular = 1;
@@ -1080,13 +1463,12 @@ allocate_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
       && h->def_regular
       && strncmp (h->root.root.string, "_SPUEAR_", 8) == 0
       && (sym_sec = h->root.u.def.section) != NULL
-      && sym_sec->output_section != NULL
-      && sym_sec->output_section->owner == info->output_bfd
+      && sym_sec->output_section != bfd_abs_section_ptr
       && spu_elf_section_data (sym_sec->output_section) != NULL
       && (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index != 0
-         || htab->non_overlay_stubs))
+         || htab->params->non_overlay_stubs))
     {
-      count_stub (htab, NULL, NULL, nonovl_stub, h, NULL);
+      return count_stub (htab, NULL, NULL, nonovl_stub, h, NULL);
     }
   
   return TRUE;
@@ -1106,14 +1488,13 @@ build_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
       && h->def_regular
       && strncmp (h->root.root.string, "_SPUEAR_", 8) == 0
       && (sym_sec = h->root.u.def.section) != NULL
-      && sym_sec->output_section != NULL
-      && sym_sec->output_section->owner == info->output_bfd
+      && sym_sec->output_section != bfd_abs_section_ptr
       && spu_elf_section_data (sym_sec->output_section) != NULL
       && (spu_elf_section_data (sym_sec->output_section)->u.o.ovl_index != 0
-         || htab->non_overlay_stubs))
+         || htab->params->non_overlay_stubs))
     {
-      build_stub (htab, NULL, NULL, nonovl_stub, h, NULL,
-                 h->root.u.def.value, sym_sec);
+      return build_stub (info, NULL, NULL, nonovl_stub, h, NULL,
+                        h->root.u.def.value, sym_sec);
     }
   
   return TRUE;
@@ -1133,7 +1514,6 @@ process_stubs (struct bfd_link_info *info, bfd_boolean build)
       Elf_Internal_Shdr *symtab_hdr;
       asection *isec;
       Elf_Internal_Sym *local_syms = NULL;
-      void *psyms;
 
       if (ibfd->xvec != &bfd_elf32_spu_vec)
        continue;
@@ -1143,11 +1523,6 @@ process_stubs (struct bfd_link_info *info, bfd_boolean build)
       if (symtab_hdr->sh_info == 0)
        continue;
 
-      /* Arrange to read and keep global syms for later stack analysis.  */
-      psyms = &local_syms;
-      if (htab->stack_analysis)
-       psyms = &symtab_hdr->contents;
-
       /* Walk over each section attached to the input bfd.  */
       for (isec = ibfd->sections; isec != NULL; isec = isec->next)
        {
@@ -1158,7 +1533,7 @@ process_stubs (struct bfd_link_info *info, bfd_boolean build)
              || isec->reloc_count == 0)
            continue;
 
-         if (!maybe_needs_stubs (isec, info->output_bfd))
+         if (!maybe_needs_stubs (isec))
            continue;
 
          /* Get the relocs.  */
@@ -1197,7 +1572,7 @@ process_stubs (struct bfd_link_info *info, bfd_boolean build)
                }
 
              /* Determine the reloc target section.  */
-             if (!get_sym_h (&h, &sym, &sym_sec, psyms, r_indx, ibfd))
+             if (!get_sym_h (&h, &sym, &sym_sec, &local_syms, r_indx, ibfd))
                goto error_ret_free_internal;
 
              stub_type = needs_ovl_stub (h, sym, sym_sec, isec, irela,
@@ -1230,7 +1605,7 @@ process_stubs (struct bfd_link_info *info, bfd_boolean build)
                  else
                    dest = sym->st_value;
                  dest += irela->r_addend;
-                 if (!build_stub (htab, ibfd, isec, stub_type, h, irela,
+                 if (!build_stub (info, ibfd, isec, stub_type, h, irela,
                                   dest, sym_sec))
                    goto error_ret_free_internal;
                }
@@ -1254,25 +1629,23 @@ process_stubs (struct bfd_link_info *info, bfd_boolean build)
   return TRUE;
 }
 
-/* Allocate space for overlay call and return stubs.  */
+/* Allocate space for overlay call and return stubs.
+   Return 0 on error, 1 if no stubs, 2 otherwise.  */
 
 int
-spu_elf_size_stubs (struct bfd_link_info *info,
-                   void (*place_spu_section) (asection *, asection *,
-                                              const char *),
-                   int non_overlay_stubs)
+spu_elf_size_stubs (struct bfd_link_info *info)
 {
-  struct spu_link_hash_table *htab = spu_hash_table (info);
+  struct spu_link_hash_table *htab;
   bfd *ibfd;
   bfd_size_type amt;
   flagword flags;
   unsigned int i;
   asection *stub;
 
-  htab->non_overlay_stubs = non_overlay_stubs;
   if (!process_stubs (info, FALSE))
     return 0;
 
+  htab = spu_hash_table (info);
   elf_link_hash_traverse (&htab->elf, allocate_spuear_stubs, info);
   if (htab->stub_err)
     return 0;
@@ -1291,10 +1664,13 @@ spu_elf_size_stubs (struct bfd_link_info *info,
   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)))
+      || !bfd_set_section_alignment (ibfd, stub,
+                                    ovl_stub_size_log2 (htab->params)))
     return 0;
-  stub->size = htab->stub_count[0] * OVL_STUB_SIZE;
-  (*place_spu_section) (stub, NULL, ".text");
+  stub->size = htab->stub_count[0] * ovl_stub_size (htab->params);
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    /* Extra space for linked list entries.  */
+    stub->size += htab->stub_count[0] * 16;
 
   for (i = 0; i < htab->num_overlays; ++i)
     {
@@ -1303,45 +1679,105 @@ spu_elf_size_stubs (struct bfd_link_info *info,
       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)))
+         || !bfd_set_section_alignment (ibfd, stub,
+                                        ovl_stub_size_log2 (htab->params)))
        return 0;
-      stub->size = htab->stub_count[ovl] * OVL_STUB_SIZE;
-      (*place_spu_section) (stub, osec, NULL);
+      stub->size = htab->stub_count[ovl] * ovl_stub_size (htab->params);
     }
 
- /* htab->ovtab consists of two arrays.
-    .  struct {
-    .    u32 vma;
-    .    u32 size;
-    .    u32 file_off;
-    .    u32 buf;
-    .  } _ovly_table[];
-    .
-    .  struct {
-    .    u32 mapped;
-    .  } _ovly_buf_table[];
-    .  */
-
-  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;
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    {
+      /* Space for icache manager tables.
+        a) Tag array, one quadword per cache line.
+        b) Rewrite "to" list, one quadword per cache line.
+        c) Rewrite "from" list, one byte per outgoing branch (rounded up to
+           a power-of-two number of full quadwords) per cache line.  */
+
+      flags = SEC_ALLOC;
+      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;
+
+      htab->ovtab->size = (16 + 16 + (16 << htab->fromelem_size_log2))
+                         << htab->num_lines_log2;
 
-  htab->ovtab->size = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
-  (*place_spu_section) (htab->ovtab, NULL, ".data");
+      flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY;
+      htab->init = bfd_make_section_anyway_with_flags (ibfd, ".ovini", flags);
+      if (htab->init == NULL
+         || !bfd_set_section_alignment (ibfd, htab->init, 4))
+       return 0;
+
+      htab->init->size = 16;
+    }
+  else
+    {
+      /* htab->ovtab consists of two arrays.
+        .      struct {
+        .        u32 vma;
+        .        u32 size;
+        .        u32 file_off;
+        .        u32 buf;
+        .      } _ovly_table[];
+        .
+        .      struct {
+        .        u32 mapped;
+        .      } _ovly_buf_table[];
+        .  */
+
+      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;
+
+      htab->ovtab->size = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
+    }
 
   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;
 }
 
+/* Called from ld to place overlay manager data sections.  This is done
+   after the overlay manager itself is loaded, mainly so that the
+   linker's htab->init section is placed after any other .ovl.init
+   sections.  */
+
+void
+spu_elf_place_overlay_data (struct bfd_link_info *info)
+{
+  struct spu_link_hash_table *htab = spu_hash_table (info);
+  unsigned int i;
+  const char *ovout;
+
+  if (htab->stub_count == NULL)
+    return;
+
+  (*htab->params->place_spu_section) (htab->stub_sec[0], NULL, ".text");
+
+  for (i = 0; i < htab->num_overlays; ++i)
+    {
+      asection *osec = htab->ovl_sec[i];
+      unsigned int ovl = spu_elf_section_data (osec)->u.o.ovl_index;
+      (*htab->params->place_spu_section) (htab->stub_sec[ovl], osec, NULL);
+    }
+
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    (*htab->params->place_spu_section) (htab->init, NULL, ".ovl.init");
+
+  ovout = ".data";
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    ovout = ".bss";
+  (*htab->params->place_spu_section) (htab->ovtab, NULL, ovout);
+
+  (*htab->params->place_spu_section) (htab->toe, NULL, ".toe");
+}
+
 /* Functions to handle embedded spu_ovl.o object.  */
 
 static void *
@@ -1388,6 +1824,15 @@ spu_elf_open_builtin_lib (bfd **ovl_bfd, const struct _ovl_stream *stream)
   return *ovl_bfd != NULL;
 }
 
+static unsigned int
+overlay_index (asection *sec)
+{
+  if (sec == NULL
+      || sec->output_section == bfd_abs_section_ptr)
+    return 0;
+  return spu_elf_section_data (sec->output_section)->u.o.ovl_index;
+}
+
 /* Define an STT_OBJECT symbol.  */
 
 static struct elf_link_hash_entry *
@@ -1410,7 +1855,7 @@ define_ovtab_symbol (struct spu_link_hash_table *htab, const char *name)
       h->ref_regular_nonweak = 1;
       h->non_elf = 0;
     }
-  else
+  else if (h->root.u.def.section->owner != NULL)
     {
       (*_bfd_error_handler) (_("%B is not allowed to define %s"),
                             h->root.u.def.section->owner,
@@ -1418,14 +1863,21 @@ define_ovtab_symbol (struct spu_link_hash_table *htab, const char *name)
       bfd_set_error (bfd_error_bad_value);
       return NULL;
     }
+  else
+    {
+      (*_bfd_error_handler) (_("you are not allowed to define %s in a script"),
+                            h->root.root.string);
+      bfd_set_error (bfd_error_bad_value);
+      return NULL;
+    }
 
   return h;
 }
 
 /* Fill in all stubs and the overlay tables.  */
 
-bfd_boolean
-spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms)
+static bfd_boolean
+spu_elf_build_stubs (struct bfd_link_info *info)
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
   struct elf_link_hash_entry *h;
@@ -1434,7 +1886,6 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms)
   bfd *obfd;
   unsigned int i;
 
-  htab->emit_stub_syms = emit_syms;
   if (htab->stub_count == NULL)
     return TRUE;
 
@@ -1449,31 +1900,39 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms)
        htab->stub_sec[i]->size = 0;
       }
 
-  h = elf_link_hash_lookup (&htab->elf, "__ovly_load", FALSE, FALSE, FALSE);
-  htab->ovly_load = h;
-  BFD_ASSERT (h != NULL
-             && (h->root.type == bfd_link_hash_defined
-                 || h->root.type == bfd_link_hash_defweak)
-             && h->def_regular);
-
-  s = h->root.u.def.section->output_section;
-  if (spu_elf_section_data (s)->u.o.ovl_index)
+  for (i = 0; i < 2; i++)
     {
-      (*_bfd_error_handler) (_("%s in overlay section"),
-                            h->root.root.string);
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
-    }
+      h = htab->ovly_entry[i];
+      BFD_ASSERT (h != NULL);
 
-  h = elf_link_hash_lookup (&htab->elf, "__ovly_return", FALSE, FALSE, FALSE);
-  htab->ovly_return = h;
+      if ((h->root.type == bfd_link_hash_defined
+          || h->root.type == bfd_link_hash_defweak)
+         && h->def_regular)
+       {
+         s = h->root.u.def.section->output_section;
+         if (spu_elf_section_data (s)->u.o.ovl_index)
+           {
+             (*_bfd_error_handler) (_("%s in overlay section"),
+                                    h->root.root.string);
+             bfd_set_error (bfd_error_bad_value);
+             return FALSE;
+           }
+       }
+      else
+       BFD_ASSERT (0);
+    }
 
   /* Fill in all the stubs.  */
   process_stubs (info, TRUE);
+  if (!htab->stub_err)
+    elf_link_hash_traverse (&htab->elf, build_spuear_stubs, info);
 
-  elf_link_hash_traverse (&htab->elf, build_spuear_stubs, info);
   if (htab->stub_err)
-    return FALSE;
+    {
+      (*_bfd_error_handler) (_("overlay stub relocation overflow"));
+      bfd_set_error (bfd_error_bad_value);
+      return FALSE;
+    }
 
   for (i = 0; i <= htab->num_overlays; i++)
     {
@@ -1486,61 +1945,169 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms)
       htab->stub_sec[i]->rawsize = 0;
     }
 
-  if (htab->stub_err)
-    {
-      (*_bfd_error_handler) (_("overlay stub relocation overflow"));
-      bfd_set_error (bfd_error_bad_value);
-      return FALSE;
-    }
+  if (htab->ovtab == NULL || htab->ovtab->size == 0)
+    return TRUE;
 
   htab->ovtab->contents = bfd_zalloc (htab->ovtab->owner, htab->ovtab->size);
   if (htab->ovtab->contents == NULL)
     return FALSE;
 
-  /* Write out _ovly_table.  */
   p = htab->ovtab->contents;
-  /* set low bit of .size to mark non-overlay area as present.  */
-  p[7] = 1;
-  obfd = htab->ovtab->output_section->owner;
-  for (s = obfd->sections; s != NULL; s = s->next)
+  if (htab->params->ovly_flavour == ovly_soft_icache)
     {
-      unsigned int ovl_index = spu_elf_section_data (s)->u.o.ovl_index;
+      bfd_vma off;
+
+      h = define_ovtab_symbol (htab, "__icache_tag_array");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 0;
+      h->size = 16 << htab->num_lines_log2;
+      off = h->size;
+
+      h = define_ovtab_symbol (htab, "__icache_tag_array_size");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 16 << htab->num_lines_log2;
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_rewrite_to");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = off;
+      h->size = 16 << htab->num_lines_log2;
+      off += h->size;
+
+      h = define_ovtab_symbol (htab, "__icache_rewrite_to_size");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 16 << htab->num_lines_log2;
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_rewrite_from");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = off;
+      h->size = 16 << (htab->fromelem_size_log2 + htab->num_lines_log2);
+      off += h->size;
+
+      h = define_ovtab_symbol (htab, "__icache_rewrite_from_size");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 16 << (htab->fromelem_size_log2
+                                  + htab->num_lines_log2);
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_log2_fromelemsize");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->fromelem_size_log2;
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_base");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->ovl_sec[0]->vma;
+      h->root.u.def.section = bfd_abs_section_ptr;
+      h->size = htab->num_buf << htab->line_size_log2;
+
+      h = define_ovtab_symbol (htab, "__icache_linesize");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 1 << htab->line_size_log2;
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_log2_linesize");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->line_size_log2;
+      h->root.u.def.section = bfd_abs_section_ptr;
 
-      if (ovl_index != 0)
+      h = define_ovtab_symbol (htab, "__icache_neg_log2_linesize");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = -htab->line_size_log2;
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_cachesize");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 1 << (htab->num_lines_log2 + htab->line_size_log2);
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_log2_cachesize");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->num_lines_log2 + htab->line_size_log2;
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      h = define_ovtab_symbol (htab, "__icache_neg_log2_cachesize");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = -(htab->num_lines_log2 + htab->line_size_log2);
+      h->root.u.def.section = bfd_abs_section_ptr;
+
+      if (htab->init != NULL && htab->init->size != 0)
        {
-         unsigned long off = ovl_index * 16;
-         unsigned int ovl_buf = spu_elf_section_data (s)->u.o.ovl_buf;
+         htab->init->contents = bfd_zalloc (htab->init->owner,
+                                            htab->init->size);
+         if (htab->init->contents == NULL)
+           return FALSE;
 
-         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.  */
-         bfd_put_32 (htab->ovtab->owner, ovl_buf, p + off + 12);
+         h = define_ovtab_symbol (htab, "__icache_fileoff");
+         if (h == NULL)
+           return FALSE;
+         h->root.u.def.value = 0;
+         h->root.u.def.section = htab->init;
+         h->size = 8;
        }
     }
+  else
+    {
+      /* Write out _ovly_table.  */
+      /* set low bit of .size to mark non-overlay area as present.  */
+      p[7] = 1;
+      obfd = htab->ovtab->output_section->owner;
+      for (s = obfd->sections; s != NULL; s = s->next)
+       {
+         unsigned int ovl_index = spu_elf_section_data (s)->u.o.ovl_index;
 
-  h = define_ovtab_symbol (htab, "_ovly_table");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = 16;
-  h->size = htab->num_overlays * 16;
+         if (ovl_index != 0)
+           {
+             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.  */
+             bfd_put_32 (htab->ovtab->owner, ovl_buf, p + off + 12);
+           }
+       }
 
-  h = define_ovtab_symbol (htab, "_ovly_table_end");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16 + 16;
-  h->size = 0;
+      h = define_ovtab_symbol (htab, "_ovly_table");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = 16;
+      h->size = htab->num_overlays * 16;
 
-  h = define_ovtab_symbol (htab, "_ovly_buf_table");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16 + 16;
-  h->size = htab->num_buf * 4;
+      h = define_ovtab_symbol (htab, "_ovly_table_end");
+      if (h == NULL)
+       return FALSE;
+      h->root.u.def.value = htab->num_overlays * 16 + 16;
+      h->size = 0;
 
-  h = define_ovtab_symbol (htab, "_ovly_buf_table_end");
-  if (h == NULL)
-    return FALSE;
-  h->root.u.def.value = htab->num_overlays * 16 + 16 + htab->num_buf * 4;
-  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 + 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 + 16 + htab->num_buf * 4;
+      h->size = 0;
+    }
 
   h = define_ovtab_symbol (htab, "_EAR_");
   if (h == NULL)
@@ -1556,29 +2123,16 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms)
    LO .. HI inclusive, and stash some parameters for --auto-overlay.  */
 
 asection *
-spu_elf_check_vma (struct bfd_link_info *info,
-                  int auto_overlay,
-                  unsigned int lo,
-                  unsigned int hi,
-                  unsigned int overlay_fixed,
-                  unsigned int reserved,
-                  void (*spu_elf_load_ovl_mgr) (void),
-                  FILE *(*spu_elf_open_overlay_script) (void),
-                  void (*spu_elf_relink) (void))
+spu_elf_check_vma (struct bfd_link_info *info)
 {
   struct elf_segment_map *m;
   unsigned int i;
   struct spu_link_hash_table *htab = spu_hash_table (info);
   bfd *abfd = info->output_bfd;
+  bfd_vma hi = htab->params->local_store_hi;
+  bfd_vma lo = htab->params->local_store_lo;
 
-  if (auto_overlay & AUTO_OVERLAY)
-    htab->auto_overlay = auto_overlay;
   htab->local_store = hi + 1 - lo;
-  htab->overlay_fixed = overlay_fixed;
-  htab->reserved = reserved;
-  htab->spu_elf_load_ovl_mgr = spu_elf_load_ovl_mgr;
-  htab->spu_elf_open_overlay_script = spu_elf_open_overlay_script;
-  htab->spu_elf_relink = spu_elf_relink;
 
   for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next)
     if (m->p_type == PT_LOAD)
@@ -1589,22 +2143,25 @@ spu_elf_check_vma (struct bfd_link_info *info,
                || m->sections[i]->vma + m->sections[i]->size - 1 > hi))
          return m->sections[i];
 
-  /* No need for overlays if it all fits.  */
-  htab->auto_overlay = 0;
   return NULL;
 }
 
 /* OFFSET in SEC (presumably) is the beginning of a function prologue.
-   Search for stack adjusting insns, and return the sp delta.  */
+   Search for stack adjusting insns, and return the sp delta.
+   If a store of lr is found save the instruction offset to *LR_STORE.
+   If a stack adjusting instruction is found, save that offset to
+   *SP_ADJUST.  */
 
 static int
-find_function_stack_adjust (asection *sec, bfd_vma offset)
+find_function_stack_adjust (asection *sec,
+                           bfd_vma offset,
+                           bfd_vma *lr_store,
+                           bfd_vma *sp_adjust)
 {
-  int unrecog;
   int reg[128];
 
   memset (reg, 0, sizeof (reg));
-  for (unrecog = 0; offset + 4 <= sec->size && unrecog < 32; offset += 4)
+  for ( ; offset + 4 <= sec->size; offset += 4)
     {
       unsigned char buf[4];
       int rt, ra;
@@ -1614,11 +2171,16 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
       if (!bfd_get_section_contents (sec->owner, sec, buf, offset, 4))
        break;
 
-      if (buf[0] == 0x24 /* stqd */)
-       continue;
-
       rt = buf[3] & 0x7f;
       ra = ((buf[2] & 0x3f) << 1) | (buf[3] >> 7);
+
+      if (buf[0] == 0x24 /* stqd */)
+       {
+         if (rt == 0 /* lr */ && ra == 1 /* sp */)
+           *lr_store = offset;
+         continue;
+       }
+
       /* Partly decoded immediate field.  */
       imm = (buf[1] << 9) | (buf[2] << 1) | (buf[3] >> 7);
 
@@ -1630,8 +2192,9 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
 
          if (rt == 1 /* sp */)
            {
-             if (imm > 0)
+             if (reg[rt] > 0)
                break;
+             *sp_adjust = offset;
              return reg[rt];
            }
        }
@@ -1641,7 +2204,25 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
 
          reg[rt] = reg[ra] + reg[rb];
          if (rt == 1)
-           return reg[rt];
+           {
+             if (reg[rt] > 0)
+               break;
+             *sp_adjust = offset;
+             return reg[rt];
+           }
+       }
+      else if (buf[0] == 0x08 && (buf[1] & 0xe0) == 0 /* sf */)
+       {
+         int rb = ((buf[1] & 0x1f) << 2) | ((buf[2] & 0xc0) >> 6);
+
+         reg[rt] = reg[rb] - reg[ra];
+         if (rt == 1)
+           {
+             if (reg[rt] > 0)
+               break;
+             *sp_adjust = offset;
+             return reg[rt];
+           }
        }
       else if ((buf[0] & 0xfc) == 0x40 /* il, ilh, ilhu, ila */)
        {
@@ -1654,7 +2235,7 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
              if (buf[0] == 0x40 /* il */)
                {
                  if ((buf[1] & 0x80) == 0)
-                   goto unknown_insn;
+                   continue;
                  imm = (imm ^ 0x8000) - 0x8000;
                }
              else if ((buf[1] & 0x80) == 0 /* ilhu */)
@@ -1675,18 +2256,33 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
          reg[rt] = reg[ra] | imm;
          continue;
        }
-      else if ((buf[0] == 0x33 && imm == 1 /* brsl .+4 */)
-              || (buf[0] == 0x08 && (buf[1] & 0xe0) == 0 /* sf */))
+      else if (buf[0] == 0x32 && (buf[1] & 0x80) != 0 /* fsmbi */)
+       {
+         reg[rt] = (  ((imm & 0x8000) ? 0xff000000 : 0)
+                    | ((imm & 0x4000) ? 0x00ff0000 : 0)
+                    | ((imm & 0x2000) ? 0x0000ff00 : 0)
+                    | ((imm & 0x1000) ? 0x000000ff : 0));
+         continue;
+       }
+      else if (buf[0] == 0x16 /* andbi */)
        {
-         /* Used in pic reg load.  Say rt is trashed.  */
+         imm >>= 7;
+         imm &= 0xff;
+         imm |= imm << 8;
+         imm |= imm << 16;
+         reg[rt] = reg[ra] & imm;
+         continue;
+       }
+      else if (buf[0] == 0x33 && imm == 1 /* brsl .+4 */)
+       {
+         /* Used in pic reg load.  Say rt is trashed.  Won't be used
+            in stack adjust, but we need to continue past this branch.  */
          reg[rt] = 0;
          continue;
        }
       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:
-      ++unrecog;
     }
 
   return 0;
@@ -1722,69 +2318,6 @@ sort_syms (const void *a, const void *b)
   return *s1 < *s2 ? -1 : 1;
 }
 
-struct call_info
-{
-  struct function_info *fun;
-  struct call_info *next;
-  unsigned int count;
-  unsigned int max_depth;
-  unsigned int is_tail : 1;
-  unsigned int is_pasted : 1;
-};
-
-struct function_info
-{
-  /* List of functions called.  Also branches to hot/cold part of
-     function.  */
-  struct call_info *call_list;
-  /* For hot/cold part of function, point to owner.  */
-  struct function_info *start;
-  /* Symbol at start of function.  */
-  union {
-    Elf_Internal_Sym *sym;
-    struct elf_link_hash_entry *h;
-  } u;
-  /* Function section.  */
-  asection *sec;
-  asection *rodata;
-  /* Where last called from, and number of sections called from.  */
-  asection *last_caller;
-  unsigned int call_count;
-  /* Address range of (this part of) function.  */
-  bfd_vma lo, hi;
-  /* Stack usage.  */
-  int stack;
-  /* Distance from root of call tree.  Tail and hot/cold branches
-     count as one deeper.  We aren't counting stack frames here.  */
-  unsigned int depth;
-  /* Set if global symbol.  */
-  unsigned int global : 1;
-  /* Set if known to be start of function (as distinct from a hunk
-     in hot/cold section.  */
-  unsigned int is_func : 1;
-  /* Set if not a root node.  */
-  unsigned int non_root : 1;
-  /* Flags used during call tree traversal.  It's cheaper to replicate
-     the visit flags than have one which needs clearing after a traversal.  */
-  unsigned int visit1 : 1;
-  unsigned int visit2 : 1;
-  unsigned int marking : 1;
-  unsigned int visit3 : 1;
-  unsigned int visit4 : 1;
-  unsigned int visit5 : 1;
-  unsigned int visit6 : 1;
-  unsigned int visit7 : 1;
-};
-
-struct spu_elf_stack_info
-{
-  int num_fun;
-  int max_fun;
-  /* Variable size array describing functions, one per contiguous
-     address range belonging to a function.  */
-  struct function_info fun[1];
-};
-
 /* Allocate a struct spu_elf_stack_info with MAX_FUN struct function_info
    entries for section SEC.  */
 
@@ -1861,10 +2394,7 @@ maybe_insert_function (asection *sec,
        return &sinfo->fun[i];
     }
 
-  if (++i < sinfo->num_fun)
-    memmove (&sinfo->fun[i + 1], &sinfo->fun[i],
-            (sinfo->num_fun - i) * sizeof (sinfo->fun[i]));
-  else if (i >= sinfo->max_fun)
+  if (sinfo->num_fun >= sinfo->max_fun)
     {
       bfd_size_type amt = sizeof (struct spu_elf_stack_info);
       bfd_size_type old = amt;
@@ -1878,6 +2408,10 @@ maybe_insert_function (asection *sec,
       memset ((char *) sinfo + old, 0, amt - old);
       sec_data->u.i.stack_info = sinfo;
     }
+
+  if (++i < sinfo->num_fun)
+    memmove (&sinfo->fun[i + 1], &sinfo->fun[i],
+            (sinfo->num_fun - i) * sizeof (sinfo->fun[i]));
   sinfo->fun[i].is_func = is_func;
   sinfo->fun[i].global = global;
   sinfo->fun[i].sec = sec;
@@ -1887,7 +2421,11 @@ maybe_insert_function (asection *sec,
     sinfo->fun[i].u.sym = sym_h;
   sinfo->fun[i].lo = off;
   sinfo->fun[i].hi = off + size;
-  sinfo->fun[i].stack = -find_function_stack_adjust (sec, off);
+  sinfo->fun[i].lr_store = -1;
+  sinfo->fun[i].sp_adjust = -1;
+  sinfo->fun[i].stack = -find_function_stack_adjust (sec, off,
+                                                    &sinfo->fun[i].lr_store,
+                                                    &sinfo->fun[i].sp_adjust);
   sinfo->num_fun += 1;
   return &sinfo->fun[i];
 }
@@ -2030,6 +2568,7 @@ find_function (asection *sec, bfd_vma offset, struct bfd_link_info *info)
     }
   info->callbacks->einfo (_("%A:0x%v not found in function table\n"),
                          sec, offset);
+  bfd_set_error (bfd_error_bad_value);
   return NULL;
 }
 
@@ -2085,10 +2624,9 @@ copy_callee (struct function_info *caller, const struct call_info *call)
    overlay stub sections.  */
 
 static bfd_boolean
-interesting_section (asection *s, bfd *obfd)
+interesting_section (asection *s)
 {
-  return (s->output_section != NULL
-         && s->output_section->owner == obfd
+  return (s->output_section != bfd_abs_section_ptr
          && ((s->flags & (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_IN_MEMORY))
              == (SEC_ALLOC | SEC_LOAD | SEC_CODE))
          && s->size != 0);
@@ -2107,11 +2645,11 @@ mark_functions_via_relocs (asection *sec,
 {
   Elf_Internal_Rela *internal_relocs, *irelaend, *irela;
   Elf_Internal_Shdr *symtab_hdr;
-  Elf_Internal_Sym *syms;
   void *psyms;
+  unsigned int priority = 0;
   static bfd_boolean warned;
 
-  if (!interesting_section (sec, info->output_bfd)
+  if (!interesting_section (sec)
       || sec->reloc_count == 0)
     return TRUE;
 
@@ -2122,7 +2660,6 @@ mark_functions_via_relocs (asection *sec,
 
   symtab_hdr = &elf_tdata (sec->owner)->symtab_hdr;
   psyms = &symtab_hdr->contents;
-  syms = *(Elf_Internal_Sym **) psyms;
   irela = internal_relocs;
   irelaend = irela + sec->reloc_count;
   for (; irela < irelaend; irela++)
@@ -2143,7 +2680,7 @@ mark_functions_via_relocs (asection *sec,
          && r_type != R_SPU_ADDR16)
        {
          reject = TRUE;
-         if (!(call_tree && spu_hash_table (info)->auto_overlay))
+         if (!(call_tree && spu_hash_table (info)->params->auto_overlay))
            continue;
        }
 
@@ -2152,8 +2689,7 @@ mark_functions_via_relocs (asection *sec,
        return FALSE;
 
       if (sym_sec == NULL
-         || sym_sec->output_section == NULL
-         || sym_sec->output_section->owner != info->output_bfd)
+         || sym_sec->output_section == bfd_abs_section_ptr)
        continue;
 
       is_call = FALSE;
@@ -2167,6 +2703,12 @@ mark_functions_via_relocs (asection *sec,
          if (is_branch (insn))
            {
              is_call = (insn[0] & 0xfd) == 0x31;
+             priority = insn[1] & 0x0f;
+             priority <<= 8;
+             priority |= insn[2];
+             priority <<= 8;
+             priority |= insn[3];
+             priority >>= 7;
              if ((sym_sec->flags & (SEC_ALLOC | SEC_LOAD | SEC_CODE))
                  != (SEC_ALLOC | SEC_LOAD | SEC_CODE))
                {
@@ -2183,7 +2725,7 @@ mark_functions_via_relocs (asection *sec,
          else
            {
              reject = TRUE;
-             if (!(call_tree && spu_hash_table (info)->auto_overlay)
+             if (!(call_tree && spu_hash_table (info)->params->auto_overlay)
                  || is_hint (insn))
                continue;
            }
@@ -2247,6 +2789,7 @@ mark_functions_via_relocs (asection *sec,
        return FALSE;
       callee->is_tail = !is_call;
       callee->is_pasted = FALSE;
+      callee->priority = priority;
       callee->count = 0;
       if (callee->fun->last_caller != sec)
        {
@@ -2270,7 +2813,14 @@ mark_functions_via_relocs (asection *sec,
              callee->fun->is_func = TRUE;
            }
          else if (callee->fun->start == NULL)
-           callee->fun->start = caller;
+           {
+             struct function_info *caller_start = caller;
+             while (caller_start->start)
+               caller_start = caller_start->start;
+
+             if (caller_start != callee->fun)
+               callee->fun->start = caller_start;
+           }
          else
            {
              struct function_info *callee_start;
@@ -2297,7 +2847,7 @@ mark_functions_via_relocs (asection *sec,
    These sections are pasted together to form a single function.  */
 
 static bfd_boolean
-pasted_function (asection *sec, struct bfd_link_info *info)
+pasted_function (asection *sec)
 {
   struct bfd_link_order *l;
   struct _spu_elf_section_data *sec_data;
@@ -2346,8 +2896,9 @@ pasted_function (asection *sec, struct bfd_link_info *info)
        fun_start = &sinfo->fun[sinfo->num_fun - 1];
     }
 
-  info->callbacks->einfo (_("%A link_order not found\n"), sec);
-  return FALSE;
+  /* Don't return an error if we did not find a function preceding this
+     section.  The section may have incorrect flags.  */
+  return TRUE;
 }
 
 /* Map address ranges in code sections to functions.  */
@@ -2371,7 +2922,6 @@ discover_functions (struct bfd_link_info *info)
   sec_arr = bfd_zmalloc (bfd_idx * sizeof (*sec_arr));
   if (sec_arr == NULL)
     return FALSE;
-
   
   for (ibfd = info->input_bfds, bfd_idx = 0;
        ibfd != NULL;
@@ -2394,7 +2944,7 @@ discover_functions (struct bfd_link_info *info)
        {
          if (!gaps)
            for (sec = ibfd->sections; sec != NULL && !gaps; sec = sec->next)
-             if (interesting_section (sec, info->output_bfd))
+             if (interesting_section (sec))
                {
                  gaps = TRUE;
                  break;
@@ -2402,15 +2952,18 @@ discover_functions (struct bfd_link_info *info)
          continue;
        }
 
-      syms = (Elf_Internal_Sym *) symtab_hdr->contents;
-      if (syms == NULL)
+      if (symtab_hdr->contents != NULL)
        {
-         syms = bfd_elf_get_elf_syms (ibfd, symtab_hdr, symcount, 0,
-                                      NULL, NULL, NULL);
-         symtab_hdr->contents = (void *) syms;
-         if (syms == NULL)
-           return FALSE;
+         /* Don't use cached symbols since the generic ELF linker
+            code only reads local symbols, and we need globals too.  */ 
+         free (symtab_hdr->contents);
+         symtab_hdr->contents = NULL;
        }
+      syms = bfd_elf_get_elf_syms (ibfd, symtab_hdr, symcount, 0,
+                                  NULL, NULL, NULL);
+      symtab_hdr->contents = (void *) syms;
+      if (syms == NULL)
+       return FALSE;
 
       /* Select defined function symbols that are going to be output.  */
       psyms = bfd_malloc ((symcount + 1) * sizeof (*psyms));
@@ -2428,7 +2981,7 @@ discover_functions (struct bfd_link_info *info)
            asection *s;
 
            *p = s = bfd_section_from_elf_index (ibfd, sy->st_shndx);
-           if (s != NULL && interesting_section (s, info->output_bfd))
+           if (s != NULL && interesting_section (s))
              *psy++ = sy;
          }
       symcount = psy - psyms;
@@ -2470,7 +3023,7 @@ discover_functions (struct bfd_link_info *info)
        }
 
       for (sec = ibfd->sections; sec != NULL && !gaps; sec = sec->next)
-       if (interesting_section (sec, info->output_bfd))
+       if (interesting_section (sec))
          gaps |= check_function_ranges (sec, info);
     }
 
@@ -2511,7 +3064,7 @@ discover_functions (struct bfd_link_info *info)
 
          gaps = FALSE;
          for (sec = ibfd->sections; sec != NULL && !gaps; sec = sec->next)
-           if (interesting_section (sec, info->output_bfd))
+           if (interesting_section (sec))
              gaps |= check_function_ranges (sec, info);
          if (!gaps)
            continue;
@@ -2546,14 +3099,14 @@ discover_functions (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, info->output_bfd))
+           if (interesting_section (sec))
              {
                struct _spu_elf_section_data *sec_data;
                struct spu_elf_stack_info *sinfo;
 
                sec_data = spu_elf_section_data (sec);
                sinfo = sec_data->u.i.stack_info;
-               if (sinfo != NULL)
+               if (sinfo != NULL && sinfo->num_fun != 0)
                  {
                    int fun_idx;
                    bfd_vma hi = sec->size;
@@ -2563,10 +3116,12 @@ discover_functions (struct bfd_link_info *info)
                        sinfo->fun[fun_idx].hi = hi;
                        hi = sinfo->fun[fun_idx].lo;
                      }
+
+                   sinfo->fun[0].lo = 0;
                  }
                /* No symbols in this section.  Must be .init or .fini
                   or something similar.  */
-               else if (!pasted_function (sec, info))
+               else if (!pasted_function (sec))
                  return FALSE;
              }
        }
@@ -2695,9 +3250,9 @@ remove_cycles (struct function_info *fun,
   callp = &fun->call_list;
   while ((call = *callp) != NULL)
     {
+      call->max_depth = depth + !call->is_pasted;
       if (!call->fun->visit2)
        {
-         call->max_depth = depth + !call->is_pasted;
          if (!remove_cycles (call->fun, info, &call->max_depth))
            return FALSE;
          if (max_depth < call->max_depth)
@@ -2705,7 +3260,10 @@ remove_cycles (struct function_info *fun,
        }
       else if (call->fun->marking)
        {
-         if (!spu_hash_table (info)->auto_overlay)
+         struct spu_link_hash_table *htab = spu_hash_table (info);
+
+         if (!htab->params->auto_overlay
+             && htab->params->stack_analysis)
            {
              const char *f1 = func_name (fun);
              const char *f2 = func_name (call->fun);
@@ -2725,6 +3283,23 @@ remove_cycles (struct function_info *fun,
   return TRUE;
 }
 
+/* Check that we actually visited all nodes in remove_cycles.  If we
+   didn't, then there is some cycle in the call graph not attached to
+   any root node.  Arbitrarily choose a node in the cycle as a new
+   root and break the cycle.  */
+
+static bfd_boolean
+mark_detached_root (struct function_info *fun,
+                   struct bfd_link_info *info,
+                   void *param)
+{
+  if (fun->visit2)
+    return TRUE;
+  fun->non_root = FALSE;
+  *(unsigned int *) param = 0;
+  return remove_cycles (fun, info, param);
+}
+
 /* Populate call_list for each function.  */
 
 static bfd_boolean
@@ -2748,7 +3323,7 @@ build_call_tree (struct bfd_link_info *info)
 
   /* Transfer call info from hot/cold section part of function
      to main entry.  */
-  if (!spu_hash_table (info)->auto_overlay
+  if (!spu_hash_table (info)->params->auto_overlay
       && !for_each_node (transfer_calls, info, 0, FALSE))
     return FALSE;
 
@@ -2759,10 +3334,13 @@ build_call_tree (struct bfd_link_info *info)
   /* Remove cycles from the call graph.  We start from the root node(s)
      so that we break cycles in a reasonable place.  */
   depth = 0;
-  return for_each_node (remove_cycles, info, &depth, TRUE);
+  if (!for_each_node (remove_cycles, info, &depth, TRUE))
+    return FALSE;
+
+  return for_each_node (mark_detached_root, info, &depth, FALSE);
 }
 
-/* qsort predicate to sort calls by max_depth then count.  */
+/* qsort predicate to sort calls by priority, max_depth then count.  */
 
 static int
 sort_calls (const void *a, const void *b)
@@ -2771,6 +3349,10 @@ sort_calls (const void *a, const void *b)
   struct call_info *const *c2 = b;
   int delta;
 
+  delta = (*c2)->priority - (*c1)->priority;
+  if (delta != 0)
+    return delta;
+
   delta = (*c2)->max_depth - (*c1)->max_depth;
   if (delta != 0)
     return delta;
@@ -2779,7 +3361,7 @@ sort_calls (const void *a, const void *b)
   if (delta != 0)
     return delta;
 
-  return c1 - c2;
+  return (char *) c1 - (char *) c2;
 }
 
 struct _mos_param {
@@ -2807,13 +3389,21 @@ mark_overlay_section (struct function_info *fun,
   struct call_info *call;
   unsigned int count;
   struct _mos_param *mos_param = param;
+  struct spu_link_hash_table *htab = spu_hash_table (info);
 
   if (fun->visit4)
     return TRUE;
 
   fun->visit4 = TRUE;
-  if (!fun->sec->linker_mark)
+  if (!fun->sec->linker_mark
+      && (htab->params->ovly_flavour != ovly_soft_icache
+         || htab->params->non_ia_text
+         || strncmp (fun->sec->name, ".text.ia.", 9) == 0
+         || strcmp (fun->sec->name, ".init") == 0
+         || strcmp (fun->sec->name, ".fini") == 0))
     {
+      unsigned int size;
+
       fun->sec->linker_mark = 1;
       fun->sec->gc_mark = 1;
       fun->sec->segment_mark = 0;
@@ -2821,10 +3411,11 @@ mark_overlay_section (struct function_info *fun,
         be!), and SEC_CODE is clear on rodata sections.  We use
         this flag to differentiate the two overlay section types.  */
       fun->sec->flags |= SEC_CODE;
-      if (spu_hash_table (info)->auto_overlay & OVERLAY_RODATA)
+
+      size = fun->sec->size;
+      if (htab->params->auto_overlay & OVERLAY_RODATA)
        {
          char *name = NULL;
-         unsigned int size;
 
          /* Find the rodata section corresponding to this function's
             text section.  */
@@ -2873,18 +3464,25 @@ mark_overlay_section (struct function_info *fun,
              fun->rodata = rodata;
              if (fun->rodata)
                {
-                 fun->rodata->linker_mark = 1;
-                 fun->rodata->gc_mark = 1;
-                 fun->rodata->flags &= ~SEC_CODE;
+                 size += fun->rodata->size;
+                 if (htab->params->line_size != 0
+                     && size > htab->params->line_size)
+                   {
+                     size -= fun->rodata->size;
+                     fun->rodata = NULL;
+                   }
+                 else
+                   {
+                     fun->rodata->linker_mark = 1;
+                     fun->rodata->gc_mark = 1;
+                     fun->rodata->flags &= ~SEC_CODE;
+                   }
                }
              free (name);
            }
-         size = fun->sec->size;
-         if (fun->rodata)
-           size += fun->rodata->size;
-         if (mos_param->max_overlay_size < size)
-           mos_param->max_overlay_size = size;
        }
+      if (mos_param->max_overlay_size < size)
+       mos_param->max_overlay_size = size;
     }
 
   for (count = 0, call = fun->call_list; call != NULL; call = call->next)
@@ -2924,9 +3522,10 @@ mark_overlay_section (struct function_info *fun,
     }
 
   /* Don't put entry code into an overlay.  The overlay manager needs
-     a stack!  */
+     a stack!  Also, don't mark .ovl.init as an overlay.  */
   if (fun->lo + fun->sec->output_offset + fun->sec->output_section->vma
-      == info->output_bfd->start_address)
+      == info->output_bfd->start_address
+      || strncmp (fun->sec->output_section->name, ".ovl.init", 9) == 0)
     {
       fun->sec->linker_mark = 0;
       if (fun->rodata != NULL)
@@ -2935,6 +3534,11 @@ mark_overlay_section (struct function_info *fun,
   return TRUE;
 }
 
+/* If non-zero then unmark functions called from those within sections
+   that we need to unmark.  Unfortunately this isn't reliable since the
+   call graph cannot know the destination of function pointer calls.  */
+#define RECURSE_UNMARK 0
+
 struct _uos_param {
   asection *exclude_input_section;
   asection *exclude_output_section;
@@ -2962,9 +3566,10 @@ unmark_overlay_section (struct function_info *fun,
       || fun->sec->output_section == uos_param->exclude_output_section)
     excluded = 1;
 
-  uos_param->clearing += excluded;
+  if (RECURSE_UNMARK)
+    uos_param->clearing += excluded;
 
-  if (uos_param->clearing)
+  if (RECURSE_UNMARK ? uos_param->clearing : excluded)
     {
       fun->sec->linker_mark = 0;
       if (fun->rodata)
@@ -2975,7 +3580,8 @@ unmark_overlay_section (struct function_info *fun,
     if (!unmark_overlay_section (call->fun, info, param))
       return FALSE;
 
-  uos_param->clearing -= excluded;
+  if (RECURSE_UNMARK)
+    uos_param->clearing -= excluded;
   return TRUE;
 }
 
@@ -3007,18 +3613,19 @@ collect_lib_sections (struct function_info *fun,
   size = fun->sec->size;
   if (fun->rodata)
     size += fun->rodata->size;
-  if (size > lib_param->lib_size)
-    return TRUE;
 
-  *lib_param->lib_sections++ = fun->sec;
-  fun->sec->gc_mark = 0;
-  if (fun->rodata && fun->rodata->linker_mark && fun->rodata->gc_mark)
+  if (size <= lib_param->lib_size)
     {
-      *lib_param->lib_sections++ = fun->rodata;
-      fun->rodata->gc_mark = 0;
+      *lib_param->lib_sections++ = fun->sec;
+      fun->sec->gc_mark = 0;
+      if (fun->rodata && fun->rodata->linker_mark && fun->rodata->gc_mark)
+       {
+         *lib_param->lib_sections++ = fun->rodata;
+         fun->rodata->gc_mark = 0;
+       }
+      else
+       *lib_param->lib_sections++ = NULL;
     }
-  else
-    *lib_param->lib_sections++ = NULL;
 
   for (call = fun->call_list; call != NULL; call = call->next)
     collect_lib_sections (call->fun, info, param);
@@ -3071,6 +3678,7 @@ auto_ovl_lib_functions (struct bfd_link_info *info, unsigned int lib_size)
   unsigned int i, lib_count;
   struct _cl_param collect_lib_param;
   struct function_info dummy_caller;
+  struct spu_link_hash_table *htab;
 
   memset (&dummy_caller, 0, sizeof (dummy_caller));
   lib_count = 0;
@@ -3102,6 +3710,7 @@ auto_ovl_lib_functions (struct bfd_link_info *info, unsigned int lib_size)
   if (lib_count > 1)
     qsort (lib_sections, lib_count, 2 * sizeof (*lib_sections), sort_lib);
 
+  htab = spu_hash_table (info);
   for (i = 0; i < lib_count; i++)
     {
       unsigned int tmp, stub_size;
@@ -3133,7 +3742,7 @@ auto_ovl_lib_functions (struct bfd_link_info *info, unsigned int lib_size)
                    if (p->fun == call->fun)
                      break;
                  if (!p)
-                   stub_size += OVL_STUB_SIZE;
+                   stub_size += ovl_stub_size (htab->params);
                }
        }
       if (tmp + stub_size < lib_size)
@@ -3151,7 +3760,7 @@ auto_ovl_lib_functions (struct bfd_link_info *info, unsigned int lib_size)
          while ((p = *pp) != NULL)
            if (!p->fun->sec->linker_mark)
              {
-               lib_size += OVL_STUB_SIZE;
+               lib_size += ovl_stub_size (htab->params);
                *pp = p->next;
                free (p);
              }
@@ -3338,27 +3947,30 @@ sum_stack (struct function_info *fun,
     sum_stack_param->overall_stack = cum_stack;
 
   htab = spu_hash_table (info);
-  if (htab->auto_overlay)
+  if (htab->params->auto_overlay)
     return TRUE;
 
   f1 = func_name (fun);
-  if (!fun->non_root)
-    info->callbacks->info (_("  %s: 0x%v\n"), f1, (bfd_vma) cum_stack);
-  info->callbacks->minfo (_("%s: 0x%v 0x%v\n"),
-                         f1, (bfd_vma) stack, (bfd_vma) cum_stack);
-
-  if (has_call)
+  if (htab->params->stack_analysis)
     {
-      info->callbacks->minfo (_("  calls:\n"));
-      for (call = fun->call_list; call; call = call->next)
-       if (!call->is_pasted)
-         {
-           const char *f2 = func_name (call->fun);
-           const char *ann1 = call->fun == max ? "*" : " ";
-           const char *ann2 = call->is_tail ? "t" : " ";
+      if (!fun->non_root)
+       info->callbacks->info (_("  %s: 0x%v\n"), f1, (bfd_vma) cum_stack);
+      info->callbacks->minfo (_("%s: 0x%v 0x%v\n"),
+                             f1, (bfd_vma) stack, (bfd_vma) cum_stack);
 
-           info->callbacks->minfo (_("   %s%s %s\n"), ann1, ann2, f2);
-         }
+      if (has_call)
+       {
+         info->callbacks->minfo (_("  calls:\n"));
+         for (call = fun->call_list; call; call = call->next)
+           if (!call->is_pasted)
+             {
+               const char *f2 = func_name (call->fun);
+               const char *ann1 = call->fun == max ? "*" : " ";
+               const char *ann2 = call->is_tail ? "t" : " ";
+
+               info->callbacks->minfo (_("   %s%s %s\n"), ann1, ann2, f2);
+             }
+       }
     }
 
   if (sum_stack_param->emit_stack_syms)
@@ -3427,14 +4039,91 @@ sort_bfds (const void *a, const void *b)
   return strcmp ((*abfd1)->filename, (*abfd2)->filename);
 }
 
-/* Handle --auto-overlay.  */
+static unsigned int
+print_one_overlay_section (FILE *script,
+                          unsigned int base,
+                          unsigned int count,
+                          unsigned int ovlynum,
+                          unsigned int *ovly_map,
+                          asection **ovly_sections,
+                          struct bfd_link_info *info)
+{
+  unsigned int j;
+         
+  for (j = base; j < count && ovly_map[j] == ovlynum; j++)
+    {
+      asection *sec = ovly_sections[2 * j];
+
+      if (fprintf (script, "   %s%c%s (%s)\n",
+                  (sec->owner->my_archive != NULL
+                   ? sec->owner->my_archive->filename : ""),
+                  info->path_separator,
+                  sec->owner->filename,
+                  sec->name) <= 0)
+       return -1;
+      if (sec->segment_mark)
+       {
+         struct call_info *call = find_pasted_call (sec);
+         while (call != NULL)
+           {
+             struct function_info *call_fun = call->fun;
+             sec = call_fun->sec;
+             if (fprintf (script, "   %s%c%s (%s)\n",
+                          (sec->owner->my_archive != NULL
+                           ? sec->owner->my_archive->filename : ""),
+                          info->path_separator,
+                          sec->owner->filename,
+                          sec->name) <= 0)
+               return -1;
+             for (call = call_fun->call_list; call; call = call->next)
+               if (call->is_pasted)
+                 break;
+           }
+       }
+    }
+
+  for (j = base; j < count && ovly_map[j] == ovlynum; j++)
+    {
+      asection *sec = ovly_sections[2 * j + 1];
+      if (sec != NULL
+         && fprintf (script, "   %s%c%s (%s)\n",
+                     (sec->owner->my_archive != NULL
+                      ? sec->owner->my_archive->filename : ""),
+                     info->path_separator,
+                     sec->owner->filename,
+                     sec->name) <= 0)
+       return -1;
+
+      sec = ovly_sections[2 * j];
+      if (sec->segment_mark)
+       {
+         struct call_info *call = find_pasted_call (sec);
+         while (call != NULL)
+           {
+             struct function_info *call_fun = call->fun;
+             sec = call_fun->rodata;
+             if (sec != NULL
+                 && fprintf (script, "   %s%c%s (%s)\n",
+                             (sec->owner->my_archive != NULL
+                              ? sec->owner->my_archive->filename : ""),
+                             info->path_separator,
+                             sec->owner->filename,
+                             sec->name) <= 0)
+               return -1;
+             for (call = call_fun->call_list; call; call = call->next)
+               if (call->is_pasted)
+                 break;
+           }
+       }
+    }
+
+  return j;
+}
 
-static void spu_elf_auto_overlay (struct bfd_link_info *, void (*) (void))
-     ATTRIBUTE_NORETURN;
+/* Handle --auto-overlay.  */
 
 static void
-spu_elf_auto_overlay (struct bfd_link_info *info,
-                     void (*spu_elf_load_ovl_mgr) (void))
+spu_elf_auto_overlay (struct bfd_link_info *info)
 {
   bfd *ibfd;
   bfd **bfd_arr;
@@ -3442,10 +4131,12 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
   unsigned int fixed_size, lo, hi;
   struct spu_link_hash_table *htab;
   unsigned int base, i, count, bfd_count;
-  int ovlynum;
+  unsigned int region, ovlynum;
   asection **ovly_sections, **ovly_p;
+  unsigned int *ovly_map;
   FILE *script;
   unsigned int total_overlay_size, overlay_size;
+  const char *ovly_mgr_entry;
   struct elf_link_hash_entry *h;
   struct _mos_param mos_param;
   struct _uos_param uos_param;
@@ -3472,12 +4163,34 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
   if (!build_call_tree (info))
     goto err_exit;
 
+  htab = spu_hash_table (info);
+  if (htab->reserved == 0)
+    {
+      struct _sum_stack_param sum_stack_param;
+
+      sum_stack_param.emit_stack_syms = 0;
+      sum_stack_param.overall_stack = 0;
+      if (!for_each_node (sum_stack, info, &sum_stack_param, TRUE))
+       goto err_exit;
+      htab->reserved = sum_stack_param.overall_stack + htab->extra_stack_space;
+    }
+
+  /* No need for overlays if everything already fits.  */
+  if (fixed_size + htab->reserved <= htab->local_store
+      && htab->params->ovly_flavour != ovly_soft_icache)
+    {
+      htab->params->auto_overlay = 0;
+      return;
+    }
+
   uos_param.exclude_input_section = 0;
   uos_param.exclude_output_section
     = bfd_get_section_by_name (info->output_bfd, ".interrupt");
 
-  htab = spu_hash_table (info);
-  h = elf_link_hash_lookup (&htab->elf, "__ovly_load",
+  ovly_mgr_entry = "__ovly_load";
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    ovly_mgr_entry = "__icache_br_handler";
+  h = elf_link_hash_lookup (&htab->elf, ovly_mgr_entry,
                            FALSE, FALSE, FALSE);
   if (h != NULL
       && (h->root.type == bfd_link_hash_defined
@@ -3491,13 +4204,7 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
     {
       /* If no user overlay manager, spu_elf_load_ovl_mgr will add our
         builtin version to .text, and will adjust .text size.  */
-      asection *text = bfd_get_section_by_name (info->output_bfd, ".text");
-      if (text != NULL)
-       fixed_size -= text->size;
-      spu_elf_load_ovl_mgr ();
-      text = bfd_get_section_by_name (info->output_bfd, ".text");
-      if (text != NULL)
-       fixed_size += text->size;
+      fixed_size += (*htab->params->spu_elf_load_ovl_mgr) ();
     }
 
   /* Mark overlay sections, and find max overlay section size.  */
@@ -3542,6 +4249,10 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
            fixed_size -= sec->size;
            total_overlay_size += sec->size;
          }
+       else if ((sec->flags & (SEC_ALLOC | SEC_LOAD)) == (SEC_ALLOC | SEC_LOAD)
+                && sec->output_section->owner == info->output_bfd
+                && strncmp (sec->output_section->name, ".ovl.init", 9) == 0)
+         fixed_size -= sec->size;
       if (count != old_count)
        bfd_arr[bfd_count++] = ibfd;
     }
@@ -3556,37 +4267,20 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
       for (i = 1; i < bfd_count; ++i)
        if (strcmp (bfd_arr[i - 1]->filename, bfd_arr[i]->filename) == 0)
          {
-           if (bfd_arr[i - 1]->my_archive && bfd_arr[i]->my_archive)
+           if (bfd_arr[i - 1]->my_archive == bfd_arr[i]->my_archive)
              {
-               if (bfd_arr[i - 1]->my_archive == bfd_arr[i]->my_archive)
+               if (bfd_arr[i - 1]->my_archive && bfd_arr[i]->my_archive)
                  info->callbacks->einfo (_("%s duplicated in %s\n"),
-                                         bfd_arr[i - 1]->filename,
-                                         bfd_arr[i - 1]->my_archive->filename);
-               else
-                 info->callbacks->einfo (_("%s in both %s and %s\n"),
-                                         bfd_arr[i - 1]->filename,
-                                         bfd_arr[i - 1]->my_archive->filename,
+                                         bfd_arr[i]->filename,
                                          bfd_arr[i]->my_archive->filename);
+               else
+                 info->callbacks->einfo (_("%s duplicated\n"),
+                                         bfd_arr[i]->filename);
+               ok = FALSE;
              }
-           else if (bfd_arr[i - 1]->my_archive)
-             info->callbacks->einfo (_("%s in %s and as an object\n"),
-                                     bfd_arr[i - 1]->filename,
-                                     bfd_arr[i - 1]->my_archive->filename);
-           else if (bfd_arr[i]->my_archive)
-             info->callbacks->einfo (_("%s in %s and as an object\n"),
-                                     bfd_arr[i]->filename,
-                                     bfd_arr[i]->my_archive->filename);
-           else
-             info->callbacks->einfo (_("%s duplicated\n"),
-                                     bfd_arr[i]->filename);
-           ok = FALSE;
          }
       if (!ok)
        {
-         /* FIXME: modify plain object files from foo.o to ./foo.o
-            and emit EXCLUDE_FILE to handle the duplicates in
-            archives.  There is a pathological case we can't handle:
-            We may have duplicate file names within a single archive.  */
          info->callbacks->einfo (_("sorry, no support for duplicate "
                                    "object files in auto-overlay script\n"));
          bfd_set_error (bfd_error_bad_value);
@@ -3595,41 +4289,57 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
     }
   free (bfd_arr);
 
-  if (htab->reserved == 0)
-    {
-      struct _sum_stack_param sum_stack_param;
-
-      sum_stack_param.emit_stack_syms = 0;
-      sum_stack_param.overall_stack = 0;
-      if (!for_each_node (sum_stack, info, &sum_stack_param, TRUE))
-       goto err_exit;
-      htab->reserved = sum_stack_param.overall_stack;
-    }
   fixed_size += htab->reserved;
-  fixed_size += htab->non_ovly_stub * OVL_STUB_SIZE;
+  fixed_size += htab->non_ovly_stub * ovl_stub_size (htab->params);
   if (fixed_size + mos_param.max_overlay_size <= htab->local_store)
     {
-      /* Guess number of overlays.  Assuming overlay buffer is on
-        average only half full should be conservative.  */
-      ovlynum = total_overlay_size * 2 / (htab->local_store - fixed_size);
-      /* Space for _ovly_table[], _ovly_buf_table[] and toe.  */
-      fixed_size += ovlynum * 16 + 16 + 4 + 16;
+      if (htab->params->ovly_flavour == ovly_soft_icache)
+       {
+         /* Stubs in the non-icache area are bigger.  */
+         fixed_size += htab->non_ovly_stub * 16;
+         /* Space for icache manager tables.
+            a) Tag array, one quadword per cache line.
+            - word 0: ia address of present line, init to zero.  */
+         fixed_size += 16 << htab->num_lines_log2;
+         /* b) Rewrite "to" list, one quadword per cache line.  */
+         fixed_size += 16 << htab->num_lines_log2;
+         /* c) Rewrite "from" list, one byte per outgoing branch (rounded up
+               to a power-of-two number of full quadwords) per cache line.  */
+         fixed_size += 16 << (htab->fromelem_size_log2
+                              + htab->num_lines_log2);
+         /* d) Pointer to __ea backing store (toe), 1 quadword.  */
+         fixed_size += 16;
+       }
+      else
+       {
+         /* Guess number of overlays.  Assuming overlay buffer is on
+            average only half full should be conservative.  */
+         ovlynum = (total_overlay_size * 2 * htab->params->num_lines
+                    / (htab->local_store - fixed_size));
+         /* Space for _ovly_table[], _ovly_buf_table[] and toe.  */
+         fixed_size += ovlynum * 16 + 16 + 4 + 16;
+       }
     }
 
   if (fixed_size + mos_param.max_overlay_size > htab->local_store)
-    info->callbacks->einfo (_("non-overlay plus maximum overlay size "
-                             "of 0x%x exceeds local store\n"),
-                           fixed_size + mos_param.max_overlay_size);
+    info->callbacks->einfo (_("non-overlay size of 0x%v plus maximum overlay "
+                             "size of 0x%v exceeds local store\n"),
+                           (bfd_vma) fixed_size,
+                           (bfd_vma) mos_param.max_overlay_size);
 
   /* Now see if we should put some functions in the non-overlay area.  */
-  if (fixed_size < htab->overlay_fixed
-      && htab->overlay_fixed + mos_param.max_overlay_size < htab->local_store)
+  else if (fixed_size < htab->overlay_fixed)
     {
-      unsigned int lib_size = htab->overlay_fixed - fixed_size;
+      unsigned int max_fixed, lib_size;
+
+      max_fixed = htab->local_store - mos_param.max_overlay_size;
+      if (max_fixed > htab->overlay_fixed)
+       max_fixed = htab->overlay_fixed;
+      lib_size = max_fixed - fixed_size;
       lib_size = auto_ovl_lib_functions (info, lib_size);
       if (lib_size == (unsigned int) -1)
        goto err_exit;
-      fixed_size = htab->overlay_fixed - lib_size;
+      fixed_size = max_fixed - lib_size;
     }
 
   /* Build an array of sections, suitably sorted to place into
@@ -3641,26 +4351,25 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
   if (!for_each_node (collect_overlays, info, &ovly_p, TRUE))
     goto err_exit;
   count = (size_t) (ovly_p - ovly_sections) / 2;
-
-  script = htab->spu_elf_open_overlay_script ();
-
-  if (fprintf (script, "SECTIONS\n{\n OVERLAY :\n {\n") <= 0)
-    goto file_err;
+  ovly_map = bfd_malloc (count * sizeof (*ovly_map));
+  if (ovly_map == NULL)
+    goto err_exit;
 
   memset (&dummy_caller, 0, sizeof (dummy_caller));
-  overlay_size = htab->local_store - fixed_size;
+  overlay_size = (htab->local_store - fixed_size) / htab->params->num_lines;
+  if (htab->params->line_size != 0)
+    overlay_size = htab->params->line_size;
   base = 0;
   ovlynum = 0;
   while (base < count)
     {
       unsigned int size = 0;
-      unsigned int j;
 
       for (i = base; i < count; i++)
        {
          asection *sec;
          unsigned int tmp;
-         unsigned int stub_size;
+         unsigned int num_stubs;
          struct call_info *call, *pasty;
          struct _spu_elf_section_data *sec_data;
          struct spu_elf_stack_info *sinfo;
@@ -3725,24 +4434,27 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
            }
 
          /* Calculate call stub size.  */
-         stub_size = 0;
+         num_stubs = 0;
          for (call = dummy_caller.call_list; call; call = call->next)
            {
              unsigned int k;
 
-             stub_size += OVL_STUB_SIZE;
+             ++num_stubs;
              /* If the call is within this overlay, we won't need a
                 stub.  */
              for (k = base; k < i + 1; k++)
                if (call->fun->sec == ovly_sections[2 * k])
                  {
-                   stub_size -= OVL_STUB_SIZE;
+                   --num_stubs;
                    break;
                  }
            }
-         if (tmp + stub_size > overlay_size)
+         if (htab->params->ovly_flavour == ovly_soft_icache
+             && num_stubs > htab->params->max_branch)
+           break;
+         if (tmp + num_stubs * ovl_stub_size (htab->params)
+             > overlay_size)
            break;
-         
          size = tmp;
        }
 
@@ -3756,86 +4468,128 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
          goto err_exit;
        }
 
-      if (fprintf (script, "  .ovly%d {\n", ++ovlynum) <= 0)
+      while (dummy_caller.call_list != NULL)
+       {
+         struct call_info *call = dummy_caller.call_list;
+         dummy_caller.call_list = call->next;
+         free (call);
+       }
+
+      ++ovlynum;
+      while (base < i)
+       ovly_map[base++] = ovlynum;
+    }
+
+  script = htab->params->spu_elf_open_overlay_script ();
+
+  if (fprintf (script, "SECTIONS\n{\n") <= 0)
+    goto file_err;
+
+  if (htab->params->ovly_flavour == ovly_soft_icache)
+    {
+      if (fprintf (script,
+                  " .data.icache ALIGN (16) : { *(.ovtab) *(.data.icache) }\n"
+                  " . = ALIGN (%u);\n"
+                  " .ovl.init : { *(.ovl.init) }\n"
+                  " . = ABSOLUTE (ADDR (.ovl.init));\n",
+                  htab->params->line_size) <= 0)
        goto file_err;
-      for (j = base; j < i; j++)
+
+      base = 0;
+      ovlynum = 1;
+      while (base < count)
        {
-         asection *sec = ovly_sections[2 * j];
+         unsigned int indx = ovlynum - 1;
+         unsigned int vma, lma;
 
-         if (fprintf (script, "   [%c]%s (%s)\n",
-                      sec->owner->filename[0],
-                      sec->owner->filename + 1,
-                      sec->name) <= 0)
+         vma = (indx & (htab->params->num_lines - 1)) << htab->line_size_log2;
+         lma = indx << htab->line_size_log2;
+
+         if (fprintf (script, " .ovly%u ABSOLUTE (ADDR (.ovl.init)) + %u "
+                      ": AT (ALIGN (LOADADDR (.ovl.init) + SIZEOF (.ovl.init), 16) + %u) {\n",
+                      ovlynum, vma, lma) <= 0)
            goto file_err;
-         if (sec->segment_mark)
-           {
-             struct call_info *call = find_pasted_call (sec);
-             while (call != NULL)
-               {
-                 struct function_info *call_fun = call->fun;
-                 sec = call_fun->sec;
-                 if (fprintf (script, "   [%c]%s (%s)\n",
-                              sec->owner->filename[0],
-                              sec->owner->filename + 1,
-                              sec->name) <= 0)
-                   goto file_err;
-                 for (call = call_fun->call_list; call; call = call->next)
-                   if (call->is_pasted)
-                     break;
-               }
-           }
-       }
 
-      for (j = base; j < i; j++)
-       {
-         asection *sec = ovly_sections[2 * j + 1];
-         if (sec != NULL && fprintf (script, "   [%c]%s (%s)\n",
-                                     sec->owner->filename[0],
-                                     sec->owner->filename + 1,
-                                     sec->name) <= 0)
+         base = print_one_overlay_section (script, base, count, ovlynum,
+                                           ovly_map, ovly_sections, info);
+         if (base == (unsigned) -1)
            goto file_err;
 
-         sec = ovly_sections[2 * j];
-         if (sec->segment_mark)
-           {
-             struct call_info *call = find_pasted_call (sec);
-             while (call != NULL)
-               {
-                 struct function_info *call_fun = call->fun;
-                 sec = call_fun->rodata;
-                 if (sec != NULL && fprintf (script, "   [%c]%s (%s)\n",
-                                             sec->owner->filename[0],
-                                             sec->owner->filename + 1,
-                                             sec->name) <= 0)
-                   goto file_err;
-                 for (call = call_fun->call_list; call; call = call->next)
-                   if (call->is_pasted)
-                     break;
-               }
-           }
+         if (fprintf (script, "  }\n") <= 0)
+           goto file_err;
+
+         ovlynum++;
        }
 
-      if (fprintf (script, "  }\n") <= 0)
+      if (fprintf (script, " . = ABSOLUTE (ADDR (.ovl.init)) + %u;\n",
+                  1 << (htab->num_lines_log2 + htab->line_size_log2)) <= 0)
+       goto file_err;
+    }
+  else
+    {
+      if (fprintf (script,
+                  " . = ALIGN (16);\n"
+                  " .ovl.init : { *(.ovl.init) }\n"
+                  " . = ABSOLUTE (ADDR (.ovl.init));\n") <= 0)
        goto file_err;
 
-      while (dummy_caller.call_list != NULL)
+      for (region = 1; region <= htab->params->num_lines; region++)
        {
-         struct call_info *call = dummy_caller.call_list;
-         dummy_caller.call_list = call->next;
-         free (call);
+         ovlynum = region;
+         base = 0;
+         while (base < count && ovly_map[base] < ovlynum)
+           base++;
+
+         if (base == count)
+           break;
+
+         if (region == 1)
+           {
+             /* We need to set lma since we are overlaying .ovl.init.  */
+             if (fprintf (script,
+                          " OVERLAY : AT (ALIGN (LOADADDR (.ovl.init) + SIZEOF (.ovl.init), 16))\n {\n") <= 0)
+               goto file_err;
+           }
+         else
+           {
+             if (fprintf (script, " OVERLAY :\n {\n") <= 0)
+               goto file_err;
+           }
+
+         while (base < count)
+           {
+             if (fprintf (script, "  .ovly%u {\n", ovlynum) <= 0)
+               goto file_err;
+
+             base = print_one_overlay_section (script, base, count, ovlynum,
+                                               ovly_map, ovly_sections, info);
+             if (base == (unsigned) -1)
+               goto file_err;
+
+             if (fprintf (script, "  }\n") <= 0)
+               goto file_err;
+
+             ovlynum += htab->params->num_lines;
+             while (base < count && ovly_map[base] < ovlynum)
+               base++;
+           }
+
+         if (fprintf (script, " }\n") <= 0)
+           goto file_err;
        }
 
-      base = i;
     }
+
+  free (ovly_map);
   free (ovly_sections);
 
-  if (fprintf (script, " }\n}\nINSERT AFTER .text;\n") <= 0)
+  if (fprintf (script, "}\nINSERT BEFORE .text;\n") <= 0)
     goto file_err;
   if (fclose (script) != 0)
     goto file_err;
 
-  if (htab->auto_overlay & AUTO_RELINK)
-    htab->spu_elf_relink ();
+  if (htab->params->auto_overlay & AUTO_RELINK)
+    (*htab->params->spu_elf_relink) ();
 
   xexit (0);
 
@@ -3849,8 +4603,9 @@ spu_elf_auto_overlay (struct bfd_link_info *info,
 /* Provide an estimate of total stack required.  */
 
 static bfd_boolean
-spu_elf_stack_analysis (struct bfd_link_info *info, int emit_stack_syms)
+spu_elf_stack_analysis (struct bfd_link_info *info)
 {
+  struct spu_link_hash_table *htab;
   struct _sum_stack_param sum_stack_param;
 
   if (!discover_functions (info))
@@ -3859,17 +4614,22 @@ spu_elf_stack_analysis (struct bfd_link_info *info, int emit_stack_syms)
   if (!build_call_tree (info))
     return FALSE;
 
-  info->callbacks->info (_("Stack size for call graph root nodes.\n"));
-  info->callbacks->minfo (_("\nStack size for functions.  "
-                           "Annotations: '*' max stack, 't' tail call\n"));
+  htab = spu_hash_table (info);
+  if (htab->params->stack_analysis)
+    {
+      info->callbacks->info (_("Stack size for call graph root nodes.\n"));
+      info->callbacks->minfo (_("\nStack size for functions.  "
+                               "Annotations: '*' max stack, 't' tail call\n"));
+    }
 
-  sum_stack_param.emit_stack_syms = emit_stack_syms;
+  sum_stack_param.emit_stack_syms = htab->params->emit_stack_syms;
   sum_stack_param.overall_stack = 0;
   if (!for_each_node (sum_stack, info, &sum_stack_param, TRUE))
     return FALSE;
 
-  info->callbacks->info (_("Maximum stack required is 0x%v\n"),
-                        (bfd_vma) sum_stack_param.overall_stack);
+  if (htab->params->stack_analysis)
+    info->callbacks->info (_("Maximum stack required is 0x%v\n"),
+                          (bfd_vma) sum_stack_param.overall_stack);
   return TRUE;
 }
 
@@ -3880,12 +4640,17 @@ spu_elf_final_link (bfd *output_bfd, struct bfd_link_info *info)
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
 
-  if (htab->auto_overlay)
-    spu_elf_auto_overlay (info, htab->spu_elf_load_ovl_mgr);
+  if (htab->params->auto_overlay)
+    spu_elf_auto_overlay (info);
 
-  if (htab->stack_analysis
-      && !spu_elf_stack_analysis (info, htab->emit_stack_syms))
-    info->callbacks->einfo ("%X%P: stack analysis error: %E\n");
+  if ((htab->params->stack_analysis
+       || (htab->params->ovly_flavour == ovly_soft_icache
+          && htab->params->lrlive_analysis))
+      && !spu_elf_stack_analysis (info))
+    info->callbacks->einfo ("%X%P: stack/lrlive analysis error: %E\n");
+
+  if (!spu_elf_build_stubs (info))
+    info->callbacks->einfo ("%F%P: can not build overlay stubs: %E\n");
 
   return bfd_elf_final_link (output_bfd, info);
 }
@@ -3895,16 +4660,27 @@ spu_elf_final_link (bfd *output_bfd, struct bfd_link_info *info)
    that need to be emitted.  */
 
 static unsigned int
-spu_elf_count_relocs (asection *sec, Elf_Internal_Rela *relocs)
+spu_elf_count_relocs (struct bfd_link_info *info, asection *sec)
 {
+  Elf_Internal_Rela *relocs;
   unsigned int count = 0;
-  Elf_Internal_Rela *relend = relocs + sec->reloc_count;
 
-  for (; relocs < relend; relocs++)
+  relocs = _bfd_elf_link_read_relocs (sec->owner, sec, NULL, NULL,
+                                     info->keep_memory);
+  if (relocs != NULL)
     {
-      int r_type = ELF32_R_TYPE (relocs->r_info);
-      if (r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64)
-       ++count;
+      Elf_Internal_Rela *rel;
+      Elf_Internal_Rela *relend = relocs + sec->reloc_count;
+
+      for (rel = relocs; rel < relend; rel++)
+       {
+         int r_type = ELF32_R_TYPE (rel->r_info);
+         if (r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64)
+           ++count;
+       }
+
+      if (elf_section_data (sec)->relocs != relocs)
+       free (relocs);
     }
 
   return count;
@@ -3926,15 +4702,18 @@ 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;
-  asection *ea = bfd_get_section_by_name (output_bfd, "._ea");
+  asection *ea;
   int ret = TRUE;
   bfd_boolean emit_these_relocs = FALSE;
   bfd_boolean is_ea_sym;
   bfd_boolean stubs;
+  unsigned int iovl = 0;
 
   htab = spu_hash_table (info);
   stubs = (htab->stub_sec != NULL
-          && maybe_needs_stubs (input_section, output_bfd));
+          && maybe_needs_stubs (input_section));
+  iovl = overlay_index (input_section);
+  ea = bfd_get_section_by_name (output_bfd, "._ea");
   symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
   sym_hashes = (struct elf_link_hash_entry **) (elf_sym_hashes (input_bfd));
 
@@ -3973,10 +4752,51 @@ spu_elf_relocate_section (bfd *output_bfd,
        }
       else
        {
-         RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
-                                  r_symndx, symtab_hdr, sym_hashes,
-                                  h, sec, relocation,
-                                  unresolved_reloc, warned);
+         if (sym_hashes == NULL)
+           return FALSE;
+
+         h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+
+         while (h->root.type == bfd_link_hash_indirect
+                || h->root.type == bfd_link_hash_warning)
+           h = (struct elf_link_hash_entry *) h->root.u.i.link;
+
+         relocation = 0;
+         if (h->root.type == bfd_link_hash_defined
+             || h->root.type == bfd_link_hash_defweak)
+           {
+             sec = h->root.u.def.section;
+             if (sec == NULL
+                 || sec->output_section == NULL)
+               /* Set a flag that will be cleared later if we find a
+                  relocation value for this symbol.  output_section
+                  is typically NULL for symbols satisfied by a shared
+                  library.  */
+               unresolved_reloc = TRUE;
+             else
+               relocation = (h->root.u.def.value
+                             + sec->output_section->vma
+                             + sec->output_offset);
+           }
+         else if (h->root.type == bfd_link_hash_undefweak)
+           ;
+         else if (info->unresolved_syms_in_objects == RM_IGNORE
+                  && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT)
+           ;
+         else if (!info->relocatable
+                  && !(r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64))
+           {
+             bfd_boolean err;
+             err = (info->unresolved_syms_in_objects == RM_GENERATE_ERROR
+                    || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT);
+             if (!info->callbacks->undefined_symbol (info,
+                                                     h->root.root.string,
+                                                     input_bfd,
+                                                     input_section,
+                                                     rel->r_offset, err))
+               return FALSE;
+             warned = TRUE;
+           }
          sym_name = h->root.root.string;
        }
 
@@ -3998,7 +4818,58 @@ spu_elf_relocate_section (bfd *output_bfd,
                   && sec != NULL
                   && sec->output_section == ea);
 
-      if (r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64)
+      /* If this symbol is in an overlay area, we may need to relocate
+        to the overlay stub.  */
+      addend = rel->r_addend;
+      if (stubs
+         && !is_ea_sym
+         && (stub_type = needs_ovl_stub (h, sym, sec, input_section, rel,
+                                         contents, info)) != no_stub)
+       {
+         unsigned int ovl = 0;
+         struct got_entry *g, **head;
+
+         if (stub_type != nonovl_stub)
+           ovl = iovl;
+
+         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 (htab->params->ovly_flavour == ovly_soft_icache
+               ? g->br_addr == (rel->r_offset
+                                + input_section->output_offset
+                                + input_section->output_section->vma)
+               : g->addend == addend && (g->ovl == ovl || g->ovl == 0))
+             break;
+         if (g == NULL)
+           abort ();
+
+         relocation = g->stub_addr;
+         addend = 0;
+       }
+      else
+       {
+         /* For soft icache, encode the overlay index into addresses.  */
+         if (htab->params->ovly_flavour == ovly_soft_icache
+             && (r_type == R_SPU_ADDR16_HI
+                 || r_type == R_SPU_ADDR32 || r_type == R_SPU_REL32)
+             && !is_ea_sym)
+           {
+             unsigned int ovl = overlay_index (sec);
+             if (ovl != 0)
+               {
+                 unsigned int set_id = ((ovl - 1) >> htab->num_lines_log2) + 1;
+                 relocation += set_id << 18;
+               }
+           }
+       }
+
+      if (unresolved_reloc)
+       ;
+      else if (r_type == R_SPU_PPU32 || r_type == R_SPU_PPU64)
        {
          if (is_ea_sym)
            {
@@ -4016,8 +4887,7 @@ spu_elf_relocate_section (bfd *output_bfd,
          emit_these_relocs = TRUE;
          continue;
        }
-
-      if (is_ea_sym)
+      else if (is_ea_sym)
        unresolved_reloc = TRUE;
 
       if (unresolved_reloc)
@@ -4032,35 +4902,6 @@ spu_elf_relocate_section (bfd *output_bfd,
          ret = FALSE;
        }
 
-      /* If this symbol is in an overlay area, we may need to relocate
-        to the overlay stub.  */
-      addend = rel->r_addend;
-      if (stubs
-         && (stub_type = needs_ovl_stub (h, sym, sec, input_section, rel,
-                                         contents, info)) != no_stub)
-       {
-         unsigned int ovl = 0;
-         struct got_entry *g, **head;
-
-         if (stub_type != nonovl_stub)
-           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;
-       }
-
       r = _bfd_final_link_relocate (howto,
                                    input_bfd,
                                    input_section,
@@ -4143,7 +4984,7 @@ spu_elf_relocate_section (bfd *output_bfd,
 
 /* Adjust _SPUEAR_ syms to point at their overlay stubs.  */
 
-static bfd_boolean
+static int
 spu_elf_output_symbol_hook (struct bfd_link_info *info,
                            const char *sym_name ATTRIBUTE_UNUSED,
                            Elf_Internal_Sym *sym,
@@ -4163,7 +5004,9 @@ spu_elf_output_symbol_hook (struct bfd_link_info *info,
       struct got_entry *g;
 
       for (g = h->got.glist; g != NULL; g = g->next)
-       if (g->addend == 0 && g->ovl == 0)
+       if (htab->params->ovly_flavour == ovly_soft_icache
+           ? g->br_addr == g->stub_addr
+           : g->addend == 0 && g->ovl == 0)
          {
            sym->st_shndx = (_bfd_elf_section_from_bfd_section
                             (htab->stub_sec[0]->output_section->owner,
@@ -4173,7 +5016,7 @@ spu_elf_output_symbol_hook (struct bfd_link_info *info,
          }
     }
 
-  return TRUE;
+  return 1;
 }
 
 static int spu_plugin = 0;
@@ -4204,10 +5047,15 @@ spu_elf_post_process_headers (bfd *abfd,
 static int
 spu_elf_additional_program_headers (bfd *abfd, struct bfd_link_info *info)
 {
-  struct spu_link_hash_table *htab = spu_hash_table (info);
-  int extra = htab->num_overlays;
+  int extra = 0;
   asection *sec;
 
+  if (info != NULL)
+    {
+      struct spu_link_hash_table *htab = spu_hash_table (info);
+      extra = htab->num_overlays;
+    }
+
   if (extra)
     ++extra;
 
@@ -4225,7 +5073,8 @@ static bfd_boolean
 spu_elf_modify_segment_map (bfd *abfd, struct bfd_link_info *info)
 {
   asection *toe, *s;
-  struct elf_segment_map *m;
+  struct elf_segment_map *m, *m_overlay;
+  struct elf_segment_map **p, **p_overlay;
   unsigned int i;
 
   if (info == NULL)
@@ -4272,6 +5121,37 @@ spu_elf_modify_segment_map (bfd *abfd, struct bfd_link_info *info)
            break;
          }
 
+
+  /* Some SPU ELF loaders ignore the PF_OVERLAY flag and just load all
+     PT_LOAD segments.  This can cause the .ovl.init section to be
+     overwritten with the contents of some overlay segment.  To work
+     around this issue, we ensure that all PF_OVERLAY segments are
+     sorted first amongst the program headers; this ensures that even
+     with a broken loader, the .ovl.init section (which is not marked
+     as PF_OVERLAY) will be placed into SPU local store on startup.  */
+
+  /* Move all overlay segments onto a separate list.  */
+  p = &elf_tdata (abfd)->segment_map;
+  p_overlay = &m_overlay;
+  while (*p != NULL)
+    {
+      if ((*p)->p_type == PT_LOAD && (*p)->count == 1
+         && spu_elf_section_data ((*p)->sections[0])->u.o.ovl_index != 0)
+       {
+         struct elf_segment_map *m = *p;
+         *p = m->next;
+         *p_overlay = m;
+         p_overlay = &m->next;
+         continue;
+       }
+
+      p = &((*p)->next);
+    }
+
+  /* Re-insert overlay segments at the head of the segment map.  */
+  *p_overlay = elf_tdata (abfd)->segment_map;
+  elf_tdata (abfd)->segment_map = m_overlay;
+
   return TRUE;
 }
 
@@ -4319,7 +5199,8 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
            /* Mark this as an overlay header.  */
            phdr[i].p_flags |= PF_OVERLAY;
 
-           if (htab->ovtab != NULL && htab->ovtab->size != 0)
+           if (htab->ovtab != NULL && htab->ovtab->size != 0
+               && htab->params->ovly_flavour != ovly_soft_icache)
              {
                bfd_byte *p = htab->ovtab->contents;
                unsigned int off = o * 16 + 8;
@@ -4328,6 +5209,13 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
                bfd_put_32 (htab->ovtab->owner, phdr[i].p_offset, p + off);
              }
          }
+      /* Soft-icache has its file offset put in .ovl.init.  */
+      if (htab->init != NULL && htab->init->size != 0)
+       {
+         bfd_vma val = elf_section_data (htab->ovl_sec[0])->this_hdr.sh_offset;
+
+         bfd_put_32 (htab->init->owner, val, htab->init->contents + 4);
+       }
     }
 
   /* Round up p_filesz and p_memsz of PT_LOAD segments to multiples
@@ -4384,7 +5272,7 @@ spu_elf_modify_program_headers (bfd *abfd, struct bfd_link_info *info)
 #define elf_backend_can_gc_sections    1
 
 #define bfd_elf32_bfd_reloc_type_lookup                spu_elf_reloc_type_lookup
-#define bfd_elf32_bfd_reloc_name_lookup        spu_elf_reloc_name_lookup
+#define bfd_elf32_bfd_reloc_name_lookup                spu_elf_reloc_name_lookup
 #define elf_info_to_howto                      spu_elf_info_to_howto
 #define elf_backend_count_relocs               spu_elf_count_relocs
 #define elf_backend_relocate_section           spu_elf_relocate_section
This page took 0.061845 seconds and 4 git commands to generate.