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.