From c25bc9fc0d99cdc97211346362b677428747eabd Mon Sep 17 00:00:00 2001 From: "H.J. Lu" Date: Sun, 14 Jun 2009 01:39:46 +0000 Subject: [PATCH] bfd/ 2009-06-13 H.J. Lu PR ld/10269 * elf32-i386.c: Include "objalloc.h" and "hashtab.h". (elf_i386_link_hash_table): Add loc_hash_table and loc_hash_memory. (elf_i386_local_hash): New. (elf_i386_local_htab_hash): Likewise. (elf_i386_local_htab_eq): Likewise. (elf_i386_get_local_sym_hash): Likewise. (elf_i386_link_hash_table_free): Likewise. (elf_i386_allocate_local_dynrelocs): Likewise. (elf_i386_finish_local_dynamic_symbol): Likewise. (bfd_elf64_bfd_link_hash_table_free): Likewise. (elf_i386_link_hash_table_create): Create loc_hash_table and loc_hash_memory. (elf_i386_check_relocs): Handle local STT_GNU_IFUNC symbols. (elf_i386_size_dynamic_sections): Likewise. (elf_i386_relocate_section): Likewise. (elf_i386_finish_dynamic_sections): Likewise. (elf_i386_finish_dynamic_symbol): Check _DYNAMIC only if sym isn't NULL. * elf64-x86-64.c: Include "objalloc.h" and "hashtab.h". (elf64_x86_64_link_hash_table): Add loc_hash_table and loc_hash_memory. (elf64_x86_64_local_hash): New. (elf64_x86_64_local_htab_hash): Likewise. (elf64_x86_64_local_htab_eq): Likewise. (elf64_x86_64_get_local_sym_hash): Likewise. (elf64_x86_64_link_hash_table_free): Likewise. (elf64_x86_64_allocate_local_dynrelocs): Likewise. (elf64_x86_64_finish_local_dynamic_symbol): Likewise. (bfd_elf64_bfd_link_hash_table_free): Likewise. (elf64_x86_64_link_hash_table_create): Create loc_hash_table and loc_hash_memory. (elf64_x86_64_check_relocs): Handle local STT_GNU_IFUNC symbols. (elf64_x86_64_size_dynamic_sections): Likewise. (elf64_x86_64_relocate_section): Likewise. (elf64_x86_64_finish_dynamic_sections): Likewise. (elf64_x86_64_finish_dynamic_symbol): Check _DYNAMIC only if sym isn't NULL. gas/ 2009-06-13 H.J. Lu PR ld/10269 * config/tc-i386.c (md_apply_fix): Use TC_FORCE_RELOCATION instead of generic_force_reloc. * config/tc-i386.h (TC_FORCE_RELOCATION): New. ld/testsuite/ 2009-06-13 H.J. Lu PR ld/10269 *: ld-ifunc/ifunc-1-local-x86.d: New. *: ld-ifunc/ifunc-1-local-x86.s: Likewise. *: ld-ifunc/ifunc-2-local-i386.d: Likewise. *: ld-ifunc/ifunc-2-local-i386.s: Likewise. *: ld-ifunc/ifunc-2-local-x86-64.d: Likewise. *: ld-ifunc/ifunc-2-local-x86-64.s: Likewise. *: ld-ifunc/ifunc-4-local-x86.d: Likewise. *: ld-ifunc/ifunc-4-local-x86.s: Likewise. *: ld-ifunc/ifunc-5-local-i386.s: Likewise. *: ld-ifunc/ifunc-5-local-x86-64.s: Likewise. *: ld-ifunc/ifunc-5a-local-i386.d: Likewise. *: ld-ifunc/ifunc-5a-local-x86-64.d: Likewise. *: ld-ifunc/ifunc-5b-local-i386.d: Likewise. *: ld-ifunc/ifunc-5b-local-x86-64.d: Likewise. --- bfd/ChangeLog | 44 ++++ bfd/elf32-i386.c | 224 ++++++++++++++++- bfd/elf64-x86-64.c | 225 +++++++++++++++++- gas/ChangeLog | 8 + gas/config/tc-i386.c | 2 +- gas/config/tc-i386.h | 6 + ld/testsuite/ChangeLog | 18 ++ ld/testsuite/ld-ifunc/ifunc-1-local-x86.d | 7 + ld/testsuite/ld-ifunc/ifunc-1-local-x86.s | 13 + ld/testsuite/ld-ifunc/ifunc-2-local-i386.d | 8 + ld/testsuite/ld-ifunc/ifunc-2-local-i386.s | 18 ++ ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.d | 9 + ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.s | 17 ++ ld/testsuite/ld-ifunc/ifunc-4-local-x86.d | 7 + ld/testsuite/ld-ifunc/ifunc-4-local-x86.s | 18 ++ ld/testsuite/ld-ifunc/ifunc-5-local-i386.s | 22 ++ ld/testsuite/ld-ifunc/ifunc-5-local-x86-64.s | 18 ++ ld/testsuite/ld-ifunc/ifunc-5a-local-i386.d | 9 + ld/testsuite/ld-ifunc/ifunc-5a-local-x86-64.d | 9 + ld/testsuite/ld-ifunc/ifunc-5b-local-i386.d | 9 + ld/testsuite/ld-ifunc/ifunc-5b-local-x86-64.d | 9 + 21 files changed, 684 insertions(+), 16 deletions(-) create mode 100644 ld/testsuite/ld-ifunc/ifunc-1-local-x86.d create mode 100644 ld/testsuite/ld-ifunc/ifunc-1-local-x86.s create mode 100644 ld/testsuite/ld-ifunc/ifunc-2-local-i386.d create mode 100644 ld/testsuite/ld-ifunc/ifunc-2-local-i386.s create mode 100644 ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.d create mode 100644 ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.s create mode 100644 ld/testsuite/ld-ifunc/ifunc-4-local-x86.d create mode 100644 ld/testsuite/ld-ifunc/ifunc-4-local-x86.s create mode 100644 ld/testsuite/ld-ifunc/ifunc-5-local-i386.s create mode 100644 ld/testsuite/ld-ifunc/ifunc-5-local-x86-64.s create mode 100644 ld/testsuite/ld-ifunc/ifunc-5a-local-i386.d create mode 100644 ld/testsuite/ld-ifunc/ifunc-5a-local-x86-64.d create mode 100644 ld/testsuite/ld-ifunc/ifunc-5b-local-i386.d create mode 100644 ld/testsuite/ld-ifunc/ifunc-5b-local-x86-64.d diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 7f1ce47acd0..6022765a4ad 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,47 @@ +2009-06-13 H.J. Lu + + PR ld/10269 + * elf32-i386.c: Include "objalloc.h" and "hashtab.h". + (elf_i386_link_hash_table): Add loc_hash_table and + loc_hash_memory. + (elf_i386_local_hash): New. + (elf_i386_local_htab_hash): Likewise. + (elf_i386_local_htab_eq): Likewise. + (elf_i386_get_local_sym_hash): Likewise. + (elf_i386_link_hash_table_free): Likewise. + (elf_i386_allocate_local_dynrelocs): Likewise. + (elf_i386_finish_local_dynamic_symbol): Likewise. + (bfd_elf64_bfd_link_hash_table_free): Likewise. + (elf_i386_link_hash_table_create): Create loc_hash_table and + loc_hash_memory. + (elf_i386_check_relocs): Handle local STT_GNU_IFUNC symbols. + (elf_i386_size_dynamic_sections): Likewise. + (elf_i386_relocate_section): Likewise. + (elf_i386_finish_dynamic_sections): Likewise. + (elf_i386_finish_dynamic_symbol): Check _DYNAMIC only if sym + isn't NULL. + + * elf64-x86-64.c: Include "objalloc.h" and "hashtab.h". + (elf64_x86_64_link_hash_table): Add loc_hash_table and + loc_hash_memory. + (elf64_x86_64_local_hash): New. + (elf64_x86_64_local_htab_hash): Likewise. + (elf64_x86_64_local_htab_eq): Likewise. + (elf64_x86_64_get_local_sym_hash): Likewise. + (elf64_x86_64_link_hash_table_free): Likewise. + (elf64_x86_64_allocate_local_dynrelocs): Likewise. + (elf64_x86_64_finish_local_dynamic_symbol): Likewise. + (bfd_elf64_bfd_link_hash_table_free): Likewise. + (elf64_x86_64_link_hash_table_create): Create loc_hash_table + and loc_hash_memory. + (elf64_x86_64_check_relocs): Handle local STT_GNU_IFUNC + symbols. + (elf64_x86_64_size_dynamic_sections): Likewise. + (elf64_x86_64_relocate_section): Likewise. + (elf64_x86_64_finish_dynamic_sections): Likewise. + (elf64_x86_64_finish_dynamic_symbol): Check _DYNAMIC only if + sym isn't NULL. + 2009-06-10 Philip Blundell * elf32-arm.c (elf32_arm_fix_exidx_coverage): Avoid crash if diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index b2918cfbc37..273bd85939a 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -26,6 +26,8 @@ #include "elf-bfd.h" #include "elf-vxworks.h" #include "bfd_stdint.h" +#include "objalloc.h" +#include "hashtab.h" /* 386 uses REL relocations instead of RELA. */ #define USE_REL 1 @@ -706,6 +708,10 @@ struct elf_i386_link_hash_table /* _TLS_MODULE_BASE_ symbol. */ struct bfd_link_hash_entry *tls_module_base; + + /* Used by local STT_GNU_IFUNC symbols. */ + htab_t loc_hash_table; + void *loc_hash_memory; }; /* Get the i386 ELF linker hash table from a link_info structure. */ @@ -748,6 +754,82 @@ elf_i386_link_hash_newfunc (struct bfd_hash_entry *entry, return entry; } +static hashval_t +elf_i386_local_hash (int id, int r_sym) +{ + return ((((id & 0xff) << 24) | ((id & 0xff00) << 8)) + ^ r_sym ^ (id >> 16)); +} + +/* Compute a hash of a local hash entry. We use elf_link_hash_entry + for local symbol so that we can handle local STT_GNU_IFUNC symbols + as global symbol. We reuse indx and dynstr_index for local symbol + hash since they aren't used by global symbols in this backend. */ + +static hashval_t +elf_i386_local_htab_hash (const void *ptr) +{ + struct elf_link_hash_entry *h + = (struct elf_link_hash_entry *) ptr; + return elf_i386_local_hash (h->indx, h->dynstr_index); +} + +/* Compare local hash entries. */ + +static int +elf_i386_local_htab_eq (const void *ptr1, const void *ptr2) +{ + struct elf_link_hash_entry *h1 + = (struct elf_link_hash_entry *) ptr1; + struct elf_link_hash_entry *h2 + = (struct elf_link_hash_entry *) ptr2; + + return h1->indx == h2->indx && h1->dynstr_index == h2->dynstr_index; +} + +/* Find and/or create a hash entry for local symbol. */ + +static struct elf_link_hash_entry * +elf_i386_get_local_sym_hash (struct elf_i386_link_hash_table *htab, + bfd *abfd, const Elf_Internal_Rela *rel, + bfd_boolean create) +{ + struct elf_i386_link_hash_entry e, *ret; + asection *sec = abfd->sections; + hashval_t h = elf_i386_local_hash (sec->id, + ELF32_R_SYM (rel->r_info)); + void **slot; + + e.elf.indx = sec->id; + e.elf.dynstr_index = ELF32_R_SYM (rel->r_info); + slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h, + create ? INSERT : NO_INSERT); + + if (!slot) + return NULL; + + if (*slot) + { + ret = (struct elf_i386_link_hash_entry *) *slot; + return &ret->elf; + } + + ret = (struct elf_i386_link_hash_entry *) + objalloc_alloc ((struct objalloc *) htab->loc_hash_memory, + sizeof (struct elf_i386_link_hash_entry)); + if (ret) + { + memset (ret, 0, sizeof (*ret)); + ret->elf.indx = sec->id; + ret->elf.dynstr_index = ELF32_R_SYM (rel->r_info); + ret->elf.dynindx = -1; + ret->elf.plt.offset = (bfd_vma) -1; + ret->elf.got.offset = (bfd_vma) -1; + *slot = ret; + } + return &ret->elf; +} + /* Create an i386 ELF linker hash table. */ static struct bfd_link_hash_table * @@ -788,9 +870,38 @@ elf_i386_link_hash_table_create (bfd *abfd) ret->plt0_pad_byte = 0; ret->tls_module_base = NULL; + ret->loc_hash_table = htab_try_create (1024, + elf_i386_local_htab_hash, + elf_i386_local_htab_eq, + NULL); + ret->loc_hash_memory = objalloc_create (); + if (!ret->loc_hash_table || !ret->loc_hash_memory) + { + free (ret); + return NULL; + } + return &ret->elf.root; } +/* Destroy an i386 ELF linker hash table. */ + +static void +elf_i386_link_hash_table_free (struct bfd_link_hash_table *hash) +{ + struct elf_i386_link_hash_table *htab + = (struct elf_i386_link_hash_table *) hash; + + if (htab->loc_hash_table) + htab_delete (htab->loc_hash_table); + if (htab->loc_hash_memory) + objalloc_free ((struct objalloc *) htab->loc_hash_memory); + _bfd_generic_link_hash_table_free (hash); +} + +/* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up + shortcuts to them in our hash table. */ + /* Create .got, .gotplt, and .rel.got sections in DYNOBJ, and set up shortcuts to them in our hash table. */ @@ -1228,6 +1339,7 @@ elf_i386_check_relocs (bfd *abfd, const Elf_Internal_Rela *rel; const Elf_Internal_Rela *rel_end; asection *sreloc; + Elf_Internal_Sym *isymbuf; if (info->relocatable) return TRUE; @@ -1236,6 +1348,7 @@ elf_i386_check_relocs (bfd *abfd, htab = elf_i386_hash_table (info); symtab_hdr = &elf_symtab_hdr (abfd); + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; sym_hashes = elf_sym_hashes (abfd); sreloc = NULL; @@ -1259,14 +1372,50 @@ elf_i386_check_relocs (bfd *abfd, } if (r_symndx < symtab_hdr->sh_info) - h = NULL; + { + /* A local symbol. */ + Elf_Internal_Sym *isym; + + /* Read this BFD's local symbols. */ + if (isymbuf == NULL) + { + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + return FALSE; + } + + /* Check relocation against local STT_GNU_IFUNC symbol. */ + isym = isymbuf + r_symndx; + if (ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + { + h = elf_i386_get_local_sym_hash (htab, abfd, rel, + TRUE); + if (h == NULL) + return FALSE; + + /* Fake a STT_GNU_IFUNC symbol. */ + h->type = STT_GNU_IFUNC; + h->def_regular = 1; + h->ref_regular = 1; + h->forced_local = 1; + h->root.type = bfd_link_hash_defined; + } + else + h = NULL; + } else { h = sym_hashes[r_symndx - symtab_hdr->sh_info]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; + } + if (h != NULL) + { /* Create the ifunc sections for static executables. If we never see an indirect function symbol nor we are building a static executable, those sections will be empty and @@ -1331,7 +1480,8 @@ elf_i386_check_relocs (bfd *abfd, (_("%B: relocation %s against STT_GNU_IFUNC " "symbol `%s' isn't handled by %s"), abfd, elf_howto_table[r_type].name, - h->root.root.string, __FUNCTION__); + h != NULL ? h->root.root.string : "a local symbol", + __FUNCTION__); bfd_set_error (bfd_error_bad_value); return FALSE; @@ -2337,6 +2487,25 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) return TRUE; } +/* Allocate space in .plt, .got and associated reloc sections for + local dynamic relocs. */ + +static bfd_boolean +elf_i386_allocate_local_dynrelocs (void **slot, void *inf) +{ + struct elf_link_hash_entry *h + = (struct elf_link_hash_entry *) *slot; + + if (h->type != STT_GNU_IFUNC + || !h->def_regular + || !h->ref_regular + || !h->forced_local + || h->root.type != bfd_link_hash_defined) + abort (); + + return elf_i386_allocate_dynrelocs (h, inf); +} + /* Find any dynamic relocs that apply to read-only sections. */ static bfd_boolean @@ -2511,6 +2680,11 @@ elf_i386_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, sym dynamic relocs. */ elf_link_hash_traverse (&htab->elf, elf_i386_allocate_dynrelocs, info); + /* Allocate .plt and .got entries, and space for local symbols. */ + htab_traverse (htab->loc_hash_table, + elf_i386_allocate_local_dynrelocs, + info); + /* For every jump slot reserved in the sgotplt, reloc_count is incremented. However, when we reserve space for TLS descriptors, it's not incremented, so in order to compute the space reserved @@ -2916,6 +3090,18 @@ elf_i386_relocate_section (bfd *output_bfd, break; } } + else if (ELF32_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + { + /* Relocate against local STT_GNU_IFUNC symbol. */ + h = elf_i386_get_local_sym_hash (htab, input_bfd, + rel, FALSE); + if (h == NULL) + abort (); + + /* Set STT_GNU_IFUNC symbol value. */ + h->root.u.def.value = sym->st_value; + h->root.u.def.section = sec; + } } else { @@ -2990,8 +3176,8 @@ elf_i386_relocate_section (bfd *output_bfd, asection *sreloc; bfd_vma offset; - /* Need a dynamic relocation get the the real - function adddress. */ + /* Need a dynamic relocation to get the real function + adddress. */ offset = _bfd_elf_section_offset (output_bfd, info, input_section, @@ -4310,16 +4496,34 @@ do_glob_dat: bfd_elf32_swap_reloc_out (output_bfd, &rel, loc); } - /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. SYM may + be NULL for local symbols. + On VxWorks, the _GLOBAL_OFFSET_TABLE_ symbol is not absolute: it is relative to the ".got" section. */ - if (strcmp (h->root.root.string, "_DYNAMIC") == 0 - || (!htab->is_vxworks && h == htab->elf.hgot)) + if (sym != NULL + && (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || (!htab->is_vxworks && h == htab->elf.hgot))) sym->st_shndx = SHN_ABS; return TRUE; } +/* Finish up local dynamic symbol handling. We set the contents of + various dynamic sections here. */ + +static bfd_boolean +elf_i386_finish_local_dynamic_symbol (void **slot, void *inf) +{ + struct elf_link_hash_entry *h + = (struct elf_link_hash_entry *) *slot; + struct bfd_link_info *info + = (struct bfd_link_info *) inf; + + return elf_i386_finish_dynamic_symbol (info->output_bfd, info, + h, NULL); +} + /* Used to decide how to sort relocs in an optimal manner for the dynamic linker, before writing them out. */ @@ -4527,6 +4731,11 @@ elf_i386_finish_dynamic_sections (bfd *output_bfd, if (htab->sgot && htab->sgot->size > 0) elf_section_data (htab->sgot->output_section)->this_hdr.sh_entsize = 4; + /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols. */ + htab_traverse (htab->loc_hash_table, + elf_i386_finish_local_dynamic_symbol, + info); + return TRUE; } @@ -4592,6 +4801,7 @@ elf_i386_add_symbol_hook (bfd * abfd ATTRIBUTE_UNUSED, #define bfd_elf32_bfd_is_local_label_name elf_i386_is_local_label_name #define bfd_elf32_bfd_link_hash_table_create elf_i386_link_hash_table_create +#define bfd_elf32_bfd_link_hash_table_free elf_i386_link_hash_table_free #define bfd_elf32_bfd_reloc_type_lookup elf_i386_reloc_type_lookup #define bfd_elf32_bfd_reloc_name_lookup elf_i386_reloc_name_lookup diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index 6819b34c5af..2808906a46a 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -26,6 +26,8 @@ #include "libbfd.h" #include "elf-bfd.h" #include "bfd_stdint.h" +#include "objalloc.h" +#include "hashtab.h" #include "elf/x86-64.h" @@ -520,6 +522,10 @@ struct elf64_x86_64_link_hash_table /* _TLS_MODULE_BASE_ symbol. */ struct bfd_link_hash_entry *tls_module_base; + + /* Used by local STT_GNU_IFUNC symbols. */ + htab_t loc_hash_table; + void *loc_hash_memory; }; /* Get the x86-64 ELF linker hash table from a link_info structure. */ @@ -562,6 +568,82 @@ elf64_x86_64_link_hash_newfunc (struct bfd_hash_entry *entry, return entry; } +static hashval_t +elf64_x86_64_local_hash (int id, int r_sym) +{ + return ((((id & 0xff) << 24) | ((id & 0xff00) << 8)) + ^ r_sym ^ (id >> 16)); +} + +/* Compute a hash of a local hash entry. We use elf_link_hash_entry + for local symbol so that we can handle local STT_GNU_IFUNC symbols + as global symbol. We reuse indx and dynstr_index for local symbol + hash since they aren't used by global symbols in this backend. */ + +static hashval_t +elf64_x86_64_local_htab_hash (const void *ptr) +{ + struct elf_link_hash_entry *h + = (struct elf_link_hash_entry *) ptr; + return elf64_x86_64_local_hash (h->indx, h->dynstr_index); +} + +/* Compare local hash entries. */ + +static int +elf64_x86_64_local_htab_eq (const void *ptr1, const void *ptr2) +{ + struct elf_link_hash_entry *h1 + = (struct elf_link_hash_entry *) ptr1; + struct elf_link_hash_entry *h2 + = (struct elf_link_hash_entry *) ptr2; + + return h1->indx == h2->indx && h1->dynstr_index == h2->dynstr_index; +} + +/* Find and/or create a hash entry for local symbol. */ + +static struct elf_link_hash_entry * +elf64_x86_64_get_local_sym_hash (struct elf64_x86_64_link_hash_table *htab, + bfd *abfd, const Elf_Internal_Rela *rel, + bfd_boolean create) +{ + struct elf64_x86_64_link_hash_entry e, *ret; + asection *sec = abfd->sections; + hashval_t h = elf64_x86_64_local_hash (sec->id, + ELF64_R_SYM (rel->r_info)); + void **slot; + + e.elf.indx = sec->id; + e.elf.dynstr_index = ELF64_R_SYM (rel->r_info); + slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h, + create ? INSERT : NO_INSERT); + + if (!slot) + return NULL; + + if (*slot) + { + ret = (struct elf64_x86_64_link_hash_entry *) *slot; + return &ret->elf; + } + + ret = (struct elf64_x86_64_link_hash_entry *) + objalloc_alloc ((struct objalloc *) htab->loc_hash_memory, + sizeof (struct elf64_x86_64_link_hash_entry)); + if (ret) + { + memset (ret, 0, sizeof (*ret)); + ret->elf.indx = sec->id; + ret->elf.dynstr_index = ELF64_R_SYM (rel->r_info); + ret->elf.dynindx = -1; + ret->elf.plt.offset = (bfd_vma) -1; + ret->elf.got.offset = (bfd_vma) -1; + *slot = ret; + } + return &ret->elf; +} + /* Create an X86-64 ELF linker hash table. */ static struct bfd_link_hash_table * @@ -600,9 +682,35 @@ elf64_x86_64_link_hash_table_create (bfd *abfd) ret->sgotplt_jump_table_size = 0; ret->tls_module_base = NULL; + ret->loc_hash_table = htab_try_create (1024, + elf64_x86_64_local_htab_hash, + elf64_x86_64_local_htab_eq, + NULL); + ret->loc_hash_memory = objalloc_create (); + if (!ret->loc_hash_table || !ret->loc_hash_memory) + { + free (ret); + return NULL; + } + return &ret->elf.root; } +/* Destroy an X86-64 ELF linker hash table. */ + +static void +elf64_x86_64_link_hash_table_free (struct bfd_link_hash_table *hash) +{ + struct elf64_x86_64_link_hash_table *htab + = (struct elf64_x86_64_link_hash_table *) hash; + + if (htab->loc_hash_table) + htab_delete (htab->loc_hash_table); + if (htab->loc_hash_memory) + objalloc_free ((struct objalloc *) htab->loc_hash_memory); + _bfd_generic_link_hash_table_free (hash); +} + /* Create .got, .gotplt, and .rela.got sections in DYNOBJ, and set up shortcuts to them in our hash table. */ @@ -1012,6 +1120,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, const Elf_Internal_Rela *rel; const Elf_Internal_Rela *rel_end; asection *sreloc; + Elf_Internal_Sym *isymbuf; if (info->relocatable) return TRUE; @@ -1020,6 +1129,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, htab = elf64_x86_64_hash_table (info); symtab_hdr = &elf_symtab_hdr (abfd); + isymbuf = (Elf_Internal_Sym *) symtab_hdr->contents; sym_hashes = elf_sym_hashes (abfd); sreloc = NULL; @@ -1042,14 +1152,50 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, } if (r_symndx < symtab_hdr->sh_info) - h = NULL; + { + /* A local symbol. */ + Elf_Internal_Sym *isym; + + /* Read this BFD's local symbols. */ + if (isymbuf == NULL) + { + if (isymbuf == NULL) + isymbuf = bfd_elf_get_elf_syms (abfd, symtab_hdr, + symtab_hdr->sh_info, 0, + NULL, NULL, NULL); + if (isymbuf == NULL) + return FALSE; + } + + /* Check relocation against local STT_GNU_IFUNC symbol. */ + isym = isymbuf + r_symndx; + if (ELF64_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + { + h = elf64_x86_64_get_local_sym_hash (htab, abfd, rel, + TRUE); + if (h == NULL) + return FALSE; + + /* Fake a STT_GNU_IFUNC symbol. */ + h->type = STT_GNU_IFUNC; + h->def_regular = 1; + h->ref_regular = 1; + h->forced_local = 1; + h->root.type = bfd_link_hash_defined; + } + else + h = NULL; + } else { h = sym_hashes[r_symndx - symtab_hdr->sh_info]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; + } + if (h != NULL) + { /* Create the ifunc sections for static executables. If we never see an indirect function symbol nor we are building a static executable, those sections will be empty and @@ -1117,7 +1263,8 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info, (_("%B: relocation %s against STT_GNU_IFUNC " "symbol `%s' isn't handled by %s"), abfd, x86_64_elf_howto_table[r_type].name, - h->root.root.string, __FUNCTION__); + h != NULL ? h->root.root.string : "a local symbol", + __FUNCTION__); bfd_set_error (bfd_error_bad_value); return FALSE; @@ -2145,6 +2292,25 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) return TRUE; } +/* Allocate space in .plt, .got and associated reloc sections for + local dynamic relocs. */ + +static bfd_boolean +elf64_x86_64_allocate_local_dynrelocs (void **slot, void *inf) +{ + struct elf_link_hash_entry *h + = (struct elf_link_hash_entry *) *slot; + + if (h->type != STT_GNU_IFUNC + || !h->def_regular + || !h->ref_regular + || !h->forced_local + || h->root.type != bfd_link_hash_defined) + abort (); + + return elf64_x86_64_allocate_dynrelocs (h, inf); +} + /* Find any dynamic relocs that apply to read-only sections. */ static bfd_boolean @@ -2313,6 +2479,11 @@ elf64_x86_64_size_dynamic_sections (bfd *output_bfd ATTRIBUTE_UNUSED, elf_link_hash_traverse (&htab->elf, elf64_x86_64_allocate_dynrelocs, info); + /* Allocate .plt and .got entries, and space for local symbols. */ + htab_traverse (htab->loc_hash_table, + elf64_x86_64_allocate_local_dynrelocs, + info); + /* For every jump slot reserved in the sgotplt, reloc_count is incremented. However, when we reserve space for TLS descriptors, it's not incremented, so in order to compute the space reserved @@ -2630,7 +2801,21 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, sym = local_syms + r_symndx; sec = local_sections[r_symndx]; - relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + relocation = _bfd_elf_rela_local_sym (output_bfd, sym, + &sec, rel); + + /* Relocate against local STT_GNU_IFUNC symbol. */ + if (ELF64_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + { + h = elf64_x86_64_get_local_sym_hash (htab, input_bfd, + rel, FALSE); + if (h == NULL) + abort (); + + /* Set STT_GNU_IFUNC symbol value. */ + h->root.u.def.value = sym->st_value; + h->root.u.def.section = sec; + } } else { @@ -2710,8 +2895,8 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info, bfd_byte *loc; asection *sreloc; - /* Need a dynamic relocation get the the real - function address. */ + /* Need a dynamic relocation to get the real function + address. */ outrel.r_offset = _bfd_elf_section_offset (output_bfd, info, input_section, @@ -3941,14 +4126,31 @@ do_glob_dat: bfd_elf64_swap_reloca_out (output_bfd, &rela, loc); } - /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */ - if (strcmp (h->root.root.string, "_DYNAMIC") == 0 - || h == htab->elf.hgot) + /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. SYM may + be NULL for local symbols. */ + if (sym != NULL + && (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || h == htab->elf.hgot)) sym->st_shndx = SHN_ABS; return TRUE; } +/* Finish up local dynamic symbol handling. We set the contents of + various dynamic sections here. */ + +static bfd_boolean +elf64_x86_64_finish_local_dynamic_symbol (void **slot, void *inf) +{ + struct elf_link_hash_entry *h + = (struct elf_link_hash_entry *) *slot; + struct bfd_link_info *info + = (struct bfd_link_info *) inf; + + return elf64_x86_64_finish_dynamic_symbol (info->output_bfd, + info, h, NULL); +} + /* Used to decide how to sort relocs in an optimal manner for the dynamic linker, before writing them out. */ @@ -4139,6 +4341,11 @@ elf64_x86_64_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *inf elf_section_data (htab->sgot->output_section)->this_hdr.sh_entsize = GOT_ENTRY_SIZE; + /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols. */ + htab_traverse (htab->loc_hash_table, + elf64_x86_64_finish_local_dynamic_symbol, + info); + return TRUE; } @@ -4387,6 +4594,8 @@ static const struct bfd_elf_special_section #define bfd_elf64_bfd_link_hash_table_create \ elf64_x86_64_link_hash_table_create +#define bfd_elf64_bfd_link_hash_table_free \ + elf64_x86_64_link_hash_table_free #define bfd_elf64_bfd_reloc_type_lookup elf64_x86_64_reloc_type_lookup #define bfd_elf64_bfd_reloc_name_lookup \ elf64_x86_64_reloc_name_lookup diff --git a/gas/ChangeLog b/gas/ChangeLog index 4aa9d034812..5ddad8d5f16 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,11 @@ +2009-06-13 H.J. Lu + + PR ld/10269 + * config/tc-i386.c (md_apply_fix): Use TC_FORCE_RELOCATION + instead of generic_force_reloc. + + * config/tc-i386.h (TC_FORCE_RELOCATION): New. + 2009-06-11 Anthony Green * config/tc-moxie.c (md_chars_to_number): Define. diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c index faa638fbbf1..b0293d80367 100644 --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@ -7235,7 +7235,7 @@ md_apply_fix (fixP, valP, seg) if ((sym_seg == seg || (symbol_section_p (fixP->fx_addsy) && sym_seg != absolute_section)) - && !generic_force_reloc (fixP)) + && !TC_FORCE_RELOCATION (fixP)) { /* Yes, we add the values in twice. This is because bfd_install_relocation subtracts them out again. I think diff --git a/gas/config/tc-i386.h b/gas/config/tc-i386.h index bb4821a002f..b4809f7287c 100644 --- a/gas/config/tc-i386.h +++ b/gas/config/tc-i386.h @@ -138,6 +138,12 @@ extern int tc_i386_fix_adjustable (struct fix *); (OUTPUT_FLAVOR == bfd_target_elf_flavour) #endif +/* BSF_GNU_INDIRECT_FUNCTION symbols always need relocatoon. */ +#define TC_FORCE_RELOCATION(FIX) \ + ((symbol_get_bfdsym ((FIX)->fx_addsy)->flags \ + & BSF_GNU_INDIRECT_FUNCTION) \ + || generic_force_reloc (FIX)) + /* This expression evaluates to true if the relocation is for a local object for which we still want to do the relocation at runtime. False if we are willing to perform this relocation while building diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index bdf657f65fd..26a526dec12 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,21 @@ +2009-06-13 H.J. Lu + + PR ld/10269 + *: ld-ifunc/ifunc-1-local-x86.d: New. + *: ld-ifunc/ifunc-1-local-x86.s: Likewise. + *: ld-ifunc/ifunc-2-local-i386.d: Likewise. + *: ld-ifunc/ifunc-2-local-i386.s: Likewise. + *: ld-ifunc/ifunc-2-local-x86-64.d: Likewise. + *: ld-ifunc/ifunc-2-local-x86-64.s: Likewise. + *: ld-ifunc/ifunc-4-local-x86.d: Likewise. + *: ld-ifunc/ifunc-4-local-x86.s: Likewise. + *: ld-ifunc/ifunc-5-local-i386.s: Likewise. + *: ld-ifunc/ifunc-5-local-x86-64.s: Likewise. + *: ld-ifunc/ifunc-5a-local-i386.d: Likewise. + *: ld-ifunc/ifunc-5a-local-x86-64.d: Likewise. + *: ld-ifunc/ifunc-5b-local-i386.d: Likewise. + *: ld-ifunc/ifunc-5b-local-x86-64.d: Likewise. + 2009-06-03 H.J. Lu * ld-ifunc/ifunc-2-x86-64.d: Pass --64 to as and -melf_x86_64 to diff --git a/ld/testsuite/ld-ifunc/ifunc-1-local-x86.d b/ld/testsuite/ld-ifunc/ifunc-1-local-x86.d new file mode 100644 index 00000000000..ee043b1207b --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-1-local-x86.d @@ -0,0 +1,7 @@ +#ld: -shared +#objdump: -dw +#target: x86_64-*-* i?86-*-* + +#... +[ \t0-9a-f]+:[ \t0-9a-f]+call[ \t0-9a-fq]+<\*ABS\*@plt> +#pass diff --git a/ld/testsuite/ld-ifunc/ifunc-1-local-x86.s b/ld/testsuite/ld-ifunc/ifunc-1-local-x86.s new file mode 100644 index 00000000000..bd2b5bb2c8e --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-1-local-x86.s @@ -0,0 +1,13 @@ + .type foo, %gnu_indirect_function + .set __GI_foo, foo + .text + .type foo, @function +foo: + ret + .size foo, .-foo +.globl bar + .type bar, @function +bar: + call __GI_foo@PLT + ret + .size bar, .-bar diff --git a/ld/testsuite/ld-ifunc/ifunc-2-local-i386.d b/ld/testsuite/ld-ifunc/ifunc-2-local-i386.d new file mode 100644 index 00000000000..7dfc1fe3c4c --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-2-local-i386.d @@ -0,0 +1,8 @@ +#ld: -m elf_i386 -shared +#as: --32 +#objdump: -dw +#target: x86_64-*-* i?86-*-* + +#... +[ \t0-9a-f]+:[ \t0-9a-f]+call[ \t0-9a-f]+<\*ABS\*@plt> +#pass diff --git a/ld/testsuite/ld-ifunc/ifunc-2-local-i386.s b/ld/testsuite/ld-ifunc/ifunc-2-local-i386.s new file mode 100644 index 00000000000..4e0b6aefe76 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-2-local-i386.s @@ -0,0 +1,18 @@ + .type foo, %gnu_indirect_function + .set __GI_foo, foo + .text + .type foo, @function +foo: + ret + .size foo, .-foo +.globl bar + .type bar, @function +bar: + call .L6 +.L6: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+[.-.L6], %ebx + call __GI_foo + leal __GI_foo@GOTOFF(%ebx), %eax + ret + .size bar, .-bar diff --git a/ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.d b/ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.d new file mode 100644 index 00000000000..fa8710254d0 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.d @@ -0,0 +1,9 @@ +#as: --64 +#ld: -shared -melf_x86_64 +#objdump: -dw +#target: x86_64-*-* + +#... +[ \t0-9a-f]+:[ \t0-9a-f]+call[ \t0-9a-fq]+<\*ABS\*@plt> +[ \t0-9a-f]+:[ \t0-9a-f]+lea[ \t]+.*\(%rip\),%rax.*[ \t0-9a-fq]+<\*ABS\*@plt> +#pass diff --git a/ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.s b/ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.s new file mode 100644 index 00000000000..4137ff1e10d --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-2-local-x86-64.s @@ -0,0 +1,17 @@ + .type foo, %gnu_indirect_function + .global __GI_foo + .hidden __GI_foo + .set __GI_foo, foo + .text +.globl foo + .type foo, @function +foo: + ret + .size foo, .-foo +.globl bar + .type bar, @function +bar: + call __GI_foo + leaq __GI_foo(%rip), %rax + ret + .size bar, .-bar diff --git a/ld/testsuite/ld-ifunc/ifunc-4-local-x86.d b/ld/testsuite/ld-ifunc/ifunc-4-local-x86.d new file mode 100644 index 00000000000..5fe66e09e11 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-4-local-x86.d @@ -0,0 +1,7 @@ +#ld: +#readelf: -r --wide +#target: x86_64-*-* i?86-*-* + +#... +[0-9a-f]+[ ]+[0-9a-f]+[ ]+R_[_0-9A-Z]+_IRELATIVE[ ]*[0-9a-f]* +#pass diff --git a/ld/testsuite/ld-ifunc/ifunc-4-local-x86.s b/ld/testsuite/ld-ifunc/ifunc-4-local-x86.s new file mode 100644 index 00000000000..4f72d7fb057 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-4-local-x86.s @@ -0,0 +1,18 @@ + .text + .type foo, %gnu_indirect_function + .type foo, @function +foo: + ret + .size foo, .-foo + .type start,"function" + .global start +start: + .type _start,"function" + .global _start +_start: + .type __start,"function" + .global __start +__start: + .type __start,"function" + call foo + movl $foo,%eax diff --git a/ld/testsuite/ld-ifunc/ifunc-5-local-i386.s b/ld/testsuite/ld-ifunc/ifunc-5-local-i386.s new file mode 100644 index 00000000000..d7bb5cf3fee --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-5-local-i386.s @@ -0,0 +1,22 @@ + .text + .type foo, %gnu_indirect_function + .type foo, @function +foo: + ret + .size foo, .-foo + .type start,"function" + .global start +start: + .type _start,"function" + .global _start +_start: + .type __start,"function" + .global __start +__start: + .type __start,"function" + call .L6 +.L6: + popl %ebx + addl $_GLOBAL_OFFSET_TABLE_+[.-.L6], %ebx + call foo@PLT + leal foo@GOT(%ebx), %eax diff --git a/ld/testsuite/ld-ifunc/ifunc-5-local-x86-64.s b/ld/testsuite/ld-ifunc/ifunc-5-local-x86-64.s new file mode 100644 index 00000000000..5469747727f --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-5-local-x86-64.s @@ -0,0 +1,18 @@ + .text + .type foo, %gnu_indirect_function + .type foo, @function +foo: + ret + .size foo, .-foo + .type start,"function" + .global start +start: + .type _start,"function" + .global _start +_start: + .type __start,"function" + .global __start +__start: + .type __start,"function" + call foo@PLT + movq foo@GOTPCREL(%rip), %rax diff --git a/ld/testsuite/ld-ifunc/ifunc-5a-local-i386.d b/ld/testsuite/ld-ifunc/ifunc-5a-local-i386.d new file mode 100644 index 00000000000..88814f7f0e0 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-5a-local-i386.d @@ -0,0 +1,9 @@ +#source: ifunc-5-local-i386.s +#ld: -m elf_i386 +#as: --32 +#readelf: -r --wide +#target: x86_64-*-* i?86-*-* + +Relocation section '.rel.plt' at .* +[ ]+Offset[ ]+Info[ ]+Type[ ]+.* +[0-9a-f]+[ ]+[0-9a-f]+[ ]+R_386_IRELATIVE[ ]* diff --git a/ld/testsuite/ld-ifunc/ifunc-5a-local-x86-64.d b/ld/testsuite/ld-ifunc/ifunc-5a-local-x86-64.d new file mode 100644 index 00000000000..73a822dbf49 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-5a-local-x86-64.d @@ -0,0 +1,9 @@ +#source: ifunc-5-local-x86-64.s +#as: --64 +#ld: -melf_x86_64 +#readelf: -r --wide +#target: x86_64-*-* + +Relocation section '.rela.plt' at .* +[ ]+Offset[ ]+Info[ ]+Type[ ]+.* +[0-9a-f]+[ ]+[0-9a-f]+[ ]+R_X86_64_IRELATIVE[ ]+[0-9a-f]* diff --git a/ld/testsuite/ld-ifunc/ifunc-5b-local-i386.d b/ld/testsuite/ld-ifunc/ifunc-5b-local-i386.d new file mode 100644 index 00000000000..75cb7d9da59 --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-5b-local-i386.d @@ -0,0 +1,9 @@ +#source: ifunc-5-local-i386.s +#ld: -shared -m elf_i386 -z nocombreloc +#as: --32 +#readelf: -r --wide +#target: x86_64-*-* i?86-*-* + +Relocation section '.rel.plt' at .* +[ ]+Offset[ ]+Info[ ]+Type[ ]+.* +[0-9a-f]+[ ]+[0-9a-f]+[ ]+R_386_IRELATIVE[ ]* diff --git a/ld/testsuite/ld-ifunc/ifunc-5b-local-x86-64.d b/ld/testsuite/ld-ifunc/ifunc-5b-local-x86-64.d new file mode 100644 index 00000000000..3913dd60c9c --- /dev/null +++ b/ld/testsuite/ld-ifunc/ifunc-5b-local-x86-64.d @@ -0,0 +1,9 @@ +#source: ifunc-5-local-x86-64.s +#as: --64 +#ld: -melf_x86_64 -shared -z nocombreloc +#readelf: -r --wide +#target: x86_64-*-* + +Relocation section '.rela.plt' at .* +[ ]+Offset[ ]+Info[ ]+Type[ ]+.* +[0-9a-f]+[ ]+[0-9a-f]+[ ]+R_X86_64_IRELATIVE[ ]+[0-9a-f]* -- 2.34.1