gcc -fPIC -c -o x.o x.c
gcc -shared -o libx.so x.o
/usr/local/bin/ld: x.o: relocation R_X86_64_PC32 against `foo' can not be used
when making a shared object; recompile with -fPIC
/usr/local/bin/ld: final link failed: Bad value
collect2: ld returned 1 exit status
make: *** [libx.so] Error 1
With the old linker, I got
[hjl@gnu-20 x86_64-3]$ make CC="gcc -B/usr/bin/"
gcc -B/usr/bin/ -fPIC -c -o x.o x.c
gcc -B/usr/bin/ -shared -o libx.so x.o
gcc -B/usr/bin/ -o foo m.c libx.so -Wl,-rpath,.
./foo
called from main foo_p: 0x400610
called from shared foo: 0x2a9566d8d8
shared foo: 0x2a9566d8d8
shared foo: 0x2a9566d8d8
called from shared foo_p: 0x400610
shared foo: 0x2a9566d8d8
shared foo: 0x2a9566d8d8
called from main foo: 0x400610
got from main foo: 0x2a9566d8d8
Function pointer `foo' are't the same in DSO and main
$ cat > foo.c
__attribute__((visibility("protected")))
void * foo (void) { return (void *)foo; }
$ gcc -fPIC -shared foo.c
/usr/bin/ld: /tmp/cclrufLV.o: relocation R_X86_64_PC32 against protected symbol `foo' can not be used when making a shared object
/usr/bin/ld: final link failed: Bad value
collect2: ld returned 1 exit status
$ gcc -Wl,-Bsymbolic-functions -fPIC -shared foo.c && echo success
success
$ cat > empty.dynlist
{ "__this_symbol_isnt_present__"; };
$ gcc -Wl,--dynamic-list,empty.dynlist -fPIC -shared foo.c && echo success
success
I also cannot confirm that icc does anything different:
$ icc -fPIC -shared foo.c
ld: /tmp/iccf15gTK.o: relocation R_X86_64_PC32 against protected symbol `foo' can not be used when making a shared object
ld: final link failed: Bad value
$ icc -O3 -S -o /dev/stdout -fPIC -shared foo.c | grep -A4 foo:
..B1.1: # Preds ..B1.0
..___tag_value_foo.1: #2.19
lea foo(%rip), %rax #2.36
ret #2.36
What's more, if you actually do compile the following program into a shared library, it succeeds:
$ cat > foo.S
.text
.globl foo
.protected foo
.type foo, @function
movq foo@GOTPCREL(%rip), %rax
$ gcc -shared foo.S && echo success
success
But the resulting shared object has the following (extracted from eu-readelf):
Relocation section [ 5] '.rela.dyn' for section [ 0] '' at offset 0x230 contains 1 entry:
Offset Type Value Addend Name
0x0000000000200330 X86_64_GLOB_DAT 0x0000000000000248 +0 foo
2: 0000000000000248 0 FUNC GLOBAL PROTECTED 6 foo
Now we introduce a third component to this discussion: the dynamic linker. What will it do?
This has become a decision, not a bug: what should the compiler do when taking the address of a function when said function is under protected visibility. Both solutions are technically correct and would load the same function address under the correct circumstances.
The compiler is also taking on the "protected" visibility to the letter (at least, according to its own definition of so):
"protected"
Protected visibility is like default visibility except that it
indicates that references within the defining module will
bind to the definition in that module. That is, the declared
entity cannot be overridden by another module.
Since the symbol was marked as "protected" in the symbol table, it's expected that the linker and dynamic linker will bind it locally. That being the case, the compiler can optimise for that fact. It can calculate what value would be placed in the GOT entry and load that instead. That's the LEA instruction.
The linker, however, mandates that the address to symbol should not be loaded directly, but only through the GOT. This is necessary because the psABI requires that the function address resolve to the PLT entry found in the position-dependent executable. If the executable takes the address of this global (but protected) symbol, it will hardcode the address to its own address space, forcing other ELF modules to follow suit.
Finally, what does the dynamic linker do when an "entity (that) cannot be overridden by another module" is overridden by another module? The glibc 2.14 loader will resolve the GOT entry's relocation to the executable's PLT stub, even if the symbol in question has protected visibility. Other loaders might work differently.
As it stands, the psABI requires that the address to a protected function be loaded through the GOT, even though the compiler thinks it knows what the address will be.
However, I really wish the compiler *not* to change its behaviour for PIC code, but instead change its behaviour for ELF position-dependent executables. I am asking for a change in the psABI and requesting that the loading of function addresses for "default" visibility symbols (not protected!) should be done via the GOT. In other words, I'm asking that we optimise for shared libraries, not for executables.
Versions:
GCC: 4.6.0
ld: 2.21.51.0.6-6.fc15 20110118
ICC: 12.1.0 20111011