Index: ELF/Arch/PPC.cpp =================================================================== --- ELF/Arch/PPC.cpp +++ ELF/Arch/PPC.cpp @@ -21,7 +21,7 @@ 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; Index: ELF/Arch/X86.cpp =================================================================== --- ELF/Arch/X86.cpp +++ ELF/Arch/X86.cpp @@ -46,6 +46,7 @@ } // namespace X86::X86() { + GotBaseSymOff = -1; CopyRel = R_386_COPY; GotRel = R_386_GLOB_DAT; PltRel = R_386_JUMP_SLOT; Index: ELF/Arch/X86_64.cpp =================================================================== --- ELF/Arch/X86_64.cpp +++ ELF/Arch/X86_64.cpp @@ -51,6 +51,7 @@ } // namespace template X86_64::X86_64() { + GotBaseSymOff = -1; CopyRel = R_X86_64_COPY; GotRel = R_X86_64_GLOB_DAT; PltRel = R_X86_64_JUMP_SLOT; Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -361,7 +361,7 @@ // These expressions always compute a constant if (isRelExprOneOf(E)) return true; Index: ELF/Target.h =================================================================== --- ELF/Target.h +++ ELF/Target.h @@ -66,6 +66,10 @@ // 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; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -87,6 +87,8 @@ uint64_t FileSize; uint64_t SectionHeaderOff; + + bool HasGotBaseSym = false; }; } // anonymous namespace @@ -815,19 +817,13 @@ Symtab::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::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(InX::MipsGot) + : cast(InX::Got); + HasGotBaseSym = addOptionalRegular("_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 @@ // 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 &V) { +static void removeUnusedSyntheticSections(std::vector &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 @@ 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 @@ return; addPredefinedSections(); - removeUnusedSyntheticSections(OutputSections); + removeUnusedSyntheticSections(OutputSections, HasGotBaseSym); clearOutputSections(); sortSections(); Index: test/ELF/arm-got-relative.s =================================================================== --- test/ELF/arm-got-relative.s +++ test/ELF/arm-got-relative.s @@ -16,9 +16,9 @@ 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 @@ 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 @@ // 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 Index: test/ELF/global-offset-table-position-aarch64.s =================================================================== --- /dev/null +++ test/ELF/global-offset-table-position-aarch64.s @@ -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 Index: test/ELF/global-offset-table-position-arm.s =================================================================== --- /dev/null +++ test/ELF/global-offset-table-position-arm.s @@ -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 Index: test/ELF/global-offset-table-position-i386.s =================================================================== --- /dev/null +++ test/ELF/global-offset-table-position-i386.s @@ -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) Index: test/ELF/global-offset-table-position-mips.s =================================================================== --- /dev/null +++ test/ELF/global-offset-table-position-mips.s @@ -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) Index: test/ELF/global-offset-table-position.s =================================================================== --- /dev/null +++ test/ELF/global-offset-table-position.s @@ -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 Index: test/ELF/global_offset_table_shared.s =================================================================== --- test/ELF/global_offset_table_shared.s +++ test/ELF/global_offset_table_shared.s @@ -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