diff mbox

PR target/65248: [5 Regression] Copy relocation in PIE against protected symbol

Message ID 20150228164223.GA22402@gmail.com
State New
Headers show

Commit Message

H.J. Lu Feb. 28, 2015, 4:42 p.m. UTC
Ue copy relocation in PIE improves performance.  But copy relocation
can't be used to access protected symbols defined in shared libaries
and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
know if an external definition is protected or not.  This option adds
-mcopyreloc-in-pie to give user an option to turn it off to avoid problem
at link-time.  OK for trunk?

Thanks.


H.J.
---
gcc/

	PR target/65248
	* config/i386/i386.c (ix86_option_override_internal): Set
	flag_copyreloc_in_pie to HAVE_LD_PIE_COPYRELOC if not set.
	(legitimate_pic_address_disp_p): Replace HAVE_LD_PIE_COPYRELOC
	with flag_copyreloc_in_pie.
	* config/i386/i386.opt (mcopyreloc-in-pie): New.
	* doc/invoke.texi: Document -mcopyreloc-in-pie.

gcc/testsuite/

	PR target/65248
	* gcc.target/i386/pr65248-1.c: New.
	* gcc.target/i386/pr65248-2.c: Likewise.
	* gcc.target/i386/pr65248-3.c: Likewise.
	* gcc.target/i386/pr65248-4.c: Likewise.
---
 gcc/config/i386/i386.c                    |  6 +++++-
 gcc/config/i386/i386.opt                  |  4 ++++
 gcc/doc/invoke.texi                       | 10 +++++++++-
 gcc/testsuite/gcc.target/i386/pr65248-1.c | 13 +++++++++++++
 gcc/testsuite/gcc.target/i386/pr65248-2.c | 13 +++++++++++++
 gcc/testsuite/gcc.target/i386/pr65248-3.c | 16 ++++++++++++++++
 gcc/testsuite/gcc.target/i386/pr65248-4.c | 16 ++++++++++++++++
 7 files changed, 76 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-1.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-2.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-3.c
 create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-4.c

Comments

Sandra Loosemore March 1, 2015, 1:27 a.m. UTC | #1
On 02/28/2015 09:42 AM, H.J. Lu wrote:
> @@ -22704,6 +22705,13 @@ For systems that use the GNU C Library, the default is on.
>   Specify that the assembler should encode SSE instructions with VEX
>   prefix.  The option @option{-mavx} turns this on by default.
>
> +@item -mcopyreloc-in-pie
> +@itemx -mno-copyreloc-in-pie
> +@opindex mcopyreloc-in-pie
> +Use copy relocations in Position Independent Executable (PIE).  It
> +requires linker support.  This option is turned on by default if linker
> +used to build GCC supports it.
> +

How about:  "...if GCC is configured to use a linker that supports these 
relocations."

I assume this is a property of the target linker, and not literally the 
host linker used to build GCC?

-Sandra
Uros Bizjak March 2, 2015, 7:48 a.m. UTC | #2
On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> Ue copy relocation in PIE improves performance.  But copy relocation
> can't be used to access protected symbols defined in shared libaries
> and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
> know if an external definition is protected or not.  This option adds
> -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
> at link-time.  OK for trunk?

If the option does not work universally for all cases, then the
default should be off.

Uros.
Richard Biener March 2, 2015, 8:40 a.m. UTC | #3
On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> Ue copy relocation in PIE improves performance.  But copy relocation
> can't be used to access protected symbols defined in shared libaries
> and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
> know if an external definition is protected or not.  This option adds
> -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
> at link-time.  OK for trunk?

I wonder if the linker can fix this up?  That is, turn the relocation into
a valid one?

Richard.

