Index: lld/ELF/LinkerScript.h =================================================================== --- lld/ELF/LinkerScript.h +++ lld/ELF/LinkerScript.h @@ -257,6 +257,7 @@ void allocateHeaders(std::vector &Phdrs); void addSymbol(SymbolAssignment *Cmd); void processCommands(OutputSectionFactory &Factory); + std::vector createCommonSections(); // Parsed linker script configurations are set to this struct. ScriptConfiguration Opt; Index: lld/ELF/LinkerScript.cpp =================================================================== --- lld/ELF/LinkerScript.cpp +++ lld/ELF/LinkerScript.cpp @@ -174,17 +174,17 @@ return C->Kind == BytesDataKind; } -static std::string filename(InputSectionBase *S) { - if (!S->File) +static std::string filename(InputFile *File) { + if (!File) return ""; - if (S->File->ArchiveName.empty()) - return S->File->getName(); - return (S->File->ArchiveName + "(" + S->File->getName() + ")").str(); + if (File->ArchiveName.empty()) + return File->getName(); + return (File->ArchiveName + "(" + File->getName() + ")").str(); } bool LinkerScript::shouldKeep(InputSectionBase *S) { for (InputSectionDescription *ID : Opt.KeptSections) { - std::string Filename = filename(S); + std::string Filename = filename(S->File); if (ID->FilePat.match(Filename)) for (SectionPattern &P : ID->SectionPatterns) if (P.SectionPat.match(S->Name)) @@ -284,7 +284,7 @@ if (Sec->Type == SHT_REL || Sec->Type == SHT_RELA) continue; - std::string Filename = filename(Sec); + std::string Filename = filename(Sec->File); if (!Cmd->FilePat.match(Filename) || Pat.ExcludedFilePat.match(Filename) || !Pat.SectionPat.match(Sec->Name)) @@ -328,8 +328,9 @@ void LinkerScript::discard(ArrayRef V) { for (InputSectionBase *S : V) { S->Live = false; - if (S == InX::ShStrTab || S == InX::Common || S == InX::Dynamic || - S == InX::DynSymTab || S == InX::DynStrTab) + if (std::find(InX::Commons.begin(), InX::Commons.end(), S) != InX::Commons.end() || + S == InX::ShStrTab || S == InX::Dynamic || S == InX::DynSymTab || + S == InX::DynStrTab) error("discarding " + S->Name + " section is not allowed"); discard(S->DependentSections); } @@ -872,7 +873,7 @@ if (auto *D = dyn_cast(B)) return {D->Section, D->Value, Loc}; if (auto *C = dyn_cast(B)) - return {InX::Common, C->Offset, Loc}; + return {C->Section, C->Offset, Loc}; } error(Loc + ": symbol not found: " + S); return 0; @@ -909,3 +910,97 @@ error(Loc + ": section header '" + PhdrName + "' is not listed in PHDRS"); return NoPhdr; } + +// Create a list of common symbols that match the given file patters and do +// not match the excluded files pattern. +static std::vector +getPatternCommonSymbols(const StringMatcher *FilePat, + const StringMatcher *ExcludedFilePat) { + std::vector V; + for (Symbol *S : Symtab->getSymbols()) { + auto *DC = dyn_cast(S->body()); + if (!DC) + continue; + + std::string Filename = filename(DC->getFile()); + // Look at the symbols that have not been allocated to a section yet. + // If they match the file pattern and are not excluded, add them to the + // list. + // If no file pattern is specified, just add all remaining unallocated + // symbols. + if (!DC->Section && + ((FilePat && ExcludedFilePat && FilePat->match(Filename) && + !ExcludedFilePat->match(Filename)) || + !FilePat)) + V.push_back(DC); + } + return V; +} + +// Create common section for the givel list of symbols and allocate space for +// them. +static InputSection +*createPatternCommonSection(const StringMatcher *FilePat, + const StringMatcher *ExcludedFilePat) { + if (!Config->DefineCommon) + return nullptr; + + std::vector Syms = + getPatternCommonSymbols(FilePat, ExcludedFilePat); + if (Syms.empty()) + return nullptr; + + // Sort the common symbols by alignment as a heuristic to pack them better. + std::stable_sort(Syms.begin(), Syms.end(), + [](const DefinedCommon *A, const DefinedCommon *B) { + return A->Alignment > B->Alignment; + }); + + // Allocate space for common symbols. + BssSection *Sec = make("COMMON"); + for (DefinedCommon *Sym : Syms) + if (Sym->Live) { + Sym->Offset = Sec->reserveSpace(Sym->Size, Sym->Alignment); + Sym->Section = Sec; + } + + // Any file mathcing the pattern will do. It will be used in + // computeInputSection() to place this section. + if (FilePat) + Sec->File = Syms[0]->getFile(); + return Sec; +} + +std::vector LinkerScript::createCommonSections() +{ + std::vector SV; + for (size_t I = 0; I < Opt.Commands.size(); ++I) { + auto *Sec = dyn_cast(Opt.Commands[I]); + if (!Sec) + continue; + + for (BaseCommand *Base : Sec->Commands) { + auto *Cmd = dyn_cast(Base); + if (!Cmd) + continue; + + for (const SectionPattern &Pat : Cmd->SectionPatterns) { + if (!Pat.SectionPat.match("COMMON")) + continue; + + // Find all commons matching a file pattern, create an input + // section for it and add it to the return list. + InputSection *ComSec = + createPatternCommonSection(&Cmd->FilePat, &Pat.ExcludedFilePat); + if (ComSec) + SV.push_back(ComSec); + } + } + } + + // Any unallocated symbols go into the default COMMON section. + InputSection *ComSec = createPatternCommonSection(nullptr, nullptr); + if (ComSec) + SV.push_back(ComSec); + return SV; +} Index: lld/ELF/MapFile.cpp =================================================================== --- lld/ELF/MapFile.cpp +++ lld/ELF/MapFile.cpp @@ -57,7 +57,7 @@ DR->Section->Live) V.push_back(DR); } else if (auto *DC = dyn_cast(B)) { - if (InX::Common) + if (DC->Section) V.push_back(DC); } } @@ -71,8 +71,8 @@ for (Defined *S : Syms) { if (auto *DR = dyn_cast(S)) Ret[DR->Section].push_back(S); - else - Ret[InX::Common].push_back(S); + else if (auto *DC = dyn_cast(S)) + Ret[DC->Section].push_back(S); } // Sort symbols by address. We want to print out symbols in the Index: lld/ELF/Symbols.h =================================================================== --- lld/ELF/Symbols.h +++ lld/ELF/Symbols.h @@ -27,6 +27,7 @@ class ArchiveFile; class BitcodeFile; +class BssSection; class InputFile; class LazyObjFile; template class ObjFile; @@ -173,6 +174,7 @@ // Computed by the writer. uint64_t Offset; uint64_t Size; + BssSection *Section; }; // Regular defined symbols read from object file symbol tables. Index: lld/ELF/Symbols.cpp =================================================================== --- lld/ELF/Symbols.cpp +++ lld/ELF/Symbols.cpp @@ -99,11 +99,13 @@ } return VA; } - case SymbolBody::DefinedCommonKind: + case SymbolBody::DefinedCommonKind: { if (!Config->DefineCommon) return 0; - return InX::Common->getParent()->Addr + InX::Common->OutSecOff + - cast(Body).Offset; + auto DC = cast(Body); + return DC.Section->getParent()->Addr + DC.Section->OutSecOff + + DC.Offset; + } case SymbolBody::SharedKind: { auto &SS = cast(Body); if (SS.CopyRelSec) @@ -202,9 +204,9 @@ return nullptr; } - if (isa(this)) { + if (auto *S = dyn_cast(this)) { if (Config->DefineCommon) - return InX::Common->getParent(); + return S->Section->getParent(); return nullptr; } @@ -278,7 +280,7 @@ uint8_t StOther, uint8_t Type) : Defined(SymbolBody::DefinedCommonKind, Name, /*IsLocal=*/false, StOther, Type), - Live(!Config->GcSections), Alignment(Alignment), Size(Size) {} + Live(!Config->GcSections), Alignment(Alignment), Size(Size), Section(nullptr) {} // If a shared symbol is referred via a copy relocation, its alignment // becomes part of the ABI. This function returns a symbol alignment. Index: lld/ELF/SyntheticSections.h =================================================================== --- lld/ELF/SyntheticSections.h +++ lld/ELF/SyntheticSections.h @@ -740,7 +740,6 @@ size_t Size = 0; }; -template InputSection *createCommonSection(); InputSection *createInterpSection(); template MergeInputSection *createCommentSection(); void decompressAndMergeSections(); @@ -754,7 +753,7 @@ static BssSection *Bss; static BssSection *BssRelRo; static BuildIdSection *BuildId; - static InputSection *Common; + static std::vector Commons; static SyntheticSection *Dynamic; static StringTableSection *DynStrTab; static SymbolTableBaseSection *DynSymTab; Index: lld/ELF/SyntheticSections.cpp =================================================================== --- lld/ELF/SyntheticSections.cpp +++ lld/ELF/SyntheticSections.cpp @@ -54,37 +54,6 @@ return 0; } -template static std::vector getCommonSymbols() { - std::vector V; - for (Symbol *S : Symtab->getSymbols()) - if (auto *B = dyn_cast(S->body())) - V.push_back(B); - return V; -} - -// Find all common symbols and allocate space for them. -template InputSection *elf::createCommonSection() { - if (!Config->DefineCommon) - return nullptr; - - // Sort the common symbols by alignment as an heuristic to pack them better. - std::vector Syms = getCommonSymbols(); - if (Syms.empty()) - return nullptr; - - std::stable_sort(Syms.begin(), Syms.end(), - [](const DefinedCommon *A, const DefinedCommon *B) { - return A->Alignment > B->Alignment; - }); - - // Allocate space for common symbols. - BssSection *Sec = make("COMMON"); - for (DefinedCommon *Sym : Syms) - if (Sym->Live) - Sym->Offset = Sec->reserveSpace(Sym->Size, Sym->Alignment); - return Sec; -} - // Returns an LLD version string. static ArrayRef getVersion() { // Check LLD_VERSION first for ease of testing. @@ -2316,7 +2285,7 @@ BssSection *InX::Bss; BssSection *InX::BssRelRo; BuildIdSection *InX::BuildId; -InputSection *InX::Common; +std::vector InX::Commons; SyntheticSection *InX::Dynamic; StringTableSection *InX::DynStrTab; SymbolTableBaseSection *InX::DynSymTab; @@ -2344,11 +2313,6 @@ template void PltSection::addEntry(SymbolBody &Sym); template void PltSection::addEntry(SymbolBody &Sym); -template InputSection *elf::createCommonSection(); -template InputSection *elf::createCommonSection(); -template InputSection *elf::createCommonSection(); -template InputSection *elf::createCommonSection(); - template MergeInputSection *elf::createCommentSection(); template MergeInputSection *elf::createCommentSection(); template MergeInputSection *elf::createCommentSection(); Index: lld/ELF/Writer.cpp =================================================================== --- lld/ELF/Writer.cpp +++ lld/ELF/Writer.cpp @@ -302,9 +302,9 @@ Add(InX::BuildId); } - InX::Common = createCommonSection(); - if (InX::Common) - Add(InX::Common); + InX::Commons = Script->createCommonSections(); + for (auto S : InX::Commons) + Add(S); InX::Bss = make(".bss"); Add(InX::Bss); Index: lld/test/ELF/linkerscript/Inputs/common-filespec1.s =================================================================== --- /dev/null +++ lld/test/ELF/linkerscript/Inputs/common-filespec1.s @@ -0,0 +1,2 @@ +.comm common_uniq_1,8,8 +.comm common_multiple,16,8 Index: lld/test/ELF/linkerscript/Inputs/common-filespec2.s =================================================================== --- /dev/null +++ lld/test/ELF/linkerscript/Inputs/common-filespec2.s @@ -0,0 +1,2 @@ +.comm common_uniq_2,16,16 +.comm common_multiple,32,8 Index: lld/test/ELF/linkerscript/common-exclude.s =================================================================== --- /dev/null +++ lld/test/ELF/linkerscript/common-exclude.s @@ -0,0 +1,86 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile0.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec1.s -o %tfile1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec2.s -o %tfile2.o +# RUN: echo "SECTIONS { .common.incl : { *(EXCLUDE_FILE (*file2.o) COMMON) } .common.excl : { *(COMMON) } }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %tfile0.o %tfile1.o %tfile2.o +# RUN: llvm-readobj -s -t %t1 | FileCheck %s + +# Commons from file0 and file1 are not excluded, so they must be in .common.incl +# Commons from file2 are excluded from the first rule and should be caught by +# the second in .common.excl +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: .common.incl +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_WRITE +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x8 +# CHECK-NEXT: Offset: 0x +# CHECK-NEXT: Size: 12 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 8 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: .common.excl +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_WRITE +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x20 +# CHECK-NEXT: Offset: 0x +# CHECK-NEXT: Size: 48 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 16 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK: Symbol { +# CHECK: Name: common_multiple +# CHECK-NEXT: Value: 0x30 +# CHECK-NEXT: Size: 32 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common.excl +# CHECK-NEXT: } +# CHECK: Symbol { +# CHECK: Name: common_uniq_0 +# CHECK-NEXT: Value: 0x10 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common.incl +# CHECK-NEXT: } +# CHECK: Symbol { +# CHECK: Name: common_uniq_1 +# CHECK-NEXT: Value: 0x8 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common.incl +# CHECK-NEXT: } +# CHECK: Symbol { +# CHECK: Name: common_uniq_2 +# CHECK-NEXT: Value: 0x20 +# CHECK-NEXT: Size: 16 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common.excl +# CHECK-NEXT: } + +.globl _start +_start: + jmp _start + +.comm common_uniq_0,4,4 +.comm common_multiple,8,8 Index: lld/test/ELF/linkerscript/common-filespec.s =================================================================== --- /dev/null +++ lld/test/ELF/linkerscript/common-filespec.s @@ -0,0 +1,105 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %tfile0.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec1.s -o %tfile1.o +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %p/Inputs/common-filespec2.s -o %tfile2.o +# RUN: echo "SECTIONS { .common_0 : { *file0.o(COMMON) } .common_1 : { *file1.o(COMMON) } .common_2 : { *file2.o(COMMON) } }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %tfile0.o %tfile1.o %tfile2.o +# RUN: llvm-readobj -s -t %t1 | FileCheck %s + +# Make sure all 3 sections are allocated and they have sizes and alignments +# corresponding to the commons assigned to them +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: .common_0 +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_WRITE +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x4 +# CHECK-NEXT: Offset: 0x +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 4 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: .common_1 +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_WRITE +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x8 +# CHECK-NEXT: Offset: 0x +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 8 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } +# CHECK: Section { +# CHECK: Index: +# CHECK: Name: .common_2 +# CHECK-NEXT: Type: SHT_NOBITS +# CHECK-NEXT: Flags [ +# CHECK-NEXT: SHF_ALLOC +# CHECK-NEXT: SHF_WRITE +# CHECK-NEXT: ] +# CHECK-NEXT: Address: 0x10 +# CHECK-NEXT: Offset: 0x +# CHECK-NEXT: Size: 48 +# CHECK-NEXT: Link: 0 +# CHECK-NEXT: Info: 0 +# CHECK-NEXT: AddressAlignment: 16 +# CHECK-NEXT: EntrySize: 0 +# CHECK-NEXT: } + +# Commons with unique name in each file must be assigned to that file's section. +# For a common with multiple definitions, the largest one wins and it must be +# assigned to the section from the file which provided the winning def +# CHECK: Symbol { +# CHECK: Name: common_multiple +# CHECK-NEXT: Value: 0x20 +# CHECK-NEXT: Size: 32 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common_2 +# CHECK-NEXT: } +# CHECK: Symbol { +# CHECK: Name: common_uniq_0 +# CHECK-NEXT: Value: 0x4 +# CHECK-NEXT: Size: 4 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common_0 +# CHECK-NEXT: } +# CHECK: Symbol { +# CHECK: Name: common_uniq_1 +# CHECK-NEXT: Value: 0x8 +# CHECK-NEXT: Size: 8 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common_1 +# CHECK-NEXT: } +# CHECK: Symbol { +# CHECK: Name: common_uniq_2 +# CHECK-NEXT: Value: 0x10 +# CHECK-NEXT: Size: 16 +# CHECK-NEXT: Binding: Global +# CHECK-NEXT: Type: Object +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common_2 +# CHECK-NEXT: } + +.globl _start +_start: + jmp _start + +.comm common_uniq_0,4,4 +.comm common_multiple,8,8