Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -163,14 +163,6 @@ if (Config->Optimize == 0) return false; - // We don't merge if linker script has SECTIONS command. When script - // do layout it can merge several sections with different attributes - // into single output sections. We currently do not support adding - // mergeable input sections to regular output ones as well as adding - // regular input sections to mergeable output. - if (ScriptConfig->HasContents) - return false; - // A mergeable section with size 0 is useless because they don't have // any data to merge. A mergeable string section with size 0 can be // argued as invalid because it doesn't end with a null character. Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -136,6 +136,8 @@ static bool classof(const InputSectionBase *S); void splitIntoPieces(); + void writeTo(uint8_t *Buf); + // Mark the piece at a given offset live. Used by GC. void markLiveAt(uintX_t Offset) { LiveOffsets.insert(Offset); } @@ -143,7 +145,7 @@ // in the output section. uintX_t getOffset(uintX_t Offset) const; - void finalizePieces(); + uintX_t finalizePieces(uintX_t Offset = (uintX_t )-1); // Splittable sections are handled as a sequence of data // rather than a single large blob of data. @@ -157,7 +159,6 @@ std::vector splitStrings(ArrayRef A, size_t Size); std::vector splitNonStrings(ArrayRef A, size_t Size); - llvm::DenseMap OffsetMap; llvm::DenseSet LiveOffsets; }; Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -620,20 +620,40 @@ // Create a map from input offsets to output offsets for all section pieces. // It is called after finalize(). -template void MergeInputSection::finalizePieces() { +template +typename ELFT::uint MergeInputSection::finalizePieces(uintX_t Offset) { OffsetMap.grow(this->Pieces.size()); + uintX_t Size = 0; for (SectionPiece &Piece : this->Pieces) { if (!Piece.Live) continue; + + // Offsets of tail-merged strings are computed lazily. + // The other case is when using linkerscript and have combination + // of mergeable and non-mergeable input sections in regular output section. if (Piece.OutputOff == size_t(-1)) { - // Offsets of tail-merged strings are computed lazily. - auto *OutSec = static_cast *>(this->OutSec); ArrayRef D = Piece.data(); StringRef S((const char *)D.data(), D.size()); - Piece.OutputOff = OutSec->getOffset(S); + uintX_t Off; + if (Offset == (uintX_t)-1) + Off = static_cast *>(this->OutSec)->getOffset(S); + else + Off = Offset + Size; + Piece.OutputOff = Off; + Size += Piece.size(); } OffsetMap[Piece.InputOff] = Piece.OutputOff; } + return Size; +} + +template void MergeInputSection::writeTo(uint8_t *Buf) { + for (SectionPiece &Piece : Pieces) { + if (Piece.OutputOff == (uintX_t)-1) + continue; + ArrayRef Data = Piece.data(); + memcpy(Buf + Piece.OutputOff, Data.data(), Data.size()); + } } template Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -140,7 +140,7 @@ public: LinkerScript(); ~LinkerScript(); - void createSections(OutputSectionFactory &Factory); + void createSections(); std::vector> createPhdrs(); bool ignoreInterpSection(); @@ -165,6 +165,10 @@ std::vector *> createInputSectionList(OutputSectionCommand &Cmd); + std::pair *, bool> + createOutputSection(InputSectionBase *C, StringRef Name, + bool AllowMergeable); + // "ScriptConfig" is a bit too long, so define a short name for it. ScriptConfiguration &Opt = *ScriptConfig; @@ -174,6 +178,8 @@ llvm::SpecificBumpPtrAllocator> LAlloc; + std::vector>> OwningSections; + uintX_t Dot; }; Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -259,7 +259,51 @@ } template -void LinkerScript::createSections(OutputSectionFactory &Factory) { +std::pair *, bool> +LinkerScript::createOutputSection(InputSectionBase *C, + StringRef Name, bool AllowMergeable) { + typedef typename ELFT::Shdr Elf_Shdr; + + OutputSectionBase *Sec = nullptr; + for (std::unique_ptr> &S : OwningSections) { + if (S->getName() == Name) + return {S.get(), false}; + } + + const Elf_Shdr *H = C->getSectionHdr(); + uintX_t Flags = H->sh_flags & ~SHF_GROUP & ~SHF_COMPRESSED; + uintX_t Alignment = 0; + unsigned Kind = C->SectionKind; + if (Kind == InputSectionBase::Merge) { + if (!AllowMergeable) + Kind = InputSectionBase::Regular; + Alignment = std::max(H->sh_addralign, H->sh_entsize); + } + uint32_t Type = H->sh_type; + + bool IsNew; + std::tie(Sec, IsNew) = + allocSection(Kind, Name, Flags, Alignment, Type); + if (IsNew) + OwningSections.emplace_back(Sec); + return {Sec, IsNew}; +} + +template +static bool hasIncompatibles(InputSectionBase *Head, + std::vector *> &Vec) { + uint64_t Flags = Head->getSectionHdr()->sh_flags; + for (InputSectionBase *S : Vec) { + if (isa>(S)) + continue; + if ((Flags & SHF_MERGE) != (S->getSectionHdr()->sh_flags & SHF_MERGE)) + return true; + } + return false; +} + +template +void LinkerScript::createSections() { for (const std::unique_ptr &Base1 : Opt.Commands) { if (auto *Cmd = dyn_cast(Base1.get())) { if (shouldDefine(Cmd)) @@ -279,8 +323,12 @@ continue; OutputSectionBase *OutSec; + bool AllowMergeable = !hasIncompatibles(Head, V); + if (!AllowMergeable) + warning("SHF_MERGE flags will be ignored for " + Cmd->Name); bool IsNew; - std::tie(OutSec, IsNew) = Factory.create(Head, Cmd->Name); + std::tie(OutSec, IsNew) = + createOutputSection(Head, Cmd->Name, AllowMergeable); if (IsNew) OutputSections->push_back(OutSec); @@ -302,7 +350,8 @@ continue; OutputSectionBase *OutSec; bool IsNew; - std::tie(OutSec, IsNew) = Factory.create(S, getOutputSectionName(S)); + std::tie(OutSec, IsNew) = + createOutputSection(S, getOutputSectionName(S), true); if (IsNew) OutputSections->push_back(OutSec); OutSec->addSection(S); @@ -320,8 +369,8 @@ typedef typename ELFT::uint uintX_t; uintX_t Off = 0; - for (InputSection *I : OutSec->Sections) { - if (auto *L = dyn_cast>(I)) { + for (InputSectionBase *B : OutSec->Sections) { + if (auto *L = dyn_cast>(B)) { uintX_t Value = L->Cmd->Expression(Sec->getVA() + Off) - Sec->getVA(); if (L->Cmd->Name == ".") { Off = Value; @@ -332,10 +381,12 @@ Sym->Section = OutSec; Sym->Value = Value; } - } else { + } else if (auto *I = dyn_cast>(B)) { Off = alignTo(Off, I->Alignment); I->OutSecOff = Off; Off += I->getSize(); + } else { + Off += dyn_cast>(B)->finalizePieces(Off); } // Update section size inside for-loop, so that SIZEOF // works correctly in the case below: Index: ELF/OutputSections.h =================================================================== --- ELF/OutputSections.h +++ ELF/OutputSections.h @@ -34,11 +34,17 @@ template class MergeInputSection; template class MipsReginfoInputSection; template class OutputSection; +template class OutputSectionBase; template class ObjectFile; template class SharedFile; template class SharedSymbol; template class DefinedRegular; +template +std::pair *, bool> +allocSection(unsigned Kind, StringRef Name, typename ELFT::uint Flags, + typename ELFT::uint Alignment, uint32_t Type); + // This represents a section in an output file. // Different sub classes represent different types of sections. Some contain // input sections, others are created by the linker. @@ -403,7 +409,7 @@ void assignOffsets() override; typename Base::Kind getKind() const override { return Base::Regular; } static bool classof(const Base *B) { return B->getKind() == Base::Regular; } - std::vector *> Sections; + std::vector *> Sections; }; template Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -835,14 +835,14 @@ this->Header.sh_link = Out::SymTab->SectionIndex; // sh_info for SHT_REL[A] sections should contain the section header index of // the section to which the relocation applies. - InputSectionBase *S = Sections[0]->getRelocatedSection(); + InputSectionBase *S = + dyn_cast>(Sections[0])->getRelocatedSection(); this->Header.sh_info = S->OutSec->SectionIndex; } template -void OutputSection::addSection(InputSectionBase *C) { - assert(C->Live); - auto *S = cast>(C); +void OutputSection::addSection(InputSectionBase *S) { + assert(S->Live); Sections.push_back(S); S->OutSec = this; this->updateAlignment(S->Alignment); @@ -865,7 +865,8 @@ // and scan relocations to setup sections' offsets. template void OutputSection::assignOffsets() { uintX_t Off = this->Header.sh_size; - for (InputSection *S : Sections) { + for (InputSectionBase *C : Sections) { + InputSection *S = dyn_cast>(C); Off = alignTo(Off, S->Alignment); S->OutSecOff = Off; Off += S->getSize(); @@ -885,8 +886,10 @@ auto Comp = [](const Pair &A, const Pair &B) { return A.first < B.first; }; std::vector V; - for (InputSection *S : Sections) + for (InputSectionBase *B : Sections) { + auto S = dyn_cast>(B); V.push_back({getPriority(S->getSectionName()), S}); + } std::stable_sort(V.begin(), V.end(), Comp); Sections.clear(); for (Pair &P : V) @@ -925,8 +928,10 @@ // are too many real-world use cases of .ctors, so we had no choice to // support that with this rather ad-hoc semantics. template -static bool compCtors(const InputSection *A, - const InputSection *B) { +static bool compCtors(const InputSectionBase *L, + const InputSectionBase *R) { + auto A = dyn_cast>(L); + auto B = dyn_cast>(R); bool BeginA = isCrtbegin(A->getFile()->getName()); bool BeginB = isCrtbegin(B->getFile()->getName()); if (BeginA != BeginB) @@ -961,15 +966,22 @@ } template void OutputSection::writeTo(uint8_t *Buf) { + auto WriteOne = [&](InputSectionBase *B) { + if (auto C = dyn_cast>(B)) + C->writeTo(Buf); + else + dyn_cast>(B)->writeTo(Buf); + }; + ArrayRef Filler = Script::X->getFiller(this->Name); if (!Filler.empty()) fill(Buf, this->getSize(), Filler); if (Config->Threads) { parallel_for_each(Sections.begin(), Sections.end(), - [=](InputSection *C) { C->writeTo(Buf); }); + [=](InputSectionBase *B) { WriteOne(B); }); } else { - for (InputSection *C : Sections) - C->writeTo(Buf); + for (InputSectionBase *B : Sections) + WriteOne(B); } } @@ -1784,22 +1796,17 @@ template std::pair *, bool> -OutputSectionFactory::create(InputSectionBase *C, - StringRef OutsecName) { - SectionKey Key = createKey(C, OutsecName); - OutputSectionBase *&Sec = Map[Key]; - if (Sec) - return {Sec, false}; - - switch (C->SectionKind) { +elf::allocSection(unsigned Kind, StringRef Name, typename ELFT::uint Flags, + typename ELFT::uint Alignment, uint32_t Type) { + OutputSectionBase *Sec; + switch (Kind) { case InputSectionBase::Regular: - Sec = new OutputSection(Key.Name, Key.Type, Key.Flags); + Sec = new OutputSection(Name, Type, Flags); break; case InputSectionBase::EHFrame: return {Out::EhFrame, false}; case InputSectionBase::Merge: - Sec = new MergeOutputSection(Key.Name, Key.Type, Key.Flags, - Key.Alignment); + Sec = new MergeOutputSection(Name, Type, Flags, Alignment); break; case InputSectionBase::MipsReginfo: Sec = new MipsReginfoOutputSection(); @@ -1813,11 +1820,27 @@ case InputSectionBase::Layout: llvm_unreachable("Invalid section type"); } - Out::Pool.emplace_back(Sec); return {Sec, true}; } template +std::pair *, bool> +OutputSectionFactory::create(InputSectionBase *C, + StringRef OutsecName) { + SectionKey Key = createKey(C, OutsecName); + OutputSectionBase *&Sec = Map[Key]; + if (Sec) + return {Sec, false}; + + bool Allocated; + std::tie(Sec, Allocated) = allocSection( + C->SectionKind, Key.Name, Key.Flags, Key.Alignment, Key.Type); + if (Allocated) + Out::Pool.emplace_back(Sec); + return {Sec, Allocated}; +} + +template SectionKey OutputSectionFactory::createKey(InputSectionBase *C, StringRef OutsecName) { @@ -2005,3 +2028,16 @@ template class OutputSectionFactory; } } + +template std::pair *, bool> +elf::allocSection(unsigned Kind, StringRef Name, ELF32LE::uint Flags, + ELF32LE::uint Alignment, uint32_t Type); +template std::pair *, bool> +elf::allocSection(unsigned Kind, StringRef Name, ELF32BE::uint Flags, + ELF32BE::uint Alignment, uint32_t Type); +template std::pair *, bool> +elf::allocSection(unsigned Kind, StringRef Name, ELF64LE::uint Flags, + ELF64LE::uint Alignment, uint32_t Type); +template std::pair *, bool> +elf::allocSection(unsigned Kind, StringRef Name, ELF64BE::uint Flags, + ELF64BE::uint Alignment, uint32_t Type); Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -248,7 +248,7 @@ Script::X->OutputSections = &OutputSections; if (ScriptConfig->HasContents) - Script::X->createSections(Factory); + Script::X->createSections(); else createSections(); Index: test/ELF/linkerscript/linkerscript-mix-sections-gc.s =================================================================== --- test/ELF/linkerscript/linkerscript-mix-sections-gc.s +++ test/ELF/linkerscript/linkerscript-mix-sections-gc.s @@ -0,0 +1,35 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +# RUN: echo "SECTIONS { .foo : { *(.foo.*) } }" > %t.script +# RUN: ld.lld --gc-sections -o %t1 --script %t.script %t +# RUN: llvm-objdump -s %t1 | FileCheck %s +# CHECK: Contents of section .foo: +# CHECK-NEXT: {{.*}} fzzzzz. + +.text +.globl _start +_start: + movl $s0, %eax + movl $s2, %eax + +.hidden s0 +.type s0,@object +.section .foo.0,"aM",@progbits,1 +.globl s0 +s0: +.asciz "foo" + +.hidden s1 +.type s1,@object +.section .foo.1,"aMS",@progbits,1 +.globl s1 +s1: +.asciz "boo" + +.hidden s2 +.type s2,@object +.section .foo.2,"a" +.globl s2 +s2: +.asciz "zzzzz" Index: test/ELF/linkerscript/linkerscript-mix-sections.s =================================================================== --- test/ELF/linkerscript/linkerscript-mix-sections.s +++ test/ELF/linkerscript/linkerscript-mix-sections.s @@ -0,0 +1,34 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t + +# RUN: echo "SECTIONS { .foo : { *(.foo.*) } }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-objdump -s %t1 | FileCheck %s +# CHECK: Contents of section .foo: +# CHECK-NEXT: {{.*}} foo.boo.zzzzz. + +.text +.globl _start +_start: + nop + +.hidden s0 +.type s0,@object +.section .foo.0,"aM",@progbits,1 +.globl s0 +s0: +.asciz "foo" + +.hidden s1 +.type s1,@object +.section .foo.1,"aMS",@progbits,1 +.globl s1 +s1: +.asciz "boo" + +.hidden s2 +.type s2,@object +.section .foo.2,"a" +.globl s2 +s2: +.asciz "zzzzz"