{
struct elf_link_hash_table elf;
+ /* Various options passed from the linker. */
+ struct ppc_elf_params *params;
+
/* Short-cuts to get to dynamic linker sections. */
asection *got;
asection *relgot;
/* The type of PLT we have chosen to use. */
enum ppc_elf_plt_type plt_type;
- /* Set if we should emit symbols for stubs. */
- unsigned int emit_stub_syms:1;
-
- /* Set if __tls_get_addr optimization should not be done. */
- unsigned int no_tls_get_addr_opt:1;
-
/* True if the target system is VxWorks. */
unsigned int is_vxworks:1;
return &ret->elf.root;
}
+/* Hook linker params into hash table. */
+
+void
+ppc_elf_link_params (struct bfd_link_info *info, struct ppc_elf_params *params)
+{
+ struct ppc_elf_link_hash_table *htab = ppc_elf_hash_table (info);
+
+ if (htab)
+ htab->params = params;
+}
+
/* Create .got and the related sections. */
static bfd_boolean
s = bfd_make_section_anyway_with_flags (abfd, ".glink", flags);
htab->glink = s;
if (s == NULL
- || !bfd_set_section_alignment (abfd, s, 4))
+ || !bfd_set_section_alignment (abfd, s,
+ htab->params->ppc476_workaround ? 6 : 4))
return FALSE;
if (!info->no_ld_generated_unwind_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;
+
+ /* PR15323, ref flags aren't set for references in the same
+ object. */
+ h->root.non_ir_ref = 1;
}
/* If a relocation refers to _GLOBAL_OFFSET_TABLE_, create the .got.
htab->plt_type = PLT_OLD;
htab->old_bfd = abfd;
}
+ if (h != NULL && h->type == STT_GNU_IFUNC)
+ {
+ if (info->shared)
+ {
+ info->callbacks->einfo (_("%P: %H: @local call to ifunc %s\n"),
+ abfd, sec, rel->r_offset,
+ h->root.root.string);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+ h->needs_plt = 1;
+ if (!update_plt_info (abfd, &h->plt.plist, NULL, 0))
+ return FALSE;
+ }
break;
/* This relocation describes the C++ object vtable hierarchy.
if ((info->shared
&& (must_be_dyn_reloc (info, r_type)
|| (h != NULL
- && (! info->symbolic
+ && (!SYMBOLIC_BIND (info, h)
|| h->root.type == bfd_link_hash_defweak
|| !h->def_regular))))
|| (ELIMINATE_COPY_RELOCS
Returns -1 on error, 0 for old PLT, 1 for new PLT. */
int
ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED,
- struct bfd_link_info *info,
- enum ppc_elf_plt_type plt_style,
- int emit_stub_syms)
+ struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
flagword flags;
htab = ppc_elf_hash_table (info);
- htab->emit_stub_syms = emit_stub_syms;
-
if (htab->plt_type == PLT_UNSET)
{
struct elf_link_hash_entry *h;
- if (plt_style == PLT_OLD)
+ if (htab->params->plt_style == PLT_OLD)
htab->plt_type = PLT_OLD;
else if (info->shared
&& htab->elf.dynamic_sections_created
else
{
bfd *ibfd;
- enum ppc_elf_plt_type plt_type = plt_style;
+ enum ppc_elf_plt_type plt_type = htab->params->plt_style;
/* Look through the reloc flags left by ppc_elf_check_relocs.
Use the old style bss plt if a file makes plt calls
htab->plt_type = plt_type;
}
}
- if (htab->plt_type == PLT_OLD && plt_style == PLT_NEW)
+ if (htab->plt_type == PLT_OLD && htab->params->plt_style == PLT_NEW)
{
if (htab->old_bfd != NULL)
info->callbacks->einfo (_("%P: bss-plt forced due to %B\n"),
generic ELF tls_setup function. */
asection *
-ppc_elf_tls_setup (bfd *obfd,
- struct bfd_link_info *info,
- int no_tls_get_addr_opt)
+ppc_elf_tls_setup (bfd *obfd, struct bfd_link_info *info)
{
struct ppc_elf_link_hash_table *htab;
htab = ppc_elf_hash_table (info);
htab->tls_get_addr = elf_link_hash_lookup (&htab->elf, "__tls_get_addr",
FALSE, FALSE, TRUE);
- if (!no_tls_get_addr_opt)
+ if (!htab->params->no_tls_get_addr_opt)
{
struct elf_link_hash_entry *opt, *tga;
opt = elf_link_hash_lookup (&htab->elf, "__tls_get_addr_opt",
}
}
else
- no_tls_get_addr_opt = TRUE;
+ htab->params->no_tls_get_addr_opt = TRUE;
}
- htab->no_tls_get_addr_opt = no_tls_get_addr_opt;
if (htab->plt_type == PLT_NEW
&& htab->plt != NULL
&& htab->plt->output_section != NULL)
glink_offset = s->size;
s->size += GLINK_ENTRY_SIZE;
if (h == htab->tls_get_addr
- && !htab->no_tls_get_addr_opt)
+ && !htab->params->no_tls_get_addr_opt)
s->size += TLS_GET_ADDR_GLINK_SIZE - GLINK_ENTRY_SIZE;
}
if (!doneone
}
ent->glink_offset = glink_offset;
- if (htab->emit_stub_syms
+ if (htab->params->emit_stub_syms
&& !add_stub_sym (ent, h, info))
return FALSE;
}
/* Space for the branch table. */
htab->glink->size += htab->glink->size / (GLINK_ENTRY_SIZE / 4) - 4;
/* Pad out to align the start of PLTresolve. */
- htab->glink->size += -htab->glink->size & 15;
+ htab->glink->size += -htab->glink->size & (htab->params->ppc476_workaround
+ ? 63 : 15);
htab->glink->size += GLINK_PLTRESOLVE;
- if (htab->emit_stub_syms)
+ if (htab->params->emit_stub_syms)
{
struct elf_link_hash_entry *sh;
sh = elf_link_hash_lookup (&htab->elf, "__glink",
return FALSE;
}
- if (htab->glink != NULL && htab->glink->size != 0)
+ if (htab->plt_type == PLT_NEW
+ && htab->glink != NULL
+ && htab->glink->size != 0)
{
if (!add_dynamic_entry (DT_PPC_GOT, 0))
return FALSE;
- if (!htab->no_tls_get_addr_opt
+ if (!htab->params->no_tls_get_addr_opt
&& htab->tls_get_addr != NULL
&& htab->tls_get_addr->plt.plist != NULL
- && !add_dynamic_entry (DT_PPC_TLSOPT, 0))
+ && !add_dynamic_entry (DT_PPC_OPT, PPC_OPT_TLS))
return FALSE;
}
0x429f0005, /* bcl 20, 31, .Lxxx */
0x7d8802a6, /* mflr 12 */
0x3d8c0000, /* addis 12, 12, (xxx-.Lxxx)@ha */
- 0x398c0008, /* addi 12, 12, (xxx-.Lxxx)@l */
+ 0x398c0000, /* addi 12, 12, (xxx-.Lxxx)@l */
0x7c0803a6, /* mtlr 0 */
0x7d8903a6, /* mtctr 12 */
0x4e800420, /* bctr */
0x4e800420, /* bctr */
};
+struct ppc_elf_relax_info
+{
+ unsigned int workaround_size;
+};
+
+/* This function implements long branch trampolines, and the ppc476
+ icache bug workaround. Any section needing trampolines or patch
+ space for the workaround has its size extended so that we can
+ add trampolines at the end of the section. */
+
static bfd_boolean
ppc_elf_relax_section (bfd *abfd,
asection *isec,
bfd_byte *contents = NULL;
Elf_Internal_Sym *isymbuf = NULL;
Elf_Internal_Rela *internal_relocs = NULL;
- Elf_Internal_Rela *irel, *irelend;
+ Elf_Internal_Rela *irel, *irelend = NULL;
struct one_fixup *fixups = NULL;
+ struct ppc_elf_relax_info *relax_info = NULL;
unsigned changes = 0;
+ bfd_boolean workaround_change;
struct ppc_elf_link_hash_table *htab;
- bfd_size_type trampoff;
+ bfd_size_type trampbase, trampoff, newsize;
asection *got2;
bfd_boolean maybe_pasted;
*again = FALSE;
- /* Nothing to do if there are no relocations, and no need to do
- anything with non-alloc or non-code sections. */
+ /* No need to do anything with non-alloc or non-code sections. */
if ((isec->flags & SEC_ALLOC) == 0
|| (isec->flags & SEC_CODE) == 0
- || (isec->flags & SEC_RELOC) == 0
- || isec->reloc_count == 0)
+ || (isec->flags & SEC_LINKER_CREATED) != 0
+ || isec->size < 4)
return TRUE;
/* We cannot represent the required PIC relocs in the output, so don't
if (link_info->relocatable && link_info->shared)
return TRUE;
- trampoff = (isec->size + 3) & (bfd_vma) -4;
+ htab = ppc_elf_hash_table (link_info);
+ if (htab == NULL)
+ return TRUE;
+
+ isec->size = (isec->size + 3) & -4;
+ if (isec->rawsize == 0)
+ isec->rawsize = isec->size;
+ trampbase = isec->size;
+
+ BFD_ASSERT (isec->sec_info_type == SEC_INFO_TYPE_NONE
+ || isec->sec_info_type == SEC_INFO_TYPE_TARGET);
+ isec->sec_info_type = SEC_INFO_TYPE_TARGET;
+
+ if (htab->params->ppc476_workaround)
+ {
+ if (elf_section_data (isec)->sec_info == NULL)
+ {
+ elf_section_data (isec)->sec_info
+ = bfd_zalloc (abfd, sizeof (struct ppc_elf_relax_info));
+ if (elf_section_data (isec)->sec_info == NULL)
+ return FALSE;
+ }
+ relax_info = elf_section_data (isec)->sec_info;
+ trampbase -= relax_info->workaround_size;
+ }
+
maybe_pasted = (strcmp (isec->output_section->name, ".init") == 0
|| strcmp (isec->output_section->name, ".fini") == 0);
/* Space for a branch around any trampolines. */
- if (maybe_pasted)
+ trampoff = trampbase;
+ if (maybe_pasted && trampbase == isec->rawsize)
trampoff += 4;
symtab_hdr = &elf_symtab_hdr (abfd);
- /* Get a copy of the native relocations. */
- internal_relocs = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL,
- link_info->keep_memory);
- if (internal_relocs == NULL)
- goto error_return;
-
- htab = ppc_elf_hash_table (link_info);
- got2 = bfd_get_section_by_name (abfd, ".got2");
-
- irelend = internal_relocs + isec->reloc_count;
- for (irel = internal_relocs; irel < irelend; irel++)
+ if (htab->params->branch_trampolines)
{
- unsigned long r_type = ELF32_R_TYPE (irel->r_info);
- bfd_vma toff, roff;
- asection *tsec;
- struct one_fixup *f;
- size_t insn_offset = 0;
- bfd_vma max_branch_offset, val;
- bfd_byte *hit_addr;
- unsigned long t0;
- struct elf_link_hash_entry *h;
- struct plt_entry **plist;
- unsigned char sym_type;
-
- switch (r_type)
+ /* Get a copy of the native relocations. */
+ if (isec->reloc_count != 0)
{
- case R_PPC_REL24:
- case R_PPC_LOCAL24PC:
- case R_PPC_PLTREL24:
- max_branch_offset = 1 << 25;
- break;
-
- case R_PPC_REL14:
- case R_PPC_REL14_BRTAKEN:
- case R_PPC_REL14_BRNTAKEN:
- max_branch_offset = 1 << 15;
- break;
-
- default:
- continue;
+ internal_relocs = _bfd_elf_link_read_relocs (abfd, isec, NULL, NULL,
+ link_info->keep_memory);
+ if (internal_relocs == NULL)
+ goto error_return;
}
- /* Get the value of the symbol referred to by the reloc. */
- h = NULL;
- if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
+ got2 = bfd_get_section_by_name (abfd, ".got2");
+
+ irelend = internal_relocs + isec->reloc_count;
+ for (irel = internal_relocs; irel < irelend; irel++)
{
- /* A local symbol. */
- Elf_Internal_Sym *isym;
+ unsigned long r_type = ELF32_R_TYPE (irel->r_info);
+ bfd_vma toff, roff;
+ asection *tsec;
+ struct one_fixup *f;
+ size_t insn_offset = 0;
+ bfd_vma max_branch_offset, val;
+ bfd_byte *hit_addr;
+ unsigned long t0;
+ struct elf_link_hash_entry *h;
+ struct plt_entry **plist;
+ unsigned char sym_type;
+
+ switch (r_type)
+ {
+ case R_PPC_REL24:
+ case R_PPC_LOCAL24PC:
+ case R_PPC_PLTREL24:
+ max_branch_offset = 1 << 25;
+ break;
+
+ case R_PPC_REL14:
+ case R_PPC_REL14_BRTAKEN:
+ case R_PPC_REL14_BRNTAKEN:
+ max_branch_offset = 1 << 15;
+ break;
+
+ default:
+ continue;
+ }
- /* Read this BFD's local symbols. */
- if (isymbuf == NULL)
+ /* Get the value of the symbol referred to by the reloc. */
+ h = NULL;
+ if (ELF32_R_SYM (irel->r_info) < symtab_hdr->sh_info)
{
- isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+ /* A local symbol. */
+ Elf_Internal_Sym *isym;
+
+ /* Read this BFD's local symbols. */
if (isymbuf == NULL)
- isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
- symtab_hdr->sh_info, 0,
- NULL, NULL, NULL);
- if (isymbuf == 0)
- goto error_return;
+ {
+ isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (isymbuf == NULL)
+ isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+ symtab_hdr->sh_info, 0,
+ NULL, NULL, NULL);
+ if (isymbuf == 0)
+ goto error_return;
+ }
+ isym = isymbuf + ELF32_R_SYM (irel->r_info);
+ if (isym->st_shndx == SHN_UNDEF)
+ tsec = bfd_und_section_ptr;
+ else if (isym->st_shndx == SHN_ABS)
+ tsec = bfd_abs_section_ptr;
+ else if (isym->st_shndx == SHN_COMMON)
+ tsec = bfd_com_section_ptr;
+ else
+ tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+
+ toff = isym->st_value;
+ sym_type = ELF_ST_TYPE (isym->st_info);
}
- isym = isymbuf + ELF32_R_SYM (irel->r_info);
- if (isym->st_shndx == SHN_UNDEF)
- tsec = bfd_und_section_ptr;
- else if (isym->st_shndx == SHN_ABS)
- tsec = bfd_abs_section_ptr;
- else if (isym->st_shndx == SHN_COMMON)
- tsec = bfd_com_section_ptr;
else
- tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ {
+ /* Global symbol handling. */
+ unsigned long indx;
- toff = isym->st_value;
- sym_type = ELF_ST_TYPE (isym->st_info);
- }
- else
- {
- /* Global symbol handling. */
- unsigned long indx;
+ indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
+ h = elf_sym_hashes (abfd)[indx];
- indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info;
- h = elf_sym_hashes (abfd)[indx];
+ 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;
- 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;
+ if (h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ {
+ tsec = h->root.u.def.section;
+ toff = h->root.u.def.value;
+ }
+ else if (h->root.type == bfd_link_hash_undefined
+ || h->root.type == bfd_link_hash_undefweak)
+ {
+ tsec = bfd_und_section_ptr;
+ toff = link_info->relocatable ? indx : 0;
+ }
+ else
+ continue;
+
+ sym_type = h->type;
+ }
- if (h->root.type == bfd_link_hash_defined
- || h->root.type == bfd_link_hash_defweak)
+ /* The condition here under which we call find_plt_ent must
+ match that in relocate_section. If we call find_plt_ent here
+ but not in relocate_section, or vice versa, then the branch
+ destination used here may be incorrect. */
+ plist = NULL;
+ if (h != NULL)
{
- tsec = h->root.u.def.section;
- toff = h->root.u.def.value;
+ /* We know is_branch_reloc (r_type) is true. */
+ if (h->type == STT_GNU_IFUNC
+ || r_type == R_PPC_PLTREL24)
+ plist = &h->plt.plist;
}
- else if (h->root.type == bfd_link_hash_undefined
- || h->root.type == bfd_link_hash_undefweak)
+ else if (sym_type == STT_GNU_IFUNC
+ && elf_local_got_offsets (abfd) != NULL)
{
- tsec = bfd_und_section_ptr;
- toff = link_info->relocatable ? indx : 0;
+ bfd_vma *local_got_offsets = elf_local_got_offsets (abfd);
+ struct plt_entry **local_plt = (struct plt_entry **)
+ (local_got_offsets + symtab_hdr->sh_info);
+ plist = local_plt + ELF32_R_SYM (irel->r_info);
}
- else
- continue;
-
- sym_type = h->type;
- }
-
- /* The condition here under which we call find_plt_ent must
- match that in relocate_section. If we call find_plt_ent here
- but not in relocate_section, or vice versa, then the branch
- destination used here may be incorrect. */
- plist = NULL;
- if (h != NULL)
- {
- /* We know is_branch_reloc (r_type) is true. */
- if (h->type == STT_GNU_IFUNC
- || r_type == R_PPC_PLTREL24)
- plist = &h->plt.plist;
- }
- else if (sym_type == STT_GNU_IFUNC
- && elf_local_got_offsets (abfd) != NULL)
- {
- bfd_vma *local_got_offsets = elf_local_got_offsets (abfd);
- struct plt_entry **local_plt = (struct plt_entry **)
- (local_got_offsets + symtab_hdr->sh_info);
- plist = local_plt + ELF32_R_SYM (irel->r_info);
- }
- if (plist != NULL)
- {
- bfd_vma addend = 0;
- struct plt_entry *ent;
-
- if (r_type == R_PPC_PLTREL24 && link_info->shared)
- addend = irel->r_addend;
- ent = find_plt_ent (plist, got2, addend);
- if (ent != NULL)
+ if (plist != NULL)
{
- if (htab->plt_type == PLT_NEW
- || h == NULL
- || !htab->elf.dynamic_sections_created
- || h->dynindx == -1)
- {
- tsec = htab->glink;
- toff = ent->glink_offset;
- }
- else
+ bfd_vma addend = 0;
+ struct plt_entry *ent;
+
+ if (r_type == R_PPC_PLTREL24 && link_info->shared)
+ addend = irel->r_addend;
+ ent = find_plt_ent (plist, got2, addend);
+ if (ent != NULL)
{
- tsec = htab->plt;
- toff = ent->plt.offset;
+ if (htab->plt_type == PLT_NEW
+ || h == NULL
+ || !htab->elf.dynamic_sections_created
+ || h->dynindx == -1)
+ {
+ tsec = htab->glink;
+ toff = ent->glink_offset;
+ }
+ else
+ {
+ tsec = htab->plt;
+ toff = ent->plt.offset;
+ }
}
}
- }
- /* If the branch and target are in the same section, you have
- no hope of adding stubs. We'll error out later should the
- branch overflow. */
- if (tsec == isec)
- continue;
+ /* If the branch and target are in the same section, you have
+ no hope of adding stubs. We'll error out later should the
+ branch overflow. */
+ if (tsec == isec)
+ continue;
- /* There probably isn't any reason to handle symbols in
- SEC_MERGE sections; SEC_MERGE doesn't seem a likely
- attribute for a code section, and we are only looking at
- branches. However, implement it correctly here as a
- reference for other target relax_section functions. */
- if (0 && tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
- {
- /* At this stage in linking, no SEC_MERGE symbol has been
- adjusted, so all references to such symbols need to be
- passed through _bfd_merged_section_offset. (Later, in
- relocate_section, all SEC_MERGE symbols *except* for
- section symbols have been adjusted.)
-
- gas may reduce relocations against symbols in SEC_MERGE
- sections to a relocation against the section symbol when
- the original addend was zero. When the reloc is against
- a section symbol we should include the addend in the
- offset passed to _bfd_merged_section_offset, since the
- location of interest is the original symbol. On the
- other hand, an access to "sym+addend" where "sym" is not
- a section symbol should not include the addend; Such an
- access is presumed to be an offset from "sym"; The
- location of interest is just "sym". */
- if (sym_type == STT_SECTION)
+ /* There probably isn't any reason to handle symbols in
+ SEC_MERGE sections; SEC_MERGE doesn't seem a likely
+ attribute for a code section, and we are only looking at
+ branches. However, implement it correctly here as a
+ reference for other target relax_section functions. */
+ if (0 && tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
+ {
+ /* At this stage in linking, no SEC_MERGE symbol has been
+ adjusted, so all references to such symbols need to be
+ passed through _bfd_merged_section_offset. (Later, in
+ relocate_section, all SEC_MERGE symbols *except* for
+ section symbols have been adjusted.)
+
+ gas may reduce relocations against symbols in SEC_MERGE
+ sections to a relocation against the section symbol when
+ the original addend was zero. When the reloc is against
+ a section symbol we should include the addend in the
+ offset passed to _bfd_merged_section_offset, since the
+ location of interest is the original symbol. On the
+ other hand, an access to "sym+addend" where "sym" is not
+ a section symbol should not include the addend; Such an
+ access is presumed to be an offset from "sym"; The
+ location of interest is just "sym". */
+ if (sym_type == STT_SECTION)
+ toff += irel->r_addend;
+
+ toff
+ = _bfd_merged_section_offset (abfd, &tsec,
+ elf_section_data (tsec)->sec_info,
+ toff);
+
+ if (sym_type != STT_SECTION)
+ toff += irel->r_addend;
+ }
+ /* PLTREL24 addends are special. */
+ else if (r_type != R_PPC_PLTREL24)
toff += irel->r_addend;
- toff = _bfd_merged_section_offset (abfd, &tsec,
- elf_section_data (tsec)->sec_info,
- toff);
-
- if (sym_type != STT_SECTION)
- toff += irel->r_addend;
- }
- /* PLTREL24 addends are special. */
- else if (r_type != R_PPC_PLTREL24)
- toff += irel->r_addend;
+ /* Attempted -shared link of non-pic code loses. */
+ if ((!link_info->relocatable
+ && tsec == bfd_und_section_ptr)
+ || tsec->output_section == NULL
+ || (tsec->owner != NULL
+ && (tsec->owner->flags & BFD_PLUGIN) != 0))
+ continue;
- /* Attempted -shared link of non-pic code loses. */
- if (tsec->output_section == NULL)
- continue;
+ roff = irel->r_offset;
- roff = irel->r_offset;
+ /* If the branch is in range, no need to do anything. */
+ if (tsec != bfd_und_section_ptr
+ && (!link_info->relocatable
+ /* A relocatable link may have sections moved during
+ final link, so do not presume they remain in range. */
+ || tsec->output_section == isec->output_section))
+ {
+ bfd_vma symaddr, reladdr;
- /* If the branch is in range, no need to do anything. */
- if (tsec != bfd_und_section_ptr
- && (!link_info->relocatable
- /* A relocatable link may have sections moved during
- final link, so do not presume they remain in range. */
- || tsec->output_section == isec->output_section))
- {
- bfd_vma symaddr, reladdr;
+ symaddr = tsec->output_section->vma + tsec->output_offset + toff;
+ reladdr = isec->output_section->vma + isec->output_offset + roff;
+ if (symaddr - reladdr + max_branch_offset
+ < 2 * max_branch_offset)
+ continue;
+ }
- symaddr = tsec->output_section->vma + tsec->output_offset + toff;
- reladdr = isec->output_section->vma + isec->output_offset + roff;
- if (symaddr - reladdr + max_branch_offset < 2 * max_branch_offset)
- continue;
- }
+ /* Look for an existing fixup to this address. */
+ for (f = fixups; f ; f = f->next)
+ if (f->tsec == tsec && f->toff == toff)
+ break;
- /* Look for an existing fixup to this address. */
- for (f = fixups; f ; f = f->next)
- if (f->tsec == tsec && f->toff == toff)
- break;
+ if (f == NULL)
+ {
+ size_t size;
+ unsigned long stub_rtype;
- if (f == NULL)
- {
- size_t size;
- unsigned long stub_rtype;
+ val = trampoff - roff;
+ if (val >= max_branch_offset)
+ /* Oh dear, we can't reach a trampoline. Don't try to add
+ one. We'll report an error later. */
+ continue;
- val = trampoff - roff;
- if (val >= max_branch_offset)
- /* Oh dear, we can't reach a trampoline. Don't try to add
- one. We'll report an error later. */
- continue;
+ if (link_info->shared)
+ {
+ size = 4 * ARRAY_SIZE (shared_stub_entry);
+ insn_offset = 12;
+ }
+ else
+ {
+ size = 4 * ARRAY_SIZE (stub_entry);
+ insn_offset = 0;
+ }
+ stub_rtype = R_PPC_RELAX;
+ if (tsec == htab->plt
+ || tsec == htab->glink)
+ {
+ stub_rtype = R_PPC_RELAX_PLT;
+ if (r_type == R_PPC_PLTREL24)
+ stub_rtype = R_PPC_RELAX_PLTREL24;
+ }
- if (link_info->shared)
- {
- size = 4 * ARRAY_SIZE (shared_stub_entry);
- insn_offset = 12;
+ /* Hijack the old relocation. Since we need two
+ relocations for this use a "composite" reloc. */
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+ stub_rtype);
+ irel->r_offset = trampoff + insn_offset;
+ if (r_type == R_PPC_PLTREL24
+ && stub_rtype != R_PPC_RELAX_PLTREL24)
+ irel->r_addend = 0;
+
+ /* Record the fixup so we don't do it again this section. */
+ f = bfd_malloc (sizeof (*f));
+ f->next = fixups;
+ f->tsec = tsec;
+ f->toff = toff;
+ f->trampoff = trampoff;
+ fixups = f;
+
+ trampoff += size;
+ changes++;
}
else
{
- size = 4 * ARRAY_SIZE (stub_entry);
- insn_offset = 0;
- }
- stub_rtype = R_PPC_RELAX;
- if (tsec == htab->plt
- || tsec == htab->glink)
- {
- stub_rtype = R_PPC_RELAX_PLT;
- if (r_type == R_PPC_PLTREL24)
- stub_rtype = R_PPC_RELAX_PLTREL24;
- }
-
- /* Hijack the old relocation. Since we need two
- relocations for this use a "composite" reloc. */
- irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
- stub_rtype);
- irel->r_offset = trampoff + insn_offset;
- if (r_type == R_PPC_PLTREL24
- && stub_rtype != R_PPC_RELAX_PLTREL24)
- irel->r_addend = 0;
-
- /* Record the fixup so we don't do it again this section. */
- f = bfd_malloc (sizeof (*f));
- f->next = fixups;
- f->tsec = tsec;
- f->toff = toff;
- f->trampoff = trampoff;
- fixups = f;
-
- trampoff += size;
- changes++;
- }
- else
- {
- val = f->trampoff - roff;
- if (val >= max_branch_offset)
- continue;
+ val = f->trampoff - roff;
+ if (val >= max_branch_offset)
+ continue;
- /* Nop out the reloc, since we're finalizing things here. */
- irel->r_info = ELF32_R_INFO (0, R_PPC_NONE);
- }
+ /* Nop out the reloc, since we're finalizing things here. */
+ irel->r_info = ELF32_R_INFO (0, R_PPC_NONE);
+ }
- /* Get the section contents. */
- if (contents == NULL)
- {
- /* Get cached copy if it exists. */
- if (elf_section_data (isec)->this_hdr.contents != NULL)
- contents = elf_section_data (isec)->this_hdr.contents;
- else
+ /* Get the section contents. */
+ if (contents == NULL)
{
+ /* Get cached copy if it exists. */
+ if (elf_section_data (isec)->this_hdr.contents != NULL)
+ contents = elf_section_data (isec)->this_hdr.contents;
/* Go get them off disk. */
- if (!bfd_malloc_and_get_section (abfd, isec, &contents))
+ else if (!bfd_malloc_and_get_section (abfd, isec, &contents))
goto error_return;
}
- }
- /* Fix up the existing branch to hit the trampoline. */
- hit_addr = contents + roff;
- switch (r_type)
- {
- case R_PPC_REL24:
- case R_PPC_LOCAL24PC:
- case R_PPC_PLTREL24:
- t0 = bfd_get_32 (abfd, hit_addr);
- t0 &= ~0x3fffffc;
- t0 |= val & 0x3fffffc;
- bfd_put_32 (abfd, t0, hit_addr);
- break;
+ /* Fix up the existing branch to hit the trampoline. */
+ hit_addr = contents + roff;
+ switch (r_type)
+ {
+ case R_PPC_REL24:
+ case R_PPC_LOCAL24PC:
+ case R_PPC_PLTREL24:
+ t0 = bfd_get_32 (abfd, hit_addr);
+ t0 &= ~0x3fffffc;
+ t0 |= val & 0x3fffffc;
+ bfd_put_32 (abfd, t0, hit_addr);
+ break;
- case R_PPC_REL14:
- case R_PPC_REL14_BRTAKEN:
- case R_PPC_REL14_BRNTAKEN:
- t0 = bfd_get_32 (abfd, hit_addr);
- t0 &= ~0xfffc;
- t0 |= val & 0xfffc;
- bfd_put_32 (abfd, t0, hit_addr);
- break;
+ case R_PPC_REL14:
+ case R_PPC_REL14_BRTAKEN:
+ case R_PPC_REL14_BRNTAKEN:
+ t0 = bfd_get_32 (abfd, hit_addr);
+ t0 &= ~0xfffc;
+ t0 |= val & 0xfffc;
+ bfd_put_32 (abfd, t0, hit_addr);
+ break;
+ }
}
- }
- /* Write out the trampolines. */
- if (fixups != NULL)
- {
- const int *stub;
- bfd_byte *dest;
- int i, size;
-
- do
+ while (fixups != NULL)
{
struct one_fixup *f = fixups;
fixups = fixups->next;
free (f);
}
- while (fixups);
-
- contents = bfd_realloc_or_free (contents, trampoff);
- if (contents == NULL)
- goto error_return;
-
- isec->size = (isec->size + 3) & (bfd_vma) -4;
- dest = contents + isec->size;
- /* Branch around the trampolines. */
- if (maybe_pasted)
- {
- bfd_vma val = B + trampoff - isec->size;
- bfd_put_32 (abfd, val, dest);
- dest += 4;
- }
- isec->size = trampoff;
-
- if (link_info->shared)
- {
- stub = shared_stub_entry;
- size = ARRAY_SIZE (shared_stub_entry);
- }
- else
- {
- stub = stub_entry;
- size = ARRAY_SIZE (stub_entry);
- }
+ }
- i = 0;
- while (dest < contents + trampoff)
+ workaround_change = FALSE;
+ newsize = trampoff;
+ if (htab->params->ppc476_workaround)
+ {
+ bfd_vma addr, end_addr;
+ unsigned int crossings;
+ unsigned int pagesize = htab->params->pagesize;
+
+ addr = isec->output_section->vma + isec->output_offset;
+ end_addr = addr + trampoff - 1;
+ addr &= -pagesize;
+ crossings = ((end_addr & -pagesize) - addr) / pagesize;
+ if (crossings != 0)
{
- bfd_put_32 (abfd, stub[i], dest);
- i++;
- if (i == size)
- i = 0;
- dest += 4;
+ /* Keep space aligned, to ensure the patch code itself does
+ not cross a page. Don't decrease size calculated on a
+ previous pass as otherwise we might never settle on a layout. */
+ newsize = 15 - (end_addr & 15);
+ newsize += crossings * 16;
+ if (relax_info->workaround_size < newsize)
+ {
+ relax_info->workaround_size = newsize;
+ workaround_change = TRUE;
+ }
+ /* Ensure relocate_section is called. */
+ isec->flags |= SEC_RELOC;
}
- BFD_ASSERT (i == 0);
+ newsize = trampoff + relax_info->workaround_size;
}
+ if (changes || workaround_change)
+ isec->size = newsize;
+
if (isymbuf != NULL
&& symtab_hdr->contents != (unsigned char *) isymbuf)
{
rel_hdr = _bfd_elf_single_rel_hdr (isec);
rel_hdr->sh_size += changes * rel_hdr->sh_entsize;
}
- else if (elf_section_data (isec)->relocs != internal_relocs)
+ else if (internal_relocs != NULL
+ && elf_section_data (isec)->relocs != internal_relocs)
free (internal_relocs);
- *again = changes != 0;
- if (!*again && link_info->relocatable)
- {
- /* Convert the internal relax relocs to external form. */
- for (irel = internal_relocs; irel < irelend; irel++)
- if (ELF32_R_TYPE (irel->r_info) == R_PPC_RELAX)
- {
- unsigned long r_symndx = ELF32_R_SYM (irel->r_info);
-
- /* Rewrite the reloc and convert one of the trailing nop
- relocs to describe this relocation. */
- BFD_ASSERT (ELF32_R_TYPE (irelend[-1].r_info) == R_PPC_NONE);
- /* The relocs are at the bottom 2 bytes */
- irel[0].r_offset += 2;
- memmove (irel + 1, irel, (irelend - irel - 1) * sizeof (*irel));
- irel[0].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
- irel[1].r_offset += 4;
- irel[1].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_LO);
- irel++;
- }
- }
-
+ *again = changes != 0 || workaround_change;
return TRUE;
error_return:
+ while (fixups != NULL)
+ {
+ struct one_fixup *f = fixups;
+ fixups = fixups->next;
+ free (f);
+ }
if (isymbuf != NULL && (unsigned char *) isymbuf != symtab_hdr->contents)
free (isymbuf);
if (contents != NULL
}
else
{
+ bfd_boolean ignored;
+
RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel,
r_symndx, symtab_hdr, sym_hashes,
h, sec, relocation,
- unresolved_reloc, warned);
+ unresolved_reloc, warned, ignored);
sym_name = h->root.root.string;
}
addend specifies the GOT pointer offset within .got2. */
rel->r_addend += got2->output_offset;
}
- continue;
+ if (r_type != R_PPC_RELAX_PLT
+ && r_type != R_PPC_RELAX_PLTREL24
+ && r_type != R_PPC_RELAX)
+ continue;
}
/* TLS optimizations. Replace instruction sequences and relocs
unresolved_reloc = FALSE;
if (htab->plt_type == PLT_NEW
|| !htab->elf.dynamic_sections_created
- || h == NULL)
+ || h == NULL
+ || h->dynindx == -1)
relocation = (htab->glink->output_section->vma
+ htab->glink->output_offset
+ (ent->glink_offset & ~1));
/* Fall thru */
case R_PPC_RELAX:
- if (info->shared)
- relocation -= (input_section->output_section->vma
- + input_section->output_offset
- + rel->r_offset - 4);
-
{
- unsigned long t0;
- unsigned long t1;
-
- t0 = bfd_get_32 (output_bfd, contents + rel->r_offset);
- t1 = bfd_get_32 (output_bfd, contents + rel->r_offset + 4);
+ const int *stub;
+ size_t size;
+ size_t insn_offset = rel->r_offset;
+ unsigned int insn;
- /* We're clearing the bits for R_PPC_ADDR16_HA
- and R_PPC_ADDR16_LO here. */
- t0 &= ~0xffff;
- t1 &= ~0xffff;
+ if (info->shared)
+ {
+ relocation -= (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset - 4);
+ stub = shared_stub_entry;
+ bfd_put_32 (output_bfd, stub[0], contents + insn_offset - 12);
+ bfd_put_32 (output_bfd, stub[1], contents + insn_offset - 8);
+ bfd_put_32 (output_bfd, stub[2], contents + insn_offset - 4);
+ stub += 3;
+ size = ARRAY_SIZE (shared_stub_entry) - 3;
+ }
+ else
+ {
+ stub = stub_entry;
+ size = ARRAY_SIZE (stub_entry);
+ }
- /* t0 is HA, t1 is LO */
relocation += addend;
- t0 |= ((relocation + 0x8000) >> 16) & 0xffff;
- t1 |= relocation & 0xffff;
-
- bfd_put_32 (output_bfd, t0, contents + rel->r_offset);
- bfd_put_32 (output_bfd, t1, contents + rel->r_offset + 4);
+ if (info->relocatable)
+ relocation = 0;
+
+ /* First insn is HA, second is LO. */
+ insn = *stub++;
+ insn |= ((relocation + 0x8000) >> 16) & 0xffff;
+ bfd_put_32 (output_bfd, insn, contents + insn_offset);
+ insn_offset += 4;
+
+ insn = *stub++;
+ insn |= relocation & 0xffff;
+ bfd_put_32 (output_bfd, insn, contents + insn_offset);
+ insn_offset += 4;
+ size -= 2;
+
+ while (size != 0)
+ {
+ insn = *stub++;
+ --size;
+ bfd_put_32 (output_bfd, insn, contents + insn_offset);
+ insn_offset += 4;
+ }
/* Rewrite the reloc and convert one of the trailing nop
relocs to describe this relocation. */
BFD_ASSERT (ELF32_R_TYPE (relend[-1].r_info) == R_PPC_NONE);
/* The relocs are at the bottom 2 bytes */
- rel[0].r_offset += 2;
+ rel[0].r_offset += d_offset;
memmove (rel + 1, rel, (relend - rel - 1) * sizeof (*rel));
rel[0].r_info = ELF32_R_INFO (r_symndx, R_PPC_ADDR16_HA);
rel[1].r_offset += 4;
fprintf (stderr, "\n");
#endif
+ if (input_section->sec_info_type == SEC_INFO_TYPE_TARGET
+ && input_section->size != input_section->rawsize
+ && (strcmp (input_section->output_section->name, ".init") == 0
+ || strcmp (input_section->output_section->name, ".fini") == 0))
+ {
+ /* Branch around the trampolines. */
+ unsigned int insn = B + input_section->size - input_section->rawsize;
+ bfd_put_32 (input_bfd, insn, contents + input_section->rawsize);
+ }
+
+ if (htab->params->ppc476_workaround
+ && input_section->sec_info_type == SEC_INFO_TYPE_TARGET)
+ {
+ struct ppc_elf_relax_info *relax_info;
+ bfd_vma start_addr, end_addr, addr;
+ unsigned int pagesize = htab->params->pagesize;
+
+ relax_info = elf_section_data (input_section)->sec_info;
+ if (relax_info->workaround_size != 0)
+ memset (contents + input_section->size - relax_info->workaround_size,
+ 0, relax_info->workaround_size);
+
+ /* The idea is: Replace the last instruction on a page with a
+ branch to a patch area. Put the insn there followed by a
+ branch back to the next page. Complicated a little by
+ needing to handle moved conditional branches, and by not
+ wanting to touch data-in-text. */
+
+ start_addr = (input_section->output_section->vma
+ + input_section->output_offset);
+ end_addr = (start_addr + input_section->size
+ - relax_info->workaround_size);
+ for (addr = ((start_addr & -pagesize) + pagesize - 4);
+ addr < end_addr;
+ addr += pagesize)
+ {
+ bfd_vma offset = addr - start_addr;
+ Elf_Internal_Rela *lo, *hi;
+ bfd_boolean is_data;
+ bfd_vma patch_off, patch_addr;
+ unsigned int insn;
+
+ /* Do we have a data reloc at this offset? If so, leave
+ the word alone. */
+ is_data = FALSE;
+ lo = relocs;
+ hi = lo + input_section->reloc_count;
+ while (lo < hi)
+ {
+ rel = lo + (hi - lo) / 2;
+ if (rel->r_offset < offset)
+ lo = rel + 1;
+ else if (rel->r_offset > offset)
+ hi = rel;
+ else
+ {
+ switch (ELF32_R_TYPE (rel->r_info))
+ {
+ case R_PPC_ADDR32:
+ case R_PPC_UADDR32:
+ case R_PPC_REL32:
+ case R_PPC_ADDR30:
+ is_data = TRUE;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ }
+ if (is_data)
+ continue;
+
+ /* Some instructions can be left alone too. In this
+ category are most insns that unconditionally change
+ control flow, and isync. Of these, some *must* be left
+ alone, for example, the "bcl 20, 31, label" used in pic
+ sequences to give the address of the next insn. twui
+ and twu apparently are not safe. */
+ insn = bfd_get_32 (input_bfd, contents + offset);
+ if (insn == 0
+ || (insn & (0x3f << 26)) == (18u << 26) /* b */
+ || ((insn & (0x3f << 26)) == (16u << 26) /* bc always */
+ && (insn & (0x14 << 21)) == (0x14 << 21))
+ || ((insn & (0x3f << 26)) == (19u << 26) /* blr, bctr */
+ && (insn & (0x14 << 21)) == (0x14 << 21)
+ && (insn & (0x1ff << 1)) == (16u << 1))
+ || (insn & (0x3f << 26)) == (17u << 26) /* sc */
+ || ((insn & (0x3f << 26)) == (19u << 26)
+ && ((insn & (0x3ff << 1)) == (38u << 1) /* rfmci */
+ || (insn & (0x3ff << 1)) == (50u << 1) /* rfi */
+ || (insn & (0x3ff << 1)) == (51u << 1) /* rfci */
+ || (insn & (0x3ff << 1)) == (82u << 1) /* rfsvc */
+ || (insn & (0x3ff << 1)) == (150u << 1))) /* isync */)
+ continue;
+
+ patch_addr = (start_addr + input_section->size
+ - relax_info->workaround_size);
+ patch_addr = (patch_addr + 15) & -16;
+ patch_off = patch_addr - start_addr;
+ bfd_put_32 (input_bfd, B + patch_off - offset, contents + offset);
+ if ((insn & (0x3f << 26)) == (16u << 26) /* bc */
+ && (insn & 2) == 0 /* relative */)
+ {
+ bfd_vma delta = ((insn & 0xfffc) ^ 0x8000) - 0x8000;
+
+ delta += offset - patch_off;
+ if (delta + 0x8000 < 0x10000)
+ {
+ bfd_put_32 (input_bfd,
+ (insn & ~0xfffc) | (delta & 0xfffc),
+ contents + patch_off);
+ patch_off += 4;
+ bfd_put_32 (input_bfd,
+ B | ((offset + 4 - patch_off) & 0x3fffffc),
+ contents + patch_off);
+ patch_off += 4;
+ }
+ else
+ {
+ bfd_put_32 (input_bfd,
+ (insn & ~0xfffc) | 8,
+ contents + patch_off);
+ patch_off += 4;
+ bfd_put_32 (input_bfd,
+ B | ((offset + 4 - patch_off) & 0x3fffffc),
+ contents + patch_off);
+ patch_off += 4;
+ bfd_put_32 (input_bfd,
+ B | ((delta - 8) & 0x3fffffc),
+ contents + patch_off);
+ patch_off += 4;
+ }
+ }
+ else
+ {
+ bfd_put_32 (input_bfd, insn, contents + patch_off);
+ patch_off += 4;
+ bfd_put_32 (input_bfd,
+ B | ((offset + 4 - patch_off) & 0x3fffffc),
+ contents + patch_off);
+ patch_off += 4;
+ }
+ BFD_ASSERT (patch_off <= input_section->size);
+ relax_info->workaround_size = input_section->size - patch_off;
+ }
+ }
+
return ret;
}
\f
p = (unsigned char *) htab->glink->contents + ent->glink_offset;
- if (h == htab->tls_get_addr && !htab->no_tls_get_addr_opt)
+ if (h == htab->tls_get_addr && !htab->params->no_tls_get_addr_opt)
{
bfd_put_32 (output_bfd, LWZ_11_3, p);
p += 4;
#define elf_backend_can_gc_sections 1
#define elf_backend_can_refcount 1
#define elf_backend_rela_normal 1
+#define elf_backend_caches_rawsize 1
#define bfd_elf32_mkobject ppc_elf_mkobject
#define bfd_elf32_bfd_merge_private_bfd_data ppc_elf_merge_private_bfd_data
#define elf_backend_plt_sym_val ppc_elf_plt_sym_val
#define elf_backend_action_discarded ppc_elf_action_discarded
#define elf_backend_init_index_section _bfd_elf_init_1_index_section
-#define elf_backend_post_process_headers _bfd_elf_set_osabi
#define elf_backend_lookup_section_flags_hook ppc_elf_lookup_section_flags
#define elf_backend_section_processing ppc_elf_section_processing