summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/ELF/Arch/PPC64.cpp38
-rw-r--r--lld/ELF/SyntheticSections.cpp48
-rw-r--r--lld/test/ELF/ppc64-dynamic-relocations.s22
-rw-r--r--lld/test/ELF/ppc64-ifunc.s33
-rw-r--r--lld/test/ELF/ppc64-plt-stub.s4
5 files changed, 102 insertions, 43 deletions
diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp
index 34a3e833a9c..ec14a551561 100644
--- a/lld/ELF/Arch/PPC64.cpp
+++ b/lld/ELF/Arch/PPC64.cpp
@@ -43,6 +43,9 @@ public:
uint32_t calcEFlags() const override;
RelExpr getRelExpr(RelType Type, const Symbol &S,
const uint8_t *Loc) const override;
+ void writePltHeader(uint8_t *Buf) const override;
+ void writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr, uint64_t PltEntryAddr,
+ int32_t Index, unsigned RelOff) const override;
void relocateOne(uint8_t *Loc, RelType Type, uint64_t Val) const override;
void writeGotHeader(uint8_t *Buf) const override;
bool needsThunk(RelExpr Expr, RelType Type, const InputFile *File,
@@ -67,13 +70,13 @@ PPC64::PPC64() {
PltRel = R_PPC64_JMP_SLOT;
RelativeRel = R_PPC64_RELATIVE;
GotEntrySize = 8;
+ PltEntrySize = 4;
GotPltEntrySize = 8;
- PltEntrySize = 0;
- PltHeaderSize = 0;
GotBaseSymInGotPlt = false;
GotBaseSymOff = 0x8000;
GotHeaderEntriesNum = 1;
GotPltHeaderEntriesNum = 2;
+ PltHeaderSize = 60;
NeedsThunks = true;
// We need 64K pages (at least under glibc/Linux, the loader won't
@@ -170,6 +173,37 @@ void PPC64::writeGotHeader(uint8_t *Buf) const {
write64(Buf, getPPC64TocBase());
}
+void PPC64::writePltHeader(uint8_t *Buf) const {
+ // The generic resolver stub goes first.
+ write32(Buf + 0, 0x7c0802a6); // mflr r0
+ write32(Buf + 4, 0x429f0005); // bcl 20,4*cr7+so,8 <_glink+0x8>
+ write32(Buf + 8, 0x7d6802a6); // mflr r11
+ write32(Buf + 12, 0x7c0803a6); // mtlr r0
+ write32(Buf + 16, 0x7d8b6050); // subf r12, r11, r12
+ write32(Buf + 20, 0x380cffcc); // subi r0,r12,52
+ write32(Buf + 24, 0x7800f082); // srdi r0,r0,62,2
+ write32(Buf + 28, 0xe98b002c); // ld r12,44(r11)
+ write32(Buf + 32, 0x7d6c5a14); // add r11,r12,r11
+ write32(Buf + 36, 0xe98b0000); // ld r12,0(r11)
+ write32(Buf + 40, 0xe96b0008); // ld r11,8(r11)
+ write32(Buf + 44, 0x7d8903a6); // mtctr r12
+ write32(Buf + 48, 0x4e800420); // bctr
+
+ // The 'bcl' instruction will set the link register to the address of the
+ // following instruction ('mflr r11'). Here we store the offset from that
+ // instruction to the first entry in the GotPlt section.
+ int64_t GotPltOffset = InX::GotPlt->getVA() - (InX::Plt->getVA() + 8);
+ write64(Buf + 52, GotPltOffset);
+}
+
+void PPC64::writePlt(uint8_t *Buf, uint64_t GotPltEntryAddr,
+ uint64_t PltEntryAddr, int32_t Index,
+ unsigned RelOff) const {
+ int32_t Offset = PltHeaderSize + Index * PltEntrySize;
+ // bl __glink_PLTresolve
+ write32(Buf, 0x48000000 | ((-Offset) & 0x03FFFFFc));
+}
+
static std::pair<RelType, uint64_t> toAddr16Rel(RelType Type, uint64_t Val) {
uint64_t V = Val - PPC64TocOffset;
switch (Type) {
diff --git a/lld/ELF/SyntheticSections.cpp b/lld/ELF/SyntheticSections.cpp
index 36595b176eb..1c66bcbe597 100644
--- a/lld/ELF/SyntheticSections.cpp
+++ b/lld/ELF/SyntheticSections.cpp
@@ -874,9 +874,15 @@ void MipsGotSection::writeTo(uint8_t *Buf) {
}
}
+// On PowerPC the .plt section is used to hold the table of function addresses
+// instead of the .got.plt, and the type is SHT_NOBITS similar to a .bss
+// section. I don't know why we have a BSS style type for the section but it is
+// consitent across both 64-bit PowerPC ABIs as well as the 32-bit PowerPC ABI.
GotPltSection::GotPltSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- Target->GotPltEntrySize, ".got.plt") {}
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE,
+ Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
+ Target->GotPltEntrySize,
+ Config->EMachine == EM_PPC64 ? ".plt" : ".got.plt") {}
void GotPltSection::addEntry(Symbol &Sym) {
assert(Sym.PltIndex == Entries.size());
@@ -905,12 +911,25 @@ bool GotPltSection::empty() const {
!(ElfSym::GlobalOffsetTable && Target->GotBaseSymInGotPlt);
}
-// On ARM the IgotPltSection is part of the GotSection, on other Targets it is
-// part of the .got.plt
+static StringRef getIgotPltName() {
+ // On ARM the IgotPltSection is part of the GotSection.
+ if (Config->EMachine == EM_ARM)
+ return ".got";
+
+ // On PowerPC64 the GotPltSection is renamed to '.plt' so the IgotPltSection
+ // needs to be named the same.
+ if (Config->EMachine == EM_PPC64)
+ return ".plt";
+
+ return ".got.plt";
+}
+
+// On PowerPC64 the GotPltSection type is SHT_NOBITS so we have to follow suit
+// with the IgotPltSection.
IgotPltSection::IgotPltSection()
- : SyntheticSection(SHF_ALLOC | SHF_WRITE, SHT_PROGBITS,
- Target->GotPltEntrySize,
- Config->EMachine == EM_ARM ? ".got" : ".got.plt") {}
+ : SyntheticSection(SHF_ALLOC | SHF_WRITE,
+ Config->EMachine == EM_PPC64 ? SHT_NOBITS : SHT_PROGBITS,
+ Target->GotPltEntrySize, getIgotPltName()) {}
void IgotPltSection::addEntry(Symbol &Sym) {
Sym.IsInIgot = true;
@@ -1182,6 +1201,16 @@ template <class ELFT> void DynamicSection<ELFT>::finalizeContents() {
}
}
+ // Glink dynamic tag is required by the V2 abi if the plt section isn't empty.
+ if (Config->EMachine == EM_PPC64 && !InX::Plt->empty()) {
+ // The Glink tag points to 32 bytes before the first lazy symbol resolution
+ // stub, which starts directly after the header.
+ Entries.push_back({DT_PPC64_GLINK, [=] {
+ unsigned Offset = Target->PltHeaderSize - 32;
+ return InX::Plt->getVA(0) + Offset;
+ }});
+ }
+
addInt(DT_NULL, 0);
getParent()->Link = this->Link;
@@ -1899,8 +1928,11 @@ void HashTableSection::writeTo(uint8_t *Buf) {
}
}
+// On PowerPC64 the lazy symbol resolvers go into the `global linkage table`
+// in the .glink section, rather then the typical .plt section.
PltSection::PltSection(bool IsIplt)
- : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16, ".plt"),
+ : SyntheticSection(SHF_ALLOC | SHF_EXECINSTR, SHT_PROGBITS, 16,
+ Config->EMachine == EM_PPC64 ? ".glink" : ".plt"),
HeaderSize(IsIplt ? 0 : Target->PltHeaderSize), IsIplt(IsIplt) {
// The PLT needs to be writable on SPARC as the dynamic linker will
// modify the instructions in the PLT entries.
diff --git a/lld/test/ELF/ppc64-dynamic-relocations.s b/lld/test/ELF/ppc64-dynamic-relocations.s
index c23370560d2..2d9dfc6f804 100644
--- a/lld/test/ELF/ppc64-dynamic-relocations.s
+++ b/lld/test/ELF/ppc64-dynamic-relocations.s
@@ -5,7 +5,7 @@
// RUN: ld.lld -shared %t2.o -o %t2.so
// RUN: ld.lld %t.o %t2.so -o %t
// RUN: llvm-readobj -dyn-relocations %t | FileCheck %s
-// RUN: llvm-objdump -D %t | FileCheck --check-prefix=DIS %s
+// RUN: llvm-objdump --section-headers %t | FileCheck --check-prefix=DIS %s
// RUN: llvm-readelf -dynamic-table %t | FileCheck --check-prefix=DT %s
// RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o
@@ -13,29 +13,23 @@
// RUN: ld.lld -shared %t2.o -o %t2.so
// RUN: ld.lld %t.o %t2.so -o %t
// RUN: llvm-readobj -dyn-relocations %t | FileCheck %s
-// RUN: llvm-objdump -D %t | FileCheck --check-prefix=DIS %s
+// RUN: llvm-objdump --section-headers %t | FileCheck --check-prefix=DIS %s
// RUN: llvm-readelf -dynamic-table %t | FileCheck --check-prefix=DT %s
// The dynamic relocation for foo should point to 16 bytes past the start of
-// the .got.plt section.
+// the .plt section.
// CHECK: Dynamic Relocations {
-// CHECK-NEXT: 0x10020010 R_PPC64_JMP_SLOT foo 0x0
+// CHECK-NEXT: 0x10030010 R_PPC64_JMP_SLOT foo 0x0
// There should be 2 reserved doublewords before the first entry. The dynamic
// linker will fill those in with the address of the resolver entry point and
// the dynamic object identifier.
-// DIS: Disassembly of section .got.plt:
-// DIS-NEXT: .got.plt:
-// DIS-NEXT: 10020000: 00 00 00 00 <unknown>
-// DIS-NEXT: 10020004: 00 00 00 00 <unknown>
-// DIS-NEXT: 10020008: 00 00 00 00 <unknown>
-// DIS-NEXT: 1002000c: 00 00 00 00 <unknown>
-// DIS-NEXT: 10020010: 00 00 00 00 <unknown>
-// DIS-NEXT: 10020014: 00 00 00 00 <unknown>
+// DIS: Idx Name Size Address Type
+// DIS: .plt 00000018 0000000010030000 BSS
-// DT_PLTGOT should point to the start of the .got.plt section.
-// DT: 0x0000000000000003 PLTGOT 0x10020000
+// DT_PLTGOT should point to the start of the .plt section.
+// DT: 0x0000000000000003 PLTGOT 0x10030000
.text
.abiversion 2
diff --git a/lld/test/ELF/ppc64-ifunc.s b/lld/test/ELF/ppc64-ifunc.s
index ccf4c635677..9bb0d9053c9 100644
--- a/lld/test/ELF/ppc64-ifunc.s
+++ b/lld/test/ELF/ppc64-ifunc.s
@@ -5,30 +5,32 @@
# RUN: ld.lld -shared %t2.o -o %t2.so
# RUN: ld.lld %t.o %t2.so -o %t
# RUN: llvm-objdump -D %t | FileCheck %s
+# RUN: llvm-readelf -dynamic-table %t | FileCheck --check-prefix=DT %s
# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o
# RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %p/Inputs/shared-ppc64.s -o %t2.o
# RUN: ld.lld -shared %t2.o -o %t2.so
# RUN: ld.lld %t.o %t2.so -o %t
# RUN: llvm-objdump -D %t | FileCheck %s
+# RUN: llvm-readelf -dynamic-table %t | FileCheck --check-prefix=DT %s
# CHECK: Disassembly of section .text:
-# Tocbase + (-2 << 16) + 32576
-# 0x100380d0 + (-131072) + 32576 = 0x10020010 (.got.plt[2])
+# Tocbase + (0 << 16) + 32560
+# 0x100280e0 + 0 + 32560 = 0x10030010 (.plt[2])
# CHECK: __plt_foo:
# CHECK-NEXT: std 2, 24(1)
-# CHECK-NEXT: addis 12, 2, -2
-# CHECK-NEXT: ld 12, 32576(12)
+# CHECK-NEXT: addis 12, 2, 0
+# CHECK-NEXT: ld 12, 32560(12)
# CHECK-NEXT: mtctr 12
# CHECK-NEXT: bctr
-# Tocbase + (-2 << 16) + 32584
-# 0x100380d0 + (-131072) + 32584 = 0x10020018 (.got.plt[3])
+# Tocbase + (0 << 16) + 32568
+# 0x100280e0 + 0 + 32568 = 0x1003018 (.plt[3])
# CHECK: __plt_ifunc:
# CHECK-NEXT: std 2, 24(1)
-# CHECK-NEXT: addis 12, 2, -2
-# CHECK-NEXT: ld 12, 32584(12)
+# CHECK-NEXT: addis 12, 2, 0
+# CHECK-NEXT: ld 12, 32568(12)
# CHECK-NEXT: mtctr 12
# CHECK-NEXT: bctr
@@ -36,24 +38,21 @@
# CHECK-NEXT: 10010028: {{.*}} nop
# CHECK: _start:
-# CHECK-NEXT: addis 2, 12, 3
-# CHECK-NEXT: addi 2, 2, -32604
+# CHECK-NEXT: addis 2, 12, 2
+# CHECK-NEXT: addi 2, 2, -32588
# CHECK-NEXT: bl .+67108812
# CHECK-NEXT: ld 2, 24(1)
# CHECK-NEXT: bl .+67108824
# CHECK-NEXT: ld 2, 24(1)
-# Address of .got.plt
-# CHECK: Disassembly of section .got.plt:
-# CHECK-NEXT: .got.plt:
-# CHECK-NEXT: 10020000:
-
-
# Check tocbase
# CHECK: Disassembly of section .got:
# CHECK-NEXT: .got:
-# CHECK-NEXT: 100300d0:
+# CHECK-NEXT: 100200e0
+# Check .plt address
+# DT_PLTGOT should point to the start of the .plt section.
+# DT: 0x0000000000000003 PLTGOT 0x10030000
.text
.abiversion 2
diff --git a/lld/test/ELF/ppc64-plt-stub.s b/lld/test/ELF/ppc64-plt-stub.s
index 86fd4734997..a644f487b8b 100644
--- a/lld/test/ELF/ppc64-plt-stub.s
+++ b/lld/test/ELF/ppc64-plt-stub.s
@@ -15,8 +15,8 @@
// CHECK: Disassembly of section .text:
// CHECK-NEXT: __plt_foo:
// CHECK-NEXT: std 2, 24(1)
-// CHECK-NEXT: addis 12, 2, -2
-// CHECK-NEXT: ld 12, 32576(12)
+// CHECK-NEXT: addis 12, 2, 0
+// CHECK-NEXT: ld 12, 32560(12)
// CHECK-NEXT: mtctr 12
// CHECK-NEXT: bctr
OpenPOWER on IntegriCloud