[v4] x86: Add -m[no-]direct-extern-access

Message ID 20220127215240.1349520-1-hjl.tools@gmail.com
State Committed
Commit ab0b5fbfe90168d2e470aefb19e0cf31526290bc
Headers
Series [v4] x86: Add -m[no-]direct-extern-access |

Commit Message

H.J. Lu Jan. 27, 2022, 9:52 p.m. UTC
  The v3 patch was posted at

https://gcc.gnu.org/pipermail/gcc-patches/2021-July/574847.html

There is no progress with repeated pings since then.  Glibc 2.35 and
binutils 2.38 will support GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.
I'd like to remove copy relocation to improve security and performance
on x86 in GCC 12.  Here is the v4 patch

1. Rename the common option to x86 specific -mdirect-extern-access option.
2. Remove GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS linker check.
3. Use the existing GNU property function in x86 backend.

This new behavior is off by default.  Are there any objections?

Changes in the v3 patch.

1. GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support has been added to
GNU binutils 2.38.  But the -z indirect-extern-access linker option is
only available for Linux/x86.  However, the --max-cache-size=SIZE linker
option was also addded within a day.  --max-cache-size=SIZE is used to
check for GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support.

Changes in the v2 patch.

1. Rename the option to -fdirect-extern-access.

---
On systems with copy relocation:
* A copy in executable is created for the definition in a shared library
at run-time by ld.so.
* The copy is referenced by executable and shared libraries.
* Executable can access the copy directly.

Issues are:
* Overhead of a copy, time and space, may be visible at run-time.
* Read-only data in the shared library becomes read-write copy in
executable at run-time.
* Local access to data with the STV_PROTECTED visibility in the shared
library must use GOT.

On systems without function descriptor, function pointers vary depending
on where and how the functions are defined.
* If the function is defined in executable, it can be the address of
function body.
* If the function, including the function with STV_PROTECTED visibility,
is defined in the shared library, it can be the address of the PLT entry
in executable or shared library.

Issues are:
* The address of function body may not be used as its function pointer.
* ld.so needs to search loaded shared libraries for the function pointer
of the function with STV_PROTECTED visibility.

Here is a proposal to remove copy relocation and use canonical function
pointer:

1. Accesses, including in PIE and non-PIE, to undefined symbols must
use GOT.
  a. Linker may optimize out GOT access if the data is defined in PIE or
  non-PIE.
2. Read-only data in the shared library remain read-only at run-time
3. Address of global data with the STV_PROTECTED visibility in the shared
library is the address of data body.
  a. Can use IP-relative access.
  b. May need GOT without IP-relative access.
4. For systems without function descriptor,
  a. All global function pointers of undefined functions in PIE and
  non-PIE must use GOT.  Linker may optimize out GOT access if the
  function is defined in PIE or non-PIE.
  b. Function pointer of functions with the STV_PROTECTED visibility in
  executable and shared library is the address of function body.
   i. Can use IP-relative access.
   ii. May need GOT without IP-relative access.
   iii. Branches to undefined functions may use PLT.
5. Single global definition marker:

Add GNU_PROPERTY_1_NEEDED:

#define GNU_PROPERTY_1_NEEDED GNU_PROPERTY_UINT32_OR_LO

to indicate the needed properties by the object file.

Add GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS:

#define GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS (1U << 0)

to indicate that the object file requires canonical function pointers and
cannot be used with copy relocation.  This bit should be cleared in
executable when there are non-GOT or non-PLT relocations in relocatable
input files without this bit set.

  a. Protected symbol access within the shared library can be treated as
  local.
  b. Copy relocation should be disallowed at link-time and run-time.
  c. GOT function pointer reference is required at link-time and run-time.

The indirect external access marker can be used in the following ways:

1. Linker can decide the best way to resolve a relocation against a
protected symbol before seeing all relocations against the symbol.
2. Dynamic linker can decide if it is an error to have a copy relocation
in executable against the protected symbol in a shared library by checking
if the shared library is built with -mno-direct-extern-access.

Add a compiler option, -mdirect-extern-access. -mdirect-extern-access is
the default.  With -mno-direct-extern-access:

1. Always to use GOT to access undefined symbols, including in PIE and
non-PIE.  This is safe to do and does not break the ABI.
2. In executable and shared library, for symbols with the STV_PROTECTED
visibility:
  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.
These break the ABI and resulting shared libraries may not be compatible
with executables which are not compiled with -mno-direct-extern-access.
3. Generate an indirect external access marker in relocatable objects if
supported by linker.

H.J.
---
Add -m[no-]direct-extern-access and nodirect_extern_access attribute.
-mdirect-extern-access is the default.  With nodirect_extern_access
attribute, GOT is always used to access undefined data and function
symbols with nodirect_extern_access attribute, including in PIE and
non-PIE.  With -mno-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 -mno-direct-extern-access to avoid copy relocation.
4. Add ix86_reloc_rw_mask for TARGET_ASM_RELOC_RW_MASK to avoid copy
relocation with -mno-direct-extern-access.

gcc/

	PR target/35513
	PR target/100593
	* config/i386/gnu-property.cc: Include "i386-protos.h".
	(file_end_indicate_exec_stack_and_gnu_property): Generate
	a GNU_PROPERTY_1_NEEDED note for -mno-direct-extern-access or
	nodirect_extern_access attribute.
	* config/i386/i386-options.cc
	(handle_nodirect_extern_access_attribute): New function.
	(ix86_attribute_table): Add nodirect_extern_access attribute.
	* config/i386/i386-protos.h (ix86_force_load_from_GOT_p): Add a
	bool argument.
	(ix86_has_no_direct_extern_access): New.
	* config/i386/i386.cc (ix86_has_no_direct_extern_access): New.
	(ix86_force_load_from_GOT_p): Add a bool argument to indicate
	call operand.  Force non-call load from GOT for
	-mno-direct-extern-access or nodirect_extern_access attribute.
	(legitimate_pic_address_disp_p): Avoid copy relocation in PIE
	for -mno-direct-extern-access or nodirect_extern_access attribute.
	(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
	-mno-direct-extern-access to avoid copy relocation.  Check
	ptr_mode instead of TARGET_64BIT when selecting DW_EH_PE_sdata4.
	(ix86_binds_local_p): Set ix86_has_no_direct_extern_access to
	true for -mno-direct-extern-access or nodirect_extern_access
	attribute.  Don't treat protected data as extern and avoid copy
	relocation on common symbol with -mno-direct-extern-access or
	nodirect_extern_access attribute.
	(ix86_reloc_rw_mask): New to avoid copy relocation for
	-mno-direct-extern-access.
	(TARGET_ASM_RELOC_RW_MASK): New.
	* config/i386/i386.opt: Add -mdirect-extern-access.
	* doc/extend.texi: Document nodirect_extern_access attribute.
	* doc/invoke.texi: Document -m[no-]direct-extern-access.

gcc/testsuite/

	PR target/35513
	PR target/100593
	* g++.target/i386/pr35513-1.C: New file.
	* g++.target/i386/pr35513-2.C: Likewise.
	* gcc.target/i386/pr35513-1a.c: Likewise.
	* gcc.target/i386/pr35513-1b.c: Likewise.
	* gcc.target/i386/pr35513-2a.c: Likewise.
	* gcc.target/i386/pr35513-2b.c: Likewise.
	* gcc.target/i386/pr35513-3a.c: Likewise.
	* gcc.target/i386/pr35513-3b.c: Likewise.
	* gcc.target/i386/pr35513-4a.c: Likewise.
	* gcc.target/i386/pr35513-4b.c: Likewise.
	* gcc.target/i386/pr35513-5a.c: Likewise.
	* gcc.target/i386/pr35513-5b.c: Likewise.
	* gcc.target/i386/pr35513-6a.c: Likewise.
	* gcc.target/i386/pr35513-6b.c: Likewise.
	* gcc.target/i386/pr35513-7a.c: Likewise.
	* gcc.target/i386/pr35513-7b.c: Likewise.
	* gcc.target/i386/pr35513-8a.c: Likewise.
	* gcc.target/i386/pr35513-8b.c: Likewise.
	* gcc.target/i386/pr35513-9a.c: Likewise.
	* gcc.target/i386/pr35513-9b.c: Likewise.
	* gcc.target/i386/pr35513-10a.c: Likewise.
	* gcc.target/i386/pr35513-10b.c: Likewise.
	* gcc.target/i386/pr35513-11a.c: Likewise.
	* gcc.target/i386/pr35513-11b.c: Likewise.
	* gcc.target/i386/pr35513-12a.c: Likewise.
	* gcc.target/i386/pr35513-12b.c: Likewise.
---
 gcc/config/i386/gnu-property.cc             | 10 ++-
 gcc/config/i386/i386-options.cc             | 32 ++++++++++
 gcc/config/i386/i386-protos.h               |  4 +-
 gcc/config/i386/i386.cc                     | 67 ++++++++++++++++-----
 gcc/config/i386/i386.opt                    |  4 ++
 gcc/doc/extend.texi                         |  6 ++
 gcc/doc/invoke.texi                         | 15 ++++-
 gcc/testsuite/g++.target/i386/pr35513-1.C   | 25 ++++++++
 gcc/testsuite/g++.target/i386/pr35513-2.C   | 53 ++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr35513-10a.c | 20 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-10b.c | 20 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-11a.c | 20 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-11b.c | 20 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-12a.c | 20 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-12b.c | 20 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-1a.c  | 19 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-1b.c  | 19 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-2a.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-2b.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-3a.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-3b.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-4a.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-4b.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-5a.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-5b.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-6a.c  | 17 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-6b.c  | 17 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-7a.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-7b.c  | 18 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-8.c   | 44 ++++++++++++++
 gcc/testsuite/gcc.target/i386/pr35513-9a.c  | 20 ++++++
 gcc/testsuite/gcc.target/i386/pr35513-9b.c  | 20 ++++++
 32 files changed, 655 insertions(+), 17 deletions(-)
 create mode 100644 gcc/testsuite/g++.target/i386/pr35513-1.C
 create mode 100644 gcc/testsuite/g++.target/i386/pr35513-2.C
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7b.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-8.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9a.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9b.c
  

Comments

Hongtao Liu Feb. 9, 2022, 2:46 a.m. UTC | #1
On Fri, Jan 28, 2022 at 5:53 AM H.J. Lu via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> The v3 patch was posted at
>
> https://gcc.gnu.org/pipermail/gcc-patches/2021-July/574847.html
>
> There is no progress with repeated pings since then.  Glibc 2.35 and
> binutils 2.38 will support GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.
> I'd like to remove copy relocation to improve security and performance
> on x86 in GCC 12.  Here is the v4 patch
  This patch won't change default behavior for copy relocation(to
avoid conflict with existing .so files),
just give users an option under which copy relocation can be removed.
  The removal of copy relocation is an optimization of the linker(Also
improves security), whose
patches have been approved and committed to glibc2.35[1], binutils2.38[2].
  The compiler part is the final step in completing this optimization.

