* elf32-spu.c (spu_elf_size_stubs): Correct section alignment.
[binutils-gdb.git] / bfd / elf32-spu.c
index 9073b634f434e72e3a6bcb96edd58da3ec160835..7da9cb69606973a2272e71d344ecbebdc52292ae 100644 (file)
@@ -260,11 +260,19 @@ struct spu_link_hash_table
   /* The stub hash table.  */
   struct bfd_hash_table stub_hash_table;
 
+  /* Sorted array of stubs.  */
+  struct {
+    struct spu_stub_hash_entry **sh;
+    unsigned int count;
+    int err;
+  } stubs;
+
   /* Shortcuts to overlay sections.  */
   asection *stub;
   asection *ovtab;
 
   struct elf_link_hash_entry *ovly_load;
+  unsigned long ovly_load_r_symndx;
 
   /* An array of two output sections per overlay region, chosen such that
      the first section vma is the overlay buffer vma (ie. the section has
@@ -370,8 +378,8 @@ spu_elf_link_hash_table_create (bfd *abfd)
                            sizeof (struct spu_stub_hash_entry)))
     return NULL;
 
-  memset (&htab->stub, 0,
-         sizeof (*htab) - offsetof (struct spu_link_hash_table, stub));
+  memset (&htab->stubs, 0,
+         sizeof (*htab) - offsetof (struct spu_link_hash_table, stubs));
 
   return &htab->elf.root;
 }
@@ -698,6 +706,22 @@ is_branch (const unsigned char *insn)
   return (insn[0] & 0xec) == 0x20 && (insn[1] & 0x80) == 0;
 }
 
+/* Return true for all indirect branch instructions.
+   bi     00110101 000
+   bisl   00110101 001
+   iret   00110101 010
+   bisled 00110101 011
+   biz    00100101 000
+   binz   00100101 001
+   bihz   00100101 010
+   bihnz  00100101 011  */
+
+static bfd_boolean
+is_indirect_branch (const unsigned char *insn)
+{
+  return (insn[0] & 0xef) == 0x25 && (insn[1] & 0x80) == 0;
+}
+
 /* Return true for branch hint instructions.
    hbra  0001000..
    hbrr  0001001..  */
@@ -748,13 +772,6 @@ needs_ovl_stub (const char *sym_name,
   return !is_branch;
 }
 
-struct stubarr {
-  struct bfd_hash_table *stub_hash_table;
-  struct spu_stub_hash_entry **sh;
-  unsigned int count;
-  int err;
-};
-
 /* Called via elf_link_hash_traverse to allocate stubs for any _SPUEAR_
    symbols.  */
 
