aboutsummaryrefslogtreecommitdiff
path: root/bfd/elf64-x86-64.c
diff options
context:
space:
mode:
authorH.J. Lu <hjl.tools@gmail.com>2009-06-06 11:48:11 +0000
committerH.J. Lu <hjl.tools@gmail.com>2009-06-06 11:48:11 +0000
commit710ab2870ffb401c7365ab594108cd9b1b46fcb9 (patch)
treed5b2a398f8f58a8f8d0222669f0310922943ea62 /bfd/elf64-x86-64.c
parent9e93c730ef1f406e7205afce336d9c4f538c8100 (diff)
downloadgdb-710ab2870ffb401c7365ab594108cd9b1b46fcb9.zip
gdb-710ab2870ffb401c7365ab594108cd9b1b46fcb9.tar.gz
gdb-710ab2870ffb401c7365ab594108cd9b1b46fcb9.tar.bz2
2009-06-06 H.J. Lu <hongjiu.lu@intel.com>
* elf32-i386.c (elf_i386_check_relocs): Make room for dynamic relocation for R_386_32 against STT_GNU_IFUNC symbol when building shared object. Check info->executable instead of !info->shared when setting non_got_ref. (elf_i386_allocate_dynrelocs): Allocate dynamic relocation for non-GOT reference of STT_GNU_IFUNC symbol in shared object. Allocate GOT relocation agsinst STT_GNU_IFUNC symbol if needed. (elf_i386_relocate_section): Output dynamic relocation for R_386_32 against STT_GNU_IFUNC symbol to get the real function address when building shared object. (elf_i386_finish_dynamic_symbol): Output R_386_GLOB_DAT relocation for STT_GNU_IFUNC symbol in shared object. * elf64-x86-64.c (elf64_x86_64_check_relocs): Make room for dynamic relocation for R_X86_64_64 against STT_GNU_IFUNC symbol when building shared object. Check info->executable instead of !info->shared when setting non_got_ref. (elf64_x86_64_allocate_dynrelocs): Allocate dynamic relocation for non-GOT reference of STT_GNU_IFUNC symbol in shared library. Allocate GOT relocation agsinst STT_GNU_IFUNC symbol if needed. (elf64_x86_64_relocate_section): Output dynamic relocation for R_X86_64_64 against STT_GNU_IFUNC symbol to get the real function address when building shared object. (elf64_x86_64_finish_dynamic_symbol): Output R_X86_64_GLOB_DAT relocation for STT_GNU_IFUNC symbol in shared object.
Diffstat (limited to 'bfd/elf64-x86-64.c')
-rw-r--r--bfd/elf64-x86-64.c220
1 files changed, 172 insertions, 48 deletions
diff --git a/bfd/elf64-x86-64.c b/bfd/elf64-x86-64.c
index 041e285..0ba5759 100644
--- a/bfd/elf64-x86-64.c
+++ b/bfd/elf64-x86-64.c
@@ -1110,9 +1110,51 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
bfd_set_error (bfd_error_bad_value);
return FALSE;
+ case R_X86_64_64:
+ h->non_got_ref = 1;
+ h->pointer_equality_needed = 1;
+ if (info->shared)
+ {
+ struct elf64_x86_64_dyn_relocs *p;
+ struct elf64_x86_64_dyn_relocs **head;
+
+ /* We must copy these reloc types into the output
+ file. Create a reloc section in dynobj and
+ make room for this reloc. */
+ if (sreloc == NULL)
+ {
+ if (htab->elf.dynobj == NULL)
+ htab->elf.dynobj = abfd;
+
+ sreloc = _bfd_elf_make_dynamic_reloc_section
+ (sec, htab->elf.dynobj, 3, abfd, TRUE);
+
+ if (sreloc == NULL)
+ return FALSE;
+ }
+
+ head = &((struct elf64_x86_64_link_hash_entry *) h)->dyn_relocs;
+ p = *head;
+ if (p == NULL || p->sec != sec)
+ {
+ bfd_size_type amt = sizeof *p;
+
+ p = ((struct elf64_x86_64_dyn_relocs *)
+ bfd_alloc (htab->elf.dynobj, amt));
+ if (p == NULL)
+ return FALSE;
+ p->next = *head;
+ *head = p;
+ p->sec = sec;
+ p->count = 0;
+ p->pc_count = 0;
+ }
+ p->count += 1;
+ }
+ break;
+
case R_X86_64_32S:
case R_X86_64_32:
- case R_X86_64_64:
case R_X86_64_PC32:
case R_X86_64_PC64:
h->non_got_ref = 1;
@@ -1329,7 +1371,7 @@ elf64_x86_64_check_relocs (bfd *abfd, struct bfd_link_info *info,
case R_X86_64_PC32:
case R_X86_64_PC64:
case R_X86_64_64:
- if (h != NULL && !info->shared)
+ if (h != NULL && info->executable)
{
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
@@ -1795,7 +1837,7 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
/* When building a static executable, use .iplt, .igot.plt and
.rela.iplt sections for STT_GNU_IFUNC symbols. */
- if (htab->splt != 0)
+ if (htab->splt != NULL)
{
plt = htab->splt;
gotplt = htab->sgotplt;
@@ -1830,34 +1872,50 @@ elf64_x86_64_allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf)
relplt->size += sizeof (Elf64_External_Rela);
relplt->reloc_count++;
- /* No need for dynamic relocation for local STT_GNU_IFUNC symbol.
- Discard space for relocations against it. */
- if (h->dynindx == -1 || h->forced_local)
+ /* 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)
eh->dyn_relocs = NULL;
- /* 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->executable
- || info->symbolic
- || h->forced_local)
+ /* Finally, allocate space. */
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
- 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;
+ asection * sreloc = elf_section_data (p->sec)->sreloc;
+ sreloc->size += p->count * sizeof (Elf64_External_Rela);
+ }
+
+ /* For STT_GNU_IFUNC symbol, .got.plt has the real function
+ addres 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 if .got isn't used.
+ 4. 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 ((info->shared
+ && (h->dynindx == -1
+ || h->forced_local))
+ || (!info->shared
+ && !h->pointer_equality_needed)
+ || 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 (Elf64_External_Rela);
}
+
+ return TRUE;
}
else if (htab->elf.dynamic_sections_created
&& h->plt.refcount > 0)
@@ -1984,7 +2042,6 @@ 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;
@@ -2621,11 +2678,73 @@ elf64_x86_64_relocate_section (bfd *output_bfd, struct bfd_link_info *info,
return FALSE;
case R_X86_64_32S:
- if (!info->executable)
+ if (info->shared)
abort ();
+ goto do_relocation;
+
+ case R_X86_64_64:
+ if (rel->r_addend != 0)
+ {
+ (*_bfd_error_handler)
+ (_("%B: relocation %s against STT_GNU_IFUNC "
+ "symbol `%s' has non-zero addend: %d"),
+ input_bfd, x86_64_elf_howto_table[r_type].name,
+ h->root.root.string, rel->r_addend);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ /* Generate dynamic relcoation only when there is a
+ non-GOF reference in a shared object. */
+ if (info->shared && h->non_got_ref)
+ {
+ Elf_Internal_Rela outrel;
+ bfd_byte *loc;
+ asection *sreloc;
+
+ /* Need a dynamic relocation get the 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)
+ {
+ /* This symbol is resolved locally. */
+ outrel.r_info = ELF64_R_INFO (0, R_X86_64_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 = elf_section_data (input_section)->sreloc;
+ loc = sreloc->contents;
+ loc += (sreloc->reloc_count++
+ * sizeof (Elf64_External_Rela));
+ bfd_elf64_swap_reloca_out (output_bfd, &outrel, loc);
+
+ /* 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. */
+ continue;
+ }
case R_X86_64_32:
- case R_X86_64_64:
case R_X86_64_PC32:
case R_X86_64_PC64:
case R_X86_64_PLT32:
@@ -3596,7 +3715,7 @@ elf64_x86_64_finish_dynamic_symbol (bfd *output_bfd,
/* When building a static executable, use .iplt, .igot.plt and
.rela.iplt sections for STT_GNU_IFUNC symbols. */
- if (htab->splt != 0)
+ if (htab->splt != NULL)
{
plt = htab->splt;
gotplt = htab->sgotplt;
@@ -3741,25 +3860,29 @@ 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->executable
- || info->symbolic
- || h->forced_local)
- && h->def_regular
- && h->pointer_equality_needed
+ if (h->def_regular
&& 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;
+ if (info->shared)
+ {
+ /* Generate R_X86_64_GLOB_DAT. */
+ goto do_glob_dat;
+ }
+ else
+ {
+ if (!h->pointer_equality_needed)
+ abort ();
+
+ /* For non-shared object, we can't use .got.plt, which
+ contains the real function addres if we need pointer
+ equality. We load the GOT entry with the PLT entry. */
+ asection *plt = htab->splt ? htab->splt : htab->iplt;
+ 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))
@@ -3775,6 +3898,7 @@ elf64_x86_64_finish_dynamic_symbol (bfd *output_bfd,
else
{
BFD_ASSERT((h->got.offset & 1) == 0);
+do_glob_dat:
bfd_put_64 (output_bfd, (bfd_vma) 0,
htab->sgot->contents + h->got.offset);
rela.r_info = ELF64_R_INFO (h->dynindx, R_X86_64_GLOB_DAT);