On x86-64, in PIE mode, accesses to external data don't use the conservative 
GOT-relative address, but rather use pcrel. A copy relocation will be created 
if the external data is defined in a DSO.

// a.o
// (x) GCC<5, movq a@GOTPCREL(%rip), %rax; movl (%rax), %eax
// (y) GCC>=5 (since commit 130f233477ed03a7bdffb832e7eb9f0a366e0d6b), movl 
a(%rip), %eax
extern int a; int foo() { return a; }

// b.o or b.so
__attribute__((visibility("protected"))) int a;

(1) When a.o and b.o are linked together with -pie (`a` is non-preemptable)
 If R_X86_64_GOTPCRELX (-mrelax-relocations=yes) is enabled, we get:
 (x) leaq a(%rip),%rax; movl (%rax),%eax (8 bytes, 2 insns)
 (y) movl $a(%rip),%eax (6 bytes, 1 insn)
 See below.

(2) When a.o and b.so are linked together with -pie (`a` is external)
 (x) nothing special
 movq a@gotpcrel(%rip),%rax; movl (%rax),%eax

 For (y), there is a copy relocation, which plays badly with protected data 
preemption. Linkers don't have an agreement:
 ld.bfd creates a copy relocation
   movl a(%rip),%eax
 gold doesn't allow preemption (after 
https://sourceware.org/bugzilla/show_bug.cgi?id=19823)
   error: a.o: cannot make copy relocation for protected symbol 'a', defined in 
b.so
 lld has a similar error
   error: cannot preempt symbol: a\n>>> defined in b.so\n...

ld.bfd creates a copy relocation for (2)(y), that was probably how the option 
got the name HAVE_LD_PIE_COPYRELOC. To make the pointer equality rule hold, in 
a DSO, accesses to its own protected data have to go through GOT. This choice 
is against intuition and appears to violate gABI's description of STV_PROTECTED:

A symbol defined in the current component is protected if it is visible in 
other components but not preemptable, meaning that any reference to such a 
symbol from within the defining component must be resolved to the definition in 
that component, even if there is a definition in another component that would 
preempt by the default rules.

Lots of fallout was caused by HAVE_LD_PIE_COPYRELOC=1, thus I suggest we make 
HAVE_LD_PIE_COPYRELOC non-default (there could be a configure option to enable 
it), to address these issues:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=55012 protected data wrongly uses 
GOT
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=65248 (copyrel against protected 
data) wouldn't become an issue.

When compiling a.c, it is not known whether the actual definition in b.so will 
be protected or not, so it seems preferable to be conservative (use GOT). See 
(1)(y), with the link-time relaxation R_X86_64_GOTPCRELX, most inefficiency can 
be removed. We still get 2 instructions, but the cost is insignificant. Global 
data is rare. Users who really care about the extra instruction can mark their 
data as hidden.

Reply via email to