From 25f231068e4cddb11c8129980dc40cba1d42e4c5 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Wed, 29 Jul 2009 14:56:38 +0000 Subject: [PATCH] * elf32-ppc.c (ppc_elf_check_relocs): Always set up sections used by indirect function support. Count dynamic relocs for ifunc syms. (ppc_elf_adjust_dynamic_symbol): Tweak for ifunc. (allocate_dynrelocs): Allocate all non-dynamic ifunc plt entries in iplt and their relocs in reliplt. Don't make ifunc syms dynamic. Allocate got entry relocs for non-dynamic ifunc in reliplt. Handle other dynamic relocs for ifunc. (ppc_elf_size_dynamic_sections): Alloc dyn relocs for static ifunc in reliplt, likewise relocs for got against local ifunc. Typo fix on reliplt size adjust. (ppc_elf_relocate_section): Don't use plt scheme of allocating relocs 1-1 with entries for iplt, instead just add using reloc_count. Write got relocs and dyn relocs for ifunc to reliplt. Error on invalid ifunc dyn relocs. (ppc_elf_finish_dynamic_symbol): Adjust for non-dynamic ifunc plt in iplt/reliplt. * elf64-ppc.c (ppc64_elf_howto_raw): Add R_PPC64_JMP_IREL, R_PPC64_REL16, R_PPC64_REL16_LO, R_PPC64_REL16_HI, R_PPC64_REL16_HA. (ppc64_elf_reloc_type_lookup): Handle new relocs. (ppc64_elf_check_relocs): Likewise. Count dyn relocs for ifunc. (allocate_dynrelocs): As for elf32-ppc.c above. (ppc64_elf_size_dynamic_sections): Likewise. (ppc_build_one_stub): Put non-dynamic ifunc plt call stubs in iplt, and their relocs in reliplt. Use R_PPC64_JMP_IREL. (ppc_size_one_stub): Similarly. (ppc64_elf_relocate_section): As for elf32-ppc.c above. Handle new relocs too. (ppc64_elf_finish_dynamic_symbol): As for elf32-ppc.c above. --- bfd/ChangeLog | 32 +++++ bfd/elf32-ppc.c | 208 +++++++++++++++++++++---------- bfd/elf64-ppc.c | 321 +++++++++++++++++++++++++++++++++++------------- 3 files changed, 408 insertions(+), 153 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 5d3be83fdc..5a1049380e 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,35 @@ +2009-07-30 Alan Modra + + * elf32-ppc.c (ppc_elf_check_relocs): Always set up sections + used by indirect function support. Count dynamic relocs for + ifunc syms. + (ppc_elf_adjust_dynamic_symbol): Tweak for ifunc. + (allocate_dynrelocs): Allocate all non-dynamic ifunc plt entries + in iplt and their relocs in reliplt. Don't make ifunc syms + dynamic. Allocate got entry relocs for non-dynamic ifunc in + reliplt. Handle other dynamic relocs for ifunc. + (ppc_elf_size_dynamic_sections): Alloc dyn relocs for static + ifunc in reliplt, likewise relocs for got against local ifunc. + Typo fix on reliplt size adjust. + (ppc_elf_relocate_section): Don't use plt scheme of allocating + relocs 1-1 with entries for iplt, instead just add using + reloc_count. Write got relocs and dyn relocs for ifunc to reliplt. + Error on invalid ifunc dyn relocs. + (ppc_elf_finish_dynamic_symbol): Adjust for non-dynamic ifunc plt + in iplt/reliplt. + * elf64-ppc.c (ppc64_elf_howto_raw): Add R_PPC64_JMP_IREL, + R_PPC64_REL16, R_PPC64_REL16_LO, R_PPC64_REL16_HI, R_PPC64_REL16_HA. + (ppc64_elf_reloc_type_lookup): Handle new relocs. + (ppc64_elf_check_relocs): Likewise. Count dyn relocs for ifunc. + (allocate_dynrelocs): As for elf32-ppc.c above. + (ppc64_elf_size_dynamic_sections): Likewise. + (ppc_build_one_stub): Put non-dynamic ifunc plt call stubs in iplt, + and their relocs in reliplt. Use R_PPC64_JMP_IREL. + (ppc_size_one_stub): Similarly. + (ppc64_elf_relocate_section): As for elf32-ppc.c above. Handle new + relocs too. + (ppc64_elf_finish_dynamic_symbol): As for elf32-ppc.c above. + 2009-07-29 Hans-Peter Nilsson * elf64-mmix.c (mmix_elf_relax_section): Revert 2009-05-27 diff --git a/bfd/elf32-ppc.c b/bfd/elf32-ppc.c index 814c8a0ab1..d1e2cfced5 100644 --- a/bfd/elf32-ppc.c +++ b/bfd/elf32-ppc.c @@ -3408,6 +3408,13 @@ ppc_elf_check_relocs (bfd *abfd, ppc_elf_howto_init (); htab = ppc_elf_hash_table (info); + if (htab->glink == NULL) + { + if (htab->elf.dynobj == NULL) + htab->elf.dynobj = abfd; + if (!ppc_elf_create_glink (htab->elf.dynobj, info)) + return FALSE; + } tga = elf_link_hash_lookup (&htab->elf, "__tls_get_addr", FALSE, FALSE, TRUE); symtab_hdr = &elf_symtab_hdr (abfd); @@ -3451,23 +3458,8 @@ ppc_elf_check_relocs (bfd *abfd, tls_type = 0; ifunc = NULL; - r_type = ELF32_R_TYPE (rel->r_info); - if (!htab->is_vxworks && is_branch_reloc (r_type)) + if (!htab->is_vxworks) { - if (h != NULL && h == tga) - { - if (rel != relocs - && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD - || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD)) - /* We have a new-style __tls_get_addr call with a marker - reloc. */ - ; - else - /* Mark this section as having an old-style call. */ - sec->has_tls_get_addr_call = 1; - } - - /* STT_GNU_IFUNC symbols must have a PLT entry. */ if (h != NULL) { if (h->type == STT_GNU_IFUNC) @@ -3491,6 +3483,25 @@ ppc_elf_check_relocs (bfd *abfd, return FALSE; } } + } + + r_type = ELF32_R_TYPE (rel->r_info); + if (!htab->is_vxworks && is_branch_reloc (r_type)) + { + if (h != NULL && h == tga) + { + if (rel != relocs + && (ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSGD + || ELF32_R_TYPE (rel[-1].r_info) == R_PPC_TLSLD)) + /* We have a new-style __tls_get_addr call with a marker + reloc. */ + ; + else + /* Mark this section as having an old-style call. */ + sec->has_tls_get_addr_call = 1; + } + + /* STT_GNU_IFUNC symbols must have a PLT entry. */ if (ifunc != NULL) { bfd_vma addend = 0; @@ -3503,14 +3514,6 @@ ppc_elf_check_relocs (bfd *abfd, if (!update_plt_info (abfd, ifunc, addend < 32768 ? NULL : got2, addend)) return FALSE; - - if (htab->glink == NULL) - { - if (htab->elf.dynobj == NULL) - htab->elf.dynobj = abfd; - if (!ppc_elf_create_glink (htab->elf.dynobj, info)) - return FALSE; - } } } @@ -3938,7 +3941,9 @@ ppc_elf_check_relocs (bfd *abfd, && !info->shared && h != NULL && (h->root.type == bfd_link_hash_defweak - || !h->def_regular))) + || !h->def_regular)) + || (!info->shared + && ifunc != NULL)) { struct ppc_elf_dyn_relocs *p; struct ppc_elf_dyn_relocs **head; @@ -4859,8 +4864,10 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info, } else { - /* After adjust_dynamic_symbol, non_got_ref set means that - dyn_relocs for this symbol should be discarded. + /* After adjust_dynamic_symbol, non_got_ref set in the + non-shared case means that we have allocated space in + .dynbss for the symbol and thus dyn_relocs for this + symbol should be discarded. If we get here we know we are making a PLT entry for this symbol, and in an executable we'd normally resolve relocations against this symbol to the PLT entry. Allow @@ -4868,6 +4875,7 @@ ppc_elf_adjust_dynamic_symbol (struct bfd_link_info *info, relocs will not cause text relocation. */ if (!h->ref_regular_nonweak && h->non_got_ref + && h->type != STT_GNU_IFUNC && !htab->is_vxworks && !ppc_elf_hash_entry (h)->has_sda_refs && !readonly_dynrelocs (h)) @@ -5105,10 +5113,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)) { asection *s = htab->plt; - if (!dyn) + if (!dyn || h->dynindx == -1) s = htab->iplt; - if (htab->plt_type == PLT_NEW || !dyn) + if (htab->plt_type == PLT_NEW || !dyn || h->dynindx == -1) { if (!doneone) { @@ -5186,7 +5194,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) /* We also need to make an entry in the .rela.plt section. */ if (!doneone) { - if (!htab->elf.dynamic_sections_created) + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) htab->reliplt->size += sizeof (Elf32_External_Rela); else { @@ -5246,7 +5255,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) /* Make sure this symbol is output as a dynamic symbol. */ if (eh->elf.dynindx == -1 && !eh->elf.forced_local - && !eh->elf.def_regular + && eh->elf.type != STT_GNU_IFUNC && htab->elf.dynamic_sections_created) { if (!bfd_elf_link_record_dynamic_symbol (info, &eh->elf)) @@ -5279,19 +5288,24 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) eh->elf.got.offset = (bfd_vma) -1; else { + asection *rsec = NULL; eh->elf.got.offset = allocate_got (htab, need); dyn = htab->elf.dynamic_sections_created; if ((info->shared || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, &eh->elf)) && (ELF_ST_VISIBILITY (eh->elf.other) == STV_DEFAULT || eh->elf.root.type != bfd_link_hash_undefweak)) + rsec = htab->relgot; + else if (h->type == STT_GNU_IFUNC) + rsec = htab->reliplt; + if (rsec != NULL) { /* All the entries we allocated need relocs. Except LD only needs one. */ if ((eh->tls_mask & TLS_LD) != 0 && eh->elf.def_dynamic) need -= 4; - htab->relgot->size += need * (sizeof (Elf32_External_Rela) / 4); + rsec->size += need * (sizeof (Elf32_External_Rela) / 4); } } } @@ -5299,7 +5313,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) eh->elf.got.offset = (bfd_vma) -1; if (eh->dyn_relocs == NULL - || !htab->elf.dynamic_sections_created) + || (!htab->elf.dynamic_sections_created + && h->type != STT_GNU_IFUNC)) return TRUE; /* In the shared -Bsymbolic case, discard space allocated for @@ -5370,6 +5385,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) } } } + else if (h->type == STT_GNU_IFUNC) + { + if (!h->non_got_ref) + eh->dyn_relocs = NULL; + } else if (ELIMINATE_COPY_RELOCS) { /* For the non-shared case, discard space for relocs against @@ -5382,8 +5402,7 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 - && !h->forced_local - && !h->def_regular) + && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; @@ -5404,6 +5423,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) for (p = eh->dyn_relocs; p != NULL; p = p->next) { asection *sreloc = elf_section_data (p->sec)->sreloc; + if (!htab->elf.dynamic_sections_created) + sreloc = htab->reliplt; sreloc->size += p->count * sizeof (Elf32_External_Rela); } @@ -5508,8 +5529,10 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, } else if (p->count != 0) { - elf_section_data (p->sec)->sreloc->size - += p->count * sizeof (Elf32_External_Rela); + asection *sreloc = elf_section_data (p->sec)->sreloc; + if (!htab->elf.dynamic_sections_created) + sreloc = htab->reliplt; + sreloc->size += p->count * sizeof (Elf32_External_Rela); if ((p->sec->output_section->flags & (SEC_READONLY | SEC_ALLOC)) == (SEC_READONLY | SEC_ALLOC)) @@ -5553,6 +5576,9 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, if (info->shared) htab->relgot->size += (need * (sizeof (Elf32_External_Rela) / 4)); + else if ((*lgot_masks & PLT_IFUNC) != 0) + htab->reliplt->size += (need + * (sizeof (Elf32_External_Rela) / 4)); } } else @@ -5590,7 +5616,7 @@ ppc_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, if (!doneone) { - htab->reliplt += sizeof (Elf32_External_Rela); + htab->reliplt->size += sizeof (Elf32_External_Rela); doneone = TRUE; } } @@ -6912,7 +6938,7 @@ ppc_elf_relocate_section (bfd *output_bfd, } ifunc = NULL; - if (!htab->is_vxworks && is_branch_reloc (r_type)) + if (!htab->is_vxworks) { if (h != NULL) { @@ -6929,7 +6955,7 @@ ppc_elf_relocate_section (bfd *output_bfd, ifunc = local_plt + r_symndx; } } - if (ifunc != NULL) + if (ifunc != NULL && is_branch_reloc (r_type)) { struct plt_entry *ent = find_plt_ent (ifunc, got2, rel->r_addend); @@ -6943,8 +6969,9 @@ ppc_elf_relocate_section (bfd *output_bfd, + ent->plt.offset); rela.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE); rela.r_addend = relocation; - loc = (htab->reliplt->contents - + ent->plt.offset * sizeof (Elf32_External_Rela) / 4); + loc = htab->reliplt->contents; + loc += (htab->reliplt->reloc_count++ + * sizeof (Elf32_External_Rela)); bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); ent->plt.offset |= 1; @@ -7081,6 +7108,7 @@ ppc_elf_relocate_section (bfd *output_bfd, off &= ~1; else { + asection *rsec; unsigned int tls_m = (tls_mask & (TLS_LD | TLS_GD | TLS_DTPREL | TLS_TPREL | TLS_TPRELGD)); @@ -7119,11 +7147,16 @@ ppc_elf_relocate_section (bfd *output_bfd, } /* Generate relocs for the dynamic linker. */ + rsec = NULL; if ((info->shared || indx != 0) && (offp == &htab->tlsld_got.offset || h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak)) + rsec = htab->relgot; + else if (ifunc != NULL) + rsec = htab->reliplt; + if (rsec != NULL) { outrel.r_offset = (htab->got->output_section->vma + htab->got->output_offset @@ -7134,8 +7167,8 @@ ppc_elf_relocate_section (bfd *output_bfd, outrel.r_info = ELF32_R_INFO (indx, R_PPC_DTPMOD32); if (tls_ty == (TLS_TLS | TLS_GD)) { - loc = htab->relgot->contents; - loc += (htab->relgot->reloc_count++ + loc = rsec->contents; + loc += (rsec->reloc_count++ * sizeof (Elf32_External_Rela)); bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); @@ -7148,18 +7181,20 @@ ppc_elf_relocate_section (bfd *output_bfd, outrel.r_info = ELF32_R_INFO (indx, R_PPC_DTPREL32); else if (tls_ty == (TLS_TLS | TLS_TPREL)) outrel.r_info = ELF32_R_INFO (indx, R_PPC_TPREL32); - else if (indx == 0) - outrel.r_info = ELF32_R_INFO (indx, R_PPC_RELATIVE); - else + else if (indx != 0) outrel.r_info = ELF32_R_INFO (indx, R_PPC_GLOB_DAT); + else if (ifunc != NULL) + outrel.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE); + else + outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE); if (indx == 0 && tls_ty != (TLS_TLS | TLS_LD)) { outrel.r_addend += relocation; if (tls_ty & (TLS_GD | TLS_DTPREL | TLS_TPREL)) outrel.r_addend -= htab->elf.tls_sec->vma; } - loc = htab->relgot->contents; - loc += (htab->relgot->reloc_count++ + loc = rsec->contents; + loc += (rsec->reloc_count++ * sizeof (Elf32_External_Rela)); bfd_elf32_swap_reloca_out (output_bfd, &outrel, loc); } @@ -7350,7 +7385,9 @@ ppc_elf_relocate_section (bfd *output_bfd, && h != NULL && h->dynindx != -1 && !h->non_got_ref - && !h->def_regular)) + && !h->def_regular) + || (!info->shared + && ifunc != NULL)) { int skip; @@ -7366,8 +7403,9 @@ ppc_elf_relocate_section (bfd *output_bfd, time. */ if (sreloc == NULL) { - sreloc = _bfd_elf_get_dynamic_reloc_section - (input_bfd, input_section, /*rela?*/ TRUE); + sreloc = elf_section_data (input_section)->sreloc; + if (!htab->elf.dynamic_sections_created) + sreloc = htab->reliplt; if (sreloc == NULL) return FALSE; } @@ -7397,13 +7435,35 @@ ppc_elf_relocate_section (bfd *output_bfd, { outrel.r_addend = relocation + rel->r_addend; - if (r_type == R_PPC_ADDR32) - outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE); - else + if (r_type != R_PPC_ADDR32) { long indx = 0; - if (r_symndx == 0 || bfd_is_abs_section (sec)) + if (ifunc != NULL) + { + /* If we get here when building a static + executable, then the libc startup function + responsible for applying indirect function + relocations is going to complain about + the reloc type. + If we get here when building a dynamic + executable, it will be because we have + a text relocation. The dynamic loader + will set the text segment writable and + non-executable to apply text relocations. + So we'll segfault when trying to run the + indirection function to resolve the reloc. */ + (*_bfd_error_handler) + (_("%B(%A+0x%lx): relocation %s for indirect " + "function %s unsupported"), + input_bfd, + input_section, + (long) rel->r_offset, + howto->name, + sym_name); + ret = FALSE; + } + else if (r_symndx == 0 || bfd_is_abs_section (sec)) ; else if (sec == NULL || sec->owner == NULL) { @@ -7418,7 +7478,8 @@ ppc_elf_relocate_section (bfd *output_bfd, against a section symbol. It would be proper to subtract the symbol's value, osec->vma, from the emitted reloc addend, - but ld.so expects buggy relocs. */ + but ld.so expects buggy relocs. + FIXME: Why not always use a zero index? */ osec = sec->output_section; indx = elf_section_data (osec)->dynindx; if (indx == 0) @@ -7437,6 +7498,10 @@ ppc_elf_relocate_section (bfd *output_bfd, outrel.r_info = ELF32_R_INFO (indx, r_type); } + else if (ifunc != NULL) + outrel.r_info = ELF32_R_INFO (0, R_PPC_IRELATIVE); + else + outrel.r_info = ELF32_R_INFO (0, R_PPC_RELATIVE); } loc = sreloc->contents; @@ -7891,7 +7956,8 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, bfd_vma reloc_index; if (htab->plt_type == PLT_NEW - || !htab->elf.dynamic_sections_created) + || !htab->elf.dynamic_sections_created + || h->dynindx == -1) reloc_index = ent->plt.offset / 4; else { @@ -7905,7 +7971,8 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, /* This symbol has an entry in the procedure linkage table. Set it up. */ if (htab->plt_type == PLT_VXWORKS - && htab->elf.dynamic_sections_created) + && htab->elf.dynamic_sections_created + && h->dynindx != -1) { bfd_vma got_offset; const bfd_vma *plt_entry; @@ -8028,14 +8095,16 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, else { asection *splt = htab->plt; - if (!htab->elf.dynamic_sections_created) + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) splt = htab->iplt; rela.r_offset = (splt->output_section->vma + splt->output_offset + ent->plt.offset); if (htab->plt_type == PLT_OLD - || !htab->elf.dynamic_sections_created) + || !htab->elf.dynamic_sections_created + || h->dynindx == -1) { /* We don't need to fill in the .plt. The ppc dynamic linker will fill it in. */ @@ -8065,11 +8134,14 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, else rela.r_info = ELF32_R_INFO (h->dynindx, R_PPC_JMP_SLOT); - if (!htab->elf.dynamic_sections_created) - loc = htab->reliplt->contents; + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) + loc = (htab->reliplt->contents + + (htab->reliplt->reloc_count++ + * sizeof (Elf32_External_Rela))); else - loc = htab->relplt->contents; - loc += reloc_index * sizeof (Elf32_External_Rela); + loc = (htab->relplt->contents + + reloc_index * sizeof (Elf32_External_Rela)); bfd_elf32_swap_reloca_out (output_bfd, &rela, loc); if (!h->def_regular) @@ -8096,10 +8168,12 @@ ppc_elf_finish_dynamic_symbol (bfd *output_bfd, } if (htab->plt_type == PLT_NEW - || !htab->elf.dynamic_sections_created) + || !htab->elf.dynamic_sections_created + || h->dynindx == -1) { asection *splt = htab->plt; - if (!htab->elf.dynamic_sections_created) + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) splt = htab->iplt; write_glink_stub (ent, splt, info); diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c index d744990a51..f15465806a 100644 --- a/bfd/elf64-ppc.c +++ b/bfd/elf64-ppc.c @@ -1874,6 +1874,20 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { 0xffff, /* dst_mask */ FALSE), /* pcrel_offset */ + HOWTO (R_PPC64_JMP_IREL, /* type */ + 0, /* rightshift */ + 0, /* size (0=byte, 1=short, 2=long, 4=64 bits) */ + 0, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + ppc64_elf_unhandled_reloc, /* special_function */ + "R_PPC64_JMP_IREL", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0, /* dst_mask */ + FALSE), /* pcrel_offset */ + HOWTO (R_PPC64_IRELATIVE, /* type */ 0, /* rightshift */ 4, /* size (0=byte, 1=short, 2=long, 4=64 bits) */ @@ -1888,6 +1902,67 @@ static reloc_howto_type ppc64_elf_howto_raw[] = { ONES (64), /* dst_mask */ FALSE), /* pcrel_offset */ + /* A 16 bit relative relocation. */ + HOWTO (R_PPC64_REL16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_REL16", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A 16 bit relative relocation without overflow. */ + HOWTO (R_PPC64_REL16_LO, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont,/* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_REL16_LO", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* The high order 16 bits of a relative address. */ + HOWTO (R_PPC64_REL16_HI, /* type */ + 16, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_PPC64_REL16_HI", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* The high order 16 bits of a relative address, plus 1 if the contents of + the low 16 bits, treated as a signed number, is negative. */ + HOWTO (R_PPC64_REL16_HA, /* type */ + 16, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + TRUE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_dont, /* complain_on_overflow */ + ppc64_elf_ha_reloc, /* special_function */ + "R_PPC64_REL16_HA", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + /* GNU extension to record C++ vtable hierarchy. */ HOWTO (R_PPC64_GNU_VTINHERIT, /* type */ 0, /* rightshift */ @@ -2156,6 +2231,14 @@ ppc64_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, break; case BFD_RELOC_PPC64_DTPREL16_HIGHESTA: r = R_PPC64_DTPREL16_HIGHESTA; break; + case BFD_RELOC_16_PCREL: r = R_PPC64_REL16; + break; + case BFD_RELOC_LO16_PCREL: r = R_PPC64_REL16_LO; + break; + case BFD_RELOC_HI16_PCREL: r = R_PPC64_REL16_HI; + break; + case BFD_RELOC_HI16_S_PCREL: r = R_PPC64_REL16_HA; + break; case BFD_RELOC_VTABLE_INHERIT: r = R_PPC64_GNU_VTINHERIT; break; case BFD_RELOC_VTABLE_ENTRY: r = R_PPC64_GNU_VTENTRY; @@ -4763,6 +4846,29 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, tls_type = 0; ifunc = NULL; + if (h != NULL) + { + if (h->type == STT_GNU_IFUNC) + { + h->needs_plt = 1; + ifunc = &h->plt.plist; + } + } + else + { + Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache, + abfd, r_symndx); + if (isym == NULL) + return FALSE; + + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + { + ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx, + rel->r_addend, PLT_IFUNC); + if (ifunc == NULL) + return FALSE; + } + } r_type = ELF64_R_TYPE (rel->r_info); if (is_branch_reloc (r_type)) { @@ -4780,29 +4886,6 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, } /* STT_GNU_IFUNC symbols must have a PLT entry. */ - if (h != NULL) - { - if (h->type == STT_GNU_IFUNC) - { - h->needs_plt = 1; - ifunc = &h->plt.plist; - } - } - else - { - Elf_Internal_Sym *isym = bfd_sym_from_r_symndx (&htab->sym_cache, - abfd, r_symndx); - if (isym == NULL) - return FALSE; - - if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) - { - ifunc = update_local_sym_info (abfd, symtab_hdr, r_symndx, - rel->r_addend, PLT_IFUNC); - if (ifunc == NULL) - return FALSE; - } - } if (ifunc != NULL && !update_plt_info (abfd, ifunc, rel->r_addend)) return FALSE; @@ -4944,6 +5027,12 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, break; /* Nor do these. */ + case R_PPC64_REL16: + case R_PPC64_REL16_LO: + case R_PPC64_REL16_HI: + case R_PPC64_REL16_HA: + break; + case R_PPC64_TOC16: case R_PPC64_TOC16_LO: case R_PPC64_TOC16_HI: @@ -5194,7 +5283,9 @@ ppc64_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, && !info->shared && h != NULL && (h->root.type == bfd_link_hash_defweak - || !h->def_regular))) + || !h->def_regular)) + || (!info->shared + && ifunc != NULL)) { struct ppc_dyn_relocs *p; struct ppc_dyn_relocs **head; @@ -7990,7 +8081,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) for (pent = h->plt.plist; pent != NULL; pent = pent->next) if (pent->plt.refcount > 0) { - if (!htab->elf.dynamic_sections_created) + if (!htab->elf.dynamic_sections_created + || h->dynindx == -1) { s = htab->iplt; pent->plt.offset = s->size; @@ -8069,12 +8161,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) 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. */ if (h->dynindx == -1 && !h->forced_local + && h->type != STT_GNU_IFUNC && htab->elf.dynamic_sections_created) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) @@ -8097,20 +8191,25 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) 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)) - ppc64_elf_tdata (gent->owner)->relgot->size - += (gent->tls_type & eh->tls_mask & TLS_GD - ? 2 * sizeof (Elf64_External_Rela) - : sizeof (Elf64_External_Rela)); + 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)); } else gent->got.offset = (bfd_vma) -1; if (eh->dyn_relocs == NULL - || !htab->elf.dynamic_sections_created) + || (!htab->elf.dynamic_sections_created + && h->type != STT_GNU_IFUNC)) return TRUE; /* In the shared -Bsymbolic case, discard space allocated for @@ -8160,6 +8259,11 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) } } } + else if (h->type == STT_GNU_IFUNC) + { + if (!h->non_got_ref) + eh->dyn_relocs = NULL; + } else if (ELIMINATE_COPY_RELOCS) { /* For the non-shared case, discard space for relocs against @@ -8193,6 +8297,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) for (p = eh->dyn_relocs; p != NULL; p = p->next) { asection *sreloc = elf_section_data (p->sec)->sreloc; + if (!htab->elf.dynamic_sections_created) + sreloc = htab->reliplt; sreloc->size += p->count * sizeof (Elf64_External_Rela); } @@ -8291,6 +8397,8 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, else if (p->count != 0) { srel = elf_section_data (p->sec)->sreloc; + if (!htab->elf.dynamic_sections_created) + srel = htab->reliplt; srel->size += p->count * sizeof (Elf64_External_Rela); if ((p->sec->output_section->flags & SEC_READONLY) != 0) info->flags |= DF_TEXTREL; @@ -8324,19 +8432,15 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, } else { + unsigned int num = 1; ent->got.offset = s->size; if ((ent->tls_type & *lgot_masks & TLS_GD) != 0) - { - s->size += 16; - if (info->shared) - srel->size += 2 * sizeof (Elf64_External_Rela); - } - else - { - s->size += 8; - if (info->shared) - srel->size += sizeof (Elf64_External_Rela); - } + 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); } } else @@ -8356,7 +8460,7 @@ ppc64_elf_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, ent->plt.offset = s->size; s->size += PLT_ENTRY_SIZE; - htab->reliplt += sizeof (Elf64_External_Rela); + htab->reliplt->size += sizeof (Elf64_External_Rela); } else ent->plt.offset = (bfd_vma) -1; @@ -9029,7 +9133,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) abort (); plt = htab->plt; - if (!htab->elf.dynamic_sections_created) + if (!htab->elf.dynamic_sections_created + || stub_entry->h == NULL + || stub_entry->h->elf.dynindx == -1) plt = htab->iplt; dest += plt->output_offset + plt->output_section->vma; @@ -9041,20 +9147,15 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) bfd_byte *rl; rela.r_offset = dest; - rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); rela.r_addend = (stub_entry->target_value + stub_entry->target_section->output_offset + stub_entry->target_section->output_section->vma); - if (!htab->elf.dynamic_sections_created) - rl = (htab->reliplt->contents - + (stub_entry->plt_ent->plt.offset - / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela)))); - else - rl = (htab->relplt->contents - + ((stub_entry->plt_ent->plt.offset - PLT_INITIAL_ENTRY_SIZE) - / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela)))); - bfd_elf32_swap_reloca_out (info->output_bfd, &rela, rl); + rl = (htab->reliplt->contents + + (htab->reliplt->reloc_count++ + * sizeof (Elf64_External_Rela))); + bfd_elf64_swap_reloca_out (info->output_bfd, &rela, rl); stub_entry->plt_ent->plt.offset |= 1; } @@ -9162,7 +9263,9 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) if (off >= (bfd_vma) -2) abort (); plt = htab->plt; - if (!htab->elf.dynamic_sections_created) + if (!htab->elf.dynamic_sections_created + || stub_entry->h == NULL + || stub_entry->h->elf.dynindx == -1) plt = htab->iplt; off += (plt->output_offset + plt->output_section->vma @@ -11093,7 +11196,9 @@ ppc64_elf_relocate_section (bfd *output_bfd, && sec->output_section != NULL && sec->id <= htab->top_id && (htab->stub_group[sec->id].toc_off - != htab->stub_group[input_section->id].toc_off))) + != htab->stub_group[input_section->id].toc_off)) + || (h == NULL + && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)) && (stub_entry = ppc_get_stub_entry (input_section, sec, fdh, rel, htab)) != NULL && (stub_entry->stub_type == ppc_stub_plt_call @@ -11377,14 +11482,23 @@ ppc64_elf_relocate_section (bfd *output_bfd, /* Generate relocs for the dynamic linker, except in the case of TLSLD where we'll use one entry per module. */ - asection *relgot = ppc64_elf_tdata (input_bfd)->relgot; + asection *relgot; + bfd_boolean ifunc; *offp = off | 1; + relgot = NULL; + ifunc = (h != NULL + ? 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 || ELF_ST_VISIBILITY (h->elf.other) == STV_DEFAULT || h->elf.root.type != bfd_link_hash_undefweak)) + relgot = ppc64_elf_tdata (input_bfd)->relgot; + else if (ifunc) + relgot = htab->reliplt; + if (relgot != NULL) { outrel.r_offset = (got->output_section->vma + got->output_offset @@ -11411,9 +11525,14 @@ ppc64_elf_relocate_section (bfd *output_bfd, outrel.r_info = ELF64_R_INFO (indx, R_PPC64_DTPREL64); else if (tls_type == (TLS_TLS | TLS_TPREL)) outrel.r_info = ELF64_R_INFO (indx, R_PPC64_TPREL64); - else if (indx == 0) + else if (indx != 0) + outrel.r_info = ELF64_R_INFO (indx, R_PPC64_GLOB_DAT); + else { - outrel.r_info = ELF64_R_INFO (indx, R_PPC64_RELATIVE); + if (ifunc) + outrel.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + else + outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); /* Write the .got section contents for the sake of prelink. */ @@ -11421,8 +11540,6 @@ ppc64_elf_relocate_section (bfd *output_bfd, bfd_put_64 (output_bfd, outrel.r_addend + relocation, loc); } - else - outrel.r_info = ELF64_R_INFO (indx, R_PPC64_GLOB_DAT); if (indx == 0 && tls_type != (TLS_TLS | TLS_LD)) { @@ -11541,6 +11658,12 @@ ppc64_elf_relocate_section (bfd *output_bfd, addend -= sec->output_section->vma; break; + case R_PPC64_REL16: + case R_PPC64_REL16_LO: + case R_PPC64_REL16_HI: + case R_PPC64_REL16_HA: + break; + case R_PPC64_REL14: case R_PPC64_REL14_BRNTAKEN: case R_PPC64_REL14_BRTAKEN: @@ -11633,7 +11756,11 @@ ppc64_elf_relocate_section (bfd *output_bfd, && h != NULL && h->elf.dynindx != -1 && !h->elf.non_got_ref - && !h->elf.def_regular)) + && !h->elf.def_regular) + || (!info->shared + && (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC))) { Elf_Internal_Rela outrel; bfd_boolean skip, relocate; @@ -11698,19 +11825,42 @@ ppc64_elf_relocate_section (bfd *output_bfd, entry in this lib. */ unresolved_reloc = FALSE; } - outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); + if (!is_opd + && r_type == R_PPC64_ADDR64 + && (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)) + outrel.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + else + { + outrel.r_info = ELF64_R_INFO (0, R_PPC64_RELATIVE); - /* We need to relocate .opd contents for ld.so. - Prelink also wants simple and consistent rules - for relocs. This make all RELATIVE relocs have - *r_offset equal to r_addend. */ - relocate = TRUE; + /* We need to relocate .opd contents for ld.so. + Prelink also wants simple and consistent rules + for relocs. This make all RELATIVE relocs have + *r_offset equal to r_addend. */ + relocate = TRUE; + } } else { long indx = 0; - if (r_symndx == 0 || bfd_is_abs_section (sec)) + if (h != NULL + ? h->elf.type == STT_GNU_IFUNC + : ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + { + (*_bfd_error_handler) + (_("%B(%A+0x%lx): relocation %s for indirect " + "function %s unsupported"), + input_bfd, + input_section, + (long) rel->r_offset, + ppc64_elf_howto_table[r_type]->name, + sym_name); + ret = FALSE; + } + else if (r_symndx == 0 || bfd_is_abs_section (sec)) ; else if (sec == NULL || sec->owner == NULL) { @@ -11748,6 +11898,8 @@ ppc64_elf_relocate_section (bfd *output_bfd, } sreloc = elf_section_data (input_section)->sreloc; + if (!htab->elf.dynamic_sections_created) + sreloc = htab->reliplt; if (sreloc == NULL) abort (); @@ -11789,6 +11941,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, case R_PPC64_COPY: case R_PPC64_GLOB_DAT: case R_PPC64_JMP_SLOT: + case R_PPC64_JMP_IREL: case R_PPC64_RELATIVE: /* We shouldn't ever see these dynamic relocs in relocatable files. */ @@ -11821,6 +11974,7 @@ ppc64_elf_relocate_section (bfd *output_bfd, break; case R_PPC64_ADDR16_HA: + case R_PPC64_REL16_HA: case R_PPC64_ADDR16_HIGHERA: case R_PPC64_ADDR16_HIGHESTA: case R_PPC64_TOC16_HA: @@ -12037,14 +12191,6 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd, { /* This symbol has an entry in the procedure linkage table. Set it up. */ - asection *plt = htab->plt; - if (!htab->elf.dynamic_sections_created) - plt = htab->iplt; - - rela.r_offset = (plt->output_section->vma - + plt->output_offset - + ent->plt.offset); - if (!htab->elf.dynamic_sections_created || h->dynindx == -1) { @@ -12052,26 +12198,29 @@ ppc64_elf_finish_dynamic_symbol (bfd *output_bfd, && h->def_regular && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak)); - rela.r_info = ELF64_R_INFO (0, R_PPC64_IRELATIVE); + rela.r_offset = (htab->iplt->output_section->vma + + htab->iplt->output_offset + + ent->plt.offset); + rela.r_info = ELF64_R_INFO (0, R_PPC64_JMP_IREL); rela.r_addend = (h->root.u.def.value + h->root.u.def.section->output_offset + h->root.u.def.section->output_section->vma + ent->addend); + loc = (htab->reliplt->contents + + (htab->reliplt->reloc_count++ + * sizeof (Elf64_External_Rela))); } else { + rela.r_offset = (htab->plt->output_section->vma + + htab->plt->output_offset + + ent->plt.offset); rela.r_info = ELF64_R_INFO (h->dynindx, R_PPC64_JMP_SLOT); rela.r_addend = ent->addend; + loc = (htab->relplt->contents + + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE) + / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela)))); } - - if (!htab->elf.dynamic_sections_created) - loc = (htab->reliplt->contents - + (ent->plt.offset - / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela)))); - else - loc = (htab->relplt->contents - + ((ent->plt.offset - PLT_INITIAL_ENTRY_SIZE) - / (PLT_ENTRY_SIZE / sizeof (Elf64_External_Rela)))); bfd_elf64_swap_reloca_out (output_bfd, &rela, loc); } -- 2.34.1