Index: ELF/InputSection.h =================================================================== --- ELF/InputSection.h +++ ELF/InputSection.h @@ -256,6 +256,19 @@ const llvm::object::Elf_Mips_RegInfo *Reginfo = nullptr; }; +// A special kind of section used to store common symbols +template class CommonInputSection : public InputSection { + typedef typename ELFT::uint uintX_t; + +public: + CommonInputSection(); + +private: + void build(); + + typename ELFT::Shdr Hdr; +}; + } // namespace elf } // namespace lld Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -14,6 +14,7 @@ #include "InputFiles.h" #include "LinkerScript.h" #include "OutputSections.h" +#include "SymbolTable.h" #include "Target.h" #include "Thunks.h" @@ -666,6 +667,30 @@ return S->SectionKind == InputSectionBase::MipsOptions; } +template +CommonInputSection::CommonInputSection() + : InputSection(nullptr, &Hdr) { + + build(); +} + +template void CommonInputSection::build() { + Hdr.sh_addralign = 1; + Hdr.sh_size = 0; + Hdr.sh_type = SHT_NOBITS; + Hdr.sh_flags = SHF_ALLOC | SHF_WRITE; + + for (Symbol *S : Symtab::X->getSymbols()) { + SymbolBody *Body = S->body(); + + if (auto *C = dyn_cast>(Body)) { + Hdr.sh_addralign = std::max(Hdr.sh_addralign, C->Alignment); + Hdr.sh_size = alignTo(Hdr.sh_size, C->Alignment); + Hdr.sh_size += C->Size; + } + } +} + template class elf::InputSectionBase; template class elf::InputSectionBase; template class elf::InputSectionBase; @@ -695,3 +720,8 @@ template class elf::MipsOptionsInputSection; template class elf::MipsOptionsInputSection; template class elf::MipsOptionsInputSection; + +template class elf::CommonInputSection; +template class elf::CommonInputSection; +template class elf::CommonInputSection; +template class elf::CommonInputSection; Index: ELF/LinkerScript.h =================================================================== --- ELF/LinkerScript.h +++ ELF/LinkerScript.h @@ -23,6 +23,8 @@ template class InputSectionBase; template class OutputSectionBase; template class OutputSectionFactory; +template class DefinedCommon; +template class CommonInputSection; typedef std::function Expr; @@ -129,6 +131,8 @@ int compareSections(StringRef A, StringRef B); void addScriptedSymbols(); bool hasPhdrsCommands(); + bool hasCommonSection(); + void addCommonSymbols(ArrayRef *> CommonSymbols); private: std::vector>> getSectionMap(); @@ -148,6 +152,7 @@ void dispatchAssignment(SymbolAssignment *Cmd); uintX_t Dot; + std::unique_ptr> Common; }; // Variable template is a C++14 feature, so we can't template Index: ELF/LinkerScript.cpp =================================================================== --- ELF/LinkerScript.cpp +++ ELF/LinkerScript.cpp @@ -102,6 +102,13 @@ for (InputSectionBase *S : F->getSections()) if (!isDiscarded(S) && !S->OutSec && match(Patterns, S->getSectionName())) Ret.push_back(S); + + if (llvm::find(Patterns, "COMMON")) { + Common = llvm::make_unique>(); + if (Common->getSize()) + Ret.push_back(Common.get()); + } + return Ret; } @@ -402,6 +409,26 @@ return 0; } +template bool LinkerScript::hasCommonSection() { + return Common && Common->OutSec; +} + +template +void LinkerScript::addCommonSymbols( + ArrayRef *> CommonSymbols) { + // Only one output section is supported for common symbols. + // This method expects symbols already sorted by layout. + + int si = Common->OutSec->SectionIndex; + uintX_t Off = Common->OutSecOff; + for (DefinedCommon *C : CommonSymbols) { + C->Section = Common->OutSec; + C->Offset = Off; + Off = alignTo(Off, C->Alignment); + Off += C->Size; + } +} + class elf::ScriptParser : public ScriptParserBase { typedef void (ScriptParser::*Handler)(); Index: ELF/OutputSections.cpp =================================================================== --- ELF/OutputSections.cpp +++ ELF/OutputSections.cpp @@ -1438,7 +1438,7 @@ break; } case SymbolBody::DefinedCommonKind: - return Out::Bss; + return cast>(Sym)->getSection(); case SymbolBody::SharedKind: if (cast>(Sym)->needsCopy()) return Out::Bss; Index: ELF/SymbolTable.cpp =================================================================== --- ELF/SymbolTable.cpp +++ ELF/SymbolTable.cpp @@ -322,13 +322,15 @@ // We have a new non-common defined symbol with the specified binding. Return 1 // if the new symbol should win, -1 if the new symbol should lose, or 0 if there // is a conflict. If the new symbol wins, also update the binding. -static int compareDefinedNonCommon(Symbol *S, bool WasInserted, uint8_t Binding) { +template +static int compareDefinedNonCommon(Symbol *S, bool WasInserted, + uint8_t Binding) { if (int Cmp = compareDefined(S, WasInserted, Binding)) { if (Cmp > 0) S->Binding = Binding; return Cmp; } - if (isa(S->body())) { + if (isa>(S->body())) { // Non-common symbols take precedence over common symbols. if (Config->WarnCommon) warning("common " + S->body()->getName() + " is overridden"); @@ -350,9 +352,10 @@ int Cmp = compareDefined(S, WasInserted, Binding); if (Cmp > 0) { S->Binding = Binding; - replaceBody(S, N, Size, Alignment, StOther, Type, File); + replaceBody>(S, N, Size, Alignment, StOther, Type, + File); } else if (Cmp == 0) { - auto *C = dyn_cast(S->body()); + auto *C = dyn_cast>(S->body()); if (!C) { // Non-common symbols take precedence over common symbols. if (Config->WarnCommon) @@ -388,7 +391,7 @@ insert(Name, Sym.getType(), Sym.getVisibility(), /*CanOmitFromDynSym*/ false, /*IsUsedInRegularObj*/ true, Section ? Section->getFile() : nullptr); - int Cmp = compareDefinedNonCommon(S, WasInserted, Sym.getBinding()); + int Cmp = compareDefinedNonCommon(S, WasInserted, Sym.getBinding()); if (Cmp > 0) replaceBody>(S, Name, Sym, Section); else if (Cmp == 0) @@ -404,7 +407,7 @@ std::tie(S, WasInserted) = insert(Name, STT_NOTYPE, StOther & 3, /*CanOmitFromDynSym*/ false, /*IsUsedInRegularObj*/ true, nullptr); - int Cmp = compareDefinedNonCommon(S, WasInserted, Binding); + int Cmp = compareDefinedNonCommon(S, WasInserted, Binding); if (Cmp > 0) replaceBody>(S, Name, StOther); else if (Cmp == 0) @@ -421,7 +424,7 @@ std::tie(S, WasInserted) = insert(N, STT_NOTYPE, STV_HIDDEN, /*CanOmitFromDynSym*/ false, /*IsUsedInRegularObj*/ true, nullptr); - int Cmp = compareDefinedNonCommon(S, WasInserted, STB_GLOBAL); + int Cmp = compareDefinedNonCommon(S, WasInserted, STB_GLOBAL); if (Cmp > 0) replaceBody>(S, N, Value, Section); else if (Cmp == 0) @@ -459,8 +462,8 @@ bool WasInserted; std::tie(S, WasInserted) = insert(Name, Type, StOther & 3, CanOmitFromDynSym, /*IsUsedInRegularObj*/ false, F); - int Cmp = - compareDefinedNonCommon(S, WasInserted, IsWeak ? STB_WEAK : STB_GLOBAL); + int Cmp = compareDefinedNonCommon(S, WasInserted, + IsWeak ? STB_WEAK : STB_GLOBAL); if (Cmp > 0) replaceBody(S, Name, StOther, Type, F); else if (Cmp == 0) Index: ELF/Symbols.h =================================================================== --- ELF/Symbols.h +++ ELF/Symbols.h @@ -167,7 +167,7 @@ BitcodeFile *file() { return (BitcodeFile *)this->File; } }; -class DefinedCommon : public Defined { +template class DefinedCommon : public Defined { public: DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, uint8_t StOther, uint8_t Type, InputFile *File); @@ -176,14 +176,17 @@ return S->kind() == SymbolBody::DefinedCommonKind; } + OutputSectionBase *getSection() const; // The output offset of this common symbol in the output bss. Computed by the // writer. - uint64_t OffsetInBss; + uint64_t Offset; // The maximum alignment we have seen for this symbol. uint64_t Alignment; uint64_t Size; + // Output section for this symbol (default is .bss) + OutputSectionBase *Section = nullptr; }; // Regular defined symbols read from object file symbol tables. @@ -433,7 +436,8 @@ // assume that the size and alignment of ELF64LE symbols is sufficient for any // ELFT, and we verify this with the static_asserts in replaceBody. llvm::AlignedCharArrayUnion< - DefinedBitcode, DefinedCommon, DefinedRegular, + DefinedBitcode, DefinedCommon, + DefinedRegular, DefinedSynthetic, Undefined, SharedSymbol, LazyArchive, LazyObject> Body; Index: ELF/Symbols.cpp =================================================================== --- ELF/Symbols.cpp +++ ELF/Symbols.cpp @@ -63,8 +63,10 @@ return VA - Out::TlsPhdr->p_vaddr; return VA; } - case SymbolBody::DefinedCommonKind: - return Out::Bss->getVA() + cast(Body).OffsetInBss; + case SymbolBody::DefinedCommonKind: { + auto &D = cast>(Body); + return D.getSection()->getVA() + D.Offset; + } case SymbolBody::SharedKind: { auto &SS = cast>(Body); if (!SS.NeedsCopyOrPltAddr) @@ -175,7 +177,7 @@ } template typename ELFT::uint SymbolBody::getSize() const { - if (const auto *C = dyn_cast(this)) + if (const auto *C = dyn_cast>(this)) return C->Size; if (const auto *DR = dyn_cast>(this)) return DR->Size; @@ -218,13 +220,20 @@ : Defined(SymbolBody::DefinedSyntheticKind, N, STV_HIDDEN, 0 /* Type */), Value(Value), Section(Section) {} -DefinedCommon::DefinedCommon(StringRef N, uint64_t Size, uint64_t Alignment, - uint8_t StOther, uint8_t Type, InputFile *File) +template +DefinedCommon::DefinedCommon(StringRef N, uint64_t Size, + uint64_t Alignment, uint8_t StOther, + uint8_t Type, InputFile *File) : Defined(SymbolBody::DefinedCommonKind, N, StOther, Type), Alignment(Alignment), Size(Size) { this->File = File; } +template +OutputSectionBase *DefinedCommon::getSection() const { + return Section ? Section : Out::Bss; +} + std::unique_ptr Lazy::fetch() { if (auto *S = dyn_cast(this)) return S->fetch(); @@ -329,3 +338,8 @@ template class elf::DefinedSynthetic; template class elf::DefinedSynthetic; template class elf::DefinedSynthetic; + +template class elf::DefinedCommon; +template class elf::DefinedCommon; +template class elf::DefinedCommon; +template class elf::DefinedCommon; Index: ELF/Writer.cpp =================================================================== --- ELF/Writer.cpp +++ ELF/Writer.cpp @@ -68,7 +68,7 @@ void writeSections(); void writeBuildId(); - void addCommonSymbols(std::vector &Syms); + void addCommonSymbols(std::vector *> &Syms); std::unique_ptr Buffer; @@ -502,21 +502,15 @@ // Until this function is called, common symbols do not belong to any section. // This function adds them to end of BSS section. template -void Writer::addCommonSymbols(std::vector &Syms) { +void Writer::addCommonSymbols(std::vector *> &Syms) { if (Syms.empty()) return; - // Sort the common symbols by alignment as an heuristic to pack them better. - std::stable_sort(Syms.begin(), Syms.end(), - [](const DefinedCommon *A, const DefinedCommon *B) { - return A->Alignment > B->Alignment; - }); - uintX_t Off = Out::Bss->getSize(); - for (DefinedCommon *C : Syms) { + for (DefinedCommon *C : Syms) { Off = alignTo(Off, C->Alignment); Out::Bss->updateAlignment(C->Alignment); - C->OffsetInBss = Off; + C->Offset = Off; Off += C->Size; } @@ -743,7 +737,7 @@ // Now that we have defined all possible symbols including linker- // synthesized ones. Visit all symbols to give the finishing touches. - std::vector CommonSymbols; + std::vector *> CommonSymbols; for (Symbol *S : Symtab.getSymbols()) { SymbolBody *Body = S->body(); @@ -752,7 +746,7 @@ if (S->IsUsedInRegularObj && Body->isUndefined() && !S->isWeak()) reportUndefined(Symtab, Body); - if (auto *C = dyn_cast(Body)) + if (auto *C = dyn_cast>(Body)) CommonSymbols.push_back(C); if (!includeInSymtab(*Body)) @@ -772,7 +766,17 @@ if (HasError) return; - addCommonSymbols(CommonSymbols); + // Sort the common symbols by alignment as an heuristic to pack them better. + std::stable_sort( + CommonSymbols.begin(), CommonSymbols.end(), + [](const DefinedCommon *A, const DefinedCommon *B) { + return A->Alignment > B->Alignment; + }); + + if (Script::X->hasCommonSection()) + Script::X->addCommonSymbols(CommonSymbols); + else + addCommonSymbols(CommonSymbols); // So far we have added sections from input object files. // This function adds linker-created Out::* sections. Index: test/ELF/linkerscript/linkerscript-common.s =================================================================== --- test/ELF/linkerscript/linkerscript-common.s +++ test/ELF/linkerscript/linkerscript-common.s @@ -0,0 +1,34 @@ +# REQUIRES: x86 +# RUN: llvm-mc -filetype=obj -triple=x86_64-unknown-linux %s -o %t +# RUN: echo "SECTIONS { .common : { *(COMMON) } }" > %t.script +# RUN: ld.lld -o %t1 --script %t.script %t +# RUN: llvm-readobj -t %t1 | FileCheck %s + +# q2 alignment is greater than q1, so it should have smaller offset +# because of sorting +# CHECK: Symbol { +# CHECK: Name: q1 (8) +# CHECK-NEXT: Value: 0x1D8 +# CHECK-NEXT: Size: 128 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common (0x1) +# CHECK-NEXT: } +# CHECK-NEXT: Symbol { +# CHECK-NEXT: Name: q2 (11) +# CHECK-NEXT: Value: 0x158 +# CHECK-NEXT: Size: 128 +# CHECK-NEXT: Binding: Global (0x1) +# CHECK-NEXT: Type: Object (0x1) +# CHECK-NEXT: Other: 0 +# CHECK-NEXT: Section: .common (0x1) +# CHECK-NEXT: } + +.globl _start +_start: + jmp _start + +.comm q1,128,8 +.comm q2,128,256 +