You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
496 lines
16 KiB
496 lines
16 KiB
commit a345bc8d317a159e3e887632d80c5a8282d34f07 |
|
Author: Alan Modra <amodra@gmail.com> |
|
Date: Fri Nov 1 19:24:20 2013 +1030 |
|
|
|
PowerPC64 ELFv2 symbols defined in plt |
|
|
|
In a non-pic executable, ELFv2 like other targets, needs to emit a plt |
|
entry even for non-call references to functions defined in shared |
|
libraries, and define the function on the plt code. |
|
|
|
* elf64-ppc.c (ppc64_elf_copy_indirect_symbol): Copy |
|
pointer_equality_needed flag. |
|
(ppc64_elf_check_relocs): For ELFv2 arrange to emit plt |
|
entries for references to functions in shared libraries on |
|
non-call relocs. |
|
(readonly_dynrelocs): Split into function of the same name and.. |
|
(maybe_set_textrel): ..this new function. Update call. |
|
(ppc64_elf_adjust_dynamic_symbol): Don't emit dynrelocs for |
|
ELFv2 in most cases if we have a plt entry. Use new |
|
readonly_relocs. |
|
(allocate_dynrelocs): For ELFv2, don't allocate dynreloc space |
|
for ifunc in static executables. |
|
(size_global_entry_stubs): New function. |
|
(ppc64_elf_size_dynamic_sections): Call size_global_entry_stubs. |
|
Save end of glink branch table. |
|
(ppc64_elf_hash_symbol): New function. |
|
(build_global_entry_stubs): New function. |
|
(ppc64_elf_build_stubs): Call build_global_entry_stubs. Adjust |
|
glink sizing. |
|
(ppc64_elf_relocate_section): Tidy plt16/32/64 reloc code. |
|
(ppc64_elf_finish_dynamic_symbol): For ELFv2, adjust symbols |
|
defined on plt code. |
|
|
|
Index: gdb-7.6.1/bfd/elf64-ppc.c |
|
=================================================================== |
|
--- gdb-7.6.1.orig/bfd/elf64-ppc.c |
|
+++ gdb-7.6.1/bfd/elf64-ppc.c |
|
@@ -109,6 +109,7 @@ static bfd_vma opd_entry_value |
|
#define elf_backend_maybe_function_sym ppc64_elf_maybe_function_sym |
|
#define elf_backend_always_size_sections ppc64_elf_func_desc_adjust |
|
#define elf_backend_size_dynamic_sections ppc64_elf_size_dynamic_sections |
|
+#define elf_backend_hash_symbol ppc64_elf_hash_symbol |
|
#define elf_backend_init_index_section _bfd_elf_init_2_index_sections |
|
#define elf_backend_action_discarded ppc64_elf_action_discarded |
|
#define elf_backend_relocate_section ppc64_elf_relocate_section |
|
@@ -174,6 +175,9 @@ static bfd_vma opd_entry_value |
|
|
|
#define LD_R2_0R1 0xe8410000 /* ld %r2,0(%r1) */ |
|
|
|
+#define ADDIS_R12_R12 0x3d8c0000 /* addis %r12,%r12,xxx@ha */ |
|
+#define LD_R12_0R12 0xe98c0000 /* ld %r12,xxx@l(%r12) */ |
|
+ |
|
/* glink call stub instructions. We enter with the index in R0. */ |
|
#define GLINK_CALL_STUB_SIZE (16*4) |
|
/* 0: */ |
|
@@ -4502,6 +4506,7 @@ ppc64_elf_copy_indirect_symbol (struct b |
|
edir->elf.ref_regular |= eind->elf.ref_regular; |
|
edir->elf.ref_regular_nonweak |= eind->elf.ref_regular_nonweak; |
|
edir->elf.needs_plt |= eind->elf.needs_plt; |
|
+ edir->elf.pointer_equality_needed |= eind->elf.pointer_equality_needed; |
|
|
|
/* Copy over any dynamic relocs we may have on the indirect sym. */ |
|
if (eind->dyn_relocs != NULL) |
|
@@ -5219,6 +5224,14 @@ ppc64_elf_check_relocs (bfd *abfd, struc |
|
if (!update_local_sym_info (abfd, symtab_hdr, r_symndx, |
|
rel->r_addend, tls_type)) |
|
return FALSE; |
|
+ |
|
+ /* We may also need a plt entry if the symbol turns out to be |
|
+ an ifunc. */ |
|
+ if (h != NULL && !info->shared && abiversion (abfd) == 2) |
|
+ { |
|
+ if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend)) |
|
+ return FALSE; |
|
+ } |
|
break; |
|
|
|
case R_PPC64_PLT16_HA: |
|
@@ -5467,12 +5480,6 @@ ppc64_elf_check_relocs (bfd *abfd, struc |
|
} |
|
/* Fall through. */ |
|
|
|
- case R_PPC64_REL30: |
|
- case R_PPC64_REL32: |
|
- case R_PPC64_REL64: |
|
- case R_PPC64_ADDR14: |
|
- case R_PPC64_ADDR14_BRNTAKEN: |
|
- case R_PPC64_ADDR14_BRTAKEN: |
|
case R_PPC64_ADDR16: |
|
case R_PPC64_ADDR16_DS: |
|
case R_PPC64_ADDR16_HA: |
|
@@ -5483,6 +5490,23 @@ ppc64_elf_check_relocs (bfd *abfd, struc |
|
case R_PPC64_ADDR16_HIGHESTA: |
|
case R_PPC64_ADDR16_LO: |
|
case R_PPC64_ADDR16_LO_DS: |
|
+ if (h != NULL && !info->shared && abiversion (abfd) == 2 |
|
+ && rel->r_addend == 0) |
|
+ { |
|
+ /* We may need a .plt entry if this reloc refers to a |
|
+ function in a shared lib. */ |
|
+ if (!update_plt_info (abfd, &h->plt.plist, rel->r_addend)) |
|
+ return FALSE; |
|
+ h->pointer_equality_needed = 1; |
|
+ } |
|
+ /* Fall through. */ |
|
+ |
|
+ case R_PPC64_REL30: |
|
+ case R_PPC64_REL32: |
|
+ case R_PPC64_REL64: |
|
+ case R_PPC64_ADDR14: |
|
+ case R_PPC64_ADDR14_BRNTAKEN: |
|
+ case R_PPC64_ADDR14_BRTAKEN: |
|
case R_PPC64_ADDR24: |
|
case R_PPC64_ADDR32: |
|
case R_PPC64_UADDR16: |
|
@@ -6667,6 +6691,25 @@ ppc64_elf_func_desc_adjust (bfd *obfd AT |
|
return TRUE; |
|
} |
|
|
|
+/* Return true if we have dynamic relocs that apply to read-only sections. */ |
|
+ |
|
+static bfd_boolean |
|
+readonly_dynrelocs (struct elf_link_hash_entry *h) |
|
+{ |
|
+ struct ppc_link_hash_entry *eh; |
|
+ struct elf_dyn_relocs *p; |
|
+ |
|
+ eh = (struct ppc_link_hash_entry *) h; |
|
+ for (p = eh->dyn_relocs; p != NULL; p = p->next) |
|
+ { |
|
+ asection *s = p->sec->output_section; |
|
+ |
|
+ if (s != NULL && (s->flags & SEC_READONLY) != 0) |
|
+ return TRUE; |
|
+ } |
|
+ return FALSE; |
|
+} |
|
+ |
|
/* Adjust a symbol defined by a dynamic object and referenced by a |
|
regular object. The current definition is in some section of the |
|
dynamic object, but we're not including those sections. We have to |
|
@@ -6704,6 +6747,26 @@ ppc64_elf_adjust_dynamic_symbol (struct |
|
h->plt.plist = NULL; |
|
h->needs_plt = 0; |
|
} |
|
+ else if (abiversion (info->output_bfd) == 2) |
|
+ { |
|
+ /* 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 |
|
+ dynamic relocs if the reference is weak, and the dynamic |
|
+ relocs will not cause text relocation. */ |
|
+ if (!h->ref_regular_nonweak |
|
+ && h->non_got_ref |
|
+ && h->type != STT_GNU_IFUNC |
|
+ && !readonly_dynrelocs (h)) |
|
+ h->non_got_ref = 0; |
|
+ |
|
+ /* If making a plt entry, then we don't need copy relocs. */ |
|
+ return TRUE; |
|
+ } |
|
} |
|
else |
|
h->plt.plist = NULL; |
|
@@ -6738,26 +6801,12 @@ ppc64_elf_adjust_dynamic_symbol (struct |
|
if (!h->def_dynamic || !h->ref_regular || h->def_regular) |
|
return TRUE; |
|
|
|
- if (ELIMINATE_COPY_RELOCS) |
|
+ /* If we didn't find any dynamic relocs in read-only sections, then |
|
+ we'll be keeping the dynamic relocs and avoiding the copy reloc. */ |
|
+ if (ELIMINATE_COPY_RELOCS && !readonly_dynrelocs (h)) |
|
{ |
|
- struct ppc_link_hash_entry * eh; |
|
- struct elf_dyn_relocs *p; |
|
- |
|
- eh = (struct ppc_link_hash_entry *) h; |
|
- for (p = eh->dyn_relocs; p != NULL; p = p->next) |
|
- { |
|
- s = p->sec->output_section; |
|
- if (s != NULL && (s->flags & SEC_READONLY) != 0) |
|
- break; |
|
- } |
|
- |
|
- /* If we didn't find any dynamic relocs in read-only sections, then |
|
- we'll be keeping the dynamic relocs and avoiding the copy reloc. */ |
|
- if (p == NULL) |
|
- { |
|
- h->non_got_ref = 0; |
|
- return TRUE; |
|
- } |
|
+ h->non_got_ref = 0; |
|
+ return TRUE; |
|
} |
|
|
|
if (h->plt.plist != NULL) |
|
@@ -9167,7 +9216,8 @@ allocate_dynrelocs (struct elf_link_hash |
|
|
|
if (eh->dyn_relocs == NULL |
|
|| (!htab->elf.dynamic_sections_created |
|
- && h->type != STT_GNU_IFUNC)) |
|
+ && (h->type != STT_GNU_IFUNC |
|
+ || !htab->opd_abi))) |
|
return TRUE; |
|
|
|
/* In the shared -Bsymbolic case, discard space allocated for |
|
@@ -9263,28 +9313,59 @@ allocate_dynrelocs (struct elf_link_hash |
|
return TRUE; |
|
} |
|
|
|
-/* Find any dynamic relocs that apply to read-only sections. */ |
|
+/* Called via elf_link_hash_traverse from ppc64_elf_size_dynamic_sections |
|
+ to set up space for global entry stubs. These are put in glink, |
|
+ after the branch table. */ |
|
|
|
static bfd_boolean |
|
-readonly_dynrelocs (struct elf_link_hash_entry *h, void *inf) |
|
+size_global_entry_stubs (struct elf_link_hash_entry *h, void *inf) |
|
{ |
|
- struct ppc_link_hash_entry *eh; |
|
- struct elf_dyn_relocs *p; |
|
+ struct bfd_link_info *info; |
|
+ struct ppc_link_hash_table *htab; |
|
+ struct plt_entry *pent; |
|
+ asection *s; |
|
|
|
- eh = (struct ppc_link_hash_entry *) h; |
|
- for (p = eh->dyn_relocs; p != NULL; p = p->next) |
|
- { |
|
- asection *s = p->sec->output_section; |
|
+ if (h->root.type == bfd_link_hash_indirect) |
|
+ return TRUE; |
|
|
|
- if (s != NULL && (s->flags & SEC_READONLY) != 0) |
|
- { |
|
- struct bfd_link_info *info = inf; |
|
+ if (!h->pointer_equality_needed) |
|
+ return TRUE; |
|
|
|
- info->flags |= DF_TEXTREL; |
|
+ if (h->def_regular) |
|
+ return TRUE; |
|
|
|
- /* Not an error, just cut short the traversal. */ |
|
- return FALSE; |
|
- } |
|
+ info = inf; |
|
+ htab = ppc_hash_table (info); |
|
+ if (htab == NULL) |
|
+ return FALSE; |
|
+ |
|
+ s = htab->glink; |
|
+ for (pent = h->plt.plist; pent != NULL; pent = pent->next) |
|
+ if (pent->plt.offset != (bfd_vma) -1 |
|
+ && pent->addend == 0) |
|
+ { |
|
+ s->size = (s->size + 15) & -16; |
|
+ s->size += 16; |
|
+ break; |
|
+ } |
|
+ return TRUE; |
|
+} |
|
+ |
|
+/* Set DF_TEXTREL if we find any dynamic relocs that apply to |
|
+ read-only sections. */ |
|
+ |
|
+static bfd_boolean |
|
+maybe_set_textrel (struct elf_link_hash_entry *h, void *info) |
|
+{ |
|
+ if (h->root.type == bfd_link_hash_indirect) |
|
+ return TRUE; |
|
+ |
|
+ if (readonly_dynrelocs (h)) |
|
+ { |
|
+ ((struct bfd_link_info *) info)->flags |= DF_TEXTREL; |
|
+ |
|
+ /* Not an error, just cut short the traversal. */ |
|
+ return FALSE; |
|
} |
|
return TRUE; |
|
} |
|
@@ -9435,6 +9516,12 @@ ppc64_elf_size_dynamic_sections (bfd *ou |
|
/* Allocate global sym .plt and .got entries, and space for global |
|
sym dynamic relocs. */ |
|
elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, info); |
|
+ /* Stash the end of glink branch table. */ |
|
+ if (htab->glink != NULL) |
|
+ htab->glink->rawsize = htab->glink->size; |
|
+ |
|
+ if (!htab->opd_abi && !info->shared) |
|
+ elf_link_hash_traverse (&htab->elf, size_global_entry_stubs, info); |
|
|
|
first_tlsld = NULL; |
|
for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) |
|
@@ -9632,7 +9719,7 @@ ppc64_elf_size_dynamic_sections (bfd *ou |
|
/* If any dynamic relocs apply to a read-only section, |
|
then we need a DT_TEXTREL entry. */ |
|
if ((info->flags & DF_TEXTREL) == 0) |
|
- elf_link_hash_traverse (&htab->elf, readonly_dynrelocs, info); |
|
+ elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info); |
|
|
|
if ((info->flags & DF_TEXTREL) != 0) |
|
{ |
|
@@ -9646,6 +9733,19 @@ ppc64_elf_size_dynamic_sections (bfd *ou |
|
return TRUE; |
|
} |
|
|
|
+/* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */ |
|
+ |
|
+static bfd_boolean |
|
+ppc64_elf_hash_symbol (struct elf_link_hash_entry *h) |
|
+{ |
|
+ if (h->plt.plist != NULL |
|
+ && !h->def_regular |
|
+ && !h->pointer_equality_needed) |
|
+ return FALSE; |
|
+ |
|
+ return _bfd_elf_hash_symbol (h); |
|
+} |
|
+ |
|
/* Determine the type of stub needed, if any, for a call. */ |
|
|
|
static inline enum ppc_stub_type |
|
@@ -12049,6 +12149,79 @@ ppc64_elf_toc (bfd *obfd) |
|
return TOCstart; |
|
} |
|
|
|
+/* Called via elf_link_hash_traverse from ppc64_elf_build_stubs to |
|
+ write out any global entry stubs. */ |
|
+ |
|
+static bfd_boolean |
|
+build_global_entry_stubs (struct elf_link_hash_entry *h, void *inf) |
|
+{ |
|
+ struct bfd_link_info *info; |
|
+ struct ppc_link_hash_table *htab; |
|
+ struct plt_entry *pent; |
|
+ asection *s; |
|
+ |
|
+ if (h->root.type == bfd_link_hash_indirect) |
|
+ return TRUE; |
|
+ |
|
+ if (!h->pointer_equality_needed) |
|
+ return TRUE; |
|
+ |
|
+ if (h->def_regular) |
|
+ return TRUE; |
|
+ |
|
+ info = inf; |
|
+ htab = ppc_hash_table (info); |
|
+ if (htab == NULL) |
|
+ return FALSE; |
|
+ |
|
+ s = htab->glink; |
|
+ for (pent = h->plt.plist; pent != NULL; pent = pent->next) |
|
+ if (pent->plt.offset != (bfd_vma) -1 |
|
+ && pent->addend == 0) |
|
+ { |
|
+ bfd_byte *p; |
|
+ asection *plt; |
|
+ bfd_vma off; |
|
+ |
|
+ /* For ELFv2, if this symbol is not defined in a regular file |
|
+ and we are not generating a shared library or pie, then we |
|
+ need to define the symbol in the executable on a call stub. |
|
+ This is to avoid text relocations. */ |
|
+ h->root.u.def.section = s; |
|
+ h->root.u.def.value = s->size; |
|
+ s->size += 16; |
|
+ p = s->contents + h->root.u.def.value; |
|
+ plt = htab->plt; |
|
+ if (!htab->elf.dynamic_sections_created |
|
+ || h->dynindx == -1) |
|
+ plt = htab->iplt; |
|
+ off = pent->plt.offset + plt->output_offset + plt->output_section->vma; |
|
+ off -= h->root.u.def.value + s->output_offset + s->output_section->vma; |
|
+ |
|
+ if (off + 0x80008000 > 0xffffffff || (off & 3) != 0) |
|
+ { |
|
+ info->callbacks->einfo |
|
+ (_("%P: linkage table error against `%T'\n"), |
|
+ h->root.root.string); |
|
+ bfd_set_error (bfd_error_bad_value); |
|
+ htab->stub_error = TRUE; |
|
+ } |
|
+ |
|
+ if (PPC_HA (off) != 0) |
|
+ { |
|
+ bfd_put_32 (s->owner, ADDIS_R12_R12 | PPC_HA (off), p); |
|
+ p += 4; |
|
+ } |
|
+ bfd_put_32 (s->owner, LD_R12_0R12 | PPC_LO (off), p); |
|
+ p += 4; |
|
+ bfd_put_32 (s->owner, MTCTR_R12, p); |
|
+ p += 4; |
|
+ bfd_put_32 (s->owner, BCTR, p); |
|
+ break; |
|
+ } |
|
+ return TRUE; |
|
+} |
|
+ |
|
/* Build all the stubs associated with the current output file. |
|
The stubs are kept in a hash table attached to the main linker |
|
hash table. This function is called via gldelf64ppc_finish. */ |
|
@@ -12184,7 +12357,7 @@ ppc64_elf_build_stubs (bfd_boolean emit_ |
|
|
|
/* Build the .glink lazy link call stubs. */ |
|
indx = 0; |
|
- while (p < htab->glink->contents + htab->glink->size) |
|
+ while (p < htab->glink->contents + htab->glink->rawsize) |
|
{ |
|
if (htab->opd_abi) |
|
{ |
|
@@ -12207,7 +12380,13 @@ ppc64_elf_build_stubs (bfd_boolean emit_ |
|
indx++; |
|
p += 4; |
|
} |
|
- htab->glink->rawsize = p - htab->glink->contents; |
|
+ |
|
+ /* Build .glink global entry stubs. */ |
|
+ if (htab->glink->size > htab->glink->rawsize) |
|
+ { |
|
+ htab->glink->size = (htab->glink->rawsize + 15) & -16; |
|
+ elf_link_hash_traverse (&htab->elf, build_global_entry_stubs, info); |
|
+ } |
|
} |
|
|
|
if (htab->brlt->size != 0) |
|
@@ -12311,7 +12490,7 @@ ppc64_elf_build_stubs (bfd_boolean emit_ |
|
bfd_put_32 (htab->elf.dynobj, val, p); |
|
p += 4; |
|
/* .glink size. */ |
|
- bfd_put_32 (htab->elf.dynobj, htab->glink->rawsize - 8, p); |
|
+ bfd_put_32 (htab->elf.dynobj, htab->glink->size - 8, p); |
|
p += 4; |
|
/* Augmentation. */ |
|
p += 1; |
|
@@ -12361,7 +12540,6 @@ ppc64_elf_build_stubs (bfd_boolean emit_ |
|
} |
|
|
|
if (stub_sec != NULL |
|
- || htab->glink->rawsize != htab->glink->size |
|
|| (htab->glink_eh_frame != NULL |
|
&& htab->glink_eh_frame->rawsize != htab->glink_eh_frame->size)) |
|
{ |
|
@@ -13588,13 +13766,14 @@ ppc64_elf_relocate_section (bfd *output_ |
|
{ |
|
struct plt_entry *ent; |
|
for (ent = h->elf.plt.plist; ent != NULL; ent = ent->next) |
|
- if (ent->addend == orig_rel.r_addend |
|
- && ent->plt.offset != (bfd_vma) -1) |
|
+ if (ent->plt.offset != (bfd_vma) -1 |
|
+ && ent->addend == orig_rel.r_addend) |
|
{ |
|
relocation = (htab->plt->output_section->vma |
|
+ htab->plt->output_offset |
|
+ ent->plt.offset); |
|
unresolved_reloc = FALSE; |
|
+ break; |
|
} |
|
} |
|
break; |
|
@@ -14297,6 +14476,30 @@ ppc64_elf_finish_dynamic_symbol (bfd *ou |
|
/ PLT_ENTRY_SIZE (htab) * sizeof (Elf64_External_Rela))); |
|
} |
|
bfd_elf64_swap_reloca_out (output_bfd, &rela, loc); |
|
+ |
|
+ if (!htab->opd_abi) |
|
+ { |
|
+ if (!h->def_regular) |
|
+ { |
|
+ /* Mark the symbol as undefined, rather than as |
|
+ defined in glink. Leave the value if there were |
|
+ any relocations where pointer equality matters |
|
+ (this is a clue for the dynamic linker, to make |
|
+ function pointer comparisons work between an |
|
+ application and shared library), otherwise set it |
|
+ to zero. */ |
|
+ sym->st_shndx = SHN_UNDEF; |
|
+ if (!h->pointer_equality_needed) |
|
+ sym->st_value = 0; |
|
+ else if (!h->ref_regular_nonweak) |
|
+ { |
|
+ /* This breaks function pointer comparisons, but |
|
+ that is better than breaking tests for a NULL |
|
+ function pointer. */ |
|
+ sym->st_value = 0; |
|
+ } |
|
+ } |
|
+ } |
|
} |
|
|
|
if (h->needs_copy)
|
|
|