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);
+}
