diff mbox

PING: [PATCH] X86: Optimize access to globals in PIE with copy reloc

Message ID CAMe9rOotQUoXkO67fPddxo0PUK+-q9ZZTKTEceCf7mZL9-v7xA@mail.gmail.com
State New
Headers show

Commit Message

H.J. Lu Oct. 19, 2015, 8:04 p.m. UTC
PING.


---------- Forwarded message ----------
From: H.J. Lu <hjl.tools@gmail.com>
Date: Wed, Jul 1, 2015 at 5:11 AM
Subject: [PATCH] X86: Optimize access to globals in PIE with copy reloc
To: gcc-patches@gcc.gnu.org


Normally, with PIE, GCC accesses globals that are extern to the module
using GOT.  This is two instructions, one to get the address of the global
from GOT and the other to get the value.  Examples:

---
extern int a_glob;
int
main ()
{
  return a_glob;
}
---

With PIE, the generated code accesses global via GOT using two memory
loads:

        movq    a_glob@GOTPCREL(%rip), %rax
        movl    (%rax), %eax

for 64-bit or

        movl    a_glob@GOT(%ecx), %eax
        movl    (%eax), %eax

for 32-bit.

Some experiments on google and SPEC CPU benchmarks show that the extra
instruction affects performance by 1% to 5%.

Solution - Copy Relocations:

When the linker supports copy relocations, GCC can always assume that
the global will be defined in the executable.  For globals that are
truly extern (come from shared objects), the linker will create copy
relocations and have them defined in the executable.  Result is that
no global access needs to go through GOT and hence improves performance.
We can generate

        movl    a_glob(%rip), %eax

for 64-bit and

        movl    a_glob@GOTOFF(%eax), %eax

for 32-bit.  This optimization only applies to undefined non-weak
non-TLS global data.  Undefined weak global or TLS data access still
must go through GOT.

This patch reverts legitimate_pic_address_disp_p change made in revision
218397, which only applies to x86-64.  Instead, this patch updates
targetm.binds_local_p to indicate if undefined non-weak non-TLS global
data is defined locally in PIE.  It also introduces a new target hook,
binds_tls_local_p to distinguish TLS variable from non-TLS variable.  By
default, binds_tls_local_p is the same as binds_local_p which assumes
TLS variable.

This patch checks if 32-bit and 64-bit linkers support PIE with copy
reloc at configure time.  64-bit linker is enabled in binutils 2.25
and 32-bit linker is enabled in binutils 2.26.  This optimization
is enabled only if the linker support is available.

Since copy relocation in PIE is incompatible with DSO created by
-Wl,-Bsymbolic, this patch also adds a new option, -fsymbolic, which
controls how references to global symbols are bound.  The -fsymbolic
option binds references to global symbols to the local definitions
and external references globally.  It avoids copy relocations in PIE
and optimizes global symbol references in shared library created
by -Wl,-Bsymbolic.

gcc/

        PR target/65846
        PR target/65886
        * configure.ac (HAVE_LD_PIE_COPYRELOC): Renamed to ...
        (HAVE_LD_X86_64_PIE_COPYRELOC): This.
        (HAVE_LD_386_PIE_COPYRELOC): New.   Defined to 1 if Linux/ia32
        linker supports PIE with copy reloc.
        * output.h (default_binds_tls_local_p): New.
        (default_binds_local_p_3): Add 2 bool arguments.
        * target.def (binds_tls_local_p): New target hook.
        * varasm.c (decl_default_tls_model): Replace targetm.binds_local_p
        with targetm.binds_tls_local_p.
        (default_binds_local_p_3): Add a bool argument to indicate TLS
        variable and a bool argument to indicate if an undefined non-TLS
        non-weak data is local.  Double check TLS variable.  If an
        undefined non-TLS non-weak data is local, treat it as defined
        locally.
        (default_binds_local_p): Pass true and false to
        default_binds_local_p_3.
        (default_binds_local_p_2): Likewise.
        (default_binds_local_p_1): Likewise.
        (default_binds_tls_local_p): New.
        * config.in: Regenerated.
        * configure: Likewise.
        * doc/tm.texi: Likewise.
        * config/i386/i386.c (legitimate_pic_address_disp_p): Don't
        check HAVE_LD_PIE_COPYRELOC here.
        (ix86_binds_local): New.
        (ix86_binds_tls_local_p): Likewise.
        (ix86_binds_local_p): Use it.
        (TARGET_BINDS_TLS_LOCAL_P): New.
        * doc/tm.texi.in (TARGET_BINDS_TLS_LOCAL_P): New hook.