[1] https://sourceware.org/pipermail/libc-alpha/2021-October/131742.html
[2] https://sourceware.org/pipermail/binutils/2021-July/117308.html
>
> 1. Rename the common option to x86 specific -mdirect-extern-access option.
> 2. Remove GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS linker check.
> 3. Use the existing GNU property function in x86 backend.
>
> This new behavior is off by default.  Are there any objections?
>
> Changes in the v3 patch.
>
> 1. GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support has been added to
> GNU binutils 2.38.  But the -z indirect-extern-access linker option is
> only available for Linux/x86.  However, the --max-cache-size=SIZE linker
> option was also addded within a day.  --max-cache-size=SIZE is used to
> check for GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support.
>
> Changes in the v2 patch.
>
> 1. Rename the option to -fdirect-extern-access.
>
> ---
> On systems with copy relocation:
> * A copy in executable is created for the definition in a shared library
> at run-time by ld.so.
> * The copy is referenced by executable and shared libraries.
> * Executable can access the copy directly.
>
> Issues are:
> * Overhead of a copy, time and space, may be visible at run-time.
> * Read-only data in the shared library becomes read-write copy in
> executable at run-time.
> * Local access to data with the STV_PROTECTED visibility in the shared
> library must use GOT.
>
> On systems without function descriptor, function pointers vary depending
> on where and how the functions are defined.
> * If the function is defined in executable, it can be the address of
> function body.
> * If the function, including the function with STV_PROTECTED visibility,
> is defined in the shared library, it can be the address of the PLT entry
> in executable or shared library.
>
> Issues are:
> * The address of function body may not be used as its function pointer.
> * ld.so needs to search loaded shared libraries for the function pointer
> of the function with STV_PROTECTED visibility.
>
> Here is a proposal to remove copy relocation and use canonical function
> pointer:
>
> 1. Accesses, including in PIE and non-PIE, to undefined symbols must
> use GOT.
>   a. Linker may optimize out GOT access if the data is defined in PIE or
>   non-PIE.
> 2. Read-only data in the shared library remain read-only at run-time
> 3. Address of global data with the STV_PROTECTED visibility in the shared
> library is the address of data body.
>   a. Can use IP-relative access.
>   b. May need GOT without IP-relative access.
> 4. For systems without function descriptor,
>   a. All global function pointers of undefined functions in PIE and
>   non-PIE must use GOT.  Linker may optimize out GOT access if the
>   function is defined in PIE or non-PIE.
>   b. Function pointer of functions with the STV_PROTECTED visibility in
>   executable and shared library is the address of function body.
>    i. Can use IP-relative access.
>    ii. May need GOT without IP-relative access.
>    iii. Branches to undefined functions may use PLT.
> 5. Single global definition marker:
>
> Add GNU_PROPERTY_1_NEEDED:
>
> #define GNU_PROPERTY_1_NEEDED GNU_PROPERTY_UINT32_OR_LO
>
> to indicate the needed properties by the object file.
>
> Add GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS:
>
> #define GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS (1U << 0)
>
> to indicate that the object file requires canonical function pointers and
> cannot be used with copy relocation.  This bit should be cleared in
> executable when there are non-GOT or non-PLT relocations in relocatable
> input files without this bit set.
>
>   a. Protected symbol access within the shared library can be treated as
>   local.
>   b. Copy relocation should be disallowed at link-time and run-time.
>   c. GOT function pointer reference is required at link-time and run-time.
>
> The indirect external access marker can be used in the following ways:
>
> 1. Linker can decide the best way to resolve a relocation against a
> protected symbol before seeing all relocations against the symbol.
> 2. Dynamic linker can decide if it is an error to have a copy relocation
> in executable against the protected symbol in a shared library by checking
> if the shared library is built with -mno-direct-extern-access.
>
> Add a compiler option, -mdirect-extern-access. -mdirect-extern-access is
> the default.  With -mno-direct-extern-access:
>
> 1. Always to use GOT to access undefined symbols, including in PIE and
> non-PIE.  This is safe to do and does not break the ABI.
> 2. In executable and shared library, for symbols with the STV_PROTECTED
> visibility:
>   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.
> These break the ABI and resulting shared libraries may not be compatible
> with executables which are not compiled with -mno-direct-extern-access.
> 3. Generate an indirect external access marker in relocatable objects if
> supported by linker.
>
> H.J.
> ---
> Add -m[no-]direct-extern-access and nodirect_extern_access attribute.
> -mdirect-extern-access is the default.  With nodirect_extern_access
> attribute, GOT is always used to access undefined data and function
> symbols with nodirect_extern_access attribute, including in PIE and
> non-PIE.  With -mno-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 -mno-direct-extern-access to avoid copy relocation.
> 4. Add ix86_reloc_rw_mask for TARGET_ASM_RELOC_RW_MASK to avoid copy
> relocation with -mno-direct-extern-access.
>
> gcc/
>
>         PR target/35513
>         PR target/100593
>         * config/i386/gnu-property.cc: Include "i386-protos.h".
>         (file_end_indicate_exec_stack_and_gnu_property): Generate
>         a GNU_PROPERTY_1_NEEDED note for -mno-direct-extern-access or
>         nodirect_extern_access attribute.
>         * config/i386/i386-options.cc
>         (handle_nodirect_extern_access_attribute): New function.
>         (ix86_attribute_table): Add nodirect_extern_access attribute.
>         * config/i386/i386-protos.h (ix86_force_load_from_GOT_p): Add a
>         bool argument.
>         (ix86_has_no_direct_extern_access): New.
>         * config/i386/i386.cc (ix86_has_no_direct_extern_access): New.
>         (ix86_force_load_from_GOT_p): Add a bool argument to indicate
>         call operand.  Force non-call load from GOT for
>         -mno-direct-extern-access or nodirect_extern_access attribute.
>         (legitimate_pic_address_disp_p): Avoid copy relocation in PIE
>         for -mno-direct-extern-access or nodirect_extern_access attribute.
>         (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
>         -mno-direct-extern-access to avoid copy relocation.  Check
>         ptr_mode instead of TARGET_64BIT when selecting DW_EH_PE_sdata4.
>         (ix86_binds_local_p): Set ix86_has_no_direct_extern_access to
>         true for -mno-direct-extern-access or nodirect_extern_access
>         attribute.  Don't treat protected data as extern and avoid copy
>         relocation on common symbol with -mno-direct-extern-access or
>         nodirect_extern_access attribute.
>         (ix86_reloc_rw_mask): New to avoid copy relocation for
>         -mno-direct-extern-access.
>         (TARGET_ASM_RELOC_RW_MASK): New.
>         * config/i386/i386.opt: Add -mdirect-extern-access.
>         * doc/extend.texi: Document nodirect_extern_access attribute.
>         * doc/invoke.texi: Document -m[no-]direct-extern-access.
>
> gcc/testsuite/
>
>         PR target/35513
>         PR target/100593
>         * g++.target/i386/pr35513-1.C: New file.
>         * g++.target/i386/pr35513-2.C: Likewise.
>         * gcc.target/i386/pr35513-1a.c: Likewise.
>         * gcc.target/i386/pr35513-1b.c: Likewise.
>         * gcc.target/i386/pr35513-2a.c: Likewise.
>         * gcc.target/i386/pr35513-2b.c: Likewise.
>         * gcc.target/i386/pr35513-3a.c: Likewise.
>         * gcc.target/i386/pr35513-3b.c: Likewise.
>         * gcc.target/i386/pr35513-4a.c: Likewise.
>         * gcc.target/i386/pr35513-4b.c: Likewise.
>         * gcc.target/i386/pr35513-5a.c: Likewise.
>         * gcc.target/i386/pr35513-5b.c: Likewise.
>         * gcc.target/i386/pr35513-6a.c: Likewise.
>         * gcc.target/i386/pr35513-6b.c: Likewise.
>         * gcc.target/i386/pr35513-7a.c: Likewise.
>         * gcc.target/i386/pr35513-7b.c: Likewise.
>         * gcc.target/i386/pr35513-8a.c: Likewise.
>         * gcc.target/i386/pr35513-8b.c: Likewise.
>         * gcc.target/i386/pr35513-9a.c: Likewise.
>         * gcc.target/i386/pr35513-9b.c: Likewise.
>         * gcc.target/i386/pr35513-10a.c: Likewise.
>         * gcc.target/i386/pr35513-10b.c: Likewise.
>         * gcc.target/i386/pr35513-11a.c: Likewise.
>         * gcc.target/i386/pr35513-11b.c: Likewise.
>         * gcc.target/i386/pr35513-12a.c: Likewise.
>         * gcc.target/i386/pr35513-12b.c: Likewise.
> ---
>  gcc/config/i386/gnu-property.cc             | 10 ++-
>  gcc/config/i386/i386-options.cc             | 32 ++++++++++
>  gcc/config/i386/i386-protos.h               |  4 +-
>  gcc/config/i386/i386.cc                     | 67 ++++++++++++++++-----
>  gcc/config/i386/i386.opt                    |  4 ++
>  gcc/doc/extend.texi                         |  6 ++
>  gcc/doc/invoke.texi                         | 15 ++++-
>  gcc/testsuite/g++.target/i386/pr35513-1.C   | 25 ++++++++
>  gcc/testsuite/g++.target/i386/pr35513-2.C   | 53 ++++++++++++++++
>  gcc/testsuite/gcc.target/i386/pr35513-10a.c | 20 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-10b.c | 20 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-11a.c | 20 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-11b.c | 20 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-12a.c | 20 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-12b.c | 20 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-1a.c  | 19 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-1b.c  | 19 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-2a.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-2b.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-3a.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-3b.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-4a.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-4b.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-5a.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-5b.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-6a.c  | 17 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-6b.c  | 17 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-7a.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-7b.c  | 18 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-8.c   | 44 ++++++++++++++
>  gcc/testsuite/gcc.target/i386/pr35513-9a.c  | 20 ++++++
>  gcc/testsuite/gcc.target/i386/pr35513-9b.c  | 20 ++++++
>  32 files changed, 655 insertions(+), 17 deletions(-)
>  create mode 100644 gcc/testsuite/g++.target/i386/pr35513-1.C
>  create mode 100644 gcc/testsuite/g++.target/i386/pr35513-2.C
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7b.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-8.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9a.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9b.c
>
> diff --git a/gcc/config/i386/gnu-property.cc b/gcc/config/i386/gnu-property.cc
> index f08984f32a1..ea63c1ec58c 100644
> --- a/gcc/config/i386/gnu-property.cc
> +++ b/gcc/config/i386/gnu-property.cc
> @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "tm.h"
>  #include "output.h"
>  #include "linux-common.h"
> +#include "i386-protos.h"
>
>  static void
>  emit_gnu_property (unsigned int type, unsigned int data)
> @@ -60,7 +61,9 @@ file_end_indicate_exec_stack_and_gnu_property (void)
>  {
>    file_end_indicate_exec_stack ();
>
> -  if (flag_cf_protection == CF_NONE && !ix86_needed)
> +  if (flag_cf_protection == CF_NONE
> +      && !ix86_needed
> +      && !ix86_has_no_direct_extern_access)
>      return;
>
>    unsigned int feature_1 = 0;
> @@ -121,4 +124,9 @@ file_end_indicate_exec_stack_and_gnu_property (void)
>    /* Generate GNU_PROPERTY_X86_ISA_1_NEEDED.  */
>    if (isa_1)
>      emit_gnu_property (0xc0008002, isa_1);
> +
> +  if (ix86_has_no_direct_extern_access)
> +    /* Emite a GNU_PROPERTY_1_NEEDED note with
> +       GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.  */
> +    emit_gnu_property (0xb0008000, (1U << 0));
>  }
> diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
> index 715d9a15ff5..b82f7060007 100644
> --- a/gcc/config/i386/i386-options.cc
> +++ b/gcc/config/i386/i386-options.cc
> @@ -3775,6 +3775,36 @@ ix86_handle_fentry_name (tree *node, tree name, tree args,
>    return NULL_TREE;
>  }
>
> +/* Handle a "nodirect_extern_access" attribute; arguments as in
> +   struct attribute_spec.handler.  */
> +
> +static tree
> +handle_nodirect_extern_access_attribute (tree *pnode, tree name,
> +                                        tree ARG_UNUSED (args),
> +                                        int ARG_UNUSED (flags),
> +                                        bool *no_add_attrs)
> +{
> +  tree node = *pnode;
> +
> +  if (VAR_OR_FUNCTION_DECL_P (node))
> +    {
> +      if ((!TREE_STATIC (node) && TREE_CODE (node) != FUNCTION_DECL
> +          && !DECL_EXTERNAL (node)) || !TREE_PUBLIC (node))
> +       {
> +         warning (OPT_Wattributes,
> +                  "%qE attribute have effect only on public objects", name);
> +         *no_add_attrs = true;
> +       }
> +    }
> +  else
> +    {
> +      warning (OPT_Wattributes, "%qE attribute ignored", name);
> +      *no_add_attrs = true;
> +    }
> +
> +  return NULL_TREE;
> +}
> +
>  /* Table of valid machine attributes.  */
>  const struct attribute_spec ix86_attribute_table[] =
>  {
> @@ -3855,6 +3885,8 @@ const struct attribute_spec ix86_attribute_table[] =
>      ix86_handle_fentry_name, NULL },
>    { "cf_check", 0, 0, true, false, false, false,
>      ix86_handle_fndecl_attribute, NULL },
> +  { "nodirect_extern_access", 0, 0, true, false, false, false,
> +    handle_nodirect_extern_access_attribute, NULL },
>
>    /* End element.  */
>    { NULL, 0, 0, false, false, false, false, NULL, NULL }
> diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
> index 6b3c9516a7f..b7e9aa75d25 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);
>
> @@ -401,3 +401,5 @@ extern rtl_opt_pass *make_pass_insert_endbr_and_patchable_area
>    (gcc::context *);
>  extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
>    (gcc::context *);
> +
> +extern bool ix86_has_no_direct_extern_access;
> diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> index ad5a5caa413..3f520c007bd 100644
> --- a/gcc/config/i386/i386.cc
> +++ b/gcc/config/i386/i386.cc
> @@ -363,6 +363,9 @@ unsigned int ix86_default_incoming_stack_boundary;
>  /* Alignment for incoming stack boundary in bits.  */
>  unsigned int ix86_incoming_stack_boundary;
>
> +/* True if there is no direct access to extern symbols.  */
> +bool ix86_has_no_direct_extern_access;
> +
>  /* Calling abi specific va_list type nodes.  */
>  tree sysv_va_list_type_node;
>  tree ms_va_list_type_node;
> @@ -10513,13 +10516,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: -mno-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
> @@ -10527,11 +10534,16 @@ 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
> +              && (!ix86_direct_extern_access
> +                  || (SYMBOL_REF_DECL (x)
> +                      && lookup_attribute ("nodirect_extern_access",
> +                                           DECL_ATTRIBUTES (SYMBOL_REF_DECL (x))))))
> +             || (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));
>  }
>
> @@ -10798,7 +10810,11 @@ legitimate_pic_address_disp_p (rtx disp)
>             }
>           else if (!SYMBOL_REF_FAR_ADDR_P (op0)
>                    && (SYMBOL_REF_LOCAL_P (op0)
> -                      || (HAVE_LD_PIE_COPYRELOC
> +                      || ((ix86_direct_extern_access
> +                           && !(SYMBOL_REF_DECL (op0)
> +                                && lookup_attribute ("nodirect_extern_access",
> +                                                     DECL_ATTRIBUTES (SYMBOL_REF_DECL (op0)))))
> +                          && HAVE_LD_PIE_COPYRELOC
>                            && flag_pie
>                            && !SYMBOL_REF_WEAK (op0)
>                            && !SYMBOL_REF_FUNCTION_P (op0)))
> @@ -13754,7 +13770,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.  */
> @@ -22520,10 +22536,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 || !ix86_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;
> @@ -23613,10 +23629,28 @@ 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)));
> +  bool direct_extern_access
> +    = (ix86_direct_extern_access
> +       && !(VAR_OR_FUNCTION_DECL_P (exp)
> +           && lookup_attribute ("nodirect_extern_access",
> +                                DECL_ATTRIBUTES (exp))));
> +  if (!direct_extern_access)
> +    ix86_has_no_direct_extern_access = true;
> +  return default_binds_local_p_3 (exp, flag_shlib != 0, true,
> +                                 direct_extern_access,
> +                                 (direct_extern_access
> +                                  && (!flag_pic
> +                                      || (TARGET_64BIT
> +                                          && HAVE_LD_PIE_COPYRELOC != 0))));
> +}
> +
> +/* If flag_pic or ix86_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 || !ix86_direct_extern_access) ? 3 : 0;
>  }
>  #endif
>
> @@ -24681,6 +24715,11 @@ ix86_libgcc_floating_mode_supported_p
>  #undef TARGET_IFUNC_REF_LOCAL_OK
>  #define TARGET_IFUNC_REF_LOCAL_OK hook_bool_void_true
>
> +#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/config/i386/i386.opt b/gcc/config/i386/i386.opt
> index eb829d13d40..d8e8656a8ab 100644
> --- a/gcc/config/i386/i386.opt
> +++ b/gcc/config/i386/i386.opt
> @@ -1206,3 +1206,7 @@ Support MWAIT and MONITOR built-in functions and code generation.
>  mavx512fp16
>  Target Mask(ISA2_AVX512FP16) Var(ix86_isa_flags2) Save
>  Support MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512FP16 built-in functions and code generation.
> +
> +mdirect-extern-access
> +Target Var(ix86_direct_extern_access) Init(1)
> +Do not use GOT to access external symbols.
> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> index a961fc4e0a2..dbc453c7108 100644
> --- a/gcc/doc/extend.texi
> +++ b/gcc/doc/extend.texi
> @@ -7220,6 +7220,12 @@ On x86 targets, the @code{fentry_section} attribute sets the name
>  of the section to record function entry instrumentation calls in when
>  enabled with @option{-pg -mrecord-mcount}
>
> +@item nodirect_extern_access
> +@cindex @code{nodirect_extern_access} function attribute
> +@opindex mno-direct-extern-access
> +This attribute, attached to a global variable or function, is the
> +counterpart to option @option{-mno-direct-extern-access}.
> +
>  @end table
>
>  @node Xstormy16 Function Attributes
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 9e588db4fce..7f2de745f48 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -1436,7 +1436,7 @@ See RS/6000 and PowerPC Options.
>  -mgeneral-regs-only  -mcall-ms2sysv-xlogues -mrelax-cmpxchg-loop @gol
>  -mindirect-branch=@var{choice}  -mfunction-return=@var{choice} @gol
>  -mindirect-branch-register -mharden-sls=@var{choice} @gol
> --mindirect-branch-cs-prefix -mneeded}
> +-mindirect-branch-cs-prefix -mneeded -mno-direct-extern-access}
>
>  @emph{x86 Windows Options}
>  @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
> @@ -32789,6 +32789,19 @@ x32 environments.
>  @opindex mneeded
>  Emit GNU_PROPERTY_X86_ISA_1_NEEDED GNU property for Linux target to
>  indicate the micro-architecture ISA level required to execute the binary.
> +
> +@item -mno-direct-extern-access
> +@opindex mno-direct-extern-access
> +@opindex mdirect-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.  The default is
> +@option{-mdirect-extern-access}.
> +
> +@strong{Warning:} shared libraries compiled with
> +@option{-mno-direct-extern-access} and executable compiled with
> +@option{-mdirect-extern-access} may not be binary compatible if
> +protected symbols are used in shared libraries and executable.
>  @end table
>
>  @node x86 Windows Options
> diff --git a/gcc/testsuite/g++.target/i386/pr35513-1.C b/gcc/testsuite/g++.target/i386/pr35513-1.C
> new file mode 100644
> index 00000000000..6f8db37fb7c
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/i386/pr35513-1.C
> @@ -0,0 +1,25 @@
> +// { dg-do run }
> +// { dg-options "-O2 -mno-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++.target/i386/pr35513-2.C b/gcc/testsuite/g++.target/i386/pr35513-2.C
> new file mode 100644
> index 00000000000..9143ff3f0a5
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/i386/pr35513-2.C
> @@ -0,0 +1,53 @@
> +// { dg-do run  }
> +// { dg-options "-O2 -mno-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-10a.c b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
> new file mode 100644
> index 00000000000..d7b5c98fa8c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> +
> +/* Weak common symbol with -fpic.  */
> +__attribute__((weak, visibility("protected")))
> +int xxx;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-10b.c b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
> new file mode 100644
> index 00000000000..a40692e6e3d
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> +
> +/* Weak common symbol with -fpic.  */
> +__attribute__((weak, visibility("protected"),nodirect_extern_access))
> +int xxx;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11a.c b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
> new file mode 100644
> index 00000000000..5489f1e5cee
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> +
> +/* Initialized symbol with -fpic.  */
> +__attribute__((visibility("protected")))
> +int xxx = -1;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11b.c b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
> new file mode 100644
> index 00000000000..2704900fed5
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> +
> +/* Initialized symbol with -fpic.  */
> +__attribute__((visibility("protected"), nodirect_extern_access))
> +int xxx = -1;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12a.c b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
> new file mode 100644
> index 00000000000..8b3123f9042
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> +
> +/* Weak initialized symbol with -fpic.  */
> +__attribute__((weak, visibility("protected")))
> +int xxx = -1;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12b.c b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
> new file mode 100644
> index 00000000000..a1b6b9e92df
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> +
> +/* Weak initialized symbol with -fpic.  */
> +__attribute__((weak, visibility("protected"), nodirect_extern_access))
> +int xxx = -1;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1a.c b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
> new file mode 100644
> index 00000000000..972542423cb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1b.c b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
> new file mode 100644
> index 00000000000..54a579a9e37
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
> @@ -0,0 +1,19 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
> +
> +extern void bar (void) __attribute__ ((nodirect_extern_access));
> +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 } } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2a.c b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
> new file mode 100644
> index 00000000000..74fa8fc9d97
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2b.c b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
> new file mode 100644
> index 00000000000..ae2edff8d93
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
> +
> +extern int bar __attribute__ ((nodirect_extern_access));
> +
> +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 } } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3a.c b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
> new file mode 100644
> index 00000000000..4ca4332c4ab
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpie -mno-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 } } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3b.c b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
> new file mode 100644
> index 00000000000..c3888039834
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpie -mdirect-extern-access" } */
> +
> +extern int bar __attribute__ ((nodirect_extern_access));
> +
> +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 } } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4a.c b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
> new file mode 100644
> index 00000000000..9c3a199404c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4b.c b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
> new file mode 100644
> index 00000000000..e1a50784bf9
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
> +
> +extern void foo (void) __attribute__ ((nodirect_extern_access));
> +
> +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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5a.c b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
> new file mode 100644
> index 00000000000..4d2e1732838
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5b.c b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
> new file mode 100644
> index 00000000000..81e98ed7836
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
> +
> +extern void foo (void) __attribute__ ((nodirect_extern_access));
> +
> +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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6a.c b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
> new file mode 100644
> index 00000000000..ece878e3c3a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
> @@ -0,0 +1,17 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6b.c b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
> new file mode 100644
> index 00000000000..3f679defdab
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
> @@ -0,0 +1,17 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
> +
> +extern void foo (void) __attribute__ ((nodirect_extern_access));
> +
> +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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7a.c b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
> new file mode 100644
> index 00000000000..1de014d39c2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7b.c b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
> new file mode 100644
> index 00000000000..984e2dc2752
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
> @@ -0,0 +1,18 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
> +
> +extern void foo (void) __attribute__ ((nodirect_extern_access));
> +
> +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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> 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..7ba67de2156
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-8.c
> @@ -0,0 +1,44 @@
> +/* { dg-do assemble { target { *-*-linux* && { ! ia32 } } } } */
> +/* { dg-require-effective-target maybe_x32 } */
> +/* { dg-options "-mx32 -O2 -fno-pic -fexceptions -fasynchronous-unwind-tables -mno-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);
> +}
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9a.c b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
> new file mode 100644
> index 00000000000..533f1d2ddb4
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> +
> +/* Common symbol with -fpic.  */
> +__attribute__((visibility("protected")))
> +int xxx;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9b.c b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
> new file mode 100644
> index 00000000000..b6c66f43b40
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
> @@ -0,0 +1,20 @@
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> +
> +/* Common symbol with -fpic.  */
> +__attribute__((visibility("protected"), nodirect_extern_access))
> +int xxx;
> +
> +int
> +foo ()
> +{
> +  return xxx;
> +}
> +
> +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> +
> --
> 2.34.1
>
  
