diff --git a/lld/ELF/ICF.cpp b/lld/ELF/ICF.cpp index 1cdcf6be9d8a9..849f6bdd445f9 100644 --- a/lld/ELF/ICF.cpp +++ b/lld/ELF/ICF.cpp @@ -80,6 +80,7 @@ #include "SymbolTable.h" #include "Symbols.h" #include "SyntheticSections.h" +#include "llvm/ADT/EquivalenceClasses.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Support/Parallel.h" @@ -333,6 +334,28 @@ bool ICF::equalsConstant(const InputSection *a, const InputSection *b) { : constantEq(a, ra.relas, b, rb.relas); } +template +static SmallVector getReloc(const InputSection *sec, + Relocs relocs) { + SmallVector syms; + for (auto ri = relocs.begin(), re = relocs.end(); ri != re; ++ri) { + Symbol &sym = sec->file->getRelocTargetSym(*ri); + syms.push_back(&sym); + } + return syms; +} + +template +static SmallVector getRelocTargetSyms(const InputSection *sec) { + const RelsOrRelas rel = sec->template relsOrRelas(); + if (rel.areRelocsCrel()) + return getReloc(sec, rel.crels); + if (rel.areRelocsRel()) + return getReloc(sec, rel.rels); + + return getReloc(sec, rel.relas); +} + // Compare two lists of relocations. Returns true if all pairs of // relocations point to the same section in terms of ICF. template @@ -535,14 +558,28 @@ template void ICF::run() { auto print = [&ctx = ctx]() -> ELFSyncStream { return {ctx, ctx.arg.printIcfSections ? DiagLevel::Msg : DiagLevel::None}; }; + + EquivalenceClasses symbolEquivalence; // Merge sections by the equivalence class. + // Merge symbols identified as equivalent during ICF. forEachClassRange(0, sections.size(), [&](size_t begin, size_t end) { if (end - begin == 1) return; print() << "selected section " << sections[begin]; + SmallVector syms = getRelocTargetSyms(sections[begin]); for (size_t i = begin + 1; i < end; ++i) { print() << " removing identical section " << sections[i]; sections[begin]->replace(sections[i]); + SmallVector replacedSyms = + getRelocTargetSyms(sections[i]); + assert(syms.size() == replacedSyms.size() && + "Should have same number of syms!"); + for (size_t i = 0; i < syms.size(); i++) { + if (syms[i] == replacedSyms[i] || !syms[i]->isGlobal() || + !replacedSyms[i]->isGlobal()) + continue; + symbolEquivalence.unionSets(syms[i], replacedSyms[i]); + } // At this point we know sections merged are fully identical and hence // we want to remove duplicate implicit dependencies such as link order @@ -561,11 +598,26 @@ template void ICF::run() { d->folded = true; } }; - for (Symbol *sym : ctx.symtab->getSymbols()) + for (Symbol *sym : ctx.symtab->getSymbols()) { fold(sym); + auto it = symbolEquivalence.findLeader(sym); + if (it != symbolEquivalence.member_end() && *it != sym) { + print() << "redirecting '" << sym->getName() << "' in symtab to '" + << (*it)->getName() << "'"; + ctx.symtab->redirect(sym, *it); + } + } parallelForEach(ctx.objectFiles, [&](ELFFileBase *file) { for (Symbol *sym : file->getLocalSymbols()) fold(sym); + for (Symbol *&sym : file->getMutableGlobalSymbols()) { + auto it = symbolEquivalence.findLeader(sym); + if (it != symbolEquivalence.member_end() && *it != sym) { + print() << "redirecting '" << sym->getName() << "' to '" + << (*it)->getName() << "'"; + sym = *it; + } + } }); // InputSectionDescription::sections is populated by processSectionCommands(). diff --git a/lld/ELF/SymbolTable.cpp b/lld/ELF/SymbolTable.cpp index b8a70d4e898fc..91e47e15b01a4 100644 --- a/lld/ELF/SymbolTable.cpp +++ b/lld/ELF/SymbolTable.cpp @@ -29,6 +29,12 @@ using namespace llvm::ELF; using namespace lld; using namespace lld::elf; +void SymbolTable::redirect(Symbol *from, Symbol *to) { + int &fromIdx = symMap[CachedHashStringRef(from->getName())]; + const int toIdx = symMap[CachedHashStringRef(to->getName())]; + fromIdx = toIdx; +} + void SymbolTable::wrap(Symbol *sym, Symbol *real, Symbol *wrap) { // Redirect __real_foo to the original foo and foo to the original __wrap_foo. int &idx1 = symMap[CachedHashStringRef(sym->getName())]; diff --git a/lld/ELF/SymbolTable.h b/lld/ELF/SymbolTable.h index d6443742f7baa..e3a39bac85f97 100644 --- a/lld/ELF/SymbolTable.h +++ b/lld/ELF/SymbolTable.h @@ -41,6 +41,7 @@ class SymbolTable { SymbolTable(Ctx &ctx) : ctx(ctx) {} ArrayRef getSymbols() const { return symVector; } + void redirect(Symbol *from, Symbol *to); void wrap(Symbol *sym, Symbol *real, Symbol *wrap); Symbol *insert(StringRef name); diff --git a/lld/test/ELF/aarch64-got-merging-icf.s b/lld/test/ELF/aarch64-got-merging-icf.s new file mode 100644 index 0000000000000..01d7bdfd75185 --- /dev/null +++ b/lld/test/ELF/aarch64-got-merging-icf.s @@ -0,0 +1,95 @@ +## REQUIRES: aarch64 +## Check that symbols that ICF assumes to be the same get a single GOT entry + +# RUN: llvm-mc -filetype=obj -triple=aarch64 %s -o %t +# RUN: llvm-mc -filetype=obj -crel -triple=aarch64 %s -o %tcrel +# RUN: ld.lld %t -o %t2 --icf=all +# RUN: ld.lld %tcrel -o %tcrel2 --icf=all + +# RUN: llvm-objdump --section-headers %t2 | FileCheck %s --check-prefix=EXE +# RUN: llvm-objdump --section-headers %tcrel2 | FileCheck %s --check-prefix=EXE + +# RUN: ld.lld -shared %t -o %t3 --icf=all +# RUN: ld.lld -shared %tcrel -o %tcrel3 --icf=all + +# RUN: llvm-objdump --section-headers %t3 | FileCheck %s --check-prefix=DSO +# RUN: llvm-objdump --section-headers %tcrel3 | FileCheck %s --check-prefix=DSO + +## All global g* symbols should merge into a single GOT entry while non-global +## gets its own GOT entry. +# EXE: {{.*}}.got 00000010{{.*}} + +## When symbols are preemptible in DSO mode, GOT entries wouldn't be merged +# DSO: {{.*}}.got 00000028{{.*}} + +.addrsig + +callee: +ret + +.macro f, index, isglobal + +# (Kept unique) first instruction of the GOT code sequence +.section .text.f1_\index,"ax",@progbits +f1_\index: +adrp x0, :got:g\index +mov x1, #\index +b f2_\index + +# Folded, second instruction of the GOT code sequence +.section .text.f2_\index,"ax",@progbits +f2_\index: +ldr x0, [x0, :got_lo12:g\index] +b callee + +# Folded +.ifnb \isglobal +.globl g\index +.endif +.section .rodata.g\index,"a",@progbits +g_\index: +.long 111 +.long 122 + +g\index: +.byte 123 + +.section .text._start,"ax",@progbits +bl f1_\index + +.endm + +## Another set of sections merging: g1 <- g2. Linker should be able to +## resolve both g1 and g2 to g0 based on ICF on previous sections. + +.section .text.t1_0,"ax",@progbits +t1_0: +adrp x2, :got:g1 +mov x3, #1 +b t2_0 + +.section .text.t2_0,"ax",@progbits +t2_0: +ldr x2, [x2, :got_lo12:g1] +b callee + +.section .text.t1_1,"ax",@progbits +t1_1: +adrp x2, :got:g2 +mov x3, #2 +b t2_1 + +.section .text.t2_1,"ax",@progbits +t2_1: +ldr x2, [x2, :got_lo12:g2] +b callee + +.section .text._start,"ax",@progbits +.globl _start +_start: + +f 0 1 +f 1 1 +f 2 1 +f 3 1 +f 4 diff --git a/lld/test/ELF/icf-preemptible.s b/lld/test/ELF/icf-preemptible.s index 4bd1eca438b19..9352493600695 100644 --- a/lld/test/ELF/icf-preemptible.s +++ b/lld/test/ELF/icf-preemptible.s @@ -17,6 +17,12 @@ # EXE-NEXT: selected section {{.*}}:(.text.h1) # EXE-NEXT: removing identical section {{.*}}:(.text.h2) # EXE-NEXT: removing identical section {{.*}}:(.text.h3) +# EXE-NEXT: redirecting 'f2' in symtab to 'f1' +# EXE-NEXT: redirecting 'g2' in symtab to 'g1' +# EXE-NEXT: redirecting 'g3' in symtab to 'g1' +# EXE-NEXT: redirecting 'f2' to 'f1' +# EXE-NEXT: redirecting 'g2' to 'g1' +# EXE-NEXT: redirecting 'g3' to 'g1' # EXE-NOT: {{.}} ## Definitions are preemptible in a DSO. Only leaf functions can be folded.