X-Git-Url: http://drtracing.org/?a=blobdiff_plain;ds=sidebyside;f=bfd%2Felf64-ppc.c;h=494abed33080e217b04e0c1b3385746272413d30;hb=33c0ec9de7564cdf93754037b2a568dbd848f3c9;hp=a940dab31e345486ed9296496388270f042298f2;hpb=32ca96409d156a54b74a5866b5dea33f9079ef0c;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index a940dab31e..494abed330 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -1,6 +1,6 @@ /* PowerPC64-specific support for 64-bit ELF. Copyright 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, - 2009 Free Software Foundation, Inc. + 2009, 2010 Free Software Foundation, Inc. Written by Linus Nordberg, Swox AB , based on elf32-ppc.c by Ian Lance Taylor. Largely rewritten by Alan Modra. @@ -2526,6 +2526,54 @@ ppc64_elf_unhandled_reloc (bfd *abfd, arelent *reloc_entry, asymbol *symbol, return bfd_reloc_dangerous; } +/* Track GOT entries needed for a given symbol. We might need more + than one got entry per symbol. */ +struct got_entry +{ + struct got_entry *next; + + /* The symbol addend that we'll be placing in the GOT. */ + bfd_vma addend; + + /* Unlike other ELF targets, we use separate GOT entries for the same + symbol referenced from different input files. This is to support + automatic multiple TOC/GOT sections, where the TOC base can vary + from one input file to another. After partitioning into TOC groups + we merge entries within the group. + + Point to the BFD owning this GOT entry. */ + bfd *owner; + + /* Zero for non-tls entries, or TLS_TLS and one of TLS_GD, TLS_LD, + TLS_TPREL or TLS_DTPREL for tls entries. */ + char tls_type; + + /* Non-zero if got.ent points to real entry. */ + char is_indirect; + + /* Reference count until size_dynamic_sections, GOT offset thereafter. */ + union + { + bfd_signed_vma refcount; + bfd_vma offset; + struct got_entry *ent; + } got; +}; + +/* The same for PLT. */ +struct plt_entry +{ + struct plt_entry *next; + + bfd_vma addend; + + union + { + bfd_signed_vma refcount; + bfd_vma offset; + } plt; +}; + struct ppc64_elf_obj_tdata { struct elf_obj_tdata elf; @@ -2538,12 +2586,9 @@ struct ppc64_elf_obj_tdata on removed .opd entries to this section so that the sym is removed. */ asection *deleted_section; - /* TLS local dynamic got entry handling. Suppose for multiple GOT + /* TLS local dynamic got entry handling. Support for multiple GOT sections means we potentially need one of these for each input bfd. */ - union { - bfd_signed_vma refcount; - bfd_vma offset; - } tlsld_got; + struct got_entry tlsld_got; /* A copy of relocs before they are modified for --emit-relocs. */ Elf_Internal_Rela *opd_relocs; @@ -2557,7 +2602,7 @@ struct ppc64_elf_obj_tdata #define is_ppc64_elf(bfd) \ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \ - && elf_object_id (bfd) == PPC64_ELF_TDATA) + && elf_object_id (bfd) == PPC64_ELF_DATA) /* Override the generic function because we store some extras. */ @@ -2565,7 +2610,7 @@ static bfd_boolean ppc64_elf_mkobject (bfd *abfd) { return bfd_elf_allocate_object (abfd, sizeof (struct ppc64_elf_obj_tdata), - PPC64_ELF_TDATA); + PPC64_ELF_DATA); } /* Fix bad default arch selected for a 64 bit input bfd when the @@ -3453,50 +3498,6 @@ struct ppc_dyn_relocs bfd_size_type pc_count; }; -/* Track GOT entries needed for a given symbol. We might need more - than one got entry per symbol. */ -struct got_entry -{ - struct got_entry *next; - - /* The symbol addend that we'll be placing in the GOT. */ - bfd_vma addend; - - /* Unlike other ELF targets, we use separate GOT entries for the same - symbol referenced from different input files. This is to support - automatic multiple TOC/GOT sections, where the TOC base can vary - from one input file to another. FIXME: After group_sections we - ought to merge entries within the group. - - Point to the BFD owning this GOT entry. */ - bfd *owner; - - /* Zero for non-tls entries, or TLS_TLS and one of TLS_GD, TLS_LD, - TLS_TPREL or TLS_DTPREL for tls entries. */ - char tls_type; - - /* Reference count until size_dynamic_sections, GOT offset thereafter. */ - union - { - bfd_signed_vma refcount; - bfd_vma offset; - } got; -}; - -/* The same for PLT. */ -struct plt_entry -{ - struct plt_entry *next; - - bfd_vma addend; - - union - { - bfd_signed_vma refcount; - bfd_vma offset; - } plt; -}; - /* Of those relocs that might be copied as dynamic relocs, this function selects those that must be copied when linking a shared library, even when the symbol is local. */ @@ -3732,6 +3733,8 @@ struct ppc_link_hash_table /* Temp used when calculating TOC pointers. */ bfd_vma toc_curr; + bfd *toc_bfd; + asection *toc_first_sec; /* Highest input section id. */ int top_id; @@ -3762,6 +3765,9 @@ struct ppc_link_hash_table struct ppc_link_hash_entry *tls_get_addr; struct ppc_link_hash_entry *tls_get_addr_fd; + /* The size of reliplt used by got entry relocs. */ + bfd_size_type got_reli_size; + /* Statistics. */ unsigned long stub_count[ppc_stub_plt_call]; @@ -3775,8 +3781,9 @@ struct ppc_link_hash_table unsigned int no_tls_get_addr_opt:1; /* Support for multiple toc sections. */ - unsigned int no_multi_toc:1; + unsigned int do_multi_toc:1; unsigned int multi_toc_needed:1; + unsigned int second_toc_pass:1; /* Set on error. */ unsigned int stub_error:1; @@ -3793,14 +3800,32 @@ struct ppc_link_hash_table /* Rename some of the generic section flags to better document how they are used here. */ -#define has_toc_reloc has_gp_reloc -#define makes_toc_func_call need_finalize_relax -#define call_check_in_progress reloc_done + +/* Nonzero if this section has TLS related relocations. */ +#define has_tls_reloc sec_flg0 + +/* Nonzero if this section has a call to __tls_get_addr. */ +#define has_tls_get_addr_call sec_flg1 + +/* Nonzero if this section has any toc or got relocs. */ +#define has_toc_reloc sec_flg2 + +/* Nonzero if this section has small toc/got relocs, ie. that expect + the reloc to be in the range -32768 to 32767. */ +#define has_small_toc_reloc sec_flg3 + +/* Nonzero if this section has a call to another section that uses + the toc or got. */ +#define makes_toc_func_call sec_flg4 + +/* Recursion protection when determining above flag. */ +#define call_check_in_progress sec_flg5 /* Get the ppc64 ELF linker hash table from a link_info structure. */ #define ppc_hash_table(p) \ - ((struct ppc_link_hash_table *) ((p)->hash)) + (elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) \ + == PPC64_ELF_DATA ? ((struct ppc_link_hash_table *) ((p)->hash)) : NULL) #define ppc_stub_hash_lookup(table, string, create, copy) \ ((struct ppc_stub_hash_entry *) \ @@ -3945,7 +3970,8 @@ ppc64_elf_link_hash_table_create (bfd *abfd) return NULL; if (!_bfd_elf_link_hash_table_init (&htab->elf, abfd, link_hash_newfunc, - sizeof (struct ppc_link_hash_entry))) + sizeof (struct ppc_link_hash_entry), + PPC64_ELF_DATA)) { free (htab); return NULL; @@ -4002,6 +4028,8 @@ ppc64_elf_init_stub_bfd (bfd *abfd, struct bfd_link_info *info) linker created stub bfd. This ensures that the GOT header is at the start of the output TOC section. */ htab = ppc_hash_table (info); + if (htab == NULL) + return; htab->stub_bfd = abfd; htab->elf.dynobj = abfd; } @@ -4161,6 +4189,8 @@ create_linkage_sections (bfd *dynobj, struct bfd_link_info *info) flagword flags; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Create .sfpr for code to save and restore fp regs. */ flags = (SEC_ALLOC | SEC_LOAD | SEC_CODE | SEC_READONLY @@ -4229,6 +4259,8 @@ create_got_section (bfd *abfd, struct bfd_link_info *info) if (!is_ppc64_elf (abfd)) return FALSE; + if (htab == NULL) + return FALSE; if (!htab->got) { @@ -4270,6 +4302,9 @@ ppc64_elf_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) return FALSE; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + if (!htab->got) htab->got = bfd_get_section_by_name (dynobj, ".got"); htab->plt = bfd_get_section_by_name (dynobj, ".plt"); @@ -4602,6 +4637,9 @@ add_symbol_adjust (struct ppc_link_hash_entry *eh, struct bfd_link_info *info) abort (); htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + fdh = lookup_fdh (eh, htab); if (fdh == NULL) { @@ -4649,9 +4687,11 @@ ppc64_elf_process_dot_syms (bfd *ibfd, struct bfd_link_info *info) struct ppc_link_hash_table *htab; struct ppc_link_hash_entry **p, *eh; - htab = ppc_hash_table (info); if (!is_ppc64_elf (info->output_bfd)) return TRUE; + htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; if (is_ppc64_elf (ibfd)) { @@ -4690,7 +4730,12 @@ static bfd_boolean ppc64_elf_as_needed_cleanup (bfd *ibfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) { - ppc_hash_table (info)->dot_syms = NULL; + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + if (htab == NULL) + return FALSE; + + htab->dot_syms = NULL; return TRUE; } @@ -4734,6 +4779,7 @@ update_local_sym_info (bfd *abfd, Elf_Internal_Shdr *symtab_hdr, ent->addend = r_addend; ent->owner = abfd; ent->tls_type = tls_type; + ent->is_indirect = FALSE; ent->got.refcount = 0; local_got_ents[r_symndx] = ent; } @@ -4815,6 +4861,9 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, BFD_ASSERT (is_ppc64_elf (abfd)); htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + tga = elf_link_hash_lookup (&htab->elf, "__tls_get_addr", FALSE, FALSE, TRUE); dottga = elf_link_hash_lookup (&htab->elf, ".__tls_get_addr", @@ -4966,6 +5015,17 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, case R_PPC64_GOT16_LO_DS: /* This symbol requires a global offset table entry. */ sec->has_toc_reloc = 1; + if (r_type == R_PPC64_GOT_TLSLD16 + || r_type == R_PPC64_GOT_TLSGD16 + || r_type == R_PPC64_GOT_TPREL16_DS + || r_type == R_PPC64_GOT_DTPREL16_DS + || r_type == R_PPC64_GOT16 + || r_type == R_PPC64_GOT16_DS) + { + htab->do_multi_toc = 1; + sec->has_small_toc_reloc = 1; + } + if (ppc64_elf_tdata (abfd)->got == NULL && !create_got_section (abfd, info)) return FALSE; @@ -4991,6 +5051,7 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, ent->addend = rel->r_addend; ent->owner = abfd; ent->tls_type = tls_type; + ent->is_indirect = FALSE; ent->got.refcount = 0; eh->elf.got.glist = ent; } @@ -5061,10 +5122,12 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, break; case R_PPC64_TOC16: + case R_PPC64_TOC16_DS: + htab->do_multi_toc = 1; + sec->has_small_toc_reloc = 1; case R_PPC64_TOC16_LO: case R_PPC64_TOC16_HI: case R_PPC64_TOC16_HA: - case R_PPC64_TOC16_DS: case R_PPC64_TOC16_LO_DS: sec->has_toc_reloc = 1; break; @@ -5539,6 +5602,9 @@ ppc64_elf_gc_keep (struct bfd_link_info *info) struct ppc_link_hash_table *htab = ppc_hash_table (info); struct bfd_sym_chain *sym; + if (htab == NULL) + return; + for (sym = info->gc_sym_list; sym != NULL; sym = sym->next) { struct ppc_link_hash_entry *eh, *fh; @@ -5624,7 +5690,7 @@ ppc64_elf_gc_mark_dynamic_ref (struct elf_link_hash_entry *h, void *inf) static asection * ppc64_elf_gc_mark_hook (asection *sec, - struct bfd_link_info *info ATTRIBUTE_UNUSED, + struct bfd_link_info *info, Elf_Internal_Rela *rel, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) @@ -5683,7 +5749,7 @@ ppc64_elf_gc_mark_hook (asection *sec, break; default: - break; + return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } } } @@ -5726,6 +5792,9 @@ ppc64_elf_gc_sweep_hook (bfd *abfd, struct bfd_link_info *info, elf_section_data (sec)->local_dynrel = NULL; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + symtab_hdr = &elf_symtab_hdr (abfd); sym_hashes = elf_sym_hashes (abfd); local_got_ents = elf_local_got_ents (abfd); @@ -5889,7 +5958,7 @@ struct sfpr_def_parms /* Auto-generate _save*, _rest* functions in .sfpr. */ -static unsigned int +static bfd_boolean sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm) { struct ppc_link_hash_table *htab = ppc_hash_table (info); @@ -5898,6 +5967,9 @@ sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm) bfd_boolean writing = FALSE; char sym[16]; + if (htab == NULL) + return FALSE; + memcpy (sym, parm->name, len); sym[len + 2] = 0; @@ -6124,6 +6196,8 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf) info = inf; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Resolve undefined references to dot-symbols as the value in the function descriptor, if we have one in a regular object. @@ -6262,6 +6336,9 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED, }; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + if (htab->sfpr == NULL) /* We don't have any relocs. */ return TRUE; @@ -6294,6 +6371,8 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info, asection *s; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Deal with function syms. */ if (h->type == STT_FUNC @@ -6453,6 +6532,9 @@ ppc64_elf_hide_symbol (struct bfd_link_info *info, save = *p; *(char *) p = '.'; htab = ppc_hash_table (info); + if (htab == NULL) + return; + fh = (struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, p, FALSE, FALSE, FALSE); *(char *) p = save; @@ -6819,8 +6901,7 @@ dec_dynrel_count (bfd_vma r_info, applications. */ bfd_boolean -ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, - bfd_boolean non_overlapping) +ppc64_elf_edit_opd (struct bfd_link_info *info, bfd_boolean non_overlapping) { bfd *ibfd; bfd_boolean some_edited = FALSE; @@ -6990,7 +7071,7 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, new_contents = NULL; amt = sec->size * sizeof (long) / 8; opd = &ppc64_elf_section_data (sec)->u.opd; - opd->adjust = bfd_zalloc (obfd, amt); + opd->adjust = bfd_zalloc (sec->owner, amt); if (opd->adjust == NULL) return FALSE; ppc64_elf_section_data (sec)->sec_type = sec_opd; @@ -7066,8 +7147,12 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, if (h != NULL && h->root.root.string[0] == '.') { - fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, - ppc_hash_table (info)); + struct ppc_link_hash_table *htab; + + htab = ppc_hash_table (info); + if (htab != NULL) + fdh = lookup_fdh ((struct ppc_link_hash_entry *) h, + htab); if (fdh != NULL && fdh->elf.root.type != bfd_link_hash_defined && fdh->elf.root.type != bfd_link_hash_defweak) @@ -7217,13 +7302,21 @@ ppc64_elf_edit_opd (bfd *obfd, struct bfd_link_info *info, /* Set htab->tls_get_addr and call the generic ELF tls_setup function. */ asection * -ppc64_elf_tls_setup (bfd *obfd, - struct bfd_link_info *info, - int no_tls_get_addr_opt) +ppc64_elf_tls_setup (struct bfd_link_info *info, + int no_tls_get_addr_opt, + int *no_multi_toc) { struct ppc_link_hash_table *htab; htab = ppc_hash_table (info); + if (htab == NULL) + return NULL; + + if (*no_multi_toc) + htab->do_multi_toc = 0; + else if (!htab->do_multi_toc) + *no_multi_toc = 1; + htab->tls_get_addr = ((struct ppc_link_hash_entry *) elf_link_hash_lookup (&htab->elf, ".__tls_get_addr", FALSE, FALSE, TRUE)); @@ -7304,7 +7397,7 @@ ppc64_elf_tls_setup (bfd *obfd, no_tls_get_addr_opt = TRUE; } htab->no_tls_get_addr_opt = no_tls_get_addr_opt; - return _bfd_elf_tls_setup (obfd, info); + return _bfd_elf_tls_setup (info->output_bfd, info); } /* Return TRUE iff REL is a branch reloc with a global symbol matching @@ -7341,7 +7434,7 @@ branch_reloc_hash_match (const bfd *ibfd, dynamic relocations. */ bfd_boolean -ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) +ppc64_elf_tls_optimize (struct bfd_link_info *info) { bfd *ibfd; asection *sec; @@ -7352,6 +7445,9 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) return TRUE; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { Elf_Internal_Sym *locsyms = NULL; @@ -7409,10 +7505,13 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) if (h != NULL) { - if (h->root.type != bfd_link_hash_defined - && h->root.type != bfd_link_hash_defweak) + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + value = h->root.u.def.value; + else if (h->root.type == bfd_link_hash_undefweak) + value = 0; + else continue; - value = h->root.u.def.value; } else /* Symbols referenced by TLS relocs must be of type @@ -7425,11 +7524,17 @@ ppc64_elf_tls_optimize (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) || !h->def_dynamic) { is_local = TRUE; - value += sym_sec->output_offset; - value += sym_sec->output_section->vma; - value -= htab->elf.tls_sec->vma; - ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31) - < (bfd_vma) 1 << 32); + if (h != NULL + && h->root.type == bfd_link_hash_undefweak) + ok_tprel = TRUE; + else + { + value += sym_sec->output_offset; + value += sym_sec->output_section->vma; + value -= htab->elf.tls_sec->vma; + ok_tprel = (value + TP_OFFSET + ((bfd_vma) 1 << 31) + < (bfd_vma) 1 << 32); + } } r_type = ELF64_R_TYPE (rel->r_info); @@ -7769,7 +7874,7 @@ adjust_toc_syms (struct elf_link_hash_entry *h, void *inf) unused .toc entries. */ bfd_boolean -ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) +ppc64_elf_edit_toc (struct bfd_link_info *info) { bfd *ibfd; struct adjust_toc_info toc_inf; @@ -8165,6 +8270,42 @@ ppc64_elf_edit_toc (bfd *obfd ATTRIBUTE_UNUSED, struct bfd_link_info *info) return TRUE; } +/* Allocate space for one GOT entry. */ + +static void +allocate_got (struct elf_link_hash_entry *h, + struct bfd_link_info *info, + struct got_entry *gent) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + bfd_boolean dyn; + struct ppc_link_hash_entry *eh = (struct ppc_link_hash_entry *) h; + int entsize = (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD) + ? 16 : 8); + int rentsize = (gent->tls_type & eh->tls_mask & TLS_GD + ? 2 : 1) * sizeof (Elf64_External_Rela); + asection *got = ppc64_elf_tdata (gent->owner)->got; + + gent->got.offset = got->size; + got->size += entsize; + + dyn = htab->elf.dynamic_sections_created; + if ((info->shared + || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)) + && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT + || h->root.type != bfd_link_hash_undefweak)) + { + asection *relgot = ppc64_elf_tdata (gent->owner)->relgot; + relgot->size += rentsize; + } + else if (h->type == STT_GNU_IFUNC) + { + asection *relgot = htab->reliplt; + relgot->size += rentsize; + htab->got_reli_size += rentsize; + } +} + /* Allocate space in .plt, .got and associated reloc sections for dynamic relocs. */ @@ -8186,6 +8327,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) info = (struct bfd_link_info *) inf; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; if ((htab->elf.dynamic_sections_created && h->dynindx != -1 @@ -8276,9 +8419,6 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) for (gent = h->got.glist; gent != NULL; gent = gent->next) if (gent->got.refcount > 0) { - bfd_boolean dyn; - asection *rsec; - /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic, nor will all TLS symbols. */ @@ -8294,31 +8434,15 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) if ((gent->tls_type & TLS_LD) != 0 && !h->def_dynamic) { - ppc64_tlsld_got (gent->owner)->refcount += 1; + ppc64_tlsld_got (gent->owner)->got.refcount += 1; gent->got.offset = (bfd_vma) -1; continue; } if (!is_ppc64_elf (gent->owner)) - continue; + abort (); - s = ppc64_elf_tdata (gent->owner)->got; - gent->got.offset = s->size; - s->size - += (gent->tls_type & eh->tls_mask & (TLS_GD | TLS_LD)) ? 16 : 8; - dyn = htab->elf.dynamic_sections_created; - rsec = NULL; - if ((info->shared - || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)) - && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT - || h->root.type != bfd_link_hash_undefweak)) - rsec = ppc64_elf_tdata (gent->owner)->relgot; - else if (h->type == STT_GNU_IFUNC) - rsec = htab->reliplt; - if (rsec != NULL) - rsec->size += (gent->tls_type & eh->tls_mask & TLS_GD - ? 2 * sizeof (Elf64_External_Rela) - : sizeof (Elf64_External_Rela)); + allocate_got (h, info, gent); } else gent->got.offset = (bfd_vma) -1; @@ -8463,6 +8587,9 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, bfd *ibfd; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + dynobj = htab->elf.dynobj; if (dynobj == NULL) abort (); @@ -8543,7 +8670,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, { if ((ent->tls_type & *lgot_masks & TLS_LD) != 0) { - ppc64_tlsld_got (ibfd)->refcount += 1; + ppc64_tlsld_got (ibfd)->got.refcount += 1; ent->got.offset = (bfd_vma) -1; } else @@ -8556,7 +8683,12 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, if (info->shared) srel->size += num * sizeof (Elf64_External_Rela); else if ((*lgot_masks & PLT_IFUNC) != 0) - htab->reliplt->size += num * sizeof (Elf64_External_Rela); + { + htab->reliplt->size + += num * sizeof (Elf64_External_Rela); + htab->got_reli_size + += num * sizeof (Elf64_External_Rela); + } } } else @@ -8591,10 +8723,11 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, if (!is_ppc64_elf (ibfd)) continue; - if (ppc64_tlsld_got (ibfd)->refcount > 0) + if (ppc64_tlsld_got (ibfd)->got.refcount > 0) { s = ppc64_elf_tdata (ibfd)->got; - ppc64_tlsld_got (ibfd)->offset = s->size; + ppc64_tlsld_got (ibfd)->got.offset = s->size; + ppc64_tlsld_got (ibfd)->owner = ibfd; s->size += 16; if (info->shared) { @@ -8603,7 +8736,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, } } else - ppc64_tlsld_got (ibfd)->offset = (bfd_vma) -1; + ppc64_tlsld_got (ibfd)->got.offset = (bfd_vma) -1; } /* We now have determined the sizes of the various dynamic sections. @@ -9027,6 +9160,8 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) info = in_arg; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Make a note of the offset within the stubs for this entry. */ stub_entry->stub_offset = stub_entry->stub_sec->size; @@ -9423,6 +9558,8 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) info = in_arg; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; if (stub_entry->stub_type == ppc_stub_plt_call) { @@ -9562,9 +9699,10 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) 0 when no stubs will be needed, and 1 on success. */ int -ppc64_elf_setup_section_lists (bfd *output_bfd, - struct bfd_link_info *info, - int no_multi_toc) +ppc64_elf_setup_section_lists + (struct bfd_link_info *info, + asection *(*add_stub_section) (const char *, asection *), + void (*layout_sections_again) (void)) { bfd *input_bfd; int top_id, top_index, id; @@ -9573,7 +9711,11 @@ ppc64_elf_setup_section_lists (bfd *output_bfd, bfd_size_type amt; struct ppc_link_hash_table *htab = ppc_hash_table (info); - htab->no_multi_toc = no_multi_toc; + if (htab == NULL) + return -1; + /* Stash our params away. */ + htab->add_stub_section = add_stub_section; + htab->layout_sections_again = layout_sections_again; if (htab->brlt == NULL) return 0; @@ -9602,12 +9744,10 @@ ppc64_elf_setup_section_lists (bfd *output_bfd, for (id = 0; id < 3; id++) htab->stub_group[id].toc_off = TOC_BASE_OFF; - elf_gp (output_bfd) = htab->toc_curr = ppc64_elf_toc (output_bfd); - /* We can't use output_bfd->section_count here to find the top output section index as some sections may have been removed, and strip_excluded_output_sections doesn't renumber the indices. */ - for (section = output_bfd->sections, top_index = 0; + for (section = info->output_bfd->sections, top_index = 0; section != NULL; section = section->next) { @@ -9625,41 +9765,367 @@ ppc64_elf_setup_section_lists (bfd *output_bfd, return 1; } +/* Set up for first pass at multitoc partitioning. */ + +void +ppc64_elf_start_multitoc_partition (struct bfd_link_info *info) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + + elf_gp (info->output_bfd) = ppc64_elf_toc (info->output_bfd); + htab->toc_curr = elf_gp (info->output_bfd); + htab->toc_bfd = NULL; + htab->toc_first_sec = NULL; +} + /* The linker repeatedly calls this function for each TOC input section and linker generated GOT section. Group input bfds such that the toc - within a group is less than 64k in size. Will break with cute linker - scripts that play games with dot in the output toc section. */ + within a group is less than 64k in size. */ -void +bfd_boolean ppc64_elf_next_toc_section (struct bfd_link_info *info, asection *isec) { struct ppc_link_hash_table *htab = ppc_hash_table (info); + bfd_vma addr, off; - if (!htab->no_multi_toc) + if (htab == NULL) + return FALSE; + + if (!htab->second_toc_pass) { - bfd_vma addr = isec->output_offset + isec->output_section->vma; - bfd_vma off = addr - htab->toc_curr; + /* Keep track of the first .toc or .got section for this input bfd. */ + if (htab->toc_bfd != isec->owner) + { + htab->toc_bfd = isec->owner; + htab->toc_first_sec = isec; + } + addr = isec->output_offset + isec->output_section->vma; + off = addr - htab->toc_curr; if (off + isec->size > 0x10000) - htab->toc_curr = addr; + { + addr = (htab->toc_first_sec->output_offset + + htab->toc_first_sec->output_section->vma); + htab->toc_curr = addr; + } + + /* toc_curr is the base address of this toc group. Set elf_gp + for the input section to be the offset relative to the + output toc base plus 0x8000. Making the input elf_gp an + offset allows us to move the toc as a whole without + recalculating input elf_gp. */ + off = htab->toc_curr - elf_gp (isec->output_section->owner); + off += TOC_BASE_OFF; + + /* Die if someone uses a linker script that doesn't keep input + file .toc and .got together. */ + if (elf_gp (isec->owner) != 0 + && elf_gp (isec->owner) != off) + return FALSE; + + elf_gp (isec->owner) = off; + return TRUE; + } - elf_gp (isec->owner) = (htab->toc_curr - - elf_gp (isec->output_section->owner) - + TOC_BASE_OFF); + /* During the second pass toc_first_sec points to the start of + a toc group, and toc_curr is used to track the old elf_gp. + We use toc_bfd to ensure we only look at each bfd once. */ + if (htab->toc_bfd == isec->owner) + return TRUE; + htab->toc_bfd = isec->owner; + + if (htab->toc_first_sec == NULL + || htab->toc_curr != elf_gp (isec->owner)) + { + htab->toc_curr = elf_gp (isec->owner); + htab->toc_first_sec = isec; + } + addr = (htab->toc_first_sec->output_offset + + htab->toc_first_sec->output_section->vma); + off = addr - elf_gp (isec->output_section->owner) + TOC_BASE_OFF; + elf_gp (isec->owner) = off; + + return TRUE; +} + +/* This function removes unneeded got entries (those with got.offset == -1) + and merges entries in the same toc group. */ + +static void +merge_got_entries (struct got_entry **pent) +{ + struct got_entry *ent, *ent2; + + while ((ent = *pent) != NULL) + { + if (!ent->is_indirect) + { + if (ent->got.offset == (bfd_vma) -1) + { + *pent = ent->next; + continue; + } + for (ent2 = ent->next; ent2 != NULL; ent2 = ent2->next) + if (!ent2->is_indirect + && ent2->got.offset != (bfd_vma) -1 + && ent2->addend == ent->addend + && ent2->tls_type == ent->tls_type + && elf_gp (ent2->owner) == elf_gp (ent->owner)) + { + ent2->is_indirect = TRUE; + ent2->got.ent = ent; + } + } + pent = &ent->next; } } -/* Called after the last call to the above function. */ +/* Called via elf_link_hash_traverse to merge GOT entries for global + symbol H. */ + +static bfd_boolean +merge_global_got (struct elf_link_hash_entry *h, void *inf ATTRIBUTE_UNUSED) +{ + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + merge_got_entries (&h->got.glist); + + return TRUE; +} + +/* Called via elf_link_hash_traverse to allocate GOT entries for global + symbol H. */ + +static bfd_boolean +reallocate_got (struct elf_link_hash_entry *h, void *inf) +{ + struct got_entry *gent; + + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + for (gent = h->got.glist; gent != NULL; gent = gent->next) + if (!gent->is_indirect) + allocate_got (h, (struct bfd_link_info *) inf, gent); + return TRUE; +} + +/* Called on the first multitoc pass after the last call to + ppc64_elf_next_toc_section. This function removes duplicate GOT + entries. */ + +bfd_boolean +ppc64_elf_layout_multitoc (struct bfd_link_info *info) +{ + struct ppc_link_hash_table *htab = ppc_hash_table (info); + struct bfd *ibfd, *ibfd2; + bfd_boolean done_something; + + htab->multi_toc_needed = htab->toc_curr != elf_gp (info->output_bfd); + + /* Merge local got entries within a toc group. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + struct got_entry **lgot_ents; + struct got_entry **end_lgot_ents; + Elf_Internal_Shdr *symtab_hdr; + bfd_size_type locsymcount; + + if (!is_ppc64_elf (ibfd)) + continue; + + lgot_ents = elf_local_got_ents (ibfd); + if (!lgot_ents) + continue; + + symtab_hdr = &elf_symtab_hdr (ibfd); + locsymcount = symtab_hdr->sh_info; + end_lgot_ents = lgot_ents + locsymcount; + + for (; lgot_ents < end_lgot_ents; ++lgot_ents) + merge_got_entries (lgot_ents); + } + + /* And the same for global sym got entries. */ + elf_link_hash_traverse (&htab->elf, merge_global_got, info); + + /* And tlsld_got. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + struct got_entry *ent, *ent2; + + if (!is_ppc64_elf (ibfd)) + continue; + + ent = ppc64_tlsld_got (ibfd); + if (!ent->is_indirect + && ent->got.offset != (bfd_vma) -1) + { + for (ibfd2 = ibfd->link_next; ibfd2 != NULL; ibfd2 = ibfd2->link_next) + { + if (!is_ppc64_elf (ibfd2)) + continue; + + ent2 = ppc64_tlsld_got (ibfd2); + if (!ent2->is_indirect + && ent2->got.offset != (bfd_vma) -1 + && elf_gp (ibfd2) == elf_gp (ibfd)) + { + ent2->is_indirect = TRUE; + ent2->got.ent = ent; + } + } + } + } + + /* Zap sizes of got sections. */ + htab->reliplt->rawsize = htab->reliplt->size; + htab->reliplt->size -= htab->got_reli_size; + htab->got_reli_size = 0; + + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + asection *got, *relgot; + + if (!is_ppc64_elf (ibfd)) + continue; + + got = ppc64_elf_tdata (ibfd)->got; + if (got != NULL) + { + got->rawsize = got->size; + got->size = 0; + relgot = ppc64_elf_tdata (ibfd)->relgot; + relgot->rawsize = relgot->size; + relgot->size = 0; + } + } + + /* Now reallocate the got, local syms first. We don't need to + allocate section contents again since we never increase size. */ + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + struct got_entry **lgot_ents; + struct got_entry **end_lgot_ents; + struct plt_entry **local_plt; + struct plt_entry **end_local_plt; + char *lgot_masks; + bfd_size_type locsymcount; + Elf_Internal_Shdr *symtab_hdr; + asection *s, *srel; + + if (!is_ppc64_elf (ibfd)) + continue; + + lgot_ents = elf_local_got_ents (ibfd); + if (!lgot_ents) + continue; + + symtab_hdr = &elf_symtab_hdr (ibfd); + locsymcount = symtab_hdr->sh_info; + end_lgot_ents = lgot_ents + locsymcount; + local_plt = (struct plt_entry **) end_lgot_ents; + end_local_plt = local_plt + locsymcount; + lgot_masks = (char *) end_local_plt; + s = ppc64_elf_tdata (ibfd)->got; + srel = ppc64_elf_tdata (ibfd)->relgot; + for (; lgot_ents < end_lgot_ents; ++lgot_ents, ++lgot_masks) + { + struct got_entry *ent; + + for (ent = *lgot_ents; ent != NULL; ent = ent->next) + if (!ent->is_indirect) + { + unsigned int num = 1; + ent->got.offset = s->size; + if ((ent->tls_type & *lgot_masks & TLS_GD) != 0) + num = 2; + s->size += num * 8; + if (info->shared) + srel->size += num * sizeof (Elf64_External_Rela); + else if ((*lgot_masks & PLT_IFUNC) != 0) + { + htab->reliplt->size + += num * sizeof (Elf64_External_Rela); + htab->got_reli_size + += num * sizeof (Elf64_External_Rela); + } + } + } + } + + elf_link_hash_traverse (&htab->elf, reallocate_got, info); + + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + struct got_entry *ent; + + if (!is_ppc64_elf (ibfd)) + continue; + + ent = ppc64_tlsld_got (ibfd); + if (!ent->is_indirect + && ent->got.offset != (bfd_vma) -1) + { + asection *s = ppc64_elf_tdata (ibfd)->got; + ent->got.offset = s->size; + s->size += 16; + if (info->shared) + { + asection *srel = ppc64_elf_tdata (ibfd)->relgot; + srel->size += sizeof (Elf64_External_Rela); + } + } + } + + done_something = htab->reliplt->rawsize != htab->reliplt->size; + if (!done_something) + for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) + { + asection *got; + + if (!is_ppc64_elf (ibfd)) + continue; + + got = ppc64_elf_tdata (ibfd)->got; + if (got != NULL) + { + done_something = got->rawsize != got->size; + if (done_something) + break; + } + } + + if (done_something) + (*htab->layout_sections_again) (); + + /* Set up for second pass over toc sections to recalculate elf_gp + on input sections. */ + htab->toc_bfd = NULL; + htab->toc_first_sec = NULL; + htab->second_toc_pass = TRUE; + return done_something; +} + +/* Called after second pass of multitoc partitioning. */ void -ppc64_elf_reinit_toc (bfd *output_bfd, struct bfd_link_info *info) +ppc64_elf_finish_multitoc_partition (struct bfd_link_info *info) { struct ppc_link_hash_table *htab = ppc_hash_table (info); - htab->multi_toc_needed = htab->toc_curr != elf_gp (output_bfd); + if (htab == NULL) + return; - /* toc_curr tracks the TOC offset used for code sections below in - ppc64_elf_next_input_section. Start off at 0x8000. */ + /* After the second pass, toc_curr tracks the TOC offset used + for code sections below in ppc64_elf_next_input_section. */ htab->toc_curr = TOC_BASE_OFF; } @@ -9701,6 +10167,9 @@ toc_adjusting_stub_needed (struct bfd_link_info *info, asection *isec) local_syms = NULL; ret = 0; htab = ppc_hash_table (info); + if (htab == NULL) + return -1; + for (rel = relstart; rel < relstart + isec->reloc_count; ++rel) { enum elf_ppc64_reloc_type r_type; @@ -9873,6 +10342,9 @@ ppc64_elf_next_input_section (struct bfd_link_info *info, asection *isec) { struct ppc_link_hash_table *htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + if ((isec->output_section->flags & SEC_CODE) != 0 && isec->output_section->index <= htab->top_index) { @@ -10032,19 +10504,15 @@ group_sections (struct ppc_link_hash_table *htab, instruction. */ bfd_boolean -ppc64_elf_size_stubs (bfd *output_bfd, - struct bfd_link_info *info, - bfd_signed_vma group_size, - asection *(*add_stub_section) (const char *, asection *), - void (*layout_sections_again) (void)) +ppc64_elf_size_stubs (struct bfd_link_info *info, bfd_signed_vma group_size) { bfd_size_type stub_group_size; bfd_boolean stubs_always_before_branch; struct ppc_link_hash_table *htab = ppc_hash_table (info); - /* Stash our params away. */ - htab->add_stub_section = add_stub_section; - htab->layout_sections_again = layout_sections_again; + if (htab == NULL) + return FALSE; + stubs_always_before_branch = group_size < 0; if (group_size < 0) stub_group_size = -group_size; @@ -10096,7 +10564,7 @@ ppc64_elf_size_stubs (bfd *output_bfd, /* If this section is a link-once section that will be discarded, then don't create any stubs. */ if (section->output_section == NULL - || section->output_section->owner != output_bfd) + || section->output_section->owner != info->output_bfd) continue; /* Get the relocs. */ @@ -10466,6 +10934,9 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms, bfd_byte *p; int stub_sec_count = 0; + if (htab == NULL) + return FALSE; + htab->emit_stub_syms = emit_stub_syms; /* Allocate memory to hold the linker stubs. */ @@ -10665,7 +11136,9 @@ void ppc64_elf_restore_symbols (struct bfd_link_info *info) { struct ppc_link_hash_table *htab = ppc_hash_table (info); - elf_link_hash_traverse (&htab->elf, undo_symbol_twiddle, info); + + if (htab != NULL) + elf_link_hash_traverse (&htab->elf, undo_symbol_twiddle, info); } /* What to do when ld finds relocations against symbols defined in @@ -10745,6 +11218,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, ppc_howto_init (); htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; /* Don't relocate stub sections. */ if (input_section->owner == htab->stub_bfd) @@ -11498,6 +11973,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, checking whether the function is defined. */ else if (h != NULL && h->elf.root.type == bfd_link_hash_undefweak + && h->elf.dynindx == -1 && r_type == R_PPC64_REL24 && relocation == 0 && addend == 0) @@ -11575,14 +12051,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_vma *offp; bfd_vma off; unsigned long indx = 0; + struct got_entry *ent; if (tls_type == (TLS_TLS | TLS_LD) && (h == NULL || !h->elf.def_dynamic)) - offp = &ppc64_tlsld_got (input_bfd)->offset; + ent = ppc64_tlsld_got (input_bfd); else { - struct got_entry *ent; if (h != NULL) { @@ -11615,12 +12091,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, && ent->owner == input_bfd && ent->tls_type == tls_type) break; - if (ent == NULL) - abort (); - offp = &ent->got.offset; } - got = ppc64_elf_tdata (input_bfd)->got; + if (ent == NULL) + abort (); + if (ent->is_indirect) + ent = ent->got.ent; + offp = &ent->got.offset; + got = ppc64_elf_tdata (ent->owner)->got; if (got == NULL) abort (); @@ -11644,11 +12122,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, ? h->elf.type == STT_GNU_IFUNC : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC); if ((info->shared || indx != 0) - && (offp == &ppc64_tlsld_got (input_bfd)->offset - || h == NULL + && (h == NULL + || (tls_type == (TLS_TLS | TLS_LD) + && !h->elf.def_dynamic) || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT || h->elf.root.type != bfd_link_hash_undefweak)) - relgot = ppc64_elf_tdata (input_bfd)->relgot; + relgot = ppc64_elf_tdata (ent->owner)->relgot; else if (ifunc) relgot = htab->reliplt; if (relgot != NULL) @@ -11735,10 +12214,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, if (off >= (bfd_vma) -2) abort (); - relocation = got->output_offset + off; - - /* TOC base (r2) is TOC start plus 0x8000. */ - addend = -TOC_BASE_OFF; + relocation = got->output_section->vma + got->output_offset + off; + addend = -(TOCstart + htab->stub_group[input_section->id].toc_off); } break; @@ -11833,6 +12310,22 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_TPREL16_HIGHERA: case R_PPC64_TPREL16_HIGHEST: case R_PPC64_TPREL16_HIGHESTA: + if (h != NULL + && h->elf.root.type == bfd_link_hash_undefweak + && h->elf.dynindx == -1) + { + /* Make this relocation against an undefined weak symbol + resolve to zero. This is really just a tweak, since + code using weak externs ought to check that they are + defined before using them. */ + bfd_byte *p = contents + rel->r_offset - d_offset; + + insn = bfd_get_32 (output_bfd, p); + insn = _bfd_elf_ppc_at_tprel_transform (insn, 13); + if (insn != 0) + bfd_put_32 (output_bfd, insn, p); + break; + } addend -= htab->elf.tls_sec->vma + TP_OFFSET; if (info->shared) /* The TPREL16 relocs shouldn't really be used in shared @@ -12336,6 +12829,8 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd, bfd_byte *loc; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; for (ent = h->plt.plist; ent != NULL; ent = ent->next) if (ent->plt.offset != (bfd_vma) -1) @@ -12435,6 +12930,9 @@ ppc64_elf_finish_dynamic_sections (bfd *output_bfd, asection *sdyn; htab = ppc_hash_table (info); + if (htab == NULL) + return FALSE; + dynobj = htab->elf.dynobj; sdyn = bfd_get_section_by_name (dynobj, ".dynamic");