H.J. Lu Feb. 9, 2022, 3:04 a.m. UTC | #2
On Tue, Feb 8, 2022 at 6:38 PM Hongtao Liu <crazylht@gmail.com> wrote:
>
> On Fri, Jan 28, 2022 at 5:53 AM H.J. Lu via Gcc-patches
> <gcc-patches@gcc.gnu.org> wrote:
> >
> > The v3 patch was posted at
> >
> > https://gcc.gnu.org/pipermail/gcc-patches/2021-July/574847.html
> >
> > There is no progress with repeated pings since then.  Glibc 2.35 and
> > binutils 2.38 will support GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.
> > I'd like to remove copy relocation to improve security and performance
> > on x86 in GCC 12.  Here is the v4 patch
>   This patch won't change default behavior for copy relocation(to
> avoid conflict with existing .so files),
> just give users an option under which copy relocation can be removed.
>   The removal of copy relocation is an optimization of the linker(Also
> improves security), whose
> patches have been approved and committed to glibc2.35[1], binutils2.38[2].
>   The compiler part is the final step in completing this optimization.

Thanks for looking into it.  Since the new behavior is off by default and
none of the maintainers feel comfortable to review my patch,  which is
related to linker and glibc, for the last 6 months,  now is the good time
to check it in as any other time.  I will check it in tomorrow.

