/* X86-64 specific support for ELF
- Copyright (C) 2000-2015 Free Software Foundation, Inc.
+ Copyright (C) 2000-2016 Free Software Foundation, Inc.
Contributed by Jan Hubicka <jh@suse.cz>.
This file is part of BFD, the Binary File Descriptor library.
#include "dwarf2.h"
#include "libiberty.h"
+#include "opcode/i386.h"
#include "elf/x86-64.h"
#ifdef CORE_HEADER
HOWTO(R_X86_64_PLT32_BND, 0, 2, 32, TRUE, 0, complain_overflow_signed,
bfd_elf_generic_reloc, "R_X86_64_PLT32_BND", FALSE, 0xffffffff, 0xffffffff,
TRUE),
+ HOWTO(R_X86_64_GOTPCRELX, 0, 2, 32, TRUE, 0, complain_overflow_signed,
+ bfd_elf_generic_reloc, "R_X86_64_GOTPCRELX", FALSE, 0xffffffff,
+ 0xffffffff, TRUE),
+ HOWTO(R_X86_64_REX_GOTPCRELX, 0, 2, 32, TRUE, 0, complain_overflow_signed,
+ bfd_elf_generic_reloc, "R_X86_64_REX_GOTPCRELX", FALSE, 0xffffffff,
+ 0xffffffff, TRUE),
/* We have a gap in the reloc numbers here.
R_X86_64_standard counts the number up to this point, and
R_X86_64_vt_offset is the value to subtract from a reloc type of
R_X86_64_GNU_VT* to form an index into this table. */
-#define R_X86_64_standard (R_X86_64_PLT32_BND + 1)
+#define R_X86_64_standard (R_X86_64_REX_GOTPCRELX + 1)
#define R_X86_64_vt_offset (R_X86_64_GNU_VTINHERIT - R_X86_64_standard)
/* GNU extension to record C++ vtable hierarchy. */
{ BFD_RELOC_X86_64_TLSDESC_CALL, R_X86_64_TLSDESC_CALL, },
{ BFD_RELOC_X86_64_TLSDESC, R_X86_64_TLSDESC, },
{ BFD_RELOC_X86_64_IRELATIVE, R_X86_64_IRELATIVE, },
- { BFD_RELOC_X86_64_PC32_BND, R_X86_64_PC32_BND,},
- { BFD_RELOC_X86_64_PLT32_BND, R_X86_64_PLT32_BND,},
+ { BFD_RELOC_X86_64_PC32_BND, R_X86_64_PC32_BND, },
+ { BFD_RELOC_X86_64_PLT32_BND, R_X86_64_PLT32_BND, },
+ { BFD_RELOC_X86_64_GOTPCRELX, R_X86_64_GOTPCRELX, },
+ { BFD_RELOC_X86_64_REX_GOTPCRELX, R_X86_64_REX_GOTPCRELX, },
{ BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, },
{ BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, },
};
#define elf_backend_arch_data &elf_x86_64_arch_bed
+/* Is a undefined weak symbol which is resolved to 0. Reference to an
+ undefined weak symbol is resolved to 0 when building executable if
+ it isn't dynamic and
+ 1. Has non-GOT/non-PLT relocations in text section. Or
+ 2. Has no GOT/PLT relocation.
+ */
+#define UNDEFINED_WEAK_RESOLVED_TO_ZERO(INFO, EH) \
+ ((EH)->elf.root.type == bfd_link_hash_undefweak \
+ && bfd_link_executable (INFO) \
+ && (elf_x86_64_hash_table (INFO)->interp == NULL \
+ || !(EH)->has_got_reloc \
+ || (EH)->has_non_got_reloc \
+ || !(INFO)->dynamic_undefined_weak))
+
/* x86-64 ELF linker hash entry. */
struct elf_x86_64_link_hash_entry
/* TRUE if symbol has at least one BND relocation. */
unsigned int has_bnd_reloc : 1;
+ /* TRUE if symbol has GOT or PLT relocations. */
+ unsigned int has_got_reloc : 1;
+
+ /* TRUE if symbol has non-GOT/non-PLT relocations in text sections. */
+ unsigned int has_non_got_reloc : 1;
+
+ /* Reference count of C/C++ function pointer relocations in read-write
+ section which can be resolved at run-time. */
+ bfd_signed_vma func_pointer_refcount;
+
/* Information about the GOT PLT entry. Filled when there are both
GOT and PLT relocations against the same function. */
union gotplt_union plt_got;
struct elf_link_hash_table elf;
/* Short-cuts to get to dynamic linker sections. */
+ asection *interp;
asection *sdynbss;
asection *srelbss;
asection *plt_eh_frame;
bfd_vma next_jump_slot_index;
/* The index of the next R_X86_64_IRELATIVE entry in .rela.plt. */
bfd_vma next_irelative_index;
+
+ /* TRUE if there are dynamic relocs against IFUNC symbols that apply
+ to read-only sections. */
+ bfd_boolean readonly_dynrelocs_against_ifunc;
};
/* Get the x86-64 ELF linker hash table from a link_info structure. */
eh->tls_type = GOT_UNKNOWN;
eh->needs_copy = 0;
eh->has_bnd_reloc = 0;
+ eh->has_got_reloc = 0;
+ eh->has_non_got_reloc = 0;
+ eh->func_pointer_refcount = 0;
eh->plt_bnd.offset = (bfd_vma) -1;
eh->plt_got.offset = (bfd_vma) -1;
eh->tlsdesc_got = (bfd_vma) -1;
ret->elf.indx = sec->id;
ret->elf.dynstr_index = htab->r_sym (rel->r_info);
ret->elf.dynindx = -1;
+ ret->func_pointer_refcount = 0;
ret->plt_got.offset = (bfd_vma) -1;
*slot = ret;
}
if (!htab->sdynbss)
abort ();
- if (info->executable)
+ if (bfd_link_executable (info))
{
/* Always allow copy relocs for building executables. */
asection *s = bfd_get_linker_section (dynobj, ".rela.bss");
if (!edir->has_bnd_reloc)
edir->has_bnd_reloc = eind->has_bnd_reloc;
+ if (!edir->has_got_reloc)
+ edir->has_got_reloc = eind->has_got_reloc;
+
+ if (!edir->has_non_got_reloc)
+ edir->has_non_got_reloc = eind->has_non_got_reloc;
+
if (eind->dyn_relocs != NULL)
{
if (edir->dyn_relocs != NULL)
dir->pointer_equality_needed |= ind->pointer_equality_needed;
}
else
- _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+ {
+ if (eind->func_pointer_refcount > 0)
+ {
+ edir->func_pointer_refcount += eind->func_pointer_refcount;
+ eind->func_pointer_refcount = 0;
+ }
+
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+ }
}
static bfd_boolean
case R_X86_64_GOTPC32_TLSDESC:
case R_X86_64_TLSDESC_CALL:
case R_X86_64_GOTTPOFF:
- if (info->executable)
+ if (bfd_link_executable (info))
{
if (h == NULL)
to_type = R_X86_64_TPOFF32;
{
unsigned int new_to_type = to_type;
- if (info->executable
+ if (bfd_link_executable (info)
&& h != NULL
&& h->dynindx == -1
&& tls_type == GOT_TLS_IE)
break;
case R_X86_64_TLSLD:
- if (info->executable)
+ if (bfd_link_executable (info))
to_type = R_X86_64_TPOFF32;
break;
/* Rename some of the generic section flags to better document how they
are used here. */
-#define need_convert_mov_to_lea sec_flg0
+#define need_convert_load sec_flg0
/* Look through the relocs for a section during the first phase, and
calculate needed space in the global offset table, procedure
asection *sreloc;
bfd_boolean use_plt_got;
- if (info->relocatable)
+ if (bfd_link_relocatable (info))
return TRUE;
BFD_ASSERT (is_x86_64_elf (abfd));
unsigned int r_type;
unsigned long r_symndx;
struct elf_link_hash_entry *h;
+ struct elf_x86_64_link_hash_entry *eh;
Elf_Internal_Sym *isym;
const char *name;
bfd_boolean size_reloc;
case R_X86_64_32S:
case R_X86_64_PC64:
case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
case R_X86_64_GOTPCREL64:
if (htab->elf.dynobj == NULL)
htab->elf.dynobj = abfd;
rel, rel_end, h, r_symndx))
return FALSE;
+ eh = (struct elf_x86_64_link_hash_entry *) h;
switch (r_type)
{
case R_X86_64_TLSLD:
goto create_got;
case R_X86_64_TPOFF32:
- if (!info->executable && ABI_64_P (abfd))
+ if (!bfd_link_executable (info) && ABI_64_P (abfd))
{
if (h)
name = h->root.root.string;
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
+ if (eh != NULL)
+ eh->has_got_reloc = 1;
break;
case R_X86_64_GOTTPOFF:
- if (!info->executable)
+ if (!bfd_link_executable (info))
info->flags |= DF_STATIC_TLS;
/* Fall through */
case R_X86_64_GOT32:
case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
case R_X86_64_TLSGD:
case R_X86_64_GOT64:
case R_X86_64_GOTPCREL64:
if (h != NULL)
{
h->got.refcount += 1;
- old_tls_type = elf_x86_64_hash_entry (h)->tls_type;
+ old_tls_type = eh->tls_type;
}
else
{
if (old_tls_type != tls_type)
{
- if (h != NULL)
- elf_x86_64_hash_entry (h)->tls_type = tls_type;
+ if (eh != NULL)
+ eh->tls_type = tls_type;
else
elf_x86_64_local_got_tls_type (abfd) [r_symndx] = tls_type;
}
case R_X86_64_GOTPC32:
case R_X86_64_GOTPC64:
create_got:
+ if (eh != NULL)
+ eh->has_got_reloc = 1;
if (htab->elf.sgot == NULL)
{
if (htab->elf.dynobj == NULL)
if (h == NULL)
continue;
+ eh->has_got_reloc = 1;
h->needs_plt = 1;
h->plt.refcount += 1;
break;
/* Let's help debug shared library creation. These relocs
cannot be used in shared libs. Don't error out for
sections we don't care about, such as debug sections or
- non-constant sections. */
- if (info->shared
+ non-constant sections, or when relocation overflow check
+ is disabled. */
+ if (!info->no_reloc_overflow_check
+ && bfd_link_pic (info)
&& (sec->flags & SEC_ALLOC) != 0
&& (sec->flags & SEC_READONLY) != 0)
{
case R_X86_64_PC64:
case R_X86_64_64:
pointer:
- if (h != NULL && info->executable)
+ 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. */
+ 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))))
{
/* If this reloc is in a read-only section, we might
need a copy reloc. We can't check reliably at this
/* We may need a .plt entry if the function this reloc
refers to is in a shared lib. */
h->plt.refcount += 1;
- if (r_type != R_X86_64_PC32
- && r_type != R_X86_64_PC32_BND
- && r_type != R_X86_64_PC64)
- h->pointer_equality_needed = 1;
+ if (r_type == R_X86_64_PC32)
+ {
+ /* Since something like ".long foo - ." may be used
+ as pointer, make sure that PLT is used if foo is
+ a function defined in a shared library. */
+ if ((sec->flags & SEC_CODE) == 0)
+ h->pointer_equality_needed = 1;
+ }
+ else if (r_type != R_X86_64_PC32_BND
+ && r_type != R_X86_64_PC64)
+ {
+ h->pointer_equality_needed = 1;
+ /* At run-time, R_X86_64_64 can be resolved for both
+ x86-64 and x32. But R_X86_64_32 and R_X86_64_32S
+ can only be resolved for x32. */
+ if ((sec->flags & SEC_READONLY) == 0
+ && (r_type == R_X86_64_64
+ || (!ABI_64_P (abfd)
+ && (r_type == R_X86_64_32
+ || r_type == R_X86_64_32S))))
+ eh->func_pointer_refcount += 1;
+ }
}
size_reloc = FALSE;
may need to keep relocations for symbols satisfied by a
dynamic library if we manage to avoid copy relocs for the
symbol. */
- if ((info->shared
+ if ((bfd_link_pic (info)
&& (sec->flags & SEC_ALLOC) != 0
&& (! IS_X86_64_PCREL_TYPE (r_type)
|| (h != NULL
- && (! SYMBOLIC_BIND (info, h)
+ && (! (bfd_link_pie (info)
+ || SYMBOLIC_BIND (info, h))
|| h->root.type == bfd_link_hash_defweak
|| !h->def_regular))))
|| (ELIMINATE_COPY_RELOCS
- && !info->shared
+ && !bfd_link_pic (info)
&& (sec->flags & SEC_ALLOC) != 0
&& h != NULL
&& (h->root.type == bfd_link_hash_defweak
/* If this is a global symbol, we count the number of
relocations we need for this symbol. */
if (h != NULL)
- {
- head = &((struct elf_x86_64_link_hash_entry *) h)->dyn_relocs;
- }
+ head = &eh->dyn_relocs;
else
{
/* Track dynamic relocs needed for local syms too.
return FALSE;
}
- if (r_type == R_X86_64_GOTPCREL
+ if ((r_type == R_X86_64_GOTPCREL
+ || r_type == R_X86_64_GOTPCRELX
+ || r_type == R_X86_64_REX_GOTPCRELX)
&& (h == NULL || h->type != STT_GNU_IFUNC))
- sec->need_convert_mov_to_lea = 1;
+ sec->need_convert_load = 1;
}
return TRUE;
bfd_signed_vma *local_got_refcounts;
const Elf_Internal_Rela *rel, *relend;
- if (info->relocatable)
+ if (bfd_link_relocatable (info))
return TRUE;
htab = elf_x86_64_hash_table (info);
unsigned long r_symndx;
unsigned int r_type;
struct elf_link_hash_entry *h = NULL;
+ bfd_boolean pointer_reloc;
r_symndx = htab->r_sym (rel->r_info);
if (r_symndx >= symtab_hdr->sh_info)
rel, relend, h, r_symndx))
return FALSE;
+ pointer_reloc = FALSE;
switch (r_type)
{
case R_X86_64_TLSLD:
case R_X86_64_GOTTPOFF:
case R_X86_64_GOT32:
case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
case R_X86_64_GOT64:
case R_X86_64_GOTPCREL64:
case R_X86_64_GOTPLT64:
}
break;
- case R_X86_64_8:
- case R_X86_64_16:
case R_X86_64_32:
- case R_X86_64_64:
case R_X86_64_32S:
+ pointer_reloc = !ABI_64_P (abfd);
+ goto pointer;
+
+ case R_X86_64_64:
+ pointer_reloc = TRUE;
+ case R_X86_64_8:
+ case R_X86_64_16:
case R_X86_64_PC8:
case R_X86_64_PC16:
case R_X86_64_PC32:
case R_X86_64_PC64:
case R_X86_64_SIZE32:
case R_X86_64_SIZE64:
- if (info->shared
+pointer:
+ if (bfd_link_pic (info)
&& (h == NULL || h->type != STT_GNU_IFUNC))
break;
/* Fall thru */
{
if (h->plt.refcount > 0)
h->plt.refcount -= 1;
+ if (pointer_reloc && (sec->flags & SEC_READONLY) == 0)
+ {
+ struct elf_x86_64_link_hash_entry *eh
+ = (struct elf_x86_64_link_hash_entry *) h;
+ if (eh->func_pointer_refcount > 0)
+ eh->func_pointer_refcount -= 1;
+ }
}
break;
return TRUE;
}
+/* Remove undefined weak symbol from the dynamic symbol table if it
+ is resolved to 0. */
+
+static bfd_boolean
+elf_x86_64_fixup_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+{
+ if (h->dynindx != -1
+ && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info,
+ elf_x86_64_hash_entry (h)))
+ {
+ h->dynindx = -1;
+ _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+ h->dynstr_index);
+ }
+ return TRUE;
+}
+
/* Adjust a symbol defined by a dynamic object and referenced by a
regular object. The current definition is in some section of the
dynamic object, but we're not including those sections. We have to
only references to the symbol are via the global offset table.
For such cases we need not do anything here; the relocations will
be handled correctly by relocate_section. */
- if (!info->executable)
+ if (!bfd_link_executable (info))
return TRUE;
/* If there are no references to this symbol that do not use the
struct elf_dyn_relocs *p;
const struct elf_backend_data *bed;
unsigned int plt_entry_size;
+ bfd_boolean resolved_to_zero;
if (h->root.type == bfd_link_hash_indirect)
return TRUE;
bed = get_elf_backend_data (info->output_bfd);
plt_entry_size = GET_PLT_ENTRY_SIZE (info->output_bfd);
+ resolved_to_zero = UNDEFINED_WEAK_RESOLVED_TO_ZERO (info, eh);
+
/* We can't use the GOT PLT if pointer equality is needed since
finish_dynamic_symbol won't clear symbol value and the dynamic
linker won't update the GOT slot. We will get into an infinite
eh->plt_got.refcount = 1;
}
+ /* Clear the reference count of function pointer relocations if
+ symbol isn't a normal function. */
+ if (h->type != STT_FUNC)
+ eh->func_pointer_refcount = 0;
+
/* 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
{
if (_bfd_elf_allocate_ifunc_dyn_relocs (info, h,
&eh->dyn_relocs,
+ &htab->readonly_dynrelocs_against_ifunc,
plt_entry_size,
plt_entry_size,
GOT_ENTRY_SIZE))
else
return FALSE;
}
+ /* Don't create the PLT entry if there are only function pointer
+ relocations which can be resolved at run-time. */
else if (htab->elf.dynamic_sections_created
- && (h->plt.refcount > 0 || eh->plt_got.refcount > 0))
+ && (h->plt.refcount > eh->func_pointer_refcount
+ || eh->plt_got.refcount > 0))
{
bfd_boolean use_plt_got;
+ /* Clear the reference count of function pointer relocations
+ if PLT is used. */
+ eh->func_pointer_refcount = 0;
+
if ((info->flags & DF_BIND_NOW) && !h->pointer_equality_needed)
{
/* Don't use the regular PLT for DF_BIND_NOW. */
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1
- && !h->forced_local)
+ && !h->forced_local
+ && !resolved_to_zero)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
- if (info->shared
+ if (bfd_link_pic (info)
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
{
asection *s = htab->elf.splt;
location in the .plt. This is required to make function
pointers compare as equal between the normal executable and
the shared library. */
- if (! info->shared
+ if (! bfd_link_pic (info)
&& !h->def_regular)
{
if (use_plt_got)
script. */
htab->elf.sgotplt->size += GOT_ENTRY_SIZE;
- /* We also need to make an entry in the .rela.plt
- section. */
- htab->elf.srelplt->size += bed->s->sizeof_rela;
- htab->elf.srelplt->reloc_count++;
+ /* There should be no PLT relocation against resolved
+ undefined weak symbol in executable. */
+ if (!resolved_to_zero)
+ {
+ /* We also need to make an entry in the .rela.plt
+ section. */
+ htab->elf.srelplt->size += bed->s->sizeof_rela;
+ htab->elf.srelplt->reloc_count++;
+ }
}
}
else
{
+ eh->plt_got.offset = (bfd_vma) -1;
h->plt.offset = (bfd_vma) -1;
h->needs_plt = 0;
}
}
else
{
+ eh->plt_got.offset = (bfd_vma) -1;
h->plt.offset = (bfd_vma) -1;
h->needs_plt = 0;
}
/* If R_X86_64_GOTTPOFF symbol is now local to the binary,
make it a R_X86_64_TPOFF32 requiring no GOT entry. */
if (h->got.refcount > 0
- && info->executable
+ && bfd_link_executable (info)
&& h->dynindx == -1
&& elf_x86_64_hash_entry (h)->tls_type == GOT_TLS_IE)
{
/* Make sure this symbol is output as a dynamic symbol.
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1
- && !h->forced_local)
+ && !h->forced_local
+ && !resolved_to_zero)
{
if (! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
dyn = htab->elf.dynamic_sections_created;
/* R_X86_64_TLSGD needs one dynamic relocation if local symbol
- and two if global.
- R_X86_64_GOTTPOFF needs one dynamic relocation. */
+ and two if global. R_X86_64_GOTTPOFF needs one dynamic
+ relocation. No dynamic relocation against resolved undefined
+ weak symbol in executable. */
if ((GOT_TLS_GD_P (tls_type) && h->dynindx == -1)
|| tls_type == GOT_TLS_IE)
htab->elf.srelgot->size += bed->s->sizeof_rela;
else if (GOT_TLS_GD_P (tls_type))
htab->elf.srelgot->size += 2 * bed->s->sizeof_rela;
else if (! GOT_TLS_GDESC_P (tls_type)
- && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ && ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ && !resolved_to_zero)
|| h->root.type != bfd_link_hash_undefweak)
- && (info->shared
+ && (bfd_link_pic (info)
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
htab->elf.srelgot->size += bed->s->sizeof_rela;
if (GOT_TLS_GDESC_P (tls_type))
space for pc-relative relocs that have become local due to symbol
visibility changes. */
- if (info->shared)
+ if (bfd_link_pic (info))
{
/* Relocs that use pc_count are those that appear on a call
insn, or certain REL relocs that can generated via assembly.
}
/* Also discard relocs on undefined weak syms with non-default
- visibility. */
+ visibility or in PIE. */
if (eh->dyn_relocs != NULL)
{
if (h->root.type == bfd_link_hash_undefweak)
{
- if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ /* Undefined weak symbol is never bound locally in shared
+ library. */
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ || resolved_to_zero)
eh->dyn_relocs = NULL;
-
- /* Make sure undefined weak symbols are output as a dynamic
- symbol in PIEs. */
else if (h->dynindx == -1
&& ! h->forced_local
&& ! bfd_elf_link_record_dynamic_symbol (info, h))
}
/* For PIE, discard space for pc-relative relocs against
symbols which turn out to need copy relocs. */
- else if (info->executable
+ else if (bfd_link_executable (info)
&& (h->needs_copy || eh->needs_copy)
&& h->def_dynamic
&& !h->def_regular)
{
/* For the non-shared case, discard space for relocs against
symbols which turn out to need copy relocs or are not
- dynamic. */
+ dynamic. Keep dynamic relocations for run-time function
+ pointer initialization. */
- if (!h->non_got_ref
+ if ((!h->non_got_ref
+ || eh->func_pointer_refcount > 0
+ || (h->root.type == bfd_link_hash_undefweak
+ && !resolved_to_zero))
&& ((h->def_dynamic
&& !h->def_regular)
|| (htab->elf.dynamic_sections_created
Undefined weak syms won't yet be marked as dynamic. */
if (h->dynindx == -1
&& ! h->forced_local
+ && ! resolved_to_zero
&& ! bfd_elf_link_record_dynamic_symbol (info, h))
return FALSE;
}
eh->dyn_relocs = NULL;
+ eh->func_pointer_refcount = 0;
keep: ;
}
info->flags |= DF_TEXTREL;
- if ((info->warn_shared_textrel && info->shared)
+ if ((info->warn_shared_textrel && bfd_link_pic (info))
|| info->error_textrel)
info->callbacks->einfo (_("%P: %B: warning: relocation against `%s' in readonly section `%A'\n"),
p->sec->owner, h->root.root.string,
return TRUE;
}
-/* Convert
+/* With the local symbol, foo, we convert
mov foo@GOTPCREL(%rip), %reg
to
lea foo(%rip), %reg
- with the local symbol, foo. */
+ and convert
+ call/jmp *foo@GOTPCREL(%rip)
+ to
+ nop call foo/jmp foo nop
+ When PIC is false, convert
+ test %reg, foo@GOTPCREL(%rip)
+ to
+ test $foo, %reg
+ and convert
+ binop foo@GOTPCREL(%rip), %reg
+ to
+ binop $foo, %reg
+ where binop is one of adc, add, and, cmp, or, sbb, sub, xor
+ instructions. */
static bfd_boolean
-elf_x86_64_convert_mov_to_lea (bfd *abfd, asection *sec,
- struct bfd_link_info *link_info)
+elf_x86_64_convert_load (bfd *abfd, asection *sec,
+ struct bfd_link_info *link_info)
{
Elf_Internal_Shdr *symtab_hdr;
Elf_Internal_Rela *internal_relocs;
bfd_boolean changed_relocs;
bfd_signed_vma *local_got_refcounts;
bfd_vma maxpagesize;
+ bfd_boolean is_pic;
+ bfd_boolean require_reloc_pc32;
/* Don't even try to convert non-ELF outputs. */
if (!is_elf_hash_table (link_info->hash))
/* Nothing to do if there is no need or no output. */
if ((sec->flags & (SEC_CODE | SEC_RELOC)) != (SEC_CODE | SEC_RELOC)
- || sec->need_convert_mov_to_lea == 0
+ || sec->need_convert_load == 0
|| bfd_is_abs_section (sec->output_section))
return TRUE;
goto error_return;
}
+ is_pic = bfd_link_pic (link_info);
+
+ /* TRUE if we can convert only to R_X86_64_PC32. Enable it for
+ --no-relax. */
+ require_reloc_pc32
+ = link_info->disable_target_specific_optimizations > 1;
+
irelend = internal_relocs + sec->reloc_count;
for (irel = internal_relocs; irel < irelend; irel++)
{
asection *tsec;
char symtype;
bfd_vma toff, roff;
- enum {
- none, local, global
- } convert_mov_to_lea;
+ bfd_signed_vma raddend;
unsigned int opcode;
+ unsigned int modrm;
+ bfd_boolean relocx;
+ bfd_boolean to_reloc_pc32;
- if (r_type != R_X86_64_GOTPCREL)
+ relocx = (r_type == R_X86_64_GOTPCRELX
+ || r_type == R_X86_64_REX_GOTPCRELX);
+ if (!relocx && r_type != R_X86_64_GOTPCREL)
continue;
roff = irel->r_offset;
+ if (roff < (r_type == R_X86_64_REX_GOTPCRELX ? 3 : 2))
+ continue;
- if (roff < 2)
+ raddend = irel->r_addend;
+ /* Addend for 32-bit PC-relative relocation must be -4. */
+ if (raddend != -4)
continue;
opcode = bfd_get_8 (abfd, contents + roff - 2);
- /* PR ld/18591: Don't convert R_X86_64_GOTPCREL relocation if it
- isn't for mov instruction. */
+ /* Convert mov to lea since it has been done for a while. */
if (opcode != 0x8b)
- continue;
+ {
+ /* Only convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX
+ for call, jmp or one of adc, add, and, cmp, or, sbb, sub,
+ test, xor instructions. */
+ if (!relocx)
+ continue;
+ }
- tsec = NULL;
- convert_mov_to_lea = none;
+ /* We convert only to R_X86_64_PC32:
+ 1. Branch.
+ 2. R_X86_64_GOTPCREL since we can't modify REX byte.
+ 3. require_reloc_pc32 is true.
+ 4. PIC.
+ */
+ to_reloc_pc32 = (opcode == 0xff
+ || !relocx
+ || require_reloc_pc32
+ || is_pic);
/* Get the symbol referred to by the reloc. */
if (r_symndx < symtab_hdr->sh_info)
{
Elf_Internal_Sym *isym;
- /* Silence older GCC warning. */
- h = NULL;
-
isym = bfd_sym_from_r_symndx (&htab->sym_cache,
abfd, r_symndx);
symtype = ELF_ST_TYPE (isym->st_info);
- /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation and
- skip relocation against undefined symbols. */
- if (symtype != STT_GNU_IFUNC && isym->st_shndx != SHN_UNDEF)
- {
- if (isym->st_shndx == SHN_ABS)
- tsec = bfd_abs_section_ptr;
- else if (isym->st_shndx == SHN_COMMON)
- tsec = bfd_com_section_ptr;
- else if (isym->st_shndx == SHN_X86_64_LCOMMON)
- tsec = &_bfd_elf_large_com_section;
- else
- tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+ /* STT_GNU_IFUNC must keep GOTPCREL relocations and skip
+ relocation against undefined symbols. */
+ if (symtype == STT_GNU_IFUNC || isym->st_shndx == SHN_UNDEF)
+ continue;
- toff = isym->st_value;
- convert_mov_to_lea = local;
- }
+ if (isym->st_shndx == SHN_ABS)
+ tsec = bfd_abs_section_ptr;
+ else if (isym->st_shndx == SHN_COMMON)
+ tsec = bfd_com_section_ptr;
+ else if (isym->st_shndx == SHN_X86_64_LCOMMON)
+ tsec = &_bfd_elf_large_com_section;
+ else
+ tsec = bfd_section_from_elf_index (abfd, isym->st_shndx);
+
+ h = NULL;
+ toff = isym->st_value;
}
else
{
|| h->root.type == bfd_link_hash_warning)
h = (struct elf_link_hash_entry *) h->root.u.i.link;
- /* STT_GNU_IFUNC must keep R_X86_64_GOTPCREL relocation. We also
- avoid optimizing _DYNAMIC since ld.so may use its link-time
- address. */
- if (h->def_regular
- && h->type != STT_GNU_IFUNC
- && h != htab->elf.hdynamic
- && SYMBOL_REFERENCES_LOCAL (link_info, h))
+ /* STT_GNU_IFUNC must keep GOTPCREL relocations. We also
+ avoid optimizing GOTPCREL relocations againt _DYNAMIC
+ since ld.so may use its link-time address. */
+ if (h->type == STT_GNU_IFUNC)
+ continue;
+
+ /* Undefined weak symbol is only bound locally in executable
+ and its reference is resolved as 0 without relocation
+ overflow. We can only perform this optimization for
+ GOTPCRELX relocations since we need to modify REX byte.
+ It is OK convert mov with R_X86_64_GOTPCREL to
+ R_X86_64_PC32. */
+ if ((relocx || opcode == 0x8b)
+ && UNDEFINED_WEAK_RESOLVED_TO_ZERO (link_info,
+ elf_x86_64_hash_entry (h)))
{
+ if (opcode == 0xff)
+ {
+ /* Skip for branch instructions since R_X86_64_PC32
+ may overflow. */
+ if (require_reloc_pc32)
+ continue;
+ }
+ else if (relocx)
+ {
+ /* For non-branch instructions, we can convert to
+ R_X86_64_32/R_X86_64_32S since we know if there
+ is a REX byte. */
+ to_reloc_pc32 = FALSE;
+ }
+
+ /* Since we don't know the current PC when PIC is true,
+ we can't convert to R_X86_64_PC32. */
+ if (to_reloc_pc32 && is_pic)
+ continue;
+
+ goto convert;
+ }
+ else if ((h->def_regular
+ || h->root.type == bfd_link_hash_defined
+ || h->root.type == bfd_link_hash_defweak)
+ && h != htab->elf.hdynamic
+ && SYMBOL_REFERENCES_LOCAL (link_info, h))
+ {
+ /* bfd_link_hash_new or bfd_link_hash_undefined is
+ set by an assignment in a linker script in
+ bfd_elf_record_link_assignment. */
+ if (h->def_regular
+ && (h->root.type == bfd_link_hash_new
+ || h->root.type == bfd_link_hash_undefined))
+ {
+ /* Skip since R_X86_64_32/R_X86_64_32S may overflow. */
+ if (require_reloc_pc32)
+ continue;
+ goto convert;
+ }
tsec = h->root.u.def.section;
toff = h->root.u.def.value;
symtype = h->type;
- convert_mov_to_lea = global;
}
+ else
+ continue;
}
- if (convert_mov_to_lea == none)
- continue;
+ /* We can only estimate relocation overflow for R_X86_64_PC32. */
+ if (!to_reloc_pc32)
+ goto convert;
if (tsec->sec_info_type == SEC_INFO_TYPE_MERGE)
{
access is presumed to be an offset from "sym"; The
location of interest is just "sym". */
if (symtype == STT_SECTION)
- toff += irel->r_addend;
+ toff += raddend;
toff = _bfd_merged_section_offset (abfd, &tsec,
elf_section_data (tsec)->sec_info,
toff);
if (symtype != STT_SECTION)
- toff += irel->r_addend;
+ toff += raddend;
}
else
- toff += irel->r_addend;
+ toff += raddend;
/* Don't convert if R_X86_64_PC32 relocation overflows. */
if (tsec->output_section == sec->output_section)
}
else
{
- asection *asect;
- bfd_size_type size;
+ bfd_signed_vma distance;
/* At this point, we don't know the load addresses of TSEC
section nor SEC section. We estimate the distrance between
- SEC and TSEC. */
- size = 0;
- for (asect = sec->output_section;
- asect != NULL && asect != tsec->output_section;
- asect = asect->next)
+ SEC and TSEC. We store the estimated distances in the
+ compressed_size field of the output section, which is only
+ used to decompress the compressed input section. */
+ if (sec->output_section->compressed_size == 0)
{
- asection *i;
- for (i = asect->output_section->map_head.s;
- i != NULL;
- i = i->map_head.s)
+ asection *asect;
+ bfd_size_type size = 0;
+ for (asect = link_info->output_bfd->sections;
+ asect != NULL;
+ asect = asect->next)
{
- size = align_power (size, i->alignment_power);
- size += i->size;
+ asection *i;
+ for (i = asect->map_head.s;
+ i != NULL;
+ i = i->map_head.s)
+ {
+ size = align_power (size, i->alignment_power);
+ size += i->size;
+ }
+ asect->compressed_size = size;
}
}
- /* Don't convert R_X86_64_GOTPCREL if TSEC isn't placed after
- SEC. */
- if (asect == NULL)
+ /* Don't convert GOTPCREL relocations if TSEC isn't placed
+ after SEC. */
+ distance = (tsec->output_section->compressed_size
+ - sec->output_section->compressed_size);
+ if (distance < 0)
continue;
/* Take PT_GNU_RELRO segment into account by adding
maxpagesize. */
- if ((toff + size + maxpagesize - roff + 0x80000000)
+ if ((toff + distance + maxpagesize - roff + 0x80000000)
> 0xffffffff)
continue;
}
- bfd_put_8 (abfd, 0x8d, contents + roff - 2);
- irel->r_info = htab->r_info (r_symndx, R_X86_64_PC32);
+convert:
+ if (opcode == 0xff)
+ {
+ /* We have "call/jmp *foo@GOTPCREL(%rip)". */
+ unsigned int nop;
+ unsigned int disp;
+ bfd_vma nop_offset;
+
+ /* Convert R_X86_64_GOTPCRELX and R_X86_64_REX_GOTPCRELX to
+ R_X86_64_PC32. */
+ modrm = bfd_get_8 (abfd, contents + roff - 1);
+ if (modrm == 0x25)
+ {
+ /* Convert to "jmp foo nop". */
+ modrm = 0xe9;
+ nop = NOP_OPCODE;
+ nop_offset = irel->r_offset + 3;
+ disp = bfd_get_32 (abfd, contents + irel->r_offset);
+ irel->r_offset -= 1;
+ bfd_put_32 (abfd, disp, contents + irel->r_offset);
+ }
+ else
+ {
+ /* Convert to "nop call foo". ADDR_PREFIX_OPCODE
+ is a nop prefix. */
+ modrm = 0xe8;
+ nop = link_info->call_nop_byte;
+ if (link_info->call_nop_as_suffix)
+ {
+ nop_offset = irel->r_offset + 3;
+ disp = bfd_get_32 (abfd, contents + irel->r_offset);
+ irel->r_offset -= 1;
+ bfd_put_32 (abfd, disp, contents + irel->r_offset);
+ }
+ else
+ nop_offset = irel->r_offset - 2;
+ }
+ bfd_put_8 (abfd, nop, contents + nop_offset);
+ bfd_put_8 (abfd, modrm, contents + irel->r_offset - 1);
+ r_type = R_X86_64_PC32;
+ }
+ else
+ {
+ unsigned int rex;
+ unsigned int rex_mask = REX_R;
+
+ if (r_type == R_X86_64_REX_GOTPCRELX)
+ rex = bfd_get_8 (abfd, contents + roff - 3);
+ else
+ rex = 0;
+
+ if (opcode == 0x8b)
+ {
+ if (to_reloc_pc32)
+ {
+ /* Convert "mov foo@GOTPCREL(%rip), %reg" to
+ "lea foo(%rip), %reg". */
+ opcode = 0x8d;
+ r_type = R_X86_64_PC32;
+ }
+ else
+ {
+ /* Convert "mov foo@GOTPCREL(%rip), %reg" to
+ "mov $foo, %reg". */
+ opcode = 0xc7;
+ modrm = bfd_get_8 (abfd, contents + roff - 1);
+ modrm = 0xc0 | (modrm & 0x38) >> 3;
+ if ((rex & REX_W) != 0
+ && ABI_64_P (link_info->output_bfd))
+ {
+ /* Keep the REX_W bit in REX byte for LP64. */
+ r_type = R_X86_64_32S;
+ goto rewrite_modrm_rex;
+ }
+ else
+ {
+ /* If the REX_W bit in REX byte isn't needed,
+ use R_X86_64_32 and clear the W bit to avoid
+ sign-extend imm32 to imm64. */
+ r_type = R_X86_64_32;
+ /* Clear the W bit in REX byte. */
+ rex_mask |= REX_W;
+ goto rewrite_modrm_rex;
+ }
+ }
+ }
+ else
+ {
+ /* R_X86_64_PC32 isn't supported. */
+ if (to_reloc_pc32)
+ continue;
+
+ modrm = bfd_get_8 (abfd, contents + roff - 1);
+ if (opcode == 0x85)
+ {
+ /* Convert "test %reg, foo@GOTPCREL(%rip)" to
+ "test $foo, %reg". */
+ modrm = 0xc0 | (modrm & 0x38) >> 3;
+ opcode = 0xf7;
+ }
+ else
+ {
+ /* Convert "binop foo@GOTPCREL(%rip), %reg" to
+ "binop $foo, %reg". */
+ modrm = 0xc0 | (modrm & 0x38) >> 3 | (opcode & 0x3c);
+ opcode = 0x81;
+ }
+
+ /* Use R_X86_64_32 with 32-bit operand to avoid relocation
+ overflow when sign-extending imm32 to imm64. */
+ r_type = (rex & REX_W) != 0 ? R_X86_64_32S : R_X86_64_32;
+
+rewrite_modrm_rex:
+ bfd_put_8 (abfd, modrm, contents + roff - 1);
+
+ if (rex)
+ {
+ /* Move the R bit to the B bit in REX byte. */
+ rex = (rex & ~rex_mask) | (rex & REX_R) >> 2;
+ bfd_put_8 (abfd, rex, contents + roff - 3);
+ }
+
+ /* No addend for R_X86_64_32/R_X86_64_32S relocations. */
+ irel->r_addend = 0;
+ }
+
+ bfd_put_8 (abfd, opcode, contents + roff - 2);
+ }
+
+ irel->r_info = htab->r_info (r_symndx, r_type);
changed_contents = TRUE;
changed_relocs = TRUE;
- if (convert_mov_to_lea == local)
+ if (h)
{
- if (local_got_refcounts != NULL
- && local_got_refcounts[r_symndx] > 0)
- local_got_refcounts[r_symndx] -= 1;
+ if (h->got.refcount > 0)
+ h->got.refcount -= 1;
}
else
{
- if (h->got.refcount > 0)
- h->got.refcount -= 1;
+ if (local_got_refcounts != NULL
+ && local_got_refcounts[r_symndx] > 0)
+ local_got_refcounts[r_symndx] -= 1;
}
}
if (htab->elf.dynamic_sections_created)
{
/* Set the contents of the .interp section to the interpreter. */
- if (info->executable)
+ if (bfd_link_executable (info) && !info->nointerp)
{
s = bfd_get_linker_section (dynobj, ".interp");
if (s == NULL)
abort ();
s->size = htab->dynamic_interpreter_size;
s->contents = (unsigned char *) htab->dynamic_interpreter;
+ htab->interp = s;
}
}
{
struct elf_dyn_relocs *p;
- if (!elf_x86_64_convert_mov_to_lea (ibfd, s, info))
+ if (!elf_x86_64_convert_load (ibfd, s, info))
return FALSE;
for (p = (struct elf_dyn_relocs *)
&& (info->flags & DF_TEXTREL) == 0)
{
info->flags |= DF_TEXTREL;
- if ((info->warn_shared_textrel && info->shared)
+ if ((info->warn_shared_textrel && bfd_link_pic (info))
|| info->error_textrel)
info->callbacks->einfo (_("%P: %B: warning: relocation in readonly section `%A'\n"),
p->sec->owner, p->sec);
if (GOT_TLS_GD_P (*local_tls_type))
s->size += GOT_ENTRY_SIZE;
}
- if (info->shared
+ if (bfd_link_pic (info)
|| GOT_TLS_GD_ANY_P (*local_tls_type)
|| *local_tls_type == GOT_TLS_IE)
{
#define add_dynamic_entry(TAG, VAL) \
_bfd_elf_add_dynamic_entry (info, TAG, VAL)
- if (info->executable)
+ if (bfd_link_executable (info))
{
if (!add_dynamic_entry (DT_DEBUG, 0))
return FALSE;
if ((info->flags & DF_TEXTREL) != 0)
{
- if ((elf_tdata (output_bfd)->has_gnu_symbols
- & elf_gnu_symbol_ifunc) == elf_gnu_symbol_ifunc)
+ if (htab->readonly_dynrelocs_against_ifunc)
{
info->callbacks->einfo
(_("%P%X: read-only segment has dynamic IFUNC relocations; recompile with -fPIC\n"));
struct elf_x86_64_link_hash_table *htab;
struct bfd_link_hash_entry *base;
- if (!info->executable)
+ if (!bfd_link_executable (info))
return;
htab = elf_x86_64_hash_table (info);
&& (contents [offset - 1] & 0xf0) == 0x80));
}
+static bfd_boolean
+elf_x86_64_need_pic (bfd *input_bfd, struct elf_link_hash_entry *h,
+ reloc_howto_type *howto)
+{
+ const char *fmt;
+ const char *v;
+ const char *pic = "";
+
+ switch (ELF_ST_VISIBILITY (h->other))
+ {
+ case STV_HIDDEN:
+ v = _("hidden symbol");
+ break;
+ case STV_INTERNAL:
+ v = _("internal symbol");
+ break;
+ case STV_PROTECTED:
+ v = _("protected symbol");
+ break;
+ default:
+ v = _("symbol");
+ pic = _("; recompile with -fPIC");
+ break;
+ }
+
+ if (h->def_regular)
+ fmt = _("%B: relocation %s against %s `%s' can not be used when making a shared object%s");
+ else
+ fmt = _("%B: relocation %s against undefined %s `%s' can not be used when making a shared object%s");
+
+ (*_bfd_error_handler) (fmt, input_bfd, howto->name,
+ v, h->root.root.string, pic);
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+}
+
/* Relocate an x86_64 ELF section. */
static bfd_boolean
bfd_vma *local_got_offsets;
bfd_vma *local_tlsdesc_gotents;
Elf_Internal_Rela *rel;
+ Elf_Internal_Rela *wrel;
Elf_Internal_Rela *relend;
const unsigned int plt_entry_size = GET_PLT_ENTRY_SIZE (info->output_bfd);
elf_x86_64_set_tls_module_base (info);
- rel = relocs;
+ rel = wrel = relocs;
relend = relocs + input_section->reloc_count;
- for (; rel < relend; rel++)
+ for (; rel < relend; wrel++, rel++)
{
unsigned int r_type;
reloc_howto_type *howto;
int tls_type;
asection *base_got, *resolved_plt;
bfd_vma st_size;
+ bfd_boolean resolved_to_zero;
r_type = ELF32_R_TYPE (rel->r_info);
if (r_type == (int) R_X86_64_GNU_VTINHERIT
|| r_type == (int) R_X86_64_GNU_VTENTRY)
- continue;
+ {
+ if (wrel != rel)
+ *wrel = *rel;
+ continue;
+ }
if (r_type >= (int) R_X86_64_standard)
{
st_size = sym->st_size;
/* Relocate against local STT_GNU_IFUNC symbol. */
- if (!info->relocatable
+ if (!bfd_link_relocatable (info)
&& ELF_ST_TYPE (sym->st_info) == STT_GNU_IFUNC)
{
h = elf_x86_64_get_local_sym_hash (htab, input_bfd,
}
if (sec != NULL && discarded_section (sec))
- RELOC_AGAINST_DISCARDED_SECTION (info, input_bfd, input_section,
- rel, 1, relend, howto, 0, contents);
+ {
+ _bfd_clear_contents (howto, input_bfd, input_section,
+ contents + rel->r_offset);
+ wrel->r_offset = rel->r_offset;
+ wrel->r_info = 0;
+ wrel->r_addend = 0;
+
+ /* For ld -r, remove relocations in debug sections against
+ sections defined in discarded sections. Not done for
+ eh_frame editing code expects to be present. */
+ if (bfd_link_relocatable (info)
+ && (input_section->flags & SEC_DEBUGGING))
+ wrel--;
- if (info->relocatable)
- continue;
+ continue;
+ }
+
+ if (bfd_link_relocatable (info))
+ {
+ if (wrel != rel)
+ *wrel = *rel;
+ continue;
+ }
if (rel->r_addend == 0 && !ABI_64_P (output_bfd))
{
(*_bfd_error_handler)
(_("%B: relocation %s against STT_GNU_IFUNC "
"symbol `%s' isn't handled by %s"), input_bfd,
- x86_64_elf_howto_table[r_type].name,
- name, __FUNCTION__);
+ howto->name, name, __FUNCTION__);
bfd_set_error (bfd_error_bad_value);
return FALSE;
case R_X86_64_32S:
- if (info->shared)
+ if (bfd_link_pic (info))
abort ();
goto do_relocation;
(*_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,
- name, rel->r_addend);
+ input_bfd, howto->name, name, rel->r_addend);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
/* Generate dynamic relcoation only when there is a
non-GOT reference in a shared object. */
- if (info->shared && h->non_got_ref)
+ if (bfd_link_pic (info) && h->non_got_ref)
{
Elf_Internal_Rela outrel;
asection *sreloc;
if (h->dynindx == -1
|| h->forced_local
- || info->executable)
+ || bfd_link_executable (info))
{
/* This symbol is resolved locally. */
outrel.r_info = htab->r_info (0, R_X86_64_IRELATIVE);
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;
}
}
+ resolved_to_zero = (eh != NULL
+ && UNDEFINED_WEAK_RESOLVED_TO_ZERO (info, eh));
+
/* When generating a shared object, the relocations handled here are
copied into the output file to be resolved at run time. */
switch (r_type)
/* Relocation is to the entry for this symbol in the global
offset table. */
case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
case R_X86_64_GOTPCREL64:
/* Use global offset table entry as symbol value. */
case R_X86_64_GOTPLT64:
dyn = htab->elf.dynamic_sections_created;
- if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h)
- || (info->shared
+ if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, bfd_link_pic (info), h)
+ || (bfd_link_pic (info)
&& SYMBOL_REFERENCES_LOCAL (info, h))
|| (ELF_ST_VISIBILITY (h->other)
&& h->root.type == bfd_link_hash_undefweak))
bfd_put_64 (output_bfd, relocation,
base_got->contents + off);
- if (info->shared)
+ if (bfd_link_pic (info))
{
asection *s;
Elf_Internal_Rela outrel;
relocation = base_got->output_section->vma
+ base_got->output_offset + off;
- if (r_type != R_X86_64_GOTPCREL && r_type != R_X86_64_GOTPCREL64)
+ if (r_type != R_X86_64_GOTPCREL
+ && r_type != R_X86_64_GOTPCRELX
+ && r_type != R_X86_64_REX_GOTPCRELX
+ && r_type != R_X86_64_GOTPCREL64)
relocation -= htab->elf.sgotplt->output_section->vma
- htab->elf.sgotplt->output_offset;
symbol for shared library since it may not be local when
used as function address or with copy relocation. We also
need to make sure that a symbol is referenced locally. */
- if (info->shared && h)
+ if (bfd_link_pic (info) && h)
{
if (!h->def_regular)
{
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
- else if (!info->executable
+ else if (!bfd_link_executable (info)
&& !SYMBOL_REFERENCES_LOCAL (info, h)
&& (h->type == STT_FUNC
|| h->type == STT_OBJECT)
case R_X86_64_PC32:
case R_X86_64_PC32_BND:
/* Don't complain about -fPIC if the symbol is undefined when
- building executable. */
- if (info->shared
- && (input_section->flags & SEC_ALLOC) != 0
+ building executable unless it is unresolved weak symbol. */
+ if ((input_section->flags & SEC_ALLOC) != 0
&& (input_section->flags & SEC_READONLY) != 0
&& h != NULL
- && !(info->executable
- && h->root.type == bfd_link_hash_undefined))
+ && ((bfd_link_executable (info)
+ && h->root.type == bfd_link_hash_undefweak
+ && !resolved_to_zero)
+ || (bfd_link_pic (info)
+ && !(bfd_link_pie (info)
+ && h->root.type == bfd_link_hash_undefined))))
{
bfd_boolean fail = FALSE;
bfd_boolean branch
defined locally or for a branch. */
fail = !h->def_regular && !branch;
}
- else if (!(info->executable
+ else if (!(bfd_link_pie (info)
&& (h->needs_copy || eh->needs_copy)))
{
/* Symbol doesn't need copy reloc and isn't referenced
}
if (fail)
- {
- const char *fmt;
- const char *v;
- const char *pic = "";
-
- switch (ELF_ST_VISIBILITY (h->other))
- {
- case STV_HIDDEN:
- v = _("hidden symbol");
- break;
- case STV_INTERNAL:
- v = _("internal symbol");
- break;
- case STV_PROTECTED:
- v = _("protected symbol");
- break;
- default:
- v = _("symbol");
- pic = _("; recompile with -fPIC");
- break;
- }
-
- if (h->def_regular)
- fmt = _("%B: relocation %s against %s `%s' can not be used when making a shared object%s");
- else
- fmt = _("%B: relocation %s against undefined %s `%s' can not be used when making a shared object%s");
-
- (*_bfd_error_handler) (fmt, input_bfd,
- x86_64_elf_howto_table[r_type].name,
- v, h->root.root.string, pic);
- bfd_set_error (bfd_error_bad_value);
- return FALSE;
- }
+ return elf_x86_64_need_pic (input_bfd, h, howto);
}
/* Fall through. */
/* Don't copy a pc-relative relocation into the output file
if the symbol needs copy reloc or the symbol is undefined
- when building executable. */
- if ((info->shared
- && !(info->executable
+ when building executable. Copy dynamic function pointer
+ relocations. Don't generate dynamic relocations against
+ resolved undefined weak symbols in PIE. */
+ if ((bfd_link_pic (info)
+ && !(bfd_link_pie (info)
&& h != NULL
&& (h->needs_copy
|| eh->needs_copy
|| h->root.type == bfd_link_hash_undefined)
&& IS_X86_64_PCREL_TYPE (r_type))
&& (h == NULL
- || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
- || h->root.type != bfd_link_hash_undefweak)
+ || ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ && !resolved_to_zero)
+ || h->root.type != bfd_link_hash_undefweak))
&& ((! IS_X86_64_PCREL_TYPE (r_type)
&& r_type != R_X86_64_SIZE32
&& r_type != R_X86_64_SIZE64)
|| ! SYMBOL_CALLS_LOCAL (info, h)))
|| (ELIMINATE_COPY_RELOCS
- && !info->shared
+ && !bfd_link_pic (info)
&& h != NULL
&& h->dynindx != -1
- && !h->non_got_ref
- && ((h->def_dynamic
- && !h->def_regular)
- || h->root.type == bfd_link_hash_undefweak
+ && (!h->non_got_ref
+ || eh->func_pointer_refcount > 0
+ || (h->root.type == bfd_link_hash_undefweak
+ && !resolved_to_zero))
+ && ((h->def_dynamic && !h->def_regular)
+ /* Undefined weak symbol is bound locally when
+ PIC is false. */
|| h->root.type == bfd_link_hash_undefined)))
{
Elf_Internal_Rela outrel;
else if (h != NULL
&& h->dynindx != -1
&& (IS_X86_64_PCREL_TYPE (r_type)
- || ! info->shared
- || ! SYMBOLIC_BIND (info, h)
+ || !(bfd_link_executable (info)
+ || SYMBOLIC_BIND (info, h))
|| ! h->def_regular))
{
+ if ((r_type != R_X86_64_PC64 && r_type != R_X86_64_64)
+ && bfd_link_executable (info)
+ && h->root.type == bfd_link_hash_undefweak
+ && !resolved_to_zero)
+ return elf_x86_64_need_pic (input_bfd, h, howto);
outrel.r_info = htab->r_info (h->dynindx, r_type);
outrel.r_addend = rel->r_addend;
}
else
{
- /* This symbol is local, or marked to become local. */
- if (r_type == htab->pointer_r_type)
+ /* This symbol is local, or marked to become local.
+ When relocation overflow check is disabled, we
+ convert R_X86_64_32 to dynamic R_X86_64_RELATIVE. */
+ if (r_type == htab->pointer_r_type
+ || (r_type == R_X86_64_32
+ && info->no_reloc_overflow_check))
{
relocate = TRUE;
outrel.r_info = htab->r_info (0, R_X86_64_RELATIVE);
"symbol `%s' at 0x%lx in section `%A' is "
"out of range"),
input_bfd, input_section, addend,
- x86_64_elf_howto_table[r_type].name,
- name, (unsigned long) rel->r_offset);
+ howto->name, name,
+ (unsigned long) rel->r_offset);
else
(*_bfd_error_handler)
(_("%B: addend 0x%x in relocation %s against "
"symbol `%s' at 0x%lx in section `%A' is "
"out of range"),
input_bfd, input_section, addend,
- x86_64_elf_howto_table[r_type].name,
- name, (unsigned long) rel->r_offset);
+ howto->name, name,
+ (unsigned long) rel->r_offset);
bfd_set_error (bfd_error_bad_value);
return FALSE;
}
contents + roff + 8 + largepic);
/* Skip R_X86_64_PC32/R_X86_64_PLT32/R_X86_64_PLTOFF64. */
rel++;
+ wrel++;
continue;
}
else if (ELF32_R_TYPE (rel->r_info) == R_X86_64_GOTPC32_TLSDESC)
contents + roff + 8 + largepic);
/* Skip R_X86_64_PLT32/R_X86_64_PLTOFF64. */
rel++;
+ wrel++;
continue;
}
else if (ELF32_R_TYPE (rel->r_info) == R_X86_64_GOTPC32_TLSDESC)
"\x0f\x1f\x40\x00\x64\x8b\x04\x25\0\0\0", 12);
/* Skip R_X86_64_PC32/R_X86_64_PLT32/R_X86_64_PLTOFF64. */
rel++;
+ wrel++;
continue;
}
break;
case R_X86_64_DTPOFF32:
- if (!info->executable|| (input_section->flags & SEC_CODE) == 0)
+ if (!bfd_link_executable (info)
+ || (input_section->flags & SEC_CODE) == 0)
relocation -= elf_x86_64_dtpoff_base (info);
else
relocation = elf_x86_64_tpoff (info, relocation);
case R_X86_64_TPOFF32:
case R_X86_64_TPOFF64:
- BFD_ASSERT (info->executable);
+ BFD_ASSERT (bfd_link_executable (info));
relocation = elf_x86_64_tpoff (info, relocation);
break;
return FALSE;
}
}
+
+ if (wrel != rel)
+ *wrel = *rel;
+ }
+
+ if (wrel != rel)
+ {
+ Elf_Internal_Shdr *rel_hdr;
+ size_t deleted = rel - wrel;
+
+ rel_hdr = _bfd_elf_single_rel_hdr (input_section->output_section);
+ rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+ if (rel_hdr->sh_size == 0)
+ {
+ /* It is too late to remove an empty reloc section. Leave
+ one NONE reloc.
+ ??? What is wrong with an empty section??? */
+ rel_hdr->sh_size = rel_hdr->sh_entsize;
+ deleted -= 1;
+ }
+ rel_hdr = _bfd_elf_single_rel_hdr (input_section);
+ rel_hdr->sh_size -= rel_hdr->sh_entsize * deleted;
+ input_section->reloc_count -= deleted;
}
return TRUE;
elf_x86_64_finish_dynamic_symbol (bfd *output_bfd,
struct bfd_link_info *info,
struct elf_link_hash_entry *h,
- Elf_Internal_Sym *sym ATTRIBUTE_UNUSED)
+ Elf_Internal_Sym *sym)
{
struct elf_x86_64_link_hash_table *htab;
const struct elf_x86_64_backend_data *abed;
bfd_boolean use_plt_bnd;
struct elf_x86_64_link_hash_entry *eh;
+ bfd_boolean local_undefweak;
htab = elf_x86_64_hash_table (info);
if (htab == NULL)
eh = (struct elf_x86_64_link_hash_entry *) h;
+ /* We keep PLT/GOT entries without dynamic PLT/GOT relocations for
+ resolved undefined weak symbols in executable so that their
+ references have value 0 at run-time. */
+ local_undefweak = UNDEFINED_WEAK_RESOLVED_TO_ZERO (info, eh);
+
if (h->plt.offset != (bfd_vma) -1)
{
bfd_vma plt_index;
/* This symbol has an entry in the procedure linkage table. Set
it up. */
if ((h->dynindx == -1
- && !((h->forced_local || info->executable)
+ && !local_undefweak
+ && !((h->forced_local || bfd_link_executable (info))
&& h->def_regular
&& h->type == STT_GNU_IFUNC))
|| plt == NULL
resolved_plt->contents + plt_offset + plt_got_offset);
/* Fill in the entry in the global offset table, initially this
- points to the second part of the PLT entry. */
- bfd_put_64 (output_bfd, (plt->output_section->vma
- + plt->output_offset
- + h->plt.offset + abed->plt_lazy_offset),
- gotplt->contents + got_offset);
-
- /* Fill in the entry in the .rela.plt section. */
- rela.r_offset = (gotplt->output_section->vma
- + gotplt->output_offset
- + got_offset);
- if (h->dynindx == -1
- || ((info->executable
- || ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
- && h->def_regular
- && h->type == STT_GNU_IFUNC))
+ points to the second part of the PLT entry. Leave the entry
+ as zero for undefined weak symbol in PIE. No PLT relocation
+ against undefined weak symbol in PIE. */
+ if (!local_undefweak)
{
- /* If an STT_GNU_IFUNC symbol is locally defined, generate
- R_X86_64_IRELATIVE instead of R_X86_64_JUMP_SLOT. */
- 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);
- /* R_X86_64_IRELATIVE comes last. */
- plt_index = htab->next_irelative_index--;
- }
- else
- {
- rela.r_info = htab->r_info (h->dynindx, R_X86_64_JUMP_SLOT);
- rela.r_addend = 0;
- plt_index = htab->next_jump_slot_index++;
- }
+ bfd_put_64 (output_bfd, (plt->output_section->vma
+ + plt->output_offset
+ + h->plt.offset
+ + abed->plt_lazy_offset),
+ gotplt->contents + got_offset);
+
+ /* Fill in the entry in the .rela.plt section. */
+ rela.r_offset = (gotplt->output_section->vma
+ + gotplt->output_offset
+ + got_offset);
+ if (h->dynindx == -1
+ || ((bfd_link_executable (info)
+ || 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_X86_64_IRELATIVE instead of R_X86_64_JUMP_SLOT. */
+ 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);
+ /* R_X86_64_IRELATIVE comes last. */
+ plt_index = htab->next_irelative_index--;
+ }
+ else
+ {
+ rela.r_info = htab->r_info (h->dynindx, R_X86_64_JUMP_SLOT);
+ rela.r_addend = 0;
+ plt_index = htab->next_jump_slot_index++;
+ }
- /* Don't fill PLT entry for static executables. */
- if (plt == htab->elf.splt)
- {
- bfd_vma plt0_offset = h->plt.offset + plt_plt_insn_end;
-
- /* Put relocation index. */
- bfd_put_32 (output_bfd, plt_index,
- plt->contents + h->plt.offset + abed->plt_reloc_offset);
-
- /* Put offset for jmp .PLT0 and check for overflow. We don't
- check relocation index for overflow since branch displacement
- will overflow first. */
- if (plt0_offset > 0x80000000)
- info->callbacks->einfo (_("%F%B: branch displacement overflow in PLT entry for `%s'\n"),
- output_bfd, h->root.root.string);
- bfd_put_32 (output_bfd, - plt0_offset,
- plt->contents + h->plt.offset + plt_plt_offset);
- }
+ /* Don't fill PLT entry for static executables. */
+ if (plt == htab->elf.splt)
+ {
+ bfd_vma plt0_offset = h->plt.offset + plt_plt_insn_end;
+
+ /* Put relocation index. */
+ bfd_put_32 (output_bfd, plt_index,
+ (plt->contents + h->plt.offset
+ + abed->plt_reloc_offset));
+
+ /* Put offset for jmp .PLT0 and check for overflow. We don't
+ check relocation index for overflow since branch displacement
+ will overflow first. */
+ if (plt0_offset > 0x80000000)
+ info->callbacks->einfo (_("%F%B: branch displacement overflow in PLT entry for `%s'\n"),
+ output_bfd, h->root.root.string);
+ bfd_put_32 (output_bfd, - plt0_offset,
+ plt->contents + h->plt.offset + plt_plt_offset);
+ }
- bed = get_elf_backend_data (output_bfd);
- loc = relplt->contents + plt_index * bed->s->sizeof_rela;
- bed->s->swap_reloca_out (output_bfd, &rela, loc);
+ bed = get_elf_backend_data (output_bfd);
+ loc = relplt->contents + plt_index * bed->s->sizeof_rela;
+ bed->s->swap_reloca_out (output_bfd, &rela, loc);
+ }
}
else if (eh->plt_got.offset != (bfd_vma) -1)
{
plt->contents + plt_offset + plt_got_offset);
}
- if (!h->def_regular
+ if (!local_undefweak
+ && !h->def_regular
&& (h->plt.offset != (bfd_vma) -1
|| eh->plt_got.offset != (bfd_vma) -1))
{
sym->st_value = 0;
}
+ /* Don't generate dynamic GOT relocation against undefined weak
+ symbol in executable. */
if (h->got.offset != (bfd_vma) -1
&& ! GOT_TLS_GD_ANY_P (elf_x86_64_hash_entry (h)->tls_type)
- && elf_x86_64_hash_entry (h)->tls_type != GOT_TLS_IE)
+ && elf_x86_64_hash_entry (h)->tls_type != GOT_TLS_IE
+ && !local_undefweak)
{
Elf_Internal_Rela rela;
if (h->def_regular
&& h->type == STT_GNU_IFUNC)
{
- if (info->shared)
+ if (bfd_link_pic (info))
{
/* Generate R_X86_64_GLOB_DAT. */
goto do_glob_dat;
return TRUE;
}
}
- else if (info->shared
+ else if (bfd_link_pic (info)
&& SYMBOL_REFERENCES_LOCAL (info, h))
{
if (!h->def_regular)
info, h, NULL);
}
+/* Finish up undefined weak symbol handling in PIE. Fill its PLT entry
+ here since undefined weak symbol may not be dynamic and may not be
+ called for elf_x86_64_finish_dynamic_symbol. */
+
+static bfd_boolean
+elf_x86_64_pie_finish_undefweak_symbol (struct bfd_hash_entry *bh,
+ void *inf)
+{
+ struct elf_link_hash_entry *h = (struct elf_link_hash_entry *) bh;
+ struct bfd_link_info *info = (struct bfd_link_info *) inf;
+
+ if (h->root.type != bfd_link_hash_undefweak
+ || h->dynindx != -1)
+ return TRUE;
+
+ return elf_x86_64_finish_dynamic_symbol (info->output_bfd,
+ info, h, NULL);
+}
+
/* Used to decide how to sort relocs in an optimal manner for the
dynamic linker, before writing them out. */
static enum elf_reloc_type_class
-elf_x86_64_reloc_type_class (const struct bfd_link_info *info ATTRIBUTE_UNUSED,
+elf_x86_64_reloc_type_class (const struct bfd_link_info *info,
const asection *rel_sec ATTRIBUTE_UNUSED,
const Elf_Internal_Rela *rela)
{
+ bfd *abfd = info->output_bfd;
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+ struct elf_x86_64_link_hash_table *htab = elf_x86_64_hash_table (info);
+
+ if (htab->elf.dynsym != NULL
+ && htab->elf.dynsym->contents != NULL)
+ {
+ /* Check relocation against STT_GNU_IFUNC symbol if there are
+ dynamic symbols. */
+ unsigned long r_symndx = htab->r_sym (rela->r_info);
+ Elf_Internal_Sym sym;
+ if (!bed->s->swap_symbol_in (abfd,
+ (htab->elf.dynsym->contents
+ + r_symndx * bed->s->sizeof_sym),
+ 0, &sym))
+ abort ();
+
+ if (ELF_ST_TYPE (sym.st_info) == STT_GNU_IFUNC)
+ return reloc_class_ifunc;
+ }
+
switch ((int) ELF32_R_TYPE (rela->r_info))
{
case R_X86_64_RELATIVE:
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,
+ elf_x86_64_pie_finish_undefweak_symbol,
+ info);
+
return TRUE;
}
reloc_index = H_GET_32 (abfd, (plt_contents + plt_offset
+ bed->plt_reloc_offset));
- if (reloc_index >= count)
- abort ();
- if (plt_bnd)
+ if (reloc_index < count)
{
- /* This is the index in .plt section. */
- long plt_index = plt_offset / bed->plt_entry_size;
- /* Store VMA + the offset in .plt.bnd section. */
- plt_sym_val[reloc_index] =
- (plt_bnd->vma
- + (plt_index - 1) * sizeof (elf_x86_64_legacy_plt2_entry));
+ if (plt_bnd)
+ {
+ /* This is the index in .plt section. */
+ long plt_index = plt_offset / bed->plt_entry_size;
+ /* Store VMA + the offset in .plt.bnd section. */
+ plt_sym_val[reloc_index] =
+ (plt_bnd->vma
+ + (plt_index - 1) * sizeof (elf_x86_64_legacy_plt2_entry));
+ }
+ else
+ plt_sym_val[reloc_index] = plt->vma + plt_offset;
}
- else
- plt_sym_val[reloc_index] = plt->vma + plt_offset;
plt_offset += bed->plt_entry_size;
/* PR binutils/18437: Skip extra relocations in the .rela.plt
elf_x86_64_additional_program_headers
#define elf_backend_hash_symbol \
elf_x86_64_hash_symbol
+#define elf_backend_omit_section_dynsym \
+ ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
+#define elf_backend_fixup_symbol \
+ elf_x86_64_fixup_symbol
#include "elf64-target.h"
/* The 64-bit static TLS arena size is rounded to the nearest 16-byte
boundary. */
-#undef elf_backend_static_tls_alignment
+#undef elf_backend_static_tls_alignment
#define elf_backend_static_tls_alignment 16
/* The Solaris 2 ABI requires a plt symbol on all platforms.
Cf. Linker and Libraries Guide, Ch. 2, Link-Editor, Generating the Output
File, p.63. */
-#undef elf_backend_want_plt_sym
+#undef elf_backend_want_plt_sym
#define elf_backend_want_plt_sym 1
+#undef elf_backend_strtab_flags
+#define elf_backend_strtab_flags SHF_STRINGS
+
+static bfd_boolean
+elf64_x86_64_set_special_info_link (const bfd *ibfd ATTRIBUTE_UNUSED,
+ bfd *obfd ATTRIBUTE_UNUSED,
+ const Elf_Internal_Shdr *isection ATTRIBUTE_UNUSED,
+ Elf_Internal_Shdr *osection ATTRIBUTE_UNUSED)
+{
+ /* PR 19938: FIXME: Need to add code for setting the sh_info
+ and sh_link fields of Solaris specific section types. */
+ return FALSE;
+}
+
+#undef elf_backend_set_special_section_info_and_link
+#define elf_backend_set_special_section_info_and_link elf64_x86_64_set_special_info_link
+
#include "elf64-target.h"
/* Native Client support. */
#undef elf_backend_static_tls_alignment
#undef elf_backend_want_plt_sym
#define elf_backend_want_plt_sym 0
+#undef elf_backend_strtab_flags
+#undef elf_backend_set_special_section_info_and_link
/* NaCl uses substantially different PLT entries for the same effects. */