diff options
Diffstat (limited to 'bfd')
-rw-r--r-- | bfd/ChangeLog | 26 | ||||
-rw-r--r-- | bfd/bfd-in2.h | 1 | ||||
-rw-r--r-- | bfd/elf32-ppc.c | 884 | ||||
-rw-r--r-- | bfd/elf32-ppc.h | 26 | ||||
-rw-r--r-- | bfd/section.c | 1 |
5 files changed, 611 insertions, 327 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 0d09b68..2793df0 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,29 @@ +2014-02-03 Alan Modra <amodra@gmail.com> + + * elf32-ppc.c (struct ppc_elf_link_hash_table): Add params. + Delete emit_stub_syms, no_tls_get_addr_opt. Update all uses. + (ppc_elf_link_params): New function. + (ppc_elf_create_glink): Align .glink to 64 bytes for ppc476 + workaround. + (ppc_elf_select_plt_layout): Remove plt_style and emit_stub_syms + parameters. Use htab->params instead. + (ppc_elf_tls_setup): Remove no_tls_get_addr_opt parameter. + (ppc_elf_size_dynamic_sections): Align __glink_PLTresolve to + 64 bytes for ppc476 workaround. + (struct ppc_elf_relax_info): New. + (ppc_elf_relax_section): Exclude linker created sections and + those too small to hold one instruction. Don't add another + branch around trampolines on later relax passes. Don't + generate trampolines for undefined symbols when !relocatable, + nor for plugin symbols. Allocate space for ppc476 workaround + patch area. Free fixups on error return path. + (ppc_elf_relocate_section): Handle ppc476 workaround patching. + * elf32-ppc.h (struct ppc_elf_params): New. + (ppc_elf_select_plt_layout, ppc_elf_tls_setup): Update prototype. + (ppc_elf_link_params): Declare. + * section.c (SEC_INFO_TYPE_TARGET): Define. + * bfd-in2.h: Regenerate. + 2014-02-02 Sandra Loosemore <sandra@codesourcery.com> * elf32-nios2.c (struct elf32_nios2_link_hash_table): Add diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 73e2332..036f65a 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -1437,6 +1437,7 @@ typedef struct bfd_section #define SEC_INFO_TYPE_MERGE 2 #define SEC_INFO_TYPE_EH_FRAME 3 #define SEC_INFO_TYPE_JUST_SYMS 4 +#define SEC_INFO_TYPE_TARGET 5 /* Nonzero if this section uses RELA relocations, rather than REL. */ unsigned int use_rela_p:1; diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index 61c45d8..d13d31b 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -3139,6 +3139,9 @@ struct ppc_elf_link_hash_table { 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; @@ -3184,12 +3187,6 @@ struct ppc_elf_link_hash_table /* 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; @@ -3289,6 +3286,17 @@ ppc_elf_link_hash_table_create (bfd *abfd) 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 @@ -3341,7 +3349,8 @@ ppc_elf_create_glink (bfd *abfd, struct bfd_link_info *info) 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) @@ -4801,22 +4810,18 @@ ppc_elf_vle_split16 (bfd *output_bfd, bfd_byte *contents, 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 @@ -4838,7 +4843,7 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED, 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 @@ -4861,7 +4866,7 @@ ppc_elf_select_plt_layout (bfd *output_bfd ATTRIBUTE_UNUSED, 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"), @@ -5097,16 +5102,14 @@ ppc_elf_gc_sweep_hook (bfd *abfd, 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", @@ -5151,9 +5154,8 @@ ppc_elf_tls_setup (bfd *obfd, } } 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) @@ -5784,7 +5786,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) 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 @@ -5797,7 +5799,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) } ent->glink_offset = glink_offset; - if (htab->emit_stub_syms + if (htab->params->emit_stub_syms && !add_stub_sym (ent, h, info)) return FALSE; } @@ -6339,10 +6341,11 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, /* 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", @@ -6501,7 +6504,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, { 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_OPT, PPC_OPT_TLS)) @@ -6634,6 +6637,19 @@ static const int stub_entry[] = 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. FIXME: We write out + trampoline templates here and later modify them in + relocate_section. We'd save a realloc if we left writing the + templates to relocate_section. */ + static bfd_boolean ppc_elf_relax_section (bfd *abfd, asection *isec, @@ -6654,22 +6670,23 @@ ppc_elf_relax_section (bfd *abfd, 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 @@ -6678,360 +6695,431 @@ ppc_elf_relax_section (bfd *abfd, 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; + + if (htab->params->ppc476_workaround) + { + if (elf_section_data (isec)->sec_info == NULL) + { + BFD_ASSERT (isec->sec_info_type == SEC_INFO_TYPE_NONE); + isec->sec_info_type = SEC_INFO_TYPE_TARGET; + 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; - /* Read this BFD's local symbols. */ - if (isymbuf == NULL) + default: + continue; + } + + /* 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); + 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) + { + /* 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; + if (contents == NULL) + { + if (elf_section_data (isec)->this_hdr.contents != NULL) + contents = elf_section_data (isec)->this_hdr.contents; + else if (!bfd_malloc_and_get_section (abfd, isec, &contents)) + goto error_return; + } + } + /* Ensure relocate_section is called. */ + isec->flags |= SEC_RELOC; + } + newsize = trampoff + relax_info->workaround_size; + } + + if (changes || workaround_change) + { + contents = bfd_realloc_or_free (contents, newsize); 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; + bfd_vma val = B + newsize - isec->rawsize; + bfd_put_32 (abfd, val, contents + isec->rawsize); } - isec->size = trampoff; + } + + /* Write out the trampolines. */ + if (changes) + { + const int *stub; + bfd_byte *dest; + int i, size; + + dest = contents + trampbase; + if (maybe_pasted && trampbase == isec->rawsize) + dest += 4; if (link_info->shared) { @@ -7056,6 +7144,9 @@ ppc_elf_relax_section (bfd *abfd, BFD_ASSERT (i == 0); } + if (changes || workaround_change) + isec->size = newsize; + if (isymbuf != NULL && symtab_hdr->contents != (unsigned char *) isymbuf) { @@ -7071,7 +7162,7 @@ ppc_elf_relax_section (bfd *abfd, if (contents != NULL && elf_section_data (isec)->this_hdr.contents != contents) { - if (!changes && !link_info->keep_memory) + if (!changes && !workaround_change && !link_info->keep_memory) free (contents); else { @@ -7106,11 +7197,12 @@ ppc_elf_relax_section (bfd *abfd, 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) + *again = changes != 0 || workaround_change; + if (!*again && link_info->relocatable && htab->params->branch_trampolines) { /* Convert the internal relax relocs to external form. */ for (irel = internal_relocs; irel < irelend; irel++) @@ -7134,6 +7226,12 @@ ppc_elf_relax_section (bfd *abfd, 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 @@ -9076,6 +9174,144 @@ ppc_elf_relocate_section (bfd *output_bfd, fprintf (stderr, "\n"); #endif + 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; } @@ -9350,7 +9586,7 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, 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; diff --git a/bfd/elf32-ppc.h b/bfd/elf32-ppc.h index 0bf973c..0909245 100644 --- a/bfd/elf32-ppc.h +++ b/bfd/elf32-ppc.h @@ -26,9 +26,29 @@ enum ppc_elf_plt_type PLT_VXWORKS }; -int ppc_elf_select_plt_layout (bfd *, struct bfd_link_info *, - enum ppc_elf_plt_type, int); -asection *ppc_elf_tls_setup (bfd *, struct bfd_link_info *, int); +/* Various options passed from the linker to bfd backend. */ +struct ppc_elf_params +{ + /* Chooses the type of .plt. */ + enum ppc_elf_plt_type plt_style; + + /* Whether to emit symbols for stubs. */ + int emit_stub_syms; + + /* Whether to emit special stub for __tls_get_addr calls. */ + int no_tls_get_addr_opt; + + /* Insert trampolines for branches that won't reach their destination. */ + int branch_trampolines; + + /* Avoid execution falling into new page. */ + int ppc476_workaround; + int pagesize; +}; + +void ppc_elf_link_params (struct bfd_link_info *, struct ppc_elf_params *); +int ppc_elf_select_plt_layout (bfd *, struct bfd_link_info *); +asection *ppc_elf_tls_setup (bfd *, struct bfd_link_info *); bfd_boolean ppc_elf_tls_optimize (bfd *, struct bfd_link_info *); void ppc_elf_set_sdata_syms (bfd *, struct bfd_link_info *); extern bfd_boolean ppc_elf_modify_segment_map (bfd *, diff --git a/bfd/section.c b/bfd/section.c index a661228..d21df1c 100644 --- a/bfd/section.c +++ b/bfd/section.c @@ -388,6 +388,7 @@ CODE_FRAGMENT .#define SEC_INFO_TYPE_MERGE 2 .#define SEC_INFO_TYPE_EH_FRAME 3 .#define SEC_INFO_TYPE_JUST_SYMS 4 +.#define SEC_INFO_TYPE_TARGET 5 . . {* Nonzero if this section uses RELA relocations, rather than REL. *} . unsigned int use_rela_p:1; |