gcc/testsuite/

        PR target/65846
        PR target/65886
        * gcc.target/i386/pie-copyrelocs-1.c: Updated for ia32.
        * gcc.target/i386/pie-copyrelocs-2.c: Likewise.
        * gcc.target/i386/pie-copyrelocs-3.c: Likewise.
        * gcc.target/i386/pie-copyrelocs-4.c: Likewise.
        * gcc.target/i386/pr32219-9.c: Likewise.
        * gcc.target/i386/pr32219-10.c: New file.
        * gcc.target/i386/pr65886-1.c: Likewise.
        * gcc.target/i386/pr65886-2.c: Likewise.
        * gcc.target/i386/pr65886-3.c: Likewise.
        * gcc.target/i386/pr65886-4.c: Likewise.
        * gcc.target/i386/pr65886-4.c: Likewise.
        * gcc.target/i386/pr65886-5.c: Likewise.

        * lib/target-supports.exp (check_effective_target_pie_copyreloc):
        Check HAVE_LD_X86_64_PIE_COPYRELOC and HAVE_LD_386_PIE_COPYRELOC
        instead of HAVE_LD_X86_64_PIE_COPYRELOC.
---
 gcc/common.opt                                   |  4 ++
 gcc/config.in                                    | 18 ++++---
 gcc/config/i386/i386.c                           | 44 ++++++++++-----
 gcc/configure                                    | 68 ++++++++++++++++++++---
 gcc/configure.ac                                 | 64 +++++++++++++++++++---
 gcc/doc/invoke.texi                              | 12 ++++-
 gcc/doc/tm.texi                                  | 10 ++++
 gcc/doc/tm.texi.in                               |  2 +
 gcc/output.h                                     |  4 +-
 gcc/target.def                                   | 14 +++++
 gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c |  4 +-
 gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c |  4 +-
 gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c |  2 +-
 gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c |  4 +-
 gcc/testsuite/gcc.target/i386/pr32219-10.c       | 16 ++++++
 gcc/testsuite/gcc.target/i386/pr32219-9.c        |  2 +
 gcc/testsuite/gcc.target/i386/pr65886-1.c        | 17 ++++++
 gcc/testsuite/gcc.target/i386/pr65886-2.c        | 15 ++++++
 gcc/testsuite/gcc.target/i386/pr65886-3.c        | 16 ++++++
 gcc/testsuite/gcc.target/i386/pr65886-4.c        | 15 ++++++
 gcc/testsuite/gcc.target/i386/pr65886-5.c        | 18 +++++++
 gcc/testsuite/gcc.target/i386/pr65886-6.c        | 18 +++++++
 gcc/testsuite/lib/target-supports.exp            | 10 +++-
 gcc/varasm.c                                     | 69 ++++++++++++++++++------
 24 files changed, 393 insertions(+), 57 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr32219-10.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65886-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65886-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65886-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65886-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65886-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65886-6.c

   if (!DECL_P (exp))
@@ -6851,10 +6855,33 @@ default_binds_local_p_3 (const_tree exp, bool
shlib, bool weak_dominate,
                              || (!in_lto_p
                                  && DECL_INITIAL (exp) == error_mark_node)));