> Thanks.
>
>
> H.J.
> ---
> gcc/
>
>         PR target/65248
>         * config/i386/i386.c (ix86_option_override_internal): Set
>         flag_copyreloc_in_pie to HAVE_LD_PIE_COPYRELOC if not set.
>         (legitimate_pic_address_disp_p): Replace HAVE_LD_PIE_COPYRELOC
>         with flag_copyreloc_in_pie.
>         * config/i386/i386.opt (mcopyreloc-in-pie): New.
>         * doc/invoke.texi: Document -mcopyreloc-in-pie.
>
> gcc/testsuite/
>
>         PR target/65248
>         * gcc.target/i386/pr65248-1.c: New.
>         * gcc.target/i386/pr65248-2.c: Likewise.
>         * gcc.target/i386/pr65248-3.c: Likewise.
>         * gcc.target/i386/pr65248-4.c: Likewise.
> ---
>  gcc/config/i386/i386.c                    |  6 +++++-
>  gcc/config/i386/i386.opt                  |  4 ++++
>  gcc/doc/invoke.texi                       | 10 +++++++++-
>  gcc/testsuite/gcc.target/i386/pr65248-1.c | 13 +++++++++++++
>  gcc/testsuite/gcc.target/i386/pr65248-2.c | 13 +++++++++++++
>  gcc/testsuite/gcc.target/i386/pr65248-3.c | 16 ++++++++++++++++
>  gcc/testsuite/gcc.target/i386/pr65248-4.c | 16 ++++++++++++++++
>  7 files changed, 76 insertions(+), 2 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-1.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-2.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-3.c
>  create mode 100644 gcc/testsuite/gcc.target/i386/pr65248-4.c
>
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index bec1324..6768ee8 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c
> @@ -4248,6 +4248,10 @@ ix86_option_override_internal (bool main_args_p,
>  #endif
>     }
>
> +  /* Set the default value for -mcopyreloc-in-pie.  */
> +  if (opts->x_flag_copyreloc_in_pie == -1)
> +    opts->x_flag_copyreloc_in_pie = HAVE_LD_PIE_COPYRELOC;
> +
>    if (!(opts_set->x_target_flags & MASK_VZEROUPPER))
>      opts->x_target_flags |= MASK_VZEROUPPER;
>    if (!ix86_tune_features[X86_TUNE_AVX256_UNALIGNED_LOAD_OPTIMAL]
> @@ -13230,7 +13234,7 @@ legitimate_pic_address_disp_p (rtx disp)
>             }
>           else if (!SYMBOL_REF_FAR_ADDR_P (op0)
>                    && (SYMBOL_REF_LOCAL_P (op0)
> -                      || (HAVE_LD_PIE_COPYRELOC
> +                      || (flag_copyreloc_in_pie
>                            && flag_pie
>                            && !SYMBOL_REF_WEAK (op0)
>                            && !SYMBOL_REF_FUNCTION_P (op0)))
> diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
> index 301430c..55c712c 100644
> --- a/gcc/config/i386/i386.opt
> +++ b/gcc/config/i386/i386.opt
> @@ -826,6 +826,10 @@ mfentry
>  Target Report Var(flag_fentry) Init(-1)
>  Emit profiling counter call at function entry before prologue.
>
> +mcopyreloc-in-pie
> +Target Report Var(flag_copyreloc_in_pie) Init(-1)
> +Use copy relocations in Position Independent Executable (PIE)
> +
>  mrecord-mcount
>  Target Report Var(flag_record_mcount) Init(0)
>  Generate __mcount_loc section with all mcount or __fentry__ calls.
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index a87376e..7fd4e77 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -1078,7 +1078,8 @@ See RS/6000 and PowerPC Options.
>  -m32 -m64 -mx32 -m16 -mlarge-data-threshold=@var{num} @gol
>  -msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol
>  -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
> --malign-data=@var{type} -mstack-protector-guard=@var{guard}}
> +-malign-data=@var{type} -mstack-protector-guard=@var{guard}} @gol
> +-mcopyreloc-in-pie
>
>  @emph{x86 Windows Options}
>  @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
> @@ -22704,6 +22705,13 @@ For systems that use the GNU C Library, the default is on.
>  Specify that the assembler should encode SSE instructions with VEX
>  prefix.  The option @option{-mavx} turns this on by default.
>
> +@item -mcopyreloc-in-pie
> +@itemx -mno-copyreloc-in-pie
> +@opindex mcopyreloc-in-pie
> +Use copy relocations in Position Independent Executable (PIE).  It
> +requires linker support.  This option is turned on by default if linker
> +used to build GCC supports it.
> +
>  @item -mfentry
>  @itemx -mno-fentry
>  @opindex mfentry
> diff --git a/gcc/testsuite/gcc.target/i386/pr65248-1.c b/gcc/testsuite/gcc.target/i386/pr65248-1.c
> new file mode 100644
> index 0000000..91aa6be
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr65248-1.c
> @@ -0,0 +1,13 @@
> +/* Check that GOTPCREL isn't used to access glob_a.  */
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpie -mcopyreloc-in-pie" } */
> +
> +extern int glob_a;
> +
> +int foo ()
> +{
> +  return glob_a;
> +}
> +
> +/* glob_a should never be accessed with a GOTPCREL.  */
> +/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { ! ia32 } } } } */
> diff --git a/gcc/testsuite/gcc.target/i386/pr65248-2.c b/gcc/testsuite/gcc.target/i386/pr65248-2.c
> new file mode 100644
> index 0000000..dc8445c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr65248-2.c
> @@ -0,0 +1,13 @@
> +/* Check that GOTPCREL isn't used to access glob_a.  */
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpie -mno-copyreloc-in-pie" } */
> +
> +int glob_a;
> +
> +int foo ()
> +{
> +  return glob_a;
> +}
> +
> +/* glob_a should never be accessed with a GOTPCREL.  */
> +/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { ! ia32 } } } } */
> diff --git a/gcc/testsuite/gcc.target/i386/pr65248-3.c b/gcc/testsuite/gcc.target/i386/pr65248-3.c
> new file mode 100644
> index 0000000..9398fd2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr65248-3.c
> @@ -0,0 +1,16 @@
> +/* Check that GOTPCREL is used to access glob_a.  */
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpie -mno-copyreloc-in-pie" } */
> +
> +extern int glob_a;
> +
> +int foo ()
> +{
> +  if (&glob_a != 0)
> +    return glob_a;
> +  else
> +    return 0;
> +}
> +
> +/* weak glob_a should be accessed with a GOTPCREL.  */
> +/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */
> diff --git a/gcc/testsuite/gcc.target/i386/pr65248-4.c b/gcc/testsuite/gcc.target/i386/pr65248-4.c
> new file mode 100644
> index 0000000..6d7cc86
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/i386/pr65248-4.c
> @@ -0,0 +1,16 @@
> +/* Check that GOTPCREL is used to access glob_a.  */
> +/* { dg-do compile { target *-*-linux* } } */
> +/* { dg-options "-O2 -fpie -mno-copyreloc-in-pie" } */
> +
> +extern int glob_a  __attribute__((weak));
> +
> +int foo ()
> +{
> +  if (&glob_a != 0)
> +    return glob_a;
> +  else
> +    return 0;
> +}
> +
> +/* weak glob_a should be accessed with a GOTPCREL.  */
> +/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */
> --
> 2.1.0
>
> Reply-To:
>
Alan Modra March 2, 2015, 10:09 a.m. UTC | #4
On Mon, Mar 02, 2015 at 09:40:01AM +0100, Richard Biener wrote:
> On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> > Ue copy relocation in PIE improves performance.  But copy relocation
> > can't be used to access protected symbols defined in shared libaries
> > and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
> > know if an external definition is protected or not.  This option adds
> > -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
> > at link-time.  OK for trunk?
> 
> I wonder if the linker can fix this up?  That is, turn the relocation into
> a valid one?

