This is an archive of the discontinued LLVM Phabricator instance.

[LLD][ELF] Use start of .got.plt as the location for _GLOBAL_OFFSET_TABLE_
ClosedPublic

Authored by peter.smith on Mar 8 2018, 10:06 AM.

Details

Summary

For most Targets the _GLOBAL_OFFSET_TABLE_ symbol is expected to be at the start of the .got.plt section so that _GLOBAL_OFFSET_TABLE_[0] = reserved value that is by convention the address of the dynamic section. Previously we had defined _GLOBAL_OFFSET_TABLE_ as either the start or end of the .got section with the intention that the .got.plt section would follow the .got. However this does not always hold with the current default section ordering so _GLOBAL_OFFSET_TABLE_[0] may not be consistent with the reserved first entry of the .got.plt.

In the patch, for X86, X86_64, Mips, Arm and AArch64 I've altered the location of the _GLOBAL_OFFSET_TABLE_ to be the start of the .got.plt section. For Power I've kept it as the ABI defined .got + 0x8000. For all except AArch64 this is consistent with Gold and BFD. They have chosen to use the start of the .got section for _GLOBAL_OFFSET_TABLE_, but I don't know of any software that depends on this, for example glibc/dl-machine.h reserves the same number of entries in the .got.plt as other targets and never directly accesses .got.plt[0] to use it for some other purpose.

Majority of the changes are to the tests as we now have to generate a .got.plt section if the _GLOBAL_OFFSET_TABLE_ symbol is defined rather than the .got.

