[Bug 970] New: #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

Bug ID: 970
Summary: #pragma visibility hidden in
MdePkg/Include/X64/ProcessorBind.h is misconceived
Product: EDK2
Version: Current
Hardware: All
OS: All
Status: UNCONFIRMED
Severity: minor
Priority: Lowest
Component: Code
Assignee: michael.d.kinney(a)intel.com
Reporter: zenith432(a)users.sourceforge.net
CC: edk2-bugs(a)lists.01.org

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.
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.
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.

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.

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

Join bugs@edk2.groups.io to automatically receive all group messages.