No it can't (*), nor can the dynamic linker.  Copy relocs aren't
really the issue.  They are just a means of initializing a linker
generated variable to be used in place of a variable in a shared
library.  The issue is the linker generated .dynbss variable itself.

Consider an ELF executable linked against a shared library, with the
executable referencing (but not defining) a variable defined in the
shared library.  You'd expect that the executable and shared library
would both use the same location for the variable.  Indeed, that is
true.  Both executable and shared library use the shared library's
variable.  Except there is a wrinkle.  If the executable is non-PIC,
code in the executable will require dynamic text relocations as the
variable's address isn't known until run time.  To avoid that, some
clever person thought: "Why not have the linker define the variable in
the executable?  ELF run time linking semantics mean the shared
library will now use the linker defined copy, so we'll still just be
using one copy of the variable".  Any everyone was happy.  At least
until ELF visibility was invented.

When ELF visibility comes into play, a variable defined in a shared
library with non-default visibility is *not* overridden by another
definition in the executable, be it an actual definition or a linker
generated one.  There is no problem of course if there is an actual
definition in the executable.  In that case the programmer would
expect to see two different variables used.  However, if the shared
library contains a protected visibility variable, and the linker
introduces a copy, then it has changed the meaning of the program.  At
the source level we only had one definition of the variable, but at
run time we'd end up using two different locations.

