diff options
-rw-r--r-- | bfd/ChangeLog | 14 | ||||
-rw-r--r-- | bfd/elf32-i386.c | 60 | ||||
-rw-r--r-- | bfd/elf64-x86-64.c | 45 |
3 files changed, 106 insertions, 13 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog index b43f595..ef39b35 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,17 @@ +2009-06-03 H.J. Lu <hongjiu.lu@intel.com> + + * elf32-i386.c (elf_i386_allocate_dynrelocs): Allocate + GOT entry for STT_GNU_IFUNC symbol with pointer equality. + (elf_i386_relocate_section): Adjust R_386_GOT32 relocation + against STT_GNU_IFUNC symbols for static executables. + (elf_i386_finish_dynamic_symbol): Load GOT entry with + PLT entry for STT_GNU_IFUNC symbol with pointer equality. + + * elf64-x86-64.c (elf64_x86_64_allocate_dynrelocs): Allocate + GOT entry for STT_GNU_IFUNC symbol with pointer equality. + (elf64_x86_64_finish_dynamic_symbol): Load GOT entry with + PLT entry for STT_GNU_IFUNC symbol with pointer equality. + 2009-06-02 Richard Sandiford <r.sandiford@uk.ibm.com> * coff-rs6000.c (xcoff_ppc_relocate_section): Allow undefined diff --git a/bfd/elf32-i386.c b/bfd/elf32-i386.c index ade5aa8..81b5093 100644 --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@ -2003,10 +2003,26 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) /* STT_GNU_IFUNC symbol uses .got.plt, not .got. But for shared library, we must go through GOT and we can't use R_386_IRELATIVE unless it is forced local. */ - if (!info->shared + if (info->executable || info->symbolic || h->forced_local) - h->got.refcount = 0; + { + if (h->pointer_equality_needed + && htab->sgot != NULL) + { + /* We can't use .got.plt, which contains the real + function addres, since we need pointer equality. + We will load the GOT entry with the PLT entry + in elf_i386_finish_dynamic_symbol and don't + need GOT relocation. */ + h->got.offset = htab->sgot->size; + htab->sgot->size += 4; + eh->tlsdesc_got = (bfd_vma) -1; + goto skip_relgot; + } + else + h->got.refcount = 0; + } } else if (htab->elf.dynamic_sections_created && h->plt.refcount > 0) @@ -2153,6 +2169,7 @@ elf_i386_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf) else h->got.offset = (bfd_vma) -1; +skip_relgot: if (eh->dyn_relocs == NULL) return TRUE; @@ -2966,11 +2983,16 @@ elf_i386_relocate_section (bfd *output_bfd, relocation += gotplt->output_offset; } else - relocation = (base_got->output_section->vma - + base_got->output_offset + off - - gotplt->output_section->vma - - gotplt->output_offset); - + { + relocation = (base_got->output_section->vma + + base_got->output_offset + off + - gotplt->output_section->vma + - gotplt->output_offset); + /* Adjust for static executables. */ + if (htab->splt == NULL) + relocation += gotplt->output_offset; + } + goto do_relocation; case R_386_GOTOFF: @@ -4113,8 +4135,28 @@ elf_i386_finish_dynamic_symbol (bfd *output_bfd, of a version file, we just want to emit a RELATIVE reloc. 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->executable + || info->symbolic + || h->forced_local) + && h->def_regular + && h->pointer_equality_needed + && h->type == STT_GNU_IFUNC) + { + /* The STT_GNU_IFUNC symbol is locally defined. But we can't + use .got.plt, which contains the real function addres, + since we need pointer equality. We load the GOT entry + with the PLT entry without relocation. */ + asection *plt = htab->splt ? htab->splt : htab->iplt; + if (htab->sgot == NULL + || h->plt.offset == (bfd_vma) -1) + abort (); + bfd_put_32 (output_bfd, (plt->output_section->vma + + plt->output_offset + h->plt.offset), + htab->sgot->contents + h->got.offset); + return TRUE; + } + else if (info->shared + && SYMBOL_REFERENCES_LOCAL (info, h)) { BFD_ASSERT((h->got.offset & 1) != 0); rel.r_info = ELF32_R_INFO (0, R_386_RELATIVE); diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c index e858325..1dec3c3 100644 --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@ -1841,10 +1841,26 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) /* STT_GNU_IFUNC symbol uses .got.plt, not .got. But for shared library, we must go through GOT and we can't use R_X86_64_IRELATIVE unless it is forced local. */ - if (!info->shared + if (info->executable || info->symbolic || h->forced_local) - h->got.refcount = 0; + { + if (h->pointer_equality_needed + && htab->sgot != NULL) + { + /* We can't use .got.plt, which contains the real + function addres, since we need pointer equality. + We will load the GOT entry with the PLT entry + in elf64_x86_64_finish_dynamic_symbol and don't + need GOT relocation. */ + h->got.offset = htab->sgot->size; + htab->sgot->size += GOT_ENTRY_SIZE; + eh->tlsdesc_got = (bfd_vma) -1; + goto skip_relgot; + } + else + h->got.refcount = 0; + } } else if (htab->elf.dynamic_sections_created && h->plt.refcount > 0) @@ -1971,6 +1987,7 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) else h->got.offset = (bfd_vma) -1; +skip_relgot: if (eh->dyn_relocs == NULL) return TRUE; @@ -3727,8 +3744,28 @@ elf64_x86_64_finish_dynamic_symbol (bfd *output_bfd, of a version file, we just want to emit a RELATIVE reloc. 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->executable + || info->symbolic + || h->forced_local) + && h->def_regular + && h->pointer_equality_needed + && h->type == STT_GNU_IFUNC) + { + /* The STT_GNU_IFUNC symbol is locally defined. But we can't + use .got.plt, which contains the real function addres, + since we need pointer equality. We load the GOT entry + with the PLT entry without relocation. */ + asection *plt = htab->splt ? htab->splt : htab->iplt; + if (htab->sgot == NULL + || h->plt.offset == (bfd_vma) -1) + abort (); + bfd_put_64 (output_bfd, (plt->output_section->vma + + plt->output_offset + h->plt.offset), + htab->sgot->contents + h->got.offset); + return TRUE; + } + else if (info->shared + && SYMBOL_REFERENCES_LOCAL (info, h)) { if (!h->def_regular) return FALSE; |