[PATCH v3 1/2] Add -f[no-]direct-extern-access

H.J. Lu hjl.tools@gmail.com
Fri Jul 9 14:50:13 GMT 2021


-fdirect-extern-access is the default.  With -fno-direct-extern-access:

1. Always use GOT to access undefined data and function symbols,
   including in PIE and non-PIE.  These will avoid copy relocations
   in executables.  This is compatible with existing executables and
   shared libraries.
2. In executable and shared library, bind symbols with the STV_PROTECTED
   visibility locally:
   a. The address of data symbol is the address of data body.
   b. For systems without function descriptor, the function pointer is
      the address of function body.
   c. The resulting shared libraries may not be incompatible with
      executables which have copy relocations on protected symbols or
      use executable PLT entries as function addresses for protected
      functions in shared libraries.
3. Update asm_preferred_eh_data_format to select PC relative EH encoding
format with -fno-direct-extern-access to avoid copy relocation.
4. Add ix86_reloc_rw_mask for TARGET_ASM_RELOC_RW_MASK to avoid copy
relocation with -fno-direct-extern-access.

gcc/

	PR target/35513
	PR target/100593
	* common.opt: Add -fdirect-extern-access.
	* config/i386/i386-protos.h (ix86_force_load_from_GOT_p): Add a
	bool argument.
	* config/i386/i386.c (ix86_force_load_from_GOT_p): Add a bool
	argument to indicate call operand.  Force non-call load
	from GOT for -fno-direct-extern-access.
	(legitimate_pic_address_disp_p): Avoid copy relocation in PIE
	for -fno-direct-extern-access.
	(ix86_print_operand): Pass true to ix86_force_load_from_GOT_p
	for call operand.
	(asm_preferred_eh_data_format): Use PC-relative format for
	-fno-direct-extern-access to avoid copy relocation.  Check
	ptr_mode instead of TARGET_64BIT when selecting DW_EH_PE_sdata4.
	(ix86_binds_local_p): Don't treat protected data as extern and
	avoid copy relocation on common symbol with
	-fno-direct-extern-access.
	(ix86_reloc_rw_mask): New to avoid copy relocation for
	-fno-direct-extern-access.
	(TARGET_ASM_RELOC_RW_MASK): New.
	* doc/invoke.texi: Document -f[no-]direct-extern-access.

gcc/testsuite/

	PR target/35513
	PR target/100593
	* g++.dg/pr35513-1.C: New file.
	* g++.dg/pr35513-2.C: Likewise.
	* gcc.target/i386/pr35513-1.c: Likewise.
	* gcc.target/i386/pr35513-2.c: Likewise.
	* gcc.target/i386/pr35513-3.c: Likewise.
	* gcc.target/i386/pr35513-4.c: Likewise.
	* gcc.target/i386/pr35513-5.c: Likewise.
	* gcc.target/i386/pr35513-6.c: Likewise.
	* gcc.target/i386/pr35513-7.c: Likewise.
	* gcc.target/i386/pr35513-8.c: Likewise.
---
 gcc/common.opt                            |  4 ++
 gcc/config/i386/i386-protos.h             |  2 +-
 gcc/config/i386/i386.c                    | 50 +++++++++++++++------
 gcc/doc/invoke.texi                       | 13 ++++++
 gcc/testsuite/g++.dg/pr35513-1.C          | 25 +++++++++++
 gcc/testsuite/g++.dg/pr35513-2.C          | 53 +++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr35513-1.c | 16 +++++++
 gcc/testsuite/gcc.target/i386/pr35513-2.c | 15 +++++++
 gcc/testsuite/gcc.target/i386/pr35513-3.c | 15 +++++++
 gcc/testsuite/gcc.target/i386/pr35513-4.c | 15 +++++++
 gcc/testsuite/gcc.target/i386/pr35513-5.c | 15 +++++++
 gcc/testsuite/gcc.target/i386/pr35513-6.c | 14 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-7.c | 15 +++++++
 gcc/testsuite/gcc.target/i386/pr35513-8.c | 41 ++++++++++++++++++
 14 files changed, 278 insertions(+), 15 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/pr35513-1.C
 create mode 100644 gcc/testsuite/g++.dg/pr35513-2.C
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-8.c

diff --git a/gcc/common.opt b/gcc/common.opt
index d9da1131eda..67ad811d54d 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1432,6 +1432,10 @@ fdiagnostics-minimum-margin-width=
 Common Joined UInteger Var(diagnostics_minimum_margin_width) Init(6)
 Set minimum width of left margin of source code when showing source.
 