> [1] https://sourceware.org/pipermail/libc-alpha/2021-October/131742.html
> [2] https://sourceware.org/pipermail/binutils/2021-July/117308.html
> >
> > 1. Rename the common option to x86 specific -mdirect-extern-access option.
> > 2. Remove GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS linker check.
> > 3. Use the existing GNU property function in x86 backend.
> >
> > This new behavior is off by default.  Are there any objections?
> >
> > Changes in the v3 patch.
> >
> > 1. GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support has been added to
> > GNU binutils 2.38.  But the -z indirect-extern-access linker option is
> > only available for Linux/x86.  However, the --max-cache-size=SIZE linker
> > option was also addded within a day.  --max-cache-size=SIZE is used to
> > check for GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support.
> >
> > Changes in the v2 patch.
> >
> > 1. Rename the option to -fdirect-extern-access.
> >
> > ---
> > On systems with copy relocation:
> > * A copy in executable is created for the definition in a shared library
> > at run-time by ld.so.
> > * The copy is referenced by executable and shared libraries.
> > * Executable can access the copy directly.
> >
> > Issues are:
> > * Overhead of a copy, time and space, may be visible at run-time.
> > * Read-only data in the shared library becomes read-write copy in
> > executable at run-time.
> > * Local access to data with the STV_PROTECTED visibility in the shared
> > library must use GOT.
> >
> > On systems without function descriptor, function pointers vary depending
> > on where and how the functions are defined.
> > * If the function is defined in executable, it can be the address of
> > function body.
> > * If the function, including the function with STV_PROTECTED visibility,
> > is defined in the shared library, it can be the address of the PLT entry
> > in executable or shared library.
> >
> > Issues are:
> > * The address of function body may not be used as its function pointer.
> > * ld.so needs to search loaded shared libraries for the function pointer
> > of the function with STV_PROTECTED visibility.
> >
> > Here is a proposal to remove copy relocation and use canonical function
> > pointer:
> >
> > 1. Accesses, including in PIE and non-PIE, to undefined symbols must
> > use GOT.
> >   a. Linker may optimize out GOT access if the data is defined in PIE or
> >   non-PIE.
> > 2. Read-only data in the shared library remain read-only at run-time
> > 3. Address of global data with the STV_PROTECTED visibility in the shared
> > library is the address of data body.
> >   a. Can use IP-relative access.
> >   b. May need GOT without IP-relative access.
> > 4. For systems without function descriptor,
> >   a. All global function pointers of undefined functions in PIE and
> >   non-PIE must use GOT.  Linker may optimize out GOT access if the
> >   function is defined in PIE or non-PIE.
> >   b. Function pointer of functions with the STV_PROTECTED visibility in
> >   executable and shared library is the address of function body.
> >    i. Can use IP-relative access.
> >    ii. May need GOT without IP-relative access.
> >    iii. Branches to undefined functions may use PLT.
> > 5. Single global definition marker:
> >
> > Add GNU_PROPERTY_1_NEEDED:
> >
> > #define GNU_PROPERTY_1_NEEDED GNU_PROPERTY_UINT32_OR_LO
> >
> > to indicate the needed properties by the object file.
> >
> > Add GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS:
> >
> > #define GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS (1U << 0)
> >
> > to indicate that the object file requires canonical function pointers and
> > cannot be used with copy relocation.  This bit should be cleared in
> > executable when there are non-GOT or non-PLT relocations in relocatable
> > input files without this bit set.
> >
> >   a. Protected symbol access within the shared library can be treated as
> >   local.
> >   b. Copy relocation should be disallowed at link-time and run-time.
> >   c. GOT function pointer reference is required at link-time and run-time.
> >
> > The indirect external access marker can be used in the following ways:
> >
> > 1. Linker can decide the best way to resolve a relocation against a
> > protected symbol before seeing all relocations against the symbol.
> > 2. Dynamic linker can decide if it is an error to have a copy relocation
> > in executable against the protected symbol in a shared library by checking
> > if the shared library is built with -mno-direct-extern-access.
> >
> > Add a compiler option, -mdirect-extern-access. -mdirect-extern-access is
> > the default.  With -mno-direct-extern-access:
> >
> > 1. Always to use GOT to access undefined symbols, including in PIE and
> > non-PIE.  This is safe to do and does not break the ABI.
> > 2. In executable and shared library, for symbols with the STV_PROTECTED
> > visibility:
> >   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.
> > These break the ABI and resulting shared libraries may not be compatible
> > with executables which are not compiled with -mno-direct-extern-access.
> > 3. Generate an indirect external access marker in relocatable objects if
> > supported by linker.
> >
> > H.J.
> > ---
> > Add -m[no-]direct-extern-access and nodirect_extern_access attribute.
> > -mdirect-extern-access is the default.  With nodirect_extern_access
> > attribute, GOT is always used to access undefined data and function
> > symbols with nodirect_extern_access attribute, including in PIE and
> > non-PIE.  With -mno-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 -mno-direct-extern-access to avoid copy relocation.
> > 4. Add ix86_reloc_rw_mask for TARGET_ASM_RELOC_RW_MASK to avoid copy
> > relocation with -mno-direct-extern-access.
> >
> > gcc/
> >
> >         PR target/35513
> >         PR target/100593
> >         * config/i386/gnu-property.cc: Include "i386-protos.h".
> >         (file_end_indicate_exec_stack_and_gnu_property): Generate
> >         a GNU_PROPERTY_1_NEEDED note for -mno-direct-extern-access or
> >         nodirect_extern_access attribute.
> >         * config/i386/i386-options.cc
> >         (handle_nodirect_extern_access_attribute): New function.
> >         (ix86_attribute_table): Add nodirect_extern_access attribute.
> >         * config/i386/i386-protos.h (ix86_force_load_from_GOT_p): Add a
> >         bool argument.
> >         (ix86_has_no_direct_extern_access): New.
> >         * config/i386/i386.cc (ix86_has_no_direct_extern_access): New.
> >         (ix86_force_load_from_GOT_p): Add a bool argument to indicate
> >         call operand.  Force non-call load from GOT for
> >         -mno-direct-extern-access or nodirect_extern_access attribute.
> >         (legitimate_pic_address_disp_p): Avoid copy relocation in PIE
> >         for -mno-direct-extern-access or nodirect_extern_access attribute.
> >         (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
> >         -mno-direct-extern-access to avoid copy relocation.  Check
> >         ptr_mode instead of TARGET_64BIT when selecting DW_EH_PE_sdata4.
> >         (ix86_binds_local_p): Set ix86_has_no_direct_extern_access to
> >         true for -mno-direct-extern-access or nodirect_extern_access
> >         attribute.  Don't treat protected data as extern and avoid copy
> >         relocation on common symbol with -mno-direct-extern-access or
> >         nodirect_extern_access attribute.
> >         (ix86_reloc_rw_mask): New to avoid copy relocation for
> >         -mno-direct-extern-access.
> >         (TARGET_ASM_RELOC_RW_MASK): New.
> >         * config/i386/i386.opt: Add -mdirect-extern-access.
> >         * doc/extend.texi: Document nodirect_extern_access attribute.
> >         * doc/invoke.texi: Document -m[no-]direct-extern-access.
> >
> > gcc/testsuite/
> >
> >         PR target/35513
> >         PR target/100593
> >         * g++.target/i386/pr35513-1.C: New file.
> >         * g++.target/i386/pr35513-2.C: Likewise.
> >         * gcc.target/i386/pr35513-1a.c: Likewise.
> >         * gcc.target/i386/pr35513-1b.c: Likewise.
> >         * gcc.target/i386/pr35513-2a.c: Likewise.
> >         * gcc.target/i386/pr35513-2b.c: Likewise.
> >         * gcc.target/i386/pr35513-3a.c: Likewise.
> >         * gcc.target/i386/pr35513-3b.c: Likewise.
> >         * gcc.target/i386/pr35513-4a.c: Likewise.
> >         * gcc.target/i386/pr35513-4b.c: Likewise.
> >         * gcc.target/i386/pr35513-5a.c: Likewise.
> >         * gcc.target/i386/pr35513-5b.c: Likewise.
> >         * gcc.target/i386/pr35513-6a.c: Likewise.
> >         * gcc.target/i386/pr35513-6b.c: Likewise.
> >         * gcc.target/i386/pr35513-7a.c: Likewise.
> >         * gcc.target/i386/pr35513-7b.c: Likewise.
> >         * gcc.target/i386/pr35513-8a.c: Likewise.
> >         * gcc.target/i386/pr35513-8b.c: Likewise.
> >         * gcc.target/i386/pr35513-9a.c: Likewise.
> >         * gcc.target/i386/pr35513-9b.c: Likewise.
> >         * gcc.target/i386/pr35513-10a.c: Likewise.
> >         * gcc.target/i386/pr35513-10b.c: Likewise.
> >         * gcc.target/i386/pr35513-11a.c: Likewise.
> >         * gcc.target/i386/pr35513-11b.c: Likewise.
> >         * gcc.target/i386/pr35513-12a.c: Likewise.
> >         * gcc.target/i386/pr35513-12b.c: Likewise.
> > ---
> >  gcc/config/i386/gnu-property.cc             | 10 ++-
> >  gcc/config/i386/i386-options.cc             | 32 ++++++++++
> >  gcc/config/i386/i386-protos.h               |  4 +-
> >  gcc/config/i386/i386.cc                     | 67 ++++++++++++++++-----
> >  gcc/config/i386/i386.opt                    |  4 ++
> >  gcc/doc/extend.texi                         |  6 ++
> >  gcc/doc/invoke.texi                         | 15 ++++-
> >  gcc/testsuite/g++.target/i386/pr35513-1.C   | 25 ++++++++
> >  gcc/testsuite/g++.target/i386/pr35513-2.C   | 53 ++++++++++++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-10a.c | 20 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-10b.c | 20 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-11a.c | 20 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-11b.c | 20 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-12a.c | 20 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-12b.c | 20 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-1a.c  | 19 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-1b.c  | 19 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-2a.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-2b.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-3a.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-3b.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-4a.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-4b.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-5a.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-5b.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-6a.c  | 17 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-6b.c  | 17 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-7a.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-7b.c  | 18 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-8.c   | 44 ++++++++++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-9a.c  | 20 ++++++
> >  gcc/testsuite/gcc.target/i386/pr35513-9b.c  | 20 ++++++
> >  32 files changed, 655 insertions(+), 17 deletions(-)
> >  create mode 100644 gcc/testsuite/g++.target/i386/pr35513-1.C
> >  create mode 100644 gcc/testsuite/g++.target/i386/pr35513-2.C
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7b.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-8.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9a.c
> >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9b.c
> >
> > diff --git a/gcc/config/i386/gnu-property.cc b/gcc/config/i386/gnu-property.cc
> > index f08984f32a1..ea63c1ec58c 100644
> > --- a/gcc/config/i386/gnu-property.cc
> > +++ b/gcc/config/i386/gnu-property.cc
> > @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
> >  #include "tm.h"
> >  #include "output.h"
> >  #include "linux-common.h"
> > +#include "i386-protos.h"
> >
> >  static void
> >  emit_gnu_property (unsigned int type, unsigned int data)
> > @@ -60,7 +61,9 @@ file_end_indicate_exec_stack_and_gnu_property (void)
> >  {
> >    file_end_indicate_exec_stack ();
> >
> > -  if (flag_cf_protection == CF_NONE && !ix86_needed)
> > +  if (flag_cf_protection == CF_NONE
> > +      && !ix86_needed
> > +      && !ix86_has_no_direct_extern_access)
> >      return;
> >
> >    unsigned int feature_1 = 0;
> > @@ -121,4 +124,9 @@ file_end_indicate_exec_stack_and_gnu_property (void)
> >    /* Generate GNU_PROPERTY_X86_ISA_1_NEEDED.  */
> >    if (isa_1)
> >      emit_gnu_property (0xc0008002, isa_1);
> > +
> > +  if (ix86_has_no_direct_extern_access)
> > +    /* Emite a GNU_PROPERTY_1_NEEDED note with
> > +       GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.  */
> > +    emit_gnu_property (0xb0008000, (1U << 0));
> >  }
> > diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
> > index 715d9a15ff5..b82f7060007 100644
> > --- a/gcc/config/i386/i386-options.cc
> > +++ b/gcc/config/i386/i386-options.cc
> > @@ -3775,6 +3775,36 @@ ix86_handle_fentry_name (tree *node, tree name, tree args,
> >    return NULL_TREE;
> >  }
> >
> > +/* Handle a "nodirect_extern_access" attribute; arguments as in
> > +   struct attribute_spec.handler.  */
> > +
> > +static tree
> > +handle_nodirect_extern_access_attribute (tree *pnode, tree name,
> > +                                        tree ARG_UNUSED (args),
> > +                                        int ARG_UNUSED (flags),
> > +                                        bool *no_add_attrs)
> > +{
> > +  tree node = *pnode;
> > +
> > +  if (VAR_OR_FUNCTION_DECL_P (node))
> > +    {
> > +      if ((!TREE_STATIC (node) && TREE_CODE (node) != FUNCTION_DECL
> > +          && !DECL_EXTERNAL (node)) || !TREE_PUBLIC (node))
> > +       {
> > +         warning (OPT_Wattributes,
> > +                  "%qE attribute have effect only on public objects", name);
> > +         *no_add_attrs = true;
> > +       }
> > +    }
> > +  else
> > +    {
> > +      warning (OPT_Wattributes, "%qE attribute ignored", name);
> > +      *no_add_attrs = true;
> > +    }
> > +
> > +  return NULL_TREE;
> > +}
> > +
> >  /* Table of valid machine attributes.  */
> >  const struct attribute_spec ix86_attribute_table[] =
> >  {
> > @@ -3855,6 +3885,8 @@ const struct attribute_spec ix86_attribute_table[] =
> >      ix86_handle_fentry_name, NULL },
> >    { "cf_check", 0, 0, true, false, false, false,
> >      ix86_handle_fndecl_attribute, NULL },
> > +  { "nodirect_extern_access", 0, 0, true, false, false, false,
> > +    handle_nodirect_extern_access_attribute, NULL },
> >
> >    /* End element.  */
> >    { NULL, 0, 0, false, false, false, false, NULL, NULL }
> > diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
> > index 6b3c9516a7f..b7e9aa75d25 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);
> >
> > @@ -401,3 +401,5 @@ extern rtl_opt_pass *make_pass_insert_endbr_and_patchable_area
> >    (gcc::context *);
> >  extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
> >    (gcc::context *);
> > +
> > +extern bool ix86_has_no_direct_extern_access;
> > diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> > index ad5a5caa413..3f520c007bd 100644
> > --- a/gcc/config/i386/i386.cc
> > +++ b/gcc/config/i386/i386.cc
> > @@ -363,6 +363,9 @@ unsigned int ix86_default_incoming_stack_boundary;
> >  /* Alignment for incoming stack boundary in bits.  */
> >  unsigned int ix86_incoming_stack_boundary;
> >
> > +/* True if there is no direct access to extern symbols.  */
> > +bool ix86_has_no_direct_extern_access;
> > +
> >  /* Calling abi specific va_list type nodes.  */
> >  tree sysv_va_list_type_node;
> >  tree ms_va_list_type_node;
> > @@ -10513,13 +10516,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: -mno-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
> > @@ -10527,11 +10534,16 @@ 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
> > +              && (!ix86_direct_extern_access
> > +                  || (SYMBOL_REF_DECL (x)
> > +                      && lookup_attribute ("nodirect_extern_access",
> > +                                           DECL_ATTRIBUTES (SYMBOL_REF_DECL (x))))))
> > +             || (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));
> >  }
> >
> > @@ -10798,7 +10810,11 @@ legitimate_pic_address_disp_p (rtx disp)
> >             }
> >           else if (!SYMBOL_REF_FAR_ADDR_P (op0)
> >                    && (SYMBOL_REF_LOCAL_P (op0)
> > -                      || (HAVE_LD_PIE_COPYRELOC
> > +                      || ((ix86_direct_extern_access
> > +                           && !(SYMBOL_REF_DECL (op0)
> > +                                && lookup_attribute ("nodirect_extern_access",
> > +                                                     DECL_ATTRIBUTES (SYMBOL_REF_DECL (op0)))))
> > +                          && HAVE_LD_PIE_COPYRELOC
> >                            && flag_pie
> >                            && !SYMBOL_REF_WEAK (op0)
> >                            && !SYMBOL_REF_FUNCTION_P (op0)))
> > @@ -13754,7 +13770,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.  */
> > @@ -22520,10 +22536,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 || !ix86_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;
> > @@ -23613,10 +23629,28 @@ 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)));
> > +  bool direct_extern_access
> > +    = (ix86_direct_extern_access
> > +       && !(VAR_OR_FUNCTION_DECL_P (exp)
> > +           && lookup_attribute ("nodirect_extern_access",
> > +                                DECL_ATTRIBUTES (exp))));
> > +  if (!direct_extern_access)
> > +    ix86_has_no_direct_extern_access = true;
> > +  return default_binds_local_p_3 (exp, flag_shlib != 0, true,
> > +                                 direct_extern_access,
> > +                                 (direct_extern_access
> > +                                  && (!flag_pic
> > +                                      || (TARGET_64BIT
> > +                                          && HAVE_LD_PIE_COPYRELOC != 0))));
> > +}
> > +
> > +/* If flag_pic or ix86_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 || !ix86_direct_extern_access) ? 3 : 0;
> >  }
> >  #endif
> >
> > @@ -24681,6 +24715,11 @@ ix86_libgcc_floating_mode_supported_p
> >  #undef TARGET_IFUNC_REF_LOCAL_OK
> >  #define TARGET_IFUNC_REF_LOCAL_OK hook_bool_void_true
> >
> > +#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/config/i386/i386.opt b/gcc/config/i386/i386.opt
> > index eb829d13d40..d8e8656a8ab 100644
> > --- a/gcc/config/i386/i386.opt
> > +++ b/gcc/config/i386/i386.opt
> > @@ -1206,3 +1206,7 @@ Support MWAIT and MONITOR built-in functions and code generation.
> >  mavx512fp16
> >  Target Mask(ISA2_AVX512FP16) Var(ix86_isa_flags2) Save
> >  Support MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512FP16 built-in functions and code generation.
> > +
> > +mdirect-extern-access
> > +Target Var(ix86_direct_extern_access) Init(1)
> > +Do not use GOT to access external symbols.
> > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > index a961fc4e0a2..dbc453c7108 100644
> > --- a/gcc/doc/extend.texi
> > +++ b/gcc/doc/extend.texi
> > @@ -7220,6 +7220,12 @@ On x86 targets, the @code{fentry_section} attribute sets the name
> >  of the section to record function entry instrumentation calls in when
> >  enabled with @option{-pg -mrecord-mcount}
> >
> > +@item nodirect_extern_access
> > +@cindex @code{nodirect_extern_access} function attribute
> > +@opindex mno-direct-extern-access
> > +This attribute, attached to a global variable or function, is the
> > +counterpart to option @option{-mno-direct-extern-access}.
> > +
> >  @end table
> >
> >  @node Xstormy16 Function Attributes
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 9e588db4fce..7f2de745f48 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -1436,7 +1436,7 @@ See RS/6000 and PowerPC Options.
> >  -mgeneral-regs-only  -mcall-ms2sysv-xlogues -mrelax-cmpxchg-loop @gol
> >  -mindirect-branch=@var{choice}  -mfunction-return=@var{choice} @gol
> >  -mindirect-branch-register -mharden-sls=@var{choice} @gol
> > --mindirect-branch-cs-prefix -mneeded}
> > +-mindirect-branch-cs-prefix -mneeded -mno-direct-extern-access}
> >
> >  @emph{x86 Windows Options}
> >  @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
> > @@ -32789,6 +32789,19 @@ x32 environments.
> >  @opindex mneeded
> >  Emit GNU_PROPERTY_X86_ISA_1_NEEDED GNU property for Linux target to
> >  indicate the micro-architecture ISA level required to execute the binary.
> > +
> > +@item -mno-direct-extern-access
> > +@opindex mno-direct-extern-access
> > +@opindex mdirect-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.  The default is
> > +@option{-mdirect-extern-access}.
> > +
> > +@strong{Warning:} shared libraries compiled with
> > +@option{-mno-direct-extern-access} and executable compiled with
> > +@option{-mdirect-extern-access} may not be binary compatible if
> > +protected symbols are used in shared libraries and executable.
> >  @end table
> >
> >  @node x86 Windows Options
> > diff --git a/gcc/testsuite/g++.target/i386/pr35513-1.C b/gcc/testsuite/g++.target/i386/pr35513-1.C
> > new file mode 100644
> > index 00000000000..6f8db37fb7c
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.target/i386/pr35513-1.C
> > @@ -0,0 +1,25 @@
> > +// { dg-do run }
> > +// { dg-options "-O2 -mno-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++.target/i386/pr35513-2.C b/gcc/testsuite/g++.target/i386/pr35513-2.C
> > new file mode 100644
> > index 00000000000..9143ff3f0a5
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.target/i386/pr35513-2.C
> > @@ -0,0 +1,53 @@
> > +// { dg-do run  }
> > +// { dg-options "-O2 -mno-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-10a.c b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
> > new file mode 100644
> > index 00000000000..d7b5c98fa8c
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > +
> > +/* Weak common symbol with -fpic.  */
> > +__attribute__((weak, visibility("protected")))
> > +int xxx;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-10b.c b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
> > new file mode 100644
> > index 00000000000..a40692e6e3d
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > +
> > +/* Weak common symbol with -fpic.  */
> > +__attribute__((weak, visibility("protected"),nodirect_extern_access))
> > +int xxx;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11a.c b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
> > new file mode 100644
> > index 00000000000..5489f1e5cee
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > +
> > +/* Initialized symbol with -fpic.  */
> > +__attribute__((visibility("protected")))
> > +int xxx = -1;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11b.c b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
> > new file mode 100644
> > index 00000000000..2704900fed5
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > +
> > +/* Initialized symbol with -fpic.  */
> > +__attribute__((visibility("protected"), nodirect_extern_access))
> > +int xxx = -1;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12a.c b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
> > new file mode 100644
> > index 00000000000..8b3123f9042
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > +
> > +/* Weak initialized symbol with -fpic.  */
> > +__attribute__((weak, visibility("protected")))
> > +int xxx = -1;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12b.c b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
> > new file mode 100644
> > index 00000000000..a1b6b9e92df
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > +
> > +/* Weak initialized symbol with -fpic.  */
> > +__attribute__((weak, visibility("protected"), nodirect_extern_access))
> > +int xxx = -1;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1a.c b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
> > new file mode 100644
> > index 00000000000..972542423cb
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
> > @@ -0,0 +1,19 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1b.c b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
> > new file mode 100644
> > index 00000000000..54a579a9e37
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
> > @@ -0,0 +1,19 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
> > +
> > +extern void bar (void) __attribute__ ((nodirect_extern_access));
> > +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 } } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2a.c b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
> > new file mode 100644
> > index 00000000000..74fa8fc9d97
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2b.c b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
> > new file mode 100644
> > index 00000000000..ae2edff8d93
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
> > +
> > +extern int bar __attribute__ ((nodirect_extern_access));
> > +
> > +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 } } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3a.c b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
> > new file mode 100644
> > index 00000000000..4ca4332c4ab
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpie -mno-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 } } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3b.c b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
> > new file mode 100644
> > index 00000000000..c3888039834
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpie -mdirect-extern-access" } */
> > +
> > +extern int bar __attribute__ ((nodirect_extern_access));
> > +
> > +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 } } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4a.c b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
> > new file mode 100644
> > index 00000000000..9c3a199404c
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4b.c b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
> > new file mode 100644
> > index 00000000000..e1a50784bf9
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
> > +
> > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > +
> > +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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5a.c b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
> > new file mode 100644
> > index 00000000000..4d2e1732838
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5b.c b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
> > new file mode 100644
> > index 00000000000..81e98ed7836
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
> > +
> > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > +
> > +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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6a.c b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
> > new file mode 100644
> > index 00000000000..ece878e3c3a
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
> > @@ -0,0 +1,17 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6b.c b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
> > new file mode 100644
> > index 00000000000..3f679defdab
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
> > @@ -0,0 +1,17 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
> > +
> > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > +
> > +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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7a.c b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
> > new file mode 100644
> > index 00000000000..1de014d39c2
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7b.c b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
> > new file mode 100644
> > index 00000000000..984e2dc2752
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
> > @@ -0,0 +1,18 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
> > +
> > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > +
> > +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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > 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..7ba67de2156
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-8.c
> > @@ -0,0 +1,44 @@
> > +/* { dg-do assemble { target { *-*-linux* && { ! ia32 } } } } */
> > +/* { dg-require-effective-target maybe_x32 } */
> > +/* { dg-options "-mx32 -O2 -fno-pic -fexceptions -fasynchronous-unwind-tables -mno-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);
> > +}
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9a.c b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
> > new file mode 100644
> > index 00000000000..533f1d2ddb4
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > +
> > +/* Common symbol with -fpic.  */
> > +__attribute__((visibility("protected")))
> > +int xxx;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9b.c b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
> > new file mode 100644
> > index 00000000000..b6c66f43b40
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
> > @@ -0,0 +1,20 @@
> > +/* { dg-do compile { target *-*-linux* } } */
> > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > +
> > +/* Common symbol with -fpic.  */
> > +__attribute__((visibility("protected"), nodirect_extern_access))
> > +int xxx;
> > +
> > +int
> > +foo ()
> > +{
> > +  return xxx;
> > +}
> > +
> > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > +
> > --
> > 2.34.1
> >
>
>
> --
> BR,
> Hongtao
  
