[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 <stdio.h>
  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
This commit is contained in:
Fangrui Song 2019-12-17 11:23:37 -08:00
parent 45acc35ac2
commit fb2944bd7f
5 changed files with 80 additions and 24 deletions

View File

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

View File

@ -707,13 +707,13 @@ InputSection *MicroMipsR6Thunk::getTargetInputSection() const {
return dyn_cast<InputSection>(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);

View File

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

View File

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

View File

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