From 932636bc5cf27cc5d9a2d633381d4f5504317c81 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 8 Feb 2010 20:28:43 +0000 Subject: [PATCH] include/ * elf/sparc.h (R_SPARC_JMP_IREL, R_SPARC_IRELATIVE): Define. bfd/ * reloc.c (BFD_RELOC_SPARC_JMP_IREL): New. (BFD_RELOC_SPARC_IRELATIVE): Likewise. * bfd-in2.h: Regenerate. * libbfd.h: Regenerate. * elfxx-sparc.h (_bfd_sparc_elf_link_hash_table): Add loc_hash_table and loc_hash_memory. (_bfd_sparc_elf_link_hash_table_free): Declare. * elf32-sparc.c (elf32_sparc_add_symbol_hook): New. (elf_backend_add_symbol_hook, elf_backend_post_process_headers, bfd_elf32_bfd_link_hash_table_free): Define. * elf64-sparc.c (elf64_sparc_add_symbol_hook): Set has_ifunc_symbols if STT_GNU_IFUNC. (bfd_elf64_bfd_link_hash_table_free): Define. (elf_backend_post_process_headers): Define always. * elfxx-sparc.c (sparc_jmp_irel_howto, sparc_irelative_howto): New. (sparc_reloc_map): Add entries for new IFUNC relocs. (_bfd_sparc_elf_reloc_type_lookup): Handle new IFUNC relocs. (_bfd_sparc_elf_info_to_howto_ptr): Likewise. (elf_sparc_local_htab_hash, elf_sparc_local_htab_eq, elf_sparc_get_local_sym_hash): New. (_bfd_sparc_elf_create_dynamic_sections): Move PLT ops initialization from here... (_bfd_sparc_elf_link_hash_table_create): ... to here. Allocate local hash table. (_bfd_sparc_elf_link_hash_table_free): New. (create_ifunc_sections): New. (_bfd_sparc_elf_check_relocs): Unconditionally assign htab->elf.dynobj and call create_ifunc_sections(). For local STT_GNU_IFUNC symbols cons up a fake local hash table entry for it. Unconditionally add a PLT refcount for STT_GNU_IFUNC symbols when h->def_regular. Count dyn relocs for ifunc. (_bfd_sparc_elf_adjust_dynamic_symbol): Handle ifunc. (allocate_dynrelocs): Unconditionally emit a PLT entry when STT_GNU_IFUNC and h->def_regular. Count GOT dyn relocs for ifunc. (allocate_local_dynrelocs): New function. (_bfd_sparc_elf_size_dynamic_sections): Invoke it over the local hash table. Emit dynamic relocs to irelplt when not shared. Treat iplt like splt. (_bfd_sparc_elf_relocate_section): Handle ifunc relocations by hand. (_bfd_sparc_elf_finish_dynamic_symbol): Adjust for non-dynamic ifunc plt in iplt/irelplt. ld/testsuite/ * ld-ifunc/ifunc.exp: Run for sparc. --- bfd/ChangeLog | 43 ++++ bfd/bfd-in2.h | 2 + bfd/elf32-sparc.c | 22 ++ bfd/elf64-sparc.c | 9 +- bfd/elfxx-sparc.c | 538 +++++++++++++++++++++++++++++++++++----- bfd/elfxx-sparc.h | 6 + bfd/libbfd.h | 2 + bfd/reloc.c | 4 + include/ChangeLog | 4 + include/elf/sparc.h | 2 + ld/testsuite/ChangeLog | 4 + ld/testsuite/ld-ifunc/ifunc.exp | 7 +- 12 files changed, 579 insertions(+), 64 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 5248d09ec..966396d36 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -32,6 +32,49 @@ * elfxx-sparc.c (_bfd_sparc_elf_adjust_dynamic_symbol): Handle nocopyreloc. + * reloc.c (BFD_RELOC_SPARC_JMP_IREL): New. + (BFD_RELOC_SPARC_IRELATIVE): Likewise. + * bfd-in2.h: Regenerate. + * libbfd.h: Regenerate. + + * elfxx-sparc.h (_bfd_sparc_elf_link_hash_table): Add loc_hash_table + and loc_hash_memory. + (_bfd_sparc_elf_link_hash_table_free): Declare. + * elf32-sparc.c (elf32_sparc_add_symbol_hook): New. + (elf_backend_add_symbol_hook, elf_backend_post_process_headers, + bfd_elf32_bfd_link_hash_table_free): Define. + * elf64-sparc.c (elf64_sparc_add_symbol_hook): Set + has_ifunc_symbols if STT_GNU_IFUNC. + (bfd_elf64_bfd_link_hash_table_free): Define. + (elf_backend_post_process_headers): Define always. + * elfxx-sparc.c (sparc_jmp_irel_howto, sparc_irelative_howto): New. + (sparc_reloc_map): Add entries for new IFUNC relocs. + (_bfd_sparc_elf_reloc_type_lookup): Handle new IFUNC relocs. + (_bfd_sparc_elf_info_to_howto_ptr): Likewise. + (elf_sparc_local_htab_hash, elf_sparc_local_htab_eq, + elf_sparc_get_local_sym_hash): New. + (_bfd_sparc_elf_create_dynamic_sections): Move PLT ops initialization + from here... + (_bfd_sparc_elf_link_hash_table_create): ... to here. Allocate + local hash table. + (_bfd_sparc_elf_link_hash_table_free): New. + (create_ifunc_sections): New. + (_bfd_sparc_elf_check_relocs): Unconditionally assign htab->elf.dynobj + and call create_ifunc_sections(). For local STT_GNU_IFUNC symbols + cons up a fake local hash table entry for it. Unconditionally add + a PLT refcount for STT_GNU_IFUNC symbols when h->def_regular. Count + dyn relocs for ifunc. + (_bfd_sparc_elf_adjust_dynamic_symbol): Handle ifunc. + (allocate_dynrelocs): Unconditionally emit a PLT entry when + STT_GNU_IFUNC and h->def_regular. Count GOT dyn relocs for ifunc. + (allocate_local_dynrelocs): New function. + (_bfd_sparc_elf_size_dynamic_sections): Invoke it over the local hash + table. Emit dynamic relocs to irelplt when not shared. Treat iplt + like splt. + (_bfd_sparc_elf_relocate_section): Handle ifunc relocations by hand. + (_bfd_sparc_elf_finish_dynamic_symbol): Adjust for non-dynamic ifunc + plt in iplt/irelplt. + 2010-02-08 Richard Sandiford * xcofflink.c (_bfd_xcoff_bfd_final_link): When calculating diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 1a0a0eaba..c90ac2db2 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -2496,6 +2496,8 @@ relocation types already defined. */ BFD_RELOC_SPARC_GOTDATA_OP_HIX22, BFD_RELOC_SPARC_GOTDATA_OP_LOX10, BFD_RELOC_SPARC_GOTDATA_OP, + BFD_RELOC_SPARC_JMP_IREL, + BFD_RELOC_SPARC_IRELATIVE, /* I think these are specific to SPARC a.out (e.g., Sun 4). */ BFD_RELOC_SPARC_BASE13, diff --git a/bfd/elf32-sparc.c b/bfd/elf32-sparc.c index ae0ac2b53..ab4f70374 100644 --- a/bfd/elf32-sparc.c +++ b/bfd/elf32-sparc.c @@ -167,6 +167,23 @@ elf32_sparc_reloc_type_class (const Elf_Internal_Rela *rela) } } +/* Hook called by the linker routine which adds symbols from an object + file. */ + +static bfd_boolean +elf32_sparc_add_symbol_hook (bfd * abfd ATTRIBUTE_UNUSED, + struct bfd_link_info * info ATTRIBUTE_UNUSED, + Elf_Internal_Sym * sym, + const char ** namep ATTRIBUTE_UNUSED, + flagword * flagsp ATTRIBUTE_UNUSED, + asection ** secp ATTRIBUTE_UNUSED, + bfd_vma * valp ATTRIBUTE_UNUSED) +{ + if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE; + return TRUE; +} + #define TARGET_BIG_SYM bfd_elf32_sparc_vec #define TARGET_BIG_NAME "elf32-sparc" #define ELF_ARCH bfd_arch_sparc @@ -188,6 +205,8 @@ elf32_sparc_reloc_type_class (const Elf_Internal_Rela *rela) _bfd_sparc_elf_reloc_name_lookup #define bfd_elf32_bfd_link_hash_table_create \ _bfd_sparc_elf_link_hash_table_create +#define bfd_elf32_bfd_link_hash_table_free \ + _bfd_sparc_elf_link_hash_table_free #define bfd_elf32_bfd_relax_section _bfd_sparc_elf_relax_section #define bfd_elf32_new_section_hook _bfd_sparc_elf_new_section_hook #define elf_backend_copy_indirect_symbol \ @@ -220,6 +239,9 @@ elf32_sparc_reloc_type_class (const Elf_Internal_Rela *rela) #define elf_backend_got_header_size 4 #define elf_backend_rela_normal 1 +#define elf_backend_post_process_headers _bfd_elf_set_osabi +#define elf_backend_add_symbol_hook elf32_sparc_add_symbol_hook + #include "elf32-target.h" /* A wrapper around _bfd_sparc_elf_link_hash_table_create that identifies diff --git a/bfd/elf64-sparc.c b/bfd/elf64-sparc.c index 18bdfb4b4..a1bde2090 100644 --- a/bfd/elf64-sparc.c +++ b/bfd/elf64-sparc.c @@ -424,6 +424,9 @@ elf64_sparc_add_symbol_hook (bfd *abfd, struct bfd_link_info *info, { static const char *const stt_types[] = { "NOTYPE", "OBJECT", "FUNCTION" }; + if (ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + elf_tdata (info->output_bfd)->has_ifunc_symbols = TRUE; + if (ELF_ST_TYPE (sym->st_info) == STT_REGISTER) { int reg; @@ -856,6 +859,8 @@ const struct elf_size_info elf64_sparc_size_info = _bfd_sparc_elf_plt_sym_val #define bfd_elf64_bfd_link_hash_table_create \ _bfd_sparc_elf_link_hash_table_create +#define bfd_elf64_bfd_link_hash_table_free \ + _bfd_sparc_elf_link_hash_table_free #define elf_info_to_howto \ _bfd_sparc_elf_info_to_howto #define elf_backend_copy_indirect_symbol \ @@ -910,6 +915,8 @@ const struct elf_size_info elf64_sparc_size_info = /* Section 5.2.4 of the ABI specifies a 256-byte boundary for the table. */ #define elf_backend_plt_alignment 8 +#define elf_backend_post_process_headers _bfd_elf_set_osabi + #include "elf64-target.h" /* FreeBSD support */ @@ -920,8 +927,6 @@ const struct elf_size_info elf64_sparc_size_info = #undef ELF_OSABI #define ELF_OSABI ELFOSABI_FREEBSD -#undef elf_backend_post_process_headers -#define elf_backend_post_process_headers _bfd_elf_set_osabi #undef elf64_bed #define elf64_bed elf64_sparc_fbsd_bed diff --git a/bfd/elfxx-sparc.c b/bfd/elfxx-sparc.c index e0d125fc6..bc36920db 100644 --- a/bfd/elfxx-sparc.c +++ b/bfd/elfxx-sparc.c @@ -31,6 +31,8 @@ #include "opcode/sparc.h" #include "elfxx-sparc.h" #include "elf-vxworks.h" +#include "objalloc.h" +#include "hashtab.h" /* In case we're on a 32-bit machine, construct a 64-bit "-1" value. */ #define MINUS_ONE (~ (bfd_vma) 0) @@ -265,6 +267,10 @@ static reloc_howto_type _bfd_sparc_elf_howto_table[] = HOWTO(R_SPARC_GOTDATA_OP_LOX10,0,2,0,FALSE,0,complain_overflow_dont, sparc_elf_lox10_reloc, "R_SPARC_GOTDATA_OP_LOX10",FALSE,0,0x000003ff, FALSE), HOWTO(R_SPARC_GOTDATA_OP,0,0, 0,FALSE,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_GOTDATA_OP",FALSE,0,0x00000000,TRUE), }; +static reloc_howto_type sparc_jmp_irel_howto = + HOWTO(R_SPARC_JMP_IREL, 0,0,00,FALSE,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_JMP_IREL",FALSE,0,0x00000000,TRUE); +static reloc_howto_type sparc_irelative_howto = + HOWTO(R_SPARC_IRELATIVE, 0,0,00,FALSE,0,complain_overflow_dont, bfd_elf_generic_reloc, "R_SPARC_IRELATIVE",FALSE,0,0x00000000,TRUE); static reloc_howto_type sparc_vtinherit_howto = HOWTO (R_SPARC_GNU_VTINHERIT, 0,2,0,FALSE,0,complain_overflow_dont, NULL, "R_SPARC_GNU_VTINHERIT", FALSE,0, 0, FALSE); static reloc_howto_type sparc_vtentry_howto = @@ -360,6 +366,8 @@ static const struct elf_reloc_map sparc_reloc_map[] = { BFD_RELOC_SPARC_GOTDATA_OP_LOX10, R_SPARC_GOTDATA_OP_LOX10 }, { BFD_RELOC_SPARC_GOTDATA_OP, R_SPARC_GOTDATA_OP }, { BFD_RELOC_SPARC_REGISTER, R_SPARC_REGISTER }, + { BFD_RELOC_SPARC_JMP_IREL, R_SPARC_JMP_IREL }, + { BFD_RELOC_SPARC_IRELATIVE, R_SPARC_IRELATIVE }, { BFD_RELOC_VTABLE_INHERIT, R_SPARC_GNU_VTINHERIT }, { BFD_RELOC_VTABLE_ENTRY, R_SPARC_GNU_VTENTRY }, { BFD_RELOC_SPARC_REV32, R_SPARC_REV32 }, @@ -373,6 +381,12 @@ _bfd_sparc_elf_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, switch (code) { + case BFD_RELOC_SPARC_JMP_IREL: + return &sparc_jmp_irel_howto; + + case BFD_RELOC_SPARC_IRELATIVE: + return &sparc_irelative_howto; + case BFD_RELOC_VTABLE_INHERIT: return &sparc_vtinherit_howto; @@ -425,6 +439,12 @@ _bfd_sparc_elf_info_to_howto_ptr (unsigned int r_type) { switch (r_type) { + case R_SPARC_JMP_IREL: + return &sparc_jmp_irel_howto; + + case R_SPARC_IRELATIVE: + return &sparc_irelative_howto; + case R_SPARC_GNU_VTINHERIT: return &sparc_vtinherit_howto; @@ -831,6 +851,78 @@ link_hash_newfunc (struct bfd_hash_entry *entry, #define ELF32_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" #define ELF64_DYNAMIC_INTERPRETER "/usr/lib/sparcv9/ld.so.1" +/* 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_sparc_local_htab_hash (const void *ptr) +{ + struct elf_link_hash_entry *h + = (struct elf_link_hash_entry *) ptr; + return ELF_LOCAL_SYMBOL_HASH (h->indx, h->dynstr_index); +} + +/* Compare local hash entries. */ + +static int +elf_sparc_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_sparc_get_local_sym_hash (struct _bfd_sparc_elf_link_hash_table *htab, + bfd *abfd, const Elf_Internal_Rela *rel, + bfd_boolean create) +{ + struct _bfd_sparc_elf_link_hash_entry e, *ret; + asection *sec = abfd->sections; + unsigned long r_symndx; + hashval_t h; + void **slot; + + r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info); + h = ELF_LOCAL_SYMBOL_HASH (sec->id, r_symndx); + + e.elf.indx = sec->id; + e.elf.dynstr_index = r_symndx; + slot = htab_find_slot_with_hash (htab->loc_hash_table, &e, h, + create ? INSERT : NO_INSERT); + + if (!slot) + return NULL; + + if (*slot) + { + ret = (struct _bfd_sparc_elf_link_hash_entry *) *slot; + return &ret->elf; + } + + ret = (struct _bfd_sparc_elf_link_hash_entry *) + objalloc_alloc ((struct objalloc *) htab->loc_hash_memory, + sizeof (struct _bfd_sparc_elf_link_hash_entry)); + if (ret) + { + memset (ret, 0, sizeof (*ret)); + ret->elf.indx = sec->id; + ret->elf.dynstr_index = r_symndx; + ret->elf.dynindx = -1; + ret->elf.plt.offset = (bfd_vma) -1; + ret->elf.got.offset = (bfd_vma) -1; + *slot = ret; + } + return &ret->elf; +} + /* Create a SPARC ELF linker hash table. */ struct bfd_link_hash_table * @@ -857,6 +949,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd) ret->bytes_per_rela = sizeof (Elf64_External_Rela); ret->dynamic_interpreter = ELF64_DYNAMIC_INTERPRETER; ret->dynamic_interpreter_size = sizeof ELF64_DYNAMIC_INTERPRETER; + + ret->build_plt_entry = sparc64_plt_entry_build; + ret->plt_header_size = PLT64_HEADER_SIZE; + ret->plt_entry_size = PLT64_ENTRY_SIZE; } else { @@ -872,6 +968,10 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd) ret->bytes_per_rela = sizeof (Elf32_External_Rela); ret->dynamic_interpreter = ELF32_DYNAMIC_INTERPRETER; ret->dynamic_interpreter_size = sizeof ELF32_DYNAMIC_INTERPRETER; + + ret->build_plt_entry = sparc32_plt_entry_build; + ret->plt_header_size = PLT32_HEADER_SIZE; + ret->plt_entry_size = PLT32_ENTRY_SIZE; } if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd, link_hash_newfunc, @@ -882,9 +982,35 @@ _bfd_sparc_elf_link_hash_table_create (bfd *abfd) return NULL; } + ret->loc_hash_table = htab_try_create (1024, + elf_sparc_local_htab_hash, + elf_sparc_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 a SPARC ELF linker hash table. */ + +void +_bfd_sparc_elf_link_hash_table_free (struct bfd_link_hash_table *hash) +{ + struct _bfd_sparc_elf_link_hash_table *htab + = (struct _bfd_sparc_elf_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 .plt, .rela.plt, .got, .rela.got, .dynbss, and .rela.bss sections in DYNOBJ, and set up shortcuts to them in our hash table. */ @@ -924,21 +1050,6 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj, = 4 * ARRAY_SIZE (sparc_vxworks_exec_plt_entry); } } - else - { - if (ABI_64_P (dynobj)) - { - htab->build_plt_entry = sparc64_plt_entry_build; - htab->plt_header_size = PLT64_HEADER_SIZE; - htab->plt_entry_size = PLT64_ENTRY_SIZE; - } - else - { - htab->build_plt_entry = sparc32_plt_entry_build; - htab->plt_header_size = PLT32_HEADER_SIZE; - htab->plt_entry_size = PLT32_ENTRY_SIZE; - } - } if (!htab->elf.splt || !htab->elf.srelplt || !htab->sdynbss || (!info->shared && !htab->srelbss)) @@ -947,6 +1058,37 @@ _bfd_sparc_elf_create_dynamic_sections (bfd *dynobj, return TRUE; } +static bfd_boolean +create_ifunc_sections (bfd *abfd, struct bfd_link_info *info) +{ + const struct elf_backend_data *bed = get_elf_backend_data (abfd); + struct elf_link_hash_table *htab = elf_hash_table (info); + flagword flags, pltflags; + asection *s; + + if (htab->irelifunc != NULL || htab->iplt != NULL) + return TRUE; + + flags = bed->dynamic_sec_flags; + pltflags = flags | SEC_ALLOC | SEC_CODE | SEC_LOAD; + + s = bfd_make_section_with_flags (abfd, ".iplt", pltflags); + if (s == NULL + || ! bfd_set_section_alignment (abfd, s, bed->plt_alignment)) + return FALSE; + htab->iplt = s; + + s = bfd_make_section_with_flags (abfd, ".rela.iplt", + flags | SEC_READONLY); + if (s == NULL + || ! bfd_set_section_alignment (abfd, s, + bed->s->log_file_align)) + return FALSE; + htab->irelplt = s; + + return TRUE; +} + /* Copy the extra info we tack onto an elf_link_hash_entry. */ void @@ -1074,12 +1216,18 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, BFD_ASSERT (is_sparc_elf (abfd) || num_relocs == 0); + if (htab->elf.dynobj == NULL) + htab->elf.dynobj = abfd; + if (!create_ifunc_sections (htab->elf.dynobj, info)) + return FALSE; + rel_end = relocs + num_relocs; for (rel = relocs; rel < rel_end; rel++) { unsigned int r_type; unsigned long r_symndx; struct elf_link_hash_entry *h; + Elf_Internal_Sym *isym; r_symndx = SPARC_ELF_R_SYMNDX (htab, rel->r_info); r_type = SPARC_ELF_R_TYPE (rel->r_info); @@ -1091,8 +1239,33 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, return FALSE; } + isym = NULL; if (r_symndx < symtab_hdr->sh_info) - h = NULL; + { + /* A local symbol. */ + isym = bfd_sym_from_r_symndx (&htab->sym_cache, + abfd, r_symndx); + if (isym == NULL) + return FALSE; + + /* Check relocation against local STT_GNU_IFUNC symbol. */ + if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + { + h = elf_sparc_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]; @@ -1101,6 +1274,12 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, h = (struct elf_link_hash_entry *) h->root.u.i.link; } + if (h && h->type == STT_GNU_IFUNC) + { + if (h->def_regular) + h->plt.refcount += 1; + } + /* Compatibility with old R_SPARC_REV32 reloc conflicting with R_SPARC_TLS_GD_HI22. */ if (! ABI_64_P (abfd) && ! checked_tlsgd) @@ -1239,8 +1418,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, if (htab->elf.sgot == NULL) { - if (htab->elf.dynobj == NULL) - htab->elf.dynobj = abfd; if (!_bfd_elf_create_got_section (htab->elf.dynobj, info)) return FALSE; } @@ -1403,7 +1580,10 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, && (sec->flags & SEC_ALLOC) != 0 && h != NULL && (h->root.type == bfd_link_hash_defweak - || !h->def_regular))) + || !h->def_regular)) + || (!info->shared + && h != NULL + && h->type == STT_GNU_IFUNC)) { struct _bfd_sparc_elf_dyn_relocs *p; struct _bfd_sparc_elf_dyn_relocs **head; @@ -1413,9 +1593,6 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, section in dynobj and make room for the reloc. */ if (sreloc == NULL) { - if (htab->elf.dynobj == NULL) - htab->elf.dynobj = abfd; - sreloc = _bfd_elf_make_dynamic_reloc_section (sec, htab->elf.dynobj, htab->word_align_power, abfd, /*rela?*/ TRUE); @@ -1435,13 +1612,8 @@ _bfd_sparc_elf_check_relocs (bfd *abfd, struct bfd_link_info *info, easily. Oh well. */ asection *s; void *vpp; - Elf_Internal_Sym *isym; - - isym = bfd_sym_from_r_symndx (&htab->sym_cache, - abfd, r_symndx); - if (isym == NULL) - return FALSE; + BFD_ASSERT (isym != NULL); s = bfd_section_from_elf_index (abfd, isym->st_shndx); if (s == NULL) s = sec; @@ -1684,6 +1856,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info, /* Make sure we know what is going on here. */ BFD_ASSERT (htab->elf.dynobj != NULL && (h->needs_plt + || h->type == STT_GNU_IFUNC || h->u.weakdef != NULL || (h->def_dynamic && h->ref_regular @@ -1697,6 +1870,7 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info, some of their functions as STT_NOTYPE when they really should be STT_FUNC. */ if (h->type == STT_FUNC + || h->type == STT_GNU_IFUNC || h->needs_plt || (h->type == STT_NOTYPE && (h->root.type == bfd_link_hash_defined @@ -1704,9 +1878,10 @@ _bfd_sparc_elf_adjust_dynamic_symbol (struct bfd_link_info *info, && (h->root.u.def.section->flags & SEC_CODE) != 0)) { if (h->plt.refcount <= 0 - || SYMBOL_CALLS_LOCAL (info, h) - || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT - && h->root.type == bfd_link_hash_undefweak)) + || (h->type != STT_GNU_IFUNC + && (SYMBOL_CALLS_LOCAL (info, h) + || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT + && h->root.type == bfd_link_hash_undefweak)))) { /* This case can occur if we saw a WPLT30 reloc in an input file, but the symbol was never referred to by a dynamic @@ -1828,8 +2003,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) htab = _bfd_sparc_elf_hash_table (info); BFD_ASSERT (htab != NULL); - if (htab->elf.dynamic_sections_created - && h->plt.refcount > 0) + if ((htab->elf.dynamic_sections_created + && h->plt.refcount > 0) + || (h->type == STT_GNU_IFUNC + && h->def_regular)) { /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ @@ -1840,10 +2017,14 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) return FALSE; } - if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h)) + if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, info->shared, h) + || h->type == STT_GNU_IFUNC) { asection *s = htab->elf.splt; + if (s == NULL) + s = htab->elf.iplt; + /* Allocate room for the header. */ if (s->size == 0) { @@ -1892,7 +2073,10 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) s->size += htab->plt_entry_size; /* We also need to make an entry in the .rela.plt section. */ - htab->elf.srelplt->size += SPARC_ELF_RELA_BYTES (htab); + if (s == htab->elf.splt) + htab->elf.srelplt->size += SPARC_ELF_RELA_BYTES (htab); + else + htab->elf.irelplt->size += SPARC_ELF_RELA_BYTES (htab); if (htab->is_vxworks) { @@ -1949,7 +2133,8 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) R_SPARC_TLS_GD_{HI22,LO10} needs one if local symbol and two if global. */ if ((tls_type == GOT_TLS_GD && h->dynindx == -1) - || tls_type == GOT_TLS_IE) + || tls_type == GOT_TLS_IE + || h->type == STT_GNU_IFUNC) htab->elf.srelgot->size += SPARC_ELF_RELA_BYTES (htab); else if (tls_type == GOT_TLS_GD) htab->elf.srelgot->size += 2 * SPARC_ELF_RELA_BYTES (htab); @@ -2060,6 +2245,25 @@ allocate_dynrelocs (struct elf_link_hash_entry *h, PTR inf) return TRUE; } +/* Allocate space in .plt, .got and associated reloc sections for + local dynamic relocs. */ + +static bfd_boolean +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 allocate_dynrelocs (h, inf); +} + /* Find any dynamic relocs that apply to read-only sections. */ static bfd_boolean @@ -2172,6 +2376,8 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd, else if (p->count != 0) { srel = elf_section_data (p->sec)->sreloc; + if (!htab->elf.dynamic_sections_created) + srel = htab->elf.irelplt; srel->size += p->count * SPARC_ELF_RELA_BYTES (htab); if ((p->sec->output_section->flags & SEC_READONLY) != 0) info->flags |= DF_TEXTREL; @@ -2222,6 +2428,9 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd, sym dynamic relocs. */ elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info); + /* Allocate .plt and .got entries, and space for local symbols. */ + htab_traverse (htab->loc_hash_table, allocate_local_dynrelocs, info); + if (! ABI_64_P (output_bfd) && !htab->is_vxworks && elf_hash_table (info)->dynamic_sections_created) @@ -2251,6 +2460,7 @@ _bfd_sparc_elf_size_dynamic_sections (bfd *output_bfd, if (s == htab->elf.splt || s == htab->elf.sgot || s == htab->sdynbss + || s == htab->elf.iplt || s == htab->elf.sgotplt) { /* Strip this section if we don't need it; see the @@ -2544,6 +2754,20 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, sym = local_syms + r_symndx; sec = local_sections[r_symndx]; relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + + if (!info->relocatable + && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + { + /* Relocate against local STT_GNU_IFUNC symbol. */ + h = elf_sparc_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 { @@ -2580,6 +2804,111 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, if (info->relocatable) continue; + if (h != NULL + && h->type == STT_GNU_IFUNC + && h->def_regular) + { + asection *plt_sec; + const char *name; + + if ((input_section->flags & SEC_ALLOC) == 0 + || h->plt.offset == (bfd_vma) -1) + abort (); + + plt_sec = htab->elf.splt; + if (! plt_sec) + plt_sec =htab->elf.iplt; + + switch (r_type) + { + case R_SPARC_GOTDATA_HIX22: + case R_SPARC_GOTDATA_LOX10: + case R_SPARC_GOTDATA_OP_HIX22: + case R_SPARC_GOTDATA_OP_LOX10: + case R_SPARC_GOT10: + case R_SPARC_GOT13: + case R_SPARC_GOT22: + if (htab->elf.sgot == NULL) + abort (); + off = h->got.offset; + if (off == (bfd_vma) -1) + abort(); + relocation = htab->elf.sgot->output_offset + off - got_base; + goto do_relocation; + + case R_SPARC_WPLT30: + case R_SPARC_WDISP30: + relocation = (plt_sec->output_section->vma + + plt_sec->output_offset + h->plt.offset); + goto do_relocation; + + case R_SPARC_32: + case R_SPARC_64: + if (info->shared && h->non_got_ref) + { + Elf_Internal_Rela outrel; + bfd_vma offset; + + offset = _bfd_elf_section_offset (output_bfd, info, + input_section, + rel->r_offset); + if (offset == (bfd_vma) -1 + || offset == (bfd_vma) -2) + abort(); + + outrel.r_offset = (input_section->output_section->vma + + input_section->output_offset + + offset); + + if (h->dynindx == -1 + || h->forced_local + || info->executable) + { + outrel.r_info = SPARC_ELF_R_INFO (htab, NULL, + 0, R_SPARC_IRELATIVE); + outrel.r_addend = relocation + rel->r_addend; + } + else + { + if (h->dynindx == -1) + abort(); + outrel.r_info = SPARC_ELF_R_INFO (htab, rel, h->dynindx, r_type); + outrel.r_addend = rel->r_addend; + } + + sparc_elf_append_rela (output_bfd, sreloc, &outrel); + continue; + } + + relocation = (plt_sec->output_section->vma + + plt_sec->output_offset + h->plt.offset); + goto do_relocation; + + case R_SPARC_HI22: + case R_SPARC_LO10: + /* We should only see such relocs in static links. */ + if (info->shared) + abort(); + relocation = (plt_sec->output_section->vma + + plt_sec->output_offset + h->plt.offset); + goto do_relocation; + + default: + if (h->root.root.string) + name = h->root.root.string; + else + name = bfd_elf_sym_name (input_bfd, symtab_hdr, sym, + NULL); + (*_bfd_error_handler) + (_("%B: relocation %s against STT_GNU_IFUNC " + "symbol `%s' isn't handled by %s"), input_bfd, + _bfd_sparc_elf_howto_table[r_type].name, + name, __FUNCTION__); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + } + switch (r_type) { case R_SPARC_GOTDATA_HIX22: @@ -3496,10 +3825,12 @@ _bfd_sparc_elf_relocate_section (bfd *output_bfd, } if (r == bfd_reloc_continue) - r = _bfd_final_link_relocate (howto, input_bfd, input_section, - contents, rel->r_offset, - relocation, rel->r_addend); - + { +do_relocation: + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, rel->r_offset, + relocation, rel->r_addend); + } if (r != bfd_reloc_ok) { switch (r) @@ -3681,13 +4012,21 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd, bfd_vma r_offset, got_offset; int rela_index; - /* This symbol has an entry in the PLT. Set it up. */ - - BFD_ASSERT (h->dynindx != -1); + /* When building a static executable, use .iplt and + .rela.iplt sections for STT_GNU_IFUNC symbols. */ + if (htab->elf.splt != NULL) + { + splt = htab->elf.splt; + srela = htab->elf.srelplt; + } + else + { + splt = htab->elf.iplt; + srela = htab->elf.irelplt; + } - splt = htab->elf.splt; - srela = htab->elf.srelplt; - BFD_ASSERT (splt != NULL && srela != NULL); + if (splt == NULL || srela == NULL) + abort (); /* Fill in the entry in the .rela.plt section. */ if (htab->is_vxworks) @@ -3710,29 +4049,73 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd, + htab->elf.sgotplt->output_offset + got_offset); rela.r_addend = 0; + rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, + R_SPARC_JMP_SLOT); } else { + bfd_boolean ifunc = FALSE; + /* Fill in the entry in the procedure linkage table. */ rela_index = SPARC_ELF_BUILD_PLT_ENTRY (htab, output_bfd, splt, h->plt.offset, splt->size, &r_offset); + if (h == NULL + || h->dynindx == -1 + || ((info->executable + || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) + && h->def_regular + && h->type == STT_GNU_IFUNC)) + { + ifunc = TRUE; + BFD_ASSERT (h == NULL + || (h->type == STT_GNU_IFUNC + && h->def_regular + && (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak))); + } + rela.r_offset = r_offset + (splt->output_section->vma + splt->output_offset); - if (! ABI_64_P (output_bfd) - || h->plt.offset < (PLT64_LARGE_THRESHOLD * PLT64_ENTRY_SIZE)) + if (ABI_64_P (output_bfd) + && h->plt.offset >= (PLT64_LARGE_THRESHOLD * PLT64_ENTRY_SIZE)) { - rela.r_addend = 0; + if (ifunc) + { + rela.r_addend = (h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset + + h->root.u.def.value); + rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, + R_SPARC_IRELATIVE); + } + else + { + rela.r_addend = (-(h->plt.offset + 4) + - splt->output_section->vma + - splt->output_offset); + rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, + R_SPARC_JMP_SLOT); + } } else { - rela.r_addend = (-(h->plt.offset + 4) - - splt->output_section->vma - - splt->output_offset); + if (ifunc) + { + rela.r_addend = (h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset + + h->root.u.def.value); + rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, + R_SPARC_JMP_IREL); + } + else + { + rela.r_addend = 0; + rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, + R_SPARC_JMP_SLOT); + } } } - rela.r_info = SPARC_ELF_R_INFO (htab, NULL, h->dynindx, R_SPARC_JMP_SLOT); /* Adjust for the first 4 reserved elements in the .plt section when setting the offset in the .rela.plt section. @@ -3780,11 +4163,29 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd, the symbol was forced to be local because of a version file. The entry in the global offset table will already have been initialized in the relocate_section function. */ - if (info->shared - && SYMBOL_REFERENCES_LOCAL (info, h)) + if (! info->shared + && h->type == STT_GNU_IFUNC + && h->def_regular) + { + asection *plt; + + /* We load the GOT entry with the PLT entry. */ + plt = htab->elf.splt ? htab->elf.splt : htab->elf.iplt; + SPARC_ELF_PUT_WORD (htab, output_bfd, + (plt->output_section->vma + + plt->output_offset + h->plt.offset), + htab->elf.sgot->contents + + (h->got.offset & ~(bfd_vma) 1)); + return TRUE; + } + else if (info->shared + && SYMBOL_REFERENCES_LOCAL (info, h)) { asection *sec = h->root.u.def.section; - rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_RELATIVE); + if (h->type == STT_GNU_IFUNC) + rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_IRELATIVE); + else + rela.r_info = SPARC_ELF_R_INFO (htab, NULL, 0, R_SPARC_RELATIVE); rela.r_addend = (h->root.u.def.value + sec->output_section->vma + sec->output_offset); @@ -3823,9 +4224,10 @@ _bfd_sparc_elf_finish_dynamic_symbol (bfd *output_bfd, /* Mark some specially defined symbols as absolute. On VxWorks, _GLOBAL_OFFSET_TABLE_ is not absolute: it is relative to the ".got" section. Likewise _PROCEDURE_LINKAGE_TABLE_ and ".plt". */ - if (strcmp (h->root.root.string, "_DYNAMIC") == 0 - || (!htab->is_vxworks - && (h == htab->elf.hgot || h == htab->elf.hplt))) + if (sym != NULL + && (strcmp (h->root.root.string, "_DYNAMIC") == 0 + || (!htab->is_vxworks + && (h == htab->elf.hgot || h == htab->elf.hplt)))) sym->st_shndx = SHN_ABS; return TRUE; @@ -4021,6 +4423,21 @@ sparc_vxworks_finish_shared_plt (bfd *output_bfd, struct bfd_link_info *info) htab->elf.splt->contents + i * 4); } +/* Finish up local dynamic symbol handling. We set the contents of + various dynamic sections here. */ + +static bfd_boolean +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 _bfd_sparc_elf_finish_dynamic_symbol (info->output_bfd, info, + h, NULL); +} + bfd_boolean _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *info) { @@ -4083,6 +4500,9 @@ _bfd_sparc_elf_finish_dynamic_sections (bfd *output_bfd, struct bfd_link_info *i elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize = SPARC_ELF_WORD_BYTES (htab); + /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols. */ + htab_traverse (htab->loc_hash_table, finish_local_dynamic_symbol, info); + return TRUE; } diff --git a/bfd/elfxx-sparc.h b/bfd/elfxx-sparc.h index a20855a99..36748ec07 100644 --- a/bfd/elfxx-sparc.h +++ b/bfd/elfxx-sparc.h @@ -59,6 +59,10 @@ struct _bfd_sparc_elf_link_hash_table /* Small local sym cache. */ struct sym_cache sym_cache; + /* Used by local STT_GNU_IFUNC symbols. */ + htab_t loc_hash_table; + void *loc_hash_memory; + /* True if the target system is VxWorks. */ int is_vxworks; @@ -102,6 +106,8 @@ extern bfd_boolean _bfd_sparc_elf_mkobject (bfd *); extern struct bfd_link_hash_table *_bfd_sparc_elf_link_hash_table_create (bfd *); +extern void _bfd_sparc_elf_link_hash_table_free + (struct bfd_link_hash_table *); extern bfd_boolean _bfd_sparc_elf_create_dynamic_sections (bfd *, struct bfd_link_info *); extern void _bfd_sparc_elf_copy_indirect_symbol diff --git a/bfd/libbfd.h b/bfd/libbfd.h index 2be8fe50d..fa9d18760 100644 --- a/bfd/libbfd.h +++ b/bfd/libbfd.h @@ -928,6 +928,8 @@ static const char *const bfd_reloc_code_real_names[] = { "@@uninitialized@@", "BFD_RELOC_SPARC_GOTDATA_OP_HIX22", "BFD_RELOC_SPARC_GOTDATA_OP_LOX10", "BFD_RELOC_SPARC_GOTDATA_OP", + "BFD_RELOC_SPARC_JMP_IREL", + "BFD_RELOC_SPARC_IRELATIVE", "BFD_RELOC_SPARC_BASE13", "BFD_RELOC_SPARC_BASE22", "BFD_RELOC_SPARC_10", diff --git a/bfd/reloc.c b/bfd/reloc.c index 9451740b9..dca56d98b 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -1870,6 +1870,10 @@ ENUMX BFD_RELOC_SPARC_GOTDATA_OP_LOX10 ENUMX BFD_RELOC_SPARC_GOTDATA_OP +ENUMX + BFD_RELOC_SPARC_JMP_IREL +ENUMX + BFD_RELOC_SPARC_IRELATIVE ENUMDOC SPARC ELF relocations. There is probably some overlap with other relocation types already defined. diff --git a/include/ChangeLog b/include/ChangeLog index db4665407..64f6e1fbd 100644 --- a/include/ChangeLog +++ b/include/ChangeLog @@ -1,3 +1,7 @@ +2010-02-08 David S. Miller + + * elf/sparc.h (R_SPARC_JMP_IREL, R_SPARC_IRELATIVE): Define. + 2010-01-13 Joel Brobecker Add new DW_AT_use_GNAT_descriptive_type CU attribute. diff --git a/include/elf/sparc.h b/include/elf/sparc.h index 68d6285ee..f7458df1a 100644 --- a/include/elf/sparc.h +++ b/include/elf/sparc.h @@ -164,6 +164,8 @@ START_RELOC_NUMBERS (elf_sparc_reloc_type) EMPTY_RELOC (R_SPARC_max_std) + RELOC_NUMBER (R_SPARC_JMP_IREL, 248) + RELOC_NUMBER (R_SPARC_IRELATIVE, 249) RELOC_NUMBER (R_SPARC_GNU_VTINHERIT, 250) RELOC_NUMBER (R_SPARC_GNU_VTENTRY, 251) RELOC_NUMBER (R_SPARC_REV32, 252) diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog index e7c7f64d1..c3ab37554 100644 --- a/ld/testsuite/ChangeLog +++ b/ld/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2010-02-08 David S. Miller + + * ld-ifunc/ifunc.exp: Run for sparc. + 2010-02-08 Nathan Sidwell * ld-powerpc/apuinfo-nul.s: New. diff --git a/ld/testsuite/ld-ifunc/ifunc.exp b/ld/testsuite/ld-ifunc/ifunc.exp index cdd4ff864..be519ce45 100644 --- a/ld/testsuite/ld-ifunc/ifunc.exp +++ b/ld/testsuite/ld-ifunc/ifunc.exp @@ -23,11 +23,12 @@ # Written by Nick Clifton -# IFUNC support has only been implemented for the ix86, x86_64 and powerpc -# so far. +# IFUNC support has only been implemented for the ix86, x86_64, powerpc, +# and sparc so far. if {!(([istarget "i?86-*-*"] || [istarget "x86_64-*-*"] - || [istarget "powerpc*-*-*"]) + || [istarget "powerpc*-*-*"] + || [istarget "sparc*-*-*"]) && ([istarget "*-*-elf*"] || ([istarget "*-*-linux*"] && ![istarget "*-*-*aout*"] -- 2.11.4.GIT