@@ -768,19 +785,19 @@ allocate_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
       && h->def_regular
       && strncmp (h->root.root.string, "_SPUEAR_", 8) == 0)
     {
-      struct stubarr *stubs = inf;
+      struct spu_link_hash_table *htab = inf;
       static Elf_Internal_Rela zero_rel;
       char *stub_name = spu_stub_name (h->root.u.def.section, h, &zero_rel);
       struct spu_stub_hash_entry *sh;
 
       if (stub_name == NULL)
        {
-         stubs->err = 1;
+         htab->stubs.err = 1;
          return FALSE;
        }
 
       sh = (struct spu_stub_hash_entry *)
-       bfd_hash_lookup (stubs->stub_hash_table, stub_name, TRUE, FALSE);
+       bfd_hash_lookup (&htab->stub_hash_table, stub_name, TRUE, FALSE);
       if (sh == NULL)
        {
          free (stub_name);
@@ -796,7 +813,7 @@ allocate_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
 
       sh->target_section = h->root.u.def.section;
       sh->target_off = h->root.u.def.value;
-      stubs->count += 1;
+      htab->stubs.count += 1;
     }
   
   return TRUE;
@@ -808,9 +825,9 @@ allocate_spuear_stubs (struct elf_link_hash_entry *h, void *inf)
 static bfd_boolean
 populate_stubs (struct bfd_hash_entry *bh, void *inf)
 {
-  struct stubarr *stubs = inf;
+  struct spu_link_hash_table *htab = inf;
 
-  stubs->sh[--stubs->count] = (struct spu_stub_hash_entry *) bh;
+  htab->stubs.sh[--htab->stubs.count] = (struct spu_stub_hash_entry *) bh;
   return TRUE;
 }
 
@@ -857,14 +874,10 @@ spu_elf_size_stubs (bfd *output_bfd,
 {
   struct spu_link_hash_table *htab = spu_hash_table (info);
   bfd *ibfd;
-  struct stubarr stubs;
   unsigned i, group;
   flagword flags;
 
   htab->non_overlay_stubs = non_overlay_stubs;
-  stubs.stub_hash_table = &htab->stub_hash_table;
-  stubs.count = 0;
-  stubs.err = 0;
   for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
     {
       extern const bfd_target bfd_elf32_spu_vec;
@@ -1038,7 +1051,7 @@ spu_elf_size_stubs (bfd *output_bfd,
                sh->target_off = sym->st_value;
              sh->target_off += irela->r_addend;
 
-             stubs.count += 1;
+             htab->stubs.count += 1;
            }
 
          /* We're done with the internal relocs, free them.  */
@@ -1056,12 +1069,12 @@ spu_elf_size_stubs (bfd *output_bfd,
        }
     }
 
-  elf_link_hash_traverse (&htab->elf, allocate_spuear_stubs, &stubs);
-  if (stubs.err)
+  elf_link_hash_traverse (&htab->elf, allocate_spuear_stubs, htab);
+  if (htab->stubs.err)
     return FALSE;
 
   *stub = NULL;
-  if (stubs.count == 0)
+  if (htab->stubs.count == 0)
     return TRUE;
 
   ibfd = info->input_bfds;
@@ -1070,7 +1083,7 @@ spu_elf_size_stubs (bfd *output_bfd,
   htab->stub = bfd_make_section_anyway_with_flags (ibfd, ".stub", flags);
   *stub = htab->stub;
   if (htab->stub == NULL
-      || !bfd_set_section_alignment (ibfd, htab->stub, 2))
+      || !bfd_set_section_alignment (ibfd, htab->stub, 4))
     return FALSE;
 
   flags = (SEC_ALLOC | SEC_LOAD
@@ -1078,7 +1091,7 @@ spu_elf_size_stubs (bfd *output_bfd,
   htab->ovtab = bfd_make_section_anyway_with_flags (ibfd, ".ovtab", flags);
   *ovtab = htab->ovtab;
   if (htab->ovtab == NULL
-      || !bfd_set_section_alignment (ibfd, htab->stub, 4))
+      || !bfd_set_section_alignment (ibfd, htab->ovtab, 4))
     return FALSE;
 
   *toe = bfd_make_section_anyway_with_flags (ibfd, ".toe", SEC_ALLOC);
@@ -1088,15 +1101,16 @@ spu_elf_size_stubs (bfd *output_bfd,
   (*toe)->size = 16;
 
   /* Retrieve all the stubs and sort.  */
-  stubs.sh = bfd_malloc (stubs.count * sizeof (*stubs.sh));
-  if (stubs.sh == NULL)
+  htab->stubs.sh = bfd_malloc (htab->stubs.count * sizeof (*htab->stubs.sh));
+  if (htab->stubs.sh == NULL)
     return FALSE;
-  i = stubs.count;
-  bfd_hash_traverse (&htab->stub_hash_table, populate_stubs, &stubs);
-  BFD_ASSERT (stubs.count == 0);
+  i = htab->stubs.count;
+  bfd_hash_traverse (&htab->stub_hash_table, populate_stubs, htab);
+  BFD_ASSERT (htab->stubs.count == 0);
 
-  stubs.count = i;
-  qsort (stubs.sh, stubs.count, sizeof (*stubs.sh), sort_stubs);
+  htab->stubs.count = i;
+  qsort (htab->stubs.sh, htab->stubs.count, sizeof (*htab->stubs.sh),
+        sort_stubs);
 
   /* Now that the stubs are sorted, place them in the stub section.
      Stubs are grouped per overlay
@@ -1113,36 +1127,41 @@ spu_elf_size_stubs (bfd *output_bfd,
      .     br __ovly_load  */
 
   group = 0;
-  for (i = 0; i < stubs.count; i++)
+  for (i = 0; i < htab->stubs.count; i++)
     {
-      if (spu_elf_section_data (stubs.sh[group]->target_section
+      if (spu_elf_section_data (htab->stubs.sh[group]->target_section
                                ->output_section)->ovl_index
-         != spu_elf_section_data (stubs.sh[i]->target_section
+         != spu_elf_section_data (htab->stubs.sh[i]->target_section
                                   ->output_section)->ovl_index)
        {
          htab->stub->size += SIZEOF_STUB2;
          for (; group != i; group++)
-           stubs.sh[group]->delta
-             = stubs.sh[i - 1]->off - stubs.sh[group]->off;
+           htab->stubs.sh[group]->delta
+             = htab->stubs.sh[i - 1]->off - htab->stubs.sh[group]->off;
        }
       if (group == i
-         || ((stubs.sh[i - 1]->target_section->output_section->vma
-              + stubs.sh[i - 1]->target_section->output_offset
-              + stubs.sh[i - 1]->target_off)
-             != (stubs.sh[i]->target_section->output_section->vma
-                 + stubs.sh[i]->target_section->output_offset
-                 + stubs.sh[i]->target_off)))
+         || ((htab->stubs.sh[i - 1]->target_section->output_section->vma
+              + htab->stubs.sh[i - 1]->target_section->output_offset
+              + htab->stubs.sh[i - 1]->target_off)
+             != (htab->stubs.sh[i]->target_section->output_section->vma
+                 + htab->stubs.sh[i]->target_section->output_offset
+                 + htab->stubs.sh[i]->target_off)))
        {
-         stubs.sh[i]->off = htab->stub->size;
+         htab->stubs.sh[i]->off = htab->stub->size;
          htab->stub->size += SIZEOF_STUB1;
+         if (info->emitrelocations)
+           htab->stub->reloc_count += 1;
        }
       else
-       stubs.sh[i]->off = stubs.sh[i - 1]->off;
+       htab->stubs.sh[i]->off = htab->stubs.sh[i - 1]->off;
     }
   if (group != i)
     htab->stub->size += SIZEOF_STUB2;
+  if (info->emitrelocations)
+    htab->stub->flags |= SEC_RELOC;
   for (; group != i; group++)
-    stubs.sh[group]->delta = stubs.sh[i - 1]->off - stubs.sh[group]->off;
+    htab->stubs.sh[group]->delta
+      = htab->stubs.sh[i - 1]->off - htab->stubs.sh[group]->off;
 
  /* htab->ovtab consists of two arrays.
     .  struct {
@@ -1212,10 +1231,9 @@ spu_elf_open_builtin_lib (bfd **ovl_bfd, const struct _ovl_stream *stream)
    write the stub that sets the overlay number too.  */
 
 static bfd_boolean
-write_one_stub (struct bfd_hash_entry *bh, void *inf)
+write_one_stub (struct spu_stub_hash_entry *ent, struct bfd_link_info *info)
 {
-  struct spu_stub_hash_entry *ent = (struct spu_stub_hash_entry *) bh;
-  struct spu_link_hash_table *htab = inf;
+  struct spu_link_hash_table *htab = spu_hash_table (info);
   asection *sec = htab->stub;
   asection *s = ent->target_section;
   unsigned int ovl;
@@ -1228,6 +1246,56 @@ write_one_stub (struct bfd_hash_entry *bh, void *inf)
   bfd_put_32 (sec->owner, BR + ((val << 5) & 0x007fff80),
              sec->contents + ent->off + 4);
 
+  if (info->emitrelocations)
+    {
+      Elf_Internal_Rela *relocs, *r;
+      struct bfd_elf_section_data *elfsec_data;
+
+      elfsec_data = elf_section_data (sec);
+      relocs = elfsec_data->relocs;
+      if (relocs == NULL)
+       {
+         bfd_size_type relsize;
+         Elf_Internal_Shdr *symtab_hdr;
+         struct elf_link_hash_entry **sym_hash;
+         unsigned long symcount;
+         bfd_vma amt;
+
+         relsize = sec->reloc_count * sizeof (*relocs);
+         relocs = bfd_alloc (sec->owner, relsize);
+         if (relocs == NULL)
+           return FALSE;
+         elfsec_data->relocs = relocs;
+         elfsec_data->rel_hdr.sh_size
+           = sec->reloc_count * sizeof (Elf32_External_Rela);
+         elfsec_data->rel_hdr.sh_entsize = sizeof (Elf32_External_Rela);
+         sec->reloc_count = 0;
+
+         /* Increase the size of symbol hash array on the bfd to
+            which we attached our .stub section.  This hack allows
+            us to create relocs against global symbols.  */
+         symtab_hdr = &elf_tdata (sec->owner)->symtab_hdr;
+         symcount = symtab_hdr->sh_size / symtab_hdr->sh_entsize;
+         symcount -= symtab_hdr->sh_info;
+         amt = symcount * sizeof (*sym_hash);
+         sym_hash = bfd_alloc (sec->owner, amt + sizeof (*sym_hash));
+         if (sym_hash == NULL)
+           return FALSE;
+         memcpy (sym_hash, elf_sym_hashes (sec->owner), amt);
+         sym_hash[symcount] = htab->ovly_load;
+         htab->ovly_load_r_symndx = symcount + symtab_hdr->sh_info;
+         elf_sym_hashes (sec->owner) = sym_hash;
+       }
+      r = relocs + sec->reloc_count;
+      sec->reloc_count += 1;
+      r->r_offset = ent->off + 4;
+      r->r_info = ELF32_R_INFO (0, R_SPU_REL16);
+      r->r_addend = (sec->output_section->vma
+                    + sec->output_offset
+                    + ent->off + 4
+                    + val);
+    }
+
   /* If this is the last stub of this group, write stub2.  */
   if (ent->delta == 0)
     {
@@ -1250,6 +1318,20 @@ write_one_stub (struct bfd_hash_entry *bh, void *inf)
 
       bfd_put_32 (sec->owner, BR + ((val << 5) & 0x007fff80),
                  sec->contents + ent->off + 12);
+
+      if (info->emitrelocations)
+       {
+         Elf_Internal_Rela *relocs, *r;
+         struct bfd_elf_section_data *elfsec_data;
+
+         elfsec_data = elf_section_data (sec);
+         relocs = elfsec_data->relocs;
+         /* The last branch is overwritten, so overwrite its reloc too.  */
+         r = relocs + sec->reloc_count - 1;
+         r->r_offset = ent->off + 12;
+         r->r_info = ELF32_R_INFO (htab->ovly_load_r_symndx, R_SPU_REL16);
+         r->r_addend = 0;
+       }
     }
 
   if (htab->emit_stub_syms)
@@ -1356,7 +1438,8 @@ spu_elf_build_stubs (struct bfd_link_info *info, int emit_syms, asection *toe)
     }
 
   /* Write out all the stubs.  */
-  bfd_hash_traverse (&htab->stub_hash_table, write_one_stub, htab);
+  for (i = 0; i < htab->stubs.count; i++)
+    write_one_stub (htab->stubs.sh[i], info);
 
   if (htab->stub_overflow)
     {
@@ -1534,7 +1617,7 @@ find_function_stack_adjust (asection *sec, bfd_vma offset)
          reg[rt] = 0;
          continue;
        }
-      else if (is_branch (buf))
+      else if (is_branch (buf) || is_indirect_branch (buf))
        /* If we hit a branch then we must be out of the prologue.  */
        break;
     unknown_insn:
@@ -2510,7 +2593,8 @@ sum_stack (struct function_info *fun,
     }
 
   f1 = func_name (fun);
-  info->callbacks->minfo (_("%s: 0x%v 0x%v\n"), f1, fun->stack, max_stack);
+  info->callbacks->minfo (_("%s: 0x%v 0x%v\n"),
+                         f1, (bfd_vma) fun->stack, max_stack);
 
   if (fun->call_list)
     {