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 @@ -202,6 +202,8 @@ void writePltHeader(uint8_t *buf) const override; void writePlt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, int32_t index) const override; + void writeIplt(uint8_t *buf, uint64_t gotPltEntryAddr, uint64_t pltEntryAddr, + int32_t index) 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 +300,7 @@ symbolicRel = R_PPC64_ADDR64; pltHeaderSize = 60; pltEntrySize = 4; - ipltEntrySize = 4; + ipltEntrySize = 20; // PPC64PltCallStub::size gotBaseSymInGotPlt = false; gotHeaderEntriesNum = 1; gotPltHeaderEntriesNum = 2; @@ -676,6 +678,20 @@ write32(buf, 0x48000000 | ((-offset) & 0x03FFFFFc)); } +void PPC64::writeIplt(uint8_t *buf, uint64_t gotPltEntryAddr, + uint64_t pltEntryAddr, int32_t index) const { + int64_t offset = gotPltEntryAddr - getPPC64TocBase(); + uint16_t offHa = (offset + 0x8000) >> 16; + uint16_t offLo = offset & 0xffff; + + // The code sequence is the same as PPC64PltCallStub. + write32(buf + 0, 0xf8410018); // std r2,24(r1) + write32(buf + 4, 0x3d820000 | offHa); // addis r12, r2, OffHa + write32(buf + 8, 0xe98c0000 | offLo); // ld r12, OffLo(r12) + write32(buf + 12, 0x7d8903a6); // mtctr r12 + write32(buf + 16, 0x4e800420); // bctr +} + 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/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,97 @@ # 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 T ifunc -# NM-DAG: 00000000100101fc T ifunc2 +# SYM: Value Size Type Bind Vis Ndx +# SYM: 00000000100282a8 0 NOTYPE LOCAL HIDDEN 4 .TOC. +# SYM: 0000000010010290 0 FUNC GLOBAL DEFAULT 3 ifunc1 +# SYM: 0000000010010210 0 IFUNC GLOBAL DEFAULT 2 ifunc2 +# SYM: 000000001001027c 0 FUNC GLOBAL DEFAULT 3 ifunc3 -# SECTIONS: .plt NOBITS 0000000010030250 000250 000010 00 WA 0 0 8 +# SECTIONS: .plt NOBITS 00000000100302b0 0002b0 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, -32620 +# 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, 32744 +# CHECK-NEXT: addis 3, 2, -2 +# CHECK-NEXT: addi 3, 3, 32724 -# .plt[0] - .TOC. = 0x10030250 - 0x10028248 = (1<<16) - 32760 -# CHECK: __plt_ifunc: -# 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 +# .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. = 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: + +## .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: std 2, 24(1) +# CHECK-NEXT: addis 12, 2, 1 +# CHECK-NEXT: ld 12, -32760(12) +# CHECK-NEXT: mtctr 12 +# CHECK-NEXT: bctr +# CHECK-EMPTY: +# CHECK-NEXT: 000000001001027c 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-NEXT: 0000000010010290 ifunc1: +# CHECK-NEXT: std 2, 24(1) +# CHECK-NEXT: addis 12, 2, 1 +# CHECK-NEXT: ld 12, -32744(12) +# CHECK-NEXT: mtctr 12 +# CHECK-NEXT: bctr -## 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. +## 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: 0x100302B0 R_PPC64_IRELATIVE - 0x10010210 +# REL-NEXT: 0x100302B8 R_PPC64_IRELATIVE - 0x10010210 +# REL-NEXT: 0x100302C0 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 +103,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 0000000010030208 # 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: 0x10020200 e8010110 00000000 # REL: .rela.dyn { # REL-NEXT: 0x100301f8 R_PPC64_IRELATIVE - 0x100101e8