From dc06b0bc9ad055d06535462d91bfc2a744b2f589 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Tue, 13 Aug 2019 09:43:40 +0000 Subject: [PATCH] [ELF] Don't special case symbolic relocations with 0 addend to ifunc in writable locations Currently the following 3 relocation types do not trigger the creation of a canonical PLT (which changes STT_GNU_IFUNC to STT_FUNC and redirects all references): 1) GOT-generating (`needsGot`) 2) PLT-generating (`needsPlt`) 3) R_ABS with 0 addend in a writable location. This is used for for ifunc function pointers in writable sections such as .data and .toc. This patch deletes case 3) to simplify the R_*_IRELATIVE generating logic added in D57371. Other advantages: * It is guaranteed no more than 1 R_*_IRELATIVE is created for an ifunc. * PPC64: no need to special case ifunc in toc-indirect to toc-relative relaxation. See D65755 The deleted elf::addIRelativeRelocs demonstrates that one-pass scan through relocations makes several optimizations difficult. This is something we can think about in the future. Reviewed By: peter.smith Differential Revision: https://reviews.llvm.org/D65995 llvm-svn: 368661 --- lld/ELF/Arch/PPC64.cpp | 10 +++--- lld/ELF/Relocations.cpp | 41 ------------------------ lld/ELF/Writer.cpp | 2 -- lld/test/ELF/aarch64-gnu-ifunc-nonpreemptable2.s | 36 +++++++++++++++++++++ lld/test/ELF/gnu-ifunc-canon.s | 11 ++----- lld/test/ELF/ppc64-toc-relax-ifunc.s | 26 +++++++++++---- 6 files changed, 63 insertions(+), 63 deletions(-) create mode 100644 lld/test/ELF/aarch64-gnu-ifunc-nonpreemptable2.s diff --git a/lld/ELF/Arch/PPC64.cpp b/lld/ELF/Arch/PPC64.cpp index c448d84..b330dff 100644 --- a/lld/ELF/Arch/PPC64.cpp +++ b/lld/ELF/Arch/PPC64.cpp @@ -172,13 +172,13 @@ bool elf::tryRelaxPPC64TocIndirection(RelType type, const Relocation &rel, : 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)) diff --git a/lld/ELF/Relocations.cpp b/lld/ELF/Relocations.cpp index 22ae172..d611acb 100644 --- a/lld/ELF/Relocations.cpp +++ b/lld/ELF/Relocations.cpp @@ -1089,15 +1089,6 @@ static void processRelocAux(InputSectionBase &sec, RelExpr expr, RelType type, getLocation(sec, sym, offset)); } -struct IRelativeReloc { - RelType type; - InputSectionBase *sec; - uint64_t offset; - Symbol *sym; -}; - -static std::vector iRelativeRelocs; - template static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i, RelTy *end) { @@ -1265,12 +1256,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i, // 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; @@ -1287,17 +1272,6 @@ static void scanReloc(InputSectionBase &sec, OffsetGetter &getOffset, RelTy *&i, *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. // @@ -1365,21 +1339,6 @@ template void elf::scanRelocations(InputSectionBase &s) { 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) diff --git a/lld/ELF/Writer.cpp b/lld/ELF/Writer.cpp index 9c7044a..111056a 100644 --- a/lld/ELF/Writer.cpp +++ b/lld/ELF/Writer.cpp @@ -1738,8 +1738,6 @@ template void Writer::finalizeSections() { reportUndefinedSymbols(); } - addIRelativeRelocs(); - if (in.plt && in.plt->isNeeded()) in.plt->addSymbols(); if (in.iplt && in.iplt->isNeeded()) diff --git a/lld/test/ELF/aarch64-gnu-ifunc-nonpreemptable2.s b/lld/test/ELF/aarch64-gnu-ifunc-nonpreemptable2.s new file mode 100644 index 0000000..725b275 --- /dev/null +++ b/lld/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 diff --git a/lld/test/ELF/gnu-ifunc-canon.s b/lld/test/ELF/gnu-ifunc-canon.s index 18ee102..a7ba3af 100644 --- a/lld/test/ELF/gnu-ifunc-canon.s +++ b/lld/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 diff --git a/lld/test/ELF/ppc64-toc-relax-ifunc.s b/lld/test/ELF/ppc64-toc-relax-ifunc.s index 54467e4..7018258 100644 --- a/lld/test/ELF/ppc64-toc-relax-ifunc.s +++ b/lld/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) -- 2.7.4