*) Except by avoiding .dynbss copies and hence requiring dynamic text
relocations.
Richard Biener March 2, 2015, 12:05 p.m. UTC | #5
On Mon, Mar 2, 2015 at 11:09 AM, Alan Modra <amodra@gmail.com> wrote:
> On Mon, Mar 02, 2015 at 09:40:01AM +0100, Richard Biener wrote:
>> On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> > Ue copy relocation in PIE improves performance.  But copy relocation
>> > can't be used to access protected symbols defined in shared libaries
>> > and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
>> > know if an external definition is protected or not.  This option adds
>> > -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
>> > at link-time.  OK for trunk?
>>
>> I wonder if the linker can fix this up?  That is, turn the relocation into
>> a valid one?
>
> No it can't (*), nor can the dynamic linker.  Copy relocs aren't
> really the issue.  They are just a means of initializing a linker
> generated variable to be used in place of a variable in a shared
> library.  The issue is the linker generated .dynbss variable itself.
>
> Consider an ELF executable linked against a shared library, with the
> executable referencing (but not defining) a variable defined in the
> shared library.  You'd expect that the executable and shared library
> would both use the same location for the variable.  Indeed, that is
> true.  Both executable and shared library use the shared library's
> variable.  Except there is a wrinkle.  If the executable is non-PIC,
> code in the executable will require dynamic text relocations as the
> variable's address isn't known until run time.  To avoid that, some
> clever person thought: "Why not have the linker define the variable in
> the executable?  ELF run time linking semantics mean the shared
> library will now use the linker defined copy, so we'll still just be
> using one copy of the variable".  Any everyone was happy.  At least
> until ELF visibility was invented.
>
> When ELF visibility comes into play, a variable defined in a shared
> library with non-default visibility is *not* overridden by another
> definition in the executable, be it an actual definition or a linker
> generated one.  There is no problem of course if there is an actual
> definition in the executable.  In that case the programmer would
> expect to see two different variables used.  However, if the shared
> library contains a protected visibility variable, and the linker
> introduces a copy, then it has changed the meaning of the program.  At
> the source level we only had one definition of the variable, but at
> run time we'd end up using two different locations.
>
> *) Except by avoiding .dynbss copies and hence requiring dynamic text
> relocations.

Ah, I see (protected visibility has haunted us in the past...).

So I think we need to turn the new option off by default.

Richard.

> --
> Alan Modra
> Australia Development Lab, IBM
H.J. Lu March 2, 2015, 1:36 p.m. UTC | #6
On Sun, Mar 1, 2015 at 11:48 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> Ue copy relocation in PIE improves performance.  But copy relocation
>> can't be used to access protected symbols defined in shared libaries
>> and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
>> know if an external definition is protected or not.  This option adds
>> -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
>> at link-time.  OK for trunk?
>
> If the option does not work universally for all cases, then the
> default should be off.

This optimization isn't the real issue.  The issue here is protected
symbol in shared library.  It can never be referenced from a normal
executable and works correctly, as Alan explained.  It doesn't matter
if this option is off or not since it only applies to PIE.  You can try
the testcase in PR 65248 without -fPIE -pie.
H.J. Lu March 2, 2015, 1:39 p.m. UTC | #7
On Mon, Mar 2, 2015 at 4:05 AM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Mon, Mar 2, 2015 at 11:09 AM, Alan Modra <amodra@gmail.com> wrote:
>> On Mon, Mar 02, 2015 at 09:40:01AM +0100, Richard Biener wrote:
>>> On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>> > Ue copy relocation in PIE improves performance.  But copy relocation
>>> > can't be used to access protected symbols defined in shared libaries
>>> > and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
>>> > know if an external definition is protected or not.  This option adds
>>> > -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
>>> > at link-time.  OK for trunk?
>>>
>>> I wonder if the linker can fix this up?  That is, turn the relocation into
>>> a valid one?
>>
>> No it can't (*), nor can the dynamic linker.  Copy relocs aren't
>> really the issue.  They are just a means of initializing a linker
>> generated variable to be used in place of a variable in a shared
>> library.  The issue is the linker generated .dynbss variable itself.
>>
>> Consider an ELF executable linked against a shared library, with the
>> executable referencing (but not defining) a variable defined in the
>> shared library.  You'd expect that the executable and shared library
>> would both use the same location for the variable.  Indeed, that is
>> true.  Both executable and shared library use the shared library's
>> variable.  Except there is a wrinkle.  If the executable is non-PIC,

            ^^^^^^^^^^^^
