[ELF] Define _GLOBAL_OFFSET_TABLE_ symbol relative to .got

On many architectures gcc and clang will recognize _GLOBAL_OFFSET_TABLE_ - .
and produce a relocation that can be processed without needing to know the
value of _GLOBAL_OFFSET_TABLE_. This is not always the case; for example ARM
gcc produces R_ARM_BASE_PREL but clang produces the more general
R_ARM_REL32 to _GLOBAL_OFFSET_TABLE_. To evaluate this relocation
correctly _GLOBAL_OFFSET_TABLE_ must be defined to be the either the base of
the GOT or end of the GOT dependent on architecture..

If/when llvm-mc is changed to recognize _GLOBAL_OFFSET_TABLE_ - . this
change will not be necessary for new objects. However there may still be
old objects and versions of clang.

Differential Revision: https://reviews.llvm.org/D34355

llvm-svn: 306282
This commit is contained in:
Peter Smith 2017-06-26 10:22:17 +00:00
parent 1158fe9715
commit 113a59e7db
13 changed files with 197 additions and 27 deletions

View File

@ -21,7 +21,7 @@ using namespace lld::elf;
namespace {
class PPC final : public TargetInfo {
public:
PPC() {}
PPC() { GotBaseSymOff = 0x8000; }
void relocateOne(uint8_t *Loc, uint32_t Type, uint64_t Val) const override;
RelExpr getRelExpr(uint32_t Type, const SymbolBody &S,
const uint8_t *Loc) const override;

View File

@ -46,6 +46,7 @@ public:
} // namespace
X86::X86() {
GotBaseSymOff = -1;
CopyRel = R_386_COPY;
GotRel = R_386_GLOB_DAT;
PltRel = R_386_JUMP_SLOT;

View File

@ -51,6 +51,7 @@ private:
} // namespace
template <class ELFT> X86_64<ELFT>::X86_64() {
GotBaseSymOff = -1;
CopyRel = R_X86_64_COPY;
GotRel = R_X86_64_GLOB_DAT;
PltRel = R_X86_64_JUMP_SLOT;

View File

@ -361,7 +361,7 @@ static bool isStaticLinkTimeConstant(RelExpr E, uint32_t Type,
// These expressions always compute a constant
if (isRelExprOneOf<R_SIZE, R_GOT_FROM_END, R_GOT_OFF, R_MIPS_GOT_LOCAL_PAGE,
R_MIPS_GOT_OFF, R_MIPS_GOT_OFF32, R_MIPS_GOT_GP_PC,
R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC,
R_MIPS_TLSGD, R_GOT_PAGE_PC, R_GOT_PC, R_GOTONLY_PC,
R_GOTONLY_PC_FROM_END, R_PLT_PC, R_TLSGD_PC, R_TLSGD,
R_PPC_PLT_OPD, R_TLSDESC_CALL, R_TLSDESC_PAGE, R_HINT>(E))
return true;

View File

@ -66,6 +66,10 @@ public:
// Given that, the smallest value that can be used in here is 0x10000.
uint64_t DefaultImageBase = 0x10000;
// Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
// end of .got
uint64_t GotBaseSymOff = 0;
uint32_t CopyRel;
uint32_t GotRel;
uint32_t PltRel;

View File

@ -87,6 +87,8 @@ private:
uint64_t FileSize;
uint64_t SectionHeaderOff;
bool HasGotBaseSym = false;
};
} // anonymous namespace
@ -815,19 +817,13 @@ template <class ELFT> void Writer<ELFT>::addReservedSymbols() {
Symtab<ELFT>::X->addAbsolute("__gnu_local_gp", STV_HIDDEN, STB_LOCAL);
}
// In the assembly for 32 bit x86 the _GLOBAL_OFFSET_TABLE_ symbol
// is magical and is used to produce a R_386_GOTPC relocation.
// The R_386_GOTPC relocation value doesn't actually depend on the
// symbol value, so it could use an index of STN_UNDEF which, according
// to the spec, means the symbol value is 0.
// Unfortunately both gas and MC keep the _GLOBAL_OFFSET_TABLE_ symbol in
// the object file.
// The situation is even stranger on x86_64 where the assembly doesn't
// need the magical symbol, but gas still puts _GLOBAL_OFFSET_TABLE_ as
// an undefined symbol in the .o files.
// Given that the symbol is effectively unused, we just create a dummy
// hidden one to avoid the undefined symbol error.
Symtab<ELFT>::X->addIgnored("_GLOBAL_OFFSET_TABLE_");
// The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention to
// be at some offset from the base of the .got section, usually 0 or the end
// of the .got
InputSection *GotSection = (InX::MipsGot) ? cast<InputSection>(InX::MipsGot)
: cast<InputSection>(InX::Got);
HasGotBaseSym = addOptionalRegular<ELFT>("_GLOBAL_OFFSET_TABLE_", GotSection,
Target->GotBaseSymOff) != nullptr;
// __tls_get_addr is defined by the dynamic linker for dynamic ELFs. For
// static linking the linker is required to optimize away any references to
@ -1136,7 +1132,8 @@ static void applySynthetic(const std::vector<SyntheticSection *> &Sections,
// to make them visible from linkescript side. But not all sections are always
// required to be in output. For example we don't need dynamic section content
// sometimes. This function filters out such unused sections from the output.
static void removeUnusedSyntheticSections(std::vector<OutputSection *> &V) {
static void removeUnusedSyntheticSections(std::vector<OutputSection *> &V,
bool HasGotBaseSym) {
// All input synthetic sections that can be empty are placed after
// all regular ones. We iterate over them all and exit at first
// non-synthetic.
@ -1147,6 +1144,8 @@ static void removeUnusedSyntheticSections(std::vector<OutputSection *> &V) {
OutputSection *OS = SS->getParent();
if (!SS->empty() || !OS)
continue;
if ((SS == InX::Got || SS == InX::MipsGot) && HasGotBaseSym)
continue;
OS->Sections.erase(std::find(OS->Sections.begin(), OS->Sections.end(), SS));
SS->Live = false;
// If there are no other sections in the output section, remove it from the
@ -1220,7 +1219,7 @@ template <class ELFT> void Writer<ELFT>::finalizeSections() {
return;
addPredefinedSections();
removeUnusedSyntheticSections(OutputSections);
removeUnusedSyntheticSections(OutputSections, HasGotBaseSym);
clearOutputSections();
sortSections();

View File

@ -16,9 +16,9 @@ _start:
bx lr
.align 2
.LGOT:
// gas implicitly uses (GOT_PREL) for _GLOBAL_OFFSET_TABLE_ in PIC
// llvm-mc needs the (GOT_PREL) suffix or it generates R_ARM_REL32
.word _GLOBAL_OFFSET_TABLE_(GOT_PREL) - (.LPIC+8)
// gas implicitly uses (R_ARM_BASE_PREL) for _GLOBAL_OFFSET_TABLE_ in PIC
// llvm-mc generates R_ARM_REL32, this will need updating when MC changes
.word _GLOBAL_OFFSET_TABLE_ - (.LPIC+8)
.word function(GOT)
.globl function
@ -28,17 +28,17 @@ function:
bx lr
// CHECK: Dynamic Relocations {
// CHECK-NEXT: 0x204C R_ARM_GLOB_DAT function 0x0
// CHECK-NEXT: 0x2048 R_ARM_GLOB_DAT function 0x0
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
// CHECK-NEXT: Value: 0x0
// CHECK-NEXT: Value: 0x2048
// CHECK-NEXT: Size:
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
// CHECK-NEXT: Other [
// CHECK-NEXT: STV_HIDDEN
// CHECK-NEXT: ]
// CHECK-NEXT: Section: Absolute
// CHECK-NEXT: Section: .got
// CODE: Disassembly of section .text:
// CODE-NEXT: _start:
@ -49,5 +49,5 @@ function:
// CODE:$d.1:
// (_GLOBAL_OFFSET_TABLE_ = 0x2048) - (0x1008 + 8) 0x1038
// CODE-NEXT: 1010: 38 10 00 00
// (Got(function) - GotBase = 0x4
// CODE-NEXT: 1014: 04 00 00 00
// (Got(function) - GotBase = 0x0
// CODE-NEXT: 1014: 00 00 00 00

View File

@ -0,0 +1,30 @@
// RUN: llvm-mc -filetype=obj -triple=aarch64-linux-gnu %s -o %t
// RUN: ld.lld -shared %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck %s
// REQUIRES: aarch64
.globl a
.type a,@object
.comm a,4,4
.globl f
.type f,@function
f:
adrp x0, :got:a
ldr x0, [x0, #:got_lo12:a]
.global _start
.type _start,@function
_start:
bl f
.data
.long _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (11)
// CHECK-NEXT: Value: 0x30090
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: None (0x0)
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
// CHECK-NEXT: Section: .got

View File

@ -0,0 +1,35 @@
// RUN: llvm-mc -filetype=obj -triple=armv7a-linux-gnueabihf %s -o %t
// RUN: ld.lld -shared %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck %s
// REQUIRES: arm
// The ARM _GLOBAL_OFFSET_TABLE_ should be defined at the start of the .got
.globl a
.type a,%object
.comm a,4,4
.globl f
.type f,%function
f:
ldr r2, .L1
.L0:
add r2, pc
.L1:
.word _GLOBAL_OFFSET_TABLE_ - (.L0+4)
.word a(GOT)
.global _start
.type _start,%function
_start:
bl f
.data
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
// CHECK-NEXT: Value: 0x3068
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
// CHECK-NEXT: Section: .got

View File

@ -0,0 +1,31 @@
// RUN: llvm-mc -filetype=obj -triple=i386-pc-linux %s -o %t
// RUN: ld.lld -shared %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck %s
// REQUIRES: x86
// The X86 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
.globl a
.type a,@object
.comm a,4,4
.globl f
.type f,@function
f:
addl $_GLOBAL_OFFSET_TABLE_, %eax
movl a@GOT(%eax), %eax
.global _start
.type _start,@function
_start:
addl $_GLOBAL_OFFSET_TABLE_, %eax
calll f@PLT
// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (1)
// CHECK-NEXT: Value: 0x306C
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: None (0x0)
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
// CHECK-NEXT: Section: .got (0xA)

View File

@ -0,0 +1,33 @@
// RUN: llvm-mc -filetype=obj -triple=mips64-unknown-linux %s -o %t
// RUN: ld.lld -shared %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck %s
// REQUIRES: mips
// The Mips _GLOBAL_OFFSET_TABLE_ should be defined at the start of the .got
.globl a
.hidden a
.type a,@object
.comm a,4,4
.globl f
.type f,@function
f:
ld $v0,%got_page(a)($gp)
daddiu $v0,$v0,%got_ofst(a)
.global _start
.type _start,@function
_start:
lw $t0,%call16(f)($gp)
.word _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_ (1)
// CHECK-NEXT: Value: 0x20000
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local (0x0)
// CHECK-NEXT: Type: None (0x0)
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
// CHECK-NEXT: Section: .got (0x9)

View File

@ -0,0 +1,31 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
// RUN: ld.lld -shared %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck %s
// REQUIRES: x86
// The X86_64 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
.globl a
.type a,@object
.comm a,4,4
.globl f
.type f,@function
f:
movq a@GOTPCREL(%rip), %rax
.global _start
.type _start,@function
_start:
callq f@PLT
.data
.long _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
// CHECK-NEXT: Value: 0x30D8
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None (0x0)
// CHECK-NEXT: Other [
// CHECK-NEXT: STV_HIDDEN
// CHECK-NEXT: ]
// CHECK-NEXT: Section: .got

View File

@ -1,9 +1,14 @@
// RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t
// RUN: ld.lld -shared %t -o %t2
// RUN: llvm-readobj -t %t2 | FileCheck %s
.long _GLOBAL_OFFSET_TABLE_
.long _GLOBAL_OFFSET_TABLE_ - .
// CHECK: Name: _GLOBAL_OFFSET_TABLE_
// CHECK-NEXT: Value:
// CHECK-NEXT: Value: 0x2060
// CHECK-NEXT: Size: 0
// CHECK-NEXT: Binding: Local
// CHECK-NEXT: Type: None
// CHECK-NEXT: Other [ (0x2)
// CHECK-NEXT: STV_HIDDEN (0x2)
// CHECK-NEXT: ]
// CHECK-NEXT: Section: .got