]> git.ipfire.org Git - thirdparty/binutils-gdb.git/commitdiff
Merge remote-tracking branch 'origin/users/hjl/linux/master' into users/hjl/linux... users/hjl/linux/applied users/hjl/linux/release/2.29.51.0.1
authorH.J. Lu <hjl.tools@gmail.com>
Fri, 19 Jan 2018 11:23:56 +0000 (03:23 -0800)
committerH.J. Lu <hjl.tools@gmail.com>
Fri, 19 Jan 2018 11:23:56 +0000 (03:23 -0800)
121 files changed:
1  2 
bfd/archive.c
bfd/bfd-in2.h
bfd/bfd.c
bfd/elf.c
bfd/elf32-i386.c
bfd/elf32-mips.c
bfd/elf64-ia64-vms.c
bfd/elf64-x86-64.c
bfd/elfcode.h
bfd/elflink.c
bfd/elfn32-mips.c
bfd/elfxx-x86.c
bfd/elfxx-x86.h
bfd/format.c
bfd/linker.c
bfd/opncls.c
bfd/plugin.c
bfd/plugin.h
bfd/section.c
bfd/syms.c
binutils/nm.c
binutils/objcopy.c
binutils/readelf.c
gas/config/obj-elf.c
gas/config/tc-i386.c
gas/config/tc-i386.h
gas/doc/as.texinfo
gas/symbols.c
gas/symbols.h
gas/testsuite/gas/elf/common7.d
gas/testsuite/gas/elf/common7.l
gas/testsuite/gas/elf/common7.s
gas/testsuite/gas/elf/elf.exp
gas/testsuite/gas/i386/gpoff.d
gas/testsuite/gas/i386/gpoff.s
gas/testsuite/gas/i386/i386.exp
gas/testsuite/gas/i386/inval-gpoff.l
gas/testsuite/gas/i386/inval-gpoff.s
gas/testsuite/gas/i386/x86-64-gpoff.d
gas/testsuite/gas/i386/x86-64-gpoff.s
gas/testsuite/gas/i386/x86-64-inval-gpoff.l
gas/testsuite/gas/i386/x86-64-inval-gpoff.s
include/bfdlink.h
include/elf/common.h
include/elf/i386.h
include/elf/x86-64.h
ld/emultempl/aarch64elf.em
ld/emultempl/alphaelf.em
ld/emultempl/armelf.em
ld/emultempl/avrelf.em
ld/emultempl/elf32.em
ld/emultempl/ppc32elf.em
ld/emultempl/ppc64elf.em
ld/emultempl/spuelf.em
ld/ld.texinfo
ld/ldexp.c
ld/ldexp.h
ld/ldfile.c
ld/ldlang.c
ld/ldlang.h
ld/ldlex.h
ld/ldmain.c
ld/ldmain.h
ld/lexsup.c
ld/plugin.c
ld/plugin.h
ld/scripttempl/armbpabi.sc
ld/scripttempl/elf.sc
ld/scripttempl/elf32sh-symbian.sc
ld/scripttempl/elf64hppa.sc
ld/scripttempl/elfxtensa.sc
ld/scripttempl/mep.sc
ld/scripttempl/pe.sc
ld/scripttempl/pep.sc
ld/testsuite/ld-i386/gpoff-1a.S
ld/testsuite/ld-i386/gpoff-1b.c
ld/testsuite/ld-i386/gpoff-2a.S
ld/testsuite/ld-i386/gpoff-2b.c
ld/testsuite/ld-i386/gpoff-3.d
ld/testsuite/ld-i386/gpoff-3.s
ld/testsuite/ld-i386/gpoff-4.d
ld/testsuite/ld-i386/gpoff-4.s
ld/testsuite/ld-i386/gpoff-5.d
ld/testsuite/ld-i386/gpoff-5.s
ld/testsuite/ld-i386/gpoff-6.d
ld/testsuite/ld-i386/gpoff-6.s
ld/testsuite/ld-i386/gpoff-7.d
ld/testsuite/ld-i386/gpoff-7.s
ld/testsuite/ld-i386/gpoff-8.s
ld/testsuite/ld-i386/gpoff-8.t
ld/testsuite/ld-i386/gpoff-8a.d
ld/testsuite/ld-i386/gpoff-8b.d
ld/testsuite/ld-i386/gpoff-8c.d
ld/testsuite/ld-i386/gpoff-8d.d
ld/testsuite/ld-i386/gpoff-8e.d
ld/testsuite/ld-i386/gpoff-8f.d
ld/testsuite/ld-i386/i386.exp
ld/testsuite/ld-plugin/lto.exp
ld/testsuite/ld-x86-64/gpoff-1a.S
ld/testsuite/ld-x86-64/gpoff-1b.c
ld/testsuite/ld-x86-64/gpoff-2a.S
ld/testsuite/ld-x86-64/gpoff-2b.c
ld/testsuite/ld-x86-64/gpoff-3.d
ld/testsuite/ld-x86-64/gpoff-3.s
ld/testsuite/ld-x86-64/gpoff-4.d
ld/testsuite/ld-x86-64/gpoff-4.s
ld/testsuite/ld-x86-64/gpoff-5.d
ld/testsuite/ld-x86-64/gpoff-5.s
ld/testsuite/ld-x86-64/gpoff-6.d
ld/testsuite/ld-x86-64/gpoff-6.s
ld/testsuite/ld-x86-64/gpoff-7.d
ld/testsuite/ld-x86-64/gpoff-7.s
ld/testsuite/ld-x86-64/gpoff-8.s
ld/testsuite/ld-x86-64/gpoff-8.t
ld/testsuite/ld-x86-64/gpoff-8a.d
ld/testsuite/ld-x86-64/gpoff-8b.d
ld/testsuite/ld-x86-64/gpoff-8c.d
ld/testsuite/ld-x86-64/gpoff-8d.d
ld/testsuite/ld-x86-64/gpoff-8e.d
ld/testsuite/ld-x86-64/gpoff-8f.d
ld/testsuite/ld-x86-64/x86-64.exp

