This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
HOWTO (R_SPU_ADDR18, 0, 2, 18, FALSE, 7, complain_overflow_bitfield,
bfd_elf_generic_reloc, "SPU_ADDR18",
FALSE, 0, 0x01ffff80, FALSE),
- HOWTO (R_SPU_ADDR32, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+ HOWTO (R_SPU_ADDR32, 0, 2, 32, FALSE, 0, complain_overflow_dont,
bfd_elf_generic_reloc, "SPU_ADDR32",
FALSE, 0, 0xffffffff, FALSE),
HOWTO (R_SPU_REL16, 2, 2, 16, TRUE, 7, complain_overflow_bitfield,
HOWTO (R_SPU_ADDR16I, 0, 2, 16, FALSE, 7, complain_overflow_signed,
bfd_elf_generic_reloc, "SPU_ADDR16I",
FALSE, 0, 0x007fff80, FALSE),
- HOWTO (R_SPU_REL32, 0, 2, 32, TRUE, 0, complain_overflow_dont,
+ HOWTO (R_SPU_REL32, 0, 2, 32, TRUE, 0, complain_overflow_dont,
bfd_elf_generic_reloc, "SPU_REL32",
FALSE, 0, 0xffffffff, TRUE),
HOWTO (R_SPU_ADDR16X, 0, 2, 16, FALSE, 7, complain_overflow_bitfield,
bfd_elf_generic_reloc, "SPU_ADDR16X",
FALSE, 0, 0x007fff80, FALSE),
- HOWTO (R_SPU_PPU32, 0, 2, 32, FALSE, 0, complain_overflow_dont,
+ HOWTO (R_SPU_PPU32, 0, 2, 32, FALSE, 0, complain_overflow_dont,
bfd_elf_generic_reloc, "SPU_PPU32",
FALSE, 0, 0xffffffff, FALSE),
- HOWTO (R_SPU_PPU64, 0, 4, 64, FALSE, 0, complain_overflow_dont,
+ HOWTO (R_SPU_PPU64, 0, 4, 64, FALSE, 0, complain_overflow_dont,
bfd_elf_generic_reloc, "SPU_PPU64",
FALSE, 0, -1, FALSE),
};
/* 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
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;
}
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.. */
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. */
&& 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);
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;
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;
}
{
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;
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. */
}
}
- 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;
(*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
. 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 {
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;
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)
{
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)
}
/* 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)
{
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:
}
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)
{
return NULL;
}
+/* Tweak the section type of .note.spu_name. */
+
+static bfd_boolean
+spu_elf_fake_sections (bfd *obfd ATTRIBUTE_UNUSED,
+ Elf_Internal_Shdr *hdr,
+ asection *sec)
+{
+ if (strcmp (sec->name, SPU_PTNOTE_SPUNAME) == 0)
+ hdr->sh_type = SHT_NOTE;
+ return TRUE;
+}
+
/* Tweak phdrs before writing them out. */
static int
#define elf_backend_modify_segment_map spu_elf_modify_segment_map
#define elf_backend_modify_program_headers spu_elf_modify_program_headers
#define elf_backend_post_process_headers spu_elf_post_process_headers
+#define elf_backend_fake_sections spu_elf_fake_sections
#define elf_backend_special_sections spu_elf_special_sections
#define bfd_elf32_bfd_final_link spu_elf_final_link