Fangrui Song May 1, 2022, 5:18 p.m. UTC | #3
On Tue, Feb 8, 2022 at 7:05 PM H.J. Lu via Gcc-patches
<gcc-patches@gcc.gnu.org> wrote:
>
> On Tue, Feb 8, 2022 at 6:38 PM Hongtao Liu <crazylht@gmail.com> wrote:
> >
> > On Fri, Jan 28, 2022 at 5:53 AM H.J. Lu via Gcc-patches
> > <gcc-patches@gcc.gnu.org> wrote:
> > >
> > > The v3 patch was posted at
> > >
> > > https://gcc.gnu.org/pipermail/gcc-patches/2021-July/574847.html
> > >
> > > There is no progress with repeated pings since then.  Glibc 2.35 and
> > > binutils 2.38 will support GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.
> > > I'd like to remove copy relocation to improve security and performance
> > > on x86 in GCC 12.  Here is the v4 patch
> >   This patch won't change default behavior for copy relocation(to
> > avoid conflict with existing .so files),
> > just give users an option under which copy relocation can be removed.
> >   The removal of copy relocation is an optimization of the linker(Also
> > improves security), whose
> > patches have been approved and committed to glibc2.35[1], binutils2.38[2].
> >   The compiler part is the final step in completing this optimization.
>
> Thanks for looking into it.  Since the new behavior is off by default and
> none of the maintainers feel comfortable to review my patch,  which is
> related to linker and glibc, for the last 6 months,  now is the good time
> to check it in as any other time.  I will check it in tomorrow.