diff --cc bfd/archive.c
Simple merge
diff --cc bfd/bfd-in2.h
index e481509c6b8ad609b53ef10f536eb1cb588372f9,f4b3720b4b15ac7aec687c847c90ce2591256475..06bc2331b53ace402752d7493fc69658a761f044
@@@ -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/bfd.c
Simple merge
diff --cc bfd/elf.c
index 6d596576f2cb473eafc57be56cac379bec995236,90aef0913292f1760264c72867b8c872527b4d8f..e3ec778de652db8b4f8e29ff9ae41cc1d267aa6a
+++ 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[] =
index df16775d54aba83aeb3df279c18e989f3055d1ec,1f380dbdc3d7aa3902b0adcaa7ac98ffe2a86c79..b958908ff7a52717f4ee258221b4c21d82a3d5bb
@@@ -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);
  }
Simple merge
Simple merge
index 8cef9f5e742b2a4bf379f679385e1e3e0537eca5,ba4f47bff469e829f5ffdc95ded6bf1061b2d24a..56a6a4b72ef87ceb5bc518fb53857bb3b9e0dee0
@@@ -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/elfcode.h
Simple merge
diff --cc bfd/elflink.c
index 336df3f59c808b72eabcbf46f77d4c288196450b,e3751fa122cf936a3c277ddd36631a5b0d2647f2..79afee25205a87f3664c1134fa986e04fc5ad339
@@@ -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;
       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.  */
