[PPC64] Add lazy symbol resolution stubs.

Adds support for .glink resolver stubs from the example implementation in the V2
ABI (Section 4.2.5.3. Procedure Linkage Table). The stubs are written to the
PltSection, and the sections are renamed to match the PPC64 ABI:
    .got.plt --> .plt    Type = SHT_NOBITS
    .plt     --> .glink

And adds the DT_PPC64_GLINK dynamic tag to the dynamic section when the plt is
not empty.

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

llvm-svn: 331840
This commit is contained in:
Sean Fertile 2018-05-09 02:07:53 +00:00
parent 27bba4495a
commit 49914cc807
5 changed files with 102 additions and 43 deletions

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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