Shall we move forward with "x86-64: Remove HAVE_LD_PIE_COPYRELOC"
(https://gcc.gnu.org/pipermail/gcc-patches/2021-November/582987.html)?
It's more about the correct behavior for -fpie.

The -mno-direct-extern-access transition will certainly take a long
time, but we should not let that block fixing the GCC 5 x86-64
regression.

> > [1] https://sourceware.org/pipermail/libc-alpha/2021-October/131742.html
> > [2] https://sourceware.org/pipermail/binutils/2021-July/117308.html
> > >
> > > 1. Rename the common option to x86 specific -mdirect-extern-access option.
> > > 2. Remove GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS linker check.
> > > 3. Use the existing GNU property function in x86 backend.
> > >
> > > This new behavior is off by default.  Are there any objections?
> > >
> > > Changes in the v3 patch.
> > >
> > > 1. GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support has been added to
> > > GNU binutils 2.38.  But the -z indirect-extern-access linker option is
> > > only available for Linux/x86.  However, the --max-cache-size=SIZE linker
> > > option was also addded within a day.  --max-cache-size=SIZE is used to
> > > check for GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS support.
> > >
> > > Changes in the v2 patch.
> > >
> > > 1. Rename the option to -fdirect-extern-access.
> > >
> > > ---
> > > On systems with copy relocation:
> > > * A copy in executable is created for the definition in a shared library
> > > at run-time by ld.so.
> > > * The copy is referenced by executable and shared libraries.
> > > * Executable can access the copy directly.
> > >
> > > Issues are:
> > > * Overhead of a copy, time and space, may be visible at run-time.
> > > * Read-only data in the shared library becomes read-write copy in
> > > executable at run-time.
> > > * Local access to data with the STV_PROTECTED visibility in the shared
> > > library must use GOT.
> > >
> > > On systems without function descriptor, function pointers vary depending
> > > on where and how the functions are defined.
> > > * If the function is defined in executable, it can be the address of
> > > function body.
> > > * If the function, including the function with STV_PROTECTED visibility,
> > > is defined in the shared library, it can be the address of the PLT entry
> > > in executable or shared library.
> > >
> > > Issues are:
> > > * The address of function body may not be used as its function pointer.
> > > * ld.so needs to search loaded shared libraries for the function pointer
> > > of the function with STV_PROTECTED visibility.
> > >
> > > Here is a proposal to remove copy relocation and use canonical function
> > > pointer:
> > >
> > > 1. Accesses, including in PIE and non-PIE, to undefined symbols must
> > > use GOT.
> > >   a. Linker may optimize out GOT access if the data is defined in PIE or
> > >   non-PIE.
> > > 2. Read-only data in the shared library remain read-only at run-time
> > > 3. Address of global data with the STV_PROTECTED visibility in the shared
> > > library is the address of data body.
> > >   a. Can use IP-relative access.
> > >   b. May need GOT without IP-relative access.
> > > 4. For systems without function descriptor,
> > >   a. All global function pointers of undefined functions in PIE and
> > >   non-PIE must use GOT.  Linker may optimize out GOT access if the
> > >   function is defined in PIE or non-PIE.
> > >   b. Function pointer of functions with the STV_PROTECTED visibility in
> > >   executable and shared library is the address of function body.
> > >    i. Can use IP-relative access.
> > >    ii. May need GOT without IP-relative access.
> > >    iii. Branches to undefined functions may use PLT.
> > > 5. Single global definition marker:
> > >
> > > Add GNU_PROPERTY_1_NEEDED:
> > >
> > > #define GNU_PROPERTY_1_NEEDED GNU_PROPERTY_UINT32_OR_LO
> > >
> > > to indicate the needed properties by the object file.
> > >
> > > Add GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS:
> > >
> > > #define GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS (1U << 0)
> > >
> > > to indicate that the object file requires canonical function pointers and
> > > cannot be used with copy relocation.  This bit should be cleared in
> > > executable when there are non-GOT or non-PLT relocations in relocatable
> > > input files without this bit set.
> > >
> > >   a. Protected symbol access within the shared library can be treated as
> > >   local.
> > >   b. Copy relocation should be disallowed at link-time and run-time.
> > >   c. GOT function pointer reference is required at link-time and run-time.
> > >
> > > The indirect external access marker can be used in the following ways:
> > >
> > > 1. Linker can decide the best way to resolve a relocation against a
> > > protected symbol before seeing all relocations against the symbol.
> > > 2. Dynamic linker can decide if it is an error to have a copy relocation
> > > in executable against the protected symbol in a shared library by checking
> > > if the shared library is built with -mno-direct-extern-access.
> > >
> > > Add a compiler option, -mdirect-extern-access. -mdirect-extern-access is
> > > the default.  With -mno-direct-extern-access:
> > >
> > > 1. Always to use GOT to access undefined symbols, including in PIE and
> > > non-PIE.  This is safe to do and does not break the ABI.
> > > 2. In executable and shared library, for symbols with the STV_PROTECTED
> > > visibility:
> > >   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.
> > > These break the ABI and resulting shared libraries may not be compatible
> > > with executables which are not compiled with -mno-direct-extern-access.
> > > 3. Generate an indirect external access marker in relocatable objects if
> > > supported by linker.
> > >
> > > H.J.
> > > ---
> > > Add -m[no-]direct-extern-access and nodirect_extern_access attribute.
> > > -mdirect-extern-access is the default.  With nodirect_extern_access
> > > attribute, GOT is always used to access undefined data and function
> > > symbols with nodirect_extern_access attribute, including in PIE and
> > > non-PIE.  With -mno-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 -mno-direct-extern-access to avoid copy relocation.
> > > 4. Add ix86_reloc_rw_mask for TARGET_ASM_RELOC_RW_MASK to avoid copy
> > > relocation with -mno-direct-extern-access.
> > >
> > > gcc/
> > >
> > >         PR target/35513
> > >         PR target/100593
> > >         * config/i386/gnu-property.cc: Include "i386-protos.h".
> > >         (file_end_indicate_exec_stack_and_gnu_property): Generate
> > >         a GNU_PROPERTY_1_NEEDED note for -mno-direct-extern-access or
> > >         nodirect_extern_access attribute.
> > >         * config/i386/i386-options.cc
> > >         (handle_nodirect_extern_access_attribute): New function.
> > >         (ix86_attribute_table): Add nodirect_extern_access attribute.
> > >         * config/i386/i386-protos.h (ix86_force_load_from_GOT_p): Add a
> > >         bool argument.
> > >         (ix86_has_no_direct_extern_access): New.
> > >         * config/i386/i386.cc (ix86_has_no_direct_extern_access): New.
> > >         (ix86_force_load_from_GOT_p): Add a bool argument to indicate
> > >         call operand.  Force non-call load from GOT for
> > >         -mno-direct-extern-access or nodirect_extern_access attribute.
> > >         (legitimate_pic_address_disp_p): Avoid copy relocation in PIE
> > >         for -mno-direct-extern-access or nodirect_extern_access attribute.
> > >         (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
> > >         -mno-direct-extern-access to avoid copy relocation.  Check
> > >         ptr_mode instead of TARGET_64BIT when selecting DW_EH_PE_sdata4.
> > >         (ix86_binds_local_p): Set ix86_has_no_direct_extern_access to
> > >         true for -mno-direct-extern-access or nodirect_extern_access
> > >         attribute.  Don't treat protected data as extern and avoid copy
> > >         relocation on common symbol with -mno-direct-extern-access or
> > >         nodirect_extern_access attribute.
> > >         (ix86_reloc_rw_mask): New to avoid copy relocation for
> > >         -mno-direct-extern-access.
> > >         (TARGET_ASM_RELOC_RW_MASK): New.
> > >         * config/i386/i386.opt: Add -mdirect-extern-access.
> > >         * doc/extend.texi: Document nodirect_extern_access attribute.
> > >         * doc/invoke.texi: Document -m[no-]direct-extern-access.
> > >
> > > gcc/testsuite/
> > >
> > >         PR target/35513
> > >         PR target/100593
> > >         * g++.target/i386/pr35513-1.C: New file.
> > >         * g++.target/i386/pr35513-2.C: Likewise.
> > >         * gcc.target/i386/pr35513-1a.c: Likewise.
> > >         * gcc.target/i386/pr35513-1b.c: Likewise.
> > >         * gcc.target/i386/pr35513-2a.c: Likewise.
> > >         * gcc.target/i386/pr35513-2b.c: Likewise.
> > >         * gcc.target/i386/pr35513-3a.c: Likewise.
> > >         * gcc.target/i386/pr35513-3b.c: Likewise.
> > >         * gcc.target/i386/pr35513-4a.c: Likewise.
> > >         * gcc.target/i386/pr35513-4b.c: Likewise.
> > >         * gcc.target/i386/pr35513-5a.c: Likewise.
> > >         * gcc.target/i386/pr35513-5b.c: Likewise.
> > >         * gcc.target/i386/pr35513-6a.c: Likewise.
> > >         * gcc.target/i386/pr35513-6b.c: Likewise.
> > >         * gcc.target/i386/pr35513-7a.c: Likewise.
> > >         * gcc.target/i386/pr35513-7b.c: Likewise.
> > >         * gcc.target/i386/pr35513-8a.c: Likewise.
> > >         * gcc.target/i386/pr35513-8b.c: Likewise.
> > >         * gcc.target/i386/pr35513-9a.c: Likewise.
> > >         * gcc.target/i386/pr35513-9b.c: Likewise.
> > >         * gcc.target/i386/pr35513-10a.c: Likewise.
> > >         * gcc.target/i386/pr35513-10b.c: Likewise.
> > >         * gcc.target/i386/pr35513-11a.c: Likewise.
> > >         * gcc.target/i386/pr35513-11b.c: Likewise.
> > >         * gcc.target/i386/pr35513-12a.c: Likewise.
> > >         * gcc.target/i386/pr35513-12b.c: Likewise.
> > > ---
> > >  gcc/config/i386/gnu-property.cc             | 10 ++-
> > >  gcc/config/i386/i386-options.cc             | 32 ++++++++++
> > >  gcc/config/i386/i386-protos.h               |  4 +-
> > >  gcc/config/i386/i386.cc                     | 67 ++++++++++++++++-----
> > >  gcc/config/i386/i386.opt                    |  4 ++
> > >  gcc/doc/extend.texi                         |  6 ++
> > >  gcc/doc/invoke.texi                         | 15 ++++-
> > >  gcc/testsuite/g++.target/i386/pr35513-1.C   | 25 ++++++++
> > >  gcc/testsuite/g++.target/i386/pr35513-2.C   | 53 ++++++++++++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-10a.c | 20 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-10b.c | 20 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-11a.c | 20 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-11b.c | 20 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-12a.c | 20 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-12b.c | 20 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-1a.c  | 19 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-1b.c  | 19 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-2a.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-2b.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-3a.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-3b.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-4a.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-4b.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-5a.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-5b.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-6a.c  | 17 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-6b.c  | 17 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-7a.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-7b.c  | 18 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-8.c   | 44 ++++++++++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-9a.c  | 20 ++++++
> > >  gcc/testsuite/gcc.target/i386/pr35513-9b.c  | 20 ++++++
> > >  32 files changed, 655 insertions(+), 17 deletions(-)
> > >  create mode 100644 gcc/testsuite/g++.target/i386/pr35513-1.C
> > >  create mode 100644 gcc/testsuite/g++.target/i386/pr35513-2.C
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-10b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-11b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-12b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-1b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-2b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-3b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-4b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-5b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-6b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-7b.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-8.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9a.c
> > >  create mode 100644 gcc/testsuite/gcc.target/i386/pr35513-9b.c
> > >
> > > diff --git a/gcc/config/i386/gnu-property.cc b/gcc/config/i386/gnu-property.cc
> > > index f08984f32a1..ea63c1ec58c 100644
> > > --- a/gcc/config/i386/gnu-property.cc
> > > +++ b/gcc/config/i386/gnu-property.cc
> > > @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3.  If not see
> > >  #include "tm.h"
> > >  #include "output.h"
> > >  #include "linux-common.h"
> > > +#include "i386-protos.h"
> > >
> > >  static void
> > >  emit_gnu_property (unsigned int type, unsigned int data)
> > > @@ -60,7 +61,9 @@ file_end_indicate_exec_stack_and_gnu_property (void)
> > >  {
> > >    file_end_indicate_exec_stack ();
> > >
> > > -  if (flag_cf_protection == CF_NONE && !ix86_needed)
> > > +  if (flag_cf_protection == CF_NONE
> > > +      && !ix86_needed
> > > +      && !ix86_has_no_direct_extern_access)
> > >      return;
> > >
> > >    unsigned int feature_1 = 0;
> > > @@ -121,4 +124,9 @@ file_end_indicate_exec_stack_and_gnu_property (void)
> > >    /* Generate GNU_PROPERTY_X86_ISA_1_NEEDED.  */
> > >    if (isa_1)
> > >      emit_gnu_property (0xc0008002, isa_1);
> > > +
> > > +  if (ix86_has_no_direct_extern_access)
> > > +    /* Emite a GNU_PROPERTY_1_NEEDED note with
> > > +       GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.  */
> > > +    emit_gnu_property (0xb0008000, (1U << 0));
> > >  }
> > > diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
> > > index 715d9a15ff5..b82f7060007 100644
> > > --- a/gcc/config/i386/i386-options.cc
> > > +++ b/gcc/config/i386/i386-options.cc
> > > @@ -3775,6 +3775,36 @@ ix86_handle_fentry_name (tree *node, tree name, tree args,
> > >    return NULL_TREE;
> > >  }
> > >
> > > +/* Handle a "nodirect_extern_access" attribute; arguments as in
> > > +   struct attribute_spec.handler.  */
> > > +
> > > +static tree
> > > +handle_nodirect_extern_access_attribute (tree *pnode, tree name,
> > > +                                        tree ARG_UNUSED (args),
> > > +                                        int ARG_UNUSED (flags),
> > > +                                        bool *no_add_attrs)
> > > +{
> > > +  tree node = *pnode;
> > > +
> > > +  if (VAR_OR_FUNCTION_DECL_P (node))
> > > +    {
> > > +      if ((!TREE_STATIC (node) && TREE_CODE (node) != FUNCTION_DECL
> > > +          && !DECL_EXTERNAL (node)) || !TREE_PUBLIC (node))
> > > +       {
> > > +         warning (OPT_Wattributes,
> > > +                  "%qE attribute have effect only on public objects", name);
> > > +         *no_add_attrs = true;
> > > +       }
> > > +    }
> > > +  else
> > > +    {
> > > +      warning (OPT_Wattributes, "%qE attribute ignored", name);
> > > +      *no_add_attrs = true;
> > > +    }
> > > +
> > > +  return NULL_TREE;
> > > +}
> > > +
> > >  /* Table of valid machine attributes.  */
> > >  const struct attribute_spec ix86_attribute_table[] =
> > >  {
> > > @@ -3855,6 +3885,8 @@ const struct attribute_spec ix86_attribute_table[] =
> > >      ix86_handle_fentry_name, NULL },
> > >    { "cf_check", 0, 0, true, false, false, false,
> > >      ix86_handle_fndecl_attribute, NULL },
> > > +  { "nodirect_extern_access", 0, 0, true, false, false, false,
> > > +    handle_nodirect_extern_access_attribute, NULL },
> > >
> > >    /* End element.  */
> > >    { NULL, 0, 0, false, false, false, false, NULL, NULL }
> > > diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
> > > index 6b3c9516a7f..b7e9aa75d25 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);
> > >
> > > @@ -401,3 +401,5 @@ extern rtl_opt_pass *make_pass_insert_endbr_and_patchable_area
> > >    (gcc::context *);
> > >  extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
> > >    (gcc::context *);
> > > +
> > > +extern bool ix86_has_no_direct_extern_access;
> > > diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
> > > index ad5a5caa413..3f520c007bd 100644
> > > --- a/gcc/config/i386/i386.cc
> > > +++ b/gcc/config/i386/i386.cc
> > > @@ -363,6 +363,9 @@ unsigned int ix86_default_incoming_stack_boundary;
> > >  /* Alignment for incoming stack boundary in bits.  */
> > >  unsigned int ix86_incoming_stack_boundary;
> > >
> > > +/* True if there is no direct access to extern symbols.  */
> > > +bool ix86_has_no_direct_extern_access;
> > > +
> > >  /* Calling abi specific va_list type nodes.  */
> > >  tree sysv_va_list_type_node;
> > >  tree ms_va_list_type_node;
> > > @@ -10513,13 +10516,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: -mno-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
> > > @@ -10527,11 +10534,16 @@ 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
> > > +              && (!ix86_direct_extern_access
> > > +                  || (SYMBOL_REF_DECL (x)
> > > +                      && lookup_attribute ("nodirect_extern_access",
> > > +                                           DECL_ATTRIBUTES (SYMBOL_REF_DECL (x))))))
> > > +             || (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));
> > >  }
> > >
> > > @@ -10798,7 +10810,11 @@ legitimate_pic_address_disp_p (rtx disp)
> > >             }
> > >           else if (!SYMBOL_REF_FAR_ADDR_P (op0)
> > >                    && (SYMBOL_REF_LOCAL_P (op0)
> > > -                      || (HAVE_LD_PIE_COPYRELOC
> > > +                      || ((ix86_direct_extern_access
> > > +                           && !(SYMBOL_REF_DECL (op0)
> > > +                                && lookup_attribute ("nodirect_extern_access",
> > > +                                                     DECL_ATTRIBUTES (SYMBOL_REF_DECL (op0)))))
> > > +                          && HAVE_LD_PIE_COPYRELOC
> > >                            && flag_pie
> > >                            && !SYMBOL_REF_WEAK (op0)
> > >                            && !SYMBOL_REF_FUNCTION_P (op0)))
> > > @@ -13754,7 +13770,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.  */
> > > @@ -22520,10 +22536,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 || !ix86_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;
> > > @@ -23613,10 +23629,28 @@ 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)));
> > > +  bool direct_extern_access
> > > +    = (ix86_direct_extern_access
> > > +       && !(VAR_OR_FUNCTION_DECL_P (exp)
> > > +           && lookup_attribute ("nodirect_extern_access",
> > > +                                DECL_ATTRIBUTES (exp))));
> > > +  if (!direct_extern_access)
> > > +    ix86_has_no_direct_extern_access = true;
> > > +  return default_binds_local_p_3 (exp, flag_shlib != 0, true,
> > > +                                 direct_extern_access,
> > > +                                 (direct_extern_access
> > > +                                  && (!flag_pic
> > > +                                      || (TARGET_64BIT
> > > +                                          && HAVE_LD_PIE_COPYRELOC != 0))));
> > > +}
> > > +
> > > +/* If flag_pic or ix86_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 || !ix86_direct_extern_access) ? 3 : 0;
> > >  }
> > >  #endif
> > >
> > > @@ -24681,6 +24715,11 @@ ix86_libgcc_floating_mode_supported_p
> > >  #undef TARGET_IFUNC_REF_LOCAL_OK
> > >  #define TARGET_IFUNC_REF_LOCAL_OK hook_bool_void_true
> > >
> > > +#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/config/i386/i386.opt b/gcc/config/i386/i386.opt
> > > index eb829d13d40..d8e8656a8ab 100644
> > > --- a/gcc/config/i386/i386.opt
> > > +++ b/gcc/config/i386/i386.opt
> > > @@ -1206,3 +1206,7 @@ Support MWAIT and MONITOR built-in functions and code generation.
> > >  mavx512fp16
> > >  Target Mask(ISA2_AVX512FP16) Var(ix86_isa_flags2) Save
> > >  Support MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512FP16 built-in functions and code generation.
> > > +
> > > +mdirect-extern-access
> > > +Target Var(ix86_direct_extern_access) Init(1)
> > > +Do not use GOT to access external symbols.
> > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
> > > index a961fc4e0a2..dbc453c7108 100644
> > > --- a/gcc/doc/extend.texi
> > > +++ b/gcc/doc/extend.texi
> > > @@ -7220,6 +7220,12 @@ On x86 targets, the @code{fentry_section} attribute sets the name
> > >  of the section to record function entry instrumentation calls in when
> > >  enabled with @option{-pg -mrecord-mcount}
> > >
> > > +@item nodirect_extern_access
> > > +@cindex @code{nodirect_extern_access} function attribute
> > > +@opindex mno-direct-extern-access
> > > +This attribute, attached to a global variable or function, is the
> > > +counterpart to option @option{-mno-direct-extern-access}.
> > > +
> > >  @end table
> > >
> > >  @node Xstormy16 Function Attributes
> > > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > > index 9e588db4fce..7f2de745f48 100644
> > > --- a/gcc/doc/invoke.texi
> > > +++ b/gcc/doc/invoke.texi
> > > @@ -1436,7 +1436,7 @@ See RS/6000 and PowerPC Options.
> > >  -mgeneral-regs-only  -mcall-ms2sysv-xlogues -mrelax-cmpxchg-loop @gol
> > >  -mindirect-branch=@var{choice}  -mfunction-return=@var{choice} @gol
> > >  -mindirect-branch-register -mharden-sls=@var{choice} @gol
> > > --mindirect-branch-cs-prefix -mneeded}
> > > +-mindirect-branch-cs-prefix -mneeded -mno-direct-extern-access}
> > >
> > >  @emph{x86 Windows Options}
> > >  @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
> > > @@ -32789,6 +32789,19 @@ x32 environments.
> > >  @opindex mneeded
> > >  Emit GNU_PROPERTY_X86_ISA_1_NEEDED GNU property for Linux target to
> > >  indicate the micro-architecture ISA level required to execute the binary.
> > > +
> > > +@item -mno-direct-extern-access
> > > +@opindex mno-direct-extern-access
> > > +@opindex mdirect-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.  The default is
> > > +@option{-mdirect-extern-access}.
> > > +
> > > +@strong{Warning:} shared libraries compiled with
> > > +@option{-mno-direct-extern-access} and executable compiled with
> > > +@option{-mdirect-extern-access} may not be binary compatible if
> > > +protected symbols are used in shared libraries and executable.
> > >  @end table
> > >
> > >  @node x86 Windows Options
> > > diff --git a/gcc/testsuite/g++.target/i386/pr35513-1.C b/gcc/testsuite/g++.target/i386/pr35513-1.C
> > > new file mode 100644
> > > index 00000000000..6f8db37fb7c
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.target/i386/pr35513-1.C
> > > @@ -0,0 +1,25 @@
> > > +// { dg-do run }
> > > +// { dg-options "-O2 -mno-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++.target/i386/pr35513-2.C b/gcc/testsuite/g++.target/i386/pr35513-2.C
> > > new file mode 100644
> > > index 00000000000..9143ff3f0a5
> > > --- /dev/null
> > > +++ b/gcc/testsuite/g++.target/i386/pr35513-2.C
> > > @@ -0,0 +1,53 @@
> > > +// { dg-do run  }
> > > +// { dg-options "-O2 -mno-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-10a.c b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
> > > new file mode 100644
> > > index 00000000000..d7b5c98fa8c
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > > +
> > > +/* Weak common symbol with -fpic.  */
> > > +__attribute__((weak, visibility("protected")))
> > > +int xxx;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-10b.c b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
> > > new file mode 100644
> > > index 00000000000..a40692e6e3d
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > > +
> > > +/* Weak common symbol with -fpic.  */
> > > +__attribute__((weak, visibility("protected"),nodirect_extern_access))
> > > +int xxx;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11a.c b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
> > > new file mode 100644
> > > index 00000000000..5489f1e5cee
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > > +
> > > +/* Initialized symbol with -fpic.  */
> > > +__attribute__((visibility("protected")))
> > > +int xxx = -1;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11b.c b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
> > > new file mode 100644
> > > index 00000000000..2704900fed5
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > > +
> > > +/* Initialized symbol with -fpic.  */
> > > +__attribute__((visibility("protected"), nodirect_extern_access))
> > > +int xxx = -1;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12a.c b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
> > > new file mode 100644
> > > index 00000000000..8b3123f9042
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > > +
> > > +/* Weak initialized symbol with -fpic.  */
> > > +__attribute__((weak, visibility("protected")))
> > > +int xxx = -1;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12b.c b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
> > > new file mode 100644
> > > index 00000000000..a1b6b9e92df
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > > +
> > > +/* Weak initialized symbol with -fpic.  */
> > > +__attribute__((weak, visibility("protected"), nodirect_extern_access))
> > > +int xxx = -1;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1a.c b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
> > > new file mode 100644
> > > index 00000000000..972542423cb
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
> > > @@ -0,0 +1,19 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1b.c b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
> > > new file mode 100644
> > > index 00000000000..54a579a9e37
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
> > > @@ -0,0 +1,19 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
> > > +
> > > +extern void bar (void) __attribute__ ((nodirect_extern_access));
> > > +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 } } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2a.c b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
> > > new file mode 100644
> > > index 00000000000..74fa8fc9d97
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2b.c b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
> > > new file mode 100644
> > > index 00000000000..ae2edff8d93
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
> > > +
> > > +extern int bar __attribute__ ((nodirect_extern_access));
> > > +
> > > +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 } } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3a.c b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
> > > new file mode 100644
> > > index 00000000000..4ca4332c4ab
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpie -mno-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 } } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3b.c b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
> > > new file mode 100644
> > > index 00000000000..c3888039834
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpie -mdirect-extern-access" } */
> > > +
> > > +extern int bar __attribute__ ((nodirect_extern_access));
> > > +
> > > +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 } } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4a.c b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
> > > new file mode 100644
> > > index 00000000000..9c3a199404c
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4b.c b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
> > > new file mode 100644
> > > index 00000000000..e1a50784bf9
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
> > > +
> > > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > > +
> > > +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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5a.c b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
> > > new file mode 100644
> > > index 00000000000..4d2e1732838
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5b.c b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
> > > new file mode 100644
> > > index 00000000000..81e98ed7836
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
> > > +
> > > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > > +
> > > +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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6a.c b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
> > > new file mode 100644
> > > index 00000000000..ece878e3c3a
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
> > > @@ -0,0 +1,17 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6b.c b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
> > > new file mode 100644
> > > index 00000000000..3f679defdab
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
> > > @@ -0,0 +1,17 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
> > > +
> > > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > > +
> > > +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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7a.c b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
> > > new file mode 100644
> > > index 00000000000..1de014d39c2
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7b.c b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
> > > new file mode 100644
> > > index 00000000000..984e2dc2752
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
> > > @@ -0,0 +1,18 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
> > > +
> > > +extern void foo (void) __attribute__ ((nodirect_extern_access));
> > > +
> > > +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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > 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..7ba67de2156
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-8.c
> > > @@ -0,0 +1,44 @@
> > > +/* { dg-do assemble { target { *-*-linux* && { ! ia32 } } } } */
> > > +/* { dg-require-effective-target maybe_x32 } */
> > > +/* { dg-options "-mx32 -O2 -fno-pic -fexceptions -fasynchronous-unwind-tables -mno-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);
> > > +}
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9a.c b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
> > > new file mode 100644
> > > index 00000000000..533f1d2ddb4
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
> > > +
> > > +/* Common symbol with -fpic.  */
> > > +__attribute__((visibility("protected")))
> > > +int xxx;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9b.c b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
> > > new file mode 100644
> > > index 00000000000..b6c66f43b40
> > > --- /dev/null
> > > +++ b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
> > > @@ -0,0 +1,20 @@
> > > +/* { dg-do compile { target *-*-linux* } } */
> > > +/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
> > > +
> > > +/* Common symbol with -fpic.  */
> > > +__attribute__((visibility("protected"), nodirect_extern_access))
> > > +int xxx;
> > > +
> > > +int
> > > +foo ()
> > > +{
> > > +  return xxx;
> > > +}
> > > +
> > > +/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
> > > +/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
> > > +/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
> > > +
> > > --
> > > 2.34.1
> > >
> >
> >
> > --
> > BR,
> > Hongtao
>
  