This is the key here.  This optimization makes PIE behave like normal
executable. Is it good or bad?

>> code in the executable will require dynamic text relocations as the
>> variable's address isn't known until run time.  To avoid that, some
>> clever person thought: "Why not have the linker define the variable in
>> the executable?  ELF run time linking semantics mean the shared
>> library will now use the linker defined copy, so we'll still just be
>> using one copy of the variable".  Any everyone was happy.  At least
>> until ELF visibility was invented.
>>
>> When ELF visibility comes into play, a variable defined in a shared
>> library with non-default visibility is *not* overridden by another
>> definition in the executable, be it an actual definition or a linker
>> generated one.  There is no problem of course if there is an actual
>> definition in the executable.  In that case the programmer would
>> expect to see two different variables used.  However, if the shared
>> library contains a protected visibility variable, and the linker
>> introduces a copy, then it has changed the meaning of the program.  At
>> the source level we only had one definition of the variable, but at
>> run time we'd end up using two different locations.
>>
>> *) Except by avoiding .dynbss copies and hence requiring dynamic text
>> relocations.
>
> Ah, I see (protected visibility has haunted us in the past...).
>
> So I think we need to turn the new option off by default.
>
H.J. Lu March 2, 2015, 1:47 p.m. UTC | #8
On Mon, Mar 2, 2015 at 5:39 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Mon, Mar 2, 2015 at 4:05 AM, Richard Biener
> <richard.guenther@gmail.com> wrote:
>> On Mon, Mar 2, 2015 at 11:09 AM, Alan Modra <amodra@gmail.com> wrote:
>>> On Mon, Mar 02, 2015 at 09:40:01AM +0100, Richard Biener wrote:
>>>> On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>>>> > Ue copy relocation in PIE improves performance.  But copy relocation
>>>> > can't be used to access protected symbols defined in shared libaries
>>>> > and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
>>>> > know if an external definition is protected or not.  This option adds
>>>> > -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
>>>> > at link-time.  OK for trunk?
>>>>
>>>> I wonder if the linker can fix this up?  That is, turn the relocation into
>>>> a valid one?
>>>
>>> No it can't (*), nor can the dynamic linker.  Copy relocs aren't
>>> really the issue.  They are just a means of initializing a linker
>>> generated variable to be used in place of a variable in a shared
>>> library.  The issue is the linker generated .dynbss variable itself.
>>>
>>> Consider an ELF executable linked against a shared library, with the
>>> executable referencing (but not defining) a variable defined in the
>>> shared library.  You'd expect that the executable and shared library
>>> would both use the same location for the variable.  Indeed, that is
>>> true.  Both executable and shared library use the shared library's
>>> variable.  Except there is a wrinkle.  If the executable is non-PIC,
>
>             ^^^^^^^^^^^^
> This is the key here.  This optimization makes PIE behave like normal
> executable. Is it good or bad?
>
>>> code in the executable will require dynamic text relocations as the
>>> variable's address isn't known until run time.  To avoid that, some
>>> clever person thought: "Why not have the linker define the variable in
>>> the executable?  ELF run time linking semantics mean the shared
>>> library will now use the linker defined copy, so we'll still just be
>>> using one copy of the variable".  Any everyone was happy.  At least
>>> until ELF visibility was invented.
>>>
>>> When ELF visibility comes into play, a variable defined in a shared
>>> library with non-default visibility is *not* overridden by another
>>> definition in the executable, be it an actual definition or a linker
>>> generated one.  There is no problem of course if there is an actual
>>> definition in the executable.  In that case the programmer would
>>> expect to see two different variables used.  However, if the shared
>>> library contains a protected visibility variable, and the linker
>>> introduces a copy, then it has changed the meaning of the program.  At
>>> the source level we only had one definition of the variable, but at
>>> run time we'd end up using two different locations.
>>>
>>> *) Except by avoiding .dynbss copies and hence requiring dynamic text
>>> relocations.
>>
>> Ah, I see (protected visibility has haunted us in the past...).
>>
>> So I think we need to turn the new option off by default.
>>

