Index: ELF/InputFiles.h =================================================================== --- ELF/InputFiles.h +++ ELF/InputFiles.h @@ -106,6 +106,7 @@ StringRef getStringTable() const { return StringTable; } uint32_t getSectionIndex(const Elf_Sym &Sym) const; + uint32_t getSectionIndex(unsigned SymIndex) const; Elf_Sym_Range getElfSymbols(bool OnlyGlobals); @@ -143,6 +144,8 @@ ArrayRef *> getSections() const { return Sections; } InputSectionBase *getSection(const Elf_Sym &Sym) const; + std::vector *> + getComdatSections(llvm::DenseSet &Signatures) const; SymbolBody &getSymbolBody(uint32_t SymbolIndex) const { return *SymbolBodies[SymbolIndex]; @@ -153,6 +156,13 @@ return getSymbolBody(SymIndex); } + template + uint32_t getRelocSectionIndex(const RelT &Rel) const { + return this->getSectionIndex(Rel.getSymbol(Config->Mips64EL)); + } + + StringRef getComdatGroupSignature(unsigned SecIndex); + const Elf_Shdr *getSymbolTable() const { return this->Symtab; }; // Get MIPS GP0 value defined by this file. This value represents the gp value @@ -190,6 +200,9 @@ // MIPS .MIPS.abiflags section defined by this file. std::unique_ptr> MipsAbiFlags; + // Section index to comdat group signature map; + llvm::DenseMap SectionGroupSig; + llvm::SpecificBumpPtrAllocator> IAlloc; llvm::SpecificBumpPtrAllocator> MAlloc; llvm::SpecificBumpPtrAllocator> EHAlloc; Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -99,6 +99,16 @@ return I; } +template +uint32_t ELFFileBase::getSectionIndex(unsigned SymIndex) const { + Elf_Sym_Range Syms = ELFObj.symbols(Symtab); + uint32_t NumSymbols = std::distance(Syms.begin(), Syms.end()); + if (SymIndex >= NumSymbols) + return -1; + + return getSectionIndex(Syms[SymIndex]); +} + template void ELFFileBase::initStringTable() { if (!Symtab) return; @@ -218,16 +228,20 @@ if (Sections[I] == &InputSection::Discarded) continue; + bool NewGroup; + StringRef GroupSig; switch (Sec.sh_type) { case SHT_GROUP: Sections[I] = &InputSection::Discarded; - if (ComdatGroups.insert(getShtGroupSignature(Sec)).second) - continue; + GroupSig = getShtGroupSignature(Sec); + NewGroup = ComdatGroups.insert(GroupSig).second; for (uint32_t SecIndex : getShtGroupEntries(Sec)) { if (SecIndex >= Size) fatal(getFilename(this) + ": invalid section index in group: " + Twine(SecIndex)); - Sections[SecIndex] = &InputSection::Discarded; + if (!NewGroup) + Sections[SecIndex] = &InputSection::Discarded; + SectionGroupSig[SecIndex] = GroupSig; } break; case SHT_SYMTAB: @@ -368,6 +382,23 @@ } template +std::vector *> elf::ObjectFile::getComdatSections( + DenseSet &Signatures) const { + std::vector *> Ret; + for (auto &P : SectionGroupSig) + if (Signatures.find(P.second) != Signatures.end() && + Sections[P.first] != &InputSectionBase::Discarded) + Ret.push_back(Sections[P.first]); + + return Ret; +} + +template +StringRef elf::ObjectFile::getComdatGroupSignature(unsigned SecIndex) { + return SectionGroupSig[SecIndex]; +} + +template SymbolBody *elf::ObjectFile::createSymbolBody(const Elf_Sym *Sym) { int Binding = Sym->getBinding(); InputSectionBase *Sec = getSection(*Sym); Index: ELF/MarkLive.cpp =================================================================== --- ELF/MarkLive.cpp +++ ELF/MarkLive.cpp @@ -43,9 +43,12 @@ namespace { // A resolved relocation. The Sec and Offset fields are set if the relocation -// was resolved to an offset within a section. +// was resolved to an offset within a section. If relocation was resolved to +// a member of comdat group then Sec may be null and Offset is an index of +// section in File. template struct ResolvedReloc { + ObjectFile *File; InputSectionBase *Sec; typename ELFT::uint Offset; }; @@ -70,11 +73,13 @@ SymbolBody &B = Sec.getFile()->getRelocTargetSym(Rel); auto *D = dyn_cast>(&B); if (!D || !D->Section) - return {nullptr, 0}; + return {nullptr, nullptr, 0}; + if (D->Section == &InputSectionBase::Discarded) + return {Sec.getFile(), nullptr, Sec.getFile()->getRelocSectionIndex(Rel)}; typename ELFT::uint Offset = D->Value; if (D->isSection()) Offset += getAddend(Sec, Rel); - return {D->Section->Repl, Offset}; + return {D->Section->Repl->getFile(), D->Section->Repl, Offset}; } // Calls Fn for each section that Sec refers to via relocations. @@ -136,7 +141,7 @@ continue; if (R.Sec->getSectionHdr()->sh_flags & SHF_EXECINSTR) continue; - Enqueue({R.Sec, 0}); + Enqueue({R.Sec->getFile(), R.Sec, 0}); } } } @@ -187,10 +192,18 @@ // sections to set their "Live" bits. template void elf::markLive() { SmallVector *, 256> Q; + DenseSet LiveComdatGroups; auto Enqueue = [&](ResolvedReloc R) { - if (!R.Sec) + if (!R.Sec) { + if (!R.File) + return; + + StringRef GroupSig = R.File->getComdatGroupSignature(R.Offset); + if (!GroupSig.empty()) + LiveComdatGroups.insert(GroupSig); return; + } // Usually, a whole section is marked as live or dead, but in mergeable // (splittable) sections, each piece of data has independent liveness bit. @@ -207,7 +220,7 @@ auto MarkSymbol = [&](const SymbolBody *Sym) { if (auto *D = dyn_cast_or_null>(Sym)) - Enqueue({D->Section, D->Value}); + Enqueue({D->Section->getFile(), D->Section, D->Value}); }; // Add GC root symbols. @@ -235,12 +248,23 @@ if (auto *EH = dyn_cast_or_null>(Sec)) scanEhFrameSection(*EH, Enqueue); if (isReserved(Sec) || Script::X->shouldKeep(Sec)) - Enqueue({Sec, 0}); + Enqueue({Sec->getFile(), Sec, 0}); } } } - // Mark all reachable sections. + // Pass 1. Mark all reachable sections, except comdat groups. + while (!Q.empty()) + forEachSuccessor(*Q.pop_back_val(), Enqueue); + + if (LiveComdatGroups.empty()) + return; + + // Pass 2. Identify and mark reachable comdat groups. + for (ObjectFile *F : Symtab::X->getObjectFiles()) + for (InputSectionBase *Sec : F->getComdatSections(LiveComdatGroups)) + Enqueue({Sec->getFile(), Sec, 0}); + while (!Q.empty()) forEachSuccessor(*Q.pop_back_val(), Enqueue); } Index: test/ELF/Inputs/comdat-gc.s =================================================================== --- test/ELF/Inputs/comdat-gc.s +++ test/ELF/Inputs/comdat-gc.s @@ -0,0 +1,10 @@ +.weak _Z3fooIiEvv +.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat +_Z3fooIiEvv: + ret + +.global _start +.section .zzz, "ax" +_start: + leaq .text._Z3fooIiEvv(%rip), %rax + Index: test/ELF/comdat-gc.s =================================================================== --- test/ELF/comdat-gc.s +++ test/ELF/comdat-gc.s @@ -0,0 +1,13 @@ +// Check that the linker doesn't crash and comdat group is not gc'ed +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t.o +// RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/comdat-gc.s -o %t2.o +// RUN: ld.lld --gc-sections %t.o %t2.o -o %t +// RUN: llvm-readobj -t %t | FileCheck %s +// CHECK: _Z3fooIiEvv + +.weak _Z3fooIiEvv + +.section .text._Z3fooIiEvv,"axG",@progbits,_Z3fooIiEvv,comdat +_Z3fooIiEvv: + ret +