-  /* A non-external variable is defined locally only if it isn't
-     uninitialized COMMON variable or common_local_p is true.  */
-  bool defined_locally = (!DECL_EXTERNAL (exp)
-                         && (!uninited_common || common_local_p));
+  /* If TLS is false, double check if EXP is a TLS variable.  */
+  if (!tls)
+    tls = TREE_CODE (exp) == VAR_DECL && DECL_THREAD_LOCAL_P (exp);
+
+  bool defined_locally;
+  if (!DECL_EXTERNAL (exp))
+    {
+      /* When a variable isn't defined externally, it is defined locally
+        only if it isn't uninitialized COMMON variable or common_local_p
+        is true and external references aren't bound globally.  */
+      defined_locally = (!uninited_common
+                        || (common_local_p && !flag_symbolic));
+
+    }
+  else
+    {
+      /* An undefined variable is defined locally only if it is a
+        non-TLS non-weak data variable, external references aren't
+        bound globally and linker can guarantee it will be defined
+        locally.  */
+      defined_locally = (!tls
+                        && data_local_p
+                        && !flag_symbolic
+                        && TREE_CODE (exp) != FUNCTION_DECL
+                        && !DECL_WEAK (exp));
+    }
+
   if (symtab_node *node = symtab_node::get (exp))
     {
       if (node->in_other_partition)
@@ -6864,7 +6891,7 @@ default_binds_local_p_3 (const_tree exp, bool
shlib, bool weak_dominate,
       else if (resolution_local_p (node->resolution))
        resolved_locally = true;
     }
-  if (defined_locally && weak_dominate && !shlib)
+  if (defined_locally && weak_dominate && (!shlib || flag_symbolic))
     resolved_locally = true;

   /* Undefined weak symbols are never defined locally.  */
@@ -6877,13 +6904,15 @@ default_binds_local_p_3 (const_tree exp, bool
shlib, bool weak_dominate,
   if (DECL_VISIBILITY (exp) != VISIBILITY_DEFAULT
       && (TREE_CODE (exp) == FUNCTION_DECL
          || !extern_protected_data
+         || flag_symbolic
          || DECL_VISIBILITY (exp) != VISIBILITY_PROTECTED)
       && (DECL_VISIBILITY_SPECIFIED (exp) || defined_locally))
     return true;

   /* If PIC, then assume that any global name can be overridden by
-     symbols resolved from other modules.  */
-  if (shlib)
+     symbols resolved from other modules unless global references are
+     bound locally  */
+  if (shlib && !flag_symbolic)
     return false;

   /* Variables defined outside this object might not be local.  */
@@ -6910,7 +6939,16 @@ default_binds_local_p_3 (const_tree exp, bool
shlib, bool weak_dominate,
 bool
 default_binds_local_p (const_tree exp)
 {
-  return default_binds_local_p_3 (exp, flag_shlib != 0, true, false, false);
+  return default_binds_local_p_3 (exp, true, flag_shlib != 0, true,
+                                 false, false, false);
+}
+
+/* Similar to default_binds_local_p, but for TLS variable.  */
+
+bool
+default_binds_tls_local_p (const_tree exp)
+{
+  return targetm.binds_local_p (exp);
 }

 /* Similar to default_binds_local_p, but common symbol may be local.  */
@@ -6918,14 +6956,15 @@ default_binds_local_p (const_tree exp)
 bool
 default_binds_local_p_2 (const_tree exp)
 {
-  return default_binds_local_p_3 (exp, flag_shlib != 0, true, false,
-                                 !flag_pic);
+  return default_binds_local_p_3 (exp, true, flag_shlib != 0, true,
+                                 false, !flag_pic, false);
 }

 bool
 default_binds_local_p_1 (const_tree exp, int shlib)
 {
-  return default_binds_local_p_3 (exp, shlib != 0, false, false, false);
+  return default_binds_local_p_3 (exp, true, shlib != 0, false, false,
+                                 false, false);
 }

 /* Return true when references to DECL must bind to current definition in
--
2.4.3
diff mbox

Patch

diff --git a/gcc/common.opt b/gcc/common.opt
index dd49ae3..2f7289d 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -998,6 +998,10 @@  fcommon
 Common Report Var(flag_no_common,0)
 Do not put uninitialized globals in the common section

+fsymbolic
+Common Report Var(flag_symbolic)
+Bind global references locally and external references globally
+
 fcompare-debug
 Driver
 ; Converted by the driver to -fcompare-debug= options.
diff --git a/gcc/config.in b/gcc/config.in
index b031a62..b48ebbb 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1362,6 +1362,12 @@ 
 #endif


+/* Define 0/1 if your 386 linker supports -pie option with copy reloc. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LD_386_PIE_COPYRELOC
+#endif
+
+
 /* Define if your linker supports --as-needed/--no-as-needed or equivalent
    options. */
 #ifndef USED_FOR_TARGET
@@ -1442,12 +1448,6 @@ 
 #endif


-/* Define 0/1 if your linker supports -pie option with copy reloc. */
-#ifndef USED_FOR_TARGET
-#undef HAVE_LD_PIE_COPYRELOC
-#endif
-
-
 /* Define if your linker links a mix of read-only and read-write sections into
    a read-write section. */
 #ifndef USED_FOR_TARGET
@@ -1473,6 +1473,12 @@ 
 #endif


+/* Define 0/1 if your x86-64 linker supports -pie option with copy reloc. */
+#ifndef USED_FOR_TARGET
+#undef HAVE_LD_X86_64_PIE_COPYRELOC
+#endif
+
+
 /* Define to 1 if you have the <limits.h> header file. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_LIMITS_H
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 7d26e8c..117a653 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -13361,11 +13361,7 @@  legitimate_pic_address_disp_p (rtx disp)
                return true;
            }
          else if (!SYMBOL_REF_FAR_ADDR_P (op0)
-                  && (SYMBOL_REF_LOCAL_P (op0)
-                      || (HAVE_LD_PIE_COPYRELOC
-                          && flag_pie
-                          && !SYMBOL_REF_WEAK (op0)
-                          && !SYMBOL_REF_FUNCTION_P (op0)))
+                  && SYMBOL_REF_LOCAL_P (op0)
                   && ix86_cmodel != CM_LARGE_PIC)
            return true;
          break;
@@ -52036,17 +52032,39 @@  ix86_initialize_bounds (tree var, tree lb,
tree ub, tree *stmts)
 }

 #if !TARGET_MACHO && !TARGET_DLLIMPORT_DECL_ATTRIBUTES
-/* For i386, common symbol is local only for non-PIE binaries.  For
-   x86-64, common symbol is local only for non-PIE binaries or linker
-   supports copy reloc in PIE binaries.   */
+/* Common and undefined non-TLS non-weak data symbols are local only
+   for non-PIE executables or linker supports copy reloc in PIE
+   executables.  */
+
+static bool
+ix86_binds_local (const_tree exp, bool tls)
+{
+  bool local_p;
+  if (!flag_pic)
+    local_p = true;
+  else if (flag_pie)
+    {
+      if (TARGET_64BIT)
+       local_p = HAVE_LD_X86_64_PIE_COPYRELOC != 0;
+      else
+       local_p = HAVE_LD_386_PIE_COPYRELOC != 0;
+    }
+  else
+    local_p = false;
+  return default_binds_local_p_3 (exp, tls, flag_shlib != 0, true,
+                                 true, local_p, local_p);
+}

 static bool
 ix86_binds_local_p (const_tree exp)
 {
-  return default_binds_local_p_3 (exp, flag_shlib != 0, true, true,
-                                 (!flag_pic
-                                  || (TARGET_64BIT
-                                      && HAVE_LD_PIE_COPYRELOC != 0)));
+  return ix86_binds_local (exp, false);
+}
+
+static bool
+ix86_binds_tls_local_p (const_tree exp)
+{
+  return ix86_binds_local (exp, true);
 }
 #endif

@@ -52271,6 +52289,8 @@  ix86_operands_ok_for_move_multiple (rtx
*operands, bool load,
 #else
 #undef TARGET_BINDS_LOCAL_P
 #define TARGET_BINDS_LOCAL_P ix86_binds_local_p
+#undef TARGET_BINDS_TLS_LOCAL_P
+#define TARGET_BINDS_TLS_LOCAL_P ix86_binds_tls_local_p
 #endif
 #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
 #undef TARGET_BINDS_LOCAL_P
diff --git a/gcc/configure b/gcc/configure
index 9561e5c..3d145d5 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -27329,13 +27329,13 @@  fi
 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_ld_pie" >&5
 $as_echo "$gcc_cv_ld_pie" >&6; }

-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking linker PIE support
with copy reloc" >&5
-$as_echo_n "checking linker PIE support with copy reloc... " >&6; }
-gcc_cv_ld_pie_copyreloc=no
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking x86-64 linker PIE
support with copy reloc" >&5
+$as_echo_n "checking x86-64 linker PIE support with copy reloc... " >&6; }
+gcc_cv_ld_x86_64_pie_copyreloc=no
 if test $gcc_cv_ld_pie = yes ; then
   if test $in_tree_ld = yes ; then
     if test "$gcc_cv_gld_major_version" -eq 2 -a
"$gcc_cv_gld_minor_version" -ge 25 -o "$gcc_cv_gld_major_version" -gt
2; then
-      gcc_cv_ld_pie_copyreloc=yes
+      gcc_cv_ld_x86_64_pie_copyreloc=yes
     fi
   elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then
     # Check if linker supports -pie option with copy reloc
@@ -27366,7 +27366,7 @@  EOF
          && $gcc_cv_ld -shared -melf_x86_64 -o conftest1.so
conftest1.o > /dev/null 2>&1 \
          && $gcc_cv_as --64 -o conftest2.o conftest2.s > /dev/null 2>&1 \
          && $gcc_cv_ld -pie -melf_x86_64 -o conftest conftest2.o
conftest1.so > /dev/null 2>&1; then
-        gcc_cv_ld_pie_copyreloc=yes
+        gcc_cv_ld_x86_64_pie_copyreloc=yes
       fi
       rm -f conftest conftest1.so conftest1.o conftest2.o conftest1.s
conftest2.s
       ;;
@@ -27375,11 +27375,63 @@  EOF
 fi

 cat >>confdefs.h <<_ACEOF
-#define HAVE_LD_PIE_COPYRELOC `if test x"$gcc_cv_ld_pie_copyreloc" =
xyes; then echo 1; else echo 0; fi`
+#define HAVE_LD_X86_64_PIE_COPYRELOC `if test
x"$gcc_cv_ld_x86_64_pie_copyreloc" = xyes; then echo 1; else echo 0;
fi`
 _ACEOF

-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_ld_pie_copyreloc" >&5
-$as_echo "$gcc_cv_ld_pie_copyreloc" >&6; }
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result:
$gcc_cv_ld_x86_64_pie_copyreloc" >&5
+$as_echo "$gcc_cv_ld_x86_64_pie_copyreloc" >&6; }
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking 386 linker PIE
support with copy reloc" >&5
+$as_echo_n "checking 386 linker PIE support with copy reloc... " >&6; }
+gcc_cv_ld_386_pie_copyreloc=no
+if test $gcc_cv_ld_pie = yes ; then
+  if test $in_tree_ld = yes ; then
+    if test "$gcc_cv_gld_major_version" -eq 2 -a
"$gcc_cv_gld_minor_version" -ge 26 -o "$gcc_cv_gld_major_version" -gt
2; then
+      gcc_cv_ld_386_pie_copyreloc=yes
+    fi
+  elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then
+    # Check if linker supports -pie option with copy reloc
+    case "$target" in
+    i?86-*-linux* | x86_64-*-linux*)
+      cat > conftest1.s <<EOF
+       .globl  a_glob
+       .data
+       .type   a_glob, @object
+       .size   a_glob, 4
+a_glob:
+       .long   2
+EOF
+      cat > conftest2.s <<EOF
+       .text
+       .globl  main
+       .type   main, @function
+main:
+       movl    a_glob@GOTOFF(%ebx), %eax
+       .size   main, .-main
+       .globl  ptr
+       .section        .data.rel,"aw",@progbits
+       .type   ptr, @object
+ptr:
+       .long   a_glob
+EOF
+      if $gcc_cv_as --32 -o conftest1.o conftest1.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -shared -melf_i386 -o conftest1.so conftest1.o
> /dev/null 2>&1 \
+         && $gcc_cv_as --32 -o conftest2.o conftest2.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -pie -melf_i386 -o conftest conftest2.o
conftest1.so > /dev/null 2>&1; then
+        gcc_cv_ld_386_pie_copyreloc=yes
+      fi
+      rm -f conftest conftest1.so conftest1.o conftest2.o conftest1.s
conftest2.s
+      ;;
+    esac
+  fi
+fi
+
+cat >>confdefs.h <<_ACEOF
+#define HAVE_LD_386_PIE_COPYRELOC `if test
x"$gcc_cv_ld_386_pie_copyreloc" = xyes; then echo 1; else echo 0; fi`
+_ACEOF
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result:
$gcc_cv_ld_386_pie_copyreloc" >&5
+$as_echo "$gcc_cv_ld_386_pie_copyreloc" >&6; }

 { $as_echo "$as_me:${as_lineno-$LINENO}: checking linker
EH-compatible garbage collection of sections" >&5
 $as_echo_n "checking linker EH-compatible garbage collection of
sections... " >&6; }
diff --git a/gcc/configure.ac b/gcc/configure.ac
index cb14639..1a57de8 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -4735,12 +4735,12 @@  if test x"$gcc_cv_ld_pie" = xyes; then
 fi
 AC_MSG_RESULT($gcc_cv_ld_pie)

-AC_MSG_CHECKING(linker PIE support with copy reloc)
-gcc_cv_ld_pie_copyreloc=no
+AC_MSG_CHECKING(x86-64 linker PIE support with copy reloc)
+gcc_cv_ld_x86_64_pie_copyreloc=no
 if test $gcc_cv_ld_pie = yes ; then
   if test $in_tree_ld = yes ; then
     if test "$gcc_cv_gld_major_version" -eq 2 -a
"$gcc_cv_gld_minor_version" -ge 25 -o "$gcc_cv_gld_major_version" -gt
2; then
-      gcc_cv_ld_pie_copyreloc=yes
+      gcc_cv_ld_x86_64_pie_copyreloc=yes
     fi
   elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then
     # Check if linker supports -pie option with copy reloc
@@ -4771,17 +4771,65 @@  EOF
          && $gcc_cv_ld -shared -melf_x86_64 -o conftest1.so
conftest1.o > /dev/null 2>&1 \
          && $gcc_cv_as --64 -o conftest2.o conftest2.s > /dev/null 2>&1 \
          && $gcc_cv_ld -pie -melf_x86_64 -o conftest conftest2.o
conftest1.so > /dev/null 2>&1; then
-        gcc_cv_ld_pie_copyreloc=yes
+        gcc_cv_ld_x86_64_pie_copyreloc=yes
       fi
       rm -f conftest conftest1.so conftest1.o conftest2.o conftest1.s
conftest2.s
       ;;
     esac
   fi
 fi
-AC_DEFINE_UNQUOTED(HAVE_LD_PIE_COPYRELOC,
-  [`if test x"$gcc_cv_ld_pie_copyreloc" = xyes; then echo 1; else echo 0; fi`],
-  [Define 0/1 if your linker supports -pie option with copy reloc.])
-AC_MSG_RESULT($gcc_cv_ld_pie_copyreloc)
+AC_DEFINE_UNQUOTED(HAVE_LD_X86_64_PIE_COPYRELOC,
+  [`if test x"$gcc_cv_ld_x86_64_pie_copyreloc" = xyes; then echo 1;
else echo 0; fi`],
+  [Define 0/1 if your x86-64 linker supports -pie option with copy reloc.])
+AC_MSG_RESULT($gcc_cv_ld_x86_64_pie_copyreloc)
+
+AC_MSG_CHECKING(386 linker PIE support with copy reloc)
+gcc_cv_ld_386_pie_copyreloc=no
+if test $gcc_cv_ld_pie = yes ; then
+  if test $in_tree_ld = yes ; then
+    if test "$gcc_cv_gld_major_version" -eq 2 -a
"$gcc_cv_gld_minor_version" -ge 26 -o "$gcc_cv_gld_major_version" -gt
2; then
+      gcc_cv_ld_386_pie_copyreloc=yes
+    fi
+  elif test x$gcc_cv_as != x -a x$gcc_cv_ld != x ; then
+    # Check if linker supports -pie option with copy reloc
+    case "$target" in
+    i?86-*-linux* | x86_64-*-linux*)
+      cat > conftest1.s <<EOF
+       .globl  a_glob
+       .data
+       .type   a_glob, @object
+       .size   a_glob, 4
+a_glob:
+       .long   2
+EOF
+      cat > conftest2.s <<EOF
+       .text
+       .globl  main
+       .type   main, @function
+main:
+       movl    a_glob@GOTOFF(%ebx), %eax
+       .size   main, .-main
+       .globl  ptr
+       .section        .data.rel,"aw",@progbits
+       .type   ptr, @object
+ptr:
+       .long   a_glob
+EOF
+      if $gcc_cv_as --32 -o conftest1.o conftest1.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -shared -melf_i386 -o conftest1.so conftest1.o
> /dev/null 2>&1 \
+         && $gcc_cv_as --32 -o conftest2.o conftest2.s > /dev/null 2>&1 \
+         && $gcc_cv_ld -pie -melf_i386 -o conftest conftest2.o
conftest1.so > /dev/null 2>&1; then
+        gcc_cv_ld_386_pie_copyreloc=yes
+      fi
+      rm -f conftest conftest1.so conftest1.o conftest2.o conftest1.s
conftest2.s
+      ;;
+    esac
+  fi
+fi
+AC_DEFINE_UNQUOTED(HAVE_LD_386_PIE_COPYRELOC,
+  [`if test x"$gcc_cv_ld_386_pie_copyreloc" = xyes; then echo 1; else
echo 0; fi`],
+  [Define 0/1 if your 386 linker supports -pie option with copy reloc.])
+AC_MSG_RESULT($gcc_cv_ld_386_pie_copyreloc)

 AC_MSG_CHECKING(linker EH-compatible garbage collection of sections)
 gcc_cv_ld_eh_gc_sections=no
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 0413106..c47b157a 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1131,7 +1131,7 @@  See S/390 and zSeries Options.
 -finhibit-size-directive  -finstrument-functions @gol
 -finstrument-functions-exclude-function-list=@var{sym},@var{sym},@dots{} @gol
 -finstrument-functions-exclude-file-list=@var{file},@var{file},@dots{} @gol
--fno-common  -fno-ident @gol
+-fno-common  -fsymbolic  -fno-ident @gol
 -fpcc-struct-return  -fpic  -fPIC -fpie -fPIE -fno-plt @gol
 -fno-jump-tables @gol
 -frecord-gcc-switches @gol
@@ -23819,6 +23819,16 @@  it provides better performance, or if you
wish to verify that the
 program will work on other systems that always treat uninitialized
 variable declarations this way.

+@item -fsymbolic
+@opindex -fsymbolic
+This option is similar to ELF linker option, @option{-Bsymbolic},
+which controls how references to global symbols are bound.  The
+@option{-fsymbolic} option binds references to global symbols to the
+local definitions and external references globally.  This option
+avoids copy relocations in position-independent executables and
+optimizes global symbol references in shared library created by
+linker option, @option{-Bsymbolic}.
+
 @item -fno-ident
 @opindex fno-ident
 Ignore the @code{#ident} directive.
diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index a16cd92..517a213 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -7222,6 +7222,16 @@  for ELF, which has a looser model of global
name binding than other
 currently supported object file formats.
 @end deftypefn

+@deftypefn {Target Hook} bool TARGET_BINDS_TLS_LOCAL_P (const_tree @var{exp})
+Returns true if TLS variable @var{exp} names an object for which name
+resolution rules must resolve to the current ``module'' (dynamic shared
+library or executable image).
+
+The default version of this hook implements the name resolution rules
+for ELF, which has a looser model of global name binding than other
+currently supported object file formats.
+@end deftypefn
+
 @deftypevr {Target Hook} bool TARGET_HAVE_TLS
 Contains the value true if the target supports thread-local storage.
 The default value is false.
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 93fb41c..5d80b1d 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -5032,6 +5032,8 @@  it is unlikely to be called.

 @hook TARGET_BINDS_LOCAL_P

+@hook TARGET_BINDS_TLS_LOCAL_P
+
 @hook TARGET_HAVE_TLS


diff --git a/gcc/output.h b/gcc/output.h
index 4ce6eea..865e03c 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -583,9 +583,11 @@  extern const char *default_strip_name_encoding
(const char *);
 extern void default_asm_output_anchor (rtx);
 extern bool default_use_anchors_for_symbol_p (const_rtx);
 extern bool default_binds_local_p (const_tree);
+extern bool default_binds_tls_local_p (const_tree);
 extern bool default_binds_local_p_1 (const_tree, int);
 extern bool default_binds_local_p_2 (const_tree);
-extern bool default_binds_local_p_3 (const_tree, bool, bool, bool, bool);
+extern bool default_binds_local_p_3 (const_tree, bool, bool, bool, bool,
+                                    bool, bool);
 extern void default_globalize_label (FILE *, const char *);
 extern void default_globalize_decl_name (FILE *, tree);
 extern void default_emit_unwind_label (FILE *, tree, int, int);
diff --git a/gcc/target.def b/gcc/target.def
index dabb1f2..15a1364 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -2891,6 +2891,20 @@  currently supported object file formats.",
  bool, (const_tree exp),
  default_binds_local_p)

+/* True if TLS variable EXP names an object for which name resolution must
+   resolve to the current executable or shared library.  */
+DEFHOOK
+(binds_tls_local_p,
+ "Returns true if TLS variable @var{exp} names an object for which name\n\
+resolution rules must resolve to the current ``module'' (dynamic shared\n\
+library or executable image).\n\
+\n\
+The default version of this hook implements the name resolution rules\n\
+for ELF, which has a looser model of global name binding than other\n\
+currently supported object file formats.",
+ bool, (const_tree exp),
+ default_binds_tls_local_p)
+
 /* Check if profiling code is before or after prologue.  */
 DEFHOOK
 (profile_before_prologue,
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c
b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c
index 7af851b..69e3f6e 100644
--- a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c
+++ b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-1.c
@@ -1,4 +1,4 @@ 
-/* Check that GOTPCREL isn't used to access glob_a.  */
+/* Check that GOTPCREL/GOT isn't used to access glob_a.  */
 /* { dg-do compile { target *-*-linux* } } */
 /* { dg-require-effective-target pie_copyreloc } */
 /* { dg-options "-O2 -fpie" } */
@@ -12,3 +12,5 @@  int foo ()

 /* glob_a should never be accessed with a GOTPCREL.  */
 /* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { !
ia32 } } } } */
+/* glob_a should never be accessed with a GOT.  */
+/* { dg-final { scan-assembler-not "glob_a@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c
b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c
index 19cb97e..eb0724b 100644
--- a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c
+++ b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-2.c
@@ -1,4 +1,4 @@ 
-/* Check that GOTPCREL isn't used to access glob_a.  */
+/* Check that GOTPCREL/GOT isn't used to access glob_a.  */
 /* { dg-do compile { target *-*-linux* } } */
 /* { dg-require-effective-target pie_copyreloc } */
 /* { dg-options "-O2 -fpie" } */
@@ -12,3 +12,5 @@  int foo ()

 /* glob_a should never be accessed with a GOTPCREL.  */
 /* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { !
ia32 } } } } */
+/* glob_a should never be accessed with a GOT.  */
+/* { dg-final { scan-assembler-not "glob_a@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c
b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c
index c2fa896..1123bd8 100644
--- a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c
+++ b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-3.c
@@ -11,4 +11,4 @@  int foo ()
 }

 /* glob_a should be accessed with a PLT.  */
-/* { dg-final { scan-assembler "glob_a@PLT" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "glob_a@PLT" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c
b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c
index 413cdf3..4724ede 100644
--- a/gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c
+++ b/gcc/testsuite/gcc.target/i386/pie-copyrelocs-4.c
@@ -1,4 +1,4 @@ 
-/* Check that GOTPCREL is used to access glob_a.  */
+/* Check that GOTPCREL/GOT is used to access glob_a.  */
 /* { dg-do compile { target *-*-linux* } } */
 /* { dg-require-effective-target pie_copyreloc } */
 /* { dg-options "-O2 -fpie" } */
@@ -15,3 +15,5 @@  int foo ()

 /* weak glob_a should be accessed with a GOTPCREL.  */
 /* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */
+/* weak glob_a should be accessed with a GOT.  */
+/* { dg-final { scan-assembler "glob_a@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-10.c
b/gcc/testsuite/gcc.target/i386/pr32219-10.c
new file mode 100644
index 0000000..3cd95f0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr32219-10.c
@@ -0,0 +1,16 @@ 
+/* Check that tpoff/ntpoff isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-require-effective-target pie } */
+/* { dg-options "-O2 -fpie" } */
+
+extern __thread int glob_a;
+
+int foo ()
+{
+  return glob_a;
+}
+
+/* glob_a should never be accessed with a tpoff.  */
+/* { dg-final { scan-assembler-not "glob_a@tpoff" { target { ! ia32 } } } } */
+/* glob_a should never be accessed with a ntpoff.  */
+/* { dg-final { scan-assembler-not "glob_a@ntpoff" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr32219-9.c
b/gcc/testsuite/gcc.target/i386/pr32219-9.c
index 8c21826..77330be 100644
--- a/gcc/testsuite/gcc.target/i386/pr32219-9.c
+++ b/gcc/testsuite/gcc.target/i386/pr32219-9.c
@@ -13,3 +13,5 @@  foo ()

 /* { dg-final { scan-assembler "movl\[ \t\]xxx\\(%rip\\), %eax" {
target { ! ia32 } } } } */
 /* { dg-final { scan-assembler-not "xxx@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "xxx@GOTOFF" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "xxx@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65886-1.c
b/gcc/testsuite/gcc.target/i386/pr65886-1.c
new file mode 100644
index 0000000..c0d0aaa
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-1.c
@@ -0,0 +1,17 @@ 
+/* Check that GOTPCREL/GOT is used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-require-effective-target pie } */
+/* { dg-options "-O2 -fpie -fsymbolic" } */
+
+extern int glob_a;
+
+int foo ()
+{
+  if (&glob_a != 0)
+    return glob_a;
+  else
+    return 0;
+}
+
+/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "glob_a@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65886-2.c
b/gcc/testsuite/gcc.target/i386/pr65886-2.c
new file mode 100644
index 0000000..b89aaf0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-2.c
@@ -0,0 +1,15 @@ 
+/* Check that GOTPCREL/GOT isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fsymbolic" } */
+
+int glob_a = 1;
+
+int foo ()
+{
+  return glob_a;
+}
+
+/* { dg-final { scan-assembler "glob_a\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { !
ia32 } } } } */
+/* { dg-final { scan-assembler "glob_a@GOTOFF\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "glob_a@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65886-3.c
b/gcc/testsuite/gcc.target/i386/pr65886-3.c
new file mode 100644
index 0000000..ae24fd9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-3.c
@@ -0,0 +1,16 @@ 
+/* Check that PLT isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fsymbolic" } */
+
+int glob_a (void)
+{
+  return -1;
+}
+
+int foo ()
+{
+  return glob_a ();
+}
+
+/* { dg-final { scan-assembler-not "glob_a@PLT" } } */
+/* { dg-final { scan-assembler-times "movl\[ \t\]\\\$-1, %eax" 2 } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65886-4.c
b/gcc/testsuite/gcc.target/i386/pr65886-4.c
new file mode 100644
index 0000000..5f00508
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-4.c
@@ -0,0 +1,15 @@ 
+/* Check that GOTPCREL/GOT isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fsymbolic" } */
+
+extern __attribute__((visibility("protected"))) int glob_a;
+
+int foo ()
+{
+  return glob_a;
+}
+
+/* { dg-final { scan-assembler "glob_a\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { !
ia32 } } } } */
+/* { dg-final { scan-assembler "glob_a@GOTOFF\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "glob_a@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65886-5.c
b/gcc/testsuite/gcc.target/i386/pr65886-5.c
new file mode 100644
index 0000000..a37e2f4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-5.c
@@ -0,0 +1,18 @@ 
+/* Check that GOTPCREL/GOT isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fsymbolic" } */
+
+int glob_a __attribute__((weak));
+
+int foo ()
+{
+  if (&glob_a != 0)
+    return glob_a;
+  else
+    return 0;
+}
+
+/* { dg-final { scan-assembler "glob_a\\(%rip\\)" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { !
ia32 } } } } */
+/* { dg-final { scan-assembler "glob_a@GOTOFF\\(" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "glob_a@GOT\\(" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65886-6.c
b/gcc/testsuite/gcc.target/i386/pr65886-6.c
new file mode 100644
index 0000000..934f53f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-6.c
@@ -0,0 +1,18 @@ 
+/* Check that PLT isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fsymbolic" } */
+
+int
+__attribute__((weak))
+glob_a (void)
+{
+  return -1;
+}
+
+int foo ()
+{
+  return glob_a ();
+}
+
+/* { dg-final { scan-assembler-not "glob_a@PLT" } } */
+/* { dg-final { scan-assembler-times "movl\[ \t\]\\\$-1, %eax" 1 } } */
diff --git a/gcc/testsuite/lib/target-supports.exp
b/gcc/testsuite/lib/target-supports.exp
index fc05e84..c9e1b8a 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -6317,8 +6317,14 @@  proc check_effective_target_pie_copyreloc { } {

        set f [open $src "w"]
        puts $f "#include \"../../auto-host.h\""
-       puts $f "#if HAVE_LD_PIE_COPYRELOC == 0"
-       puts $f "# error Linker does not support PIE with copy reloc."
+       puts $f "#ifdef __x86_64__"
+       puts $f "# if HAVE_LD_X86_64_PIE_COPYRELOC == 0"
+       puts $f "#  error 64-bit linker does not support PIE with copy reloc."
+       puts $f "# endif"
+       puts $f "#else"
+       puts $f "# if HAVE_LD_386_PIE_COPYRELOC == 0"
+       puts $f "#  error 32-bit linker does not support PIE with copy reloc."
+       puts $f "# endif"
        puts $f "#endif"
        close $f

diff --git a/gcc/varasm.c b/gcc/varasm.c
index 3e76032..b04e97f 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -6122,7 +6122,7 @@  decl_default_tls_model (const_tree decl)
   enum tls_model kind;
   bool is_local;

-  is_local = targetm.binds_local_p (decl);
+  is_local = targetm.binds_tls_local_p (decl);
   if (!flag_shlib)
     {
       if (is_local)
@@ -6816,11 +6816,15 @@  resolution_local_p (enum
ld_plugin_symbol_resolution resolution)

 /* COMMON_LOCAL_P is true means that the linker can guarantee that an
    uninitialized common symbol in the executable will still be defined
-   (through COPY relocation) in the executable.  */
+   (through COPY relocation) in the executable.  DATA_LOCAL_P is true
+   means that the linker can guarantee that undefined, non-TLS, non-weak
+   data in the executable will still be defined (through COPY relocation)
+   in the executable.  */

 bool
-default_binds_local_p_3 (const_tree exp, bool shlib, bool weak_dominate,
-                        bool extern_protected_data, bool common_local_p)
+default_binds_local_p_3 (const_tree exp, bool tls, bool shlib,
+                        bool weak_dominate, bool extern_protected_data,
+                        bool common_local_p, bool data_local_p)
 {
   /* A non-decl is an entry in the constant pool.  */