+fdirect-extern-access
+Common Var(flag_direct_extern_access) Init(1) Optimization
+Do not use GOT to access external symbols.
+
 fdisable-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fdisable-[tree|rtl|ipa]-<pass>=range1+range2	Disable an optimization pass.
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 51376fcc454..693cc3e5c78 100644
--- a/gcc/config/i386/i386-protos.h
+++ b/gcc/config/i386/i386-protos.h
@@ -79,7 +79,7 @@ extern bool ix86_expand_cmpstrn_or_cmpmem (rtx, rtx, rtx, rtx, rtx, bool);
 extern bool constant_address_p (rtx);
 extern bool legitimate_pic_operand_p (rtx);
 extern bool legitimate_pic_address_disp_p (rtx);
-extern bool ix86_force_load_from_GOT_p (rtx);
+extern bool ix86_force_load_from_GOT_p (rtx, bool = false);
 extern void print_reg (rtx, int, FILE*);
 extern void ix86_print_operand (FILE *, rtx, int);
 
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index cff26909292..7dee311051d 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -10312,13 +10312,17 @@ darwin_local_data_pic (rtx disp)
 }
 
 /* True if the function symbol operand X should be loaded from GOT.
+   If CALL_P is true, X is a call operand.
+
+   NB: -fno-direct-extern-access doesn't force load from GOT for
+   call.
 
    NB: In 32-bit mode, only non-PIC is allowed in inline assembly
    statements, since a PIC register could not be available at the
    call site.  */
 
 bool
-ix86_force_load_from_GOT_p (rtx x)
+ix86_force_load_from_GOT_p (rtx x, bool call_p)
 {
   return ((TARGET_64BIT || (!flag_pic && HAVE_AS_IX86_GOT32X))
 	  && !TARGET_PECOFF && !TARGET_MACHO
@@ -10326,11 +10330,12 @@ ix86_force_load_from_GOT_p (rtx x)
 	  && ix86_cmodel != CM_LARGE
 	  && ix86_cmodel != CM_LARGE_PIC
 	  && GET_CODE (x) == SYMBOL_REF
-	  && SYMBOL_REF_FUNCTION_P (x)
-	  && (!flag_plt
-	      || (SYMBOL_REF_DECL (x)
-		  && lookup_attribute ("noplt",
-				       DECL_ATTRIBUTES (SYMBOL_REF_DECL (x)))))
+	  && ((!call_p && !flag_direct_extern_access)
+	      || (SYMBOL_REF_FUNCTION_P (x)
+		  && (!flag_plt
+		      || (SYMBOL_REF_DECL (x)
+			  && lookup_attribute ("noplt",
+					       DECL_ATTRIBUTES (SYMBOL_REF_DECL (x)))))))
 	  && !SYMBOL_REF_LOCAL_P (x));
 }
 