fixes PR36555 (https://bugs.llvm.org/show_bug.cgi?id=36555)

Fixes PR36555.

Diff Detail

Repository
rL LLVM

Event Timeline

peter.smith created this revision.Mar 8 2018, 10:06 AM
atanasyan added inline comments.Mar 9 2018, 2:16 AM
test/ELF/global-offset-table-position-mips.s
8 ↗(On Diff #137597)

On MIPS _GLOBAL_OFFSET_TABLE_ should point to the start of the .got section. I do not know any real test case uses this fact, but all GNU tools generates code which follow this rule.

Thanks for the comment, and apologies for my misreading of the binutils source. I've updated the patch to keep Mips as it was previously.

This revision is now accepted and ready to land.Mar 9 2018, 5:06 AM
espindola edited reviewers, added: espindola; removed: rafael.Mar 9 2018, 9:49 AM

LGTM

Peter Smith via Phabricator <reviews@reviews.llvm.org> writes:

peter.smith updated this revision to Diff 137715.
peter.smith added a comment.

Thanks for the comment, and apologies for my misreading of the binutils source. I've updated the patch to keep Mips as it was previously.

https://reviews.llvm.org/D44259

Files:

ELF/Arch/Mips.cpp
ELF/Arch/PPC.cpp
ELF/Arch/X86.cpp
ELF/Arch/X86_64.cpp
ELF/SyntheticSections.cpp
ELF/SyntheticSections.h
ELF/Target.h
ELF/Writer.cpp
test/ELF/arm-got-relative.s
test/ELF/dynamic-got.s
test/ELF/global-offset-table-position-aarch64.s
test/ELF/global-offset-table-position-arm.s
test/ELF/global-offset-table-position-i386.s
test/ELF/global-offset-table-position.s
test/ELF/global_offset_table_shared.s
test/ELF/got32x-i386.s
test/ELF/i386-gotpc.s

Index: test/ELF/i386-gotpc.s

  • test/ELF/i386-gotpc.s

+++ test/ELF/i386-gotpc.s
@@ -6,15 +6,23 @@

movl $_GLOBAL_OFFSET_TABLE_, %eax

+ CHECK: Name: .got.plt
+
CHECK-NEXT: Type: SHT_PROGBITS
+ CHECK-NEXT: Flags [
+
CHECK-NEXT: SHF_ALLOC
+ CHECK-NEXT: SHF_WRITE
+
CHECK-NEXT: ]
+ CHECK-NEXT: Address: 0x2000
+
CHECK: Name: .got
CHECK-NEXT: Type: SHT_PROGBITS
CHECK-NEXT: Flags [
CHECK-NEXT: SHF_ALLOC
CHECK-NEXT: SHF_WRITE
CHECK-NEXT: ]
-
CHECK-NEXT: Address: 0x2030
+// CHECK-NEXT: Address: 0x3030

DISASM: Disassembly of section .text:
DISASM-NEXT: .text:
- DISASM-NEXT: 1000: {{.*}} movl $4144, %eax
-
0x2030 - 0x1000 = 4144
+ DISASM-NEXT: 1000: {{.*}} movl $8240, %eax
+
0x3030 - 0x1000 = 0x2030

Index: test/ELF/got32x-i386.s

  • test/ELF/got32x-i386.s

+++ test/ELF/got32x-i386.s
@@ -33,13 +33,13 @@

  1. 73728 == 0x12000 == ADDR(.got)
  2. CHECK: _start:
  3. CHECK-NEXT: 11001: 8b 05 {{.*}} movl 73728, %eax
  4. CHECK-NEXT: 11007: 8b 1d {{.*}} movl 73728, %ebx

+# CHECK-NEXT: 11001: 8b 05 {{.*}} movl 77824, %eax
+# CHECK-NEXT: 11007: 8b 1d {{.*}} movl 77824, %ebx

  1. CHECK-NEXT: 1100d: 8b 80 {{.*}} movl -4(%eax), %eax
  2. CHECK-NEXT: 11013: 8b 83 {{.*}} movl -4(%ebx), %eax
  3. CHECK: Sections:
  4. CHECK: Name Size Address
  5. CHECK: .got 00000004 0000000000012000

+# CHECK: .got 00000004 0000000000013000

  1. RUN: not ld.lld %S/Inputs/i386-got32x-baseless.elf -o %t1 -pie 2>&1 | \
  2. RUN: FileCheck %s --check-prefix=ERR

Index: test/ELF/global_offset_table_shared.s

  • test/ELF/global_offset_table_shared.s

+++ test/ELF/global_offset_table_shared.s
@@ -4,11 +4,11 @@
.long _GLOBAL_OFFSET_TABLE_ - .

CHECK: Name: _GLOBAL_OFFSET_TABLE_
-
CHECK-NEXT: Value: 0x2060
+ CHECK-NEXT: Value: 0x2000
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
+// CHECK-NEXT: Section: .got.plt

Index: test/ELF/global-offset-table-position.s

  • test/ELF/global-offset-table-position.s

+++ test/ELF/global-offset-table-position.s
@@ -3,7 +3,8 @@
RUN: llvm-readobj -t %t2 | FileCheck %s
REQUIRES: x86

- The X86_64 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
+
The X86_64 _GLOBAL_OFFSET_TABLE_ is defined at the start of the .got.plt
+// section.
.globl a
.type a,@object
.comm a,4,4
@@ -21,11 +22,11 @@
.long _GLOBAL_OFFSET_TABLE_ - .

CHECK: Name: _GLOBAL_OFFSET_TABLE_
-
CHECK-NEXT: Value: 0x30D8
+ CHECK-NEXT: Value: 0x2008
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
+// CHECK-NEXT: Section: .got.plt

Index: test/ELF/global-offset-table-position-i386.s

  • test/ELF/global-offset-table-position-i386.s

+++ test/ELF/global-offset-table-position-i386.s
@@ -3,7 +3,8 @@
RUN: llvm-readobj -t %t2 | FileCheck %s
REQUIRES: x86

- The X86 _GLOBAL_OFFSET_TABLE_ is defined at the end of the .got section.
+
The X86 _GLOBAL_OFFSET_TABLE_ is defined at the start of the .got.plt
+// section.
.globl a
.type a,@object
.comm a,4,4
@@ -21,11 +22,11 @@
calll f@PLT

CHECK: Name: _GLOBAL_OFFSET_TABLE_ (1)
-
CHECK-NEXT: Value: 0x306C
+ CHECK-NEXT: Value: 0x2000
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)
+// CHECK-NEXT: Section: .got.plt

Index: test/ELF/global-offset-table-position-arm.s

  • test/ELF/global-offset-table-position-arm.s

+++ test/ELF/global-offset-table-position-arm.s
@@ -3,7 +3,8 @@
RUN: llvm-readobj -t %t2 | FileCheck %s
REQUIRES: arm

- The ARM _GLOBAL_OFFSET_TABLE_ should be defined at the start of the .got
+
The ARM _GLOBAL_OFFSET_TABLE_ should be defined at the start of the
+// .got.plt section.
.globl a
.type a,%object
.comm a,4,4
@@ -25,11 +26,11 @@
.data

CHECK: Name: _GLOBAL_OFFSET_TABLE_
-
CHECK-NEXT: Value: 0x3068
+ CHECK-NEXT: Value: 0x2000
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
+// CHECK-NEXT: Section: .got.plt

Index: test/ELF/global-offset-table-position-aarch64.s

  • test/ELF/global-offset-table-position-aarch64.s

+++ test/ELF/global-offset-table-position-aarch64.s
@@ -20,11 +20,11 @@
.long _GLOBAL_OFFSET_TABLE_ - .

CHECK: Name: _GLOBAL_OFFSET_TABLE_ (11)
-
CHECK-NEXT: Value: 0x30090
+ CHECK-NEXT: Value: 0x20008
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
+// CHECK-NEXT: Section: .got.plt

Index: test/ELF/dynamic-got.s

  • test/ELF/dynamic-got.s

+++ test/ELF/dynamic-got.s
@@ -3,6 +3,23 @@
RUN: ld.lld --hash-style=sysv %t.o -o %t.so -shared
RUN: llvm-readobj -s -l -section-data -r %t.so | FileCheck %s

+ CHECK: Name: .got.plt
+
CHECK-NEXT: Type: SHT_PROGBITS
+ CHECK-NEXT: Flags [
+
CHECK-NEXT: SHF_ALLOC
+ CHECK-NEXT: SHF_WRITE
+
CHECK-NEXT: ]
+ CHECK-NEXT: Address:
+
CHECK-NEXT: Offset:
+ CHECK-NEXT: Size:
+
CHECK-NEXT: Link:
+ CHECK-NEXT: Info:
+
CHECK-NEXT: AddressAlignment:
+ CHECK-NEXT: EntrySize:
+
CHECK-NEXT: SectionData (
+ CHECK-NEXT: 0000: 00300000 00000000 00000000
+
CHECK-NEXT: )
+
CHECK: Name: .got
CHECK-NEXT: Type: SHT_PROGBITS
CHECK-NEXT: Flags [
@@ -17,19 +34,19 @@
CHECK-NEXT: AddressAlignment:
CHECK-NEXT: EntrySize:
CHECK-NEXT: SectionData (
- CHECK-NEXT: 0000: 00200000 |
+
CHECK-NEXT: 0000: 00300000
// CHECK-NEXT: )

CHECK: Relocations [
CHECK-NEXT: Section ({{.*}}) .rel.dyn {
- CHECK-NEXT: 0x2050 R_386_RELATIVE - 0x0
+
CHECK-NEXT: 0x3050 R_386_RELATIVE - 0x0
CHECK-NEXT: }
CHECK-NEXT: ]

CHECK: Type: PT_DYNAMIC
-
CHECK-NEXT: Offset: 0x2000
- CHECK-NEXT: VirtualAddress: 0x2000
-
CHECK-NEXT: PhysicalAddress: 0x2000
+ CHECK-NEXT: Offset: 0x3000
+
CHECK-NEXT: VirtualAddress: 0x3000
+// CHECK-NEXT: PhysicalAddress: 0x3000

calll   .L0$pb

.L0$pb:

Index: test/ELF/arm-got-relative.s

  • test/ELF/arm-got-relative.s

+++ test/ELF/arm-got-relative.s
@@ -28,26 +28,26 @@

bx lr

CHECK: Dynamic Relocations {
-
CHECK-NEXT: 0x2048 R_ARM_GLOB_DAT function 0x0
+// CHECK-NEXT: 0x3048 R_ARM_GLOB_DAT function 0x0

CHECK: Name: _GLOBAL_OFFSET_TABLE_
-
CHECK-NEXT: Value: 0x2048
+ CHECK-NEXT: Value: 0x2000
CHECK-NEXT: Size:
CHECK-NEXT: Binding: Local
CHECK-NEXT: Type: None
CHECK-NEXT: Other [
CHECK-NEXT: STV_HIDDEN
CHECK-NEXT: ]
-
CHECK-NEXT: Section: .got
+// CHECK-NEXT: Section: .got.plt

CODE: Disassembly of section .text:
CODE-NEXT: _start:
CODE-NEXT: 1000: 08 30 9f e5 ldr r3, [pc, #8]
CODE-NEXT: 1004: 08 20 9f e5 ldr r2, [pc, #8]
CODE-NEXT: 1008: 03 00 8f e0 add r0, pc, r3
CODE-NEXT: 100c: 1e ff 2f e1 bx lr
CODE:$d.1:
-
(_GLOBAL_OFFSET_TABLE_ = 0x2048) - (0x1008 + 8) 0x1038
- CODE-NEXT: 1010: 38 10 00 00
+
(_GLOBAL_OFFSET_TABLE_ = 0x2000) - (0x1008 + 8) = 0xff0
+ CODE-NEXT: 1010: f0 0f 00 00
(Got(function) - GotBase = 0x0
// CODE-NEXT: 1014: 00 00 00 00

Index: ELF/Writer.cpp

  • ELF/Writer.cpp

+++ ELF/Writer.cpp
@@ -868,11 +868,12 @@
// defining these symbols explicitly in the linker script.
template <class ELFT> void Writer<ELFT>::setReservedSymbolSections() {

if (ElfSym::GlobalOffsetTable) {
  • // 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);

+ The _GLOBAL_OFFSET_TABLE_ symbol is defined by target convention usually
+
to the start of the .got or .got.plt section.
+ InputSection *GotSection = InX::GotPlt;
+ if (!Target->GotBaseSymInGotPlt)
+ GotSection = InX::MipsGot ? cast<InputSection>(InX::MipsGot)
+ : cast<InputSection>(InX::Got);

  ElfSym::GlobalOffsetTable->Section = GotSection;
}

Index: ELF/Target.h

  • ELF/Target.h

+++ ELF/Target.h
@@ -71,9 +71,10 @@

uint64_t getImageBase();
  • // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got section. Use -1 for
  • // end of .got

+ // Offset of _GLOBAL_OFFSET_TABLE_ from base of .got or .got.plt section.

uint64_t GotBaseSymOff = 0;

+ // True if _GLOBAL_OFFSET_TABLE_ is relative to .got.plt, false if .got.
+ bool GotBaseSymInGotPlt = true;

// On systems with range extensions we place collections of Thunks at
// regular spacings that enable the majority of branches reach the Thunks.

Index: ELF/SyntheticSections.h

  • ELF/SyntheticSections.h

+++ ELF/SyntheticSections.h
@@ -269,7 +269,7 @@

void addEntry(Symbol &Sym);
size_t getSize() const override;
void writeTo(uint8_t *Buf) override;
  • bool empty() const override { return Entries.empty(); }

+ bool empty() const override;

private:

std::vector<const Symbol *> Entries;

Index: ELF/SyntheticSections.cpp

  • ELF/SyntheticSections.cpp

+++ ELF/SyntheticSections.cpp
@@ -628,8 +628,9 @@
bool GotSection::empty() const {

// We need to emit a GOT even if it's empty if there's a relocation that is
// relative to GOT(such as GOTOFFREL) or there's a symbol that points to a GOT
  • // (i.e. _GLOBAL_OFFSET_TABLE_).
  • return NumEntries == 0 && !HasGotOffRel && !ElfSym::GlobalOffsetTable;

+ // (i.e. _GLOBAL_OFFSET_TABLE_) that the target defines relative to the .got.
+ return NumEntries == 0 && !HasGotOffRel &&
+ !(ElfSym::GlobalOffsetTable && !Target->GotBaseSymInGotPlt);
}

void GotSection::writeTo(uint8_t *Buf) {
@@ -900,6 +901,14 @@

}

}

+bool GotPltSection::empty() const {
+ We need to emit a GOT.PLT even if it's empty if there's a symbol that
+
references the _GLOBAL_OFFSET_TABLE_ and the Target defines the symbol
+ relative to the .got.plt section.
+ return Entries.empty() &&
+ !(ElfSym::GlobalOffsetTable && Target->GotBaseSymInGotPlt);
+}
+
On ARM the IgotPltSection is part of the GotSection, on other Targets it is
// part of the .got.plt
IgotPltSection::IgotPltSection()

Index: ELF/Arch/X86_64.cpp

  • ELF/Arch/X86_64.cpp

+++ ELF/Arch/X86_64.cpp
@@ -51,7 +51,6 @@
} // 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;

Index: ELF/Arch/X86.cpp

  • ELF/Arch/X86.cpp

+++ ELF/Arch/X86.cpp
@@ -46,7 +46,6 @@
} // namespace

X86::X86() {

  • GotBaseSymOff = -1; CopyRel = R_386_COPY; GotRel = R_386_GLOB_DAT; PltRel = R_386_JUMP_SLOT;

Index: ELF/Arch/PPC.cpp

  • ELF/Arch/PPC.cpp

+++ ELF/Arch/PPC.cpp
@@ -21,13 +21,18 @@
namespace {
class PPC final : public TargetInfo {
public:

  • PPC() { GotBaseSymOff = 0x8000; }

+ PPC();

void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
                   const uint8_t *Loc) const override;

};
} // namespace

+PPC::PPC() {
+ GotBaseSymOff = 0x8000;
+ GotBaseSymInGotPlt = false;
+}
+
RelExpr PPC::getRelExpr(RelType Type, const Symbol &S,

                      const uint8_t *Loc) const {
switch (Type) {

Index: ELF/Arch/Mips.cpp

  • ELF/Arch/Mips.cpp

+++ ELF/Arch/Mips.cpp
@@ -50,6 +50,7 @@

DefaultMaxPageSize = 65536;
GotEntrySize = sizeof(typename ELFT::uint);
GotPltEntrySize = sizeof(typename ELFT::uint);

+ GotBaseSymInGotPlt = false;

PltEntrySize = 16;
PltHeaderSize = 32;
CopyRel = R_MIPS_COPY;
This revision was automatically updated to reflect the committed changes.