From fb2944bd7f8ac6d7c4bccd3ac2033ba58c690038 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Tue, 17 Dec 2019 11:23:37 -0800 Subject: [PATCH] [ELF][PPC32] Implement IPLT code sequence for non-preemptible IFUNC Similar to D71509 (EM_PPC64), on EM_PPC, the IPLT code sequence should be similar to a PLT call stub. Unlike EM_PPC64, EM_PPC -msecure-plt has small/large PIC model differences. * -fpic/-fpie: R_PPC_PLTREL24 r_addend=0. The call stub loads an address relative to `_GLOBAL_OFFSET_TABLE_`. * -fPIC/-fPIE: R_PPC_PLTREL24 r_addend=0x8000. (A partial linked object file may have an addend larger than 0x8000.) The call stub loads an address relative to .got2+0x8000. Just assume large PIC model for now. This patch makes: // clang -fuse-ld=lld -msecure-plt -fno-pie -no-pie a.c // clang -fuse-ld=lld -msecure-plt -fPIE -pie a.c #include static void impl(void) { puts("meow"); } void thefunc(void) __attribute__((ifunc("resolver"))); void *resolver(void) { return &impl; } int main(void) { thefunc(); void (*theptr)(void) = &thefunc; theptr(); } work on Linux glibc. -fpie will crash because the compiler and the linker do not agree on the value which r30 stores (_GLOBAL_OFFSET_TABLE_ vs .got2+0x8000). Differential Revision: https://reviews.llvm.org/D71621 --- lld/ELF/Arch/PPC.cpp | 12 ++++- lld/ELF/Thunks.cpp | 22 +++++---- lld/ELF/Thunks.h | 3 ++ ...e.s => ppc32-ifunc-nonpreemptible-nopic.s} | 21 +++------ lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s | 46 +++++++++++++++++++ 5 files changed, 80 insertions(+), 24 deletions(-) rename lld/test/ELF/{ppc32-gnu-ifunc-nonpreemptable.s => ppc32-ifunc-nonpreemptible-nopic.s} (65%) create mode 100644 lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s diff --git a/lld/ELF/Arch/PPC.cpp b/lld/ELF/Arch/PPC.cpp index 4c0f2c17da1d..3c0b0c290b58 100644 --- a/lld/ELF/Arch/PPC.cpp +++ b/lld/ELF/Arch/PPC.cpp @@ -10,6 +10,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" +#include "Thunks.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" @@ -35,6 +36,8 @@ public: uint64_t pltEntryAddr) const override { llvm_unreachable("should call writePPC32GlinkSection() instead"); } + void writeIplt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) const override; void writeGotPlt(uint8_t *buf, const Symbol &s) const override; bool needsThunk(RelExpr expr, RelType relocType, const InputFile *file, uint64_t branchAddr, const Symbol &s, @@ -144,7 +147,7 @@ PPC::PPC() { gotPltHeaderEntriesNum = 0; pltHeaderSize = 64; // size of PLTresolve in .glink pltEntrySize = 4; - ipltEntrySize = 4; + ipltEntrySize = 16; needsThunks = true; @@ -158,6 +161,13 @@ PPC::PPC() { write32(trapInstr.data(), 0x7fe00008); } +void PPC::writeIplt(uint8_t *buf, const Symbol &sym, + uint64_t /*pltEntryAddr*/) const { + // In -pie or -shared mode, assume r30 points to .got2+0x8000, and use a + // .got2.plt_pic32. thunk. + writePPC32PltCallStub(buf, sym.getGotPltVA(), sym.file, 0x8000); +} + void PPC::writeGotHeader(uint8_t *buf) const { // _GLOBAL_OFFSET_TABLE_[0] = _DYNAMIC // glibc stores _dl_runtime_resolve in _GLOBAL_OFFSET_TABLE_[1], diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp index 5c84fcc8dad9..446c88c92d9e 100644 --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -707,13 +707,13 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const { return dyn_cast(dr.section); } -void PPC32PltCallStub::writeTo(uint8_t *buf) { +void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, + const InputFile *file, int64_t addend) { if (!config->isPic) { - uint64_t va = destination.getGotPltVA(); - write32(buf + 0, 0x3d600000 | (va + 0x8000) >> 16); // lis r11,ha - write32(buf + 4, 0x816b0000 | (uint16_t)va); // lwz r11,l(r11) - write32(buf + 8, 0x7d6903a6); // mtctr r11 - write32(buf + 12, 0x4e800420); // bctr + write32(buf + 0, 0x3d600000 | (gotPltVA + 0x8000) >> 16); // lis r11,ha + write32(buf + 4, 0x816b0000 | (uint16_t)gotPltVA); // lwz r11,l(r11) + write32(buf + 8, 0x7d6903a6); // mtctr r11 + write32(buf + 12, 0x4e800420); // bctr return; } uint32_t offset; @@ -721,12 +721,12 @@ void PPC32PltCallStub::writeTo(uint8_t *buf) { // The stub loads an address relative to r30 (.got2+Addend). Addend is // almost always 0x8000. The address of .got2 is different in another object // file, so a stub cannot be shared. - offset = destination.getGotPltVA() - (in.ppc32Got2->getParent()->getVA() + - file->ppc32Got2OutSecOff + addend); + offset = gotPltVA - (in.ppc32Got2->getParent()->getVA() + + file->ppc32Got2OutSecOff + addend); } else { // The stub loads an address relative to _GLOBAL_OFFSET_TABLE_ (which is // currently the address of .got). - offset = destination.getGotPltVA() - in.got->getVA(); + offset = gotPltVA - in.got->getVA(); } uint16_t ha = (offset + 0x8000) >> 16, l = (uint16_t)offset; if (ha == 0) { @@ -742,6 +742,10 @@ void PPC32PltCallStub::writeTo(uint8_t *buf) { } } +void PPC32PltCallStub::writeTo(uint8_t *buf) { + writePPC32PltCallStub(buf, destination.getGotPltVA(), file, addend); +} + void PPC32PltCallStub::addSymbols(ThunkSection &isec) { std::string buf; raw_string_ostream os(buf); diff --git a/lld/ELF/Thunks.h b/lld/ELF/Thunks.h index e16f9a85c4c2..a8575b4cdb59 100644 --- a/lld/ELF/Thunks.h +++ b/lld/ELF/Thunks.h @@ -14,6 +14,7 @@ namespace lld { namespace elf { class Defined; +class InputFile; class Symbol; class ThunkSection; // Class to describe an instance of a Thunk. @@ -68,6 +69,8 @@ public: // ThunkSection. Thunk *addThunk(const InputSection &isec, Relocation &rel); +void writePPC32PltCallStub(uint8_t *buf, uint64_t gotPltVA, + const InputFile *file, int64_t addend); void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset); } // namespace elf diff --git a/lld/test/ELF/ppc32-gnu-ifunc-nonpreemptable.s b/lld/test/ELF/ppc32-ifunc-nonpreemptible-nopic.s similarity index 65% rename from lld/test/ELF/ppc32-gnu-ifunc-nonpreemptable.s rename to lld/test/ELF/ppc32-ifunc-nonpreemptible-nopic.s index 0bf92e3e4d72..097417d225f9 100644 --- a/lld/test/ELF/ppc32-gnu-ifunc-nonpreemptable.s +++ b/lld/test/ELF/ppc32-ifunc-nonpreemptible-nopic.s @@ -1,23 +1,19 @@ -# REQUIRES: ppc, asserts -# XFAIL: * +# REQUIRES: ppc # RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o # RUN: ld.lld %t.o -o %t # RUN: llvm-readobj -r %t | FileCheck --check-prefix=RELOC %s # RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s -# RUN: llvm-readelf -x .got2 %t | FileCheck --check-prefix=HEX %s # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s # RELOC: .rela.dyn { -# RELOC-NEXT: 0x10020108 R_PPC_IRELATIVE - 0x100100E0 +# RELOC-NEXT: 0x10020110 R_PPC_IRELATIVE - 0x100100E0 # RELOC-NEXT: } # SYM: 10010100 0 FUNC GLOBAL DEFAULT {{.*}} func -# HEX: 0x10020104 10010100 +# HEX: 0x10020110 10010100 -.section .got2,"aw" -.long func - -# CHECK: func_resolver: +# CHECK: Disassembly of section .text: +# CHECK: .text: # CHECK-NEXT: 100100e0: blr # CHECK: _start: # CHECK-NEXT: bl .+12 @@ -25,17 +21,14 @@ # CHECK-NEXT: addi 9, 9, 256 # CHECK-EMPTY: # CHECK-NEXT: 00000000.plt_call32.func: -## 0x10020108 = 65536*4098+264 +## 0x10020110 = 65536*4098+272 # CHECK-NEXT: lis 11, 4098 -# CHECK-NEXT: lwz 11, 264(11) +# CHECK-NEXT: lwz 11, 272(11) .text .globl func .type func, @gnu_indirect_function func: -.globl func_resolver -.type func_resolver, @function -func_resolver: blr .globl _start diff --git a/lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s b/lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s new file mode 100644 index 000000000000..16edee08c01d --- /dev/null +++ b/lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s @@ -0,0 +1,46 @@ +# REQUIRES: ppc +# RUN: llvm-mc -filetype=obj -triple=powerpc %s -o %t.o +# RUN: ld.lld -pie %t.o -o %t +# RUN: llvm-readobj -r %t | FileCheck --check-prefix=RELOC %s +# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s +# RUN: llvm-readelf -x .got2 %t | FileCheck --check-prefix=HEX %s +# RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s + +# RELOC: .rela.dyn { +# RELOC-NEXT: 0x30240 R_PPC_RELATIVE - 0x101A8 +# RELOC-NEXT: 0x30244 R_PPC_IRELATIVE - 0x10188 +# RELOC-NEXT: } + +# SYM: 000101a8 0 FUNC GLOBAL DEFAULT {{.*}} func +# HEX: 0x00030240 00000000 + +.section .got2,"aw" +.long func + +# CHECK: Disassembly of section .text: +# CHECK: .text: +# CHECK-NEXT: 10188: blr +# CHECK: _start: +# CHECK-NEXT: bl .+12 +# CHECK-NEXT: lis 9, 1 +# CHECK-NEXT: addi 9, 9, 424 +# CHECK-EMPTY: +# CHECK-NEXT: 00008000.got2.plt_pic32.func: +## 0x10020114 = 65536*4098+276 +# CHECK-NEXT: lwz 11, -32764(30) +# CHECK-NEXT: mtctr 11 +# CHECK-NEXT: bctr +# CHECK-NEXT: nop + +.text +.globl func +.type func, @gnu_indirect_function +func: + blr + +.globl _start +_start: + bl func+0x8000@plt + + lis 9, func@ha + la 9, func@l(9)