Index: ELF/Arch/PPC64.cpp =================================================================== --- ELF/Arch/PPC64.cpp +++ ELF/Arch/PPC64.cpp @@ -43,6 +43,9 @@ 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 @@ 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 @@ 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 toAddr16Rel(RelType Type, uint64_t Val) { uint64_t V = Val - PPC64TocOffset; switch (Type) { Index: ELF/SyntheticSections.cpp =================================================================== --- ELF/SyntheticSections.cpp +++ ELF/SyntheticSections.cpp @@ -874,9 +874,15 @@ } } +// 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 @@ !(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 @@ } } + // 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 @@ } } +// 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. Index: test/ELF/ppc64-dynamic-relocations.s =================================================================== --- test/ELF/ppc64-dynamic-relocations.s +++ 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 -// DIS-NEXT: 10020004: 00 00 00 00 -// DIS-NEXT: 10020008: 00 00 00 00 -// DIS-NEXT: 1002000c: 00 00 00 00 -// DIS-NEXT: 10020010: 00 00 00 00 -// DIS-NEXT: 10020014: 00 00 00 00 +// 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 Index: test/ELF/ppc64-ifunc.s =================================================================== --- test/ELF/ppc64-ifunc.s +++ 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 Index: test/ELF/ppc64-plt-stub.s =================================================================== --- test/ELF/ppc64-plt-stub.s +++ 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