Simple merge
diff --cc bfd/elfxx-x86.c
index 0000000000000000000000000000000000000000,a9ee4ba387b00a16459044e87aeb61538da358ef..3b755a0c069707aa69d5c24810330ce4828cc0be
mode 000000,100644..100644
--- /dev/null
@@@ -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: <corrupt x86 ISA used size: 0x%x>")
+             : (type == GNU_PROPERTY_X86_ISA_1_NEEDED
+                ? _("error: %B: <corrupt x86 ISA needed size: 0x%x>")
+                : _("error: %B: <corrupt x86 feature size: 0x%x>"))),
+            abfd, datasz);
+         return property_corrupt;
+       }
+       prop = _bfd_elf_get_property (abfd, type, datasz);
+       /* Combine properties of the same type.  */
+       prop->u.number |= bfd_h_get_32 (abfd, ptr);
+       prop->pr_kind = property_number;
+       break;
+     default:
+       return property_ignored;
+     }
+   return property_number;
+ }
+ /* Merge x86 GNU property BPROP with APROP.  If APROP isn't NULL,
+    return TRUE if APROP is updated.  Otherwise, return TRUE if BPROP
+    should be merged with ABFD.  */
+ bfd_boolean
+ _bfd_x86_elf_merge_gnu_properties (struct bfd_link_info *info,
+                                  bfd *abfd ATTRIBUTE_UNUSED,
+                                  elf_property *aprop,
+                                  elf_property *bprop)
+ {
+   unsigned int number, features;
+   bfd_boolean updated = FALSE;
+   unsigned int pr_type = aprop != NULL ? aprop->pr_type : bprop->pr_type;
+   switch (pr_type)
+     {
+     case GNU_PROPERTY_X86_ISA_1_USED:
+     case GNU_PROPERTY_X86_ISA_1_NEEDED:
+       if (aprop != NULL && bprop != NULL)
+       {
+         number = aprop->u.number;
+         aprop->u.number = number | bprop->u.number;
+         updated = number != (unsigned int) aprop->u.number;
+       }
+       else
+       {
+         /* Return TRUE if APROP is NULL to indicate that BPROP should
+            be added to ABFD.  */
+         updated = aprop == NULL;
+       }
+       break;
+     case GNU_PROPERTY_X86_FEATURE_1_AND:
+       /* Only one of APROP and BPROP can be NULL:
+        1. APROP & BPROP when both APROP and BPROP aren't NULL.
+        2. If APROP is NULL, remove x86 feature.
+        3. Otherwise, do nothing.
+        */
+       if (aprop != NULL && bprop != NULL)
+       {
+         features = 0;
+         if (info->ibt)
+           features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+         if (info->shstk)
+           features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+         number = aprop->u.number;
+         /* Add GNU_PROPERTY_X86_FEATURE_1_IBT and
+            GNU_PROPERTY_X86_FEATURE_1_SHSTK.  */
+         aprop->u.number = (number & bprop->u.number) | features;
+         updated = number != (unsigned int) aprop->u.number;
+         /* Remove the property if all feature bits are cleared.  */
+         if (aprop->u.number == 0)
+           aprop->pr_kind = property_remove;
+       }
+       else
+       {
+         features = 0;
+         if (info->ibt)
+           features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+         if (info->shstk)
+           features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+         if (features)
+           {
+             /* Add GNU_PROPERTY_X86_FEATURE_1_IBT and
+                GNU_PROPERTY_X86_FEATURE_1_SHSTK.  */
+             if (aprop != NULL)
+               {
+                 number = aprop->u.number;
+                 aprop->u.number = number | features;
+                 updated = number != (unsigned int) aprop->u.number;
+               }
+             else
+               {
+                 bprop->u.number |= features;
+                 updated = TRUE;
+               }
+           }
+         else if (aprop != NULL)
+           {
+             aprop->pr_kind = property_remove;
+             updated = TRUE;
+           }
+       }
+       break;
+     default:
+       /* Never should happen.  */
+       abort ();
+     }
+   return updated;
+ }
+ /* Set up x86 GNU properties.  Return the first relocatable ELF input
+    with GNU properties if found.  Otherwise, return NULL.  */
+ bfd *
+ _bfd_x86_elf_link_setup_gnu_properties
+   (struct bfd_link_info *info, struct elf_x86_init_table *init_table)
+ {
+   bfd_boolean normal_target;
+   bfd_boolean lazy_plt;
+   asection *sec, *pltsec;
+   bfd *dynobj;
+   bfd_boolean use_ibt_plt;
+   unsigned int plt_alignment, features;
+   struct elf_x86_link_hash_table *htab;
+   bfd *pbfd;
+   bfd *ebfd = NULL;
+   elf_property *prop;
+   const struct elf_backend_data *bed;
+   unsigned int class_align = ABI_64_P (info->output_bfd) ? 3 : 2;
+   unsigned int got_align;
+   features = 0;
+   if (info->ibt)
+     features = GNU_PROPERTY_X86_FEATURE_1_IBT;
+   if (info->shstk)
+     features |= GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+   /* Find a normal input file with GNU property note.  */
+   for (pbfd = info->input_bfds;
+        pbfd != NULL;
+        pbfd = pbfd->link.next)
+     if (bfd_get_flavour (pbfd) == bfd_target_elf_flavour
+       && bfd_count_sections (pbfd) != 0)
+       {
+       ebfd = pbfd;
+       if (elf_properties (pbfd) != NULL)
+         break;
+       }
+   if (ebfd != NULL && features)
+     {
+       /* If features is set, add GNU_PROPERTY_X86_FEATURE_1_IBT and
+        GNU_PROPERTY_X86_FEATURE_1_SHSTK.  */
+       prop = _bfd_elf_get_property (ebfd,
+                                   GNU_PROPERTY_X86_FEATURE_1_AND,
+                                   4);
+       prop->u.number |= features;
+       prop->pr_kind = property_number;
+       /* Create the GNU property note section if needed.  */
+       if (pbfd == NULL)
+       {
+         sec = bfd_make_section_with_flags (ebfd,
+                                            NOTE_GNU_PROPERTY_SECTION_NAME,
+                                            (SEC_ALLOC
+                                             | SEC_LOAD
+                                             | SEC_IN_MEMORY
+                                             | SEC_READONLY
+                                             | SEC_HAS_CONTENTS
+                                             | SEC_DATA));
+         if (sec == NULL)
+           info->callbacks->einfo (_("%F%P: failed to create GNU property section\n"));
+         if (!bfd_set_section_alignment (ebfd, sec, class_align))
+           {
+ error_alignment:
+             info->callbacks->einfo (_("%F%A: failed to align section\n"),
+                                     sec);
+           }
+         elf_section_type (sec) = SHT_NOTE;
+       }
+     }
+   pbfd = _bfd_elf_link_setup_gnu_properties (info);
+   bed = get_elf_backend_data (info->output_bfd);
+   htab = elf_x86_hash_table (info, bed->target_id);
+   if (htab == NULL)
+     return pbfd;
+   htab->r_info = init_table->r_info;
+   htab->r_sym = init_table->r_sym;
+   if (bfd_link_relocatable (info))
+     return pbfd;
+   htab->plt0_pad_byte = init_table->plt0_pad_byte;
+   use_ibt_plt = info->ibtplt || info->ibt;
+   if (!use_ibt_plt && pbfd != NULL)
+     {
+       /* Check if GNU_PROPERTY_X86_FEATURE_1_IBT is on.  */
+       elf_property_list *p;
+       /* The property list is sorted in order of type.  */
+       for (p = elf_properties (pbfd); p; p = p->next)
+       {
+         if (GNU_PROPERTY_X86_FEATURE_1_AND == p->property.pr_type)
+           {
+             use_ibt_plt = !!(p->property.u.number
+                              & GNU_PROPERTY_X86_FEATURE_1_IBT);
+             break;
+           }
+         else if (GNU_PROPERTY_X86_FEATURE_1_AND < p->property.pr_type)
+           break;
+       }
+     }
+   dynobj = htab->elf.dynobj;
+   /* Set htab->elf.dynobj here so that there is no need to check and
+      set it in check_relocs.  */
+   if (dynobj == NULL)
+     {
+       if (pbfd != NULL)
+       {
+         htab->elf.dynobj = pbfd;
+         dynobj = pbfd;
+       }
+       else
+       {
+         bfd *abfd;
+         /* Find a normal input file to hold linker created
+            sections.  */
+         for (abfd = info->input_bfds;
+              abfd != NULL;
+              abfd = abfd->link.next)
+           if (bfd_get_flavour (abfd) == bfd_target_elf_flavour
+               && (abfd->flags
+                   & (DYNAMIC | BFD_LINKER_CREATED | BFD_PLUGIN)) == 0)
+             {
+               htab->elf.dynobj = abfd;
+               dynobj = abfd;
+               break;
+             }
+       }
+     }
+   /* Return if there are no normal input files.  */
+   if (dynobj == NULL)
+     return pbfd;
+   /* Even when lazy binding is disabled by "-z now", the PLT0 entry may
+      still be used with LD_AUDIT or LD_PROFILE if PLT entry is used for
+      canonical function address.  */
+   htab->plt.has_plt0 = 1;
+   normal_target = htab->target_os == is_normal;
+   if (normal_target)
+     {
+       if (use_ibt_plt)
+       {
+         htab->lazy_plt = init_table->lazy_ibt_plt;
+         htab->non_lazy_plt = init_table->non_lazy_ibt_plt;
+       }
+       else
+       {
+         htab->lazy_plt = init_table->lazy_plt;
+         htab->non_lazy_plt = init_table->non_lazy_plt;
+       }
+     }
+   else
+     {
+       htab->lazy_plt = init_table->lazy_plt;
+       htab->non_lazy_plt = NULL;
+     }
+   pltsec = htab->elf.splt;
+   /* If the non-lazy PLT is available, use it for all PLT entries if
+      there are no PLT0 or no .plt section.  */
+   if (htab->non_lazy_plt != NULL
+       && (!htab->plt.has_plt0 || pltsec == NULL))
+     {
+       lazy_plt = FALSE;
+       if (bfd_link_pic (info))
+       htab->plt.plt_entry = htab->non_lazy_plt->pic_plt_entry;
+       else
+       htab->plt.plt_entry = htab->non_lazy_plt->plt_entry;
+       htab->plt.plt_entry_size = htab->non_lazy_plt->plt_entry_size;
+       htab->plt.plt_got_offset = htab->non_lazy_plt->plt_got_offset;
+       htab->plt.plt_got_insn_size
+       = htab->non_lazy_plt->plt_got_insn_size;
+       htab->plt.eh_frame_plt_size
+       = htab->non_lazy_plt->eh_frame_plt_size;
+       htab->plt.eh_frame_plt = htab->non_lazy_plt->eh_frame_plt;
+     }
+   else
+     {
+       lazy_plt = TRUE;
+       if (bfd_link_pic (info))
+       {
+         htab->plt.plt0_entry = htab->lazy_plt->pic_plt0_entry;
+         htab->plt.plt_entry = htab->lazy_plt->pic_plt_entry;
+       }
+       else
+       {
+         htab->plt.plt0_entry = htab->lazy_plt->plt0_entry;
+         htab->plt.plt_entry = htab->lazy_plt->plt_entry;
+       }
+       htab->plt.plt_entry_size = htab->lazy_plt->plt_entry_size;
+       htab->plt.plt_got_offset = htab->lazy_plt->plt_got_offset;
+       htab->plt.plt_got_insn_size
+       = htab->lazy_plt->plt_got_insn_size;
+       htab->plt.eh_frame_plt_size
+       = htab->lazy_plt->eh_frame_plt_size;
+       htab->plt.eh_frame_plt = htab->lazy_plt->eh_frame_plt;
+     }
+   if (htab->target_os == is_vxworks
+       && !elf_vxworks_create_dynamic_sections (dynobj, info,
+                                              &htab->srelplt2))
+     {
+       info->callbacks->einfo (_("%F%P: failed to create VxWorks dynamic sections\n"));
+       return pbfd;
+     }
+   /* Since create_dynamic_sections isn't always called, but GOT
+      relocations need GOT relocations, create them here so that we
+      don't need to do it in check_relocs.  */
+   if (htab->elf.sgot == NULL
+       && !_bfd_elf_create_got_section (dynobj, info))
+     info->callbacks->einfo (_("%F%P: failed to create GOT sections\n"));
+   got_align = (bed->target_id == X86_64_ELF_DATA) ? 3 : 2;
+   /* Align .got and .got.plt sections to their entry size.  Do it here
+      instead of in create_dynamic_sections so that they are always
+      properly aligned even if create_dynamic_sections isn't called.  */
+   sec = htab->elf.sgot;
+   if (!bfd_set_section_alignment (dynobj, sec, got_align))
+     goto error_alignment;
+   sec = htab->elf.sgotplt;
+   if (!bfd_set_section_alignment (dynobj, sec, got_align))
+     goto error_alignment;
+   /* Create the ifunc sections here so that check_relocs can be
+      simplified.  */
+   if (!_bfd_elf_create_ifunc_sections (dynobj, info))
+     info->callbacks->einfo (_("%F%P: failed to create ifunc sections\n"));
+   plt_alignment = bfd_log2 (htab->plt.plt_entry_size);
+   if (pltsec != NULL)
+     {
+       /* Whe creating executable, set the contents of the .interp
+        section to the interpreter.  */
+       if (bfd_link_executable (info) && !info->nointerp)
+       {
+         asection *s = bfd_get_linker_section (dynobj, ".interp");
+         if (s == NULL)
+           abort ();
+         s->size = htab->dynamic_interpreter_size;
+         s->contents = (unsigned char *) htab->dynamic_interpreter;
+         htab->interp = s;
+       }
+       /* Don't change PLT section alignment for NaCl since it uses
+        64-byte PLT entry and sets PLT section alignment to 32
+        bytes.  Don't create additional PLT sections for NaCl.  */
+       if (normal_target)
+       {
+         flagword pltflags = (bed->dynamic_sec_flags
+                              | SEC_ALLOC
+                              | SEC_CODE
+                              | SEC_LOAD
+                              | SEC_READONLY);
+         unsigned int non_lazy_plt_alignment
+           = bfd_log2 (htab->non_lazy_plt->plt_entry_size);
+         sec = pltsec;
+         if (!bfd_set_section_alignment (sec->owner, sec,
+                                         plt_alignment))
+           goto error_alignment;
+         /* Create the GOT procedure linkage table.  */
+         sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                   ".plt.got",
+                                                   pltflags);
+         if (sec == NULL)
+           info->callbacks->einfo (_("%F%P: failed to create GOT PLT section\n"));
+         if (!bfd_set_section_alignment (dynobj, sec,
+                                         non_lazy_plt_alignment))
+           goto error_alignment;
+         htab->plt_got = sec;
+         if (lazy_plt)
+           {
+             sec = NULL;
+             if (use_ibt_plt)
+               {
+                 /* Create the second PLT for Intel IBT support.  IBT
+                    PLT is supported only for non-NaCl target and is
+                    is needed only for lazy binding.  */
+                 sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                           ".plt.sec",
+                                                           pltflags);
+                 if (sec == NULL)
+                   info->callbacks->einfo (_("%F%P: failed to create IBT-enabled PLT section\n"));
+                 if (!bfd_set_section_alignment (dynobj, sec,
+                                                 plt_alignment))
+                   goto error_alignment;
+               }
+             else if (info->bndplt && ABI_64_P (dynobj))
+               {
+                 /* Create the second PLT for Intel MPX support.  MPX
+                    PLT is supported only for non-NaCl target in 64-bit
+                    mode and is needed only for lazy binding.  */
+                 sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                           ".plt.sec",
+                                                           pltflags);
+                 if (sec == NULL)
+                   info->callbacks->einfo (_("%F%P: failed to create BND PLT section\n"));
+                 if (!bfd_set_section_alignment (dynobj, sec,
+                                                 non_lazy_plt_alignment))
+                   goto error_alignment;
+               }
+             htab->plt_second = sec;
+           }
+       }
+       if (!info->no_ld_generated_unwind_info)
+       {
+         flagword flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY
+                           | SEC_HAS_CONTENTS | SEC_IN_MEMORY
+                           | SEC_LINKER_CREATED);
+         sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                   ".eh_frame",
+                                                   flags);
+         if (sec == NULL)
+           info->callbacks->einfo (_("%F%P: failed to create PLT .eh_frame section\n"));
+         if (!bfd_set_section_alignment (dynobj, sec, class_align))
+           goto error_alignment;
+         htab->plt_eh_frame = sec;
+         if (htab->plt_got != NULL)
+           {
+             sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                       ".eh_frame",
+                                                       flags);
+             if (sec == NULL)
+               info->callbacks->einfo (_("%F%P: failed to create GOT PLT .eh_frame section\n"));
+             if (!bfd_set_section_alignment (dynobj, sec, class_align))
+               goto error_alignment;
+             htab->plt_got_eh_frame = sec;
+           }
+         if (htab->plt_second != NULL)
+           {
+             sec = bfd_make_section_anyway_with_flags (dynobj,
+                                                       ".eh_frame",
+                                                       flags);
+             if (sec == NULL)
+               info->callbacks->einfo (_("%F%P: failed to create the second PLT .eh_frame section\n"));
+             if (!bfd_set_section_alignment (dynobj, sec, class_align))
+               goto error_alignment;
+             htab->plt_second_eh_frame = sec;
+           }
+       }
+     }
+   if (normal_target)
+     {
+       /* The .iplt section is used for IFUNC symbols in static
+        executables.  */
+       sec = htab->elf.iplt;
+       if (sec != NULL
+         && !bfd_set_section_alignment (sec->owner, sec,
+                                        plt_alignment))
+       goto error_alignment;
+     }
+   return pbfd;
+ }
++
++/* Set up GP section from symbols with GPOFF relocations.  */
++
++static bfd_boolean
++elf_x86_setup_gp (struct elf_link_hash_entry *h, void * inf)
++{
++  struct bfd_link_info *info;
++  struct elf_x86_link_hash_table *htab;
++  struct elf_x86_link_hash_entry *eh;
++  struct elf_link_hash_entry *gp;
++  asection *gpsection;
++  bfd_size_type gpsection_size;
++  const struct elf_backend_data *bed;
++
++  eh = (struct elf_x86_link_hash_entry *) h;
++
++  /* Skip if there is no GPOFF relocation or symbol is undefined.  */
++  if (!eh->has_gpoff_reloc
++      || (h->root.type != bfd_link_hash_defined
++        && h->root.type != bfd_link_hash_defweak))
++    return TRUE;
++
++  info = (struct bfd_link_info *) inf;
++  bed = get_elf_backend_data (info->output_bfd);
++  htab = elf_x86_hash_table (info, bed->target_id);
++  if (htab == NULL)
++    return FALSE;
++
++  gpsection = h->root.u.def.section->output_section;
++  gpsection_size = bfd_get_section_size (gpsection);
++
++  if (bed->target_id == X86_64_ELF_DATA && gpsection_size > 0xffffffff)
++    {
++      info->callbacks->einfo (_("%F%B: GP section `%A' size overflow\n"),
++                            info->output_bfd, gpsection);
++      return FALSE;
++    }
++
++  gp = htab->gp;
++  gp->root.type = bfd_link_hash_defined;
++  gp->root.u.def.value = gpsection_size / 2;
++  gp->root.u.def.section = gpsection;
++  gp->root.linker_def = 1;
++
++  /* Found GP section.  No need to continue.  */
++  return FALSE;
++}
++
++/* Set up GP section from local symbols with GPOFF relocations.  */
++
++static bfd_boolean
++elf_x86_setup_gp_from_local_symbol (void **slot, void *inf)
++{
++  struct elf_link_hash_entry *h
++    = (struct elf_link_hash_entry *) *slot;
++  struct bfd_link_info *info
++    = (struct bfd_link_info *) inf;
++
++  return elf_x86_setup_gp (h, info);
++}
++
++/* Set up GP section for __gp symbol.  */
++
++bfd_boolean
++_bfd_x86_elf_final_link (bfd *abfd, struct bfd_link_info *info)
++{
++  if (!bfd_link_relocatable (info))
++    {
++      struct elf_link_hash_entry *gp;
++      struct elf_x86_link_hash_table *htab;
++      const struct elf_backend_data *bed = get_elf_backend_data (abfd);
++
++      htab = elf_x86_hash_table (info, bed->target_id);
++      if (htab == NULL)
++      return FALSE;
++
++      gp = htab->gp;
++      if (gp != NULL
++        && gp->root.type != bfd_link_hash_defined
++        && gp->root.type != bfd_link_hash_defweak)
++      {
++        /* Set up __gp from a symbol with GPOFF relocations.  */
++        elf_link_hash_traverse (&htab->elf, elf_x86_setup_gp, info);
++
++        if (gp->root.type != bfd_link_hash_defined
++            && gp->root.type != bfd_link_hash_defweak)
++          {
++            /* Set up __gp from a local symbol with GPOFF
++               relocations.  */
++            htab_traverse (htab->loc_hash_table,
++                           elf_x86_setup_gp_from_local_symbol,
++                           info);
++          }
++      }
++    }
++
++  /* Invoke the regular ELF backend linker to do all the work.  */
++  return bfd_elf_final_link (abfd, info);
++}
diff --cc bfd/elfxx-x86.h
index 0000000000000000000000000000000000000000,e2a83ad830982ce0061cfbf195e1f200dba60b3e..a1697390f6587d87bc0ae5525645af071f0557b4
mode 000000,100644..100644
--- /dev/null
@@@ -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/format.c
Simple merge
diff --cc bfd/linker.c
index e4961d15f78b03aa9d9d767838e48893dc2ed231,dac21bd9e325a8cdf29860e2cc3e94c203c44bd7..7d4f47c14a98e9dc4ea49d6a8aecff6efc6ccee6
@@@ -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:
            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/opncls.c
