Index: include/lld/Core/DefinedAtom.h =================================================================== --- include/lld/Core/DefinedAtom.h +++ include/lld/Core/DefinedAtom.h @@ -258,6 +258,10 @@ /// \brief constraints on whether the linker may dead strip away this atom. virtual DeadStripKind deadStrip() const = 0; + /// \brief list of atoms that needs to be linked if this one is not + /// dead-stripped. + virtual ArrayRef associates() const; + /// \brief Under which conditions should this atom be dynamically exported. virtual DynamicExport dynamicExport() const { return dynamicExportNormal; Index: include/lld/Core/Resolver.h =================================================================== --- include/lld/Core/Resolver.h +++ include/lld/Core/Resolver.h @@ -14,6 +14,7 @@ #include "lld/Core/SharedLibraryFile.h" #include "lld/Core/SymbolTable.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" @@ -65,6 +66,7 @@ void deadStripOptimize(); bool checkUndefines(); void removeCoalescedAwayAtoms(); + void addAssociates(); void checkDylibSymbolCollisions(); void forEachUndefines(bool searchForOverrides, UndefCallback callback); @@ -107,6 +109,7 @@ llvm::DenseSet _liveAtoms; std::unique_ptr _result; llvm::DenseMap> _reverseRef; + llvm::DenseMap> _assoc; }; } // namespace lld Index: lib/Core/DefinedAtom.cpp =================================================================== --- lib/Core/DefinedAtom.cpp +++ lib/Core/DefinedAtom.cpp @@ -8,13 +8,12 @@ //===----------------------------------------------------------------------===// #include "llvm/Support/ErrorHandling.h" +#include "llvm/ADT/ArrayRef.h" #include "lld/Core/DefinedAtom.h" - namespace lld { - DefinedAtom::ContentPermissions DefinedAtom::permissions() const { // By default base permissions on content type. return permissions(this->contentType()); @@ -80,6 +79,8 @@ llvm_unreachable("unknown content type"); } +ArrayRef DefinedAtom::associates() const { + return ArrayRef(); +} } // namespace - Index: lib/Core/Resolver.cpp =================================================================== --- lib/Core/Resolver.cpp +++ lib/Core/Resolver.cpp @@ -190,6 +190,8 @@ maybeAddSectionGroupOrGnuLinkOnce(atom); } else { _symbolTable.add(atom); + if (!atom.associates().empty()) + _assoc[&atom] = atom.associates(); } // An atom that should never be dead-stripped is a dead-strip root. @@ -404,6 +406,21 @@ _atoms.end()); } +void Resolver::addAssociates() { + ScopedTask task(getDefaultDomain(), "addAssociates"); + for (auto it : _assoc) { + const DefinedAtom *atom = it.first; + // Dead strip is enabled, and the atom is dead. + if (_context.deadStrip() && _liveAtoms.count(atom) == 0) + continue; + // Atom is coalesed away with other atom. + if (_symbolTable.isCoalescedAway(atom)) + continue; + for (const DefinedAtom *a : it.second) + _atoms.push_back(a); + } +} + bool Resolver::resolve() { if (!resolveUndefines()) return false; @@ -413,6 +430,7 @@ if (!_context.allowRemainingUndefines()) return false; removeCoalescedAwayAtoms(); + addAssociates(); _result->addAtoms(_atoms); return true; } Index: lib/ReaderWriter/PECOFF/Atoms.h =================================================================== --- lib/ReaderWriter/PECOFF/Atoms.h +++ lib/ReaderWriter/PECOFF/Atoms.h @@ -167,6 +167,9 @@ uint64_t ordinal() const override { return _ordinal; } Alignment alignment() const override { return _alignment; } + void addAssociate(COFFDefinedFileAtom *other) { _assoc.push_back(other); } + ArrayRef associates() const override { return _assoc; } + private: StringRef _sectionName; Scope _scope; @@ -175,6 +178,7 @@ uint64_t _ordinal; Alignment _alignment; std::vector > _references; + std::vector _assoc; }; // A COFFDefinedAtom represents an atom read from a file and has contents. Index: lib/ReaderWriter/PECOFF/ReaderCOFF.cpp =================================================================== --- lib/ReaderWriter/PECOFF/ReaderCOFF.cpp +++ lib/ReaderWriter/PECOFF/ReaderCOFF.cpp @@ -172,6 +172,9 @@ // A map to get whether the section allows its contents to be merged or not. std::map _merge; + // COMDAT associative sections + std::map> _association; + // A sorted map to find an atom from a section and an offset within // the section. std::mapSectionNumber == llvm::COFF::IMAGE_SYM_ABSOLUTE || sym->SectionNumber == llvm::COFF::IMAGE_SYM_UNDEFINED) @@ -548,19 +552,22 @@ const coff_section *sec; if (error_code ec = _obj->getSection(sym->SectionNumber, sec)) return ec; - - if (_merge.count(sec)) - continue; - if (!(sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_COMDAT)) - continue; - - _comdatSections.insert(sec); - - if (sym->NumberOfAuxSymbols == 0) - return llvm::object::object_error::parse_failed; const coff_aux_section_definition *aux = reinterpret_cast(i.second); - _merge[sec] = getMerge(aux); + + if (sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_COMDAT) { + // Read aux symbol data. + _comdatSections.insert(sec); + _merge[sec] = getMerge(aux); + } + + // Handle associative sections. + if (aux->Selection == llvm::COFF::IMAGE_COMDAT_SELECT_ASSOCIATIVE) { + const coff_section *parent; + if (error_code ec = _obj->getSection(aux->Number, parent)) + return ec; + _association[parent].insert(sec); + } } // The sections that does not have auxiliary symbol are regular sections, in @@ -694,6 +701,29 @@ definedAtoms.push_back(atom); } } + + // A COMDAT section with SELECT_ASSOCIATIVE attribute refer to other + // section. If the referred section is linked to a binary, the + // referring section needs to be linked too. A typical use case of + // this attribute is a static initializer; a parent is a comdat BSS + // section, and a child is a static initializer code for the data. + // + // We add referring section contents to the referred section's + // associate list, so that Resolver takes care of them. + for (auto i : _association) { + const coff_section *parent = i.first; + const std::set &childSections = i.second; + assert(_sectionAtoms[parent].size() > 0); + + COFFDefinedFileAtom *p = _sectionAtoms[parent][0]; + for (const coff_section *sec : childSections) { + if (_sectionAtoms.count(sec)) { + assert(_sectionAtoms[sec].size() > 0); + p->addAssociate(_sectionAtoms[sec][0]); + } + } + } + return error_code(); } Index: lib/ReaderWriter/YAML/ReaderWriterYAML.cpp =================================================================== --- lib/ReaderWriter/YAML/ReaderWriterYAML.cpp +++ lib/ReaderWriter/YAML/ReaderWriterYAML.cpp @@ -286,6 +286,7 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(ArchMember) LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::Reference *) +LLVM_YAML_IS_SEQUENCE_VECTOR(const lld::DefinedAtom *) // Always write DefinedAtoms content bytes as a flow sequence. LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(ImplicitHex8) // for compatibility with gcc-4.7 in C++11 mode, add extra namespace @@ -813,6 +814,8 @@ _sectionName(atom->customSectionName()) { for (const lld::Reference *r : *atom) _references.push_back(r); + for (const DefinedAtom *a : atom->associates()) + _associates.push_back(a); if (!atom->occupiesDiskSpace()) return; ArrayRef cont = atom->rawContent(); @@ -820,6 +823,7 @@ for (uint8_t x : cont) _content.push_back(x); } + const lld::DefinedAtom *denormalize(IO &io) { YamlContext *info = reinterpret_cast(io.getContext()); assert(info != nullptr); @@ -837,7 +841,9 @@ << _name.size() << ")\n"); return this; } + void bind(const RefNameResolver &); + // Extract current File object from YAML I/O parsing context const lld::File &fileFromContext(IO &io) { YamlContext *info = reinterpret_cast(io.getContext()); @@ -862,6 +868,11 @@ ContentPermissions permissions() const override { return _permissions; } void setGroupChild(bool val) { _isGroupChild = val; } bool isGroupChild() const { return _isGroupChild; } + + ArrayRef associates() const override { + return _associates; + } + ArrayRef rawContent() const override { if (!occupiesDiskSpace()) return ArrayRef(); @@ -910,6 +921,7 @@ uint64_t _size; StringRef _sectionName; std::vector _references; + std::vector _associates; bool _isGroupChild; }; @@ -956,6 +968,7 @@ DefinedAtom::permissions( keys->_contentType)); io.mapOptional("references", keys->_references); + io.mapOptional("associates", keys->_associates); } }; Index: test/core/associates.objtxt =================================================================== --- /dev/null +++ test/core/associates.objtxt @@ -0,0 +1,15 @@ +# RUN: lld -core %s | FileCheck %s + +--- +defined-atoms: + - name: f1 + scope: global + associates: + - name: f2 +... + +# CHECK: defined-atoms: +# CHECK: - name: f1 +# CHECK: scope: global +# CHECK: associates: +# CHECK: - name: f2 Index: test/pecoff/Inputs/associative1.obj.yaml =================================================================== --- /dev/null +++ test/pecoff/Inputs/associative1.obj.yaml @@ -0,0 +1,53 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 4 + SectionData: 00000000 + - Name: '.CRT$XCU' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_LNK_COMDAT, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 77777777 +symbols: + - Name: .data + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + Selection: IMAGE_COMDAT_SELECT_ANY + - Name: _var + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: '.CRT$XCU' + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 4 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 1 + Selection: IMAGE_COMDAT_SELECT_ASSOCIATIVE + - Name: _init + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: test/pecoff/Inputs/associative3.obj.yaml =================================================================== --- /dev/null +++ test/pecoff/Inputs/associative3.obj.yaml @@ -0,0 +1,33 @@ +--- +header: + Machine: IMAGE_FILE_MACHINE_I386 + Characteristics: [] +sections: + - Name: .text + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 4 + SectionData: 0000000000000000 + Relocations: + - VirtualAddress: 4 + SymbolName: _var + Type: IMAGE_REL_I386_DIR32 +symbols: + - Name: .text + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + - Name: _main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL + - Name: _var + Value: 0 + SectionNumber: 0 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: test/pecoff/associative.test =================================================================== --- /dev/null +++ test/pecoff/associative.test @@ -0,0 +1,10 @@ +# RUN: yaml2obj %p/Inputs/associative1.obj.yaml > %t1.obj +# RUN: yaml2obj %p/Inputs/associative1.obj.yaml > %t2.obj +# RUN: yaml2obj %p/Inputs/associative3.obj.yaml > %t3.obj +# +# RUN: lld -flavor link /machine:x86 /subsystem:console /entry:main \ +# RUN: /out:%t.exe -- %t1.obj %t2.obj %t3.obj +# RUN: obj2yaml %t.exe | FileCheck %s + +CHECK: - Name: .CRT +CHECK: SectionData: '77777777'