diff --git a/lld/lib/ReaderWriter/PECOFF/Atoms.cpp b/lld/lib/ReaderWriter/PECOFF/Atoms.cpp new file mode 100644 index 000000000000..13c58f7ee609 --- /dev/null +++ b/lld/lib/ReaderWriter/PECOFF/Atoms.cpp @@ -0,0 +1,30 @@ +//===- lib/ReaderWriter/PECOFF/Atoms.cpp ----------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Atoms.h" + +namespace lld { +namespace coff { + +namespace { +void addEdge(COFFDefinedAtom *a, COFFDefinedAtom *b, + lld::Reference::Kind kind) { + auto ref = new COFFReference(kind); + ref->setTarget(b); + a->addReference(std::unique_ptr(ref)); +} +} + +void connectAtomsWithLayoutEdge(COFFDefinedAtom *a, COFFDefinedAtom *b) { + addEdge(a, b, lld::Reference::kindLayoutAfter); + addEdge(b, a, lld::Reference::kindLayoutBefore); +} + +} // namespace coff +} // namespace lld diff --git a/lld/lib/ReaderWriter/PECOFF/Atoms.h b/lld/lib/ReaderWriter/PECOFF/Atoms.h index 3642631a084e..bf83a002fc4f 100644 --- a/lld/lib/ReaderWriter/PECOFF/Atoms.h +++ b/lld/lib/ReaderWriter/PECOFF/Atoms.h @@ -15,10 +15,14 @@ namespace lld { namespace coff { +class COFFDefinedAtom; +using llvm::object::COFFObjectFile; using llvm::object::coff_section; using llvm::object::coff_symbol; +void connectAtomsWithLayoutEdge(COFFDefinedAtom *a, COFFDefinedAtom *b); + /// A COFFReference represents relocation information for an atom. For /// example, if atom X has a reference to atom Y with offsetInAtom=8, that /// means that the address starting at 8th byte of the content of atom X needs @@ -93,23 +97,23 @@ private: class COFFDefinedAtom : public DefinedAtom { public: COFFDefinedAtom(const File &f, llvm::StringRef n, const coff_symbol *symb, - const coff_section *sec, llvm::ArrayRef d) - : _owningFile(f), _name(n), _symbol(symb), _section(sec), _data(d) {} + const coff_section *sec, llvm::ArrayRef d, + StringRef sectionName, uint64_t ordinal) + : _owningFile(f), _name(n), _symbol(symb), _section(sec), _data(d), + _sectionName(sectionName), _ordinal(ordinal) {} virtual const class File &file() const { return _owningFile; } virtual llvm::StringRef name() const { return _name; } - virtual uint64_t ordinal() const { - return reinterpret_cast(_symbol); - } + virtual uint64_t ordinal() const { return _ordinal; } virtual uint64_t size() const { return _data.size(); } uint64_t originalOffset() const { return _symbol->Value; } - void addReference(COFFReference *reference) { - _references.push_back(reference); + void addReference(std::unique_ptr reference) { + _references.push_back(std::move(reference)); } virtual Scope scope() const { @@ -163,6 +167,8 @@ public: virtual bool isAlias() const { return false; } + virtual StringRef getSectionName() const { return _sectionName; } + virtual llvm::ArrayRef rawContent() const { return _data; } virtual reference_iterator begin() const { @@ -177,7 +183,7 @@ public: private: virtual const Reference *derefIterator(const void *iter) const { size_t index = reinterpret_cast(iter); - return _references[index]; + return _references[index].get(); } virtual void incrementIterator(const void *&iter) const { @@ -189,8 +195,10 @@ private: llvm::StringRef _name; const coff_symbol *_symbol; const coff_section *_section; - std::vector _references; + std::vector > _references; llvm::ArrayRef _data; + StringRef _sectionName; + uint64_t _ordinal; }; } // namespace coff diff --git a/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt b/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt index a10bf02093bd..ff0141e4d378 100644 --- a/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt +++ b/lld/lib/ReaderWriter/PECOFF/CMakeLists.txt @@ -1,4 +1,5 @@ add_lld_library(lldPECOFF + Atoms.cpp PECOFFTargetInfo.cpp ReaderCOFF.cpp ReaderImportHeader.cpp diff --git a/lld/lib/ReaderWriter/PECOFF/GroupedSectionsPass.h b/lld/lib/ReaderWriter/PECOFF/GroupedSectionsPass.h new file mode 100644 index 000000000000..883dc823b29b --- /dev/null +++ b/lld/lib/ReaderWriter/PECOFF/GroupedSectionsPass.h @@ -0,0 +1,122 @@ +//===- lib/ReaderWriter/PECOFF/GroupedSectionsPass.h-----------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file \brief This pass adds layout-{before,after} references to atoms in +/// grouped sections, so that they will appear in the correct order in the +/// output. +/// +/// In COFF, sections will be merged into one section by the linker if their +/// names are the same after discarding the "$" character and all characters +/// follow it from their names. The characters following the "$" character +/// determines the merge order. Assume there's an object file containing four +/// data sections in the following order. +/// +/// - .data$2 +/// - .data$3 +/// - .data$1 +/// - .data +/// +/// In this case, the resulting binary should have ".data" section with the +/// contents of ".data", ".data$1", ".data$2" and ".data$3" in that order. +/// +/// In lld, this contraint is modeled by the atom model using +/// layout-{before,after} references. Atoms in the same (unmerged-)section have +/// already been connected with layout-{before,after} edges each other when the +/// control reaches to this pass. We pick the head atom from each section that +/// needs to merged, and connects them with layout-{before,after} edges in the +/// right order, so that they'll be sorted correctly in the layout pass. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLD_READER_WRITER_PE_COFF_GROUPED_SECTIONS_PASS_H_ +#define LLD_READER_WRITER_PE_COFF_GROUPED_SECTIONS_PASS_H_ + +#include "Atoms.h" +#include "lld/Core/Pass.h" +#include "lld/Core/Pass.h" +#include "llvm/Support/Debug.h" + +#include +#include + +using lld::coff::COFFDefinedAtom; + +namespace lld { +namespace pecoff { + +namespace { +bool compareByFileOrdinal(const DefinedAtom *a, const DefinedAtom *b) { + return a->file().ordinal() > b->file().ordinal(); +} +} + +class GroupedSectionsPass : public lld::Pass { +public: + GroupedSectionsPass() {} + + virtual void perform(MutableFile &mergedFile) { + std::map> sectionToHeadAtoms( + filterHeadAtoms(mergedFile)); + std::vector> groupedAtomsList( + groupBySectionName(sectionToHeadAtoms)); + for (auto &groupedAtoms : groupedAtomsList) + connectAtoms(groupedAtoms); + } + +private: + typedef std::map> SectionToAtomsT; + + /// Returns the list of atoms that appeared at the beginning of sections. + SectionToAtomsT filterHeadAtoms(MutableFile &mutableFile) const { + SectionToAtomsT result; + for (const DefinedAtom *atom : mutableFile.defined()) { + auto *coffAtom = (COFFDefinedAtom *)atom; + if (coffAtom->ordinal() == 0) + result[coffAtom->getSectionName()].push_back(coffAtom); + } + return std::move(result); + } + + /// Group atoms that needs to be merged. Returned atoms are sorted by section + /// name and file ordinal. + std::vector> + groupBySectionName(SectionToAtomsT sectionToHeadAtoms) const { + SectionToAtomsT res; + // Note that the atoms are already sorted by section name because std::map + // is a sorted map. + for (auto &i : sectionToHeadAtoms) { + StringRef sectionName = i.first; + std::vector &atoms = i.second; + // Sections with the same name could exist if they are from different + // files. If that's the case, the sections needs to be sorted in the same + // order as they appeared in the command line. + std::stable_sort(atoms.begin(), atoms.end(), compareByFileOrdinal); + for (COFFDefinedAtom *atom : atoms) { + StringRef baseName = sectionName.split('$').first; + res[baseName].push_back(atom); + } + } + std::vector> vec; + for (auto &i : res) + vec.push_back(std::move(i.second)); + return std::move(vec); + } + + void connectAtoms(std::vector atoms) const { + if (atoms.size() < 2) + return; + for (auto it = atoms.begin(), e = atoms.end(); it + 1 != e; ++it) + connectAtomsWithLayoutEdge(*it, *(it + 1)); + } +}; + +} // namespace pecoff +} // namespace lld + +#endif diff --git a/lld/lib/ReaderWriter/PECOFF/PECOFFTargetInfo.cpp b/lld/lib/ReaderWriter/PECOFF/PECOFFTargetInfo.cpp index 40ac53d068c2..94bf1103256c 100644 --- a/lld/lib/ReaderWriter/PECOFF/PECOFFTargetInfo.cpp +++ b/lld/lib/ReaderWriter/PECOFF/PECOFFTargetInfo.cpp @@ -7,15 +7,14 @@ // //===----------------------------------------------------------------------===// -#include "lld/ReaderWriter/PECOFFTargetInfo.h" +#include "GroupedSectionsPass.h" #include "lld/Core/PassManager.h" #include "lld/Passes/LayoutPass.h" +#include "lld/ReaderWriter/PECOFFTargetInfo.h" #include "lld/ReaderWriter/Reader.h" #include "lld/ReaderWriter/Writer.h" -#include "llvm/Support/Debug.h" - namespace lld { error_code PECOFFTargetInfo::parseFile( @@ -59,6 +58,7 @@ PECOFFTargetInfo::stringFromRelocKind(Reference::Kind kind) const { } void PECOFFTargetInfo::addPasses(PassManager &pm) const { + pm.add(std::unique_ptr(new pecoff::GroupedSectionsPass())); pm.add(std::unique_ptr(new LayoutPass())); } diff --git a/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp b/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp index 4d61c39b8683..3c3693b52af8 100644 --- a/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp +++ b/lld/lib/ReaderWriter/PECOFF/ReaderCOFF.cpp @@ -184,14 +184,19 @@ private: })); llvm::ArrayRef SecData; + StringRef sectionName; if (error_code ec = Obj->getSectionContents(section, SecData)) return ec; + if (error_code ec = Obj->getSectionName(section, sectionName)) + return ec; + uint64_t ordinal = 0; // Create an atom for the entire section. if (symbols.empty()) { llvm::ArrayRef Data(SecData.data(), SecData.size()); atoms.push_back(new (AtomStorage.Allocate()) - COFFDefinedAtom(*this, "", nullptr, section, Data)); + COFFDefinedAtom(*this, "", nullptr, section, Data, + sectionName, ordinal++)); return error_code::success(); } @@ -201,7 +206,8 @@ private: uint64_t Size = symbols[0]->Value; llvm::ArrayRef Data(SecData.data(), Size); atoms.push_back(new (AtomStorage.Allocate()) - COFFDefinedAtom(*this, "", nullptr, section, Data)); + COFFDefinedAtom(*this, "", nullptr, section, Data, + sectionName, ordinal++)); } for (auto si = symbols.begin(), se = symbols.end(); si != se; ++si) { @@ -215,24 +221,12 @@ private: if (error_code ec = Obj->getSymbolName(*si, name)) return ec; atoms.push_back(new (AtomStorage.Allocate()) - COFFDefinedAtom(*this, name, *si, section, Data)); + COFFDefinedAtom(*this, name, *si, section, Data, + sectionName, ordinal++)); } return error_code::success(); } - void addEdge(COFFDefinedAtom *a, COFFDefinedAtom *b, - lld::Reference::Kind kind) const { - auto ref = new (AtomStorage.Allocate()) COFFReference(kind); - ref->setTarget(b); - a->addReference(ref); - } - - void connectAtomsWithLayoutEdge(COFFDefinedAtom *a, - COFFDefinedAtom *b) const { - addEdge(a, b, lld::Reference::kindLayoutAfter); - addEdge(b, a, lld::Reference::kindLayoutBefore); - } - /// Connect atoms appeared in the same section with layout-{before,after} /// edges. It has two purposes. /// @@ -335,9 +329,8 @@ private: COFFDefinedAtom *atom = findAtomAt(rel->VirtualAddress, section, atoms); uint32_t offsetInAtom = itemAddress - atom->originalOffset(); assert(offsetInAtom < atom->size()); - COFFReference *ref = new (AtomStorage.Allocate()) - COFFReference(targetAtom, offsetInAtom, rel->Type); - atom->addReference(ref); + atom->addReference(std::unique_ptr( + new COFFReference(targetAtom, offsetInAtom, rel->Type))); return error_code::success(); } diff --git a/lld/test/pecoff/Inputs/grouped-sections.asm b/lld/test/pecoff/Inputs/grouped-sections.asm new file mode 100644 index 000000000000..f17ab1166afe --- /dev/null +++ b/lld/test/pecoff/Inputs/grouped-sections.asm @@ -0,0 +1,17 @@ +.386 +.model flat, c + +_data$2 SEGMENT BYTE alias(".data$2") + db "orld", 0 +_data$2 ends + +_data$1 SEGMENT BYTE alias(".data$1") + db "o, w" +_data$1 ends + +.data + db "Hell" + +.code +main: +end main diff --git a/lld/test/pecoff/Inputs/grouped-sections.obj b/lld/test/pecoff/Inputs/grouped-sections.obj new file mode 100644 index 000000000000..42b58debb89e Binary files /dev/null and b/lld/test/pecoff/Inputs/grouped-sections.obj differ diff --git a/lld/test/pecoff/grouped-sections.test b/lld/test/pecoff/grouped-sections.test new file mode 100644 index 000000000000..ef694cecbac7 --- /dev/null +++ b/lld/test/pecoff/grouped-sections.test @@ -0,0 +1,15 @@ +# RUN: lld -flavor link -out %t1 -subsystem console -force \ +# RUN: -- %p/Inputs/grouped-sections.obj && llvm-objdump -s %t1 | FileCheck %s +# +# The file "grouped-sections.obj" has three data sections in the following +# order: +# +# .data$2 +# .data$1 +# .data +# +# If all the sections will be merged correctly, the resulting ".data" +# section will have the string "Hello, world". + +CHECK: Contents of section .rdata: +CHECK-NEXT: Hello, world