@@ -10596,7 +10601,8 @@ legitimate_pic_address_disp_p (rtx disp)
 	    }
 	  else if (!SYMBOL_REF_FAR_ADDR_P (op0)
 		   && (SYMBOL_REF_LOCAL_P (op0)
-		       || (HAVE_LD_PIE_COPYRELOC
+		       || (flag_direct_extern_access
+			   && HAVE_LD_PIE_COPYRELOC
 			   && flag_pie
 			   && !SYMBOL_REF_WEAK (op0)
 			   && !SYMBOL_REF_FUNCTION_P (op0)))
@@ -13498,7 +13504,7 @@ ix86_print_operand (FILE *file, rtx x, int code)
 
       if (code == 'P')
 	{
-	  if (ix86_force_load_from_GOT_p (x))
+	  if (ix86_force_load_from_GOT_p (x, true))
 	    {
 	      /* For inline assembly statement, load function address
 		 from GOT with 'P' operand modifier to avoid PLT.  */
@@ -21935,10 +21941,10 @@ int
 asm_preferred_eh_data_format (int code, int global)
 {
   /* PE-COFF is effectively always -fPIC because of the .reloc section.  */
-  if (flag_pic || TARGET_PECOFF)
+  if (flag_pic || TARGET_PECOFF || !flag_direct_extern_access)
     {
       int type = DW_EH_PE_sdata8;
-      if (!TARGET_64BIT
+      if (ptr_mode == SImode
 	  || ix86_cmodel == CM_SMALL_PIC
 	  || (ix86_cmodel == CM_MEDIUM_PIC && (global || code)))
 	type = DW_EH_PE_sdata4;
@@ -23028,10 +23034,21 @@ ix86_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
 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 default_binds_local_p_3 (exp, flag_shlib != 0, true,
+				  flag_direct_extern_access,
+				  (flag_direct_extern_access
+				   && (!flag_pic
+				       || (TARGET_64BIT
+					   && HAVE_LD_PIE_COPYRELOC != 0))));
+}
+
+/* If flag_pic or flag_direct_extern_access is false, then neither
+   local nor global relocs should be placed in readonly memory.  */
+
+static int
+ix86_reloc_rw_mask (void)
+{
+  return (flag_pic || !flag_direct_extern_access) ? 3 : 0;
 }
 #endif
 
@@ -24071,6 +24088,11 @@ ix86_run_selftests (void)
 #define TARGET_GET_MULTILIB_ABI_NAME \
   ix86_get_multilib_abi_name
 
+#if !TARGET_MACHO && !TARGET_DLLIMPORT_DECL_ATTRIBUTES
+# undef TARGET_ASM_RELOC_RW_MASK
+# define TARGET_ASM_RELOC_RW_MASK ix86_reloc_rw_mask
+#endif
+
 static bool ix86_libc_has_fast_function (int fcode ATTRIBUTE_UNUSED)
 {
 #ifdef OPTION_GLIBC
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index e67d47af676..2959c84cc3d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -658,6 +658,7 @@ Objective-C and Objective-C++ Dialects}.
 -fnon-call-exceptions  -fdelete-dead-exceptions  -funwind-tables @gol
 -fasynchronous-unwind-tables @gol
 -fno-gnu-unique @gol
+-fno-direct-extern-access @gol
 -finhibit-size-directive  -fcommon  -fno-ident @gol
 -fpcc-struct-return  -fpic  -fPIC  -fpie  -fPIE  -fno-plt @gol
 -fno-jump-tables -fno-bit-tests @gol
@@ -16633,6 +16634,18 @@ through the PLT for specific external functions.
 In position-dependent code, a few targets also convert calls to
 functions that are marked to not use the PLT to use the GOT instead.
 
+@item -fno-direct-extern-access
+@opindex fno-direct-extern-access
+@opindex fdirect-extern-access
+Without @option{-fpic} nor @option{-fPIC}, always use the GOT pointer
+to access external symbols.  With @option{-fpic} or @option{-fPIC},
+treat access to protected symbols as local symbols.
+
+@strong{Warning:} shared libraries compiled with
+@option{-fno-direct-extern-access} and executable compiled with
+@option{-fdirect-extern-access} may not be binary compatible if
+protected symbols are used in shared libraries and executable.
+
 @item -fno-jump-tables
 @opindex fno-jump-tables
 @opindex fjump-tables
diff --git a/gcc/testsuite/g++.dg/pr35513-1.C b/gcc/testsuite/g++.dg/pr35513-1.C
new file mode 100644
index 00000000000..8423e826da8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr35513-1.C
@@ -0,0 +1,25 @@
+// { dg-do run }
+// { dg-options "-O2 -fno-direct-extern-access" }
+
+#include <iostream>
+
+class Bug
+{
+};
+
+int throw_bug()
+{
+  throw Bug();
+
+  return 0;
+}
+
+int main()
+{
+  try {
+      std::cout << throw_bug();
+  } catch (Bug bug) {
+  };
+
+  return 0;
+}
diff --git a/gcc/testsuite/g++.dg/pr35513-2.C b/gcc/testsuite/g++.dg/pr35513-2.C
new file mode 100644
index 00000000000..56ed19ae1eb
--- /dev/null
+++ b/gcc/testsuite/g++.dg/pr35513-2.C
@@ -0,0 +1,53 @@
+// { dg-do run  }
+// { dg-options "-O2 -fno-direct-extern-access" }
+
+class Foo 
+{
+public:
+  Foo(int n) : n_(n) { }
+  int f() { return n_; }
+
+  int badTest();
+  int goodTest();
+
+private:
+
+  int n_;
+};
+
+int Foo::badTest()
+{
+  try {
+      throw int(99);
+  }
+
+  catch (int &i) {
+      n_ = 16;
+  }
+
+  return n_;
+}
+
+
+int Foo::goodTest()
+{
+  int	n;
+
+  try {
+      throw int(99);
+  }
+
+  catch (int &i) {
+      n = 16;
+  }
+
+  return n_;
+}
+
+int main() 
+{
+  Foo foo(5);
+  foo.goodTest();
+  foo.badTest();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1.c b/gcc/testsuite/gcc.target/i386/pr35513-1.c
new file mode 100644
index 00000000000..c5dbabc3704
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-direct-extern-access" } */
+
+extern void bar (void);
+extern void *p;
+
+void
+foo (void)
+{
+  p = &bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ia32 && got32x_reloc } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2.c b/gcc/testsuite/gcc.target/i386/pr35513-2.c
new file mode 100644
index 00000000000..8bb7cb4c13d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-2.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -fno-direct-extern-access" } */
+
+extern int bar;
+
+int
+foo (void)
+{
+  return bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ia32 && got32x_reloc } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3.c b/gcc/testsuite/gcc.target/i386/pr35513-3.c
new file mode 100644
index 00000000000..98dc54e3bf4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-3.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie -fno-direct-extern-access" } */
+
+extern int bar;
+
+int
+foo (void)
+{
+  return bar;
+}
+
+/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT" { target { ia32 && got32x_reloc } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ia32 && got32x_reloc } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4.c b/gcc/testsuite/gcc.target/i386/pr35513-4.c
new file mode 100644
index 00000000000..467081dad65
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-4.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fno-pic -fno-direct-extern-access" } */
+
+extern void foo (void);
+
+int
+bar (void)
+{
+  foo ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*foo" } } */
+/* { dg-final { scan-assembler-not "foo@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "foo@GOT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5.c b/gcc/testsuite/gcc.target/i386/pr35513-5.c
new file mode 100644
index 00000000000..b0e61b08ba9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-5.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fpic -fno-direct-extern-access" } */
+
+extern void foo (void);
+
+int
+bar (void)
+{
+  foo ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "call\[ \t\]*foo@PLT" } } */
+/* { dg-final { scan-assembler-not "foo@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "foo@GOT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6.c b/gcc/testsuite/gcc.target/i386/pr35513-6.c
new file mode 100644
index 00000000000..270504b8d0e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-6.c
@@ -0,0 +1,14 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fno-pic -fno-direct-extern-access" } */
+
+extern void foo (void);
+
+void
+bar (void)
+{
+  foo ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*foo" } } */
+/* { dg-final { scan-assembler-not "foo@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "foo@GOT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7.c b/gcc/testsuite/gcc.target/i386/pr35513-7.c
new file mode 100644
index 00000000000..2c5a83ddef8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-7.c
@@ -0,0 +1,15 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fpic -fno-direct-extern-access" } */
+
+extern void foo (void);
+
+void
+bar (void)
+{
+  foo ();
+}
+
+/* { dg-final { scan-assembler "jmp\[ \t\]*foo@PLT" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler "call\[ \t\]*foo@PLT" { target ia32 } } } */
+/* { dg-final { scan-assembler-not "foo@GOTPCREL" { target { ! ia32 } } } } */
+/* { dg-final { scan-assembler-not "foo@GOT" { target ia32 } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-8.c b/gcc/testsuite/gcc.target/i386/pr35513-8.c
new file mode 100644
index 00000000000..545979e99c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-8.c
@@ -0,0 +1,41 @@
+/* { dg-do assemble { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-require-effective-target maybe_x32 } */
+/* { dg-options "-mx32 -O2 -fno-pic -fexceptions -fasynchronous-unwind-tables -fno-direct-extern-access" } */
+
+extern int foo (int);
+extern void exit (int __status) __attribute__ ((__nothrow__ )) __attribute__ ((__noreturn__));
+struct __pthread_cleanup_frame
+{
+  void (*__cancel_routine) (void *);
+  void *__cancel_arg;
+  int __do_it;
+  int __cancel_type;
+};
+extern __inline void
+__pthread_cleanup_routine (struct __pthread_cleanup_frame *__frame)
+{
+  if (__frame->__do_it)
+    __frame->__cancel_routine (__frame->__cancel_arg);
+}
+static int cl_called;
+
+static void
+cl (void *arg)
+{
+  ++cl_called;
+}
+
+
+void *
+tf_usleep (void *arg)
+{
+
+  do { struct __pthread_cleanup_frame __clframe __attribute__ ((__cleanup__ (__pthread_cleanup_routine))) = { .__cancel_routine = (cl), .__cancel_arg = (
+																			 ((void *)0)), .__do_it = 1 };;
+
+    foo (arg == ((void *)0) ? (0x7fffffffL * 2UL + 1UL) : 0);
+
+    __clframe.__do_it = (0); } while (0);
+
+  exit (1);
+}
-- 
2.31.1



More information about the Gcc-patches mailing list