diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -9,6 +9,7 @@ #include "Symbols.h" #include "SyntheticSections.h" #include "Target.h" +#include "Thunks.h" #include "lld/Common/ErrorHandler.h" #include "llvm/Support/Endian.h" @@ -202,6 +203,8 @@ void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, const Symbol &sym, uint64_t pltEntryAddr) const override; + void writeIplt(uint8_t *buf, const Symbol &sym, + uint64_t pltEntryAddr) 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, @@ -298,7 +301,7 @@ symbolicRel = R_PPC64_ADDR64; pltHeaderSize = 60; pltEntrySize = 4; - ipltEntrySize = 4; + ipltEntrySize = 16; // PPC64PltCallStub::size gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 1; gotPltHeaderEntriesNum = 2; @@ -676,6 +679,11 @@ write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); } +void PPC64::writeIplt(uint8_t *buf, const Symbol &sym, + uint64_t /*pltEntryAddr*/) const { + writePPC64LoadAndBranch(buf, sym.getGotPltVA() - getPPC64TocBase()); +} + static std::pair toAddr16Rel(RelType type, uint64_t val) { // Relocations relative to the toc-base need to be adjusted by the Toc offset. uint64_t tocBiasedVal = val - ppc64TocOffset; diff --git a/lld/ELF/Thunks.h b/lld/ELF/Thunks.h --- a/lld/ELF/Thunks.h +++ b/lld/ELF/Thunks.h @@ -68,6 +68,8 @@ // ThunkSection. Thunk *addThunk(const InputSection &isec, Relocation &rel); +void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset); + } // namespace elf } // namespace lld diff --git a/lld/ELF/Thunks.cpp b/lld/ELF/Thunks.cpp --- a/lld/ELF/Thunks.cpp +++ b/lld/ELF/Thunks.cpp @@ -761,7 +761,7 @@ return !config->isPic || (isec.file == file && rel.addend == addend); } -static void writePPCLoadAndBranch(uint8_t *buf, int64_t offset) { +void writePPC64LoadAndBranch(uint8_t *buf, int64_t offset) { uint16_t offHa = (offset + 0x8000) >> 16; uint16_t offLo = offset & 0xffff; @@ -775,7 +775,7 @@ int64_t offset = destination.getGotPltVA() - getPPC64TocBase(); // Save the TOC pointer to the save-slot reserved in the call frame. write32(buf + 0, 0xf8410018); // std r2,24(r1) - writePPCLoadAndBranch(buf + 4, offset); + writePPC64LoadAndBranch(buf + 4, offset); } void PPC64PltCallStub::addSymbols(ThunkSection &isec) { @@ -787,7 +787,7 @@ void PPC64LongBranchThunk::writeTo(uint8_t *buf) { int64_t offset = in.ppc64LongBranchTarget->getEntryVA(&destination, addend) - getPPC64TocBase(); - writePPCLoadAndBranch(buf, offset); + writePPC64LoadAndBranch(buf, offset); } void PPC64LongBranchThunk::addSymbols(ThunkSection &isec) { diff --git a/lld/test/ELF/ppc64-ifunc.s b/lld/test/ELF/ppc64-ifunc.s --- a/lld/test/ELF/ppc64-ifunc.s +++ b/lld/test/ELF/ppc64-ifunc.s @@ -2,66 +2,94 @@ # RUN: llvm-mc -filetype=obj -triple=powerpc64le-unknown-linux %s -o %t.o # RUN: ld.lld %t.o -o %t -# RUN: llvm-nm %t | FileCheck --check-prefix=NM %s +# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s # RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECTIONS %s # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s # RUN: llvm-readobj -r %t | FileCheck --check-prefix=REL %s # RUN: llvm-mc -filetype=obj -triple=powerpc64-unknown-linux %s -o %t.o # RUN: ld.lld %t.o -o %t -# RUN: llvm-nm %t | FileCheck --check-prefix=NM %s +# RUN: llvm-readelf -s %t | FileCheck --check-prefix=SYM %s # RUN: llvm-readelf -S %t | FileCheck --check-prefix=SECTIONS %s # RUN: llvm-objdump -d --no-show-raw-insn %t | FileCheck %s # RUN: llvm-readobj -r %t | FileCheck --check-prefix=REL %s -# NM-DAG: 0000000010028248 d .TOC. -# NM-DAG: 00000000100101f8 i ifunc -# NM-DAG: 00000000100101fc i ifunc2 +# SYM: Value Size Type Bind Vis Ndx +# SYM: 0000000010028298 0 NOTYPE LOCAL HIDDEN 4 .TOC. +# SYM: 0000000010010288 0 FUNC GLOBAL DEFAULT 3 ifunc1 +# SYM: 0000000010010210 0 IFUNC GLOBAL DEFAULT 2 ifunc2 +# SYM: 0000000010010278 0 FUNC GLOBAL DEFAULT 3 ifunc3 -# SECTIONS: .plt NOBITS 0000000010030250 000250 000010 00 WA 0 0 8 +# SECTIONS: .plt NOBITS 00000000100302a0 0002a0 000018 00 WA 0 0 8 # __plt_ifunc - . = 0x10010218 - 0x10010208 = 16 # __plt_ifunc2 - . = 0x1001022c - 0x10010210 = 28 # CHECK: _start: # CHECK-NEXT: addis 2, 12, 2 -# CHECK-NEXT: addi 2, 2, -32696 -# CHECK-NEXT: 10010208: bl .+16 +# CHECK-NEXT: addi 2, 2, -32636 +# CHECK-NEXT: 1001021c: bl .+36 # CHECK-NEXT: ld 2, 24(1) -# CHECK-NEXT: 10010210: bl .+28 +# CHECK-NEXT: 10010224: bl .+48 # CHECK-NEXT: ld 2, 24(1) +# CHECK-NEXT: addis 3, 2, -2 +# CHECK-NEXT: addi 3, 3, 32752 +# CHECK-NEXT: addis 3, 2, -2 +# CHECK-NEXT: addi 3, 3, 32736 -# .plt[0] - .TOC. = 0x10030250 - 0x10028248 = (1<<16) - 32760 -# CHECK: __plt_ifunc: +# .plt[0] - .TOC. = 0x100302b0 - 0x100282a8 = (1<<16) - 32760 +# CHECK: __plt_ifunc2: # CHECK-NEXT: std 2, 24(1) # CHECK-NEXT: addis 12, 2, 1 # CHECK-NEXT: ld 12, -32760(12) # CHECK-NEXT: mtctr 12 # CHECK-NEXT: bctr -# .plt[1] - .TOC. = 0x10030250+8 - 0x10028248 = (1<<16) - 32752 -# CHECK: __plt_ifunc2: +# .plt[1] - .TOC. = 0x100302b0+8 - 0x100282a8 = (1<<16) - 32752 +# CHECK: __plt_ifunc3: # CHECK-NEXT: std 2, 24(1) # CHECK-NEXT: addis 12, 2, 1 # CHECK-NEXT: ld 12, -32752(12) # CHECK-NEXT: mtctr 12 # CHECK-NEXT: bctr +# CHECK-EMPTY: -## Check that we emit 2 R_PPC64_IRELATIVE in .rela.dyn. -## glibc powerpc64 does not eagerly resolve R_PPC64_IRELATIVE if they are in .rela.plt. +## .glink has 3 IPLT entries for ifunc1, ifunc2 and ifunc3. +## ifunc2 and ifunc3 have the same code sequence as their PLT call stubs. +# CHECK: Disassembly of section .glink: +# CHECK-EMPTY: +# CHECK-NEXT: 0000000010010268 .glink: +# CHECK-NEXT: addis 12, 2, 1 +# CHECK-NEXT: ld 12, -32760(12) +# CHECK-NEXT: mtctr 12 +# CHECK-NEXT: bctr +# CHECK-EMPTY: +# CHECK-NEXT: 0000000010010278 ifunc3: +# CHECK-NEXT: addis 12, 2, 1 +# CHECK-NEXT: ld 12, -32752(12) +# CHECK-NEXT: mtctr 12 +# CHECK-NEXT: bctr +# CHECK-EMPTY: +# CHECK-NEXT: 0000000010010288 ifunc1: +# CHECK-NEXT: addis 12, 2, 1 +# CHECK-NEXT: ld 12, -32744(12) +# CHECK-NEXT: mtctr 12 +# CHECK-NEXT: bctr + +## Check that we emit 3 R_PPC64_IRELATIVE in .rela.dyn. # REL: .rela.dyn { -# REL-NEXT: 0x10030250 R_PPC64_IRELATIVE - 0x100101F8 -# REL-NEXT: 0x10030258 R_PPC64_IRELATIVE - 0x100101FC +# REL-NEXT: 0x100302A0 R_PPC64_IRELATIVE - 0x10010210 +# REL-NEXT: 0x100302A8 R_PPC64_IRELATIVE - 0x10010210 +# REL-NEXT: 0x100302B0 R_PPC64_IRELATIVE - 0x10010210 # REL-NEXT: } -.type ifunc STT_GNU_IFUNC -.globl ifunc -ifunc: - nop - -.type ifunc2 STT_GNU_IFUNC -.globl ifunc2 +.type ifunc1,@gnu_indirect_function +.type ifunc2,@gnu_indirect_function +.type ifunc3,@gnu_indirect_function +.globl ifunc1, ifunc2, ifunc3 +ifunc1: ifunc2: - nop +ifunc3: + blr .global _start .type _start,@function @@ -72,7 +100,20 @@ addi 2, 2, .TOC.-.Lfunc_gep0@l .Lfunc_lep0: .localentry _start, .Lfunc_lep0-.Lfunc_gep0 - bl ifunc - nop + + ## ifunc1 is taken address. + ## ifunc2 is called. + ## ifunc3 is both taken address and called. + ## We need to create IPLT entries in .glink for ifunc1 and ifunc3, change + ## their types from STT_GNU_IFUNC to STT_FUNC, and set their st_shndx/st_value + ## to their .glink entries. Technically we don't need an entry for ifunc2 in + ## .glink, but we currently do that. bl ifunc2 nop + bl ifunc3 + nop + + addis 3, 2, ifunc1@toc@ha + addi 3, 3, ifunc1@toc@l + addis 3, 2, ifunc3@toc@ha + addi 3, 3, ifunc3@toc@l diff --git a/lld/test/ELF/ppc64-toc-relax-ifunc.s b/lld/test/ELF/ppc64-toc-relax-ifunc.s --- a/lld/test/ELF/ppc64-toc-relax-ifunc.s +++ b/lld/test/ELF/ppc64-toc-relax-ifunc.s @@ -14,12 +14,12 @@ ## to the address of the canonical PLT is fixed. # SEC: .text PROGBITS 00000000100101e0 -# SEC: .plt NOBITS 00000000100301f8 +# SEC: .plt NOBITS 0000000010030200 # SEC: 00000000100101e8 0 FUNC GLOBAL DEFAULT 3 ifunc ## .toc[0] stores the address of the canonical PLT. # HEX: section '.toc': -# HEX-NEXT: 0x100201f0 e8010110 00000000 +# HEX-NEXT: 0x100201f8 e8010110 00000000 # REL: .rela.dyn { # REL-NEXT: 0x100301f8 R_PPC64_IRELATIVE - 0x100101e8