Commit fb2944bd authored by Fangrui Song's avatar Fangrui Song
Browse files

[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
parent 45acc35a
Loading
Loading
Loading
Loading
+11 −1
Original line number Diff line number Diff line
@@ -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],
+13 −9
Original line number Diff line number Diff line
@@ -707,11 +707,11 @@ 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 + 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;
@@ -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() +
    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);
+3 −0
Original line number Diff line number Diff line
@@ -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
+7 −14
Original line number Diff line number Diff line
# 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
+46 −0
Original line number Diff line number Diff line
# 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)