By turning this optimization on by default, we make PIE behave the same
as normal executable and provide a way to change PIE behavior.  This
is more consistent to users.
Alan Modra March 3, 2015, 8:12 a.m. UTC | #9
On Mon, Mar 02, 2015 at 05:36:24AM -0800, H.J. Lu wrote:
> On Sun, Mar 1, 2015 at 11:48 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
> > On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
> >> Ue copy relocation in PIE improves performance.  But copy relocation
> >> can't be used to access protected symbols defined in shared libaries
> >> and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
> >> know if an external definition is protected or not.  This option adds
> >> -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
> >> at link-time.  OK for trunk?
> >
> > If the option does not work universally for all cases, then the
> > default should be off.
> 
> This optimization isn't the real issue.  The issue here is protected
> symbol in shared library.  It can never be referenced from a normal
> executable and works correctly, as Alan explained.  It doesn't matter
> if this option is off or not since it only applies to PIE.  You can try
> the testcase in PR 65248 without -fPIE -pie.

I see things differently.  We have an old linker hack to avoid dynamic
text relocations in non-PIC executables.  This hack doesn't work
properly with protected visibility variables in shared libraries.
Rather than blaming protected visibility variables, I say the hack is
broken.

Since you rely on the linker hack for your optimization, you've 
extended the problem from non-PIC executables to PIEs..

If you want the optimization enabled by default then it probably would
have been better to do the optimization entirely in the linker,
similar to the way we optimize TLS sequences.  Certainly a switch that
enables the optimization in the compiler is reasonable if that gives
better code, but I can see distros turning it off if you manage to
convince Uros and others that the default should be on.
H.J. Lu March 3, 2015, 2:20 p.m. UTC | #10
On Tue, Mar 3, 2015 at 12:12 AM, Alan Modra <amodra@gmail.com> wrote:
> On Mon, Mar 02, 2015 at 05:36:24AM -0800, H.J. Lu wrote:
>> On Sun, Mar 1, 2015 at 11:48 PM, Uros Bizjak <ubizjak@gmail.com> wrote:
>> > On Sat, Feb 28, 2015 at 5:42 PM, H.J. Lu <hjl.tools@gmail.com> wrote:
>> >> Ue copy relocation in PIE improves performance.  But copy relocation
>> >> can't be used to access protected symbols defined in shared libaries
>> >> and linker in binutils 2.26 enforces doesn't allow it.  GCC doesn't
>> >> know if an external definition is protected or not.  This option adds
>> >> -mcopyreloc-in-pie to give user an option to turn it off to avoid problem
>> >> at link-time.  OK for trunk?
>> >
>> > If the option does not work universally for all cases, then the
>> > default should be off.
>>
>> This optimization isn't the real issue.  The issue here is protected
>> symbol in shared library.  It can never be referenced from a normal
>> executable and works correctly, as Alan explained.  It doesn't matter
>> if this option is off or not since it only applies to PIE.  You can try
>> the testcase in PR 65248 without -fPIE -pie.
>
> I see things differently.  We have an old linker hack to avoid dynamic
> text relocations in non-PIC executables.  This hack doesn't work
> properly with protected visibility variables in shared libraries.
> Rather than blaming protected visibility variables, I say the hack is
> broken.
>
> Since you rely on the linker hack for your optimization, you've
> extended the problem from non-PIC executables to PIEs..
>

