[lld] fb2944b - [ELF][PPC32] Implement IPLT code sequence for non-preemptible IFUNC

Fangrui Song via llvm-commits llvm-commits at lists.llvm.org
Sun Dec 29 22:45:45 PST 2019


Author: Fangrui Song
Date: 2019-12-29T22:42:53-08:00
New Revision: fb2944bd7f8ac6d7c4bccd3ac2033ba58c690038

URL: https://github.com/llvm/llvm-project/commit/fb2944bd7f8ac6d7c4bccd3ac2033ba58c690038
DIFF: https://github.com/llvm/llvm-project/commit/fb2944bd7f8ac6d7c4bccd3ac2033ba58c690038.diff

LOG: [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

Added: 
    lld/test/ELF/ppc32-ifunc-nonpreemptible-nopic.s
    lld/test/ELF/ppc32-ifunc-nonpreemptible-pic.s

Modified: 
    lld/ELF/Arch/PPC.cpp
    lld/ELF/Thunks.cpp
    lld/ELF/Thunks.h

Removed: 
    lld/test/ELF/ppc32-gnu-ifunc-nonpreemptable.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 @@ class PPC final : public TargetInfo {
                 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<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 
diff erent 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 @@ class Thunk {
 // 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 at plt
+
+  lis 9, func at ha
+  la 9, func at l(9)


        


More information about the llvm-commits mailing list