/* This symbol is a globally unique data object. The dynamic linker
will make sure that in the entire process there is just one symbol
with this name and type in use. BSF_OBJECT must also be set. */
- #define BSF_GNU_UNIQUE (1 << 23)
+ #define BSF_GNU_UNIQUE (1 << 23)
+ /* A secondary global symbol, overridable without warnings by
+ a regular or weak global symbol of the same name. */
+#define BSF_SECONDARY (1 << 24)
+
flagword flags;
/* A pointer to the section to which this symbol is
static const struct bfd_elf_special_section special_sections_g[] =
{
{ STRING_COMMA_LEN (".gnu.linkonce.b"), -2, SHT_NOBITS, SHF_ALLOC + SHF_WRITE },
- { STRING_COMMA_LEN (".gnu.lto_"), -1, SHT_PROGBITS, SHF_EXCLUDE },
- { STRING_COMMA_LEN (".got"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { STRING_COMMA_LEN (".gnu.lto_"), -1, SHT_PROGBITS, SHF_EXCLUDE },
+ { STRING_COMMA_LEN (".got"), 0, SHT_PROGBITS, SHF_ALLOC + SHF_WRITE },
+ { STRING_COMMA_LEN (".gnu_object_only"), 0, SHT_GNU_OBJECT_ONLY, SHF_EXCLUDE },
- { STRING_COMMA_LEN (".gnu.version"), 0, SHT_GNU_versym, 0 },
+ { STRING_COMMA_LEN (".gnu.version"), 0, SHT_GNU_versym, 0 },
{ STRING_COMMA_LEN (".gnu.version_d"), 0, SHT_GNU_verdef, 0 },
{ STRING_COMMA_LEN (".gnu.version_r"), 0, SHT_GNU_verneed, 0 },
- { STRING_COMMA_LEN (".gnu.liblist"), 0, SHT_GNU_LIBLIST, SHF_ALLOC },
- { STRING_COMMA_LEN (".gnu.conflict"), 0, SHT_RELA, SHF_ALLOC },
- { STRING_COMMA_LEN (".gnu.hash"), 0, SHT_GNU_HASH, SHF_ALLOC },
- { NULL, 0, 0, 0, 0 }
+ { STRING_COMMA_LEN (".gnu.liblist"), 0, SHT_GNU_LIBLIST, SHF_ALLOC },
+ { STRING_COMMA_LEN (".gnu.conflict"), 0, SHT_RELA, SHF_ALLOC },
+ { STRING_COMMA_LEN (".gnu.hash"), 0, SHT_GNU_HASH, SHF_ALLOC },
+ { NULL, 0, 0, 0, 0 }
};
static const struct bfd_elf_special_section special_sections_h[] =
HOWTO(R_386_GOT32X, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
bfd_elf_generic_reloc, "R_386_GOT32X",
TRUE, 0xffffffff, 0xffffffff, FALSE),
++ HOWTO(R_386_GPOFF, 0, 2, 32, FALSE, 0, complain_overflow_signed,
++ bfd_elf_generic_reloc, "R_386_GPOFF",
++ TRUE, 0xffffffff, 0xffffffff, FALSE),
/* Another gap. */
--#define R_386_ext2 (R_386_GOT32X + 1 - R_386_tls_offset)
++#define R_386_ext2 (R_386_GPOFF + 1 - R_386_tls_offset)
#define R_386_vt_offset (R_386_GNU_VTINHERIT - R_386_ext2)
/* GNU extension to record C++ vtable hierarchy. */
TRACE ("BFD_RELOC_386_GOT32X");
return &elf_howto_table[R_386_GOT32X - R_386_tls_offset];
++ case BFD_RELOC_GPREL32:
++ TRACE ("BFD_RELOC_GPREL32");
++ return &elf_howto_table[R_386_GPOFF - R_386_tls_offset];
++
case BFD_RELOC_VTABLE_INHERIT:
TRACE ("BFD_RELOC_VTABLE_INHERIT");
return &elf_howto_table[R_386_GNU_VTINHERIT - R_386_vt_offset];
if (isym == NULL)
goto error_return;
-- /* Check relocation against local STT_GNU_IFUNC symbol. */
-- if (ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
++ /* Check relocation against local STT_GNU_IFUNC symbol and
++ GPOFF relocation. */
++ if (r_type == R_386_GPOFF
++ || ELF32_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
- h = elf_i386_get_local_sym_hash (htab, abfd, rel, TRUE);
+ h = _bfd_elf_x86_get_local_sym_hash (htab, abfd, rel, TRUE);
if (h == NULL)
goto error_return;
-- /* Fake a STT_GNU_IFUNC symbol. */
-- h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
-- isym, NULL);
-- h->type = STT_GNU_IFUNC;
++ if (r_type == R_386_GPOFF)
++ /* Prepare for GP section. */
++ h->root.u.def.section
++ = bfd_section_from_elf_index (abfd, isym->st_shndx);
++ else
++ /* Fake a STT_GNU_IFUNC symbol. */
++ h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
++
++ isym, NULL);
++
++ h->type = ELF_ST_TYPE (isym->st_info);
h->def_regular = 1;
h->ref_regular = 1;
h->forced_local = 1;
goto error_return;
break;
++ case R_386_GPOFF:
++ if (eh != NULL)
++ eh->has_gpoff_reloc = 1;
++ break;
++
default:
break;
}
relocation = -elf_i386_tpoff (info, relocation);
break;
++ case R_386_GPOFF:
++ if (h == NULL || h->def_regular)
++ {
++ asection *def_sec;
++
++ if (h != NULL)
++ def_sec = h->root.u.def.section;
++ else
++ def_sec = local_sections[r_symndx];
++
++ if (htab->gp->root.u.def.section
++ != def_sec->output_section)
++ {
++ if (h != NULL && h->root.root.string != NULL)
++ _bfd_error_handler
++ /* xgettext:c-format */
++ (_("%B: symbol `%s' with GPOFF relocation "
++ "defined in %B(%A) isn't in GP section `%A'"),
++ input_bfd, h->root.root.string, def_sec->owner,
++ def_sec, htab->gp->root.u.def.section);
++ else
++ _bfd_error_handler
++ /* xgettext:c-format */
++ (_("%B: GPOFF relocation at %#Lx in section "
++ "`%A' must be against symbol defined in GP "
++ "section `%A'"),
++ input_bfd, rel->r_offset, input_section,
++ htab->gp->root.u.def.section);
++ return FALSE;
++ }
++ relocation -= (htab->gp->root.u.def.section->vma
++ + htab->gp->root.u.def.value);
++ }
++ break;
++
default:
break;
}
struct bfd_link_info *info
= (struct bfd_link_info *) inf;
++ /* Skip local symbol with GPOFF relocation. */
++ if (((struct elf_x86_link_hash_entry *) h)->has_gpoff_reloc)
++ return TRUE;
++
return elf_i386_finish_dynamic_symbol (info->output_bfd, info,
h, NULL);
}
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),
++ HOWTO(R_X86_64_GPOFF, 0, 2, 32, FALSE, 0, complain_overflow_signed,
++ bfd_elf_generic_reloc, "R_X86_64_GPOFF",
++ FALSE, MINUS_ONE, MINUS_ONE, FALSE),
/* 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_REX_GOTPCRELX + 1)
++#define R_X86_64_standard (R_X86_64_GPOFF + 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_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_GPREL32, R_X86_64_GPOFF, },
{ BFD_RELOC_VTABLE_INHERIT, R_X86_64_GNU_VTINHERIT, },
{ BFD_RELOC_VTABLE_ENTRY, R_X86_64_GNU_VTENTRY, },
};
if (isym == NULL)
goto error_return;
-- /* Check relocation against local STT_GNU_IFUNC symbol. */
-- if (ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
++ /* Check relocation against local STT_GNU_IFUNC symbol and
++ GPOFF relocation. */
++ if (r_type == R_X86_64_GPOFF
++ || ELF_ST_TYPE (isym->st_info) == STT_GNU_IFUNC)
{
- h = elf_x86_64_get_local_sym_hash (htab, abfd, rel,
- TRUE);
+ h = _bfd_elf_x86_get_local_sym_hash (htab, abfd, rel,
+ TRUE);
if (h == NULL)
goto error_return;
-- /* Fake a STT_GNU_IFUNC symbol. */
-- h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
-- isym, NULL);
-- h->type = STT_GNU_IFUNC;
++ if (r_type == R_X86_64_GPOFF)
++ /* Prepare for GP section. */
++ h->root.u.def.section
++ = bfd_section_from_elf_index (abfd, isym->st_shndx);
++ else
++ /* Fake a STT_GNU_IFUNC symbol. */
++ h->root.root.string = bfd_elf_sym_name (abfd, symtab_hdr,
++ isym, NULL);
++
++ h->type = ELF_ST_TYPE (isym->st_info);
h->def_regular = 1;
h->ref_regular = 1;
h->forced_local = 1;
goto error_return;
break;
++ case R_X86_64_GPOFF:
++ if (eh != NULL)
++ eh->has_gpoff_reloc = 1;
++ break;
++
default:
break;
}
case R_X86_64_DTPOFF64:
BFD_ASSERT ((input_section->flags & SEC_CODE) == 0);
- relocation -= elf_x86_64_dtpoff_base (info);
+ relocation -= _bfd_x86_elf_dtpoff_base (info);
+ break;
+
++ case R_X86_64_GPOFF:
++ if (h == NULL || h->def_regular)
++ {
++ asection *def_sec;
++
++ if (h != NULL)
++ def_sec = h->root.u.def.section;
++ else
++ def_sec = local_sections[r_symndx];
++
++ if (htab->gp->root.u.def.section
++ != def_sec->output_section)
++ {
++ if (h != NULL && h->root.root.string != NULL)
++ _bfd_error_handler
++ /* xgettext:c-format */
++ (_("%B: symbol `%s' with GPOFF relocation "
++ "defined in %B(%A) isn't in GP section `%A'"),
++ input_bfd, h->root.root.string, def_sec->owner,
++ def_sec, htab->gp->root.u.def.section);
++ else
++ _bfd_error_handler
++ /* xgettext:c-format */
++ (_("%B: GPOFF relocation at %#Lx in section "
++ "`%A' must be against symbol defined in GP "
++ "section `%A'"),
++ input_bfd, rel->r_offset, input_section,
++ htab->gp->root.u.def.section);
++ return FALSE;
++ }
++ relocation -= (htab->gp->root.u.def.section->vma
++ + htab->gp->root.u.def.value);
++ }
+ break;
+
default:
break;
}
struct bfd_link_info *info
= (struct bfd_link_info *) inf;
++ /* Skip local symbol with GPOFF relocation. */
++ if (((struct elf_x86_link_hash_entry *) h)->has_gpoff_reloc)
++ return TRUE;
++
return elf_x86_64_finish_dynamic_symbol (info->output_bfd,
- info, h, NULL);
+ info, h, NULL);
}
/* Finish up undefined weak symbol handling in PIE. Fill its PLT entry
int bind;
bfd *oldbfd;
bfd_boolean newdyn, olddyn, olddef, newdef, newdyncommon, olddyncommon;
- bfd_boolean newweak, oldweak, newfunc, oldfunc;
+ bfd_boolean newweak, oldweak, newfunc, oldfunc, weakbind;
const struct elf_backend_data *bed;
char *new_version;
+ bfd_boolean default_sym = *matched;
*skip = FALSE;
*override = FALSE;
Do this before setting *type_change_ok or *size_change_ok so that
we warn properly when dynamic library symbols are overridden. */
- if (newdef && !newdyn && olddyn)
+ if (newdef && !newdyn && (olddyn || h->root.ldscript_def))
newweak = FALSE;
- if (olddef && newdyn)
+ if (olddef && newdyn && !oldsecondary)
oldweak = FALSE;
/* Allow changes between different types of function symbol. */
--- /dev/null
+ /* x86 specific support for ELF
+ Copyright (C) 2017-2018 Free Software Foundation, Inc.
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+ #include "elfxx-x86.h"
+ #include "elf-vxworks.h"
+ #include "objalloc.h"
+ #include "elf/i386.h"
+ #include "elf/x86-64.h"
+
+ /* The name of the dynamic interpreter. This is put in the .interp
+ section. */
+
+ #define ELF32_DYNAMIC_INTERPRETER "/usr/lib/libc.so.1"
+ #define ELF64_DYNAMIC_INTERPRETER "/lib/ld64.so.1"
+ #define ELFX32_DYNAMIC_INTERPRETER "/lib/ldx32.so.1"
+
+ bfd_boolean
+ _bfd_x86_elf_mkobject (bfd *abfd)
+ {
+ return bfd_elf_allocate_object (abfd,
+ sizeof (struct elf_x86_obj_tdata),
+ get_elf_backend_data (abfd)->target_id);
+ }
+
+ /* _TLS_MODULE_BASE_ needs to be treated especially when linking
+ executables. Rather than setting it to the beginning of the TLS
+ section, we have to set it to the end. This function may be called
+ multiple times, it is idempotent. */
+
+ void
+ _bfd_x86_elf_set_tls_module_base (struct bfd_link_info *info)
+ {
+ struct elf_x86_link_hash_table *htab;
+ struct bfd_link_hash_entry *base;
+ const struct elf_backend_data *bed;
+
+ if (!bfd_link_executable (info))
+ return;
+
+ bed = get_elf_backend_data (info->output_bfd);
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab == NULL)
+ return;
+
+ base = htab->tls_module_base;
+ if (base == NULL)
+ return;
+
+ base->u.def.value = htab->elf.tls_size;
+ }
+
+ /* Return the base VMA address which should be subtracted from real addresses
+ when resolving @dtpoff relocation.
+ This is PT_TLS segment p_vaddr. */
+
+ bfd_vma
+ _bfd_x86_elf_dtpoff_base (struct bfd_link_info *info)
+ {
+ /* If tls_sec is NULL, we should have signalled an error already. */
+ if (elf_hash_table (info)->tls_sec == NULL)
+ return 0;
+ return elf_hash_table (info)->tls_sec->vma;
+ }
+
+ /* Allocate space in .plt, .got and associated reloc sections for
+ dynamic relocs. */
+
+ static bfd_boolean
+ elf_x86_allocate_dynrelocs (struct elf_link_hash_entry *h, void *inf)
+ {
+ struct bfd_link_info *info;
+ struct elf_x86_link_hash_table *htab;
+ struct elf_x86_link_hash_entry *eh;
+ struct elf_dyn_relocs *p;
+ unsigned int plt_entry_size;
+ bfd_boolean resolved_to_zero;
+ const struct elf_backend_data *bed;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ eh = (struct elf_x86_link_hash_entry *) h;
+
+ info = (struct bfd_link_info *) inf;
+ bed = get_elf_backend_data (info->output_bfd);
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab == NULL)
+ return FALSE;
+
+ plt_entry_size = htab->plt.plt_entry_size;
+
+ 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
+ loop at run-time. */
+ if (htab->plt_got != NULL
+ && h->type != STT_GNU_IFUNC
+ && !h->pointer_equality_needed
+ && h->plt.refcount > 0
+ && h->got.refcount > 0)
+ {
+ /* Don't use the regular PLT if there are both GOT and GOTPLT
+ reloctions. */
+ h->plt.offset = (bfd_vma) -1;
+
+ /* Use the GOT PLT. */
+ eh->plt_got.refcount = 1;
+ }
+
+ /* 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)
+ {
+ if (_bfd_elf_allocate_ifunc_dyn_relocs (info, h, &eh->dyn_relocs,
+ &htab->readonly_dynrelocs_against_ifunc,
+ plt_entry_size,
+ (htab->plt.has_plt0
+ * plt_entry_size),
+ htab->got_entry_size,
+ TRUE))
+ {
+ asection *s = htab->plt_second;
+ if (h->plt.offset != (bfd_vma) -1 && s != NULL)
+ {
+ /* Use the second PLT section if it is created. */
+ eh->plt_second.offset = s->size;
+
+ /* Make room for this entry in the second PLT section. */
+ s->size += htab->non_lazy_plt->plt_entry_size;
+ }
+
+ return TRUE;
+ }
+ 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))
+ {
+ bfd_boolean use_plt_got = eh->plt_got.refcount > 0;
+
+ /* 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
+ && !resolved_to_zero
+ && h->root.type == bfd_link_hash_undefweak)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ if (bfd_link_pic (info)
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h))
+ {
+ asection *s = htab->elf.splt;
+ asection *second_s = htab->plt_second;
+ asection *got_s = htab->plt_got;
+
+ /* If this is the first .plt entry, make room for the special
+ first entry. The .plt section is used by prelink to undo
+ prelinking for dynamic relocations. */
+ if (s->size == 0)
+ s->size = htab->plt.has_plt0 * plt_entry_size;
+
+ if (use_plt_got)
+ eh->plt_got.offset = got_s->size;
+ else
+ {
+ h->plt.offset = s->size;
+ if (second_s)
+ eh->plt_second.offset = second_s->size;
+ }
+
+ /* If this symbol is not defined in a regular file, and we are
+ not generating a shared library, then set the symbol to this
+ location in the .plt. This is required to make function
+ pointers compare as equal between the normal executable and
+ the shared library. */
+ if (! bfd_link_pic (info)
+ && !h->def_regular)
+ {
+ if (use_plt_got)
+ {
+ /* We need to make a call to the entry of the GOT PLT
+ instead of regular PLT entry. */
+ h->root.u.def.section = got_s;
+ h->root.u.def.value = eh->plt_got.offset;
+ }
+ else
+ {
+ if (second_s)
+ {
+ /* We need to make a call to the entry of the
+ second PLT instead of regular PLT entry. */
+ h->root.u.def.section = second_s;
+ h->root.u.def.value = eh->plt_second.offset;
+ }
+ else
+ {
+ h->root.u.def.section = s;
+ h->root.u.def.value = h->plt.offset;
+ }
+ }
+ }
+
+ /* Make room for this entry. */
+ if (use_plt_got)
+ got_s->size += htab->non_lazy_plt->plt_entry_size;
+ else
+ {
+ s->size += plt_entry_size;
+ if (second_s)
+ second_s->size += htab->non_lazy_plt->plt_entry_size;
+
+ /* We also need to make an entry in the .got.plt section,
+ which will be placed in the .got section by the linker
+ script. */
+ htab->elf.sgotplt->size += htab->got_entry_size;
+
+ /* 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 .rel.plt
+ section. */
+ htab->elf.srelplt->size += htab->sizeof_reloc;
+ htab->elf.srelplt->reloc_count++;
+ }
+ }
+
+ if (htab->target_os == is_vxworks && !bfd_link_pic (info))
+ {
+ /* VxWorks has a second set of relocations for each PLT entry
+ in executables. They go in a separate relocation section,
+ which is processed by the kernel loader. */
+
+ /* There are two relocations for the initial PLT entry: an
+ R_386_32 relocation for _GLOBAL_OFFSET_TABLE_ + 4 and an
+ R_386_32 relocation for _GLOBAL_OFFSET_TABLE_ + 8. */
+
+ asection *srelplt2 = htab->srelplt2;
+ if (h->plt.offset == plt_entry_size)
+ srelplt2->size += (htab->sizeof_reloc * 2);
+
+ /* There are two extra relocations for each subsequent PLT entry:
+ an R_386_32 relocation for the GOT entry, and an R_386_32
+ relocation for the PLT entry. */
+
+ srelplt2->size += (htab->sizeof_reloc * 2);
+ }
+ }
+ 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;
+ }
+
+ eh->tlsdesc_got = (bfd_vma) -1;
+
+ /* For i386, if R_386_TLS_{IE_32,IE,GOTIE} symbol is now local to the
+ binary, make it a R_386_TLS_LE_32 requiring no TLS entry. For
+ x86-64, 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
+ && bfd_link_executable (info)
+ && h->dynindx == -1
+ && (elf_x86_hash_entry (h)->tls_type & GOT_TLS_IE))
+ h->got.offset = (bfd_vma) -1;
+ else if (h->got.refcount > 0)
+ {
+ asection *s;
+ bfd_boolean dyn;
+ int tls_type = elf_x86_hash_entry (h)->tls_type;
+
+ /* 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
+ && !resolved_to_zero
+ && h->root.type == bfd_link_hash_undefweak)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+
+ s = htab->elf.sgot;
+ if (GOT_TLS_GDESC_P (tls_type))
+ {
+ eh->tlsdesc_got = htab->elf.sgotplt->size
+ - elf_x86_compute_jump_table_size (htab);
+ htab->elf.sgotplt->size += 2 * htab->got_entry_size;
+ h->got.offset = (bfd_vma) -2;
+ }
+ if (! GOT_TLS_GDESC_P (tls_type)
+ || GOT_TLS_GD_P (tls_type))
+ {
+ h->got.offset = s->size;
+ s->size += htab->got_entry_size;
+ /* R_386_TLS_GD and R_X86_64_TLSGD need 2 consecutive GOT
+ slots. */
+ if (GOT_TLS_GD_P (tls_type) || tls_type == GOT_TLS_IE_BOTH)
+ s->size += htab->got_entry_size;
+ }
+ dyn = htab->elf.dynamic_sections_created;
+ /* R_386_TLS_IE_32 needs one dynamic relocation,
+ R_386_TLS_IE resp. R_386_TLS_GOTIE needs one dynamic relocation,
+ (but if both R_386_TLS_IE_32 and R_386_TLS_IE is present, we
+ need two), R_386_TLS_GD and R_X86_64_TLSGD need one if local
+ symbol and two if global. No dynamic relocation against
+ resolved undefined weak symbol in executable. */
+ if (tls_type == GOT_TLS_IE_BOTH)
+ htab->elf.srelgot->size += 2 * htab->sizeof_reloc;
+ else if ((GOT_TLS_GD_P (tls_type) && h->dynindx == -1)
+ || (tls_type & GOT_TLS_IE))
+ htab->elf.srelgot->size += htab->sizeof_reloc;
+ else if (GOT_TLS_GD_P (tls_type))
+ htab->elf.srelgot->size += 2 * htab->sizeof_reloc;
+ else if (! GOT_TLS_GDESC_P (tls_type)
+ && ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ && !resolved_to_zero)
+ || h->root.type != bfd_link_hash_undefweak)
+ && (bfd_link_pic (info)
+ || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
+ htab->elf.srelgot->size += htab->sizeof_reloc;
+ if (GOT_TLS_GDESC_P (tls_type))
+ {
+ htab->elf.srelplt->size += htab->sizeof_reloc;
+ if (bed->target_id == X86_64_ELF_DATA)
+ htab->tlsdesc_plt = (bfd_vma) -1;
+ }
+ }
+ else
+ h->got.offset = (bfd_vma) -1;
+
+ if (eh->dyn_relocs == NULL)
+ return TRUE;
+
+ /* In the shared -Bsymbolic case, discard space allocated for
+ dynamic pc-relative relocs against symbols which turn out to be
+ defined in regular objects. For the normal shared case, discard
+ space for pc-relative relocs that have become local due to symbol
+ visibility changes. */
+
+ 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.
+ We want calls to protected symbols to resolve directly to the
+ function rather than going via the plt. If people want
+ function pointer comparisons to work as expected then they
+ should avoid writing weird assembly. */
+ if (SYMBOL_CALLS_LOCAL (info, h))
+ {
+ struct elf_dyn_relocs **pp;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
+ if (htab->target_os == is_vxworks)
+ {
+ struct elf_dyn_relocs **pp;
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ if (strcmp (p->sec->output_section->name, ".tls_vars") == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+
+ /* Also discard relocs on undefined weak syms with non-default
+ visibility or in PIE. */
+ if (eh->dyn_relocs != NULL)
+ {
+ if (h->root.type == bfd_link_hash_undefweak)
+ {
+ /* Undefined weak symbol is never bound locally in shared
+ library. */
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ || resolved_to_zero)
+ {
+ if (bed->target_id == I386_ELF_DATA
+ && h->non_got_ref)
+ {
+ /* Keep dynamic non-GOT/non-PLT relocation so
+ that we can branch to 0 without PLT. */
+ struct elf_dyn_relocs **pp;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ if (p->pc_count == 0)
+ *pp = p->next;
+ else
+ {
+ /* Remove non-R_386_PC32 relocation. */
+ p->count = p->pc_count;
+ pp = &p->next;
+ }
+
+ /* Make sure undefined weak symbols are output
+ as dynamic symbols in PIEs for dynamic non-GOT
+ non-PLT reloations. */
+ if (eh->dyn_relocs != NULL
+ && !bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+ else
+ eh->dyn_relocs = NULL;
+ }
+ else if (h->dynindx == -1
+ && !h->forced_local
+ && !bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+ else if (bfd_link_executable (info)
+ && (h->needs_copy || eh->needs_copy)
+ && h->def_dynamic
+ && !h->def_regular)
+ {
+ /* NB: needs_copy is set only for x86-64. For PIE,
+ discard space for pc-relative relocs against symbols
+ which turn out to need copy relocs. */
+ struct elf_dyn_relocs **pp;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ if (p->pc_count != 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ }
+ }
+ }
+ else if (ELIMINATE_COPY_RELOCS)
+ {
+ /* For the non-shared case, discard space for relocs against
+ symbols which turn out to need copy relocs or are not
+ dynamic. Keep dynamic relocations for run-time function
+ pointer initialization. */
+
+ if ((!h->non_got_ref
+ || (h->root.type == bfd_link_hash_undefweak
+ && !resolved_to_zero))
+ && ((h->def_dynamic
+ && !h->def_regular)
+ || (htab->elf.dynamic_sections_created
+ && (h->root.type == bfd_link_hash_undefweak
+ || h->root.type == bfd_link_hash_undefined))))
+ {
+ /* 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
+ && !resolved_to_zero
+ && h->root.type == bfd_link_hash_undefweak
+ && ! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+
+ /* If that succeeded, we know we'll be keeping all the
+ relocs. */
+ if (h->dynindx != -1)
+ goto keep;
+ }
+
+ eh->dyn_relocs = NULL;
+
+ keep: ;
+ }
+
+ /* Finally, allocate space. */
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *sreloc;
+
+ sreloc = elf_section_data (p->sec)->sreloc;
+
+ BFD_ASSERT (sreloc != NULL);
+ sreloc->size += p->count * htab->sizeof_reloc;
+ }
+
+ return TRUE;
+ }
+
+ /* Find dynamic relocs for H that apply to read-only sections. */
+
+ static asection *
+ readonly_dynrelocs (struct elf_link_hash_entry *h)
+ {
+ struct elf_dyn_relocs *p;
+
+ for (p = elf_x86_hash_entry (h)->dyn_relocs; p != NULL; p = p->next)
+ {
+ asection *s = p->sec->output_section;
+
+ if (s != NULL && (s->flags & SEC_READONLY) != 0)
+ return p->sec;
+ }
+ return NULL;
+ }
+
+ /* Set DF_TEXTREL if we find any dynamic relocs that apply to
+ read-only sections. */
+
+ static bfd_boolean
+ maybe_set_textrel (struct elf_link_hash_entry *h, void *inf)
+ {
+ asection *sec;
+
+ if (h->root.type == bfd_link_hash_indirect)
+ return TRUE;
+
+ /* Skip local IFUNC symbols. */
+ if (h->forced_local && h->type == STT_GNU_IFUNC)
+ return TRUE;
+
+ sec = readonly_dynrelocs (h);
+ if (sec != NULL)
+ {
+ struct bfd_link_info *info = (struct bfd_link_info *) inf;
+
+ info->flags |= DF_TEXTREL;
+ /* xgettext:c-format */
+ info->callbacks->minfo (_("%B: dynamic relocation against `%T' "
+ "in read-only section `%A'\n"),
+ sec->owner, h->root.root.string, sec);
+
+ if ((info->warn_shared_textrel && bfd_link_pic (info))
+ || info->error_textrel)
+ /* xgettext:c-format */
+ info->callbacks->einfo (_("%P: %B: warning: relocation against `%s' "
+ "in read-only section `%A'\n"),
+ sec->owner, h->root.root.string, sec);
+
+ /* Not an error, just cut short the traversal. */
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ /* Allocate space in .plt, .got and associated reloc sections for
+ local dynamic relocs. */
+
+ static bfd_boolean
+ elf_x86_allocate_local_dynreloc (void **slot, void *inf)
+ {
+ struct elf_link_hash_entry *h
+ = (struct elf_link_hash_entry *) *slot;
+
++ /* Skip local symbol with GPOFF relocation. */
++ if (((struct elf_x86_link_hash_entry *) h)->has_gpoff_reloc)
++ return TRUE;
++
+ if (h->type != STT_GNU_IFUNC
+ || !h->def_regular
+ || !h->ref_regular
+ || !h->forced_local
+ || h->root.type != bfd_link_hash_defined)
+ abort ();
+
+ return elf_x86_allocate_dynrelocs (h, inf);
+ }
+
+ /* Find and/or create a hash entry for local symbol. */
+
+ struct elf_link_hash_entry *
+ _bfd_elf_x86_get_local_sym_hash (struct elf_x86_link_hash_table *htab,
+ bfd *abfd, const Elf_Internal_Rela *rel,
+ bfd_boolean create)
+ {
+ struct elf_x86_link_hash_entry e, *ret;
+ asection *sec = abfd->sections;
+ hashval_t h = ELF_LOCAL_SYMBOL_HASH (sec->id,
+ htab->r_sym (rel->r_info));
+ void **slot;
+
+ e.elf.indx = sec->id;
+ e.elf.dynstr_index = htab->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 elf_x86_link_hash_entry *) *slot;
+ return &ret->elf;
+ }
+
+ ret = (struct elf_x86_link_hash_entry *)
+ objalloc_alloc ((struct objalloc *) htab->loc_hash_memory,
+ sizeof (struct elf_x86_link_hash_entry));
+ if (ret)
+ {
+ memset (ret, 0, sizeof (*ret));
+ ret->elf.indx = sec->id;
+ ret->elf.dynstr_index = htab->r_sym (rel->r_info);
+ ret->elf.dynindx = -1;
+ ret->plt_got.offset = (bfd_vma) -1;
+ *slot = ret;
+ }
+ return &ret->elf;
+ }
+
+ /* Create an entry in a x86 ELF linker hash table. NB: THIS MUST BE IN
+ SYNC WITH _bfd_elf_link_hash_newfunc. */
+
+ struct bfd_hash_entry *
+ _bfd_x86_elf_link_hash_newfunc (struct bfd_hash_entry *entry,
+ struct bfd_hash_table *table,
+ const char *string)
+ {
+ /* Allocate the structure if it has not already been allocated by a
+ subclass. */
+ if (entry == NULL)
+ {
+ entry = (struct bfd_hash_entry *)
+ bfd_hash_allocate (table,
+ sizeof (struct elf_x86_link_hash_entry));
+ if (entry == NULL)
+ return entry;
+ }
+
+ /* Call the allocation method of the superclass. */
+ entry = _bfd_link_hash_newfunc (entry, table, string);
+ if (entry != NULL)
+ {
+ struct elf_x86_link_hash_entry *eh
+ = (struct elf_x86_link_hash_entry *) entry;
+ struct elf_link_hash_table *htab
+ = (struct elf_link_hash_table *) table;
+
+ memset (&eh->elf.size, 0,
+ (sizeof (struct elf_x86_link_hash_entry)
+ - offsetof (struct elf_link_hash_entry, size)));
+ /* Set local fields. */
+ eh->elf.indx = -1;
+ eh->elf.dynindx = -1;
+ eh->elf.got = htab->init_got_refcount;
+ eh->elf.plt = htab->init_plt_refcount;
+ /* Assume that we have been called by a non-ELF symbol reader.
+ This flag is then reset by the code which reads an ELF input
+ file. This ensures that a symbol created by a non-ELF symbol
+ reader will have the flag set correctly. */
+ eh->elf.non_elf = 1;
+ eh->plt_second.offset = (bfd_vma) -1;
+ eh->plt_got.offset = (bfd_vma) -1;
+ eh->tlsdesc_got = (bfd_vma) -1;
+ eh->zero_undefweak = 1;
+ }
+
+ 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. */
+
+ hashval_t
+ _bfd_x86_elf_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. */
+
+ int
+ _bfd_x86_elf_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;
+ }
+
+ /* Destroy an x86 ELF linker hash table. */
+
+ static void
+ elf_x86_link_hash_table_free (bfd *obfd)
+ {
+ struct elf_x86_link_hash_table *htab
+ = (struct elf_x86_link_hash_table *) obfd->link.hash;
+
+ if (htab->loc_hash_table)
+ htab_delete (htab->loc_hash_table);
+ if (htab->loc_hash_memory)
+ objalloc_free ((struct objalloc *) htab->loc_hash_memory);
+ _bfd_elf_link_hash_table_free (obfd);
+ }
+
+ static bfd_boolean
+ elf_i386_is_reloc_section (const char *secname)
+ {
+ return CONST_STRNEQ (secname, ".rel");
+ }
+
+ static bfd_boolean
+ elf_x86_64_is_reloc_section (const char *secname)
+ {
+ return CONST_STRNEQ (secname, ".rela");
+ }
+
+ /* Create an x86 ELF linker hash table. */
+
+ struct bfd_link_hash_table *
+ _bfd_x86_elf_link_hash_table_create (bfd *abfd)
+ {
+ struct elf_x86_link_hash_table *ret;
+ const struct elf_backend_data *bed;
+ bfd_size_type amt = sizeof (struct elf_x86_link_hash_table);
+
+ ret = (struct elf_x86_link_hash_table *) bfd_zmalloc (amt);
+ if (ret == NULL)
+ return NULL;
+
+ bed = get_elf_backend_data (abfd);
+ if (!_bfd_elf_link_hash_table_init (&ret->elf, abfd,
+ _bfd_x86_elf_link_hash_newfunc,
+ sizeof (struct elf_x86_link_hash_entry),
+ bed->target_id))
+ {
+ free (ret);
+ return NULL;
+ }
+
+ if (bed->target_id == X86_64_ELF_DATA)
+ {
+ ret->is_reloc_section = elf_x86_64_is_reloc_section;
+ ret->dt_reloc = DT_RELA;
+ ret->dt_reloc_sz = DT_RELASZ;
+ ret->dt_reloc_ent = DT_RELAENT;
+ ret->got_entry_size = 8;
+ ret->tls_get_addr = "__tls_get_addr";
+ }
+ if (ABI_64_P (abfd))
+ {
+ ret->sizeof_reloc = sizeof (Elf64_External_Rela);
+ ret->pointer_r_type = R_X86_64_64;
+ ret->dynamic_interpreter = ELF64_DYNAMIC_INTERPRETER;
+ ret->dynamic_interpreter_size = sizeof ELF64_DYNAMIC_INTERPRETER;
+ }
+ else
+ {
+ if (bed->target_id == X86_64_ELF_DATA)
+ {
+ ret->sizeof_reloc = sizeof (Elf32_External_Rela);
+ ret->pointer_r_type = R_X86_64_32;
+ ret->dynamic_interpreter = ELFX32_DYNAMIC_INTERPRETER;
+ ret->dynamic_interpreter_size
+ = sizeof ELFX32_DYNAMIC_INTERPRETER;
+ }
+ else
+ {
+ ret->is_reloc_section = elf_i386_is_reloc_section;
+ ret->dt_reloc = DT_REL;
+ ret->dt_reloc_sz = DT_RELSZ;
+ ret->dt_reloc_ent = DT_RELENT;
+ ret->sizeof_reloc = sizeof (Elf32_External_Rel);
+ ret->got_entry_size = 4;
+ ret->pointer_r_type = R_386_32;
+ ret->dynamic_interpreter = ELF32_DYNAMIC_INTERPRETER;
+ ret->dynamic_interpreter_size
+ = sizeof ELF32_DYNAMIC_INTERPRETER;
+ ret->tls_get_addr = "___tls_get_addr";
+ }
+ }
+ ret->target_id = bed->target_id;
+ ret->target_os = get_elf_x86_backend_data (abfd)->target_os;
+
+ ret->loc_hash_table = htab_try_create (1024,
+ _bfd_x86_elf_local_htab_hash,
+ _bfd_x86_elf_local_htab_eq,
+ NULL);
+ ret->loc_hash_memory = objalloc_create ();
+ if (!ret->loc_hash_table || !ret->loc_hash_memory)
+ {
+ elf_x86_link_hash_table_free (abfd);
+ return NULL;
+ }
+ ret->elf.root.hash_table_free = elf_x86_link_hash_table_free;
+
+ return &ret->elf.root;
+ }
+
+ /* Sort relocs into address order. */
+
+ int
+ _bfd_x86_elf_compare_relocs (const void *ap, const void *bp)
+ {
+ const arelent *a = * (const arelent **) ap;
+ const arelent *b = * (const arelent **) bp;
+
+ if (a->address > b->address)
+ return 1;
+ else if (a->address < b->address)
+ return -1;
+ else
+ return 0;
+ }
+
+ bfd_boolean
+ _bfd_x86_elf_link_check_relocs (bfd *abfd, struct bfd_link_info *info)
+ {
+ if (!bfd_link_relocatable (info))
+ {
+ /* Check for __tls_get_addr reference. */
+ struct elf_x86_link_hash_table *htab;
+ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab)
+ {
+ struct elf_link_hash_entry *h;
+
+ h = elf_link_hash_lookup (elf_hash_table (info),
+ htab->tls_get_addr,
+ FALSE, FALSE, FALSE);
+ if (h != NULL)
+ elf_x86_hash_entry (h)->tls_get_addr = 1;
+
+ /* "__ehdr_start" will be defined by linker as a hidden symbol
+ later if it is referenced and not defined. */
+ h = elf_link_hash_lookup (elf_hash_table (info),
+ "__ehdr_start",
+ FALSE, FALSE, FALSE);
+ if (h != NULL
+ && (h->root.type == bfd_link_hash_new
+ || h->root.type == bfd_link_hash_undefined
+ || h->root.type == bfd_link_hash_undefweak
+ || h->root.type == bfd_link_hash_common))
+ {
+ elf_x86_hash_entry (h)->local_ref = 2;
+ elf_x86_hash_entry (h)->linker_def = 1;
+ }
++
++ /* Cache and hide __gp symbol. */
++ h = elf_link_hash_lookup (elf_hash_table (info), "__gp", FALSE,
++ FALSE, FALSE);
++ if (h != NULL)
++ {
++ htab->gp = h;
++ /* It should be defined by elf_x86_64_setup_gp later. */
++ if (h->root.type != bfd_link_hash_defined
++ && h->root.type != bfd_link_hash_defweak)
++ h->def_regular = 1;
++ h->other = STV_HIDDEN;
++ bed = get_elf_backend_data (info->output_bfd);
++ bed->elf_backend_hide_symbol (info, h, TRUE);
++ }
+ }
+ }
+
+ /* Invoke the regular ELF backend linker to do all the work. */
+ return _bfd_elf_link_check_relocs (abfd, info);
+ }
+
+ /* Set the sizes of the dynamic sections. */
+
+ bfd_boolean
+ _bfd_x86_elf_size_dynamic_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+ {
+ struct elf_x86_link_hash_table *htab;
+ bfd *dynobj;
+ asection *s;
+ bfd_boolean relocs;
+ bfd *ibfd;
+ const struct elf_backend_data *bed
+ = get_elf_backend_data (output_bfd);
+
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab == NULL)
+ return FALSE;
+ dynobj = htab->elf.dynobj;
+ if (dynobj == NULL)
+ abort ();
+
+ /* Set up .got offsets for local syms, and space for local dynamic
+ relocs. */
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link.next)
+ {
+ bfd_signed_vma *local_got;
+ bfd_signed_vma *end_local_got;
+ char *local_tls_type;
+ bfd_vma *local_tlsdesc_gotent;
+ bfd_size_type locsymcount;
+ Elf_Internal_Shdr *symtab_hdr;
+ asection *srel;
+
+ if (! is_x86_elf (ibfd, htab))
+ continue;
+
+ for (s = ibfd->sections; s != NULL; s = s->next)
+ {
+ struct elf_dyn_relocs *p;
+
+ for (p = ((struct elf_dyn_relocs *)
+ elf_section_data (s)->local_dynrel);
+ p != NULL;
+ p = p->next)
+ {
+ if (!bfd_is_abs_section (p->sec)
+ && bfd_is_abs_section (p->sec->output_section))
+ {
+ /* Input section has been discarded, either because
+ it is a copy of a linkonce section or due to
+ linker script /DISCARD/, so we'll be discarding
+ the relocs too. */
+ }
+ else if (htab->target_os == is_vxworks
+ && strcmp (p->sec->output_section->name,
+ ".tls_vars") == 0)
+ {
+ /* Relocations in vxworks .tls_vars sections are
+ handled specially by the loader. */
+ }
+ else if (p->count != 0)
+ {
+ srel = elf_section_data (p->sec)->sreloc;
+ srel->size += p->count * htab->sizeof_reloc;
+ if ((p->sec->output_section->flags & SEC_READONLY) != 0
+ && (info->flags & DF_TEXTREL) == 0)
+ {
+ info->flags |= DF_TEXTREL;
+ if ((info->warn_shared_textrel && bfd_link_pic (info))
+ || info->error_textrel)
+ /* xgettext:c-format */
+ info->callbacks->einfo
+ (_("%P: %B: warning: relocation "
+ "in read-only section `%A'\n"),
+ p->sec->owner, p->sec);
+ }
+ }
+ }
+ }
+
+ local_got = elf_local_got_refcounts (ibfd);
+ if (!local_got)
+ continue;
+
+ symtab_hdr = &elf_symtab_hdr (ibfd);
+ locsymcount = symtab_hdr->sh_info;
+ end_local_got = local_got + locsymcount;
+ local_tls_type = elf_x86_local_got_tls_type (ibfd);
+ local_tlsdesc_gotent = elf_x86_local_tlsdesc_gotent (ibfd);
+ s = htab->elf.sgot;
+ srel = htab->elf.srelgot;
+ for (; local_got < end_local_got;
+ ++local_got, ++local_tls_type, ++local_tlsdesc_gotent)
+ {
+ *local_tlsdesc_gotent = (bfd_vma) -1;
+ if (*local_got > 0)
+ {
+ if (GOT_TLS_GDESC_P (*local_tls_type))
+ {
+ *local_tlsdesc_gotent = htab->elf.sgotplt->size
+ - elf_x86_compute_jump_table_size (htab);
+ htab->elf.sgotplt->size += 2 * htab->got_entry_size;
+ *local_got = (bfd_vma) -2;
+ }
+ if (! GOT_TLS_GDESC_P (*local_tls_type)
+ || GOT_TLS_GD_P (*local_tls_type))
+ {
+ *local_got = s->size;
+ s->size += htab->got_entry_size;
+ if (GOT_TLS_GD_P (*local_tls_type)
+ || *local_tls_type == GOT_TLS_IE_BOTH)
+ s->size += htab->got_entry_size;
+ }
+ if (bfd_link_pic (info)
+ || GOT_TLS_GD_ANY_P (*local_tls_type)
+ || (*local_tls_type & GOT_TLS_IE))
+ {
+ if (*local_tls_type == GOT_TLS_IE_BOTH)
+ srel->size += 2 * htab->sizeof_reloc;
+ else if (GOT_TLS_GD_P (*local_tls_type)
+ || ! GOT_TLS_GDESC_P (*local_tls_type))
+ srel->size += htab->sizeof_reloc;
+ if (GOT_TLS_GDESC_P (*local_tls_type))
+ {
+ htab->elf.srelplt->size += htab->sizeof_reloc;
+ if (bed->target_id == X86_64_ELF_DATA)
+ htab->tlsdesc_plt = (bfd_vma) -1;
+ }
+ }
+ }
+ else
+ *local_got = (bfd_vma) -1;
+ }
+ }
+
+ if (htab->tls_ld_or_ldm_got.refcount > 0)
+ {
+ /* Allocate 2 got entries and 1 dynamic reloc for R_386_TLS_LDM
+ or R_X86_64_TLSLD relocs. */
+ htab->tls_ld_or_ldm_got.offset = htab->elf.sgot->size;
+ htab->elf.sgot->size += 2 * htab->got_entry_size;
+ htab->elf.srelgot->size += htab->sizeof_reloc;
+ }
+ else
+ htab->tls_ld_or_ldm_got.offset = -1;
+
+ /* Allocate global sym .plt and .got entries, and space for global
+ sym dynamic relocs. */
+ elf_link_hash_traverse (&htab->elf, elf_x86_allocate_dynrelocs,
+ info);
+
+ /* Allocate .plt and .got entries, and space for local symbols. */
+ htab_traverse (htab->loc_hash_table, elf_x86_allocate_local_dynreloc,
+ info);
+
+ /* For every jump slot reserved in the sgotplt, reloc_count is
+ incremented. However, when we reserve space for TLS descriptors,
+ it's not incremented, so in order to compute the space reserved
+ for them, it suffices to multiply the reloc count by the jump
+ slot size.
+
+ PR ld/13302: We start next_irelative_index at the end of .rela.plt
+ so that R_{386,X86_64}_IRELATIVE entries come last. */
+ if (htab->elf.srelplt)
+ {
+ htab->next_tls_desc_index = htab->elf.srelplt->reloc_count;
+ htab->sgotplt_jump_table_size
+ = elf_x86_compute_jump_table_size (htab);
+ htab->next_irelative_index = htab->elf.srelplt->reloc_count - 1;
+ }
+ else if (htab->elf.irelplt)
+ htab->next_irelative_index = htab->elf.irelplt->reloc_count - 1;
+
+ if (htab->tlsdesc_plt)
+ {
+ /* NB: tlsdesc_plt is set only for x86-64. If we're not using
+ lazy TLS relocations, don't generate the PLT and GOT entries
+ they require. */
+ if ((info->flags & DF_BIND_NOW))
+ htab->tlsdesc_plt = 0;
+ else
+ {
+ htab->tlsdesc_got = htab->elf.sgot->size;
+ htab->elf.sgot->size += htab->got_entry_size;
+ /* Reserve room for the initial entry.
+ FIXME: we could probably do away with it in this case. */
+ if (htab->elf.splt->size == 0)
+ htab->elf.splt->size = htab->plt.plt_entry_size;
+ htab->tlsdesc_plt = htab->elf.splt->size;
+ htab->elf.splt->size += htab->plt.plt_entry_size;
+ }
+ }
+
+ if (htab->elf.sgotplt)
+ {
+ /* Don't allocate .got.plt section if there are no GOT nor PLT
+ entries and there is no reference to _GLOBAL_OFFSET_TABLE_. */
+ if ((htab->elf.hgot == NULL
+ || !htab->elf.hgot->ref_regular_nonweak)
+ && (htab->elf.sgotplt->size == bed->got_header_size)
+ && (htab->elf.splt == NULL
+ || htab->elf.splt->size == 0)
+ && (htab->elf.sgot == NULL
+ || htab->elf.sgot->size == 0)
+ && (htab->elf.iplt == NULL
+ || htab->elf.iplt->size == 0)
+ && (htab->elf.igotplt == NULL
+ || htab->elf.igotplt->size == 0))
+ htab->elf.sgotplt->size = 0;
+ }
+
+ if (_bfd_elf_eh_frame_present (info))
+ {
+ if (htab->plt_eh_frame != NULL
+ && htab->elf.splt != NULL
+ && htab->elf.splt->size != 0
+ && !bfd_is_abs_section (htab->elf.splt->output_section))
+ htab->plt_eh_frame->size = htab->plt.eh_frame_plt_size;
+
+ if (htab->plt_got_eh_frame != NULL
+ && htab->plt_got != NULL
+ && htab->plt_got->size != 0
+ && !bfd_is_abs_section (htab->plt_got->output_section))
+ htab->plt_got_eh_frame->size
+ = htab->non_lazy_plt->eh_frame_plt_size;
+
+ /* Unwind info for the second PLT and .plt.got sections are
+ identical. */
+ if (htab->plt_second_eh_frame != NULL
+ && htab->plt_second != NULL
+ && htab->plt_second->size != 0
+ && !bfd_is_abs_section (htab->plt_second->output_section))
+ htab->plt_second_eh_frame->size
+ = htab->non_lazy_plt->eh_frame_plt_size;
+ }
+
+ /* We now have determined the sizes of the various dynamic sections.
+ Allocate memory for them. */
+ relocs = FALSE;
+ for (s = dynobj->sections; s != NULL; s = s->next)
+ {
+ bfd_boolean strip_section = TRUE;
+
+ if ((s->flags & SEC_LINKER_CREATED) == 0)
+ continue;
+
+ if (s == htab->elf.splt
+ || s == htab->elf.sgot)
+ {
+ /* Strip this section if we don't need it; see the
+ comment below. */
+ /* We'd like to strip these sections if they aren't needed, but if
+ we've exported dynamic symbols from them we must leave them.
+ It's too late to tell BFD to get rid of the symbols. */
+
+ if (htab->elf.hplt != NULL)
+ strip_section = FALSE;
+ }
+ else if (s == htab->elf.sgotplt
+ || s == htab->elf.iplt
+ || s == htab->elf.igotplt
+ || s == htab->plt_second
+ || s == htab->plt_got
+ || s == htab->plt_eh_frame
+ || s == htab->plt_got_eh_frame
+ || s == htab->plt_second_eh_frame
+ || s == htab->elf.sdynbss
+ || s == htab->elf.sdynrelro)
+ {
+ /* Strip these too. */
+ }
+ else if (htab->is_reloc_section (bfd_get_section_name (dynobj, s)))
+ {
+ if (s->size != 0
+ && s != htab->elf.srelplt
+ && s != htab->srelplt2)
+ relocs = TRUE;
+
+ /* We use the reloc_count field as a counter if we need
+ to copy relocs into the output file. */
+ if (s != htab->elf.srelplt)
+ s->reloc_count = 0;
+ }
+ else
+ {
+ /* It's not one of our sections, so don't allocate space. */
+ continue;
+ }
+
+ if (s->size == 0)
+ {
+ /* If we don't need this section, strip it from the
+ output file. This is mostly to handle .rel.bss and
+ .rel.plt. We must create both sections in
+ create_dynamic_sections, because they must be created
+ before the linker maps input sections to output
+ sections. The linker does that before
+ adjust_dynamic_symbol is called, and it is that
+ function which decides whether anything needs to go
+ into these sections. */
+ if (strip_section)
+ s->flags |= SEC_EXCLUDE;
+ continue;
+ }
+
+ if ((s->flags & SEC_HAS_CONTENTS) == 0)
+ continue;
+
+ /* Allocate memory for the section contents. We use bfd_zalloc
+ here in case unused entries are not reclaimed before the
+ section's contents are written out. This should not happen,
+ but this way if it does, we get a R_386_NONE or R_X86_64_NONE
+ reloc instead of garbage. */
+ s->contents = (unsigned char *) bfd_zalloc (dynobj, s->size);
+ if (s->contents == NULL)
+ return FALSE;
+ }
+
+ if (htab->plt_eh_frame != NULL
+ && htab->plt_eh_frame->contents != NULL)
+ {
+ memcpy (htab->plt_eh_frame->contents,
+ htab->plt.eh_frame_plt,
+ htab->plt_eh_frame->size);
+ bfd_put_32 (dynobj, htab->elf.splt->size,
+ htab->plt_eh_frame->contents + PLT_FDE_LEN_OFFSET);
+ }
+
+ if (htab->plt_got_eh_frame != NULL
+ && htab->plt_got_eh_frame->contents != NULL)
+ {
+ memcpy (htab->plt_got_eh_frame->contents,
+ htab->non_lazy_plt->eh_frame_plt,
+ htab->plt_got_eh_frame->size);
+ bfd_put_32 (dynobj, htab->plt_got->size,
+ (htab->plt_got_eh_frame->contents
+ + PLT_FDE_LEN_OFFSET));
+ }
+
+ if (htab->plt_second_eh_frame != NULL
+ && htab->plt_second_eh_frame->contents != NULL)
+ {
+ memcpy (htab->plt_second_eh_frame->contents,
+ htab->non_lazy_plt->eh_frame_plt,
+ htab->plt_second_eh_frame->size);
+ bfd_put_32 (dynobj, htab->plt_second->size,
+ (htab->plt_second_eh_frame->contents
+ + PLT_FDE_LEN_OFFSET));
+ }
+
+ if (htab->elf.dynamic_sections_created)
+ {
+ /* Add some entries to the .dynamic section. We fill in the
+ values later, in elf_{i386,x86_64}_finish_dynamic_sections,
+ but we must add the entries now so that we get the correct
+ size for the .dynamic section. The DT_DEBUG entry is filled
+ in by the dynamic linker and used by the debugger. */
+ #define add_dynamic_entry(TAG, VAL) \
+ _bfd_elf_add_dynamic_entry (info, TAG, VAL)
+
+ if (bfd_link_executable (info))
+ {
+ if (!add_dynamic_entry (DT_DEBUG, 0))
+ return FALSE;
+ }
+
+ if (htab->elf.splt->size != 0)
+ {
+ /* DT_PLTGOT is used by prelink even if there is no PLT
+ relocation. */
+ if (!add_dynamic_entry (DT_PLTGOT, 0))
+ return FALSE;
+ }
+
+ if (htab->elf.srelplt->size != 0)
+ {
+ if (!add_dynamic_entry (DT_PLTRELSZ, 0)
+ || !add_dynamic_entry (DT_PLTREL, htab->dt_reloc)
+ || !add_dynamic_entry (DT_JMPREL, 0))
+ return FALSE;
+ }
+
+ if (htab->tlsdesc_plt
+ && (!add_dynamic_entry (DT_TLSDESC_PLT, 0)
+ || !add_dynamic_entry (DT_TLSDESC_GOT, 0)))
+ return FALSE;
+
+ if (relocs)
+ {
+ if (!add_dynamic_entry (htab->dt_reloc, 0)
+ || !add_dynamic_entry (htab->dt_reloc_sz, 0)
+ || !add_dynamic_entry (htab->dt_reloc_ent,
+ htab->sizeof_reloc))
+ return FALSE;
+
+ /* If any dynamic relocs apply to a read-only section,
+ then we need a DT_TEXTREL entry. */
+ if ((info->flags & DF_TEXTREL) == 0)
+ elf_link_hash_traverse (&htab->elf, maybe_set_textrel, info);
+
+ if ((info->flags & DF_TEXTREL) != 0)
+ {
+ if (htab->readonly_dynrelocs_against_ifunc)
+ {
+ info->callbacks->einfo
+ (_("%P%X: read-only segment has dynamic IFUNC relocations;"
+ " recompile with -fPIC\n"));
+ bfd_set_error (bfd_error_bad_value);
+ return FALSE;
+ }
+
+ if (!add_dynamic_entry (DT_TEXTREL, 0))
+ return FALSE;
+ }
+ }
+ if (htab->target_os == is_vxworks
+ && !elf_vxworks_add_dynamic_entries (output_bfd, info))
+ return FALSE;
+ }
+ #undef add_dynamic_entry
+
+ return TRUE;
+ }
+
+ /* Finish up the x86 dynamic sections. */
+
+ struct elf_x86_link_hash_table *
+ _bfd_x86_elf_finish_dynamic_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+ {
+ struct elf_x86_link_hash_table *htab;
+ const struct elf_backend_data *bed;
+ bfd *dynobj;
+ asection *sdyn;
+ bfd_byte *dyncon, *dynconend;
+ bfd_size_type sizeof_dyn;
+
+ bed = get_elf_backend_data (output_bfd);
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab == NULL)
+ return htab;
+
+ dynobj = htab->elf.dynobj;
+ sdyn = bfd_get_linker_section (dynobj, ".dynamic");
+
+ /* GOT is always created in setup_gnu_properties. But it may not be
+ needed. .got.plt section may be needed for static IFUNC. */
+ if (htab->elf.sgotplt && htab->elf.sgotplt->size > 0)
+ {
+ bfd_vma dynamic_addr;
+
+ if (bfd_is_abs_section (htab->elf.sgotplt->output_section))
+ {
+ _bfd_error_handler
+ (_("discarded output section: `%A'"), htab->elf.sgotplt);
+ return NULL;
+ }
+
+ elf_section_data (htab->elf.sgotplt->output_section)->this_hdr.sh_entsize
+ = htab->got_entry_size;
+
+ dynamic_addr = (sdyn == NULL
+ ? (bfd_vma) 0
+ : sdyn->output_section->vma + sdyn->output_offset);
+
+ /* Set the first entry in the global offset table to the address
+ of the dynamic section. Write GOT[1] and GOT[2], needed for
+ the dynamic linker. */
+ if (htab->got_entry_size == 8)
+ {
+ bfd_put_64 (output_bfd, dynamic_addr,
+ htab->elf.sgotplt->contents);
+ bfd_put_64 (output_bfd, (bfd_vma) 0,
+ htab->elf.sgotplt->contents + 8);
+ bfd_put_64 (output_bfd, (bfd_vma) 0,
+ htab->elf.sgotplt->contents + 8*2);
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, dynamic_addr,
+ htab->elf.sgotplt->contents);
+ bfd_put_32 (output_bfd, 0,
+ htab->elf.sgotplt->contents + 4);
+ bfd_put_32 (output_bfd, 0,
+ htab->elf.sgotplt->contents + 4*2);
+ }
+ }
+
+ if (!htab->elf.dynamic_sections_created)
+ return htab;
+
+ if (sdyn == NULL || htab->elf.sgot == NULL)
+ abort ();
+
+ sizeof_dyn = bed->s->sizeof_dyn;
+ dyncon = sdyn->contents;
+ dynconend = sdyn->contents + sdyn->size;
+ for (; dyncon < dynconend; dyncon += sizeof_dyn)
+ {
+ Elf_Internal_Dyn dyn;
+ asection *s;
+
+ (*bed->s->swap_dyn_in) (dynobj, dyncon, &dyn);
+
+ switch (dyn.d_tag)
+ {
+ default:
+ if (htab->target_os == is_vxworks
+ && elf_vxworks_finish_dynamic_entry (output_bfd, &dyn))
+ break;
+ continue;
+
+ case DT_PLTGOT:
+ s = htab->elf.sgotplt;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset;
+ break;
+
+ case DT_JMPREL:
+ dyn.d_un.d_ptr = htab->elf.srelplt->output_section->vma;
+ break;
+
+ case DT_PLTRELSZ:
+ s = htab->elf.srelplt->output_section;
+ dyn.d_un.d_val = s->size;
+ break;
+
+ case DT_TLSDESC_PLT:
+ s = htab->elf.splt;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset
+ + htab->tlsdesc_plt;
+ break;
+
+ case DT_TLSDESC_GOT:
+ s = htab->elf.sgot;
+ dyn.d_un.d_ptr = s->output_section->vma + s->output_offset
+ + htab->tlsdesc_got;
+ break;
+ }
+
+ (*bed->s->swap_dyn_out) (output_bfd, &dyn, dyncon);
+ }
+
+ if (htab->plt_got != NULL && htab->plt_got->size > 0)
+ elf_section_data (htab->plt_got->output_section)
+ ->this_hdr.sh_entsize = htab->non_lazy_plt->plt_entry_size;
+
+ if (htab->plt_second != NULL && htab->plt_second->size > 0)
+ elf_section_data (htab->plt_second->output_section)
+ ->this_hdr.sh_entsize = htab->non_lazy_plt->plt_entry_size;
+
+ /* Adjust .eh_frame for .plt section. */
+ if (htab->plt_eh_frame != NULL
+ && htab->plt_eh_frame->contents != NULL)
+ {
+ if (htab->elf.splt != NULL
+ && htab->elf.splt->size != 0
+ && (htab->elf.splt->flags & SEC_EXCLUDE) == 0
+ && htab->elf.splt->output_section != NULL
+ && htab->plt_eh_frame->output_section != NULL)
+ {
+ bfd_vma plt_start = htab->elf.splt->output_section->vma;
+ bfd_vma eh_frame_start = htab->plt_eh_frame->output_section->vma
+ + htab->plt_eh_frame->output_offset
+ + PLT_FDE_START_OFFSET;
+ bfd_put_signed_32 (dynobj, plt_start - eh_frame_start,
+ htab->plt_eh_frame->contents
+ + PLT_FDE_START_OFFSET);
+ }
+
+ if (htab->plt_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME)
+ {
+ if (! _bfd_elf_write_section_eh_frame (output_bfd, info,
+ htab->plt_eh_frame,
+ htab->plt_eh_frame->contents))
+ return NULL;
+ }
+ }
+
+ /* Adjust .eh_frame for .plt.got section. */
+ if (htab->plt_got_eh_frame != NULL
+ && htab->plt_got_eh_frame->contents != NULL)
+ {
+ if (htab->plt_got != NULL
+ && htab->plt_got->size != 0
+ && (htab->plt_got->flags & SEC_EXCLUDE) == 0
+ && htab->plt_got->output_section != NULL
+ && htab->plt_got_eh_frame->output_section != NULL)
+ {
+ bfd_vma plt_start = htab->plt_got->output_section->vma;
+ bfd_vma eh_frame_start = htab->plt_got_eh_frame->output_section->vma
+ + htab->plt_got_eh_frame->output_offset
+ + PLT_FDE_START_OFFSET;
+ bfd_put_signed_32 (dynobj, plt_start - eh_frame_start,
+ htab->plt_got_eh_frame->contents
+ + PLT_FDE_START_OFFSET);
+ }
+ if (htab->plt_got_eh_frame->sec_info_type == SEC_INFO_TYPE_EH_FRAME)
+ {
+ if (! _bfd_elf_write_section_eh_frame (output_bfd, info,
+ htab->plt_got_eh_frame,
+ htab->plt_got_eh_frame->contents))
+ return NULL;
+ }
+ }
+
+ /* Adjust .eh_frame for the second PLT section. */
+ if (htab->plt_second_eh_frame != NULL
+ && htab->plt_second_eh_frame->contents != NULL)
+ {
+ if (htab->plt_second != NULL
+ && htab->plt_second->size != 0
+ && (htab->plt_second->flags & SEC_EXCLUDE) == 0
+ && htab->plt_second->output_section != NULL
+ && htab->plt_second_eh_frame->output_section != NULL)
+ {
+ bfd_vma plt_start = htab->plt_second->output_section->vma;
+ bfd_vma eh_frame_start
+ = (htab->plt_second_eh_frame->output_section->vma
+ + htab->plt_second_eh_frame->output_offset
+ + PLT_FDE_START_OFFSET);
+ bfd_put_signed_32 (dynobj, plt_start - eh_frame_start,
+ htab->plt_second_eh_frame->contents
+ + PLT_FDE_START_OFFSET);
+ }
+ if (htab->plt_second_eh_frame->sec_info_type
+ == SEC_INFO_TYPE_EH_FRAME)
+ {
+ if (! _bfd_elf_write_section_eh_frame (output_bfd, info,
+ htab->plt_second_eh_frame,
+ htab->plt_second_eh_frame->contents))
+ return NULL;
+ }
+ }
+
+ if (htab->elf.sgot && htab->elf.sgot->size > 0)
+ elf_section_data (htab->elf.sgot->output_section)->this_hdr.sh_entsize
+ = htab->got_entry_size;
+
+ return htab;
+ }
+
+
+ bfd_boolean
+ _bfd_x86_elf_always_size_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+ {
+ asection *tls_sec = elf_hash_table (info)->tls_sec;
+
+ if (tls_sec)
+ {
+ struct elf_link_hash_entry *tlsbase;
+
+ tlsbase = elf_link_hash_lookup (elf_hash_table (info),
+ "_TLS_MODULE_BASE_",
+ FALSE, FALSE, FALSE);
+
+ if (tlsbase && tlsbase->type == STT_TLS)
+ {
+ struct elf_x86_link_hash_table *htab;
+ struct bfd_link_hash_entry *bh = NULL;
+ const struct elf_backend_data *bed
+ = get_elf_backend_data (output_bfd);
+
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab == NULL)
+ return FALSE;
+
+ if (!(_bfd_generic_link_add_one_symbol
+ (info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL,
+ tls_sec, 0, NULL, FALSE,
+ bed->collect, &bh)))
+ return FALSE;
+
+ htab->tls_module_base = bh;
+
+ tlsbase = (struct elf_link_hash_entry *)bh;
+ tlsbase->def_regular = 1;
+ tlsbase->other = STV_HIDDEN;
+ tlsbase->root.linker_def = 1;
+ (*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
+ }
+ }
+
+ return TRUE;
+ }
+
+ void
+ _bfd_x86_elf_merge_symbol_attribute (struct elf_link_hash_entry *h,
+ const Elf_Internal_Sym *isym,
+ bfd_boolean definition,
+ bfd_boolean dynamic ATTRIBUTE_UNUSED)
+ {
+ if (definition)
+ {
+ struct elf_x86_link_hash_entry *eh
+ = (struct elf_x86_link_hash_entry *) h;
+ eh->def_protected = (ELF_ST_VISIBILITY (isym->st_other)
+ == STV_PROTECTED);
+ }
+ }
+
+ /* Copy the extra info we tack onto an elf_link_hash_entry. */
+
+ void
+ _bfd_x86_elf_copy_indirect_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *dir,
+ struct elf_link_hash_entry *ind)
+ {
+ struct elf_x86_link_hash_entry *edir, *eind;
+
+ edir = (struct elf_x86_link_hash_entry *) dir;
+ eind = (struct elf_x86_link_hash_entry *) ind;
+
+ if (eind->dyn_relocs != NULL)
+ {
+ if (edir->dyn_relocs != NULL)
+ {
+ struct elf_dyn_relocs **pp;
+ struct elf_dyn_relocs *p;
+
+ /* Add reloc counts against the indirect sym to the direct sym
+ list. Merge any entries against the same section. */
+ for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
+ {
+ struct elf_dyn_relocs *q;
+
+ for (q = edir->dyn_relocs; q != NULL; q = q->next)
+ if (q->sec == p->sec)
+ {
+ q->pc_count += p->pc_count;
+ q->count += p->count;
+ *pp = p->next;
+ break;
+ }
+ if (q == NULL)
+ pp = &p->next;
+ }
+ *pp = edir->dyn_relocs;
+ }
+
+ edir->dyn_relocs = eind->dyn_relocs;
+ eind->dyn_relocs = NULL;
+ }
+
+ if (ind->root.type == bfd_link_hash_indirect
+ && dir->got.refcount <= 0)
+ {
+ edir->tls_type = eind->tls_type;
+ eind->tls_type = GOT_UNKNOWN;
+ }
+
+ /* Copy gotoff_ref so that elf_i386_adjust_dynamic_symbol will
+ generate a R_386_COPY reloc. */
+ edir->gotoff_ref |= eind->gotoff_ref;
+
+ edir->zero_undefweak |= eind->zero_undefweak;
++ edir->has_gpoff_reloc |= eind->has_gpoff_reloc;
+
+ if (ELIMINATE_COPY_RELOCS
+ && ind->root.type != bfd_link_hash_indirect
+ && dir->dynamic_adjusted)
+ {
+ /* If called to transfer flags for a weakdef during processing
+ of elf_adjust_dynamic_symbol, don't copy non_got_ref.
+ We clear it ourselves for ELIMINATE_COPY_RELOCS. */
+ if (dir->versioned != versioned_hidden)
+ dir->ref_dynamic |= ind->ref_dynamic;
+ dir->ref_regular |= ind->ref_regular;
+ dir->ref_regular_nonweak |= ind->ref_regular_nonweak;
+ dir->needs_plt |= ind->needs_plt;
+ dir->pointer_equality_needed |= ind->pointer_equality_needed;
+ }
+ else
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind);
+ }
+
+ /* Remove undefined weak symbol from the dynamic symbol table if it
+ is resolved to 0. */
+
+ bfd_boolean
+ _bfd_x86_elf_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_hash_entry (h)))
+ {
+ h->dynindx = -1;
+ _bfd_elf_strtab_delref (elf_hash_table (info)->dynstr,
+ h->dynstr_index);
+ }
+ return TRUE;
+ }
+
+ /* Return TRUE if symbol should be hashed in the `.gnu.hash' section. */
+
+ bfd_boolean
+ _bfd_x86_elf_hash_symbol (struct elf_link_hash_entry *h)
+ {
+ if (h->plt.offset != (bfd_vma) -1
+ && !h->def_regular
+ && !h->pointer_equality_needed)
+ return FALSE;
+
+ return _bfd_elf_hash_symbol (h);
+ }
+
+ /* 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
+ change the definition to something the rest of the link can
+ understand. */
+
+ bfd_boolean
+ _bfd_x86_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+ {
+ struct elf_x86_link_hash_table *htab;
+ asection *s, *srel;
+ struct elf_x86_link_hash_entry *eh;
+ struct elf_dyn_relocs *p;
+ const struct elf_backend_data *bed
+ = get_elf_backend_data (info->output_bfd);
+
+ /* STT_GNU_IFUNC symbol must go through PLT. */
+ if (h->type == STT_GNU_IFUNC)
+ {
+ /* All local STT_GNU_IFUNC references must be treate as local
+ calls via local PLT. */
+ if (h->ref_regular
+ && SYMBOL_CALLS_LOCAL (info, h))
+ {
+ bfd_size_type pc_count = 0, count = 0;
+ struct elf_dyn_relocs **pp;
+
+ eh = (struct elf_x86_link_hash_entry *) h;
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ pc_count += p->pc_count;
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ count += p->count;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+
+ if (pc_count || count)
+ {
+ h->non_got_ref = 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;
+ }
+ }
+ }
+
+ if (h->plt.refcount <= 0)
+ {
+ h->plt.offset = (bfd_vma) -1;
+ h->needs_plt = 0;
+ }
+ return TRUE;
+ }
+
+ /* 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->plt.refcount <= 0
+ || 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 PLT32 reloc in an input
+ file, but the symbol was never referred to by a dynamic
+ object, or if all references were garbage collected. In
+ such a case, we don't actually need to build a procedure
+ linkage table, and we can just do a PC32 reloc instead. */
+ h->plt.offset = (bfd_vma) -1;
+ h->needs_plt = 0;
+ }
+
+ return TRUE;
+ }
+ else
+ /* It's possible that we incorrectly decided a .plt reloc was needed
+ * for an R_386_PC32/R_X86_64_PC32 reloc to a non-function sym in
+ check_relocs. We can't decide accurately between function and
+ non-function syms in check-relocs; Objects loaded later in
+ the link may change h->type. So fix it now. */
+ h->plt.offset = (bfd_vma) -1;
+
+ eh = (struct elf_x86_link_hash_entry *) h;
+
+ /* If this is a weak symbol, and there is a real definition, the
+ processor independent code will have arranged for us to see the
+ real definition first, and we can just use the same value. */
+ if (h->is_weakalias)
+ {
+ struct elf_link_hash_entry *def = weakdef (h);
+ BFD_ASSERT (def->root.type == bfd_link_hash_defined);
+ h->root.u.def.section = def->root.u.def.section;
+ h->root.u.def.value = def->root.u.def.value;
+ if (ELIMINATE_COPY_RELOCS
+ || info->nocopyreloc
+ || SYMBOL_NO_COPYRELOC (info, eh))
+ {
+ /* NB: needs_copy is always 0 for i386. */
+ h->non_got_ref = def->non_got_ref;
+ eh->needs_copy = def->needs_copy;
+ }
+ return TRUE;
+ }
+
+ /* This is a reference to a symbol defined by a dynamic object which
+ is not a function. */
+
+ /* If we are creating a shared library, we must presume that the
+ 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 (!bfd_link_executable (info))
+ return TRUE;
+
+ /* If there are no references to this symbol that do not use the
+ GOT nor R_386_GOTOFF relocation, we don't need to generate a copy
+ reloc. NB: gotoff_ref is always 0 for x86-64. */
+ if (!h->non_got_ref && !eh->gotoff_ref)
+ return TRUE;
+
+ /* If -z nocopyreloc was given, we won't generate them either. */
+ if (info->nocopyreloc || SYMBOL_NO_COPYRELOC (info, eh))
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
+
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab == NULL)
+ return FALSE;
+
+ /* If there aren't any dynamic relocs in read-only sections nor
+ R_386_GOTOFF relocation, then we can keep the dynamic relocs and
+ avoid the copy reloc. This doesn't work on VxWorks, where we can
+ not have dynamic relocations (other than copy and jump slot
+ relocations) in an executable. */
+ if (ELIMINATE_COPY_RELOCS
+ && (bed->target_id == X86_64_ELF_DATA
+ || (!eh->gotoff_ref
+ && htab->target_os != is_vxworks)))
+ {
+ /* If we don't find any dynamic relocs in read-only sections,
+ then we'll be keeping the dynamic relocs and avoiding the copy
+ reloc. */
+ if (!readonly_dynrelocs (h))
+ {
+ h->non_got_ref = 0;
+ return TRUE;
+ }
+ }
+
+ /* We must allocate the symbol in our .dynbss section, which will
+ become part of the .bss section of the executable. There will be
+ an entry for this symbol in the .dynsym section. The dynamic
+ object will contain position independent code, so all references
+ from the dynamic object to this symbol will go through the global
+ offset table. The dynamic linker will use the .dynsym entry to
+ determine the address it must put in the global offset table, so
+ both the dynamic object and the regular object will refer to the
+ same memory location for the variable. */
+
+ /* We must generate a R_386_COPY/R_X86_64_COPY reloc to tell the
+ dynamic linker to copy the initial value out of the dynamic object
+ and into the runtime process image. */
+ if ((h->root.u.def.section->flags & SEC_READONLY) != 0)
+ {
+ s = htab->elf.sdynrelro;
+ srel = htab->elf.sreldynrelro;
+ }
+ else
+ {
+ s = htab->elf.sdynbss;
+ srel = htab->elf.srelbss;
+ }
+ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0 && h->size != 0)
+ {
+ srel->size += htab->sizeof_reloc;
+ h->needs_copy = 1;
+ }
+
+ return _bfd_elf_adjust_dynamic_copy (info, h, s);
+ }
+
+ void
+ _bfd_x86_elf_hide_symbol (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h,
+ bfd_boolean force_local)
+ {
+ if (h->root.type == bfd_link_hash_undefweak
+ && info->nointerp
+ && bfd_link_pie (info))
+ {
+ /* When there is no dynamic interpreter in PIE, make the undefined
+ weak symbol dynamic so that PC relative branch to the undefined
+ weak symbol will land to address 0. */
+ struct elf_x86_link_hash_entry *eh = elf_x86_hash_entry (h);
+ if (h->plt.refcount > 0
+ || eh->plt_got.refcount > 0)
+ return;
+ }
+
+ _bfd_elf_link_hash_hide_symbol (info, h, force_local);
+ }
+
+ /* Return TRUE if a symbol is referenced locally. It is similar to
+ SYMBOL_REFERENCES_LOCAL, but it also checks version script. It
+ works in check_relocs. */
+
+ bfd_boolean
+ _bfd_x86_elf_link_symbol_references_local (struct bfd_link_info *info,
+ struct elf_link_hash_entry *h)
+ {
+ struct elf_x86_link_hash_entry *eh = elf_x86_hash_entry (h);
+ struct elf_x86_link_hash_table *htab
+ = (struct elf_x86_link_hash_table *) info->hash;
+
+ if (eh->local_ref > 1)
+ return TRUE;
+
+ if (eh->local_ref == 1)
+ return FALSE;
+
+ /* Unversioned symbols defined in regular objects can be forced local
+ by linker version script. A weak undefined symbol is forced local
+ if
+ 1. It has non-default visibility. Or
+ 2. When building executable, there is no dynamic linker. Or
+ 3. or "-z nodynamic-undefined-weak" is used.
+ */
+ if (SYMBOL_REFERENCES_LOCAL (info, h)
+ || (h->root.type == bfd_link_hash_undefweak
+ && (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ || (bfd_link_executable (info)
+ && htab->interp == NULL)
+ || info->dynamic_undefined_weak == 0))
+ || ((h->def_regular || ELF_COMMON_DEF_P (h))
+ && h->versioned == unversioned
+ && info->version_info != NULL
+ && bfd_hide_sym_by_version (info->version_info,
+ h->root.root.string)))
+ {
+ eh->local_ref = 2;
+ return TRUE;
+ }
+
+ eh->local_ref = 1;
+ return FALSE;
+ }
+
+ /* Return the section that should be marked against GC for a given
+ relocation. */
+
+ asection *
+ _bfd_x86_elf_gc_mark_hook (asection *sec,
+ struct bfd_link_info *info,
+ Elf_Internal_Rela *rel,
+ struct elf_link_hash_entry *h,
+ Elf_Internal_Sym *sym)
+ {
+ /* Compiler should optimize this out. */
+ if (((unsigned int) R_X86_64_GNU_VTINHERIT
+ != (unsigned int) R_386_GNU_VTINHERIT)
+ || ((unsigned int) R_X86_64_GNU_VTENTRY
+ != (unsigned int) R_386_GNU_VTENTRY))
+ abort ();
+
+ if (h != NULL)
+ switch (ELF32_R_TYPE (rel->r_info))
+ {
+ case R_X86_64_GNU_VTINHERIT:
+ case R_X86_64_GNU_VTENTRY:
+ return NULL;
+ }
+
+ return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym);
+ }
+
+ static bfd_vma
+ elf_i386_get_plt_got_vma (struct elf_x86_plt *plt_p ATTRIBUTE_UNUSED,
+ bfd_vma off,
+ bfd_vma offset ATTRIBUTE_UNUSED,
+ bfd_vma got_addr)
+ {
+ return got_addr + off;
+ }
+
+ static bfd_vma
+ elf_x86_64_get_plt_got_vma (struct elf_x86_plt *plt_p,
+ bfd_vma off,
+ bfd_vma offset,
+ bfd_vma got_addr ATTRIBUTE_UNUSED)
+ {
+ return plt_p->sec->vma + offset + off + plt_p->plt_got_insn_size;
+ }
+
+ static bfd_boolean
+ elf_i386_valid_plt_reloc_p (unsigned int type)
+ {
+ return (type == R_386_JUMP_SLOT
+ || type == R_386_GLOB_DAT
+ || type == R_386_IRELATIVE);
+ }
+
+ static bfd_boolean
+ elf_x86_64_valid_plt_reloc_p (unsigned int type)
+ {
+ return (type == R_X86_64_JUMP_SLOT
+ || type == R_X86_64_GLOB_DAT
+ || type == R_X86_64_IRELATIVE);
+ }
+
+ long
+ _bfd_x86_elf_get_synthetic_symtab (bfd *abfd,
+ long count,
+ long relsize,
+ bfd_vma got_addr,
+ struct elf_x86_plt plts[],
+ asymbol **dynsyms,
+ asymbol **ret)
+ {
+ long size, i, n, len;
+ int j;
+ unsigned int plt_got_offset, plt_entry_size;
+ asymbol *s;
+ bfd_byte *plt_contents;
+ long dynrelcount;
+ arelent **dynrelbuf, *p;
+ char *names;
+ const struct elf_backend_data *bed;
+ bfd_vma (*get_plt_got_vma) (struct elf_x86_plt *, bfd_vma, bfd_vma,
+ bfd_vma);
+ bfd_boolean (*valid_plt_reloc_p) (unsigned int);
+
+ if (count == 0)
+ return -1;
+
+ dynrelbuf = (arelent **) bfd_malloc (relsize);
+ if (dynrelbuf == NULL)
+ return -1;
+
+ dynrelcount = bfd_canonicalize_dynamic_reloc (abfd, dynrelbuf,
+ dynsyms);
+ if (dynrelcount <= 0)
+ return -1;
+
+ /* Sort the relocs by address. */
+ qsort (dynrelbuf, dynrelcount, sizeof (arelent *),
+ _bfd_x86_elf_compare_relocs);
+
+ size = count * sizeof (asymbol);
+
+ /* Allocate space for @plt suffixes. */
+ n = 0;
+ for (i = 0; i < dynrelcount; i++)
+ {
+ p = dynrelbuf[i];
+ size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
+ if (p->addend != 0)
+ size += sizeof ("+0x") - 1 + 8 + 8 * ABI_64_P (abfd);
+ }
+
+ s = *ret = (asymbol *) bfd_zmalloc (size);
+ if (s == NULL)
+ goto bad_return;
+
+ bed = get_elf_backend_data (abfd);
+
+ if (bed->target_id == X86_64_ELF_DATA)
+ {
+ get_plt_got_vma = elf_x86_64_get_plt_got_vma;
+ valid_plt_reloc_p = elf_x86_64_valid_plt_reloc_p;
+ }
+ else
+ {
+ get_plt_got_vma = elf_i386_get_plt_got_vma;
+ valid_plt_reloc_p = elf_i386_valid_plt_reloc_p;
+ if (got_addr)
+ {
+ /* Check .got.plt and then .got to get the _GLOBAL_OFFSET_TABLE_
+ address. */
+ asection *sec = bfd_get_section_by_name (abfd, ".got.plt");
+ if (sec != NULL)
+ got_addr = sec->vma;
+ else
+ {
+ sec = bfd_get_section_by_name (abfd, ".got");
+ if (sec != NULL)
+ got_addr = sec->vma;
+ }
+
+ if (got_addr == (bfd_vma) -1)
+ goto bad_return;
+ }
+ }
+
+ /* Check for each PLT section. */
+ names = (char *) (s + count);
+ size = 0;
+ n = 0;
+ for (j = 0; plts[j].name != NULL; j++)
+ if ((plt_contents = plts[j].contents) != NULL)
+ {
+ long k;
+ bfd_vma offset;
+ asection *plt;
+ struct elf_x86_plt *plt_p = &plts[j];
+
+ plt_got_offset = plt_p->plt_got_offset;
+ plt_entry_size = plt_p->plt_entry_size;
+
+ plt = plt_p->sec;
+
+ if ((plt_p->type & plt_lazy))
+ {
+ /* Skip PLT0 in lazy PLT. */
+ k = 1;
+ offset = plt_entry_size;
+ }
+ else
+ {
+ k = 0;
+ offset = 0;
+ }
+
+ /* Check each PLT entry against dynamic relocations. */
+ for (; k < plt_p->count; k++)
+ {
+ int off;
+ bfd_vma got_vma;
+ long min, max, mid;
+
+ /* Get the GOT offset for i386 or the PC-relative offset
+ for x86-64, a signed 32-bit integer. */
+ off = H_GET_32 (abfd, (plt_contents + offset
+ + plt_got_offset));
+ got_vma = get_plt_got_vma (plt_p, off, offset, got_addr);
+
+ /* Binary search. */
+ p = dynrelbuf[0];
+ min = 0;
+ max = dynrelcount;
+ while ((min + 1) < max)
+ {
+ arelent *r;
+
+ mid = (min + max) / 2;
+ r = dynrelbuf[mid];
+ if (got_vma > r->address)
+ min = mid;
+ else if (got_vma < r->address)
+ max = mid;
+ else
+ {
+ p = r;
+ break;
+ }
+ }
+
+ /* Skip unknown relocation. PR 17512: file: bc9d6cf5. */
+ if (got_vma == p->address
+ && p->howto != NULL
+ && valid_plt_reloc_p (p->howto->type))
+ {
+ *s = **p->sym_ptr_ptr;
+ /* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL
+ set. Since we are defining a symbol, ensure one
+ of them is set. */
+ if ((s->flags & BSF_LOCAL) == 0)
+ s->flags |= BSF_GLOBAL;
+ s->flags |= BSF_SYNTHETIC;
+ /* This is no longer a section symbol. */
+ s->flags &= ~BSF_SECTION_SYM;
+ s->section = plt;
+ s->the_bfd = plt->owner;
+ s->value = offset;
+ s->udata.p = NULL;
+ s->name = names;
+ len = strlen ((*p->sym_ptr_ptr)->name);
+ memcpy (names, (*p->sym_ptr_ptr)->name, len);
+ names += len;
+ if (p->addend != 0)
+ {
+ char buf[30], *a;
+
+ memcpy (names, "+0x", sizeof ("+0x") - 1);
+ names += sizeof ("+0x") - 1;
+ bfd_sprintf_vma (abfd, buf, p->addend);
+ for (a = buf; *a == '0'; ++a)
+ ;
+ size = strlen (a);
+ memcpy (names, a, size);
+ names += size;
+ }
+ memcpy (names, "@plt", sizeof ("@plt"));
+ names += sizeof ("@plt");
+ n++;
+ s++;
+ /* There should be only one entry in PLT for a given
+ symbol. Set howto to NULL after processing a PLT
+ entry to guard against corrupted PLT. */
+ p->howto = NULL;
+ }
+ offset += plt_entry_size;
+ }
+ }
+
+ /* PLT entries with R_386_TLS_DESC relocations are skipped. */
+ if (n == 0)
+ {
+ bad_return:
+ count = -1;
+ }
+ else
+ count = n;
+
+ for (j = 0; plts[j].name != NULL; j++)
+ if (plts[j].contents != NULL)
+ free (plts[j].contents);
+
+ free (dynrelbuf);
+
+ return count;
+ }
+
+ /* Parse x86 GNU properties. */
+
+ enum elf_property_kind
+ _bfd_x86_elf_parse_gnu_properties (bfd *abfd, unsigned int type,
+ bfd_byte *ptr, unsigned int datasz)
+ {
+ elf_property *prop;
+
+ switch (type)
+ {
+ case GNU_PROPERTY_X86_ISA_1_USED:
+ case GNU_PROPERTY_X86_ISA_1_NEEDED:
+ case GNU_PROPERTY_X86_FEATURE_1_AND:
+ if (datasz != 4)
+ {
+ _bfd_error_handler
+ ((type == GNU_PROPERTY_X86_ISA_1_USED
+ ? _("error: %B: <corrupt x86 ISA used size: 0x%x>")
+ : (type == GNU_PROPERTY_X86_ISA_1_NEEDED
+ ? _("error: %B: <corrupt x86 ISA needed size: 0x%x>")
+ : _("error: %B: <corrupt x86 feature size: 0x%x>"))),
+ abfd, datasz);
+ return property_corrupt;
+ }
+ prop = _bfd_elf_get_property (abfd, type, datasz);
+ /* Combine properties of the same type. */
+ prop->u.number |= bfd_h_get_32 (abfd, ptr);
+ prop->pr_kind = property_number;
+ break;
+
+ default:
+ return property_ignored;
+ }
+
+ return property_number;
+ }
+
+ /* Merge x86 GNU property BPROP with APROP. If APROP isn't NULL,
+ return TRUE if APROP is updated. Otherwise, return TRUE if BPROP
+ should be merged with ABFD. */
+
+ bfd_boolean
+ _bfd_x86_elf_merge_gnu_properties (struct bfd_link_info *info,
+ bfd *abfd ATTRIBUTE_UNUSED,
+ elf_property *aprop,
+ elf_property *bprop)
+ {
+ unsigned int number, features;
+ bfd_boolean updated = FALSE;
+ unsigned int pr_type = aprop != NULL ? aprop->pr_type : bprop->pr_type;
+
+ switch (pr_type)
+ {
+ case GNU_PROPERTY_X86_ISA_1_USED:
+ case GNU_PROPERTY_X86_ISA_1_NEEDED:
+ if (aprop != NULL && bprop != NULL)
+ {
+ number = aprop->u.number;
+ aprop->u.number = number | bprop->u.number;
+ updated = number != (unsigned int) aprop->u.number;
+ }
+ else
+ {
+ /* Return TRUE if APROP is NULL to indicate that BPROP should
+ be added to ABFD. */
+ updated = aprop == NULL;
+ }
+ break;
+
+ case GNU_PROPERTY_X86_FEATURE_1_AND:
+ /* Only one of APROP and BPROP can be NULL:
+ 1. APROP & BPROP when both APROP and BPROP aren't NULL.
+ 2. If APROP is NULL, remove x86 feature.
+ 3. Otherwise, do nothing.
+ */
+ if (aprop != NULL && bprop != NULL)
+ {
+ features = 0;
+ if (info->ibt)
+ features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+ if (info->shstk)
+ features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+ number = aprop->u.number;
+ /* Add GNU_PROPERTY_X86_FEATURE_1_IBT and
+ GNU_PROPERTY_X86_FEATURE_1_SHSTK. */
+ aprop->u.number = (number & bprop->u.number) | features;
+ updated = number != (unsigned int) aprop->u.number;
+ /* Remove the property if all feature bits are cleared. */
+ if (aprop->u.number == 0)
+ aprop->pr_kind = property_remove;
+ }
+ else
+ {
+ features = 0;
+ if (info->ibt)
+ features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+ if (info->shstk)
+ features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+ if (features)
+ {
+ /* Add GNU_PROPERTY_X86_FEATURE_1_IBT and
+ GNU_PROPERTY_X86_FEATURE_1_SHSTK. */
+ if (aprop != NULL)
+ {
+ number = aprop->u.number;
+ aprop->u.number = number | features;
+ updated = number != (unsigned int) aprop->u.number;
+ }
+ else
+ {
+ bprop->u.number |= features;
+ updated = TRUE;
+ }
+ }
+ else if (aprop != NULL)
+ {
+ aprop->pr_kind = property_remove;
+ updated = TRUE;
+ }
+ }
+ break;
+
+ default:
+ /* Never should happen. */
+ abort ();
+ }
+
+ return updated;
+ }
+
+ /* Set up x86 GNU properties. Return the first relocatable ELF input
+ with GNU properties if found. Otherwise, return NULL. */
+
+ bfd *
+ _bfd_x86_elf_link_setup_gnu_properties
+ (struct bfd_link_info *info, struct elf_x86_init_table *init_table)
+ {
+ bfd_boolean normal_target;
+ bfd_boolean lazy_plt;
+ asection *sec, *pltsec;
+ bfd *dynobj;
+ bfd_boolean use_ibt_plt;
+ unsigned int plt_alignment, features;
+ struct elf_x86_link_hash_table *htab;
+ bfd *pbfd;
+ bfd *ebfd = NULL;
+ elf_property *prop;
+ const struct elf_backend_data *bed;
+ unsigned int class_align = ABI_64_P (info->output_bfd) ? 3 : 2;
+ unsigned int got_align;
+
+ features = 0;
+ if (info->ibt)
+ features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+ if (info->shstk)
+ features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+
+ /* Find a normal input file with GNU property note. */
+ for (pbfd = info->input_bfds;
+ pbfd != NULL;
+ pbfd = pbfd->link.next)
+ if (bfd_get_flavour (pbfd) == bfd_target_elf_flavour
+ && bfd_count_sections (pbfd) != 0)
+ {
+ ebfd = pbfd;
+
+ if (elf_properties (pbfd) != NULL)
+ break;
+ }
+
+ if (ebfd != NULL && features)
+ {
+ /* If features is set, add GNU_PROPERTY_X86_FEATURE_1_IBT and
+ GNU_PROPERTY_X86_FEATURE_1_SHSTK. */
+ prop = _bfd_elf_get_property (ebfd,
+ GNU_PROPERTY_X86_FEATURE_1_AND,
+ 4);
+ prop->u.number |= features;
+ prop->pr_kind = property_number;
+
+ /* Create the GNU property note section if needed. */
+ if (pbfd == NULL)
+ {
+ sec = bfd_make_section_with_flags (ebfd,
+ NOTE_GNU_PROPERTY_SECTION_NAME,
+ (SEC_ALLOC
+ | SEC_LOAD
+ | SEC_IN_MEMORY
+ | SEC_READONLY
+ | SEC_HAS_CONTENTS
+ | SEC_DATA));
+ if (sec == NULL)
+ info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
+
+ if (!bfd_set_section_alignment (ebfd, sec, class_align))
+ {
+ error_alignment:
+ info->callbacks->einfo (_("%F%A: failed to align section\n"),
+ sec);
+ }
+
+ elf_section_type (sec) = SHT_NOTE;
+ }
+ }
+
+ pbfd = _bfd_elf_link_setup_gnu_properties (info);
+
+ bed = get_elf_backend_data (info->output_bfd);
+
+ htab = elf_x86_hash_table (info, bed->target_id);
+ if (htab == NULL)
+ return pbfd;
+
+ htab->r_info = init_table->r_info;
+ htab->r_sym = init_table->r_sym;
+
+ if (bfd_link_relocatable (info))
+ return pbfd;
+
+ htab->plt0_pad_byte = init_table->plt0_pad_byte;
+
+ use_ibt_plt = info->ibtplt || info->ibt;
+ if (!use_ibt_plt && pbfd != NULL)
+ {
+ /* Check if GNU_PROPERTY_X86_FEATURE_1_IBT is on. */
+ elf_property_list *p;
+
+ /* The property list is sorted in order of type. */
+ for (p = elf_properties (pbfd); p; p = p->next)
+ {
+ if (GNU_PROPERTY_X86_FEATURE_1_AND == p->property.pr_type)
+ {
+ use_ibt_plt = !!(p->property.u.number
+ & GNU_PROPERTY_X86_FEATURE_1_IBT);
+ break;
+ }
+ else if (GNU_PROPERTY_X86_FEATURE_1_AND < p->property.pr_type)
+ break;
+ }
+ }
+
+ dynobj = htab->elf.dynobj;
+
+ /* Set htab->elf.dynobj here so that there is no need to check and
+ set it in check_relocs. */
+ if (dynobj == NULL)
+ {
+ if (pbfd != NULL)
+ {
+ htab->elf.dynobj = pbfd;
+ dynobj = pbfd;
+ }
+ else
+ {
+ bfd *abfd;
+
+ /* Find a normal input file to hold linker created
+ sections. */
+ for (abfd = info->input_bfds;
+ abfd != NULL;
+ abfd = abfd->link.next)
+ if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+ && (abfd->flags
+ & (DYNAMIC | BFD_LINKER_CREATED | BFD_PLUGIN)) == 0)
+ {
+ htab->elf.dynobj = abfd;
+ dynobj = abfd;
+ break;
+ }
+ }
+ }
+
+ /* Return if there are no normal input files. */
+ if (dynobj == NULL)
+ return pbfd;
+
+ /* Even when lazy binding is disabled by "-z now", the PLT0 entry may
+ still be used with LD_AUDIT or LD_PROFILE if PLT entry is used for
+ canonical function address. */
+ htab->plt.has_plt0 = 1;
+ normal_target = htab->target_os == is_normal;
+
+ if (normal_target)
+ {
+ if (use_ibt_plt)
+ {
+ htab->lazy_plt = init_table->lazy_ibt_plt;
+ htab->non_lazy_plt = init_table->non_lazy_ibt_plt;
+ }
+ else
+ {
+ htab->lazy_plt = init_table->lazy_plt;
+ htab->non_lazy_plt = init_table->non_lazy_plt;
+ }
+ }
+ else
+ {
+ htab->lazy_plt = init_table->lazy_plt;
+ htab->non_lazy_plt = NULL;
+ }
+
+ pltsec = htab->elf.splt;
+
+ /* If the non-lazy PLT is available, use it for all PLT entries if
+ there are no PLT0 or no .plt section. */
+ if (htab->non_lazy_plt != NULL
+ && (!htab->plt.has_plt0 || pltsec == NULL))
+ {
+ lazy_plt = FALSE;
+ if (bfd_link_pic (info))
+ htab->plt.plt_entry = htab->non_lazy_plt->pic_plt_entry;
+ else
+ htab->plt.plt_entry = htab->non_lazy_plt->plt_entry;
+ htab->plt.plt_entry_size = htab->non_lazy_plt->plt_entry_size;
+ htab->plt.plt_got_offset = htab->non_lazy_plt->plt_got_offset;
+ htab->plt.plt_got_insn_size
+ = htab->non_lazy_plt->plt_got_insn_size;
+ htab->plt.eh_frame_plt_size
+ = htab->non_lazy_plt->eh_frame_plt_size;
+ htab->plt.eh_frame_plt = htab->non_lazy_plt->eh_frame_plt;
+ }
+ else
+ {
+ lazy_plt = TRUE;
+ if (bfd_link_pic (info))
+ {
+ htab->plt.plt0_entry = htab->lazy_plt->pic_plt0_entry;
+ htab->plt.plt_entry = htab->lazy_plt->pic_plt_entry;
+ }
+ else
+ {
+ htab->plt.plt0_entry = htab->lazy_plt->plt0_entry;
+ htab->plt.plt_entry = htab->lazy_plt->plt_entry;
+ }
+ htab->plt.plt_entry_size = htab->lazy_plt->plt_entry_size;
+ htab->plt.plt_got_offset = htab->lazy_plt->plt_got_offset;
+ htab->plt.plt_got_insn_size
+ = htab->lazy_plt->plt_got_insn_size;
+ htab->plt.eh_frame_plt_size
+ = htab->lazy_plt->eh_frame_plt_size;
+ htab->plt.eh_frame_plt = htab->lazy_plt->eh_frame_plt;
+ }
+
+ if (htab->target_os == is_vxworks
+ && !elf_vxworks_create_dynamic_sections (dynobj, info,
+ &htab->srelplt2))
+ {
+ info->callbacks->einfo (_("%F%P: failed to create VxWorks dynamic sections\n"));
+ return pbfd;
+ }
+
+ /* Since create_dynamic_sections isn't always called, but GOT
+ relocations need GOT relocations, create them here so that we
+ don't need to do it in check_relocs. */
+ if (htab->elf.sgot == NULL
+ && !_bfd_elf_create_got_section (dynobj, info))
+ info->callbacks->einfo (_("%F%P: failed to create GOT sections\n"));
+
+ got_align = (bed->target_id == X86_64_ELF_DATA) ? 3 : 2;
+
+ /* Align .got and .got.plt sections to their entry size. Do it here
+ instead of in create_dynamic_sections so that they are always
+ properly aligned even if create_dynamic_sections isn't called. */
+ sec = htab->elf.sgot;
+ if (!bfd_set_section_alignment (dynobj, sec, got_align))
+ goto error_alignment;
+
+ sec = htab->elf.sgotplt;
+ if (!bfd_set_section_alignment (dynobj, sec, got_align))
+ goto error_alignment;
+
+ /* Create the ifunc sections here so that check_relocs can be
+ simplified. */
+ if (!_bfd_elf_create_ifunc_sections (dynobj, info))
+ info->callbacks->einfo (_("%F%P: failed to create ifunc sections\n"));
+
+ plt_alignment = bfd_log2 (htab->plt.plt_entry_size);
+
+ if (pltsec != NULL)
+ {
+ /* Whe creating executable, set the contents of the .interp
+ section to the interpreter. */
+ if (bfd_link_executable (info) && !info->nointerp)
+ {
+ asection *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;
+ }
+
+ /* Don't change PLT section alignment for NaCl since it uses
+ 64-byte PLT entry and sets PLT section alignment to 32
+ bytes. Don't create additional PLT sections for NaCl. */
+ if (normal_target)
+ {
+ flagword pltflags = (bed->dynamic_sec_flags
+ | SEC_ALLOC
+ | SEC_CODE
+ | SEC_LOAD
+ | SEC_READONLY);
+ unsigned int non_lazy_plt_alignment
+ = bfd_log2 (htab->non_lazy_plt->plt_entry_size);
+
+ sec = pltsec;
+ if (!bfd_set_section_alignment (sec->owner, sec,
+ plt_alignment))
+ goto error_alignment;
+
+ /* Create the GOT procedure linkage table. */
+ sec = bfd_make_section_anyway_with_flags (dynobj,
+ ".plt.got",
+ pltflags);
+ if (sec == NULL)
+ info->callbacks->einfo (_("%F%P: failed to create GOT PLT section\n"));
+
+ if (!bfd_set_section_alignment (dynobj, sec,
+ non_lazy_plt_alignment))
+ goto error_alignment;
+
+ htab->plt_got = sec;
+
+ if (lazy_plt)
+ {
+ sec = NULL;
+
+ if (use_ibt_plt)
+ {
+ /* Create the second PLT for Intel IBT support. IBT
+ PLT is supported only for non-NaCl target and is
+ is needed only for lazy binding. */
+ sec = bfd_make_section_anyway_with_flags (dynobj,
+ ".plt.sec",
+ pltflags);
+ if (sec == NULL)
+ info->callbacks->einfo (_("%F%P: failed to create IBT-enabled PLT section\n"));
+
+ if (!bfd_set_section_alignment (dynobj, sec,
+ plt_alignment))
+ goto error_alignment;
+ }
+ else if (info->bndplt && ABI_64_P (dynobj))
+ {
+ /* Create the second PLT for Intel MPX support. MPX
+ PLT is supported only for non-NaCl target in 64-bit
+ mode and is needed only for lazy binding. */
+ sec = bfd_make_section_anyway_with_flags (dynobj,
+ ".plt.sec",
+ pltflags);
+ if (sec == NULL)
+ info->callbacks->einfo (_("%F%P: failed to create BND PLT section\n"));
+
+ if (!bfd_set_section_alignment (dynobj, sec,
+ non_lazy_plt_alignment))
+ goto error_alignment;
+ }
+
+ htab->plt_second = sec;
+ }
+ }
+
+ if (!info->no_ld_generated_unwind_info)
+ {
+ flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+ | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+ | SEC_LINKER_CREATED);
+
+ sec = bfd_make_section_anyway_with_flags (dynobj,
+ ".eh_frame",
+ flags);
+ if (sec == NULL)
+ info->callbacks->einfo (_("%F%P: failed to create PLT .eh_frame section\n"));
+
+ if (!bfd_set_section_alignment (dynobj, sec, class_align))
+ goto error_alignment;
+
+ htab->plt_eh_frame = sec;
+
+ if (htab->plt_got != NULL)
+ {
+ sec = bfd_make_section_anyway_with_flags (dynobj,
+ ".eh_frame",
+ flags);
+ if (sec == NULL)
+ info->callbacks->einfo (_("%F%P: failed to create GOT PLT .eh_frame section\n"));
+
+ if (!bfd_set_section_alignment (dynobj, sec, class_align))
+ goto error_alignment;
+
+ htab->plt_got_eh_frame = sec;
+ }
+
+ if (htab->plt_second != NULL)
+ {
+ sec = bfd_make_section_anyway_with_flags (dynobj,
+ ".eh_frame",
+ flags);
+ if (sec == NULL)
+ info->callbacks->einfo (_("%F%P: failed to create the second PLT .eh_frame section\n"));
+
+ if (!bfd_set_section_alignment (dynobj, sec, class_align))
+ goto error_alignment;
+
+ htab->plt_second_eh_frame = sec;
+ }
+ }
+ }
+
+ if (normal_target)
+ {
+ /* The .iplt section is used for IFUNC symbols in static
+ executables. */
+ sec = htab->elf.iplt;
+ if (sec != NULL
+ && !bfd_set_section_alignment (sec->owner, sec,
+ plt_alignment))
+ goto error_alignment;
+ }
+
+ return pbfd;
+ }
++
++/* Set up GP section from symbols with GPOFF relocations. */
++
++static bfd_boolean
++elf_x86_setup_gp (struct elf_link_hash_entry *h, void * inf)
++{
++ struct bfd_link_info *info;
++ struct elf_x86_link_hash_table *htab;
++ struct elf_x86_link_hash_entry *eh;
++ struct elf_link_hash_entry *gp;
++ asection *gpsection;
++ bfd_size_type gpsection_size;
++ const struct elf_backend_data *bed;
++
++ eh = (struct elf_x86_link_hash_entry *) h;
++
++ /* Skip if there is no GPOFF relocation or symbol is undefined. */
++ if (!eh->has_gpoff_reloc
++ || (h->root.type != bfd_link_hash_defined
++ && h->root.type != bfd_link_hash_defweak))
++ return TRUE;
++
++ info = (struct bfd_link_info *) inf;
++ bed = get_elf_backend_data (info->output_bfd);
++ htab = elf_x86_hash_table (info, bed->target_id);
++ if (htab == NULL)
++ return FALSE;
++
++ gpsection = h->root.u.def.section->output_section;
++ gpsection_size = bfd_get_section_size (gpsection);
++
++ if (bed->target_id == X86_64_ELF_DATA && gpsection_size > 0xffffffff)
++ {
++ info->callbacks->einfo (_("%F%B: GP section `%A' size overflow\n"),
++ info->output_bfd, gpsection);
++ return FALSE;
++ }
++
++ gp = htab->gp;
++ gp->root.type = bfd_link_hash_defined;
++ gp->root.u.def.value = gpsection_size / 2;
++ gp->root.u.def.section = gpsection;
++ gp->root.linker_def = 1;
++
++ /* Found GP section. No need to continue. */
++ return FALSE;
++}
++
++/* Set up GP section from local symbols with GPOFF relocations. */
++
++static bfd_boolean
++elf_x86_setup_gp_from_local_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 elf_x86_setup_gp (h, info);
++}
++
++/* Set up GP section for __gp symbol. */
++
++bfd_boolean
++_bfd_x86_elf_final_link (bfd *abfd, struct bfd_link_info *info)
++{
++ if (!bfd_link_relocatable (info))
++ {
++ struct elf_link_hash_entry *gp;
++ struct elf_x86_link_hash_table *htab;
++ const struct elf_backend_data *bed = get_elf_backend_data (abfd);
++
++ htab = elf_x86_hash_table (info, bed->target_id);
++ if (htab == NULL)
++ return FALSE;
++
++ gp = htab->gp;
++ if (gp != NULL
++ && gp->root.type != bfd_link_hash_defined
++ && gp->root.type != bfd_link_hash_defweak)
++ {
++ /* Set up __gp from a symbol with GPOFF relocations. */
++ elf_link_hash_traverse (&htab->elf, elf_x86_setup_gp, info);
++
++ if (gp->root.type != bfd_link_hash_defined
++ && gp->root.type != bfd_link_hash_defweak)
++ {
++ /* Set up __gp from a local symbol with GPOFF
++ relocations. */
++ htab_traverse (htab->loc_hash_table,
++ elf_x86_setup_gp_from_local_symbol,
++ info);
++ }
++ }
++ }
++
++ /* Invoke the regular ELF backend linker to do all the work. */
++ return bfd_elf_final_link (abfd, info);
++}
--- /dev/null
+ /* x86 specific support for ELF
+ Copyright (C) 2017-2018 Free Software Foundation, Inc.
+
+ This file is part of BFD, the Binary File Descriptor library.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
+ MA 02110-1301, USA. */
+
+ #include "sysdep.h"
+ #include "bfd.h"
+ #include "bfdlink.h"
+ #include "libbfd.h"
+ #include "elf-bfd.h"
+ #include "bfd_stdint.h"
+ #include "hashtab.h"
+
+ #define PLT_CIE_LENGTH 20
+ #define PLT_FDE_LENGTH 36
+ #define PLT_FDE_START_OFFSET 4 + PLT_CIE_LENGTH + 8
+ #define PLT_FDE_LEN_OFFSET 4 + PLT_CIE_LENGTH + 12
+
+ #define ABI_64_P(abfd) \
+ (get_elf_backend_data (abfd)->s->elfclass == ELFCLASS64)
+
+ /* If ELIMINATE_COPY_RELOCS is non-zero, the linker will try to avoid
+ copying dynamic variables from a shared lib into an app's dynbss
+ section, and instead use a dynamic relocation to point into the
+ shared lib. */
+ #define ELIMINATE_COPY_RELOCS 1
+
+ #define elf_x86_hash_table(p, id) \
+ (is_elf_hash_table ((p)->hash) \
+ && elf_hash_table_id ((struct elf_link_hash_table *) ((p)->hash)) == (id) \
+ ? ((struct elf_x86_link_hash_table *) ((p)->hash)) : NULL)
+
+ /* Will references to this symbol always be local in this object? */
+ #define SYMBOL_REFERENCES_LOCAL_P(INFO, H) \
+ _bfd_x86_elf_link_symbol_references_local ((INFO), (H))
+
+ /* TRUE if an undefined weak symbol should be resolved to 0. Local
+ undefined weak symbol is always resolved to 0. Reference to an
+ undefined weak symbol is resolved to 0 in executable if undefined
+ weak symbol should be resolved to 0 (zero_undefweak > 0). */
+ #define UNDEFINED_WEAK_RESOLVED_TO_ZERO(INFO, EH) \
+ ((EH)->elf.root.type == bfd_link_hash_undefweak \
+ && (SYMBOL_REFERENCES_LOCAL_P ((INFO), &(EH)->elf) \
+ || (bfd_link_executable (INFO) \
+ && (EH)->zero_undefweak > 0)))
+
+ /* Should copy relocation be generated for a symbol. Don't generate
+ copy relocation against a protected symbol defined in a shared
+ object with GNU_PROPERTY_NO_COPY_ON_PROTECTED. */
+ #define SYMBOL_NO_COPYRELOC(INFO, EH) \
+ ((EH)->def_protected \
+ && ((EH)->elf.root.type == bfd_link_hash_defined \
+ || (EH)->elf.root.type == bfd_link_hash_defweak) \
+ && elf_has_no_copy_on_protected ((EH)->elf.root.u.def.section->owner) \
+ && ((EH)->elf.root.u.def.section->owner->flags & DYNAMIC) != 0 \
+ && ((EH)->elf.root.u.def.section->flags & SEC_CODE) == 0)
+
+ /* TRUE if dynamic relocation is needed. If we are creating a shared
+ library, and this is a reloc against a global symbol, or a non PC
+ relative reloc against a local symbol, then we need to copy the reloc
+ into the shared library. However, if we are linking with -Bsymbolic,
+ we do not need to copy a reloc against a global symbol which is
+ defined in an object we are including in the link (i.e., DEF_REGULAR
+ is set). At this point we have not seen all the input files, so it
+ is possible that DEF_REGULAR is not set now but will be set later (it
+ is never cleared). In case of a weak definition, DEF_REGULAR may be
+ cleared later by a strong definition in a shared library. We account
+ for that possibility below by storing information in the relocs_copied
+ field of the hash table entry. A similar situation occurs when
+ creating shared libraries and symbol visibility changes render the
+ symbol local.
+
+ 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.
+
+ We also need to generate dynamic pointer relocation against
+ STT_GNU_IFUNC symbol in the non-code section. */
+ #define NEED_DYNAMIC_RELOCATION_P(INFO, H, SEC, R_TYPE, POINTER_TYPE) \
+ ((bfd_link_pic (INFO) \
+ && (! X86_PCREL_TYPE_P (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) == POINTER_TYPE \
+ && ((SEC)->flags & SEC_CODE) == 0) \
+ || (ELIMINATE_COPY_RELOCS \
+ && !bfd_link_pic (INFO) \
+ && (H) != NULL \
+ && ((H)->root.type == bfd_link_hash_defweak \
+ || !(H)->def_regular)))
+
+ /* TRUE if dynamic relocation should be generated. 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.
+ Copy dynamic function pointer relocations. Don't generate dynamic
+ relocations against resolved undefined weak symbols in PIE, except
+ when PC32_RELOC is TRUE. Undefined weak symbol is bound locally
+ when PIC is false. */
+ #define GENERATE_DYNAMIC_RELOCATION_P(INFO, EH, R_TYPE, \
+ NEED_COPY_RELOC_IN_PIE, \
+ RESOLVED_TO_ZERO, PC32_RELOC) \
+ ((bfd_link_pic (INFO) \
+ && !(NEED_COPY_RELOC_IN_PIE) \
+ && ((EH) == NULL \
+ || ((ELF_ST_VISIBILITY ((EH)->elf.other) == STV_DEFAULT \
+ && (!(RESOLVED_TO_ZERO) || PC32_RELOC)) \
+ || (EH)->elf.root.type != bfd_link_hash_undefweak)) \
+ && ((!X86_PCREL_TYPE_P (R_TYPE) \
+ && !X86_SIZE_TYPE_P (R_TYPE)) \
+ || ! SYMBOL_CALLS_LOCAL ((INFO), &(EH)->elf))) \
+ || (ELIMINATE_COPY_RELOCS \
+ && !bfd_link_pic (INFO) \
+ && (EH) != NULL \
+ && (EH)->elf.dynindx != -1 \
+ && (!(EH)->elf.non_got_ref \
+ || ((EH)->elf.root.type == bfd_link_hash_undefweak \
+ && !(RESOLVED_TO_ZERO))) \
+ && (((EH)->elf.def_dynamic && !(EH)->elf.def_regular) \
+ || (EH)->elf.root.type == bfd_link_hash_undefined)))
+
+ /* TRUE if this input relocation should be copied to output. H->dynindx
+ may be -1 if this symbol was marked to become local. */
+ #define COPY_INPUT_RELOC_P(INFO, H, R_TYPE) \
+ ((H) != NULL \
+ && (H)->dynindx != -1 \
+ && (X86_PCREL_TYPE_P (R_TYPE) \
+ || !(bfd_link_executable (INFO) || SYMBOLIC_BIND ((INFO), (H))) \
+ || !(H)->def_regular))
+
+ /* TRUE if this is actually a static link, or it is a -Bsymbolic link
+ and the symbol is defined locally, or the symbol was forced to be
+ local because of a version file. */
+ #define RESOLVED_LOCALLY_P(INFO, H, HTAB) \
+ (!WILL_CALL_FINISH_DYNAMIC_SYMBOL ((HTAB)->elf.dynamic_sections_created, \
+ bfd_link_pic (INFO), (H)) \
+ || (bfd_link_pic (INFO) \
+ && SYMBOL_REFERENCES_LOCAL_P ((INFO), (H))) \
+ || (ELF_ST_VISIBILITY ((H)->other) \
+ && (H)->root.type == bfd_link_hash_undefweak))
+
+ /* TRUE if relative relocation should be generated. GOT reference to
+ global symbol in PIC will lead to dynamic symbol. It becomes a
+ problem when "time" or "times" is defined as a variable in an
+ executable, clashing with functions of the same name in libc. If a
+ symbol isn't undefined weak symbol, don't make it dynamic in PIC and
+ generate relative relocation. */
+ #define GENERATE_RELATIVE_RELOC_P(INFO, H) \
+ ((H)->dynindx == -1 \
+ && !(H)->forced_local \
+ && (H)->root.type != bfd_link_hash_undefweak \
+ && bfd_link_pic (INFO))
+
+ /* TRUE if this is a pointer reference to a local IFUNC. */
+ #define POINTER_LOCAL_IFUNC_P(INFO, H) \
+ ((H)->dynindx == -1 \
+ || (H)->forced_local \
+ || bfd_link_executable (INFO))
+
+ /* TRUE if this is a PLT reference to a local IFUNC. */
+ #define PLT_LOCAL_IFUNC_P(INFO, H) \
+ ((H)->dynindx == -1 \
+ || ((bfd_link_executable (INFO) \
+ || ELF_ST_VISIBILITY ((H)->other) != STV_DEFAULT) \
+ && (H)->def_regular \
+ && (H)->type == STT_GNU_IFUNC))
+
+ /* TRUE if TLS IE->LE transition is OK. */
+ #define TLS_TRANSITION_IE_TO_LE_P(INFO, H, TLS_TYPE) \
+ (bfd_link_executable (INFO) \
+ && (H) != NULL \
+ && (H)->dynindx == -1 \
+ && (TLS_TYPE & GOT_TLS_IE))
+
+ /* Verify that the symbol has an entry in the procedure linkage table. */
+ #define VERIFY_PLT_ENTRY(INFO, H, PLT, GOTPLT, RELPLT, LOCAL_UNDEFWEAK) \
+ do \
+ { \
+ if (((H)->dynindx == -1 \
+ && !LOCAL_UNDEFWEAK \
+ && !(((H)->forced_local || bfd_link_executable (INFO)) \
+ && (H)->def_regular \
+ && (H)->type == STT_GNU_IFUNC)) \
+ || (PLT) == NULL \
+ || (GOTPLT) == NULL \
+ || (RELPLT) == NULL) \
+ abort (); \
+ } \
+ while (0);
+
+ /* Verify that the symbol supports copy relocation. */
+ #define VERIFY_COPY_RELOC(H, HTAB) \
+ do \
+ { \
+ if ((H)->dynindx == -1 \
+ || ((H)->root.type != bfd_link_hash_defined \
+ && (H)->root.type != bfd_link_hash_defweak) \
+ || (HTAB)->elf.srelbss == NULL \
+ || (HTAB)->elf.sreldynrelro == NULL) \
+ abort (); \
+ } \
+ while (0);
+
+ /* x86 ELF linker hash entry. */
+
+ struct elf_x86_link_hash_entry
+ {
+ struct elf_link_hash_entry elf;
+
+ /* Track dynamic relocs copied for this symbol. */
+ struct elf_dyn_relocs *dyn_relocs;
+
+ unsigned char tls_type;
+
+ /* Bit 0: Symbol has no GOT nor PLT relocations.
+ Bit 1: Symbol has non-GOT/non-PLT relocations in text sections.
+ zero_undefweak is initialized to 1 and undefined weak symbol
+ should be resolved to 0 if zero_undefweak > 0. */
+ unsigned int zero_undefweak : 2;
+
++ /* TRUE if symbol has GPOFF relocations. */
++ unsigned int has_gpoff_reloc : 1;
++
+ /* Don't call finish_dynamic_symbol on this symbol. */
+ unsigned int no_finish_dynamic_symbol : 1;
+
+ /* TRUE if symbol is __tls_get_addr. */
+ unsigned int tls_get_addr : 1;
+
+ /* TRUE if symbol is defined as a protected symbol. */
+ unsigned int def_protected : 1;
+
+ /* 0: Symbol references are unknown.
+ 1: Symbol references aren't local.
+ 2: Symbol references are local.
+ */
+ unsigned int local_ref : 2;
+
+ /* TRUE if symbol is defined by linker. */
+ unsigned int linker_def : 1;
+
+ /* TRUE if symbol is referenced by R_386_GOTOFF relocation. This is
+ only used by i386. */
+ unsigned int gotoff_ref : 1;
+
+ /* TRUE if a weak symbol with a real definition needs a copy reloc.
+ When there is a weak symbol with a real definition, the processor
+ independent code will have arranged for us to see the real
+ definition first. We need to copy the needs_copy bit from the
+ real definition and check it when allowing copy reloc in PIE. This
+ is only used by x86-64. */
+ unsigned int needs_copy : 1;
+
+ /* Information about the GOT PLT entry. Filled when there are both
+ GOT and PLT relocations against the same function. */
+ union gotplt_union plt_got;
+
+ /* Information about the second PLT entry. */
+ union gotplt_union plt_second;
+
+ /* Offset of the GOTPLT entry reserved for the TLS descriptor,
+ starting at the end of the jump table. */
+ bfd_vma tlsdesc_got;
+ };
+
+ struct elf_x86_lazy_plt_layout
+ {
+ /* The first entry in an absolute lazy procedure linkage table looks
+ like this. */
+ const bfd_byte *plt0_entry;
+ unsigned int plt0_entry_size; /* Size of PLT0 entry. */
+
+ /* Later entries in an absolute lazy procedure linkage table look
+ like this. */
+ const bfd_byte *plt_entry;
+ unsigned int plt_entry_size; /* Size of each PLT entry. */
+
+ /* Offsets into plt0_entry that are to be replaced with GOT[1] and
+ GOT[2]. */
+ unsigned int plt0_got1_offset;
+ unsigned int plt0_got2_offset;
+
+ /* Offset of the end of the PC-relative instruction containing
+ plt0_got2_offset. This is for x86-64 only. */
+ unsigned int plt0_got2_insn_end;
+
+ /* Offsets into plt_entry that are to be replaced with... */
+ unsigned int plt_got_offset; /* ... address of this symbol in .got. */
+ unsigned int plt_reloc_offset; /* ... offset into relocation table. */
+ unsigned int plt_plt_offset; /* ... offset to start of .plt. */
+
+ /* Length of the PC-relative instruction containing plt_got_offset.
+ This is used for x86-64 only. */
+ unsigned int plt_got_insn_size;
+
+ /* Offset of the end of the PC-relative jump to plt0_entry. This is
+ used for x86-64 only. */
+ unsigned int plt_plt_insn_end;
+
+ /* Offset into plt_entry where the initial value of the GOT entry
+ points. */
+ unsigned int plt_lazy_offset;
+
+ /* The first entry in a PIC lazy procedure linkage table looks like
+ this. */
+ const bfd_byte *pic_plt0_entry;
+
+ /* Subsequent entries in a PIC lazy procedure linkage table look
+ like this. */
+ const bfd_byte *pic_plt_entry;
+
+ /* .eh_frame covering the lazy .plt section. */
+ const bfd_byte *eh_frame_plt;
+ unsigned int eh_frame_plt_size;
+ };
+
+ struct elf_x86_non_lazy_plt_layout
+ {
+ /* Entries in an absolute non-lazy procedure linkage table look like
+ this. */
+ const bfd_byte *plt_entry;
+ /* Entries in a PIC non-lazy procedure linkage table look like this. */
+ const bfd_byte *pic_plt_entry;
+
+ unsigned int plt_entry_size; /* Size of each PLT entry. */
+
+ /* Offsets into plt_entry that are to be replaced with... */
+ unsigned int plt_got_offset; /* ... address of this symbol in .got. */
+
+ /* Length of the PC-relative instruction containing plt_got_offset.
+ This is used for x86-64 only. */
+ unsigned int plt_got_insn_size;
+
+ /* .eh_frame covering the non-lazy .plt section. */
+ const bfd_byte *eh_frame_plt;
+ unsigned int eh_frame_plt_size;
+ };
+
+ struct elf_x86_plt_layout
+ {
+ /* The first entry in a lazy procedure linkage table looks like this.
+ This is only used for i386 where absolute PLT0 and PIC PLT0 are
+ different. */
+ const bfd_byte *plt0_entry;
+ /* Entries in a procedure linkage table look like this. */
+ const bfd_byte *plt_entry;
+ unsigned int plt_entry_size; /* Size of each PLT entry. */
+
+ /* 1 has PLT0. */
+ unsigned int has_plt0;
+
+ /* Offsets into plt_entry that are to be replaced with... */
+ unsigned int plt_got_offset; /* ... address of this symbol in .got. */
+
+ /* Length of the PC-relative instruction containing plt_got_offset.
+ This is only used for x86-64. */
+ unsigned int plt_got_insn_size;
+
+ /* .eh_frame covering the .plt section. */
+ const bfd_byte *eh_frame_plt;
+ unsigned int eh_frame_plt_size;
+ };
+
+ /* Values in tls_type of x86 ELF linker hash entry. */
+ #define GOT_UNKNOWN 0
+ #define GOT_NORMAL 1
+ #define GOT_TLS_GD 2
+ #define GOT_TLS_IE 4
+ #define GOT_TLS_IE_POS 5
+ #define GOT_TLS_IE_NEG 6
+ #define GOT_TLS_IE_BOTH 7
+ #define GOT_TLS_GDESC 8
+ #define GOT_TLS_GD_BOTH_P(type) \
+ ((type) == (GOT_TLS_GD | GOT_TLS_GDESC))
+ #define GOT_TLS_GD_P(type) \
+ ((type) == GOT_TLS_GD || GOT_TLS_GD_BOTH_P (type))
+ #define GOT_TLS_GDESC_P(type) \
+ ((type) == GOT_TLS_GDESC || GOT_TLS_GD_BOTH_P (type))
+ #define GOT_TLS_GD_ANY_P(type) \
+ (GOT_TLS_GD_P (type) || GOT_TLS_GDESC_P (type))
+
+ #define elf_x86_hash_entry(ent) \
+ ((struct elf_x86_link_hash_entry *)(ent))
+
+ enum elf_x86_target_os
+ {
+ is_normal,
+ is_vxworks,
+ is_nacl
+ };
+
+ /* x86 ELF linker hash table. */
+
+ struct elf_x86_link_hash_table
+ {
+ struct elf_link_hash_table elf;
+
+ /* Short-cuts to get to dynamic linker sections. */
+ asection *interp;
+ asection *plt_eh_frame;
+ asection *plt_second;
+ asection *plt_second_eh_frame;
+ asection *plt_got;
+ asection *plt_got_eh_frame;
+
+ /* Parameters describing PLT generation, lazy or non-lazy. */
+ struct elf_x86_plt_layout plt;
+
+ /* Parameters describing lazy PLT generation. */
+ const struct elf_x86_lazy_plt_layout *lazy_plt;
+
+ /* Parameters describing non-lazy PLT generation. */
+ const struct elf_x86_non_lazy_plt_layout *non_lazy_plt;
+
++ /* Cache __gp symbol. */
++ struct elf_link_hash_entry *gp;
++
+ union
+ {
+ bfd_signed_vma refcount;
+ bfd_vma offset;
+ } tls_ld_or_ldm_got;
+
+ /* The amount of space used by the jump slots in the GOT. */
+ bfd_vma sgotplt_jump_table_size;
+
+ /* Small local sym cache. */
+ struct sym_cache sym_cache;
+
+ /* _TLS_MODULE_BASE_ symbol. */
+ struct bfd_link_hash_entry *tls_module_base;
+
+ /* Used by local STT_GNU_IFUNC symbols. */
+ htab_t loc_hash_table;
+ void * loc_hash_memory;
+
+ /* The offset into sgot of the GOT entry used by the PLT entry
+ above. */
+ bfd_vma tlsdesc_got;
+
+ /* The index of the next R_X86_64_JUMP_SLOT entry in .rela.plt. */
+ 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;
+
+ /* The (unloaded but important) .rel.plt.unloaded section on VxWorks.
+ This is used for i386 only. */
+ asection *srelplt2;
+
+ /* The index of the next unused R_386_TLS_DESC slot in .rel.plt. This
+ is only used for i386. */
+ bfd_vma next_tls_desc_index;
+
+ /* The offset into splt of the PLT entry for the TLS descriptor
+ resolver. Special values are 0, if not necessary (or not found
+ to be necessary yet), and -1 if needed but not determined
+ yet. This is only used for x86-64. */
+ bfd_vma tlsdesc_plt;
+
+ /* Value used to fill the unused bytes of the first PLT entry. This
+ is only used for i386. */
+ bfd_byte plt0_pad_byte;
+
+ bfd_vma (*r_info) (bfd_vma, bfd_vma);
+ bfd_vma (*r_sym) (bfd_vma);
+ bfd_boolean (*is_reloc_section) (const char *);
+ enum elf_target_id target_id;
+ enum elf_x86_target_os target_os;
+ unsigned int sizeof_reloc;
+ unsigned int dt_reloc;
+ unsigned int dt_reloc_sz;
+ unsigned int dt_reloc_ent;
+ unsigned int got_entry_size;
+ unsigned int pointer_r_type;
+ int dynamic_interpreter_size;
+ const char *dynamic_interpreter;
+ const char *tls_get_addr;
+ };
+
+ /* Architecture-specific backend data for x86. */
+
+ struct elf_x86_backend_data
+ {
+ /* Target system. */
+ enum elf_x86_target_os target_os;
+ };
+
+ #define get_elf_x86_backend_data(abfd) \
+ ((const struct elf_x86_backend_data *) \
+ get_elf_backend_data (abfd)->arch_data)
+
+ struct elf_x86_init_table
+ {
+ /* The lazy PLT layout. */
+ const struct elf_x86_lazy_plt_layout *lazy_plt;
+
+ /* The non-lazy PLT layout. */
+ const struct elf_x86_non_lazy_plt_layout *non_lazy_plt;
+
+ /* The lazy PLT layout for IBT. */
+ const struct elf_x86_lazy_plt_layout *lazy_ibt_plt;
+
+ /* The non-lazy PLT layout for IBT. */
+ const struct elf_x86_non_lazy_plt_layout *non_lazy_ibt_plt;
+
+ bfd_byte plt0_pad_byte;
+
+ bfd_vma (*r_info) (bfd_vma, bfd_vma);
+ bfd_vma (*r_sym) (bfd_vma);
+ };
+
+ struct elf_x86_obj_tdata
+ {
+ struct elf_obj_tdata root;
+
+ /* tls_type for each local got entry. */
+ char *local_got_tls_type;
+
+ /* GOTPLT entries for TLS descriptors. */
+ bfd_vma *local_tlsdesc_gotent;
+ };
+
+ enum elf_x86_plt_type
+ {
+ plt_non_lazy = 0,
+ plt_lazy = 1 << 0,
+ plt_pic = 1 << 1,
+ plt_second = 1 << 2,
+ plt_unknown = -1
+ };
+
+ struct elf_x86_plt
+ {
+ const char *name;
+ asection *sec;
+ bfd_byte *contents;
+ enum elf_x86_plt_type type;
+ unsigned int plt_got_offset;
+ unsigned int plt_entry_size;
+ unsigned int plt_got_insn_size; /* Only used for x86-64. */
+ long count;
+ };
+
+ #define elf_x86_tdata(abfd) \
+ ((struct elf_x86_obj_tdata *) (abfd)->tdata.any)
+
+ #define elf_x86_local_got_tls_type(abfd) \
+ (elf_x86_tdata (abfd)->local_got_tls_type)
+
+ #define elf_x86_local_tlsdesc_gotent(abfd) \
+ (elf_x86_tdata (abfd)->local_tlsdesc_gotent)
+
+ #define elf_x86_compute_jump_table_size(htab) \
+ ((htab)->elf.srelplt->reloc_count * (htab)->got_entry_size)
+
+ #define is_x86_elf(bfd, htab) \
+ (bfd_get_flavour (bfd) == bfd_target_elf_flavour \
+ && elf_tdata (bfd) != NULL \
+ && elf_object_id (bfd) == (htab)->target_id)
+
+ extern bfd_boolean _bfd_x86_elf_mkobject
+ (bfd *);
+
+ extern void _bfd_x86_elf_set_tls_module_base
+ (struct bfd_link_info *);
+
+ extern bfd_vma _bfd_x86_elf_dtpoff_base
+ (struct bfd_link_info *);
+
+ extern bfd_boolean _bfd_x86_elf_readonly_dynrelocs
+ (struct elf_link_hash_entry *, void *);
+
+ extern struct elf_link_hash_entry * _bfd_elf_x86_get_local_sym_hash
+ (struct elf_x86_link_hash_table *, bfd *, const Elf_Internal_Rela *,
+ bfd_boolean);
+
+ extern hashval_t _bfd_x86_elf_local_htab_hash
+ (const void *);
+
+ extern int _bfd_x86_elf_local_htab_eq
+ (const void *, const void *);
+
+ extern struct bfd_hash_entry * _bfd_x86_elf_link_hash_newfunc
+ (struct bfd_hash_entry *, struct bfd_hash_table *, const char *);
+
+ extern struct bfd_link_hash_table * _bfd_x86_elf_link_hash_table_create
+ (bfd *);
+
+ extern int _bfd_x86_elf_compare_relocs
+ (const void *, const void *);
+
+ extern bfd_boolean _bfd_x86_elf_link_check_relocs
+ (bfd *, struct bfd_link_info *);
+
+ extern bfd_boolean _bfd_x86_elf_size_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+
+ extern struct elf_x86_link_hash_table *_bfd_x86_elf_finish_dynamic_sections
+ (bfd *, struct bfd_link_info *);
+
+ extern bfd_boolean _bfd_x86_elf_always_size_sections
+ (bfd *, struct bfd_link_info *);
+
+ extern void _bfd_x86_elf_merge_symbol_attribute
+ (struct elf_link_hash_entry *, const Elf_Internal_Sym *,
+ bfd_boolean, bfd_boolean);
+
+ extern void _bfd_x86_elf_copy_indirect_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *,
+ struct elf_link_hash_entry *);
+
+ extern bfd_boolean _bfd_x86_elf_fixup_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *);
+
+ extern bfd_boolean _bfd_x86_elf_hash_symbol
+ (struct elf_link_hash_entry *);
+
+ extern bfd_boolean _bfd_x86_elf_adjust_dynamic_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *);
+
+ extern void _bfd_x86_elf_hide_symbol
+ (struct bfd_link_info *, struct elf_link_hash_entry *, bfd_boolean);
+
+ extern bfd_boolean _bfd_x86_elf_link_symbol_references_local
+ (struct bfd_link_info *, struct elf_link_hash_entry *);
+
+ extern asection * _bfd_x86_elf_gc_mark_hook
+ (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
+ struct elf_link_hash_entry *, Elf_Internal_Sym *);
+
+ extern long _bfd_x86_elf_get_synthetic_symtab
+ (bfd *, long, long, bfd_vma, struct elf_x86_plt [], asymbol **,
+ asymbol **);
+
+ extern enum elf_property_kind _bfd_x86_elf_parse_gnu_properties
+ (bfd *, unsigned int, bfd_byte *, unsigned int);
+
+ extern bfd_boolean _bfd_x86_elf_merge_gnu_properties
+ (struct bfd_link_info *, bfd *, elf_property *, elf_property *);
+
+ extern bfd * _bfd_x86_elf_link_setup_gnu_properties
+ (struct bfd_link_info *, struct elf_x86_init_table *);
+
++extern bfd_boolean _bfd_x86_elf_final_link
++ (bfd *, struct bfd_link_info *);
++
+ #define bfd_elf64_mkobject \
+ _bfd_x86_elf_mkobject
+ #define bfd_elf32_mkobject \
+ _bfd_x86_elf_mkobject
+ #define bfd_elf64_bfd_link_hash_table_create \
+ _bfd_x86_elf_link_hash_table_create
+ #define bfd_elf32_bfd_link_hash_table_create \
+ _bfd_x86_elf_link_hash_table_create
+ #define bfd_elf64_bfd_link_check_relocs \
+ _bfd_x86_elf_link_check_relocs
+ #define bfd_elf32_bfd_link_check_relocs \
+ _bfd_x86_elf_link_check_relocs
++#define bfd_elf64_bfd_final_link \
++ _bfd_x86_elf_final_link
++#define bfd_elf32_bfd_final_link \
++ _bfd_x86_elf_final_link
+
+ #define elf_backend_size_dynamic_sections \
+ _bfd_x86_elf_size_dynamic_sections
+ #define elf_backend_always_size_sections \
+ _bfd_x86_elf_always_size_sections
+ #define elf_backend_merge_symbol_attribute \
+ _bfd_x86_elf_merge_symbol_attribute
+ #define elf_backend_copy_indirect_symbol \
+ _bfd_x86_elf_copy_indirect_symbol
+ #define elf_backend_fixup_symbol \
+ _bfd_x86_elf_fixup_symbol
+ #define elf_backend_hash_symbol \
+ _bfd_x86_elf_hash_symbol
+ #define elf_backend_adjust_dynamic_symbol \
+ _bfd_x86_elf_adjust_dynamic_symbol
+ #define elf_backend_gc_mark_hook \
+ _bfd_x86_elf_gc_mark_hook
+ #define elf_backend_omit_section_dynsym \
+ ((bfd_boolean (*) (bfd *, struct bfd_link_info *, asection *)) bfd_true)
+ #define elf_backend_parse_gnu_properties \
+ _bfd_x86_elf_parse_gnu_properties
+ #define elf_backend_merge_gnu_properties \
+ _bfd_x86_elf_merge_gnu_properties
do
{
enum link_action action;
- enum bfd_link_hash_type type;
-
- type = h->type;
+ int prev;
+
+ prev = h->type;
+ /* Treat symbols defined by early linker script pass as undefined. */
+ if (h->ldscript_def)
+ prev = bfd_link_hash_undefined;
+ /* Convert a secondary symbol to a weak symbol. Backend is
+ responsible to let a weak symbol override a secondary
+ symbol. */
- if (h->secondary)
- switch (type)
++ else if (h->secondary)
++ switch (prev)
+ {
+ default:
+ break;
+
+ case bfd_link_hash_undefined:
- type = bfd_link_hash_undefweak;
++ prev = bfd_link_hash_undefweak;
+ break;
+
+ case bfd_link_hash_defined:
- type = bfd_link_hash_defweak;
++ prev = bfd_link_hash_defweak;
+ break;
+ }
+
cycle = FALSE;
- action = link_action[(int) row][(int) type];
+ action = link_action[(int) row][prev];
switch (action)
{
case FAIL:
h->u.def.section = section;
h->u.def.value = value;
h->linker_def = 0;
+ h->ldscript_def = 0;
+ /* Mark if this is a secondary symbol. */
+ h->secondary = secondary;
+
/* If we have been asked to, we act like collect2 and
identify all functions that might be global
constructors and destructors and pass them up in a
. {* This symbol is a globally unique data object. The dynamic linker
. will make sure that in the entire process there is just one symbol
. with this name and type in use. BSF_OBJECT must also be set. *}
- .#define BSF_GNU_UNIQUE (1 << 23)
+ .#define BSF_GNU_UNIQUE (1 << 23)
.
+. {* A secondary global symbol, overridable without warnings by
+. a regular or weak global symbol of the same name. *}
+.#define BSF_SECONDARY (1 << 24)
+.
. flagword flags;
.
. {* A pointer to the section to which this symbol is
}
}
else if ((do_dyn_syms || (do_syms && !do_using_dynamic))
- && section_headers != NULL)
+ && filedata->section_headers != NULL)
{
unsigned int i;
- = elf_header.e_ident[EI_OSABI] != ELFOSABI_IRIX;
+ /* Irix 5 and 6 are broken. Object file symbol tables are not
+ always sorted correctly such that local symbols precede global
+ symbols, and the sh_info field in the symbol table is not
+ always right. */
+ bfd_boolean check_corrupt_symtab
++ = filedata->file_header.e_ident[EI_OSABI] != ELFOSABI_IRIX;
- for (i = 0, section = section_headers;
- i < elf_header.e_shnum;
+ for (i = 0, section = filedata->section_headers;
+ i < filedata->file_header.e_shnum;
i++, section++)
{
unsigned int si;
print_vma (psym->st_value, LONG_HEX);
putchar (' ');
print_vma (psym->st_size, DEC_5);
- printf (" %-7s", get_symbol_type (ELF_ST_TYPE (psym->st_info)));
+ printf (" %-7s", get_symbol_type (filedata, ELF_ST_TYPE (psym->st_info)));
- printf (" %-6s", get_symbol_binding (filedata, ELF_ST_BIND (psym->st_info)));
+ if (check_corrupt_symtab
+ && si < section->sh_info
+ && ELF_ST_BIND (psym->st_info) != STB_LOCAL)
+ printf (" %-6s", "<corrupt>");
+ else
- printf (" %-6s", get_symbol_binding (ELF_ST_BIND (psym->st_info)));
- if (elf_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
++ printf (" %-6s", get_symbol_binding (filedata, ELF_ST_BIND (psym->st_info)));
+ if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
printf (" %-7s", get_solaris_symbol_visibility (psym->st_other));
else
{
/* Pre-defined "_GLOBAL_OFFSET_TABLE_". */
static symbolS *GOT_symbol;
++/* Pre-defined "__gp". */
++static symbolS *GP_symbol;
++
/* The dwarf2 return column, adjusted for 32 or 64 bit. */
unsigned int x86_dwarf2_return_column;
{ STRING_COMMA_LEN ("GOTPCREL"), { _dummy_first_bfd_reloc_code_real,
BFD_RELOC_X86_64_GOTPCREL },
OPERAND_TYPE_IMM32_32S_DISP32 },
++ { STRING_COMMA_LEN ("GPOFF"), { BFD_RELOC_GPREL32,
++ BFD_RELOC_GPREL32 },
++ OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("TLSGD"), { BFD_RELOC_386_TLS_GD,
BFD_RELOC_X86_64_TLSGD },
OPERAND_TYPE_IMM32_32S_DISP32 },
*types = gotrel[j].types64;
}
-- if (j != 0 && GOT_symbol == NULL)
-- GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
++ if (j != 0)
++ {
++ if (gotrel[j].rel[1] == BFD_RELOC_GPREL32)
++ {
++ if (GP_symbol == NULL)
++ GP_symbol = symbol_find_or_make (GLOBAL_POINTER_NAME);
++ }
++ else
++ {
++ if (GOT_symbol == NULL)
++ GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
++ }
++ }
/* The length of the first part of our input line. */
first = cp - input_line_pointer;
if (gotfree_input_line)
input_line_pointer = gotfree_input_line;
++ if (i.reloc[this_operand] == BFD_RELOC_GPREL32
++ && i.types[this_operand].bitfield.baseindex)
++ as_bad (_("invalid GPOFF relocation"));
++
exp_seg = expression (exp);
SKIP_WHITESPACE ();
};
return GOT_symbol;
}
++ else if (name[0] == GLOBAL_POINTER_NAME[0]
++ && name[1] == GLOBAL_POINTER_NAME[1]
++ && name[2] == GLOBAL_POINTER_NAME[2]
++ && name[3] == GLOBAL_POINTER_NAME[3]
++ && strcmp (name, GLOBAL_POINTER_NAME) == 0)
++ {
++ if (!GP_symbol)
++ {
++ if (symbol_find (name))
++ as_bad (_("GP already in symbol table"));
++ GP_symbol = symbol_new (name, undefined_section,
++ (valueT) 0, &zero_address_frag);
++ };
++ return GP_symbol;
++ }
return 0;
}
case BFD_RELOC_X86_64_PLTOFF64:
case BFD_RELOC_X86_64_GOTPC32_TLSDESC:
case BFD_RELOC_X86_64_TLSDESC_CALL:
++ case BFD_RELOC_GPREL32:
case BFD_RELOC_RVA:
case BFD_RELOC_VTABLE_ENTRY:
case BFD_RELOC_VTABLE_INHERIT:
#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_"
#endif
++/* The name of the global pointer. Allow this to be overridden if need
++ be. */
++#ifndef GLOBAL_POINTER_NAME
++#define GLOBAL_POINTER_NAME "__gp"
++#endif
++
#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && !defined (LEX_AT)
#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) x86_cons (EXP, NBYTES)
#endif
--- /dev/null
- #error-output: common5.l
+#name: secondary and common directives
++#error-output: common7.l
run_dump_test "common3b"
run_dump_test "common4a"
run_dump_test "common4b"
- run_dump_test "common5"
+ run_dump_test "common5a"
+ run_dump_test "common5b"
+ run_dump_test "common5c"
+ run_dump_test "common5d"
+ run_dump_test "common6"
++ run_dump_test "common7"
}
+ run_elf_list_test "secondary1" "" "" "-s" "| grep \"secondary_\""
+ run_elf_list_test "secondary2" "" "" "-s" "| grep \"secondary_\""
+
run_dump_test "strtab"
run_dump_test "bignums"
--- /dev/null
--- /dev/null
++#objdump: -drw
++#name: i386 gpoff
++
++.*: +file format .*
++
++Disassembly of section .text:
++
++0+ <_start>:
++ +[a-f0-9]+: 8d 05 00 00 00 00 lea 0x0,%eax 2: R_386_GPOFF foo
++ +[a-f0-9]+: 64 a1 00 00 00 00 mov %fs:0x0,%eax 8: R_386_GPOFF foo
++#pass
--- /dev/null
--- /dev/null
++ .text
++_start:
++ leal foo@GPOFF, %eax
++ movl %fs:foo@GPOFF, %eax
run_dump_test "addend"
+ if { [gas_64_check] } then {
+ run_dump_test "att-regs"
+ run_dump_test "intel-regs"
+ run_dump_test "mixed-mode-reloc32"
+ run_dump_test "code64"
+ } else {
+ run_list_test "code64-inval" "-I${srcdir}/$subdir -al"
+ }
+
++ run_dump_test "gpoff"
++ run_list_test "inval-gpoff" "-al"
++
if {![istarget "*-*-nacl*"]} then {
run_dump_test "iamcu-1"
run_dump_test "iamcu-2"
run_dump_test "x86-64-gotpcrel-no-relax"
run_dump_test "x86-64-addend"
++
++ run_dump_test "x86-64-gpoff"
++ run_list_test "x86-64-inval-gpoff" "-al"
}
set ASFLAGS "$old_ASFLAGS"
--- /dev/null
--- /dev/null
++.*: Assembler messages:
++.*:3: Error: invalid GPOFF relocation
++.*:4: Error: invalid GPOFF relocation
++.*:5: Error: invalid GPOFF relocation
++GAS LISTING .*
++
++
++[ ]*1[ ]+\.text
++[ ]*2[ ]+_start:
++[ ]*3[ ]+\?\?\?\? 648B8000 movl %fs:foo@GPOFF\(%eax\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++[ ]*3[ ]+000000
++[ ]*4[ ]+\?\?\?\? 8B844800 movl %ds:foo@GPOFF\(%eax, %ecx, 2\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++[ ]*4[ ]+000000
++[ ]*5[ ]+\?\?\?\? 8B800000 movl foo@GPOFF\(%eax\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++[ ]*5[ ]+0000
--- /dev/null
--- /dev/null
++ .text
++_start:
++ movl %fs:foo@GPOFF(%eax), %eax
++ movl %ds:foo@GPOFF(%eax, %ecx, 2), %eax
++ movl foo@GPOFF(%eax), %eax
--- /dev/null
--- /dev/null
++#objdump: -drw
++#name: x86-64 gpoff
++
++.*: +file format .*
++
++Disassembly of section .text:
++
++0+ <_start>:
++ +[a-f0-9]+: 8d 04 25 00 00 00 00 lea 0x0,%eax 3: R_X86_64_GPOFF foo
++ +[a-f0-9]+: 65 8b 04 25 00 00 00 00 mov %gs:0x0,%eax b: R_X86_64_GPOFF foo
++#pass
--- /dev/null
--- /dev/null
++ .text
++_start:
++ leal foo@GPOFF, %eax
++ movl %gs:foo@GPOFF, %eax
--- /dev/null
--- /dev/null
++.*: Assembler messages:
++.*:3: Error: invalid GPOFF relocation
++.*:3: Error: non-pc-relative relocation for pc-relative field
++.*:4: Error: invalid GPOFF relocation
++.*:5: Error: invalid GPOFF relocation
++.*:6: Error: invalid GPOFF relocation
++.*:6: Error: non-pc-relative relocation for pc-relative field
++.*:7: Error: invalid GPOFF relocation
++GAS LISTING .*
++
++
++[ ]*1[ ]+\.text
++[ ]*2[ ]+_start:
++[ ]*3[ ]+\?\?\?\? 648B0500 movl %fs:foo@GPOFF\(%rip\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++\*\*\*\* Error: non-pc-relative relocation for pc-relative field
++[ ]*3[ ]+000000
++[ ]*4[ ]+\?\?\?\? 648B8000 movl %fs:foo@GPOFF\(%rax\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++[ ]*4[ ]+000000
++[ ]*5[ ]+\?\?\?\? 8B844800 movl %ds:foo@GPOFF\(%rax, %rcx, 2\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++[ ]*5[ ]+000000
++[ ]*6[ ]+\?\?\?\? 8B050000 movl foo@GPOFF\(%rip\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++\*\*\*\* Error: non-pc-relative relocation for pc-relative field
++[ ]*6[ ]+0000
++[ ]*7[ ]+\?\?\?\? 8B800000 movl foo@GPOFF\(%rax\), %eax
++\*\*\*\* Error: invalid GPOFF relocation
++[ ]*7[ ]+0000
--- /dev/null
--- /dev/null
++ .text
++_start:
++ movl %fs:foo@GPOFF(%rip), %eax
++ movl %fs:foo@GPOFF(%rax), %eax
++ movl %ds:foo@GPOFF(%rax, %rcx, 2), %eax
++ movl foo@GPOFF(%rip), %eax
++ movl foo@GPOFF(%rax), %eax
RELOC_NUMBER (R_386_IRELATIVE, 42) /* Adjust indirectly by program base */
/* Load from 32 bit GOT entry, relaxable. */
RELOC_NUMBER (R_386_GOT32X, 43)
++ RELOC_NUMBER (R_386_GPOFF, 44) /* 32 bit offset to __gp */
/* Used by Intel. */
RELOC_NUMBER (R_386_USED_BY_INTEL_200, 200)
/* Load from 32 bit signed pc relative offset to GOT entry with
REX prefix, relaxable. */
RELOC_NUMBER (R_X86_64_REX_GOTPCRELX, 42)
++ RELOC_NUMBER (R_X86_64_GPOFF, 43) /* 32 bit offset to __gp */
RELOC_NUMBER (R_X86_64_GNU_VTINHERIT, 250) /* GNU C++ hack */
RELOC_NUMBER (R_X86_64_GNU_VTENTRY, 251) /* GNU C++ hack */
END_RELOC_NUMBERS (R_X86_64_max)
}
abfd = link_info.output_bfd;
- if (avr_link_relax)
- elf_elfheader (abfd)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
- else
- elf_elfheader (abfd)->e_flags &= ~EF_AVR_LINKRELAX_PREPARED;
+
+ if (bfd_get_flavour (link_info.output_bfd) == bfd_target_elf_flavour)
+ {
+ if (avr_link_relax)
+ elf_elfheader (abfd)->e_flags |= EF_AVR_LINKRELAX_PREPARED;
+ else
+ elf_elfheader (abfd)->e_flags &= ~EF_AVR_LINKRELAX_PREPARED;
+ }
- finish_default ();
+ gld${EMULATION_NAME}_finish ();
}
EOF
return last;
}
+ /* Return whether IN is suitable to be part of OUT. */
+
+ static bfd_boolean
+ elf_orphan_compatible (asection *in, asection *out)
+ {
+ /* Non-zero sh_info implies a section with SHF_INFO_LINK with
+ unknown semantics for the generic linker, or a SHT_REL/SHT_RELA
+ section where sh_info specifies a symbol table. (We won't see
+ SHT_GROUP, SHT_SYMTAB or SHT_DYNSYM sections here.) We clearly
+ can't merge SHT_REL/SHT_RELA using differing symbol tables, and
+ shouldn't merge sections with differing unknown semantics. */
+ if (elf_section_data (out)->this_hdr.sh_info
+ != elf_section_data (in)->this_hdr.sh_info)
+ return FALSE;
+ /* We can't merge two sections with differing SHF_EXCLUDE when doing
+ a relocatable link. */
+ if (bfd_link_relocatable (&link_info)
+ && ((elf_section_flags (out) ^ elf_section_flags (in)) & SHF_EXCLUDE) != 0)
+ return FALSE;
+ return _bfd_elf_match_sections_by_type (link_info.output_bfd, out,
+ in->owner, in);
+ }
+
+static int orphan_init_done = 0;
+
/* Place an orphan section. We use this to put random SHF_ALLOC
sections in the right segment. */
s = spu_elf_check_vma (&link_info);
if (s != NULL && !params.auto_overlay)
- einfo ("%X%P: %A exceeds local store range\n", s);
+ einfo (_("%X%P: %A exceeds local store range\n"), s);
}
else if (params.auto_overlay)
- einfo ("%P: --auto-overlay ignored with zero local store range\n");
+ einfo (_("%P: --auto-overlay ignored with zero local store range\n"));
}
- finish_default ();
+ gld${EMULATION_NAME}_finish ();
}
static char *
Lazy binding is the default.
@item loadfltr
- Marks the object that its filters be processed immediately at
- runtime.
+ Specify that the object's filters be processed immediately at runtime.
- @item muldefs
- Allows multiple definitions.
++@item nosecondary
++Convert secondary symbols to weak symbols when generating a shared
++library.
+
- @item nocombreloc
- Disables multiple reloc sections combining.
+ @item max-page-size=@var{value}
+ Set the maximum memory page size supported to @var{value}.
- @item nocommon
- Generate common symbols with the STT_OBJECT type druing a relocatable
- link.
+ @item muldefs
+ Allow multiple definitions.
@item nocopyreloc
Disable linker generated .dynbss variables used in place of variables
lang_list_insert_after (&file_chain, &files, &file_chain.head);
/* Rescan archives in case new undefined symbols have appeared. */
+ files = file_chain;
+ lang_statement_iteration++;
open_input_bfds (statement_list.head, OPEN_BFD_RESCAN);
+ lang_list_remove_tail (&file_chain, &files);
+ while (files.head != NULL)
+ {
+ lang_statement_union_type **insert;
+ lang_statement_union_type **iter, *temp;
+ bfd *my_arch;
+
+ insert = find_rescan_insertion (&files.head->input_statement);
+ /* All elements from an archive can be added at once. */
+ iter = &files.head->input_statement.next;
+ my_arch = files.head->input_statement.the_bfd->my_archive;
+ if (my_arch != NULL)
+ for (; *iter != NULL; iter = &(*iter)->input_statement.next)
+ if ((*iter)->input_statement.the_bfd->my_archive != my_arch)
+ break;
+ temp = *insert;
+ *insert = files.head;
+ files.head = *iter;
+ *iter = temp;
+ if (my_arch != NULL)
+ {
+ lang_input_statement_type *parent = my_arch->usrdata;
+ if (parent != NULL)
+ parent->next = (lang_statement_union_type *)
+ ((char *) iter
+ - offsetof (lang_input_statement_type, next));
+ }
+ }
}
}
+ else
#endif /* ENABLE_PLUGINS */
+ if (bfd_link_relocatable (&link_info))
+ {
+ /* Check if .gnu_object_only section should be created. */
+ bfd *p;
+ int object_type;
+
+ object_type = 0;
+ for (p = link_info.input_bfds; p != (bfd *) NULL; p = p->link.next)
+ {
+ object_type |= 1 << p->lto_type;
+ if ((object_type & (1 << lto_mixed_object)) != 0
+ || ((object_type
+ & (1 << lto_non_ir_object
+ | 1 << lto_ir_object))
+ == (1 << lto_non_ir_object | 1 << lto_ir_object)))
+ {
+ link_info.emit_gnu_object_only = TRUE;
+ break;
+ }
+ }
+
+ if (verbose
+ && (cmdline_object_only_file_list.head
+ || cmdline_object_only_archive_list.head))
+ {
+ info_msg (_("Object-only input files:\n "));
+ print_cmdline_list (cmdline_object_only_file_list.head);
+ print_cmdline_list (cmdline_object_only_archive_list.head);
+ }
+ }
/* Make sure that nobody has tried to add a symbol to this list
before now. */
printf (" %6.2f%%\n", percent);
}
}
+
+static void
+cmdline_lists_init (void)
+{
+ cmdline_object_only_file_list.tail
+ = &cmdline_object_only_file_list.head;
+ cmdline_object_only_archive_list.tail
+ = &cmdline_object_only_archive_list.head;
+ cmdline_temp_object_only_list.tail
+ = &cmdline_temp_object_only_list.head;
+}
+
+/* Allocate an item with TYPE and DATA. */
+
+static cmdline_union_type *
+cmdline_list_new (cmdline_enum_type type, void *data)
+{
+ cmdline_union_type *new_opt;
+
+ new_opt = (cmdline_union_type *) stat_alloc (sizeof (*new_opt));
+ new_opt->header.type = type;
+ switch (type)
+ {
+ default:
+ break;
+ case cmdline_is_file_enum:
+ new_opt->file.filename = (const char *) data;
+ break;
+ case cmdline_is_bfd_enum:
+ new_opt->abfd.abfd = (bfd *) data;
+ break;
+ }
+ return new_opt;
+}
+
+/* Append an item with TYPE and DATA to LIST. */
+
+static void
+cmdline_list_append (cmdline_list_type *list, cmdline_enum_type type,
+ void *data)
+{
+ cmdline_union_type *new_opt = cmdline_list_new (type, data);
+ new_opt->header.next = NULL;
+ *list->tail = new_opt;
+ list->tail = &new_opt->header.next;
+}
+
+static void
+print_cmdline_list (cmdline_union_type *c)
+{
+ for (; c != NULL; c = c->header.next)
+ switch (c->header.type)
+ {
+ default:
+ abort ();
+ case cmdline_is_file_enum:
+ info_msg (" %s", c->file.filename);
+ break;
+ case cmdline_is_bfd_enum:
+ info_msg (" [%B]", c->abfd.abfd);
+ break;
+ }
+
+ info_msg ("\n");
+}
+
+/* Return TRUE if ABFD is on cmdline_object_only_archive_list. */
+
+static bfd_boolean
+cmdline_on_object_only_archive_list_p (bfd *abfd)
+{
+ cmdline_union_type *c, *next;
+ bfd *archive, *obfd, *oarchive;
+ ufile_ptr origin = abfd->origin;
+
+ archive = abfd->my_archive;
+ for (c = cmdline_object_only_archive_list.head; c != NULL; c = next)
+ {
+ if (c->header.type != cmdline_is_bfd_enum)
+ abort ();
+
+ next = c->header.next;
+ obfd = c->abfd.abfd;
+ oarchive = obfd->my_archive;
+
+ /* The list is grouped by archive file name and sorted by member
+ origin. */
+ if (strcmp (archive->filename, oarchive->filename) != 0)
+ continue;
+
+ if (origin == obfd->origin)
+ return TRUE;
+ else if (origin < obfd->origin)
+ return FALSE;
+ }
+
+ return FALSE;
+}
+
+/* Append an item with TYPE and DATA to cmdline_object_only_file_list
+ or cmdline_object_only_archive_list if needed. */
+
+static void
+cmdline_object_only_list_append (cmdline_enum_type type, void *data)
+{
+ cmdline_union_type *c;
+ cmdline_union_type *new_opt, *next, **prev;
+ bfd *abfd, *archive;
+ bfd *obfd, *oarchive;
+ bfd *nbfd, *narchive;
+ ufile_ptr origin, norigin;
+
+ /* Put it on cmdline_object_only_file_list if it isn't an archive
+ member. */
+ switch (type)
+ {
+ default:
+ abort ();
+ case cmdline_is_bfd_enum:
+ abfd = (bfd *) data;
+ archive = abfd->my_archive;
+ if (archive)
+ break;
++ /* Fallthru */
+ case cmdline_is_file_enum:
+ cmdline_list_append (&cmdline_object_only_file_list, type, data);
+ return;
+ }
+
+ /* Put archive member on cmdline_object_only_archive_list and sort
+ the list by archive name and archive member origin. */
+ new_opt = (cmdline_union_type *) stat_alloc (sizeof (*new_opt));
+ new_opt->header.type = cmdline_is_bfd_enum;
+ new_opt->header.next = NULL;
+ new_opt->abfd.abfd = (bfd *) data;
+
+ c = cmdline_object_only_archive_list.head;
+ if (c == NULL)
+ {
+ cmdline_object_only_archive_list.head = new_opt;
+ cmdline_object_only_archive_list.tail = &new_opt->header.next;
+ return;
+ }
+
+ prev = NULL;
+ origin = abfd->origin;
+ for (; c != NULL; c = next)
+ {
+ if (c->header.type != cmdline_is_bfd_enum)
+ abort ();
+
+ next = c->header.next;
+
+ obfd = c->abfd.abfd;
+ oarchive = obfd->my_archive;
+
+ if (strcmp (archive->filename, oarchive->filename) == 0)
+ {
+ bfd_boolean after;
+
+ if (origin < obfd->origin)
+ {
+ /* Insert it before the current. */
+ new_opt->header.next = c;
+ if (prev)
+ *prev = new_opt;
+ else
+ cmdline_object_only_archive_list.head = new_opt;
+ return;
+ }
+
+ after = TRUE;
+
+ /* Check origin. */
+ while (next)
+ {
+ if (next->header.type != cmdline_is_bfd_enum)
+ abort ();
+
+ nbfd = next->abfd.abfd;
+ norigin = nbfd->origin;
+ if (origin > norigin)
+ {
+ /* Insert it after NEXT. */
+ break;
+ }
+
+ narchive = nbfd->my_archive;
+ if (strcmp (archive->filename, narchive->filename) != 0)
+ {
+ /* Insert it befor NEXT. */
+ after = FALSE;
+ break;
+ }
+
+ c = next;
+ next = next->header.next;
+ }
+
+ if (after && next)
+ {
+ c = next;
+ next = next->header.next;
+ }
+
+ if (*cmdline_object_only_archive_list.tail == c->header.next)
+ cmdline_object_only_archive_list.tail
+ = &new_opt->header.next;
+
+ prev = &c->header.next;
+ new_opt->header.next = next;
+ *prev = new_opt;
+ return;
+ }
+
+ prev = &c->header.next;
+ }
+
+ *cmdline_object_only_archive_list.tail = new_opt;
+ cmdline_object_only_archive_list.tail = &new_opt->header.next;
+}
+
+/* Get object-only input files. */
+
+static void
+cmdline_get_object_only_input_files (void)
+{
+ cmdline_union_type *c, *next;
+ bfd *abfd, *archive;
+ bfd *nbfd, *narchive;
+
+ /* Add files first. */
+ for (c = cmdline_object_only_file_list.head;
+ c != NULL; c = c->header.next)
+ switch (c->header.type)
+ {
+ default:
+ abort ();
+ case cmdline_is_file_enum:
+ lang_add_input_file (c->file.filename,
+ lang_input_file_is_file_enum, NULL);
+ break;
+ case cmdline_is_bfd_enum:
+ abfd = c->abfd.abfd;
+ if (abfd->my_archive)
+ abort ();
+ lang_add_input_file (abfd->filename,
+ lang_input_file_is_file_enum, NULL);
+ break;
+ }
+
+ /* Add archive members next. */
+ for (c = cmdline_object_only_archive_list.head; c != NULL; c = next)
+ {
+ if (c->header.type != cmdline_is_bfd_enum)
+ abort ();
+
+ next = c->header.next;
+
+ abfd = c->abfd.abfd;
+ archive = abfd->my_archive;
+
+ /* Add the first archive of the archive member group. */
+ lang_add_input_file (archive->filename,
+ lang_input_file_is_file_enum, NULL);
+
+ /* Skip the rest members in the archive member group. */
+ do
+ {
+ if (!next)
+ break;
+
+ if (next->header.type != cmdline_is_bfd_enum)
+ abort ();
+
+ next = next->header.next;
+ if (!next)
+ break;
+ nbfd = next->abfd.abfd;
+ narchive = nbfd->my_archive;
+ }
+ while (strcmp (archive->filename, narchive->filename) == 0);
+ }
+}
+
+struct cmdline_arg
+{
+ bfd *obfd;
+ asymbol **isympp;
+ int status;
+};
+
+/* Create a section in OBFD with the same
+ name and attributes as ISECTION in IBFD. */
+
+static void
+setup_section (bfd *ibfd, sec_ptr isection, void *p)
+{
+ struct cmdline_arg *arg = (struct cmdline_arg *) p;
+ bfd *obfd = arg->obfd;
+ asymbol **isympp = arg->isympp;
+ const char *name = isection->name;
+ sec_ptr osection;
+ const char *err;
+
+ /* Skip the object-only section. */
+ if (ibfd->object_only_section == isection)
+ return;
+
+ /* If we have already failed earlier on, do not keep on generating
+ complaints now. */
+ if (arg->status)
+ return;
+
+ osection = bfd_make_section_anyway_with_flags (obfd, name,
+ isection->flags);
+
+ if (osection == NULL)
+ {
+ err = _("failed to create output section");
+ goto loser;
+ }
+
+ osection->size = isection->size;
+ osection->vma = isection->vma;
+ osection->lma = isection->lma;
+ osection->alignment_power = isection->alignment_power;
+
+ /* Copy merge entity size. */
+ osection->entsize = isection->entsize;
+
+ /* This used to be mangle_section; we do here to avoid using
+ bfd_get_section_by_name since some formats allow multiple
+ sections with the same name. */
+ isection->output_section = osection;
+ isection->output_offset = 0;
+
+ if ((isection->flags & SEC_GROUP) != 0)
+ {
+ asymbol *gsym = bfd_group_signature (isection, isympp);
+
+ if (gsym != NULL)
+ {
+ gsym->flags |= BSF_KEEP;
+ if (ibfd->xvec->flavour == bfd_target_elf_flavour)
+ elf_group_id (isection) = gsym;
+ }
+ }
+
+ /* Allow the BFD backend to copy any private data it understands
+ from the input section to the output section. */
+ if (!bfd_copy_private_section_data (ibfd, isection, obfd, osection))
+ {
+ err = _("failed to copy private data");
+ goto loser;
+ }
+
+ /* All went well. */
+ return;
+
+loser:
+ arg->status = 1;
+ einfo (_("%P%F: setup_section: %s: %s\n"), err, name);
+}
+
+/* Copy the data of input section ISECTION of IBFD
+ to an output section with the same name in OBFD.
+ If stripping then don't copy any relocation info. */
+
+static void
+copy_section (bfd *ibfd, sec_ptr isection, void *p)
+{
+ struct cmdline_arg *arg = (struct cmdline_arg *) p;
+ bfd *obfd = arg->obfd;
+ asymbol **isympp = arg->isympp;
+ arelent **relpp;
+ long relcount;
+ sec_ptr osection;
+ bfd_size_type size;
+ long relsize;
+ flagword flags;
+ const char *err;
+
+ /* Skip the object-only section. */
+ if (ibfd->object_only_section == isection)
+ return;
+
+ /* If we have already failed earlier on, do not keep on generating
+ complaints now. */
+ if (arg->status)
+ return;
+
+ flags = bfd_get_section_flags (ibfd, isection);
+ if ((flags & SEC_GROUP) != 0)
+ return;
+
+ osection = isection->output_section;
+ size = bfd_get_section_size (isection);
+
+ if (size == 0 || osection == 0)
+ return;
+
+ relsize = bfd_get_reloc_upper_bound (ibfd, isection);
+
+ if (relsize < 0)
+ {
+ /* Do not complain if the target does not support relocations. */
+ if (relsize == -1
+ && bfd_get_error () == bfd_error_invalid_operation)
+ relsize = 0;
+ else
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+ }
+
+ if (relsize == 0)
+ bfd_set_reloc (obfd, osection, NULL, 0);
+ else
+ {
+ relpp = (arelent **) xmalloc (relsize);
+ relcount = bfd_canonicalize_reloc (ibfd, isection, relpp, isympp);
+ if (relcount < 0)
+ {
+ err = _("relocation count is negative");
+ goto loser;
+ }
+
+ bfd_set_reloc (obfd, osection,
+ relcount == 0 ? NULL : relpp, relcount);
+ if (relcount == 0)
+ free (relpp);
+ }
+
+ if (bfd_get_section_flags (ibfd, isection) & SEC_HAS_CONTENTS)
+ {
+ bfd_byte *memhunk = NULL;
+
+ if (!bfd_get_full_section_contents (ibfd, isection, &memhunk))
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+
+ if (!bfd_set_section_contents (obfd, osection, memhunk, 0, size))
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+ free (memhunk);
+ }
+
+ /* All went well. */
+ return;
+
+loser:
+ einfo (_("%P%F: copy_section: %s: %s\n"), err, isection->name);
+}
+/* Open the temporary bfd created in the same directory as PATH. */
+
+static bfd *
+cmdline_fopen_temp (const char *path, const char *target,
+ const char *mode)
+{
+#define template "ldXXXXXX"
+ const char *slash = strrchr (path, '/');
+ char *tmpname;
+ size_t len;
+ int fd;
+
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+ {
+ /* We could have foo/bar\\baz, or foo\\bar, or d:bar. */
+ char *bslash = strrchr (path, '\\');
+
+ if (slash == NULL || (bslash != NULL && bslash > slash))
+ slash = bslash;
+ if (slash == NULL && path[0] != '\0' && path[1] == ':')
+ slash = path + 1;
+ }
+#endif
+
+ if (slash != (char *) NULL)
+ {
+ len = slash - path;
+ tmpname = (char *) xmalloc (len + sizeof (template) + 2);
+ memcpy (tmpname, path, len);
+
+#ifdef HAVE_DOS_BASED_FILE_SYSTEM
+ /* If tmpname is "X:", appending a slash will make it a root
+ directory on drive X, which is NOT the same as the current
+ directory on drive X. */
+ if (len == 2 && tmpname[1] == ':')
+ tmpname[len++] = '.';
+#endif
+ tmpname[len++] = '/';
+ }
+ else
+ {
+ tmpname = (char *) xmalloc (sizeof (template));
+ len = 0;
+ }
+
+ memcpy (tmpname + len, template, sizeof (template));
+#undef template
+
+#ifdef HAVE_MKSTEMP
+ fd = mkstemp (tmpname);
+#else
+ tmpname = mktemp (tmpname);
+ if (tmpname == NULL)
+ return NULL;
+ fd = open (tmpname, O_RDWR | O_CREAT | O_EXCL, 0600);
+#endif
+ if (fd == -1)
+ return NULL;
+ return bfd_fopen (tmpname, target, mode, fd);
+}
+
+/* Add the object-only section. */
+
+static void
+cmdline_add_object_only_section (bfd_byte *contents, size_t size)
+{
+ bfd_vma start;
+ flagword flags;
+ enum bfd_architecture iarch;
+ unsigned int imach;
+ long symcount;
+ long symsize;
+ asymbol **isympp = NULL;
+ asymbol **osympp = NULL;
+ bfd *obfd = NULL, *ibfd;
+ const char *err;
+ struct arg
+ {
+ bfd *obfd;
+ asymbol **isympp;
+ int status;
+ } arg;
+ char **matching;
+ char *ofilename = NULL;
+ asection *sec;
+
+ ibfd = bfd_openr (output_filename, output_target);
+ if (!ibfd)
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+
+ if (!bfd_check_format_matches (ibfd, bfd_object, &matching))
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+
+ obfd = cmdline_fopen_temp (output_filename, output_target, "w");
+ if (!obfd)
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+ /* To be used after bfd_close (). */
+ ofilename = xstrdup (bfd_get_filename (obfd));
+
+ if (!bfd_set_format (obfd, bfd_object))
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+
+ /* Copy the start address, flags and architecture of input file to
+ output file. */
+ flags = bfd_get_file_flags (ibfd);
+ start = bfd_get_start_address (ibfd);
+ iarch = bfd_get_arch (ibfd);
+ imach = bfd_get_mach (ibfd);
+ if (!bfd_set_start_address (obfd, start)
+ || !bfd_set_file_flags (obfd, flags)
+ || !bfd_set_arch_mach (obfd, iarch, imach))
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+
+ symsize = bfd_get_symtab_upper_bound (ibfd);
+ if (symsize < 0)
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+
+ isympp = (asymbol **) xmalloc (symsize);
+ symcount = bfd_canonicalize_symtab (ibfd, isympp);
+ if (symcount < 0)
+ {
+ err = bfd_errmsg (bfd_get_error ());
+ goto loser;
+ }
+
+ arg.obfd = obfd;
+ arg.isympp = isympp;
+ arg.status = 0;
+
+ /* BFD mandates that all output sections be created and sizes set before
+ any output is done. Thus, we traverse all sections multiple times. */
+ bfd_map_over_sections (ibfd, setup_section, &arg);
+
+ if (arg.status)
+ {
+ err = _("error setting up sections");
+ goto loser;
+ }
+
+ /* Allow the BFD backend to copy any private data it understands
+ from the input section to the output section. */
+ if (! bfd_copy_private_header_data (ibfd, obfd))
+ {
+ err = _("error copying private header data");
+ goto loser;
+ }
+
+ /* Create the object-only section. */
+ sec = bfd_make_section_with_flags (obfd,
+ GNU_OBJECT_ONLY_SECTION_NAME,
+ (SEC_HAS_CONTENTS
+ | SEC_READONLY
+ | SEC_DATA
+ | SEC_LINKER_CREATED));
+ if (sec == NULL)
+ {
+ err = _("can't create object-only section");
+ goto loser;
+ }
+
+ if (! bfd_set_section_size (obfd, sec, size))
+ {
+ err = _("can't set object-only section size");
+ goto loser;
+ }
+
+ if (ibfd->object_only_section)
+ {
+ /* Filter out the object-only section symbol. */
+ long src_count = 0, dst_count = 0;
+ asymbol **from, **to;
+
+ osympp = (asymbol **) xmalloc (symcount * sizeof (asymbol *));
+ from = isympp;
+ to = osympp;
+ for (; src_count < symcount; src_count++)
+ {
+ asymbol *sym = from[src_count];
+ if (bfd_get_section (sym) != ibfd->object_only_section)
+ to[dst_count++] = sym;
+ }
+ to[dst_count] = NULL;
+ symcount = dst_count;
+ bfd_set_symtab (obfd, osympp, symcount);
+ }
+ else
+ bfd_set_symtab (obfd, isympp, symcount);
+
+ /* This has to happen after the symbol table has been set. */
+ bfd_map_over_sections (ibfd, copy_section, &arg);
+
+ if (arg.status)
+ {
+ err = _("error copying sections");
+ goto loser;
+ }
+
+ /* Copy the object-only section to the output. */
+ if (! bfd_set_section_contents (obfd, sec, contents, 0, size))
+ {
+ err = _("error adding object-only section");
+ goto loser;
+ }
+
+ /* Allow the BFD backend to copy any private data it understands
+ from the input BFD to the output BFD. This is done last to
+ permit the routine to look at the filtered symbol table, which is
+ important for the ECOFF code at least. */
+ if (! bfd_copy_private_bfd_data (ibfd, obfd))
+ {
+ err = _("error copying private BFD data");
+ goto loser;
+ }
+
+ if (!bfd_close (obfd))
+ {
+ unlink (ofilename);
+ einfo (_("%P%F: failed to finish output with object-only section\n"));
+ }
+
+ /* Must be freed after bfd_close (). */
+ free (isympp);
+ if (osympp)
+ free (osympp);
+
+ if (rename (ofilename, output_filename))
+ {
+ unlink (ofilename);
+ einfo (_("%P%F: failed to rename output with object-only section\n"));
+ }
+
+ free (ofilename);
+ return;
+
+loser:
+ if (isympp)
+ free (isympp);
+ if (osympp)
+ free (osympp);
+ if (obfd)
+ bfd_close (obfd);
+ if (ofilename)
+ unlink (ofilename);
+ einfo (_("%P%F: failed to add object-only section: %s\n"), err);
+}
+
+/* Emit the final output with object-only section. */
+
+void
+cmdline_emit_object_only_section (void)
+{
+ const char *saved_output_filename = output_filename;
+ int fd;
+ size_t size, off;
+ bfd_byte *contents;
+ struct stat st;
+
+ /* Get a temporary object-only file. */
+ output_filename = make_temp_file (".obj-only.o");
+
+ had_output_filename = FALSE;
+ link_info.input_bfds = NULL;
+ link_info.input_bfds_tail = &link_info.input_bfds;
+
+ lang_init (TRUE);
+ ldexp_init (TRUE);
+
+ ld_parse_linker_script ();
+
+ /* Set up the object-only output. */
+ lang_final ();
+
+ /* Open the object-only file for output. */
+ lang_for_each_statement (ldlang_open_output);
+
+ ldemul_create_output_section_statements ();
+
+ if (!bfd_section_already_linked_table_init ())
+ einfo (_("%P%F: Failed to create hash table\n"));
+
+ /* Call cmdline_on_object_only_archive_list_p to check which member
+ should be loaded. */
+ input_flags.whole_archive = TRUE;
+
+ /* Set it to avoid adding more to cmdline lists. */
+ link_info.emitting_gnu_object_only = TRUE;
+
+ /* Get object-only input files. */
+ cmdline_get_object_only_input_files ();
+
+ /* Open object-only input files. */
+ open_input_bfds (statement_list.head, FALSE);
+
+ ldemul_after_open ();
+
+ bfd_section_already_linked_table_free ();
+
+ /* Make sure that we're not mixing architectures. We call this
+ after all the input files have been opened, but before we do any
+ other processing, so that any operations merge_private_bfd_data
+ does on the output file will be known during the rest of the
+ link. */
+ lang_check ();
+
+ /* Size up the common data. */
+ lang_common ();
+
+ /* Update wild statements. */
+ update_wild_statements (statement_list.head);
+
+ /* Run through the contours of the script and attach input sections
+ to the correct output sections. */
+ map_input_to_output_sections (statement_list.head, NULL, NULL);
+
+ /* Find any sections not attached explicitly and handle them. */
+ lang_place_orphans ();
+
+ /* Do anything special before sizing sections. This is where ELF
+ and other back-ends size dynamic sections. */
+ ldemul_before_allocation ();
+
+ /* Size up the sections. */
+ lang_size_sections (NULL, ! RELAXATION_ENABLED);
+
+ /* See if anything special should be done now we know how big
+ everything is. This is where relaxation is done. */
+ ldemul_after_allocation ();
+
+ ldemul_finish ();
+
+ /* Make sure that the section addresses make sense. */
+ if (command_line.check_section_addresses)
+ lang_check_section_addresses ();
+
+ lang_end ();
+
+ ldwrite ();
+
+ ldexp_finish (TRUE);
+ lang_finish ();
+
+ if (! bfd_close (link_info.output_bfd))
+ einfo (_("%P%F:%s: final close failed on object-only output: %E\n"),
+ output_filename);
+
+ /* Read in the object-only file. */
+ fd = open (output_filename, O_RDONLY | O_BINARY);
+ if (fd < 0)
+ {
+ bfd_set_error (bfd_error_system_call);
+ einfo (_("%P%F:%s: cannot open object-only output: %E"),
+ output_filename);
+ }
+
+ /* Get the object-only file size. */
+ if (fstat (fd, &st) != 0)
+ {
+ bfd_set_error (bfd_error_system_call);
+ einfo (_("%P%F:%s: cannot stat object-only output: %E"),
+ output_filename);
+ }
+
+ size = st.st_size;
+ off = 0;
+ contents = (bfd_byte *) xmalloc (size);
+ while (off != size)
+ {
+ ssize_t got;
+
+ got = read (fd, contents + off, size - off);
+ if (got < 0)
+ {
+ bfd_set_error (bfd_error_system_call);
+ einfo (_("%P%F:%s: read failed on object-only output: %E"),
+ output_filename);
+ }
+
+ off += got;
+ }
+
+ close (fd);
+
+ /* Remove the temporary object-only file. */
+ unlink (output_filename);
+
+ output_filename = saved_output_filename;
+
+ cmdline_add_object_only_section (contents, size);
+
+ free (contents);
+}
+
+/* Extract the object-only section. */
+
+static const char *
+cmdline_extract_object_only_section (bfd *abfd)
+{
+ const char *name = bfd_extract_object_only_section (abfd);
+
+ if (name == NULL)
+ einfo (_("%P%F: cannot extract object-only section from %B: %E"),
+ abfd);
+
+ /* It should be removed after it is done. */
+ cmdline_list_append (&cmdline_temp_object_only_list,
+ cmdline_is_file_enum, (void *) name);
+
+ return name;
+}
+
+/* Check and handle the object-only section. */
+
+void
+cmdline_check_object_only_section (bfd *abfd, bfd_boolean lto)
+{
+ const char *filename;
+
+ if (link_info.emitting_gnu_object_only
+ || abfd->format != bfd_object)
+ return;
+
+ if (lto)
+ {
+ /* For LTO link, we only need to extract object-only section
+ from the mixed object, add it to input, and put it on LTO
+ claimed output. */
+ switch (abfd->lto_type)
+ {
+ default:
+ abort ();
+ case lto_mixed_object:
+ filename = cmdline_extract_object_only_section (abfd);
+ lang_add_input_file (filename,
+ lang_input_file_is_file_enum, NULL);
+ break;
+ case lto_non_ir_object:
+ case lto_ir_object:
+ break;
+ }
+ }
+ else if (bfd_link_relocatable (&link_info))
+ {
+ /* For non-LTO relocatable link, we need to append non-IR object
+ file and the object file in object-only section to the object
+ only list. */
+ switch (abfd->lto_type)
+ {
+ default:
+ abort ();
+ case lto_mixed_object:
+ filename = cmdline_extract_object_only_section (abfd);
+ cmdline_object_only_list_append (cmdline_is_file_enum,
+ (void *) filename);
+ break;
+ case lto_non_ir_object:
+ cmdline_object_only_list_append (cmdline_is_bfd_enum, abfd);
+ break;
+ case lto_ir_object:
+ break;
+ }
+ }
+}
+
+/* Remove temporary object-only files. */
+
+void
+cmdline_remove_object_only_files (void)
+{
+ cmdline_union_type *c;
+
+#ifdef ENABLE_PLUGINS
+ if (plugin_save_temps)
+ return;
+#endif
+
+ c = cmdline_temp_object_only_list.head;
+ for (; c != NULL; c = c->header.next)
+ switch (c->header.type)
+ {
+ default:
+ abort ();
+ case cmdline_is_file_enum:
+ unlink (c->file.filename);
+ break;
+ }
+}
link_info.keep_memory = TRUE;
link_info.combreloc = TRUE;
link_info.strip_discarded = TRUE;
- link_info.emit_hash = TRUE;
+ link_info.emit_hash = DEFAULT_EMIT_SYSV_HASH;
+ link_info.emit_gnu_hash = DEFAULT_EMIT_GNU_HASH;
+ link_info.emit_secondary = TRUE;
link_info.callbacks = &link_callbacks;
link_info.input_bfds_tail = &link_info.input_bfds;
/* SVR4 linkers seem to set DT_INIT and DT_FINI based on magic _init
--- /dev/null
--- /dev/null
++ .text
++ .globl get_foo
++get_foo:
++ movl %fs:foo@GPOFF, %eax
++ ret
++
++ .globl get_foo_gpoff
++get_foo_gpoff:
++ leal foo@GPOFF, %eax
++ ret
++
++ .data
++ .globl foo_gpoff
++foo_gpoff:
++ .long foo@GPOFF
++
++ .data
++ .globl foo
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++
++extern int foo;
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++/* Structure passed to 'set_thread_area' syscall. */
++struct user_desc
++{
++ unsigned int entry_number;
++ unsigned long int base_addr;
++ unsigned int limit;
++ unsigned int seg_32bit:1;
++ unsigned int contents:2;
++ unsigned int read_exec_only:1;
++ unsigned int limit_in_pages:1;
++ unsigned int seg_not_present:1;
++ unsigned int useable:1;
++ unsigned int empty:25;
++};
++
++/* Initializing bit fields is slow. We speed it up by using a union. */
++union user_desc_init
++{
++ struct user_desc desc;
++ unsigned int vals[4];
++};
++
++int
++setup_gp (void *p)
++{
++ union user_desc_init segdescr;
++ int result;
++
++ /* Let the kernel pick a value for the 'entry_number' field. */
++ segdescr.vals[0] = -1;
++ /* The 'base_addr' field. */
++ segdescr.vals[1] = (unsigned long int) p;
++ /* The 'limit' field. We use 4GB which is 0xfffff pages. */
++ segdescr.vals[2] = 0xfffff;
++ /* Collapsed value of the bitfield:
++ .seg_32bit = 1
++ .contents = 0
++ .read_exec_only = 0
++ .limit_in_pages = 1
++ .seg_not_present = 0
++ .useable = 1 */
++ segdescr.vals[3] = 0x51;
++ result = syscall (SYS_set_thread_area, &segdescr.desc);
++ if (result == 0)
++ /* We know the index in the GDT, now load the segment register.
++ The use of the GDT is described by the value 3 in the lower
++ three bits of the segment descriptor value.
++ Note that we have to do this even if the numeric value of
++ the descriptor does not change. Loading the segment register
++ causes the segment information from the GDT to be loaded
++ which is necessary since we have changed it. */
++ asm ("movw %w0, %%fs" :: "q" (segdescr.desc.entry_number * 8 + 3));
++
++ return result;
++}
++
++int
++main ()
++{
++ if (setup_gp (&__gp) == 0
++ && foo == 0x12345678
++ && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++ && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++ && get_foo () == 0x12345678)
++ printf ("PASS\n");
++ return 0;
++}
--- /dev/null
--- /dev/null
++ .text
++ .globl get_foo
++get_foo:
++ movl %fs:foo@GPOFF, %eax
++ ret
++
++ .globl get_foo_gpoff
++get_foo_gpoff:
++ leal foo@GPOFF, %eax
++ ret
++
++ .data
++ .globl foo_gpoff
++foo_gpoff:
++ .long foo@GPOFF
++
++ .data
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++/* Structure passed to 'set_thread_area' syscall. */
++struct user_desc
++{
++ unsigned int entry_number;
++ unsigned long int base_addr;
++ unsigned int limit;
++ unsigned int seg_32bit:1;
++ unsigned int contents:2;
++ unsigned int read_exec_only:1;
++ unsigned int limit_in_pages:1;
++ unsigned int seg_not_present:1;
++ unsigned int useable:1;
++ unsigned int empty:25;
++};
++
++/* Initializing bit fields is slow. We speed it up by using a union. */
++union user_desc_init
++{
++ struct user_desc desc;
++ unsigned int vals[4];
++};
++
++int
++setup_gp (void *p)
++{
++ union user_desc_init segdescr;
++ int result;
++
++ /* Let the kernel pick a value for the 'entry_number' field. */
++ segdescr.vals[0] = -1;
++ /* The 'base_addr' field. */
++ segdescr.vals[1] = (unsigned long int) p;
++ /* The 'limit' field. We use 4GB which is 0xfffff pages. */
++ segdescr.vals[2] = 0xfffff;
++ /* Collapsed value of the bitfield:
++ .seg_32bit = 1
++ .contents = 0
++ .read_exec_only = 0
++ .limit_in_pages = 1
++ .seg_not_present = 0
++ .useable = 1 */
++ segdescr.vals[3] = 0x51;
++ result = syscall (SYS_set_thread_area, &segdescr.desc);
++ if (result == 0)
++ /* We know the index in the GDT, now load the segment register.
++ The use of the GDT is described by the value 3 in the lower
++ three bits of the segment descriptor value.
++ Note that we have to do this even if the numeric value of
++ the descriptor does not change. Loading the segment register
++ causes the segment information from the GDT to be loaded
++ which is necessary since we have changed it. */
++ asm ("movw %w0, %%fs" :: "q" (segdescr.desc.entry_number * 8 + 3));
++
++ return result;
++}
++
++
++int
++main ()
++{
++ if (setup_gp (&__gp) == 0
++ && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++ && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++ && get_foo () == 0x12345678)
++ printf ("PASS\n");
++ return 0;
++}
--- /dev/null
--- /dev/null
++#as: --32
++#ld: -melf_i386
++#error: undefined reference to `foo'
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %fs:foo@GPOFF, %eax
--- /dev/null
--- /dev/null
++#as: --32
++#ld: -melf_i386
++#error: GPOFF relocation at 0x2 in section `\.text' must be against symbol defined in GP section `\.rodata'
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %fs:foo@GPOFF, %eax
++
++ .data
++ .globl bar_gpoff
++bar_gpoff:
++ .long bar@GPOFF
++
++foo:
++ .long 0x12345678
++
++ .section .rodata,"a",@progbits
++bar:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#as: --32
++#ld: -melf_i386
++#error: symbol `bar' with GPOFF relocation defined in .*\.o\(\.rodata\) isn't in GP section `\.data'
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %fs:foo@GPOFF, %eax
++
++ .data
++ .globl bar_gpoff
++bar_gpoff:
++ .long bar@GPOFF
++
++ .globl foo
++foo:
++ .long 0x12345678
++
++ .section .rodata,"a",@progbits
++ .globl bar
++bar:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#as: --32
++#ld: -melf_i386 --gc-sections
++#objdump: -dw
++
++.*: +file format .*
++
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+: c3 ret
++#pass
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ ret
++
++ .section .text.bar,"ax",@progbits
++ .globl bar
++bar:
++ movl %fs:foo@GPOFF, %eax
--- /dev/null
--- /dev/null
++#as: --32
++#ld: -melf_i386
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+64 a1 fe ff ff ff[ \t]+mov[ \t]+%fs:0xfffffffe,%eax
++#pass
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %fs:foo@GPOFF, %eax
++
++ .data
++ .globl foo
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %fs:foo@GPOFF, %eax
++ movl __gp@GOT(%ebx), %eax
++
++ .data
++ .globl foo
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++SECTIONS
++{
++ /* Read-only sections, merged into text segment: */
++ . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
++ .hash : { *(.hash) }
++ .gnu.hash : { *(.gnu.hash) }
++ .dynsym : { *(.dynsym) }
++ .dynstr : { *(.dynstr) }
++ .init : { *(.init) }
++ .text : { *(.text) }
++ .fini : { *(.fini) }
++ .rodata : { *(.rodata) }
++ . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
++ .tdata : { *(.tdata) }
++ .tbss : { *(.tbss) }
++ .init_array : { *(.init_array) }
++ .fini_array : { *(.fini_array) }
++ .jcr : { *(.jcr) }
++ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
++ .dynamic : { *(.dynamic) }
++ .bar : { *(.bar) }
++ . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
++ .got.plt : { *(.got.plt) }
++ .data :
++ {
++ __gp = .;
++ *(.data)
++ }
++ __bss_start = .;
++ .bss :
++ {
++ *(.bss)
++ . = ALIGN(. != 0 ? 64 / 8 : 1);
++ }
++ . = ALIGN(64 / 8);
++ _end = .; PROVIDE (end = .);
++ . = DATA_SEGMENT_END (.);
++ /DISCARD/ : { *(.*) }
++}
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --32 -mrelax-relocations=yes
++#ld: -melf_i386
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+64 a1 fe ff ff ff[ \t]+mov[ \t]+%fs:0xfffffffe,%eax
++ +[a-f0-9]+:[ \t]+c7 c0 [a-f0-9 \t]+mov[ \t]+\$0x[a-f0-9]+,%eax
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --32 -mrelax-relocations=yes
++#ld: -melf_i386 -pie
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+64 a1 fe ff ff ff[ \t]+mov[ \t]+%fs:0xfffffffe,%eax
++ +[a-f0-9]+:[ \t]+8d 83 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%ebx\),%eax
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --32 -mrelax-relocations=yes
++#ld: -melf_i386 -shared
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+64 a1 fe ff ff ff[ \t]+mov[ \t]+%fs:0xfffffffe,%eax
++ +[a-f0-9]+:[ \t]+8d 83 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%ebx\),%eax
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --32 -mrelax-relocations=yes
++#ld: -melf_i386 -T gpoff-8.t
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+64 a1 00 00 00 00[ \t]+mov[ \t]+%fs:0x0,%eax
++ +[a-f0-9]+:[ \t]+c7 c0 [a-f0-9 \t]+mov[ \t]+\$0x[a-f0-9]+,%eax
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --32 -mrelax-relocations=yes
++#ld: -melf_i386 -T gpoff-8.t -pie
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+64 a1 00 00 00 00[ \t]+mov[ \t]+%fs:0x0,%eax
++ +[a-f0-9]+:[ \t]+8d 83 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%ebx\),%eax
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --32 -mrelax-relocations=yes
++#ld: -melf_i386 -T gpoff-8.t -shared
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+64 a1 00 00 00 00[ \t]+mov[ \t]+%fs:0x0,%eax
++ +[a-f0-9]+:[ \t]+8d 83 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%ebx\),%eax
++#pass
run_dump_test "pcrel8"
run_dump_test "pcrel16"
run_dump_test "pcrel16abs"
++run_dump_test "gpoff-3"
++run_dump_test "gpoff-4"
++run_dump_test "gpoff-5"
++run_dump_test "gpoff-6"
++run_dump_test "gpoff-7"
++run_dump_test "gpoff-8a"
++run_dump_test "gpoff-8b"
++run_dump_test "gpoff-8c"
++run_dump_test "gpoff-8d"
++run_dump_test "gpoff-8e"
++run_dump_test "gpoff-8f"
run_dump_test "alloc"
run_dump_test "warn1"
run_dump_test "tlsgd2"
"$NOPIE_CFLAGS" \
] \
[list \
- "Run weakundef1 with PIE" \
- "-pie" \
+ "Run pr22001-1" \
+ "$NOPIE_LDFLAGS -Wl,-z,nocopyreloc,--no-as-needed tmpdir/pr22001-1.so" \
"" \
- { weakundef1.c } \
- "weakundef1pie" \
+ { pr22001-1b.c } \
+ "pr22001-1" \
+ "pass.out" \
+ "$NOPIE_CFLAGS" \
+ ] \
+ [list \
+ "Run pr22001-1 (PIE 1)" \
+ "$NOPIE_LDFLAGS -Wl,-z,nocopyreloc,--no-as-needed tmpdir/pr22001-1.so" \
+ "" \
+ { pr22001-1c.S } \
+ "pr22001-1-pie-1" \
+ "pass.out" \
+ ] \
+ [list \
+ "Run pr22001-1 (PIE 2)" \
+ "-pie -Wl,-z,nocopyreloc,--no-as-needed tmpdir/pr22001-1.so" \
+ "" \
+ { pr22001-1c.S } \
+ "pr22001-1-pie-2" \
+ "pass.out" \
+ ] \
+ [list \
+ "Run pr22001-1 (PIC 1)" \
+ "$NOPIE_LDFLAGS -Wl,-z,nocopyreloc,--no-as-needed tmpdir/pr22001-1.so" \
+ "" \
+ { pr22001-1b.c } \
+ "pr22001-1-pic-1" \
+ "pass.out" \
+ "-fPIC" \
+ ] \
+ [list \
+ "Run pr22001-1 (PIC 2)" \
+ "-pie -Wl,-z,nocopyreloc,--no-as-needed tmpdir/pr22001-1.so" \
+ "" \
+ { pr22001-1b.c } \
+ "pr22001-1-pic-2" \
+ "pass.out" \
+ "-fPIC" \
+ ] \
+ [list \
+ "Run pr21997-1" \
+ "$NOPIE_LDFLAGS -Wl,--no-as-needed tmpdir/pr21997-1.so" \
+ "" \
+ { pr21997-1b.c } \
+ "pr21997-1" \
+ "pass.out" \
+ "$NOPIE_CFLAGS" \
+ ] \
+ [list \
+ "Run pr21997-1 (PIC 1)" \
+ "$NOPIE_LDFLAGS -Wl,--no-as-needed tmpdir/pr21997-1.so" \
+ "" \
+ { pr21997-1b.c } \
+ "pr21997-1-pic-1" \
+ "pass.out" \
+ "-fPIC" \
+ ] \
+ [list \
+ "Run pr21997-1 (PIC 2)" \
+ "-pie -Wl,--no-as-needed tmpdir/pr21997-1.so" \
+ "" \
+ { pr21997-1b.c } \
+ "pr21997-1-pic-2" \
+ "pass.out" \
+ "-fPIC" \
+ ] \
+ [list \
+ "Run pr21997-1 (PIE 1)" \
+ "$NOPIE_LDFLAGS -Wl,--no-as-needed tmpdir/pr21997-1.so" \
+ "" \
+ { pr21997-1c.S } \
+ "pr21997-1-pie-1" \
+ "pass.out" \
+ ] \
+ [list \
+ "Run pr21997-1 (PIE 2)" \
+ "-pie -Wl,--no-as-needed tmpdir/pr21997-1.so" \
+ "" \
+ { pr21997-1c.S } \
+ "pr21997-1-pie-2" \
"pass.out" \
- "-fPIE" \
] \
]
+
+ if { [at_least_gcc_version 5 0] } {
+ run_ld_link_exec_tests [list \
+ [list \
+ "Run weakundef1 with PIE" \
+ "-pie" \
+ "" \
+ { weakundef1.c } \
+ "weakundef1pie" \
+ "pass.out" \
+ "-fPIE" \
+ ] \
+ ]
+ }
++
++ if { [istarget "i?86-*-linux*"] } {
++ run_ld_link_exec_tests [list \
++ [list \
++ "Run GPOFF 1" \
++ "" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1" "pass.out" \
++ ] \
++ [list \
++ "Run GPOFF 1 (PIE)" \
++ "-pie" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1-pie" "pass.out" "-fPIE" \
++ ] \
++ [list \
++ "Run GPOFF 1 (PIC)" \
++ "-pie" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1-pic" "pass.out" "-fPIC" \
++ ] \
++ [list \
++ "Run GPOFF 1 (static)" \
++ "-static" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1-static" "pass.out" \
++ ] \
++ [list \
++ "Run GPOFF 2" \
++ "" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2" "pass.out" \
++ ] \
++ [list \
++ "Run GPOFF 2 (PIE)" \
++ "-pie" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2-pie" "pass.out" "-fPIE" \
++ ] \
++ [list \
++ "Run GPOFF 2 (PIC)" \
++ "-pie" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2-pic" "pass.out" "-fPIC" \
++ ] \
++ [list \
++ "Run GPOFF 2 (static)" \
++ "-static" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2-static" "pass.out" \
++ ] \
++ ]
++ }
}
if { !([istarget "i?86-*-linux*"]
--- /dev/null
--- /dev/null
++ .text
++ .globl get_foo
++get_foo:
++ movl %gs:foo@GPOFF, %eax
++ ret
++
++ .globl get_foo_gpoff
++get_foo_gpoff:
++ leal foo@GPOFF, %eax
++ ret
++
++ .data
++ .globl foo_gpoff
++foo_gpoff:
++ .long foo@GPOFF
++
++ .data
++ .globl foo
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++#include <asm/prctl.h>
++
++extern int foo;
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++int
++setup_gp (void *p)
++{
++ return syscall (SYS_arch_prctl, ARCH_SET_GS, p);
++}
++
++int
++main ()
++{
++ if (setup_gp (&__gp) == 0
++ && foo == 0x12345678
++ && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++ && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++ && get_foo () == 0x12345678)
++ printf ("PASS\n");
++ return 0;
++}
--- /dev/null
--- /dev/null
++ .text
++ .globl get_foo
++get_foo:
++ movl %gs:foo@GPOFF, %eax
++ ret
++
++ .globl get_foo_gpoff
++get_foo_gpoff:
++ leal foo@GPOFF, %eax
++ ret
++
++ .data
++ .globl foo_gpoff
++foo_gpoff:
++ .long foo@GPOFF
++
++ .data
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++#include <asm/prctl.h>
++
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++int
++setup_gp (void *p)
++{
++ return syscall (SYS_arch_prctl, ARCH_SET_GS, p);
++}
++
++int
++main ()
++{
++ if (setup_gp (&__gp) == 0
++ && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++ && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++ && get_foo () == 0x12345678)
++ printf ("PASS\n");
++ return 0;
++}
--- /dev/null
--- /dev/null
++#as: --64
++#ld: -melf_x86_64
++#error: undefined reference to `foo'
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %gs:foo@GPOFF, %eax
--- /dev/null
--- /dev/null
++#as: --64
++#ld: -melf_x86_64
++#error: GPOFF relocation at 0x4 in section `\.text' must be against symbol defined in GP section `\.rodata'
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %gs:foo@GPOFF, %eax
++
++ .data
++ .globl bar_gpoff
++bar_gpoff:
++ .long bar@GPOFF
++
++foo:
++ .long 0x12345678
++
++ .section .rodata,"a",@progbits
++bar:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#as: --64
++#ld: -melf_x86_64
++#error: symbol `bar' with GPOFF relocation defined in .*\.o\(\.rodata\) isn't in GP section `\.data'
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %gs:foo@GPOFF, %eax
++
++ .data
++ .globl bar_gpoff
++bar_gpoff:
++ .long bar@GPOFF
++
++ .globl foo
++foo:
++ .long 0x12345678
++
++ .section .rodata,"a",@progbits
++ .globl bar
++bar:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++#as: --64
++#ld: -melf_x86_64 --gc-sections
++#objdump: -dw
++
++.*: +file format .*
++
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+: c3 retq
++#pass
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ ret
++
++ .section .text.bar,"ax",@progbits
++ .globl bar
++bar:
++ movl %gs:foo@GPOFF, %eax
--- /dev/null
--- /dev/null
++#as: --64
++#ld: -melf_x86_64
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+65 8b 04 25 fe ff ff ff[ \t]+mov[ \t]+%gs:0xfffffffffffffffe,%eax
++#pass
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %gs:foo@GPOFF, %eax
++
++ .data
++ .globl foo
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++ .text
++ .globl _start
++_start:
++ movl %gs:foo@GPOFF, %eax
++ movq __gp@GOTPCREL(%rip), %rax
++
++ .data
++ .globl foo
++foo:
++ .long 0x12345678
--- /dev/null
--- /dev/null
++SECTIONS
++{
++ /* Read-only sections, merged into text segment: */
++ . = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
++ .hash : { *(.hash) }
++ .gnu.hash : { *(.gnu.hash) }
++ .dynsym : { *(.dynsym) }
++ .dynstr : { *(.dynstr) }
++ .init : { *(.init) }
++ .text : { *(.text) }
++ .fini : { *(.fini) }
++ .rodata : { *(.rodata) }
++ . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
++ .tdata : { *(.tdata) }
++ .tbss : { *(.tbss) }
++ .init_array : { *(.init_array) }
++ .fini_array : { *(.fini_array) }
++ .jcr : { *(.jcr) }
++ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*) }
++ .dynamic : { *(.dynamic) }
++ .bar : { *(.bar) }
++ . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
++ .got.plt : { *(.got.plt) }
++ .data :
++ {
++ __gp = .;
++ *(.data)
++ }
++ __bss_start = .;
++ .bss :
++ {
++ *(.bss)
++ . = ALIGN(. != 0 ? 64 / 8 : 1);
++ }
++ . = ALIGN(64 / 8);
++ _end = .; PROVIDE (end = .);
++ . = DATA_SEGMENT_END (.);
++ /DISCARD/ : { *(.*) }
++}
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --64 -mrelax-relocations=yes
++#ld: -melf_x86_64
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+65 8b 04 25 fe ff ff ff[ \t]+mov[ \t]+%gs:0xfffffffffffffffe,%eax
++ +[a-f0-9]+:[ \t]+48 c7 c0 [a-f0-9 \t]+mov[ \t]+\$0x[a-f0-9]+,%rax
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --64 -mrelax-relocations=yes
++#ld: -melf_x86_64 -pie
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+65 8b 04 25 fe ff ff ff[ \t]+mov[ \t]+%gs:0xfffffffffffffffe,%eax
++ +[a-f0-9]+:[ \t]+48 8d 05 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%rip\),%rax[ \t]+# [a-f0-9]+ <__gp>
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --64 -mrelax-relocations=yes
++#ld: -melf_x86_64 -shared
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+65 8b 04 25 fe ff ff ff[ \t]+mov[ \t]+%gs:0xfffffffffffffffe,%eax
++ +[a-f0-9]+:[ \t]+48 8d 05 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%rip\),%rax[ \t]+# [a-f0-9]+ <__gp>
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --64 -mrelax-relocations=yes
++#ld: -melf_x86_64 -T gpoff-8.t
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+65 8b 04 25 00 00 00 00[ \t]+mov[ \t]+%gs:0x0,%eax
++ +[a-f0-9]+:[ \t]+48 c7 c0 [a-f0-9 \t]+mov[ \t]+\$0x[a-f0-9]+,%rax
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --64 -mrelax-relocations=yes
++#ld: -melf_x86_64 -T gpoff-8.t -pie
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+65 8b 04 25 00 00 00 00[ \t]+mov[ \t]+%gs:0x0,%eax
++ +[a-f0-9]+:[ \t]+48 8d 05 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%rip\),%rax[ \t]+# [a-f0-9]+ <foo>
++#pass
--- /dev/null
--- /dev/null
++#source: gpoff-8.s
++#as: --64 -mrelax-relocations=yes
++#ld: -melf_x86_64 -T gpoff-8.t -shared
++#objdump: -dw --sym
++
++.*: +file format .*
++
++SYMBOL TABLE:
++#...
++[a-f0-9]+ l .data 0+ __gp
++#...
++
++Disassembly of section .text:
++
++0+[a-f0-9]+ <_start>:
++ +[a-f0-9]+:[ \t]+65 8b 04 25 00 00 00 00[ \t]+mov[ \t]+%gs:0x0,%eax
++ +[a-f0-9]+:[ \t]+48 8d 05 [a-f0-9 \t]+lea[ \t]+0x[a-f0-9]+\(%rip\),%rax[ \t]+# [a-f0-9]+ <foo>
++#pass
run_dump_test "abs"
run_dump_test "abs-l1om"
+ run_dump_test "apic"
run_dump_test "pcrel8"
run_dump_test "pcrel16"
++run_dump_test "gpoff-3"
++run_dump_test "gpoff-4"
++run_dump_test "gpoff-5"
++run_dump_test "gpoff-6"
++run_dump_test "gpoff-7"
++run_dump_test "gpoff-8a"
++run_dump_test "gpoff-8b"
++run_dump_test "gpoff-8c"
++run_dump_test "gpoff-8d"
++run_dump_test "gpoff-8e"
++run_dump_test "gpoff-8f"
run_dump_test "tlsgd2"
run_dump_test "tlsgd3"
run_dump_test "tlsgd12"
}
}
++ if { [istarget "x86_64-*-linux*"] } {
++ run_ld_link_exec_tests [list \
++ [list \
++ "Run GPOFF 1" \
++ "" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1" "pass.out" \
++ ] \
++ [list \
++ "Run GPOFF 1 (PIE)" \
++ "-pie" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1-pie" "pass.out" "-fPIE" \
++ ] \
++ [list \
++ "Run GPOFF 1 (PIC)" \
++ "-pie" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1-pic" "pass.out" "-fPIC" \
++ ] \
++ [list \
++ "Run GPOFF 1 (static)" \
++ "-static" \
++ "" \
++ {gpoff-1a.S gpoff-1b.c} \
++ "gpoff-1-static" "pass.out" \
++ ] \
++ [list \
++ "Run GPOFF 2" \
++ "" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2" "pass.out" \
++ ] \
++ [list \
++ "Run GPOFF 2 (PIE)" \
++ "-pie" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2-pie" "pass.out" "-fPIE" \
++ ] \
++ [list \
++ "Run GPOFF 2 (PIC)" \
++ "-pie" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2-pic" "pass.out" "-fPIC" \
++ ] \
++ [list \
++ "Run GPOFF 2 (static)" \
++ "-static" \
++ "" \
++ {gpoff-2a.S gpoff-2b.c} \
++ "gpoff-2-static" "pass.out" \
++ ] \
++ ]
++ }
++
undefined_weak "$NOPIE_CFLAGS" "$NOPIE_LDFLAGS"
undefined_weak "-fPIE" ""
undefined_weak "-fPIE" "-pie"