Patch

diff --git a/gcc/config/i386/gnu-property.cc b/gcc/config/i386/gnu-property.cc
index f08984f32a1..ea63c1ec58c 100644
--- a/gcc/config/i386/gnu-property.cc
+++ b/gcc/config/i386/gnu-property.cc
@@ -23,6 +23,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tm.h"
 #include "output.h"
 #include "linux-common.h"
+#include "i386-protos.h"
 
 static void
 emit_gnu_property (unsigned int type, unsigned int data)
@@ -60,7 +61,9 @@  file_end_indicate_exec_stack_and_gnu_property (void)
 {
   file_end_indicate_exec_stack ();
 
-  if (flag_cf_protection == CF_NONE && !ix86_needed)
+  if (flag_cf_protection == CF_NONE
+      && !ix86_needed
+      && !ix86_has_no_direct_extern_access)
     return;
 
   unsigned int feature_1 = 0;
@@ -121,4 +124,9 @@  file_end_indicate_exec_stack_and_gnu_property (void)
   /* Generate GNU_PROPERTY_X86_ISA_1_NEEDED.  */
   if (isa_1)
     emit_gnu_property (0xc0008002, isa_1);
+
+  if (ix86_has_no_direct_extern_access)
+    /* Emite a GNU_PROPERTY_1_NEEDED note with
+       GNU_PROPERTY_1_NEEDED_INDIRECT_EXTERN_ACCESS.  */
+    emit_gnu_property (0xb0008000, (1U << 0));
 }
