[Bug 970] #pragma visibility hidden in MdePkg/Include/X64/ProcessorBind.h is misconceived #pragma


bugzilla-daemon at bugzilla.tianocore.org...
 

https://bugzilla.tianocore.org/show_bug.cgi?id=970

--- Comment #1 from Ard Biesheuvel <ard.biesheuvel(a)linaro.org> ---
(In reply to zenith432 from comment #0)
The following code in MdePkg/Include/X64/ProcessorBind.h is based on a
misconception and is less likely to cause problems if removed.
=====
#if defined(__GNUC__) && defined(__pic__) && !defined(USING_LTO)
//
// Mark all symbol declarations and references as hidden, meaning they will
// not be subject to symbol preemption. This allows the compiler to refer to
// symbols directly using relative references rather than via the GOT, which
// contains absolute symbol addresses that are subject to runtime relocation.
//
// The LTO linker will not emit GOT based relocations when all symbol
// references can be resolved locally, and so there is no need to set the
// pragma in that case (and doing so will cause other issues).
//
#pragma GCC visibility push (hidden)
#endif
=====

First, even though it was originally written for GCC with ELF target, it is
actually included for clang with both ELF and MACHO targets as well. The
rationale in the documentation is based on a misconception. Neither ELF nor
MACHO associate a visibility with *symbol declarations or references*. A
symbol's visibility determines whether the symbols is local or global *at
the point it is defined*. When an external symbol is declared or referenced
- no visibility is associated to the reference, and the compiler doesn't
know whether the symbol is defined as local or global at its point of
definition.
I'm with you up until this point ...

The compiler generates a GOTPCREL relocation. The static link
editor then usually transforms instructions called "GOT loads" into
load-effective-address instructions that bypass the GOT.
... but here, I have to disagree. binutils only gained support for these
relocations in version 2.26 [0], and older versions of GCC do indeed emit a
different type of access depending on the visibility, even if this access lacks
any kind of ELF annotation (and I don't know why it should, but since you
brought it up ...)

[0] https://sourceware.org/ml/binutils-cvs/2015-10/msg00146.html


Below is a sample

================

gcc -v
....
gcc version 8.1.1 20180502 (Red Hat 8.1.1-1) (GCC)

===== a.c =====
extern int a;
extern int g(int);

int f(void)
{
return g(a);
}
===============

gcc -fpic -c -Os -o a.o a.c
objdump -drt a.o

a.o: file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 a.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 000000000000000e f
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 *UND* 0000000000000000 a
0000000000000000 *UND* 0000000000000000 g

Disassembly of section .text:

0000000000000000 <f>:
0: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 7 <f+0x7>
3: R_X86_64_REX_GOTPCRELX a-0x4
7: 8b 38 mov (%rax),%edi
9: e9 00 00 00 00 jmpq e <f+0xe>
a: R_X86_64_PLT32 g-0x4

===============
gcc -fpic -fvisibility=hidden -c -Os -o a.o a.c
objdump -drt a.o

a.o: file format elf64-x86-64

SYMBOL TABLE:
0000000000000000 l df *ABS* 0000000000000000 a.c
0000000000000000 l d .text 0000000000000000 .text
0000000000000000 l d .data 0000000000000000 .data
0000000000000000 l d .bss 0000000000000000 .bss
0000000000000000 l d .note.GNU-stack 0000000000000000 .note.GNU-stack
0000000000000000 l d .eh_frame 0000000000000000 .eh_frame
0000000000000000 l d .comment 0000000000000000 .comment
0000000000000000 g F .text 000000000000000e .hidden f
0000000000000000 *UND* 0000000000000000 _GLOBAL_OFFSET_TABLE_
0000000000000000 *UND* 0000000000000000 a
0000000000000000 *UND* 0000000000000000 g

Disassembly of section .text:

0000000000000000 <f>:
0: 48 8b 05 00 00 00 00 mov 0x0(%rip),%rax # 7 <f+0x7>
3: R_X86_64_REX_GOTPCRELX a-0x4
7: 8b 38 mov (%rax),%edi
9: e9 00 00 00 00 jmpq e <f+0xe>
a: R_X86_64_PLT32 g-0x4
================

Notice how both generate the same object code regardless of visibility. You
can see that defined symbols are marked with "l" or "g" to their left based
on whether they're local or global - but the UND external references have no
visibility associated with them.

Later, the static link editor ld transforms instructions of the form
movq symbol(a)GOTPCREL(%rip), register
to
leaq symbol(%rip), register

Similarly, the PLT32 relocations are transformed to directly call the target
bypassing the PLT.

In fact, the tool GetFw which convert ELF executables to COFF is not
instrumented to handle GOTPCREL relocations, so if the static link editor
leaves any such relocations untransformed in the ELF executable, GetFw will
break with an error.
... which is precisely why we needed the 'hidden' visibility override, to
prevent [older versions of] GCC from emitting such relocations.

I've also seen instances where the compiler generates instructions like
addq symbol(a)GOTPCREL(%rip), register
for pointer arithmetic done on a declared extern symbol. This instruction
cannot be transformed by the static link editor and stays "as is". However,
it is emitted in the object code based on the optimisation level set for the
compilation, and not based on symbol visibility. By setting a different
optimisation such cases can be eliminated.

Additionally, it is better to discontinue use of the symbol USING_LTO
because it is based on a misconception that LTO setting is global. In fact,
-flto can be set in tools_def.txt, but then disabled on a per-module basis
with -fno-lto in a module's inf file if LTO causes problems. Moreover,
object code generated from assembly source never uses LTO.
I don't remember exactly why we required the USING_LTO check, but I think it
was simply because the issue we were addressing does not occur in that case
(the code is generated for the whole program, and so the visibility can be
inferred). So the USING_LTO check can indeed be dropped imo.

--
You are receiving this mail because:
You are on the CC list for the bug.