Simple merge
diff --cc bfd/plugin.c
Simple merge
diff --cc bfd/plugin.h
Simple merge
diff --cc bfd/section.c
Simple merge
diff --cc bfd/syms.c
index 087252a1236244ef9e1a8ef63f98cd4fa8f843d4,7eafb7d17a9d51c2beb597bf39618d6b2cae1347..8720fb22f597fa4cfa6d32b21a796dc66ee0e8d2
@@@ -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/nm.c
Simple merge
Simple merge
index 09edabca56445af97ddc5b931e68392eec49b711,ae1cda9a7bd54b85f085d1bb48f8b2c4bf5f0d46..de7d065f7760a3134e407b06647e997a1f20368c
@@@ -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;
-       = elf_header.e_ident[EI_OSABI] != ELFOSABI_IRIX;
 +      /* Irix 5 and 6 are broken.  Object file symbol tables are not
 +       always sorted correctly such that local symbols precede global
 +       symbols, and the sh_info field in the symbol table is not
 +       always right.  */
 +      bfd_boolean check_corrupt_symtab
++      = filedata->file_header.e_ident[EI_OSABI] != ELFOSABI_IRIX;
  
-       for (i = 0, section = section_headers;
-          i < elf_header.e_shnum;
+       for (i = 0, section = filedata->section_headers;
+          i < filedata->file_header.e_shnum;
           i++, section++)
        {
          unsigned int si;
              print_vma (psym->st_value, LONG_HEX);
              putchar (' ');
              print_vma (psym->st_size, DEC_5);
-             printf (" %-7s", get_symbol_type (ELF_ST_TYPE (psym->st_info)));
+             printf (" %-7s", get_symbol_type (filedata, ELF_ST_TYPE (psym->st_info)));
 -            printf (" %-6s", get_symbol_binding (filedata, ELF_ST_BIND (psym->st_info)));
 +            if (check_corrupt_symtab
 +                && si < section->sh_info
 +                && ELF_ST_BIND (psym->st_info) != STB_LOCAL)
 +              printf (" %-6s", "<corrupt>");
 +            else
-               printf (" %-6s", get_symbol_binding (ELF_ST_BIND (psym->st_info)));
-             if (elf_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
++              printf (" %-6s", get_symbol_binding (filedata, ELF_ST_BIND (psym->st_info)));
+             if (filedata->file_header.e_ident[EI_OSABI] == ELFOSABI_SOLARIS)
                printf (" %-7s",  get_solaris_symbol_visibility (psym->st_other));
              else
                {
Simple merge
index 456be9e07cf805665a27f52e219c8275ebf00165,5cd83daf4b65caf13bb698adf89b789feb5bacc0..2829152bbfc4f2e48ef95f07e4f1c6d90bb528ba
@@@ -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 },
                    *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:
index f54924c4382de8507fd4ef0ef08382bac800da13,6e4f440c0940461cfb68d6cf76c8e2fcf13e9913..9e156996d3fd9139f17d3ccd391ef458835c605e
@@@ -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
Simple merge
diff --cc gas/symbols.c
Simple merge
diff --cc gas/symbols.h
Simple merge
index f7b1d80cad404f1bed2386d63d1331b3245f959a,0000000000000000000000000000000000000000..4229b195c37edd5f2adda4de3d2d4ecf912b287e
mode 100644,000000..100644
--- /dev/null
@@@ -1,2 -1,0 +1,2 @@@
- #error-output: common5.l
 +#name: secondary and common directives
++#error-output: common7.l
index 58d5142fc815846db6be3293528063da790c579f,0000000000000000000000000000000000000000..58d5142fc815846db6be3293528063da790c579f
mode 100644,000000..100644
--- /dev/null
index df8b7ed7f5c84a854cd8e32db470a6e56d9aa2b3,0000000000000000000000000000000000000000..df8b7ed7f5c84a854cd8e32db470a6e56d9aa2b3
mode 100644,000000..100644
--- /dev/null
index 629aecf601bfe25b78b6eb5ac9b3d10c4a23c5ac,bb10ac63bc023d9c403e41ae8236a49fdb950a7c..1bc7f29087e4d2b5085ae9a72303d5cb9936de1d
@@@ -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"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6a16ba49a7ff9f822bf0801841399b6235b7a0be
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4b786a24721578c331f843db9a900167974b755d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++      .text
++_start:
++      leal    foo@GPOFF, %eax
++      movl    %fs:foo@GPOFF, %eax
index 67a7a13b25950fd44f22ecb700ea24387e1a45e2,184c65ed8b28b526591e95c835c6e4e6b9da72ab..cc034dcfa608bdb229e8d73fbe4d5474f038069c
@@@ -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"
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3fad845e697ad6ceb3dedafadf3c71f7d655509c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..7556de3518e1177fad9d92fde9f12b0a9dea1d27
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2d5e8bd870ca586ae1cbe3104c80731faa9e9584
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ccd743506b05e5739594a985afe149293c584512
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++      .text
++_start:
++      leal    foo@GPOFF, %eax
++      movl    %gs:foo@GPOFF, %eax
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..e64a8bde1eeb91b7d9376b043b45621161d82c98
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8aacbf848079538bb0946739bd7bae65f45584bf
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
Simple merge
Simple merge
index 352e744f2330a7bc02ec069b0445f65c7120da39,9d80aa872a0f5250f661a9c989a1699c41d359b8..1914382aa20d00e8bef76f96793d98c473bbeb41
@@@ -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)
index 976f4fe347b513a629220446e998ae9e2fdd7d4a,8d364ab650ced6da970b348ffc2d0fd719f87a11..88120c90ce214ea0918a8e24f6f17bdbc44e6222
@@@ -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)
Simple merge
Simple merge
Simple merge
index 9bad2a28b6aedee520fbf8c9ea450426f3b1bbcd,4f33ad1f7b423717cb5e49993cff27bccc28fdd6..9db69fc0d8491ac1ee73f37d262ce31e4c149ea4
@@@ -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
  
index 4e405962b1da65ddcedcec89d065bd5af1b558e8,c0925fc9b970e66a6d014960e5a09919369d69ac..794212eb83e6c1a093441bc86ef279de34c4090c
@@@ -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.  */
  
Simple merge
Simple merge
index 0e3246fc83f55e1f6a2bd839ff34b6261a53c861,ec701857ae033ab0e40676e2886d1db0aaf20e20..732a0eb34edabac57030154887bac5281e065519
@@@ -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 5a68f88d775d7b426e62922269b8b067ee9f51c8,94d340caa653d57001b3b289da4333b961e24869..8bcc19255f0c7df84a4f5ada770c06e640db13ed
@@@ -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/ldexp.c
Simple merge
diff --cc ld/ldexp.h
Simple merge
diff --cc ld/ldfile.c
Simple merge
diff --cc ld/ldlang.c
index b7ff1d2e5fa4f3414997533af34e0ad684e1dc8e,1526d7b2ec163a0ca1a3ec9b6ef6e411ec2a33f1..4ea36626860f1cf5a983a45c1ee2189988685124
@@@ -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/ldlang.h
Simple merge
diff --cc ld/ldlex.h
Simple merge
diff --cc ld/ldmain.c
index 182291c59e9106ef166194910c50a3edd1487e48,c473ed24c3ed570fa418b3cbffe339d150be145a..43dca7872e32d16972bdae4361ef84be5cb0a304
@@@ -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/ldmain.h
Simple merge
diff --cc ld/lexsup.c
Simple merge
diff --cc ld/plugin.c
Simple merge
diff --cc ld/plugin.h
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5bb9bb5e673bfd11b598f47a0b6758ea6b5be5b1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..30b19cc8ba383d83713ec4f05649fdd2d44e1e7c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,78 @@@
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++
++extern int foo;
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++/* Structure passed to 'set_thread_area' syscall.  */
++struct user_desc
++{
++  unsigned int entry_number;
++  unsigned long int base_addr;
++  unsigned int limit;
++  unsigned int seg_32bit:1;
++  unsigned int contents:2;
++  unsigned int read_exec_only:1;
++  unsigned int limit_in_pages:1;
++  unsigned int seg_not_present:1;
++  unsigned int useable:1;
++  unsigned int empty:25;
++};
++
++/* Initializing bit fields is slow.  We speed it up by using a union.  */
++union user_desc_init
++{
++  struct user_desc desc;
++  unsigned int vals[4];
++};
++
++int
++setup_gp (void *p)
++{
++  union user_desc_init segdescr;
++  int result;
++
++  /* Let the kernel pick a value for the 'entry_number' field.  */
++  segdescr.vals[0] = -1;
++  /* The 'base_addr' field.  */
++  segdescr.vals[1] = (unsigned long int) p;
++  /* The 'limit' field.  We use 4GB which is 0xfffff pages.  */
++  segdescr.vals[2] = 0xfffff;
++  /* Collapsed value of the bitfield:
++     .seg_32bit = 1
++     .contents = 0
++     .read_exec_only = 0
++     .limit_in_pages = 1
++     .seg_not_present = 0
++     .useable = 1 */
++  segdescr.vals[3] = 0x51;
++  result = syscall (SYS_set_thread_area, &segdescr.desc);
++  if (result == 0)
++    /* We know the index in the GDT, now load the segment register.
++       The use of the GDT is described by the value 3 in the lower
++       three bits of the segment descriptor value.
++       Note that we have to do this even if the numeric value of
++       the descriptor does not change.  Loading the segment register
++       causes the segment information from the GDT to be loaded
++       which is necessary since we have changed it.   */
++    asm ("movw %w0, %%fs" :: "q" (segdescr.desc.entry_number * 8 + 3));
++
++  return result;
++}
++
++int
++main ()
++{
++  if (setup_gp (&__gp) == 0
++      && foo == 0x12345678
++      && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++      && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++      && get_foo () == 0x12345678)
++    printf ("PASS\n");
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..b54f316a8f9e505f1d05413cb6dff9eb1fd23e58
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2a4085a799012176ae2af2c46f94e8dd8e584b59
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,77 @@@
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++/* Structure passed to 'set_thread_area' syscall.  */
++struct user_desc
++{
++  unsigned int entry_number;
++  unsigned long int base_addr;
++  unsigned int limit;
++  unsigned int seg_32bit:1;
++  unsigned int contents:2;
++  unsigned int read_exec_only:1;
++  unsigned int limit_in_pages:1;
++  unsigned int seg_not_present:1;
++  unsigned int useable:1;
++  unsigned int empty:25;
++};
++
++/* Initializing bit fields is slow.  We speed it up by using a union.  */
++union user_desc_init
++{
++  struct user_desc desc;
++  unsigned int vals[4];
++};
++
++int
++setup_gp (void *p)
++{
++  union user_desc_init segdescr;
++  int result;
++
++  /* Let the kernel pick a value for the 'entry_number' field.  */
++  segdescr.vals[0] = -1;
++  /* The 'base_addr' field.  */
++  segdescr.vals[1] = (unsigned long int) p;
++  /* The 'limit' field.  We use 4GB which is 0xfffff pages.  */
++  segdescr.vals[2] = 0xfffff;
++  /* Collapsed value of the bitfield:
++     .seg_32bit = 1
++     .contents = 0
++     .read_exec_only = 0
++     .limit_in_pages = 1
++     .seg_not_present = 0
++     .useable = 1 */
++  segdescr.vals[3] = 0x51;
++  result = syscall (SYS_set_thread_area, &segdescr.desc);
++  if (result == 0)
++    /* We know the index in the GDT, now load the segment register.
++       The use of the GDT is described by the value 3 in the lower
++       three bits of the segment descriptor value.
++       Note that we have to do this even if the numeric value of
++       the descriptor does not change.  Loading the segment register
++       causes the segment information from the GDT to be loaded
++       which is necessary since we have changed it.   */
++    asm ("movw %w0, %%fs" :: "q" (segdescr.desc.entry_number * 8 + 3));
++
++  return result;
++}
++
++
++int
++main ()
++{
++  if (setup_gp (&__gp) == 0
++      && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++      && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++      && get_foo () == 0x12345678)
++    printf ("PASS\n");
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0952484cf9c5b255cda729f8bf36c6c55e52969c
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++#as: --32
++#ld: -melf_i386
++#error: undefined reference to `foo'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..91f9bf8b027f786574f2fb6e2c06eb46a1a41fda
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++      .text
++      .globl _start
++_start:
++      movl    %fs:foo@GPOFF, %eax
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f87739cb76c0ce7722671e9214b6af422478e352
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f138afd0899ff57d79ddf442302419c535057c80
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0df1ca23e5357bb574ee02b6d7d230a625ece940
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..4a9c8d1c937fbf5138d8412b2ab3d89aff227ae7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..00bdf7ad1ac009a6e31fd0740a5a42c9a986fb18
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..592c0ff3c38216c78147449ca8191f642c5ad14a
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++      .text
++      .globl _start
++_start:
++      ret
++
++      .section .text.bar,"ax",@progbits
++      .globl bar
++bar:
++      movl    %fs:foo@GPOFF, %eax
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ecabaf159fc7efa2a174aef4734aca029ea52098
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f5b220aa8a671f26c93362c162ae26c1ce1a9557
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++      .text
++      .globl _start
++_start:
++      movl    %fs:foo@GPOFF, %eax
++
++      .data
++      .globl foo
++foo:
++      .long 0x12345678
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cffe6f06f5535839a3f3e7a9255a4ab6b2b833a2
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1f016440de3677c1e4d3302b4bf722d88db5b7dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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/ : { *(.*) }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..94128c955e4476e13698cc22311eab92d2b513c4
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..9969de39fa3d81e9235f93fb379b3696df929be1
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c11ab46bb4b52a7b97ce55f1cc230ec50700d741
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..64d9fcf6c4264ebca1ccd0ad3b3726d36e9088c3
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..bfa25a28449be80a4af0b61b0df83cdb7ea80f35
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6cc4a52403efb93ea990334eae593bf703305329
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 6c53046193ae75f9ecee484900bc7e05838d1fcc,c4fc545dbb64da022b87b6d4f6cd72ef7f0886f7..03727c24cf7615f0e78549752b0a3809524ca53f
@@@ -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*"]
Simple merge
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..24e9e649d92184ed742703d0817f0232c788fc06
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..185f265b070d7378c90711acf1bcf2b6ae96a94b
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,29 @@@
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++#include <asm/prctl.h>
++
++extern int foo;
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++int
++setup_gp (void *p)
++{
++  return syscall (SYS_arch_prctl, ARCH_SET_GS, p);
++}
++
++int
++main ()
++{
++  if (setup_gp (&__gp) == 0
++      && foo == 0x12345678
++      && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++      && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++      && get_foo () == 0x12345678)
++    printf ("PASS\n");
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..cb9f26755a7b82289399377ccf631b433536708d
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..8b91e22721efa35f880c7f92601e7348faa9ea66
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,27 @@@
++#define _GNU_SOURCE
++#include <stdio.h>
++#include <unistd.h>
++#include <syscall.h>
++#include <asm/prctl.h>
++
++extern int __gp;
++extern int foo_gpoff;
++extern int get_foo (void);
++extern int get_foo_gpoff (void);
++
++int
++setup_gp (void *p)
++{
++  return syscall (SYS_arch_prctl, ARCH_SET_GS, p);
++}
++
++int
++main ()
++{
++  if (setup_gp (&__gp) == 0
++      && *(int *) ((char *) &__gp + foo_gpoff) == 0x12345678
++      && *(int *) ((char *) &__gp + get_foo_gpoff ()) == 0x12345678
++      && get_foo () == 0x12345678)
++    printf ("PASS\n");
++  return 0;
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..c8a5e6f9834b4e37967ff96ce50d2059b4e39ec5
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,3 @@@
++#as: --64
++#ld: -melf_x86_64
++#error: undefined reference to `foo'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..0513d3e4d381e78b8c223364f96012fdaabe65ed
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,4 @@@
++      .text
++      .globl _start
++_start:
++      movl    %gs:foo@GPOFF, %eax
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f8fee15d1d57624b441a3e66fedca76ea2e4f10e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..5f6cd4116ec0a0b64c50e69d66dcfbcfc7240e32
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..ddc8498cff8b4e9d6ee9696d0c65cb31ec2c6883
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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'
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3a8e6781fc47df5ee8d845f879ba36ba0d7a6557
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1676b0d3ef2fee7a730e3f8454608cc1d0bfc503
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..3e632f3cea189405d7ebc3aa45e93513aa2bcb5e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++      .text
++      .globl _start
++_start:
++      ret
++
++      .section .text.bar,"ax",@progbits
++      .globl bar
++bar:
++      movl    %gs:foo@GPOFF, %eax
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a6ebefa2652128c47c6c3f8a1ca19291c6432892
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..a900661811d6483be2a81faa0d9647a5a2abb1e0
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -1,0 -1,0 +1,9 @@@
++      .text
++      .globl _start
++_start:
++      movl    %gs:foo@GPOFF, %eax
++
++      .data
++      .globl foo
++foo:
++      .long 0x12345678
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..948bafc498d3f7bd1cb6ee50f98b3af707986c2f
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1f016440de3677c1e4d3302b4bf722d88db5b7dd
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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/ : { *(.*) }
++}
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..6c1640d34c220e4c20601e5b5e78d8e44236f23e
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..f163bc1d383ff6d0c04bf5ecca17026c4dcc4ef7
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..d5478bbb2a43e556c40a48c9524b3c61f540ab48
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..2e0a330396c35f7f54ead267b785aa432e987480
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..1a1c9d907ed9899e82c49a6c8ba0f300e9903cea
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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]+ <foo>
++#pass
index 0000000000000000000000000000000000000000,0000000000000000000000000000000000000000..fb8c3375b8d03ad32588673b0829183856c77b63
new file mode 100644 (file)
--- /dev/null
--- /dev/null
@@@ -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]+ <foo>
++#pass
index 9b83e1c5ce8d84fd111ec40c9a7b60371f53ef43,e7f338ee11ee58575c49644aea5adc5907088b14..7bf7370d6fe9239136fb1e077265bc355d34004e
@@@ -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"