[PATCH] PRs target/65846/65886: Improve copy reloc in PIE

H.J. Lu hjl.tools@gmail.com
Sat Apr 25 18:05:00 GMT 2015


On Sat, Apr 25, 2015 at 08:46:58AM -0700, H.J. Lu wrote:
> 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.
> 
> OK for master?
> 

Since -fsymbolic binds references to protected symbols locally,
default_binds_local_p_3 should return true for protected symbols
with -fsymbolic.


H.J.
---
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.

	* 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        | 19 +++++++
 gcc/testsuite/gcc.target/i386/pr65886-2.c        | 15 ++++++
 gcc/testsuite/gcc.target/i386/pr65886-3.c        | 17 ++++++
 gcc/testsuite/gcc.target/i386/pr65886-4.c        | 15 ++++++
 gcc/testsuite/lib/target-supports.exp            | 10 +++-
 gcc/varasm.c                                     | 69 ++++++++++++++++++------
 22 files changed, 360 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

diff --git a/gcc/common.opt b/gcc/common.opt
index b49ac46..ff95276 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -986,6 +986,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 f2ed301..b1757a7 100644
--- a/gcc/config.in
+++ b/gcc/config.in
@@ -1349,6 +1349,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
@@ -1429,12 +1435,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
@@ -1460,6 +1460,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 d6c2de8..d793959 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -13230,11 +13230,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;
@@ -51802,17 +51798,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
 
@@ -51951,6 +51969,8 @@ ix86_binds_local_p (const_tree exp)
 #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 9523773..920c37d 100755
--- a/gcc/configure
+++ b/gcc/configure
@@ -27095,13 +27095,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
@@ -27132,7 +27132,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
       ;;
@@ -27141,11 +27141,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 68b0ee8..434a379 100644
--- a/gcc/configure.ac
+++ b/gcc/configure.ac
@@ -4705,12 +4705,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
@@ -4741,17 +4741,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 520c2c5..9d0c3fe 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1121,7 +1121,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 @gol
 -fno-jump-tables @gol
 -frecord-gcc-switches @gol
@@ -23530,6 +23530,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 6c5bfab..5a73c8a 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -7219,6 +7219,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 8d6dfbc..c0316be 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -5034,6 +5034,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 81d2ad2..52e094c 100644
--- a/gcc/output.h
+++ b/gcc/output.h
@@ -585,9 +585,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 a00181a..4a3e0d9 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..1234ef3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-1.c
@@ -0,0 +1,19 @@
+/* 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;
+}
+
+/* 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/pr65886-2.c b/gcc/testsuite/gcc.target/i386/pr65886-2.c
new file mode 100644
index 0000000..bfcc736
--- /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;
+}
+
+/* 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/pr65886-3.c b/gcc/testsuite/gcc.target/i386/pr65886-3.c
new file mode 100644
index 0000000..50f2a2c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65886-3.c
@@ -0,0 +1,17 @@
+/* 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 ();
+}
+
+/* glob_a should never be accessed with a PLT.  */
+/* { 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..a259b3b
--- /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;
+}
+
+/* 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/lib/target-supports.exp b/gcc/testsuite/lib/target-supports.exp
index c23d1cb..94dffe0 100644
--- a/gcc/testsuite/lib/target-supports.exp
+++ b/gcc/testsuite/lib/target-supports.exp
@@ -6222,8 +6222,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 e8d996c..a95b6e3 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -6117,7 +6117,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)
@@ -6811,11 +6811,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.  */
   if (!DECL_P (exp))
@@ -6846,10 +6850,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)
@@ -6859,7 +6886,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.  */
@@ -6872,13 +6899,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.  */
@@ -6905,7 +6934,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.  */
@@ -6913,14 +6951,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.1.0



More information about the Gcc-patches mailing list