To make it clear, protected data symbols defined in shared library
can never be accessed from normal executables on x86-64 since
R_X86_64_PC32 relocation is used to reach externally defined
symbols in small and medium models and it will overflow at run-time.
On x86-64, R_X86_64_COPY relocation is required to access externally
defined data symbols in normal executables unless small and medium
models are removed from x86-64 psABI.  For x86-64, protected data
symbols are impossible.
Alan Modra March 4, 2015, 3:13 a.m. UTC | #11
On Tue, Mar 03, 2015 at 06:20:05AM -0800, H.J. Lu wrote:
> For x86-64, protected data symbols are impossible.

Impossible?  This is not even true currently since -fPIC emits code
that looks like it would fully support protected visibiliy variables
in shared libraries.

If you meant to say it is impossible with non-PIC, then even that
statement is going too far.  All you'd need to do is have GNU ld emit
dynamic text relocations and possibly add some reloc support to ld.so,
and you'd have support so long as shared libraries loaded within 2G of
the executable.  A little horrible, but quite possible.

With some more work, GNU ld could edit the current code sequences
emitted by gcc for non-PIC, to a branch to a patch area where you use
a PIC code sequence.

See also Cary's suggestion at
https://groups.google.com/forum/#!msg/generic-abi/9JX9vdstoVA/g4UGTmRdXJcJ

Or you could just acknowledge that non-PIC has limitations.  Another
similar one (same root cause of no GOT indirection) is with weak
symbols, where
	extern int foo () __attribute__ ((weak));
	if (foo)
	  foo ();
just doesn't work for foo in a shared library.  The non-PIC "if (foo)"
effectively becomes either "if (0)" or "if (1)" at link time, unless
you emit dynamic text relocations or edit the code.
H.J. Lu March 4, 2015, 2:14 p.m. UTC | #12
On Tue, Mar 3, 2015 at 7:13 PM, Alan Modra <amodra@gmail.com> wrote:
> On Tue, Mar 03, 2015 at 06:20:05AM -0800, H.J. Lu wrote:
>> For x86-64, protected data symbols are impossible.
>
> Impossible?  This is not even true currently since -fPIC emits code
> that looks like it would fully support protected visibiliy variables
> in shared libraries.
>
> If you meant to say it is impossible with non-PIC, then even that
> statement is going too far.  All you'd need to do is have GNU ld emit
> dynamic text relocations and possibly add some reloc support to ld.so,
> and you'd have support so long as shared libraries loaded within 2G of
> the executable.  A little horrible, but quite possible.
>
> With some more work, GNU ld could edit the current code sequences
> emitted by gcc for non-PIC, to a branch to a patch area where you use
> a PIC code sequence.
>
> See also Cary's suggestion at
> https://groups.google.com/forum/#!msg/generic-abi/9JX9vdstoVA/g4UGTmRdXJcJ
>
> Or you could just acknowledge that non-PIC has limitations.  Another
> similar one (same root cause of no GOT indirection) is with weak
> symbols, where
>         extern int foo () __attribute__ ((weak));
>         if (foo)
>           foo ();
> just doesn't work for foo in a shared library.  The non-PIC "if (foo)"
> effectively becomes either "if (0)" or "if (1)" at link time, unless
> you emit dynamic text relocations or edit the code.
>

I withdrew my patch.  I am working on a new approach for PIE and
normal executable.
diff mbox

Patch

diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index bec1324..6768ee8 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -4248,6 +4248,10 @@  ix86_option_override_internal (bool main_args_p,
 #endif
    }
 
