diff -ruP binutils-2.23.52.0.1-orig/bfd/configure binutils-2.23.52.0.1/bfd/configure --- binutils-2.23.52.0.1-orig/bfd/configure 2014-06-16 10:18:57.265495580 -0700 +++ binutils-2.23.52.0.1/bfd/configure 2014-06-16 11:45:14.935495580 -0700 @@ -15387,7 +15387,7 @@ bfd_elf32_xtensa_be_vec) tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;; bfd_elf64_alpha_freebsd_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_alpha_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; - bfd_elf64_bigaarch64_vec) tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;; + bfd_elf64_bigaarch64_vec) tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_big_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_bigmips_vec) tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf64_hppa_linux_vec) tb="$tb elf64-hppa.lo elf64.lo $elf"; target_size=64 ;; @@ -15396,7 +15396,7 @@ bfd_elf64_ia64_hpux_big_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_ia64_little_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_ia64_vms_vec) tb="$tb elf64-ia64-vms.lo elf64-ia64.lo elfxx-ia64.lo elf64.lo vms-lib.lo vms-misc.lo $elf"; target_size=64 ;; - bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;; + bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_little_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_littlemips_vec) tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf64_mmix_vec) tb="$tb elf64-mmix.lo elf64.lo $elf" target_size=64 ;; diff -ruP binutils-2.23.52.0.1-orig/bfd/configure.in binutils-2.23.52.0.1/bfd/configure.in --- binutils-2.23.52.0.1-orig/bfd/configure.in 2014-06-16 10:18:56.445495580 -0700 +++ binutils-2.23.52.0.1/bfd/configure.in 2014-06-16 10:50:29.815495580 -0700 @@ -860,7 +860,7 @@ bfd_elf32_xtensa_be_vec) tb="$tb xtensa-isa.lo xtensa-modules.lo elf32-xtensa.lo elf32.lo $elf" ;; bfd_elf64_alpha_freebsd_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_alpha_vec) tb="$tb elf64-alpha.lo elf64.lo $elf"; target_size=64 ;; - bfd_elf64_bigaarch64_vec) tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;; + bfd_elf64_bigaarch64_vec) tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_big_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_bigmips_vec) tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf64_hppa_linux_vec) tb="$tb elf64-hppa.lo elf64.lo $elf"; target_size=64 ;; @@ -869,7 +869,7 @@ bfd_elf64_ia64_hpux_big_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_ia64_little_vec) tb="$tb elf64-ia64.lo elfxx-ia64.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_ia64_vms_vec) tb="$tb elf64-ia64-vms.lo elf64-ia64.lo elfxx-ia64.lo elf64.lo vms-lib.lo vms-misc.lo $elf"; target_size=64 ;; - bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf64.lo $elf"; target_size=64 ;; + bfd_elf64_littleaarch64_vec)tb="$tb elf64-aarch64.lo elf-ifunc.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_little_generic_vec) tb="$tb elf64-gen.lo elf64.lo $elf"; target_size=64 ;; bfd_elf64_littlemips_vec) tb="$tb elf64-mips.lo elf64.lo elfxx-mips.lo elf-vxworks.lo elf32.lo $elf ecofflink.lo"; target_size=64 ;; bfd_elf64_mmix_vec) tb="$tb elf64-mmix.lo elf64.lo $elf" target_size=64 ;; diff -ruP binutils-2.23.52.0.1-orig/bfd/elf64-aarch64.c binutils-2.23.52.0.1/bfd/elf64-aarch64.c --- binutils-2.23.52.0.1-orig/bfd/elf64-aarch64.c 2014-06-16 10:18:56.315495580 -0700 +++ binutils-2.23.52.0.1/bfd/elf64-aarch64.c 2014-06-24 08:28:08.832196620 -0700 @@ -142,6 +142,7 @@ #include "bfd_stdint.h" #include "elf-bfd.h" #include "bfdlink.h" +#include "objalloc.h" #include "elf/aarch64.h" static bfd_reloc_status_type @@ -398,6 +399,19 @@ ALL_ONES, /* dst_mask */ FALSE), /* pcrel_offset */ + HOWTO (R_AARCH64_IRELATIVE, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 64, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_bitfield, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_AARCH64_IRELATIVE", /* name */ + FALSE, /* partial_inplace */ + 0, /* src_mask */ + ALL_ONES, /* dst_mask */ + FALSE), /* pcrel_offset */ }; /* Note: code such as elf64_aarch64_reloc_type_lookup expect to use e.g. @@ -1887,6 +1901,10 @@ loader via DT_TLSDESC_GOT. The magic value (bfd_vma) -1 indicates an offset is not allocated. */ bfd_vma dt_tlsdesc_got; + + /* Used by local STT_GNU_IFUNC symbols. */ + htab_t loc_hash_table; + void * loc_hash_memory; }; @@ -1997,6 +2015,72 @@ return entry; } +/* 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_aarch64_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 +elf64_aarch64_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_aarch64_get_local_sym_hash (struct elf64_aarch64_link_hash_table *htab, + bfd *abfd, const Elf_Internal_Rela *rel, + bfd_boolean create) +{ + struct elf64_aarch64_link_hash_entry e, *ret; + asection *asec = abfd->sections; + hashval_t h = ELF_LOCAL_SYMBOL_HASH (asec->id, + ELF64_R_SYM (rel->r_info)); + void **slot; + + e.root.indx = asec->id; + e.root.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_aarch64_link_hash_entry *) *slot; + return &ret->root; + } + + ret = (struct elf64_aarch64_link_hash_entry *) + objalloc_alloc ((struct objalloc *) htab->loc_hash_memory, + sizeof (struct elf64_aarch64_link_hash_entry)); + if (ret) + { + memset (ret, 0, sizeof (*ret)); + ret->root.indx = asec->id; + ret->root.dynstr_index = ELF64_R_SYM (rel->r_info); + ret->root.dynindx = -1; + *slot = ret; + } + return &ret->root; +} /* Copy the extra info we tack onto an elf_link_hash_entry. */ @@ -2117,6 +2201,17 @@ return NULL; } + ret->loc_hash_table = htab_try_create (1024, + elf64_aarch64_local_htab_hash, + elf64_aarch64_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->root.root; } @@ -2128,6 +2223,11 @@ struct elf64_aarch64_link_hash_table *ret = (struct elf64_aarch64_link_hash_table *) hash; + if (ret->loc_hash_table) + htab_delete (ret->loc_hash_table); + if (ret->loc_hash_memory) + objalloc_free ((struct objalloc *) ret->loc_hash_memory); + bfd_hash_table_free (&ret->stub_hash_table); _bfd_elf_link_hash_table_free (hash); } @@ -3825,8 +3925,10 @@ struct elf_link_hash_entry *h, bfd_boolean *unresolved_reloc_p, bfd_boolean save_addend, - bfd_vma *saved_addend) + bfd_vma *saved_addend, + Elf_Internal_Sym *sym) { + Elf_Internal_Shdr *symtab_hdr; unsigned int r_type = howto->type; unsigned long r_symndx; bfd_byte *hit_data = contents + rel->r_offset; @@ -3837,6 +3939,8 @@ globals = elf64_aarch64_hash_table (info); + symtab_hdr = &elf_symtab_hdr (input_bfd); + BFD_ASSERT (is_aarch64_elf (input_bfd)); r_symndx = ELF64_R_SYM (rel->r_info); @@ -3858,6 +3962,180 @@ weak_undef_p = (h ? h->root.type == bfd_link_hash_undefweak : bfd_is_und_section (sym_sec)); + + /* Since STT_GNU_IFUNC symbol must go through PLT, we handle + it here if it is defined in a non-shared object. */ + if (h != NULL + && h->type == STT_GNU_IFUNC + && h->def_regular) + { + asection *plt; + const char *name; + asection *base_got; + bfd_vma off; + + if ((input_section->flags & SEC_ALLOC) == 0 + || h->plt.offset == (bfd_vma) -1) + abort (); + + /* STT_GNU_IFUNC symbol must go through PLT. */ + plt = globals->root.splt ? globals->root.splt : globals->root.iplt; + value = (plt->output_section->vma + plt->output_offset + h->plt.offset); + + switch (r_type) + { + 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, + howto->name, name, __FUNCTION__); + bfd_set_error (bfd_error_bad_value); + return FALSE; + + case R_AARCH64_ABS64: + if (rel->r_addend != 0) + { + 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' has non-zero addend: %d"), + input_bfd, howto->name, name, rel->r_addend); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + /* Generate dynamic relocation only when there is a + non-GOT reference in a shared object. */ + if (info->shared && h->non_got_ref) + { + Elf_Internal_Rela outrel; + asection *sreloc; + + /* Need a dynamic relocation to get the real function + address. */ + outrel.r_offset = _bfd_elf_section_offset (output_bfd, + info, + input_section, + rel->r_offset); + if (outrel.r_offset == (bfd_vma) -1 + || outrel.r_offset == (bfd_vma) -2) + abort (); + + outrel.r_offset += (input_section->output_section->vma + + input_section->output_offset); + + if (h->dynindx == -1 + || h->forced_local + || info->executable) + { + /* This symbol is resolved locally. */ + outrel.r_info = ELF64_R_INFO (0, R_AARCH64_IRELATIVE); + outrel.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + outrel.r_info = ELF64_R_INFO (h->dynindx, r_type); + outrel.r_addend = 0; + } + + sreloc = globals->root.irelifunc; + elf_append_rela (output_bfd, sreloc, &outrel); + + /* If this reloc is against an external symbol, we + do not want to fiddle with the addend. Otherwise, + we need to include the symbol value so that it + becomes an addend for the dynamic reloc. For an + internal symbol, we have updated addend. */ + return bfd_reloc_ok; + } + /* FALLTHROUGH */ + case R_AARCH64_JUMP26: + case R_AARCH64_CALL26: + value = aarch64_resolve_relocation (r_type, place, value, + signed_addend, + weak_undef_p); + return bfd_elf_aarch64_put_addend (input_bfd, hit_data, + howto, value); + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_GOT_LD_PREL19: + base_got = globals->root.sgot; + off = h->got.offset; + + if (base_got == NULL) + abort (); + + if (off == (bfd_vma) -1) + { + bfd_vma plt_index; + + /* We can't use h->got.offset to save state, or + even just remember the offset, as finish_dynamic_symbol + would use that as offset into .got. */ + + if (globals->root.splt != NULL) + { + plt_index = h->plt.offset / globals->plt_entry_size - 1; + off = (plt_index + 3) * GOT_ENTRY_SIZE; + base_got = globals->root.sgotplt; + } + else + { + plt_index = h->plt.offset / globals->plt_entry_size; + off = plt_index * GOT_ENTRY_SIZE; + base_got = globals->root.igotplt; + } + + if (h->dynindx == -1 + || h->forced_local + || info->symbolic) + { + /* This references the local definition. We must + initialize this entry in the global offset table. + Since the offset must always be a multiple of 8, + we use the least significant bit to record + whether we have initialized it already. + + When doing a dynamic link, we create a .rela.got + relocation entry to initialize the value. This + is done in the finish_dynamic_symbol routine. */ + if ((off & 1) != 0) + off &= ~1; + else + { + bfd_put_64 (output_bfd, value, + base_got->contents + off); + /* Note that this is harmless as -1 | 1 is still -1. */ + h->got.offset |= 1; + } + } + value = (base_got->output_section->vma + + base_got->output_offset + off); + } + else + value = aarch64_calculate_got_entry_vma (h, globals, info, + value, output_bfd, + unresolved_reloc_p); + value = aarch64_resolve_relocation (r_type, place, value, + 0, weak_undef_p); + return bfd_elf_aarch64_put_addend (input_bfd, hit_data, howto, value); + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADD_ABS_LO12_NC: + break; + } + } + switch (r_type) { case R_AARCH64_NONE: @@ -3884,11 +4162,6 @@ *unresolved_reloc_p = FALSE; - sreloc = _bfd_elf_get_dynamic_reloc_section (input_bfd, - input_section, 1); - if (sreloc == NULL) - return bfd_reloc_notsupported; - skip = FALSE; relocate = FALSE; @@ -3925,10 +4198,14 @@ outrel.r_addend += value; } - loc = sreloc->contents + sreloc->reloc_count++ * RELOC_SIZE (htab); + sreloc = elf_section_data (input_section)->sreloc; + if (sreloc == NULL || sreloc->contents == NULL) + return bfd_reloc_notsupported; + + loc = sreloc->contents + sreloc->reloc_count++ * RELOC_SIZE (globals); bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc); - if (sreloc->reloc_count * RELOC_SIZE (htab) > sreloc->size) + if (sreloc->reloc_count * RELOC_SIZE (globals) > sreloc->size) { /* Sanity to check that we have previously allocated sufficient space in the relocation section for the @@ -4337,6 +4614,20 @@ } relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); + + /* Relocate against local STT_GNU_IFUNC symbol. */ + if (!info->relocatable + && ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC) + { + h = elf64_aarch64_get_local_sym_hash (globals, 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 { @@ -4426,7 +4717,7 @@ input_section, contents, rel, relocation, info, sec, h, &unresolved_reloc, - save_addend, &addend); + save_addend, &addend, sym); switch (r_type) { @@ -4907,25 +5198,11 @@ if (r_symndx >= symtab_hdr->sh_info) { - struct elf64_aarch64_link_hash_entry *eh; - struct elf_dyn_relocs **pp; - struct elf_dyn_relocs *p; 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; - eh = (struct elf64_aarch64_link_hash_entry *) h; - - for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next) - { - if (p->sec == sec) - { - /* Everything must go for SEC. */ - *pp = p->next; - break; - } - } } else { @@ -4934,8 +5211,32 @@ /* 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 (isym != NULL + && ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC) + { + h = elf64_aarch64_get_local_sym_hash (htab, abfd, rel, FALSE); + if (h == NULL) + abort (); + } + } + + if (h) + { + struct elf64_aarch64_link_hash_entry *eh; + struct elf_dyn_relocs **pp; + struct elf_dyn_relocs *p; + + eh = (struct elf64_aarch64_link_hash_entry *) h; + + for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next) + if (p->sec == sec) + { + /* Everything must go for SEC. */ + *pp = p->next; + break; + } } r_type = ELF64_R_TYPE (rel->r_info); @@ -4964,6 +5265,12 @@ { if (h->got.refcount > 0) h->got.refcount -= 1; + + if (h->type == STT_GNU_IFUNC) + { + if (h->plt.refcount > 0) + h->plt.refcount -= 1; + } } else if (local_got_refcounts != NULL) { @@ -5025,12 +5332,13 @@ /* If this is a function, put it in the procedure linkage table. We will fill in the contents of the procedure linkage table later, when we know the address of the .got section. */ - if (h->type == STT_FUNC || h->needs_plt) + if (h->type == STT_FUNC || h->type == STT_GNU_IFUNC || h->needs_plt) { 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 CALL26 reloc in an input file, but the symbol wasn't referred to @@ -5226,6 +5534,7 @@ struct elf_link_hash_entry *h; unsigned long r_symndx; unsigned int r_type; + Elf_Internal_Sym *isym; r_symndx = ELF64_R_SYM (rel->r_info); r_type = ELF64_R_TYPE (rel->r_info); @@ -5249,7 +5558,31 @@ } if (nsyms == 0 || 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 = elf64_aarch64_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]; @@ -5261,6 +5594,37 @@ /* Could be done earlier, if h were already available. */ r_type = aarch64_tls_transition (abfd, info, r_type, h, r_symndx); + 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 + won't appear in output. */ + switch (r_type) + { + default: + break; + + case R_AARCH64_ABS64: + case R_AARCH64_CALL26: + case R_AARCH64_JUMP26: + case R_AARCH64_LD64_GOT_LO12_NC: + case R_AARCH64_ADR_GOT_PAGE: + case R_AARCH64_GOT_LD_PREL19: + case R_AARCH64_ADR_PREL_PG_HI21: + case R_AARCH64_ADD_ABS_LO12_NC: + if (htab->root.dynobj == NULL) + htab->root.dynobj = abfd; + if (!_bfd_elf_create_ifunc_sections (htab->root.dynobj, info)) + return FALSE; + break; + } + + /* It is referenced by a non-shared object. */ + h->ref_regular = 1; + h->root.non_ir_ref = 1; + } + switch (r_type) { case R_AARCH64_ABS64: @@ -5319,7 +5683,6 @@ asection *s; void **vpp; - Elf_Internal_Sym *isym; isym = bfd_sym_from_r_symndx (&htab->sym_cache, abfd, r_symndx); @@ -5469,7 +5832,10 @@ continue; h->needs_plt = 1; - h->plt.refcount += 1; + if (h->plt.refcount <= 0) + h->plt.refcount = 1; + else + h->plt.refcount += 1; break; } } @@ -5619,8 +5985,9 @@ Elf_Internal_Ehdr *i_ehdrp; /* ELF file header, internal form. */ i_ehdrp = elf_elfheader (abfd); - i_ehdrp->e_ident[EI_OSABI] = 0; i_ehdrp->e_ident[EI_ABIVERSION] = AARCH64_ELF_ABI_VERSION; + + _bfd_elf_set_osabi (abfd, link_info); } static enum elf_reloc_type_class @@ -6010,12 +6377,6 @@ return _bfd_free_cached_info (abfd); } -static bfd_boolean -elf64_aarch64_is_function_type (unsigned int type) -{ - return type == STT_FUNC; -} - /* Create dynamic sections. This is different from the ARM backend in that the got, plt, gotplt and their relocation sections are all created in the standard part of the bfd elf backend. */ @@ -6075,7 +6436,12 @@ info = (struct bfd_link_info *) inf; htab = elf64_aarch64_hash_table (info); - if (htab->root.dynamic_sections_created && h->plt.refcount > 0) + /* Since STT_GNU_IFUNC symbol must go through the PLT, we handle it + here if it is defined and referenced in a non-shared object. */ + if (h->type == STT_GNU_IFUNC + && h->def_regular) + return TRUE; + else if (htab->root.dynamic_sections_created && h->plt.refcount > 0) { /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ @@ -6330,8 +6696,258 @@ return TRUE; } +/* Allocate space in .plt, .got and associated reloc sections for + dynamic relocs against a STT_GNU_IFUNC symbol definition. */ + +static bfd_boolean +_bfd_elf_aarch64_allocate_ifunc_dyn_relocs (struct bfd_link_info *info, + struct elf_link_hash_entry *h, + struct elf_dyn_relocs **head, + unsigned int plt_entry_size, + unsigned int plt_header_size, + unsigned int got_entry_size) +{ + asection *plt, *gotplt, *relplt; + struct elf_dyn_relocs *p; + unsigned int sizeof_reloc; + const struct elf_backend_data *bed; + struct elf_link_hash_table *htab; + + /* When a shared library references a STT_GNU_IFUNC symbol defined + in executable, the address of the resolved function may be used. + But in non-shared executable, the address of its .plt slot may + be used. Pointer equality may not work correctly. PIE should + be used if pointer equality is required here. */ + if (!info->shared + && (h->dynindx != -1 + || info->export_dynamic) + && h->pointer_equality_needed) + { + info->callbacks->einfo + (_("%F%P: dynamic STT_GNU_IFUNC symbol `%s' with pointer " + "equality in `%B' can not be used when making an " + "executable; recompile with -fPIE and relink with -pie\n"), + h->root.root.string, + h->root.u.def.section->owner); + bfd_set_error (bfd_error_bad_value); + return FALSE; + } + + htab = elf_hash_table (info); + + /* When building a shared library, we need to handle the case where it is + marked with a regular reference, but not a non-GOT reference since the + non-GOT reference bit may not be set here. */ + if (info->shared && !h->non_got_ref && h->ref_regular) + for (p = *head; p != NULL; p = p->next) + if (p->count) + { + h->non_got_ref = 1; + goto keep; + } + + /* Support garbage collection against STT_GNU_IFUNC symbols. */ + if (h->plt.refcount <= 0 && h->got.refcount <= 0) + { + h->got = htab->init_got_offset; + h->plt = htab->init_plt_offset; + *head = NULL; + return TRUE; + } + + /* Return and discard space for dynamic relocations against it if + it is never referenced in a non-shared object. */ + if (!h->ref_regular) + { + if (h->plt.refcount > 0 + || h->got.refcount > 0) + abort (); + h->got = htab->init_got_offset; + h->plt = htab->init_plt_offset; + *head = NULL; + return TRUE; + } + +keep: + bed = get_elf_backend_data (info->output_bfd); + if (bed->rela_plts_and_copies_p) + sizeof_reloc = bed->s->sizeof_rela; + else + sizeof_reloc = bed->s->sizeof_rel; + + /* When building a static executable, use .iplt, .igot.plt and + .rel[a].iplt sections for STT_GNU_IFUNC symbols. */ + if (htab->splt != NULL) + { + plt = htab->splt; + gotplt = htab->sgotplt; + relplt = htab->srelplt; + + /* If this is the first .plt entry, make room for the special + first entry. */ + if (plt->size == 0) + plt->size += plt_header_size; + } + else + { + plt = htab->iplt; + gotplt = htab->igotplt; + relplt = htab->irelplt; + } + + /* Don't update value of STT_GNU_IFUNC symbol to PLT. We need + the original value for R_*_IRELATIVE. */ + h->plt.offset = plt->size; + + /* Make room for this entry in the .plt/.iplt section. */ + plt->size += plt_entry_size; + + /* We also need to make an entry in the .got.plt/.got.iplt section, + which will be placed in the .got section by the linker script. */ + gotplt->size += got_entry_size; + + /* We also need to make an entry in the .rel[a].plt/.rel[a].iplt + section. */ + relplt->size += sizeof_reloc; + relplt->reloc_count++; + + /* We need dynamic relocation for STT_GNU_IFUNC symbol only when + there is a non-GOT reference in a shared object. */ + if (!info->shared + || !h->non_got_ref) + *head = NULL; + + /* Finally, allocate space. */ + p = *head; + if (p != NULL) + { + bfd_size_type count = 0; + do + { + count += p->count; + p = p->next; + } + while (p != NULL); + htab->irelifunc->size += count * sizeof_reloc; + } + + /* For STT_GNU_IFUNC symbol, .got.plt has the real function address + and .got has the PLT entry adddress. We will load the GOT entry + with the PLT entry in finish_dynamic_symbol if it is used. For + branch, it uses .got.plt. For symbol value, + 1. Use .got.plt in a shared object if it is forced local or not + dynamic. + 2. Use .got.plt in a non-shared object if pointer equality isn't + needed. + 3. Use .got.plt in PIE. + 4. Use .got.plt if .got isn't used. + 5. Otherwise use .got so that it can be shared among different + objects at run-time. + We only need to relocate .got entry in shared object. */ + if (h->got.refcount <= 0 + || (info->shared + && (h->dynindx == -1 + || h->forced_local)) + || (!info->shared + && !h->pointer_equality_needed) + || (info->executable && info->shared) + || htab->sgot == NULL) + { + /* Use .got.plt. */ + h->got.offset = (bfd_vma) -1; + } + else + { + h->got.offset = htab->sgot->size; + htab->sgot->size += got_entry_size; + if (info->shared) + htab->srelgot->size += sizeof_reloc; + } + + return TRUE; +} + +/* Allocate space in .plt, .got and associated reloc sections for + ifunc dynamic relocs. */ +static bfd_boolean +elf64_aarch64_allocate_ifunc_dynrelocs (struct elf_link_hash_entry *h, + void *inf) +{ + struct bfd_link_info *info; + struct elf64_aarch64_link_hash_table *htab; + struct elf64_aarch64_link_hash_entry *eh; + /* An example of a bfd_link_hash_indirect symbol is versioned + symbol. For example: __gxx_personality_v0(bfd_link_hash_indirect) + -> __gxx_personality_v0(bfd_link_hash_defined) + + There is no need to process bfd_link_hash_indirect symbols here + because we will also be presented with the concrete instance of + the symbol and elf64_aarch64_copy_indirect_symbol () will have been + called to copy all relevant data from the generic to the concrete + symbol instance. + */ + if (h->root.type == bfd_link_hash_indirect) + return TRUE; + + if (h->root.type == bfd_link_hash_warning) + h = (struct elf_link_hash_entry *) h->root.u.i.link; + + info = (struct bfd_link_info *) inf; + htab = elf64_aarch64_hash_table (info); + + eh = (struct elf64_aarch64_link_hash_entry *) h; + + /* Since STT_GNU_IFUNC symbol must go through PLT, we handle it + here if it is defined and referenced in a non-shared object. */ + if (h->type == STT_GNU_IFUNC + && h->def_regular) + return _bfd_elf_aarch64_allocate_ifunc_dyn_relocs (info, h, + &eh->dyn_relocs, + htab->plt_entry_size, + htab->plt_header_size, + GOT_ENTRY_SIZE); + return TRUE; +} + +/* Allocate space in .plt, .got and associated reloc sections for + local dynamic relocs. */ + +static bfd_boolean +elf64_aarch64_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_aarch64_allocate_dynrelocs (h, inf); +} + +/* Allocate space in .plt, .got and associate reloc sections for + local ifunc dynamic relocs. */ + +static bfd_boolean +elf64_aarch64_allocate_local_ifunc_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_aarch64_allocate_ifunc_dynrelocs (h, inf); +} /* This is the most important function of all . Innocuosly named though ! */ @@ -6470,6 +7086,20 @@ elf_link_hash_traverse (&htab->root, elf64_aarch64_allocate_dynrelocs, info); + /* Allocate global ifunc sym .plt and .got entries, and space for global + ifunc sym dynamic relocs. */ + elf_link_hash_traverse (&htab->root, elf64_aarch64_allocate_ifunc_dynrelocs, + info); + + /* Allocate .plt and .got entries, and space for local symbols. */ + htab_traverse (htab->loc_hash_table, + elf64_aarch64_allocate_local_dynrelocs, + info); + + /* Allocate .plt and .got entries, and space for local ifunc symbols. */ + htab_traverse (htab->loc_hash_table, + elf64_aarch64_allocate_local_ifunc_dynrelocs, + info); /* For every jump slot reserved in the sgotplt, reloc_count is incremented. However, when we reserve space for TLS descriptors, @@ -6625,7 +7255,8 @@ static void elf64_aarch64_create_small_pltn_entry (struct elf_link_hash_entry *h, struct elf64_aarch64_link_hash_table - *htab, bfd *output_bfd) + *htab, bfd *output_bfd, + struct bfd_link_info *info) { bfd_byte *plt_entry; bfd_vma plt_index; @@ -6634,17 +7265,50 @@ bfd_vma plt_entry_address; Elf_Internal_Rela rela; bfd_byte *loc; + asection *plt, *gotplt, *relplt; - plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size; + /* When building a static executable, use .iplt, .igot.plt and + .rela.iplt sections for STT_GNU_IFUNC symbols. */ + if (htab->root.splt != NULL) + { + plt = htab->root.splt; + gotplt = htab->root.sgotplt; + relplt = htab->root.srelplt; + } + else + { + plt = htab->root.iplt; + gotplt = htab->root.igotplt; + relplt = htab->root.irelplt; + } + + /* Get the index in the procedure linkage table which + corresponds to this symbol. This is the index of this symbol + in all the symbols for which we are making plt entries. The + first entry in the procedure linkage table is reserved. - /* Offset in the GOT is PLT index plus got GOT headers(3) - times 8. */ - got_offset = (plt_index + 3) * GOT_ENTRY_SIZE; - plt_entry = htab->root.splt->contents + h->plt.offset; - plt_entry_address = htab->root.splt->output_section->vma - + htab->root.splt->output_offset + h->plt.offset; - gotplt_entry_address = htab->root.sgotplt->output_section->vma + - htab->root.sgotplt->output_offset + got_offset; + Get the offset into the .got table of the entry that + correponds to this function. Each .got entry is GOT_ENTRY_SIZE + bytes. The first three are reserved for the dynamic linker. + + For static executables, we don't reserve anything. */ + + if (plt == htab->root.splt) + { + plt_index = (h->plt.offset - htab->plt_header_size) / htab->plt_entry_size; + got_offset = (plt_index + 3) * GOT_ENTRY_SIZE; + } + else + { + plt_index = h->plt.offset / htab->plt_entry_size; + got_offset = plt_index * GOT_ENTRY_SIZE; + } + + plt_entry = plt->contents + h->plt.offset; + plt_entry_address = plt->output_section->vma + + plt->output_section->output_offset + h->plt.offset; + gotplt_entry_address = gotplt->output_section->vma + + gotplt->output_offset + got_offset; /* Copy in the boiler-plate for the PLTn entry. */ memcpy (plt_entry, elf64_aarch64_small_plt_entry, PLT_SMALL_ENTRY_SIZE); @@ -6668,19 +7332,35 @@ /* All the GOTPLT Entries are essentially initialized to PLT0. */ bfd_put_64 (output_bfd, - (htab->root.splt->output_section->vma - + htab->root.splt->output_offset), - htab->root.sgotplt->contents + got_offset); + plt->output_section->vma + plt->output_offset, + gotplt->contents + got_offset); - /* Fill in the entry in the .rela.plt section. */ rela.r_offset = gotplt_entry_address; - rela.r_info = ELF64_R_INFO (h->dynindx, R_AARCH64_JUMP_SLOT); - rela.r_addend = 0; + + if (h->dynindx == -1 + || ((info->executable + || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) + && h->def_regular + && h->type == STT_GNU_IFUNC)) + { + /* If an STT_GNU_IFUNC symbol is locally defined, generate + R_AARCH64_IRELATIVE instead of R_AARCH64_JUMP_SLOT. */ + rela.r_info = ELF64_R_INFO (0, R_AARCH64_IRELATIVE); + rela.r_addend = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + } + else + { + /* Fill in the entry in the .rela.plt section. */ + rela.r_info = ELF64_R_INFO (h->dynindx, R_AARCH64_JUMP_SLOT); + rela.r_addend = 0; + } /* Compute the relocation entry to used based on PLT index and do not adjust reloc_count. The reloc_count has already been adjusted to account for this entry. */ - loc = htab->root.srelplt->contents + plt_index * RELOC_SIZE (htab); + loc = relplt->contents + plt_index * RELOC_SIZE (htab); bfd_elf64_swap_reloca_out (output_bfd, &rela, loc); } @@ -6740,15 +7420,35 @@ if (h->plt.offset != (bfd_vma) - 1) { + asection *plt, *gotplt, *relplt; + + /* When building a static executable, use .iplt, .igot.plt and + .rela.iplt sections for STT_GNU_IFUNC symbols. */ + if (htab->root.splt != NULL) + { + plt = htab->root.splt; + gotplt = htab->root.sgotplt; + relplt = htab->root.srelplt; + } + else + { + plt = htab->root.iplt; + gotplt = htab->root.igotplt; + relplt = htab->root.irelplt; + } + /* This symbol has an entry in the procedure linkage table. Set it up. */ - - if (h->dynindx == -1 - || htab->root.splt == NULL - || htab->root.sgotplt == NULL || htab->root.srelplt == NULL) + if ((h->dynindx == -1 + && !((h->forced_local || info->executable) + && h->def_regular + && h->type == STT_GNU_IFUNC)) + || plt == NULL + || gotplt == NULL + || relplt == NULL) abort (); - elf64_aarch64_create_small_pltn_entry (h, htab, output_bfd); + elf64_aarch64_create_small_pltn_entry (h, htab, output_bfd, info); if (!h->def_regular) { /* Mark the symbol as undefined, rather than as defined in @@ -6833,6 +7533,21 @@ return TRUE; } +/* Finish up local dynamic symbol handling. We set the contents of + various dynamic sections here. */ + +static bfd_boolean +elf64_aarch64_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_aarch64_finish_dynamic_symbol (info->output_bfd, + info, h, NULL); +} + static void elf64_aarch64_init_small_plt0_entry (bfd *output_bfd ATTRIBUTE_UNUSED, struct elf64_aarch64_link_hash_table @@ -7078,6 +7793,11 @@ elf_section_data (htab->root.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_aarch64_finish_local_dynamic_symbol, + info); + return TRUE; } @@ -7197,9 +7917,6 @@ #define elf_backend_init_index_section \ _bfd_elf_init_2_index_sections -#define elf_backend_is_function_type \ - elf64_aarch64_is_function_type - #define elf_backend_finish_dynamic_sections \ elf64_aarch64_finish_dynamic_sections diff -ruP binutils-2.23.52.0.1-orig/include/elf/aarch64.h binutils-2.23.52.0.1/include/elf/aarch64.h --- binutils-2.23.52.0.1-orig/include/elf/aarch64.h 2013-02-27 12:28:03.000000000 -0800 +++ binutils-2.23.52.0.1/include/elf/aarch64.h 2014-06-16 10:50:29.815495580 -0700 @@ -208,7 +208,9 @@ RELOC_NUMBER (R_AARCH64_TLS_DTPREL64, 1029) RELOC_NUMBER (R_AARCH64_TLS_TPREL64, 1030) RELOC_NUMBER (R_AARCH64_TLSDESC, 1031) -FAKE_RELOC (R_AARCH64_dyn_max, 1032) +RELOC_NUMBER (R_AARCH64_IRELATIVE, 1032) + +FAKE_RELOC (R_AARCH64_dyn_max, 1033) END_RELOC_NUMBERS (R_AARCH64_end)