On x86, copy relocation is used in executable to access external data defined in shared object as if it is defined locally. At run-time, dynamic linker copies symbol data from shared object to executable and its references from shared objects are resolved by GLOB_DAT relocation. Since the copy of symbol data in executable is writable even if the original symbol in shared object is read-only, this is a potential security risk.
We can avoid copy relocation by always using PIC model to access external data symbol. If the external symbol is defined locally in executable, linker can optimize instructions on memory operand with GOTPCRELX/GOT32X relocation against external symbol into a different form on immediate operand. Without copy relocation, external symbols in executable are accessed indirectly via the GOT slot. Even when the symbol is defined locally, it is still referenced indirectly through a register. However, there are a few side benefits: 1. Without copy relocation in executable for external symbol defined in shared object, symbol size can be increased while still providing backward binary compatibility. 2. Since protected symbols in shared objects are no longer copied to executable at run-time, references of protected symbols within shared objects can be resolved without GOT. Tested on i686 and x86-64. OK for master? H.J. --- gcc/ PR target/77408 * config/i386/i386.c (ix86_force_load_from_GOT_p): Return true on data symbols for -mno-copy-reloc. (legitimate_pic_address_disp_p): Allow copy relocation in 64-bit PIE only with -mcopy-reloc. * config/i386/i386.opt (mcopy-reloc): New. * doc/invoke.texi: Document -mcopy-reloc. gcc/testsuite/ PR target/77408 * gcc.target/i386/no-copy-reloc-1.c: New test. * gcc.target/i386/no-copy-reloc-2.c: Likewise. * gcc.target/i386/no-copy-reloc-3.c: Likewise. * gcc.target/i386/no-copy-reloc-4.c: Likewise. * gcc.target/i386/no-copy-reloc-5.c: Likewise. * gcc.target/i386/no-copy-reloc-6.c: Likewise. * gcc.target/i386/no-copy-reloc-7.c: Likewise. * gcc.target/i386/no-copy-reloc-8.c: Likewise. * gcc.target/i386/no-copy-reloc-9.c: Likewise. --- gcc/config/i386/i386.c | 22 +++++++++++++++------- gcc/config/i386/i386.opt | 4 ++++ gcc/doc/invoke.texi | 7 ++++++- gcc/testsuite/gcc.target/i386/no-copy-reloc-1.c | 15 +++++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-2.c | 16 ++++++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-3.c | 14 ++++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-4.c | 14 ++++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-5.c | 15 +++++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-6.c | 14 ++++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-7.c | 14 ++++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-8.c | 12 ++++++++++++ gcc/testsuite/gcc.target/i386/no-copy-reloc-9.c | 12 ++++++++++++ 12 files changed, 151 insertions(+), 8 deletions(-) create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-1.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-2.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-3.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-4.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-5.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-6.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-7.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-8.c create mode 100644 gcc/testsuite/gcc.target/i386/no-copy-reloc-9.c diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index a229a73..c84a351 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -15172,13 +15172,20 @@ darwin_local_data_pic (rtx disp) bool ix86_force_load_from_GOT_p (rtx x) { - return ((TARGET_64BIT || HAVE_AS_IX86_GOT32X) - && !TARGET_PECOFF && !TARGET_MACHO - && !flag_plt && !flag_pic - && ix86_cmodel != CM_LARGE - && GET_CODE (x) == SYMBOL_REF - && SYMBOL_REF_FUNCTION_P (x) - && !SYMBOL_REF_LOCAL_P (x)); + if ((TARGET_64BIT || HAVE_AS_IX86_GOT32X) + && !TARGET_PECOFF + && !flag_pic + && !TARGET_MACHO + && ix86_cmodel != CM_LARGE + && GET_CODE (x) == SYMBOL_REF + && !SYMBOL_REF_LOCAL_P (x)) + { + if (SYMBOL_REF_FUNCTION_P (x)) + return !flag_plt; + else + return !flag_copy_reloc; + } + return false; } /* Determine if a given RTX is a valid constant. We already know this @@ -15439,6 +15446,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_copy_reloc && 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 4b75ba8..aef23d9 100644 --- a/gcc/config/i386/i386.opt +++ b/gcc/config/i386/i386.opt @@ -897,3 +897,7 @@ Attempt to avoid generating instruction sequences containing ret bytes. mgeneral-regs-only Target Report RejectNegative Mask(GENERAL_REGS_ONLY) Var(ix86_target_flags) Save Generate code which uses only the general registers. + +mcopy-reloc +Target Report Var(flag_copy_reloc) Init(1) +Allow copy relocation when accessing external data. diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 87da1f1..71bec15 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -1179,7 +1179,7 @@ See RS/6000 and PowerPC Options. -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} @gol --mmitigate-rop -mgeneral-regs-only} +-mmitigate-rop -mgeneral-regs-only -mcopy-reloc} @emph{x86 Windows Options} @gccoptlist{-mconsole -mcygwin -mno-cygwin -mdll @gol @@ -24653,6 +24653,11 @@ Generate code that uses only the general-purpose registers. This prevents the compiler from using floating-point, vector, mask and bound registers. +@item -mcopy-reloc +@opindex mcopy-reloc +Generate code that uses copy relocation in executable to access external +data defined in shared object. It is enabled by default. + @end table These @samp{-m} switches are supported in addition to the above diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-1.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-1.c new file mode 100644 index 0000000..94de223 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-1.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -mno-copy-reloc" } */ + +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|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar\\(%rip\\)" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\(mov|lea\)l\[ \t\]*\(\\\$|\)bar," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-2.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-2.c new file mode 100644 index 0000000..90205a4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-2.c @@ -0,0 +1,16 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -mno-copy-reloc" } */ + +extern int bar; +extern int *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|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar\\(%rip\\)" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*\\\$bar," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-3.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-3.c new file mode 100644 index 0000000..c8c7cf1 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-3.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -mno-copy-reloc" } */ + +static int bar; + +int * +foo (void) +{ + return &bar; +} + +/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-4.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-4.c new file mode 100644 index 0000000..fbfa63a --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-4.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -mno-copy-reloc" } */ + +extern int bar __attribute__ ((visibility ("hidden"))); + +int * +foo (void) +{ + return &bar; +} + +/* { dg-final { scan-assembler "mov\(l|q\)\[ \t\]*\\\$bar," } } */ +/* { dg-final { scan-assembler-not "mov\(l|q\)\[ \t\]*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-5.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-5.c new file mode 100644 index 0000000..d5ec6a7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-5.c @@ -0,0 +1,15 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -mno-copy-reloc" } */ + +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|lea\)\(l|q\)\[ \t\]*\(\\\$|\)bar\\(%rip\\)" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler-not "\(mov|lea\)l\[ \t\]*\(\\\$|\)bar," { target { ia32 && got32x_reloc } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-6.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-6.c new file mode 100644 index 0000000..9eff04f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-6.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -mno-copy-reloc" } */ + +extern int bar; + +int +check (int *p) +{ + return p != &bar; +} + +/* { dg-final { scan-assembler "cmp\(l|q\)\[ \t\]*.*bar@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "movl\[ \t\]*bar@GOT," { target { ia32 && got32x_reloc } } } } */ +/* { dg-final { scan-assembler-not "cmp\(l|q\)\[ \t\]*\\\$bar," } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-7.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-7.c new file mode 100644 index 0000000..9c1dec0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-7.c @@ -0,0 +1,14 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fno-pic -mno-copy-reloc" } */ + +extern int bar[]; + +int * +foo (void) +{ + return &bar[1]; +} + +/* { 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\\+4," } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-8.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-8.c new file mode 100644 index 0000000..5d490a7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-8.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fpie -mno-copy-reloc" } */ + +extern int glob_a; + +int foo () +{ + return glob_a; +} + +/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "glob_a@GOT\\(" { target { ia32 } } } } */ diff --git a/gcc/testsuite/gcc.target/i386/no-copy-reloc-9.c b/gcc/testsuite/gcc.target/i386/no-copy-reloc-9.c new file mode 100644 index 0000000..5d490a7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/no-copy-reloc-9.c @@ -0,0 +1,12 @@ +/* { dg-do compile { target *-*-linux* } } */ +/* { dg-options "-O2 -fpie -mno-copy-reloc" } */ + +extern int glob_a; + +int foo () +{ + return glob_a; +} + +/* { dg-final { scan-assembler "glob_a@GOTPCREL" { target { ! ia32 } } } } */ +/* { dg-final { scan-assembler "glob_a@GOT\\(" { target { ia32 } } } } */ -- 2.7.4