From: H.J. Lu Date: Fri, 19 Jan 2018 11:23:56 +0000 (-0800) Subject: Merge remote-tracking branch 'origin/users/hjl/linux/master' into users/hjl/linux... X-Git-Tag: users/hjl/linux/release/2.29.51.0.1^0 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=refs%2Fheads%2Fusers%2Fhjl%2Flinux%2Fapplied;p=thirdparty%2Fbinutils-gdb.git Merge remote-tracking branch 'origin/users/hjl/linux/master' into users/hjl/linux/applied --- 4ba8500d63991518aefef86474576de565e00237 diff --cc bfd/bfd-in2.h index e481509c6b8,f4b3720b4b1..06bc2331b53 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@@ -6638,12 -6648,8 +6654,12 @@@ typedef struct bfd_symbo /* 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 diff --cc bfd/elf.c index 6d596576f2c,90aef091329..e3ec778de65 --- a/bfd/elf.c +++ b/bfd/elf.c @@@ -2603,16 -2617,15 +2617,16 @@@ static const struct bfd_elf_special_sec 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[] = diff --cc bfd/elf32-i386.c index df16775d54a,1f380dbdc3d..b958908ff7a --- a/bfd/elf32-i386.c +++ b/bfd/elf32-i386.c @@@ -150,9 -143,9 +143,12 @@@ static reloc_howto_type elf_howto_table 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. */ @@@ -340,6 -337,6 +340,10 @@@ elf_i386_reloc_type_lookup (bfd *abfd A 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]; @@@ -2063,17 -1520,17 +1527,26 @@@ elf_i386_check_relocs (bfd *abfd 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; @@@ -2450,6 -1883,6 +1899,11 @@@ do_size goto error_return; break; ++ case R_386_GPOFF: ++ if (eh != NULL) ++ eh->has_gpoff_reloc = 1; ++ break; ++ default: break; } @@@ -5354,6 -3415,6 +3436,41 @@@ disallow_got32 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; } @@@ -5882,6 -3918,6 +3974,10 @@@ elf_i386_finish_local_dynamic_symbol (v 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); } diff --cc bfd/elf64-x86-64.c index 8cef9f5e742,ba4f47bff46..56a6a4b72ef --- a/bfd/elf64-x86-64.c +++ b/bfd/elf64-x86-64.c @@@ -183,12 -173,12 +173,15 @@@ static reloc_howto_type x86_64_elf_howt 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. */ @@@ -264,6 -260,6 +263,7 @@@ static const struct elf_reloc_map x86_6 { 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, }, }; @@@ -2454,18 -1833,18 +1837,26 @@@ elf_x86_64_check_relocs (bfd *abfd, str 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; @@@ -2892,6 -2254,6 +2266,11 @@@ do_size goto error_return; break; ++ case R_X86_64_GPOFF: ++ if (eh != NULL) ++ eh->has_gpoff_reloc = 1; ++ break; ++ default: break; } @@@ -5732,9 -3756,9 +3773,44 @@@ direct 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; } @@@ -6244,8 -4267,8 +4319,12 @@@ elf_x86_64_finish_local_dynamic_symbol 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 diff --cc bfd/elflink.c index 336df3f59c8,e3751fa122c..79afee25205 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@@ -1035,9 -1044,10 +1046,10 @@@ _bfd_elf_merge_symbol (bfd *abfd 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; @@@ -1476,9 -1488,9 +1498,9 @@@ 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. */ diff --cc bfd/elfxx-x86.c index 00000000000,a9ee4ba387b..3b755a0c069 mode 000000,100644..100644 --- a/bfd/elfxx-x86.c +++ b/bfd/elfxx-x86.c @@@ -1,0 -1,2745 +1,2864 @@@ + /* 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: ") + : (type == GNU_PROPERTY_X86_ISA_1_NEEDED + ? _("error: %B: ") + : _("error: %B: "))), + 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); ++} diff --cc bfd/elfxx-x86.h index 00000000000,e2a83ad8309..a1697390f65 mode 000000,100644..100644 --- a/bfd/elfxx-x86.h +++ b/bfd/elfxx-x86.h @@@ -1,0 -1,695 +1,708 @@@ + /* 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 diff --cc bfd/linker.c index e4961d15f78,dac21bd9e32..7d4f47c14a9 --- a/bfd/linker.c +++ b/bfd/linker.c @@@ -1460,29 -1443,14 +1462,32 @@@ _bfd_generic_link_add_one_symbol (struc 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: @@@ -1526,10 -1494,8 +1531,11 @@@ 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 diff --cc bfd/syms.c index 087252a1236,7eafb7d17a9..8720fb22f59 --- a/bfd/syms.c +++ b/bfd/syms.c @@@ -305,12 -305,8 +305,12 @@@ CODE_FRAGMEN . {* 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 diff --cc binutils/readelf.c index 09edabca564,ae1cda9a7bd..de7d065f776 --- a/binutils/readelf.c +++ b/binutils/readelf.c @@@ -11436,18 -11571,12 +11573,18 @@@ process_symbol_table (Filedata * fileda } } else if ((do_dyn_syms || (do_syms && !do_using_dynamic)) - && section_headers != NULL) + && filedata->section_headers != NULL) { unsigned int i; + /* 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 - = elf_header.e_ident[EI_OSABI] != ELFOSABI_IRIX; ++ = 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; @@@ -11510,14 -11642,9 +11650,14 @@@ 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", ""); + 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 { diff --cc gas/config/tc-i386.c index 456be9e07cf,5cd83daf4b6..2829152bbfc --- a/gas/config/tc-i386.c +++ b/gas/config/tc-i386.c @@@ -672,6 -668,6 +668,9 @@@ static enum rc_type evexrcig = rne /* 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; @@@ -7776,6 -7739,6 +7742,9 @@@ lex_got (enum bfd_reloc_code_real *rel { 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 }, @@@ -7848,8 -7811,8 +7817,19 @@@ *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; @@@ -8506,6 -8475,6 +8492,10 @@@ i386_displacement (char *disp_start, ch 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 (); @@@ -10740,6 -10721,6 +10742,21 @@@ md_undefined_symbol (char *name }; 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; } @@@ -10899,6 -10880,6 +10916,7 @@@ tc_gen_reloc (asection *section ATTRIBU 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: diff --cc gas/config/tc-i386.h index f54924c4382,6e4f440c094..9e156996d3f --- a/gas/config/tc-i386.h +++ b/gas/config/tc-i386.h @@@ -128,6 -128,6 +128,12 @@@ extern const char *i386_comment_chars #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 diff --cc gas/testsuite/gas/elf/common7.d index f7b1d80cad4,00000000000..4229b195c37 mode 100644,000000..100644 --- a/gas/testsuite/gas/elf/common7.d +++ b/gas/testsuite/gas/elf/common7.d @@@ -1,2 -1,0 +1,2 @@@ +#name: secondary and common directives - #error-output: common5.l ++#error-output: common7.l diff --cc gas/testsuite/gas/elf/common7.l index 58d5142fc81,00000000000..58d5142fc81 mode 100644,000000..100644 --- a/gas/testsuite/gas/elf/common7.l +++ b/gas/testsuite/gas/elf/common7.l diff --cc gas/testsuite/gas/elf/common7.s index df8b7ed7f5c,00000000000..df8b7ed7f5c mode 100644,000000..100644 --- a/gas/testsuite/gas/elf/common7.s +++ b/gas/testsuite/gas/elf/common7.s diff --cc gas/testsuite/gas/elf/elf.exp index 629aecf601b,bb10ac63bc0..1bc7f29087e --- a/gas/testsuite/gas/elf/elf.exp +++ b/gas/testsuite/gas/elf/elf.exp @@@ -236,13 -253,12 +253,17 @@@ if { [is_elf_format] } then 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" diff --cc gas/testsuite/gas/i386/gpoff.d index 00000000000,00000000000..6a16ba49a7f new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/gpoff.d @@@ -1,0 -1,0 +1,11 @@@ ++#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 diff --cc gas/testsuite/gas/i386/gpoff.s index 00000000000,00000000000..4b786a24721 new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/gpoff.s @@@ -1,0 -1,0 +1,4 @@@ ++ .text ++_start: ++ leal foo@GPOFF, %eax ++ movl %fs:foo@GPOFF, %eax diff --cc gas/testsuite/gas/i386/i386.exp index 67a7a13b259,184c65ed8b2..cc034dcfa60 --- a/gas/testsuite/gas/i386/i386.exp +++ b/gas/testsuite/gas/i386/i386.exp @@@ -455,6 -489,15 +489,18 @@@ if [expr ([istarget "i*86-*-*"] || [is 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" @@@ -868,6 -949,6 +952,9 @@@ if [expr ([istarget "i*86-*-*"] || [ist 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" diff --cc gas/testsuite/gas/i386/inval-gpoff.l index 00000000000,00000000000..3fad845e697 new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/inval-gpoff.l @@@ -1,0 -1,0 +1,18 @@@ ++.*: 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 diff --cc gas/testsuite/gas/i386/inval-gpoff.s index 00000000000,00000000000..7556de3518e new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/inval-gpoff.s @@@ -1,0 -1,0 +1,5 @@@ ++ .text ++_start: ++ movl %fs:foo@GPOFF(%eax), %eax ++ movl %ds:foo@GPOFF(%eax, %ecx, 2), %eax ++ movl foo@GPOFF(%eax), %eax diff --cc gas/testsuite/gas/i386/x86-64-gpoff.d index 00000000000,00000000000..2d5e8bd870c new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/x86-64-gpoff.d @@@ -1,0 -1,0 +1,11 @@@ ++#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 diff --cc gas/testsuite/gas/i386/x86-64-gpoff.s index 00000000000,00000000000..ccd743506b0 new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/x86-64-gpoff.s @@@ -1,0 -1,0 +1,4 @@@ ++ .text ++_start: ++ leal foo@GPOFF, %eax ++ movl %gs:foo@GPOFF, %eax diff --cc gas/testsuite/gas/i386/x86-64-inval-gpoff.l index 00000000000,00000000000..e64a8bde1ee new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/x86-64-inval-gpoff.l @@@ -1,0 -1,0 +1,30 @@@ ++.*: 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 diff --cc gas/testsuite/gas/i386/x86-64-inval-gpoff.s index 00000000000,00000000000..8aacbf84807 new file mode 100644 --- /dev/null +++ b/gas/testsuite/gas/i386/x86-64-inval-gpoff.s @@@ -1,0 -1,0 +1,7 @@@ ++ .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 diff --cc include/elf/i386.h index 352e744f233,9d80aa872a0..1914382aa20 --- a/include/elf/i386.h +++ b/include/elf/i386.h @@@ -68,6 -68,6 +68,7 @@@ START_RELOC_NUMBERS (elf_i386_reloc_typ 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) diff --cc include/elf/x86-64.h index 976f4fe347b,8d364ab650c..88120c90ce2 --- a/include/elf/x86-64.h +++ b/include/elf/x86-64.h @@@ -82,6 -82,6 +82,7 @@@ START_RELOC_NUMBERS (elf_x86_64_reloc_t /* 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) diff --cc ld/emultempl/avrelf.em index 9bad2a28b6a,4f33ad1f7b4..9db69fc0d84 --- a/ld/emultempl/avrelf.em +++ b/ld/emultempl/avrelf.em @@@ -204,12 -217,16 +217,16 @@@ avr_finish (void } 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 diff --cc ld/emultempl/elf32.em index 4e405962b1d,c0925fc9b97..794212eb83e --- a/ld/emultempl/elf32.em +++ b/ld/emultempl/elf32.em @@@ -2009,8 -2018,29 +2019,31 @@@ output_rel_find (asection *sec, int isd 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. */ diff --cc ld/emultempl/spuelf.em index 0e3246fc83f,ec701857ae0..732a0eb34ed --- a/ld/emultempl/spuelf.em +++ b/ld/emultempl/spuelf.em @@@ -426,13 -426,13 +426,13 @@@ spu_finish (void 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 * diff --cc ld/ld.texinfo index 5a68f88d775,94d340caa65..8bcc19255f0 --- a/ld/ld.texinfo +++ b/ld/ld.texinfo @@@ -1155,18 -1206,13 +1206,17 @@@ the function is called (lazy binding), 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 diff --cc ld/ldlang.c index b7ff1d2e5fa,1526d7b2ec1..4ea36626860 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@@ -7081,41 -7221,40 +7241,71 @@@ lang_process (void 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. */ @@@ -8641,965 -8780,3 +8831,966 @@@ lang_print_memory_usage (void 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; + } +} diff --cc ld/ldmain.c index 182291c59e9,c473ed24c3e..43dca7872e3 --- a/ld/ldmain.c +++ b/ld/ldmain.c @@@ -285,8 -273,8 +276,9 @@@ main (int argc, char **argv 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 diff --cc ld/testsuite/ld-i386/gpoff-1a.S index 00000000000,00000000000..5bb9bb5e673 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-1a.S @@@ -1,0 -1,0 +1,20 @@@ ++ .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 diff --cc ld/testsuite/ld-i386/gpoff-1b.c index 00000000000,00000000000..30b19cc8ba3 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-1b.c @@@ -1,0 -1,0 +1,78 @@@ ++#define _GNU_SOURCE ++#include ++#include ++#include ++ ++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; ++} diff --cc ld/testsuite/ld-i386/gpoff-2a.S index 00000000000,00000000000..b54f316a8f9 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-2a.S @@@ -1,0 -1,0 +1,19 @@@ ++ .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 diff --cc ld/testsuite/ld-i386/gpoff-2b.c index 00000000000,00000000000..2a4085a7990 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-2b.c @@@ -1,0 -1,0 +1,77 @@@ ++#define _GNU_SOURCE ++#include ++#include ++#include ++ ++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; ++} diff --cc ld/testsuite/ld-i386/gpoff-3.d index 00000000000,00000000000..0952484cf9c new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-3.d @@@ -1,0 -1,0 +1,3 @@@ ++#as: --32 ++#ld: -melf_i386 ++#error: undefined reference to `foo' diff --cc ld/testsuite/ld-i386/gpoff-3.s index 00000000000,00000000000..91f9bf8b027 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-3.s @@@ -1,0 -1,0 +1,4 @@@ ++ .text ++ .globl _start ++_start: ++ movl %fs:foo@GPOFF, %eax diff --cc ld/testsuite/ld-i386/gpoff-4.d index 00000000000,00000000000..f87739cb76c new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-4.d @@@ -1,0 -1,0 +1,3 @@@ ++#as: --32 ++#ld: -melf_i386 ++#error: GPOFF relocation at 0x2 in section `\.text' must be against symbol defined in GP section `\.rodata' diff --cc ld/testsuite/ld-i386/gpoff-4.s index 00000000000,00000000000..f138afd0899 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-4.s @@@ -1,0 -1,0 +1,16 @@@ ++ .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 diff --cc ld/testsuite/ld-i386/gpoff-5.d index 00000000000,00000000000..0df1ca23e53 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-5.d @@@ -1,0 -1,0 +1,3 @@@ ++#as: --32 ++#ld: -melf_i386 ++#error: symbol `bar' with GPOFF relocation defined in .*\.o\(\.rodata\) isn't in GP section `\.data' diff --cc ld/testsuite/ld-i386/gpoff-5.s index 00000000000,00000000000..4a9c8d1c937 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-5.s @@@ -1,0 -1,0 +1,18 @@@ ++ .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 diff --cc ld/testsuite/ld-i386/gpoff-6.d index 00000000000,00000000000..00bdf7ad1ac new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-6.d @@@ -1,0 -1,0 +1,12 @@@ ++#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 diff --cc ld/testsuite/ld-i386/gpoff-6.s index 00000000000,00000000000..592c0ff3c38 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-6.s @@@ -1,0 -1,0 +1,9 @@@ ++ .text ++ .globl _start ++_start: ++ ret ++ ++ .section .text.bar,"ax",@progbits ++ .globl bar ++bar: ++ movl %fs:foo@GPOFF, %eax diff --cc ld/testsuite/ld-i386/gpoff-7.d index 00000000000,00000000000..ecabaf159fc new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-7.d @@@ -1,0 -1,0 +1,16 @@@ ++#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 diff --cc ld/testsuite/ld-i386/gpoff-7.s index 00000000000,00000000000..f5b220aa8a6 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-7.s @@@ -1,0 -1,0 +1,9 @@@ ++ .text ++ .globl _start ++_start: ++ movl %fs:foo@GPOFF, %eax ++ ++ .data ++ .globl foo ++foo: ++ .long 0x12345678 diff --cc ld/testsuite/ld-i386/gpoff-8.s index 00000000000,00000000000..cffe6f06f55 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8.s @@@ -1,0 -1,0 +1,10 @@@ ++ .text ++ .globl _start ++_start: ++ movl %fs:foo@GPOFF, %eax ++ movl __gp@GOT(%ebx), %eax ++ ++ .data ++ .globl foo ++foo: ++ .long 0x12345678 diff --cc ld/testsuite/ld-i386/gpoff-8.t index 00000000000,00000000000..1f016440de3 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8.t @@@ -1,0 -1,0 +1,39 @@@ ++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/ : { *(.*) } ++} diff --cc ld/testsuite/ld-i386/gpoff-8a.d index 00000000000,00000000000..94128c955e4 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8a.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-i386/gpoff-8b.d index 00000000000,00000000000..9969de39fa3 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8b.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-i386/gpoff-8c.d index 00000000000,00000000000..c11ab46bb4b new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8c.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-i386/gpoff-8d.d index 00000000000,00000000000..64d9fcf6c42 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8d.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-i386/gpoff-8e.d index 00000000000,00000000000..bfa25a28449 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8e.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-i386/gpoff-8f.d index 00000000000,00000000000..6cc4a52403e new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-i386/gpoff-8f.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-i386/i386.exp index 6c53046193a,c4fc545dbb6..03727c24cf7 --- a/ld/testsuite/ld-i386/i386.exp +++ b/ld/testsuite/ld-i386/i386.exp @@@ -289,6 -290,6 +290,17 @@@ run_dump_test "abs 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" @@@ -1250,15 -1285,106 +1296,167 @@@ if { [isnative "$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*"] diff --cc ld/testsuite/ld-x86-64/gpoff-1a.S index 00000000000,00000000000..24e9e649d92 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-1a.S @@@ -1,0 -1,0 +1,20 @@@ ++ .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 diff --cc ld/testsuite/ld-x86-64/gpoff-1b.c index 00000000000,00000000000..185f265b070 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-1b.c @@@ -1,0 -1,0 +1,29 @@@ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++ ++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; ++} diff --cc ld/testsuite/ld-x86-64/gpoff-2a.S index 00000000000,00000000000..cb9f26755a7 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-2a.S @@@ -1,0 -1,0 +1,19 @@@ ++ .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 diff --cc ld/testsuite/ld-x86-64/gpoff-2b.c index 00000000000,00000000000..8b91e22721e new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-2b.c @@@ -1,0 -1,0 +1,27 @@@ ++#define _GNU_SOURCE ++#include ++#include ++#include ++#include ++ ++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; ++} diff --cc ld/testsuite/ld-x86-64/gpoff-3.d index 00000000000,00000000000..c8a5e6f9834 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-3.d @@@ -1,0 -1,0 +1,3 @@@ ++#as: --64 ++#ld: -melf_x86_64 ++#error: undefined reference to `foo' diff --cc ld/testsuite/ld-x86-64/gpoff-3.s index 00000000000,00000000000..0513d3e4d38 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-3.s @@@ -1,0 -1,0 +1,4 @@@ ++ .text ++ .globl _start ++_start: ++ movl %gs:foo@GPOFF, %eax diff --cc ld/testsuite/ld-x86-64/gpoff-4.d index 00000000000,00000000000..f8fee15d1d5 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-4.d @@@ -1,0 -1,0 +1,3 @@@ ++#as: --64 ++#ld: -melf_x86_64 ++#error: GPOFF relocation at 0x4 in section `\.text' must be against symbol defined in GP section `\.rodata' diff --cc ld/testsuite/ld-x86-64/gpoff-4.s index 00000000000,00000000000..5f6cd4116ec new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-4.s @@@ -1,0 -1,0 +1,16 @@@ ++ .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 diff --cc ld/testsuite/ld-x86-64/gpoff-5.d index 00000000000,00000000000..ddc8498cff8 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-5.d @@@ -1,0 -1,0 +1,3 @@@ ++#as: --64 ++#ld: -melf_x86_64 ++#error: symbol `bar' with GPOFF relocation defined in .*\.o\(\.rodata\) isn't in GP section `\.data' diff --cc ld/testsuite/ld-x86-64/gpoff-5.s index 00000000000,00000000000..3a8e6781fc4 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-5.s @@@ -1,0 -1,0 +1,18 @@@ ++ .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 diff --cc ld/testsuite/ld-x86-64/gpoff-6.d index 00000000000,00000000000..1676b0d3ef2 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-6.d @@@ -1,0 -1,0 +1,12 @@@ ++#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 diff --cc ld/testsuite/ld-x86-64/gpoff-6.s index 00000000000,00000000000..3e632f3cea1 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-6.s @@@ -1,0 -1,0 +1,9 @@@ ++ .text ++ .globl _start ++_start: ++ ret ++ ++ .section .text.bar,"ax",@progbits ++ .globl bar ++bar: ++ movl %gs:foo@GPOFF, %eax diff --cc ld/testsuite/ld-x86-64/gpoff-7.d index 00000000000,00000000000..a6ebefa2652 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-7.d @@@ -1,0 -1,0 +1,16 @@@ ++#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 diff --cc ld/testsuite/ld-x86-64/gpoff-7.s index 00000000000,00000000000..a900661811d new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-7.s @@@ -1,0 -1,0 +1,9 @@@ ++ .text ++ .globl _start ++_start: ++ movl %gs:foo@GPOFF, %eax ++ ++ .data ++ .globl foo ++foo: ++ .long 0x12345678 diff --cc ld/testsuite/ld-x86-64/gpoff-8.s index 00000000000,00000000000..948bafc498d new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8.s @@@ -1,0 -1,0 +1,10 @@@ ++ .text ++ .globl _start ++_start: ++ movl %gs:foo@GPOFF, %eax ++ movq __gp@GOTPCREL(%rip), %rax ++ ++ .data ++ .globl foo ++foo: ++ .long 0x12345678 diff --cc ld/testsuite/ld-x86-64/gpoff-8.t index 00000000000,00000000000..1f016440de3 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8.t @@@ -1,0 -1,0 +1,39 @@@ ++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/ : { *(.*) } ++} diff --cc ld/testsuite/ld-x86-64/gpoff-8a.d index 00000000000,00000000000..6c1640d34c2 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8a.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-x86-64/gpoff-8b.d index 00000000000,00000000000..f163bc1d383 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8b.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-x86-64/gpoff-8c.d index 00000000000,00000000000..d5478bbb2a4 new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8c.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-x86-64/gpoff-8d.d index 00000000000,00000000000..2e0a330396c new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8d.d @@@ -1,0 -1,0 +1,18 @@@ ++#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 diff --cc ld/testsuite/ld-x86-64/gpoff-8e.d index 00000000000,00000000000..1a1c9d907ed new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8e.d @@@ -1,0 -1,0 +1,18 @@@ ++#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]+ ++#pass diff --cc ld/testsuite/ld-x86-64/gpoff-8f.d index 00000000000,00000000000..fb8c3375b8d new file mode 100644 --- /dev/null +++ b/ld/testsuite/ld-x86-64/gpoff-8f.d @@@ -1,0 -1,0 +1,18 @@@ ++#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]+ ++#pass diff --cc ld/testsuite/ld-x86-64/x86-64.exp index 9b83e1c5ce8,e7f338ee11e..7bf7370d6fe --- a/ld/testsuite/ld-x86-64/x86-64.exp +++ b/ld/testsuite/ld-x86-64/x86-64.exp @@@ -267,8 -267,9 +267,20 @@@ if { ![ld_link $ld tmpdir/$test "-m$emu 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" @@@ -1528,6 -1792,6 +1803,67 @@@ if { [isnative] && [which $CC] != 0 } } } ++ 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"