diff --git a/gcc/config/i386/i386-options.cc b/gcc/config/i386/i386-options.cc
index 715d9a15ff5..b82f7060007 100644
--- a/gcc/config/i386/i386-options.cc
+++ b/gcc/config/i386/i386-options.cc
@@ -3775,6 +3775,36 @@  ix86_handle_fentry_name (tree *node, tree name, tree args,
   return NULL_TREE;
 }
 
+/* Handle a "nodirect_extern_access" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_nodirect_extern_access_attribute (tree *pnode, tree name,
+					 tree ARG_UNUSED (args),
+					 int ARG_UNUSED (flags),
+					 bool *no_add_attrs)
+{
+  tree node = *pnode;
+
+  if (VAR_OR_FUNCTION_DECL_P (node))
+    {
+      if ((!TREE_STATIC (node) && TREE_CODE (node) != FUNCTION_DECL
+	   && !DECL_EXTERNAL (node)) || !TREE_PUBLIC (node))
+	{
+	  warning (OPT_Wattributes,
+		   "%qE attribute have effect only on public objects", name);
+	  *no_add_attrs = true;
+	}
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Table of valid machine attributes.  */
 const struct attribute_spec ix86_attribute_table[] =
 {
@@ -3855,6 +3885,8 @@  const struct attribute_spec ix86_attribute_table[] =
     ix86_handle_fentry_name, NULL },
   { "cf_check", 0, 0, true, false, false, false,
     ix86_handle_fndecl_attribute, NULL },
+  { "nodirect_extern_access", 0, 0, true, false, false, false,
+    handle_nodirect_extern_access_attribute, NULL },
 
   /* End element.  */
   { NULL, 0, 0, false, false, false, false, NULL, NULL }
diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h
index 6b3c9516a7f..b7e9aa75d25 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);
 
@@ -401,3 +401,5 @@  extern rtl_opt_pass *make_pass_insert_endbr_and_patchable_area
   (gcc::context *);
 extern rtl_opt_pass *make_pass_remove_partial_avx_dependency
   (gcc::context *);
+
+extern bool ix86_has_no_direct_extern_access;
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index ad5a5caa413..3f520c007bd 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -363,6 +363,9 @@  unsigned int ix86_default_incoming_stack_boundary;
 /* Alignment for incoming stack boundary in bits.  */
 unsigned int ix86_incoming_stack_boundary;
 
+/* True if there is no direct access to extern symbols.  */
+bool ix86_has_no_direct_extern_access;
+
 /* Calling abi specific va_list type nodes.  */
 tree sysv_va_list_type_node;
 tree ms_va_list_type_node;
@@ -10513,13 +10516,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: -mno-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
@@ -10527,11 +10534,16 @@  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
+	       && (!ix86_direct_extern_access
+		   || (SYMBOL_REF_DECL (x)
+		       && lookup_attribute ("nodirect_extern_access",
+					    DECL_ATTRIBUTES (SYMBOL_REF_DECL (x))))))
+	      || (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));
 }
 
@@ -10798,7 +10810,11 @@  legitimate_pic_address_disp_p (rtx disp)
 	    }
 	  else if (!SYMBOL_REF_FAR_ADDR_P (op0)
 		   && (SYMBOL_REF_LOCAL_P (op0)
-		       || (HAVE_LD_PIE_COPYRELOC
+		       || ((ix86_direct_extern_access
+			    && !(SYMBOL_REF_DECL (op0)
+				 && lookup_attribute ("nodirect_extern_access",
+						      DECL_ATTRIBUTES (SYMBOL_REF_DECL (op0)))))
+			   && HAVE_LD_PIE_COPYRELOC
 			   && flag_pie
 			   && !SYMBOL_REF_WEAK (op0)
 			   && !SYMBOL_REF_FUNCTION_P (op0)))
@@ -13754,7 +13770,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.  */
@@ -22520,10 +22536,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 || !ix86_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;
@@ -23613,10 +23629,28 @@  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)));
+  bool direct_extern_access
+    = (ix86_direct_extern_access
+       && !(VAR_OR_FUNCTION_DECL_P (exp)
+	    && lookup_attribute ("nodirect_extern_access",
+				 DECL_ATTRIBUTES (exp))));
+  if (!direct_extern_access)
+    ix86_has_no_direct_extern_access = true;
+  return default_binds_local_p_3 (exp, flag_shlib != 0, true,
+				  direct_extern_access,
+				  (direct_extern_access
+				   && (!flag_pic
+				       || (TARGET_64BIT
+					   && HAVE_LD_PIE_COPYRELOC != 0))));
+}
+
+/* If flag_pic or ix86_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 || !ix86_direct_extern_access) ? 3 : 0;
 }
 #endif
 
@@ -24681,6 +24715,11 @@  ix86_libgcc_floating_mode_supported_p
 #undef TARGET_IFUNC_REF_LOCAL_OK
 #define TARGET_IFUNC_REF_LOCAL_OK hook_bool_void_true
 
+#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/config/i386/i386.opt b/gcc/config/i386/i386.opt
index eb829d13d40..d8e8656a8ab 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -1206,3 +1206,7 @@  Support MWAIT and MONITOR built-in functions and code generation.
 mavx512fp16
 Target Mask(ISA2_AVX512FP16) Var(ix86_isa_flags2) Save
 Support MMX, SSE, SSE2, SSE3, SSSE3, SSE4.1, SSE4.2, AVX, AVX2, AVX512F and AVX512FP16 built-in functions and code generation.
+
+mdirect-extern-access
+Target Var(ix86_direct_extern_access) Init(1)
+Do not use GOT to access external symbols.
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index a961fc4e0a2..dbc453c7108 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -7220,6 +7220,12 @@  On x86 targets, the @code{fentry_section} attribute sets the name
 of the section to record function entry instrumentation calls in when
 enabled with @option{-pg -mrecord-mcount}
 
+@item nodirect_extern_access
+@cindex @code{nodirect_extern_access} function attribute
+@opindex mno-direct-extern-access
+This attribute, attached to a global variable or function, is the
+counterpart to option @option{-mno-direct-extern-access}.
+
 @end table
 
 @node Xstormy16 Function Attributes
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 9e588db4fce..7f2de745f48 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1436,7 +1436,7 @@  See RS/6000 and PowerPC Options.
 -mgeneral-regs-only  -mcall-ms2sysv-xlogues -mrelax-cmpxchg-loop @gol
 -mindirect-branch=@var{choice}  -mfunction-return=@var{choice} @gol
 -mindirect-branch-register -mharden-sls=@var{choice} @gol
--mindirect-branch-cs-prefix -mneeded}
+-mindirect-branch-cs-prefix -mneeded -mno-direct-extern-access}
 
 @emph{x86 Windows Options}
 @gccoptlist{-mconsole  -mcygwin  -mno-cygwin  -mdll @gol
@@ -32789,6 +32789,19 @@  x32 environments.
 @opindex mneeded
 Emit GNU_PROPERTY_X86_ISA_1_NEEDED GNU property for Linux target to
 indicate the micro-architecture ISA level required to execute the binary.
+
+@item -mno-direct-extern-access
+@opindex mno-direct-extern-access
+@opindex mdirect-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.  The default is
+@option{-mdirect-extern-access}.
+
+@strong{Warning:} shared libraries compiled with
+@option{-mno-direct-extern-access} and executable compiled with
+@option{-mdirect-extern-access} may not be binary compatible if
+protected symbols are used in shared libraries and executable.
 @end table
 
 @node x86 Windows Options
diff --git a/gcc/testsuite/g++.target/i386/pr35513-1.C b/gcc/testsuite/g++.target/i386/pr35513-1.C
new file mode 100644
index 00000000000..6f8db37fb7c
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr35513-1.C
@@ -0,0 +1,25 @@ 
+// { dg-do run }
+// { dg-options "-O2 -mno-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++.target/i386/pr35513-2.C b/gcc/testsuite/g++.target/i386/pr35513-2.C
new file mode 100644
index 00000000000..9143ff3f0a5
--- /dev/null
+++ b/gcc/testsuite/g++.target/i386/pr35513-2.C
@@ -0,0 +1,53 @@ 
+// { dg-do run  }
+// { dg-options "-O2 -mno-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-10a.c b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
new file mode 100644
index 00000000000..d7b5c98fa8c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-10a.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
+
+/* Weak common symbol with -fpic.  */
+__attribute__((weak, visibility("protected")))
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-10b.c b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
new file mode 100644
index 00000000000..a40692e6e3d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-10b.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
+
+/* Weak common symbol with -fpic.  */
+__attribute__((weak, visibility("protected"),nodirect_extern_access))
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11a.c b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
new file mode 100644
index 00000000000..5489f1e5cee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-11a.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
+
+/* Initialized symbol with -fpic.  */
+__attribute__((visibility("protected")))
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-11b.c b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
new file mode 100644
index 00000000000..2704900fed5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-11b.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
+
+/* Initialized symbol with -fpic.  */
+__attribute__((visibility("protected"), nodirect_extern_access))
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12a.c b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
new file mode 100644
index 00000000000..8b3123f9042
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-12a.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
+
+/* Weak initialized symbol with -fpic.  */
+__attribute__((weak, visibility("protected")))
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-12b.c b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
new file mode 100644
index 00000000000..a1b6b9e92df
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-12b.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
+
+/* Weak initialized symbol with -fpic.  */
+__attribute__((weak, visibility("protected"), nodirect_extern_access))
+int xxx = -1;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1a.c b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
new file mode 100644
index 00000000000..972542423cb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-1a.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-1b.c b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
new file mode 100644
index 00000000000..54a579a9e37
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-1b.c
@@ -0,0 +1,19 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
+
+extern void bar (void) __attribute__ ((nodirect_extern_access));
+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 } } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2a.c b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
new file mode 100644
index 00000000000..74fa8fc9d97
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-2a.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -mno-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 } } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-2b.c b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
new file mode 100644
index 00000000000..ae2edff8d93
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-2b.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fno-pic -mdirect-extern-access" } */
+
+extern int bar __attribute__ ((nodirect_extern_access));
+
+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 } } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3a.c b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
new file mode 100644
index 00000000000..4ca4332c4ab
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-3a.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie -mno-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 } } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-3b.c b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
new file mode 100644
index 00000000000..c3888039834
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-3b.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie -mdirect-extern-access" } */
+
+extern int bar __attribute__ ((nodirect_extern_access));
+
+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 } } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4a.c b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
new file mode 100644
index 00000000000..9c3a199404c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-4a.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-4b.c b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
new file mode 100644
index 00000000000..e1a50784bf9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-4b.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
+
+extern void foo (void) __attribute__ ((nodirect_extern_access));
+
+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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5a.c b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
new file mode 100644
index 00000000000..4d2e1732838
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-5a.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-5b.c b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
new file mode 100644
index 00000000000..81e98ed7836
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-5b.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
+
+extern void foo (void) __attribute__ ((nodirect_extern_access));
+
+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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6a.c b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
new file mode 100644
index 00000000000..ece878e3c3a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-6a.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fno-pic -mno-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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-6b.c b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
new file mode 100644
index 00000000000..3f679defdab
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-6b.c
@@ -0,0 +1,17 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fno-pic -mdirect-extern-access" } */
+
+extern void foo (void) __attribute__ ((nodirect_extern_access));
+
+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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7a.c b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
new file mode 100644
index 00000000000..1de014d39c2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-7a.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fpic -mno-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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-7b.c b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
new file mode 100644
index 00000000000..984e2dc2752
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-7b.c
@@ -0,0 +1,18 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fplt -fpic -mdirect-extern-access" } */
+
+extern void foo (void) __attribute__ ((nodirect_extern_access));
+
+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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
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..7ba67de2156
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-8.c
@@ -0,0 +1,44 @@ 
+/* { dg-do assemble { target { *-*-linux* && { ! ia32 } } } } */
+/* { dg-require-effective-target maybe_x32 } */
+/* { dg-options "-mx32 -O2 -fno-pic -fexceptions -fasynchronous-unwind-tables -mno-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);
+}
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9a.c b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
new file mode 100644
index 00000000000..533f1d2ddb4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-9a.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mno-direct-extern-access" } */
+
+/* Common symbol with -fpic.  */
+__attribute__((visibility("protected")))
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+
diff --git a/gcc/testsuite/gcc.target/i386/pr35513-9b.c b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
new file mode 100644
index 00000000000..b6c66f43b40
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr35513-9b.c
@@ -0,0 +1,20 @@ 
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -mdirect-extern-access" } */
+
+/* Common symbol with -fpic.  */
+__attribute__((visibility("protected"), nodirect_extern_access))
+int xxx;
+
+int
+foo ()
+{
+  return xxx;
+}
+
+/* { dg-final { scan-assembler "xxx\\(%rip\\)" { 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 } } } */
+/* { dg-final { scan-assembler "\.section\[ \t]+.note.gnu.property," } } */
+/* { dg-final { scan-assembler "\.long\[ \t]+0xb0008000" } } */
+