+  /* Set the default value for -mcopyreloc-in-pie.  */
+  if (opts->x_flag_copyreloc_in_pie == -1)
+    opts->x_flag_copyreloc_in_pie = HAVE_LD_PIE_COPYRELOC;
+
   if (!(opts_set->x_target_flags & MASK_VZEROUPPER))
     opts->x_target_flags |= MASK_VZEROUPPER;
   if (!ix86_tune_features[X86_TUNE_AVX256_UNALIGNED_LOAD_OPTIMAL]
@@ -13230,7 +13234,7 @@  legitimate_pic_address_disp_p (rtx disp)
 	    }
 	  else if (!SYMBOL_REF_FAR_ADDR_P (op0)
 		   && (SYMBOL_REF_LOCAL_P (op0)
-		       || (HAVE_LD_PIE_COPYRELOC
+		       || (flag_copyreloc_in_pie
 			   && flag_pie
 			   && !SYMBOL_REF_WEAK (op0)
 			   && !SYMBOL_REF_FUNCTION_P (op0)))
diff --git a/gcc/config/i386/i386.opt b/gcc/config/i386/i386.opt
index 301430c..55c712c 100644
--- a/gcc/config/i386/i386.opt
+++ b/gcc/config/i386/i386.opt
@@ -826,6 +826,10 @@  mfentry
 Target Report Var(flag_fentry) Init(-1)
 Emit profiling counter call at function entry before prologue.
 
+mcopyreloc-in-pie
+Target Report Var(flag_copyreloc_in_pie) Init(-1)
+Use copy relocations in Position Independent Executable (PIE)
+
 mrecord-mcount
 Target Report Var(flag_record_mcount) Init(0)
 Generate __mcount_loc section with all mcount or __fentry__ calls.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index a87376e..7fd4e77 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -1078,7 +1078,8 @@  See RS/6000 and PowerPC Options.
 -m32 -m64 -mx32 -m16 -mlarge-data-threshold=@var{num} @gol
 -msse2avx -mfentry -mrecord-mcount -mnop-mcount -m8bit-idiv @gol
 -mavx256-split-unaligned-load -mavx256-split-unaligned-store @gol
--malign-data=@var{type} -mstack-protector-guard=@var{guard}}
+-malign-data=@var{type} -mstack-protector-guard=@var{guard}} @gol
+-mcopyreloc-in-pie
 
 @emph{x86 Windows Options}
 @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol
@@ -22704,6 +22705,13 @@  For systems that use the GNU C Library, the default is on.
 Specify that the assembler should encode SSE instructions with VEX
 prefix.  The option @option{-mavx} turns this on by default.
 
+@item -mcopyreloc-in-pie
+@itemx -mno-copyreloc-in-pie
+@opindex mcopyreloc-in-pie
+Use copy relocations in Position Independent Executable (PIE).  It
+requires linker support.  This option is turned on by default if linker
+used to build GCC supports it.
+
 @item -mfentry
 @itemx -mno-fentry
 @opindex mfentry
diff --git a/gcc/testsuite/gcc.target/i386/pr65248-1.c b/gcc/testsuite/gcc.target/i386/pr65248-1.c
new file mode 100644
index 0000000..91aa6be
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65248-1.c
@@ -0,0 +1,13 @@ 
+/* Check that GOTPCREL isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie -mcopyreloc-in-pie" } */
+
+extern int glob_a;
+
+int foo ()
+{
+  return glob_a;
+}
+
+/* glob_a should never be accessed with a GOTPCREL.  */
+/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65248-2.c b/gcc/testsuite/gcc.target/i386/pr65248-2.c
new file mode 100644
index 0000000..dc8445c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65248-2.c
@@ -0,0 +1,13 @@ 
+/* Check that GOTPCREL isn't used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie -mno-copyreloc-in-pie" } */
+
+int glob_a;
+
+int foo ()
+{
+  return glob_a;
+}
+
+/* glob_a should never be accessed with a GOTPCREL.  */
+/* { dg-final { scan-assembler-not "glob_a@GOTPCREL" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65248-3.c b/gcc/testsuite/gcc.target/i386/pr65248-3.c
new file mode 100644
index 0000000..9398fd2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65248-3.c
@@ -0,0 +1,16 @@ 
+/* Check that GOTPCREL is used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie -mno-copyreloc-in-pie" } */
+
+extern int glob_a;
+
+int foo ()
+{
+  if (&glob_a != 0)
+    return glob_a;
+  else
+    return 0;
+}
+
+/* weak glob_a should be accessed with a GOTPCREL.  */
+/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr65248-4.c b/gcc/testsuite/gcc.target/i386/pr65248-4.c
new file mode 100644
index 0000000..6d7cc86
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr65248-4.c
@@ -0,0 +1,16 @@ 
+/* Check that GOTPCREL is used to access glob_a.  */
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpie -mno-copyreloc-in-pie" } */
+
+extern int glob_a  __attribute__((weak));
+
+int foo ()
+{
+  if (&glob_a != 0)
+    return glob_a;
+  else
+    return 0;
+}
+
+/* weak glob_a should be accessed with a GOTPCREL.  */
+/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */