|| !bfd_set_section_alignment (dynobj, htab->plt_eh_frame, 3))
return FALSE;
}
+
+ /* Align .got section to its entry size. */
+ if (htab->elf.sgot != NULL
+ && !bfd_set_section_alignment (dynobj, htab->elf.sgot, 3))
+ return FALSE;
+
+ /* Align .got.plt section to its entry size. */
+ if (htab->elf.sgotplt != NULL
+ && !bfd_set_section_alignment (dynobj, htab->elf.sgotplt, 3))
+ return FALSE;
+
return TRUE;
}
if (bfd_link_relocatable (info))
return TRUE;
+ /* Don't do anything special with non-loaded, non-alloced sections.
+ In particular, any relocs in such sections should not affect GOT
+ and PLT reference counting (ie. we don't allow them to create GOT
+ or PLT entries), there's no possibility or desire to optimize TLS
+ relocs, and there's not much point in propagating relocs to shared
+ libs that the dynamic linker won't relocate. */
+ if ((sec->flags & SEC_ALLOC) == 0)
+ return TRUE;
+
BFD_ASSERT (is_x86_64_elf (abfd));
htab = elf_x86_64_hash_table (info);
&& h != NULL
&& !h->def_regular
&& h->def_dynamic
- && (sec->flags & SEC_READONLY) == 0))
- && (sec->flags & SEC_ALLOC) != 0)
+ && (sec->flags & SEC_READONLY) == 0)))
return elf_x86_64_need_pic (abfd, sec, h, symtab_hdr, isym,
&x86_64_elf_howto_table[r_type]);
/* Fall through. */
pointer:
if (eh != NULL && (sec->flags & SEC_CODE) != 0)
eh->has_non_got_reloc = 1;
- /* STT_GNU_IFUNC symbol must go through PLT even if it is
- locally defined and undefined symbol may turn out to be
- a STT_GNU_IFUNC symbol later. */
+ /* We are called after all symbols have been resolved. Only
+ relocation against STT_GNU_IFUNC symbol must go through
+ PLT. */
if (h != NULL
&& (bfd_link_executable (info)
- || ((h->type == STT_GNU_IFUNC
- || h->root.type == bfd_link_hash_undefweak
- || h->root.type == bfd_link_hash_undefined)
- && SYMBOLIC_BIND (info, h))))
+ || h->type == STT_GNU_IFUNC))
{
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
adjust_dynamic_symbol. */
h->non_got_ref = 1;
- /* We may need a .plt entry if the function this reloc
- refers to is in a shared lib. */
- h->plt.refcount += 1;
+ /* We may need a .plt entry if the symbol is a function
+ defined in a shared lib or is a STT_GNU_IFUNC function
+ referenced from the code or read-only section. */
+ if (!h->def_regular
+ || (sec->flags & (SEC_CODE | SEC_READONLY)) != 0)
+ h->plt.refcount += 1;
+
if (r_type == R_X86_64_PC32)
{
/* Since something like ".long foo - ." may be used
If on the other hand, we are creating an executable, we
may need to keep relocations for symbols satisfied by a
dynamic library if we manage to avoid copy relocs for the
- symbol. */
+ symbol.
+
+ Generate dynamic pointer relocation against STT_GNU_IFUNC
+ symbol in the non-code section. */
if ((bfd_link_pic (info)
- && (sec->flags & SEC_ALLOC) != 0
&& (! IS_X86_64_PCREL_TYPE (r_type)
|| (h != NULL
&& (! (bfd_link_pie (info)
|| SYMBOLIC_BIND (info, h))
|| h->root.type == bfd_link_hash_defweak
|| !h->def_regular))))
+ || (h != NULL
+ && h->type == STT_GNU_IFUNC
+ && r_type == htab->pointer_r_type
+ && (sec->flags & SEC_CODE) == 0)
|| (ELIMINATE_COPY_RELOCS
&& !bfd_link_pic (info)
- && (sec->flags & SEC_ALLOC) != 0
&& h != NULL
&& (h->root.type == bfd_link_hash_defweak
|| !h->def_regular)))
if (pc_count || count)
{
- h->needs_plt = 1;
h->non_got_ref = 1;
- if (h->plt.refcount <= 0)
- h->plt.refcount = 1;
- else
- h->plt.refcount += 1;
+ if (pc_count)
+ {
+ /* Increment PLT reference count only for PC-relative
+ references. */
+ h->needs_plt = 1;
+ if (h->plt.refcount <= 0)
+ h->plt.refcount = 1;
+ else
+ h->plt.refcount += 1;
+ }
}
}
&htab->readonly_dynrelocs_against_ifunc,
plt_entry_size,
plt_entry_size,
- GOT_ENTRY_SIZE))
+ GOT_ENTRY_SIZE, TRUE))
{
asection *s = htab->plt_bnd;
if (h->plt.offset != (bfd_vma) -1 && s != NULL)
continue;
abort ();
}
- else if (h->plt.offset == (bfd_vma) -1)
- abort ();
+
+ switch (r_type)
+ {
+ default:
+ break;
+
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
+ case R_X86_64_GOTPCREL64:
+ base_got = htab->elf.sgot;
+ off = h->got.offset;
+
+ if (base_got == NULL)
+ abort ();
+
+ if (off == (bfd_vma) -1)
+ {
+ /* We can't use h->got.offset here to save state, or
+ even just remember the offset, as finish_dynamic_symbol
+ would use that as offset into .got. */
+
+ if (h->plt.offset == (bfd_vma) -1)
+ abort ();
+
+ if (htab->elf.splt != NULL)
+ {
+ plt_index = h->plt.offset / plt_entry_size - 1;
+ off = (plt_index + 3) * GOT_ENTRY_SIZE;
+ base_got = htab->elf.sgotplt;
+ }
+ else
+ {
+ plt_index = h->plt.offset / plt_entry_size;
+ off = plt_index * GOT_ENTRY_SIZE;
+ base_got = htab->elf.igotplt;
+ }
+
+ if (h->dynindx == -1
+ || h->forced_local
+ || info->symbolic)
+ {
+ /* This references the local defitionion. 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, relocation,
+ base_got->contents + off);
+ /* Note that this is harmless for the GOTPLT64
+ case, as -1 | 1 still is -1. */
+ h->got.offset |= 1;
+ }
+ }
+ }
+
+ relocation = (base_got->output_section->vma
+ + base_got->output_offset + off);
+
+ goto do_relocation;
+ }
+
+ if (h->plt.offset == (bfd_vma) -1)
+ {
+ /* Handle static pointers of STT_GNU_IFUNC symbols. */
+ if (r_type == htab->pointer_r_type
+ && (input_section->flags & SEC_CODE) == 0)
+ goto do_ifunc_pointer;
+ goto bad_ifunc_reloc;
+ }
/* STT_GNU_IFUNC symbol must go through PLT. */
if (htab->elf.splt != NULL)
switch (r_type)
{
default:
+bad_ifunc_reloc:
if (h->root.root.string)
name = h->root.root.string;
else
NULL);
(*_bfd_error_handler)
(_("%B: relocation %s against STT_GNU_IFUNC "
- "symbol `%s' isn't handled by %s"), input_bfd,
- howto->name, name, __FUNCTION__);
+ "symbol `%s' isn't supported"), input_bfd,
+ howto->name, name);
bfd_set_error (bfd_error_bad_value);
return FALSE;
goto do_relocation;
/* FALLTHROUGH */
case R_X86_64_64:
+do_ifunc_pointer:
if (rel->r_addend != 0)
{
if (h->root.root.string)
}
/* Generate dynamic relcoation only when there is a
- non-GOT reference in a shared object. */
- if (bfd_link_pic (info) && h->non_got_ref)
+ non-GOT reference in a shared object or there is no
+ PLT. */
+ if ((bfd_link_pic (info) && h->non_got_ref)
+ || h->plt.offset == (bfd_vma) -1)
{
Elf_Internal_Rela outrel;
asection *sreloc;
outrel.r_addend = 0;
}
- sreloc = htab->elf.irelifunc;
+ /* Dynamic relocations are stored in
+ 1. .rela.ifunc section in PIC object.
+ 2. .rela.got section in dynamic executable.
+ 3. .rela.iplt section in static executable. */
+ if (bfd_link_pic (info))
+ sreloc = htab->elf.irelifunc;
+ else if (htab->elf.splt != NULL)
+ sreloc = htab->elf.srelgot;
+ else
+ sreloc = htab->elf.irelplt;
elf_append_rela (output_bfd, sreloc, &outrel);
/* If this reloc is against an external symbol, we
case R_X86_64_PLT32:
case R_X86_64_PLT32_BND:
goto do_relocation;
-
- case R_X86_64_GOTPCREL:
- case R_X86_64_GOTPCRELX:
- case R_X86_64_REX_GOTPCRELX:
- case R_X86_64_GOTPCREL64:
- base_got = htab->elf.sgot;
- off = h->got.offset;
-
- if (base_got == NULL)
- abort ();
-
- if (off == (bfd_vma) -1)
- {
- /* We can't use h->got.offset here to save state, or
- even just remember the offset, as finish_dynamic_symbol
- would use that as offset into .got. */
-
- if (htab->elf.splt != NULL)
- {
- plt_index = h->plt.offset / plt_entry_size - 1;
- off = (plt_index + 3) * GOT_ENTRY_SIZE;
- base_got = htab->elf.sgotplt;
- }
- else
- {
- plt_index = h->plt.offset / plt_entry_size;
- off = plt_index * GOT_ENTRY_SIZE;
- base_got = htab->elf.igotplt;
- }
-
- if (h->dynindx == -1
- || h->forced_local
- || info->symbolic)
- {
- /* This references the local defitionion. 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, relocation,
- base_got->contents + off);
- /* Note that this is harmless for the GOTPLT64
- case, as -1 | 1 still is -1. */
- h->got.offset |= 1;
- }
- }
- }
-
- relocation = (base_got->output_section->vma
- + base_got->output_offset + off);
-
- goto do_relocation;
}
}
&& (h->needs_copy
|| eh->needs_copy
|| h->root.type == bfd_link_hash_undefined)
- && IS_X86_64_PCREL_TYPE (r_type))
+ && (IS_X86_64_PCREL_TYPE (r_type)
+ || r_type == R_X86_64_SIZE32
+ || r_type == R_X86_64_SIZE64))
&& (h == NULL
|| ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
&& !resolved_to_zero)
&& !local_undefweak)
{
Elf_Internal_Rela rela;
+ asection *relgot = htab->elf.srelgot;
/* This symbol has an entry in the global offset table. Set it
up. */
if (h->def_regular
&& h->type == STT_GNU_IFUNC)
{
- if (bfd_link_pic (info))
+ if (h->plt.offset == (bfd_vma) -1)
+ {
+ /* STT_GNU_IFUNC is referenced without PLT. */
+ if (htab->elf.splt == NULL)
+ {
+ /* use .rel[a].iplt section to store .got relocations
+ in static executable. */
+ relgot = htab->elf.irelplt;
+ }
+ if (SYMBOL_REFERENCES_LOCAL (info, h))
+ {
+ rela.r_info = htab->r_info (0,
+ R_X86_64_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
+ goto do_glob_dat;
+ }
+ else if (bfd_link_pic (info))
{
/* Generate R_X86_64_GLOB_DAT. */
goto do_glob_dat;
rela.r_addend = 0;
}
- elf_append_rela (output_bfd, htab->elf.srelgot, &rela);
+ elf_append_rela (output_bfd, relgot, &rela);
}
if (h->needs_copy)
elf_section_data (htab->elf.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,
- elf_x86_64_finish_local_dynamic_symbol,
- info);
-
/* Fill PLT entries for undefined weak symbols in PIE. */
if (bfd_link_pie (info))
bfd_hash_traverse (&info->hash->table,
return TRUE;
}
+/* Fill PLT/GOT entries and allocate dynamic relocations for local
+ STT_GNU_IFUNC symbols, which aren't in the ELF linker hash table.
+ It has to be done before elf_link_sort_relocs is called so that
+ dynamic relocations are properly sorted. */
+
+static bfd_boolean
+elf_x86_64_output_arch_local_syms
+ (bfd *output_bfd ATTRIBUTE_UNUSED,
+ struct bfd_link_info *info,
+ void *flaginfo ATTRIBUTE_UNUSED,
+ int (*func) (void *, const char *,
+ Elf_Internal_Sym *,
+ asection *,
+ struct elf_link_hash_entry *) ATTRIBUTE_UNUSED)
+{
+ struct elf_x86_64_link_hash_table *htab = elf_x86_64_hash_table (info);
+ if (htab == NULL)
+ return FALSE;
+
+ /* Fill PLT and GOT entries for local STT_GNU_IFUNC symbols. */
+ htab_traverse (htab->loc_hash_table,
+ elf_x86_64_finish_local_dynamic_symbol,
+ info);
+
+ return TRUE;
+}
+
/* Return an array of PLT entry symbol values. */
static bfd_vma *
#define elf_backend_create_dynamic_sections elf_x86_64_create_dynamic_sections
#define elf_backend_finish_dynamic_sections elf_x86_64_finish_dynamic_sections
#define elf_backend_finish_dynamic_symbol elf_x86_64_finish_dynamic_symbol
+#define elf_backend_output_arch_local_syms elf_x86_64_output_arch_local_syms
#define elf_backend_gc_mark_hook elf_x86_64_gc_mark_hook
#define elf_backend_grok_prstatus elf_x86_64_grok_prstatus
#define elf_backend_grok_psinfo elf_x86_64_grok_psinfo