Index: ELF/Arch/PPC64.cpp =================================================================== --- ELF/Arch/PPC64.cpp +++ ELF/Arch/PPC64.cpp @@ -172,13 +172,13 @@ : getRelaTocSymAndAddend(tocISB, rel.addend); // Only non-preemptable defined symbols can be relaxed. - // - // The toc entry of a non-preemptable ifunc is relocated by R_PPC64_IRELATIVE, - // which will run at load time to determine the relocated value. It is not - // known until load time, so the access cannot be relaxed. - if (!d || d->isPreemptible || d->isGnuIFunc()) + if (!d || d->isPreemptible) return false; + // R_PPC64_ADDR64 should have created a canonical PLT for the non-preemptable + // ifunc and changed its type to STT_FUNC. + assert(!d->isGnuIFunc()); + // Two instructions can materialize a 32-bit signed offset from the toc base. uint64_t tocRelative = d->getVA(addend) - getPPC64TocBase(); if (!isInt<32>(tocRelative)) Index: ELF/Relocations.cpp =================================================================== --- ELF/Relocations.cpp +++ ELF/Relocations.cpp @@ -1099,8 +1099,6 @@ Symbol *sym; }; -static std::vector iRelativeRelocs; - template static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i, RelTy *end) { @@ -1268,12 +1266,6 @@ // correctly, the IRELATIVE relocations are stored in an array which a // statically linked executable's startup code must enumerate using the // linker-defined symbols __rela?_iplt_{start,end}. - // - // - An absolute relocation to a non-preemptible ifunc (such as a global - // variable containing a pointer to the ifunc) needs to be relocated in - // the exact same way as a GOT entry, so we can avoid needing to make the - // PLT entry canonical by translating such relocations into IRELATIVE - // relocations in the relaIplt. if (!sym.isInPlt()) { // Create PLT and GOTPLT slots for the symbol. sym.isInIplt = true; @@ -1290,17 +1282,6 @@ *directSym); sym.pltIndex = directSym->pltIndex; } - if (expr == R_ABS && addend == 0 && (sec.flags & SHF_WRITE)) { - // We might be able to represent this as an IRELATIVE. But we don't know - // yet whether some later relocation will make the symbol point to a - // canonical PLT, which would make this either a dynamic RELATIVE (PIC) or - // static (non-PIC) relocation. So we keep a record of the information - // required to process the relocation, and after scanRelocs() has been - // called on all relocations, the relocation is resolved by - // addIRelativeRelocs(). - iRelativeRelocs.push_back({type, &sec, offset, &sym}); - return; - } if (needsGot(expr)) { // Redirect GOT accesses to point to the Igot. // @@ -1368,21 +1349,6 @@ scanRelocs(s, s.rels()); } -// Figure out which representation to use for any absolute relocs to -// non-preemptible ifuncs that we visited during scanRelocs(). -void elf::addIRelativeRelocs() { - for (IRelativeReloc &r : iRelativeRelocs) { - if (r.sym->type == STT_GNU_IFUNC) - in.relaIplt->addReloc( - {target->iRelativeRel, r.sec, r.offset, true, r.sym, 0}); - else if (config->isPic) - addRelativeReloc(r.sec, r.offset, r.sym, 0, R_ABS, r.type); - else - r.sec->relocations.push_back({R_ABS, r.type, r.offset, 0, r.sym}); - } - iRelativeRelocs.clear(); -} - static bool mergeCmp(const InputSection *a, const InputSection *b) { // std::merge requires a strict weak ordering. if (a->outSecOff < b->outSecOff) Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -1740,8 +1740,6 @@ reportUndefinedSymbols(); } - addIRelativeRelocs(); - if (in.plt && in.plt->isNeeded()) in.plt->addSymbols(); if (in.iplt && in.iplt->isNeeded()) Index: test/ELF/aarch64-gnu-ifunc-nonpreemptable2.s =================================================================== --- /dev/null +++ test/ELF/aarch64-gnu-ifunc-nonpreemptable2.s @@ -0,0 +1,36 @@ +# REQUIRES: aarch64 +# RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux-gnu %s -o %t.o +# RUN: ld.lld %t.o -o %t +# RUN: llvm-readelf -S -s %t | FileCheck %s --check-prefix=SEC +# RUN: llvm-readelf -x .rodata -x .data %t | FileCheck --check-prefix=HEX %s +# RUN: llvm-readobj -r %t | FileCheck %s --check-prefix=RELOC + +## ifunc is a non-preemptable STT_GNU_IFUNC. Check we create a canonical PLT +## and redirect .rodata and .data references to it. + +# SEC: .text PROGBITS 0000000000210000 +# SEC: .got.plt PROGBITS 0000000000220008 +# SEC: 0000000000210010 0 FUNC GLOBAL DEFAULT 4 ifunc + +## .rodata[0] and .data[0] store the address of the canonical PLT. +# HEX: section '.rodata': +# HEX-NEXT: 0x00200170 10002100 00000000 +# HEX: section '.data': +# HEX-NEXT: 0x00220000 10002100 00000000 + +# RELOC: .rela.dyn { +# RELOC-NEXT: 0x220008 R_AARCH64_IRELATIVE - 0x210000 +# RELOC-NEXT: } + +.globl ifunc +.type ifunc,@gnu_indirect_function +ifunc: + ret + +.rodata +.p2align 3 +.xword ifunc + +.data +.p2align 3 +.xword ifunc Index: test/ELF/gnu-ifunc-canon.s =================================================================== --- test/ELF/gnu-ifunc-canon.s +++ test/ELF/gnu-ifunc-canon.s @@ -4,7 +4,7 @@ // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/gnu-ifunc-canon-ro-abs.s -o %t-ro-abs.o // RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %S/Inputs/gnu-ifunc-canon-rw-addend.s -o %t-rw-addend.o // RUN: ld.lld %t.o -o %t1 -// RUN: llvm-readobj -r %t1 | FileCheck --check-prefix=IREL2 %s +// RUN: llvm-readobj -r %t1 | FileCheck --check-prefix=IREL1 %s // RUN: ld.lld %t.o %t-ro-pcrel.o -o %t2 // RUN: llvm-readobj -r %t2 | FileCheck --check-prefix=IREL1 %s // RUN: ld.lld %t.o %t-ro-abs.o -o %t3 @@ -22,7 +22,7 @@ // RUN: ld.lld %t-rw-addend.o %t.o -o %t7 // RUN: llvm-readobj -r %t7 | FileCheck --check-prefix=IREL1 %s // RUN: ld.lld %t.o -o %t8 -pie -// RUN: llvm-readobj -r %t8 | FileCheck --check-prefix=IREL2 %s +// RUN: llvm-readobj -r %t8 | FileCheck --check-prefix=IREL1-REL2 %s // RUN: ld.lld %t.o %t-ro-pcrel.o -o %t9 -pie // RUN: llvm-readobj -r %t9 | FileCheck --check-prefix=IREL1-REL2 %s // RUN: ld.lld %t.o %t-rw-addend.o -o %t10 -pie @@ -32,13 +32,6 @@ // RUN: ld.lld %t-rw-addend.o %t.o -o %t12 -pie // RUN: llvm-readobj -r %t12 | FileCheck --check-prefix=IREL1-REL3 %s -// Two relocs, one for the GOT and the other for .data. -// IREL2-NOT: R_X86_64_ -// IREL2: .rela.dyn -// IREL2-NEXT: R_X86_64_IRELATIVE -// IREL2-NEXT: R_X86_64_IRELATIVE -// IREL2-NOT: R_X86_64_ - // One reloc for the canonical PLT. // IREL1-NOT: R_X86_64_ // IREL1: .rela.dyn Index: test/ELF/ppc64-toc-relax-ifunc.s =================================================================== --- test/ELF/ppc64-toc-relax-ifunc.s +++ test/ELF/ppc64-toc-relax-ifunc.s @@ -4,14 +4,28 @@ # RUN: echo '.globl ifunc; .type ifunc, %gnu_indirect_function; ifunc:' | \ # RUN: llvm-mc -filetype=obj -triple=powerpc64le - -o %t1.o # RUN: ld.lld %t.o %t1.o -o %t -# RUN: llvm-objdump -d %t | FileCheck %s +# RUN: llvm-readelf -S -s %t | FileCheck --check-prefix=SEC %s +# RUN: llvm-readelf -x .toc %t | FileCheck --check-prefix=HEX %s +# RUN: llvm-objdump -d %t | FileCheck --check-prefix=DIS %s -## ifunc is a non-preemptable STT_GNU_IFUNC. Its toc entry will be -## relocated by R_PPC64_IRELATIVE, not representable by a toc-relative value. -## Check the toc-indirect access is not relaxed. +## ifunc is a non-preemptable STT_GNU_IFUNC. The R_PPC64_ADDR64 in .toc +## creates a canonical PLT for it and changes its type to STT_FUNC. We can thus +## still perform toc-indirect to toc-relative relaxation because the distance +## to the address of the canonical PLT is fixed. -# CHECK: nop -# CHECK-NEXT: ld 3, -32768(2) +# SEC: .text PROGBITS 0000000010010000 +# SEC: .plt NOBITS 0000000010030000 +# SEC: 0000000010010010 0 FUNC GLOBAL DEFAULT 3 ifunc + +## .toc[0] stores the address of the canonical PLT. +# HEX: section '.toc': +# HEX-NEXT: 0x10020000 10000110 00000000 + +# REL: .rela.dyn { +# REL-NEXT: 0x10030000 R_PPC64_IRELATIVE - 0x10010008 +# REL-NEXT: } + +# DIS: addi 3, 3, addis 3, 2, .toc@toc@ha ld 3, .toc@toc@l(3)