Index: include/lld/ReaderWriter/ELFLinkingContext.h =================================================================== --- include/lld/ReaderWriter/ELFLinkingContext.h +++ include/lld/ReaderWriter/ELFLinkingContext.h @@ -295,21 +295,13 @@ bool collectStats() const { return _collectStats; } void setCollectStats(bool s) { _collectStats = s; } - // We can parse several linker scripts via command line whose ASTs are stored - // in the current linking context via addLinkerScript(). - void addLinkerScript(std::unique_ptr script) { - _scripts.push_back(std::move(script)); - } - - const std::vector> &scripts() const { - return _scripts; - } - // --wrap option. void addWrapForSymbol(StringRef sym) { _wrapCalls.insert(sym); } const llvm::StringSet<> &wrapCalls() const { return _wrapCalls; } + script::Sema &linkerScriptSema() { return _linkerScriptSema; } + private: ELFLinkingContext() = delete; @@ -353,7 +345,10 @@ llvm::StringSet<> _wrapCalls; std::map _absoluteSymbols; llvm::StringSet<> _dynamicallyExportedSymbols; - std::vector> _scripts; + + // The linker script semantic object, which owns all script ASTs, is stored + // in the current linking context via _linkerScriptSema. + script::Sema _linkerScriptSema; }; } // end namespace lld Index: include/lld/ReaderWriter/LinkerScript.h =================================================================== --- include/lld/ReaderWriter/LinkerScript.h +++ include/lld/ReaderWriter/LinkerScript.h @@ -18,6 +18,8 @@ #include "lld/Core/Error.h" #include "lld/Core/LLVM.h" #include "lld/Core/range.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Allocator.h" @@ -26,6 +28,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include #include namespace lld { @@ -154,6 +157,7 @@ Group, Input, InputSectionsCmd, + InputSectionName, Output, OutputArch, OutputFormat, @@ -161,6 +165,7 @@ Overlay, SearchDir, Sections, + SortedGroup, SymbolAssignment, }; @@ -598,23 +603,15 @@ /// .y: { *(SORT(.text*)) } /// /* ^~~~~~~~~~~^ InputSectionSortedGroup : InputSection */ /// } -class InputSection { +class InputSection : public Command { public: - enum class Kind { InputSectionName, SortedGroup }; - - Kind getKind() const { return _kind; } - inline llvm::BumpPtrAllocator &getAllocator() const; - - virtual void dump(raw_ostream &os) const = 0; - - virtual ~InputSection() {} + static bool classof(const Command *c) { + return c->getKind() == Kind::InputSectionName || + c->getKind() == Kind::SortedGroup; + } protected: - InputSection(Parser &ctx, Kind k) : _ctx(ctx), _kind(k) {} - -private: - Parser &_ctx; - Kind _kind; + InputSection(Parser &ctx, Kind k) : Command(ctx, k) {} }; class InputSectionName : public InputSection { @@ -625,10 +622,11 @@ void dump(raw_ostream &os) const override; - static bool classof(const InputSection *c) { + static bool classof(const Command *c) { return c->getKind() == Kind::InputSectionName; } bool hasExcludeFile() const { return _excludeFile; } + StringRef name() const { return _name; } private: StringRef _name; @@ -637,6 +635,8 @@ class InputSectionSortedGroup : public InputSection { public: + typedef llvm::ArrayRef::const_iterator const_iterator; + InputSectionSortedGroup(Parser &ctx, WildcardSortMode sort, const SmallVectorImpl §ions) : InputSection(ctx, Kind::SortedGroup), _sortMode(sort) { @@ -648,12 +648,15 @@ } void dump(raw_ostream &os) const override; - WildcardSortMode getSortMode() const { return _sortMode; } + WildcardSortMode sortMode() const { return _sortMode; } - static bool classof(const InputSection *c) { + static bool classof(const Command *c) { return c->getKind() == Kind::SortedGroup; } + const_iterator begin() const { return _sections.begin(); } + const_iterator end() const { return _sections.end(); } + private: WildcardSortMode _sortMode; llvm::ArrayRef _sections; @@ -671,13 +674,14 @@ /// } class InputSectionsCmd : public Command { public: + typedef llvm::ArrayRef::const_iterator const_iterator; typedef std::vector VectorTy; - InputSectionsCmd(Parser &ctx, StringRef fileName, StringRef archiveName, + InputSectionsCmd(Parser &ctx, StringRef memberName, StringRef archiveName, bool keep, WildcardSortMode fileSortMode, WildcardSortMode archiveSortMode, const SmallVectorImpl §ions) - : Command(ctx, Kind::InputSectionsCmd), _fileName(fileName), + : Command(ctx, Kind::InputSectionsCmd), _memberName(memberName), _archiveName(archiveName), _keep(keep), _fileSortMode(fileSortMode), _archiveSortMode(archiveSortMode) { size_t numSections = sections.size(); @@ -693,8 +697,15 @@ return c->getKind() == Kind::InputSectionsCmd; } + StringRef memberName() const { return _memberName; } + StringRef archiveName() const { return _archiveName; } + const_iterator begin() const { return _sections.begin(); } + const_iterator end() const { return _sections.end(); } + WildcardSortMode archiveSortMode() const { return _archiveSortMode; } + WildcardSortMode fileSortMode() const { return _fileSortMode; } + private: - StringRef _fileName; + StringRef _memberName; StringRef _archiveName; bool _keep; WildcardSortMode _fileSortMode; @@ -718,6 +729,8 @@ public: enum Constraint { C_None, C_OnlyIfRO, C_OnlyIfRW }; + typedef llvm::ArrayRef::const_iterator const_iterator; + OutputSectionDescription( Parser &ctx, StringRef sectionName, const Expression *address, const Expression *align, const Expression *subAlign, const Expression *at, @@ -743,6 +756,10 @@ void dump(raw_ostream &os) const override; + const_iterator begin() const { return _outputSectionCommands.begin(); } + const_iterator end() const { return _outputSectionCommands.end(); } + StringRef name() const { return _sectionName; } + private: StringRef _sectionName; const Expression *_address; @@ -1083,15 +1100,195 @@ Token _bufferedToken; }; +/// script::Sema traverses all parsed linker script structures and populate +/// internal data structures to be able to answer the following questions: +/// +/// * According to the linker script, which input section goes first in the +/// output file layout, input section A or input section B? +/// +/// * What is the name of the output section that input section A should be +/// mapped to? +/// +/// * Which linker script expressions should be calculated before emitting +/// a given section? +/// +/// * How to evaluate a given linker script expression? +/// +class Sema { +public: + /// From the linker script point of view, this class represents the minimum + /// set of information to uniquely identify an input section. + struct SectionKey { + StringRef archivePath; + StringRef memberPath; + StringRef sectionName; + }; + + Sema(); + + /// We can parse several linker scripts via command line whose ASTs are stored + /// here via addLinkerScript(). + void addLinkerScript(std::unique_ptr script) { + _scripts.push_back(std::move(script)); + } + + const std::vector> &getLinkerScripts() { + return _scripts; + } + + /// Prepare our data structures according to the linker scripts currently in + /// our control (control given via addLinkerScript()). Called once all linker + /// scripts have been parsed. + void perform(); + + /// Answer if we have layout commands (section mapping rules). If we don't, + /// the output file writer can assume there is no linker script special rule + /// to handle. + bool hasLayoutCommands() const { return _layoutCommands.size() > 0; } + + /// Return true if this section has a mapping rule in the linker script + bool hasMapping(const SectionKey &key) const { + return getLayoutOrder(key, true) >= 0; + } + + /// Order function - used to sort input sections in the output file according + /// to linker script custom mappings. Return true if lhs should appear before + /// rhs. + bool less(const SectionKey &lhs, const SectionKey &rhs) const; + + /// Retrieve the name of the output section that this input section is mapped + /// to, according to custom linker script mappings. + StringRef getOutputSection(const SectionKey &key) const; + + /// Retrieve all the linker script expressions that need to be evaluated + /// before the given section is emitted. This is *not* const because the + /// first section to retrieve a given set of expression is the only one to + /// receive it. This set is marked as "delivered" and no other sections can + /// retrieve this set again. If we don't do this, multiple sections may map + /// to the same set of expressions because of wildcards rules. + std::vector getExprs(const SectionKey &key); + + /// Evaluate a single linker script expression according to our current + /// context (symbol table). This function is *not* constant because it can + /// update our symbol table with new symbols calculated in this expression. + std::error_code evalExpr(const SymbolAssignment *assgn, uint64_t &curPos); + + void dump() const; + +private: + /// A custom hash operator to teach the STL how to handle our custom keys. + /// This will be used in our hash table mapping Sections to a Layout Order + /// number (caching results). + struct SectionKeyHash { + int64_t operator()(const SectionKey &k) const { + return llvm::hash_combine(k.archivePath, k.memberPath, k.sectionName); + } + }; + + /// Teach the STL when two section keys are the same. This will be used in + /// our hash table mapping Sections to a Layout Order number (caching results) + struct SectionKeyEq { + bool operator()(const SectionKey &lhs, const SectionKey &rhs) const { + return ((lhs.archivePath == rhs.archivePath) && + (lhs.memberPath == rhs.memberPath) && + (lhs.sectionName == rhs.sectionName)); + } + }; + + /// Given an order id, check if it matches the tuple + /// and returns the + /// internal id that matched, or -1 if no matches. + int matchSectionName(int id, const SectionKey &key) const; + + /// Returns a number that will determine the order of this input section + /// in the final layout. If coarse is true, we simply return the layour order + /// of the higher-level node InputSectionsCmd, used to order input sections. + /// If coarse is false, we return the layout index down to the internal + /// InputSectionsCmd arrangement, used to get the set of preceding linker + ///expressions. + int getLayoutOrder(const SectionKey &key, bool coarse) const; + + /// Compare two sections that have the same mapping rule (i.e., are matched + /// by the same InputSectionsCmd). + /// Determine if lhs < rhs by analyzing the InputSectionsCmd structure. + bool localCompare(int order, const SectionKey &lhs, + const SectionKey &rhs) const; + + + /// Our goal with all linearizeAST overloaded functions is to + /// traverse the linker script AST while putting nodes in a vector and + /// thus enforcing order among nodes (which comes first). + /// + /// The order among nodes is determined by their indexes in this vector + /// (_layoutCommands). This index allows us to solve the problem of + /// establishing the order among two different input sections: we match each + /// input sections with their respective layout command and use the indexes + /// of these commands to order these sections. + /// + /// Example: + /// + /// Given the linker script: + /// SECTIONS { + /// .text : { *(.text) } + /// .data : { *(.data) } + /// } + /// + /// The _layoutCommands vector should contain: + /// id 0 : (_sectionName = ".text") + /// id 1 : (_memberName = "*") + /// id 2 : (_name = ".text) + /// id 3 : (_sectionName = ".data") + /// id 4 : (_memberName = "*") + /// id 5 : (_name = ".data") + /// + /// If we need to sort the following input sections: + /// + /// input section A: .text from libc.a (member errno.o) + /// input section B: .data from libc.a (member write.o) + /// + /// Then we match input section A with the InputSectionsCmd of id 1, and + /// input section B with the InputSectionsCmd of id 4. Since 1 < 4, we + /// put A before B. + /// + /// The second problem handled by the linearization of the AST is the task + /// of finding all preceding expressions that need to be calculated before + /// emitting a given section. This task is easier to deal with when all nodes + /// are in a vector because otherwise we would need to traverse multiple + /// levels of the AST to find the set of expressions that preceed a layout + /// command. + /// + /// The linker script commands that are linearized ("layout commands") are: + /// + /// * OutputSectionDescription, containing an output section name + /// * InputSectionsCmd, containing an input file name + /// * InputSectionName, containing a single input section name + /// * InputSectionSortedName, a group of input section names + /// * SymbolAssignment, containing an expression that may + /// change the address where the linker is outputting data + /// + void linearizeAST(const Sections *sections); + void linearizeAST(const InputSectionsCmd *inputSections); + void linearizeAST(const InputSection *inputSection); + + void perform(const LinkerScript *ls); + + std::vector> _scripts; + std::vector _layoutCommands; + std::unordered_multimap _memberToLayoutOrder; + std::vector> _memberNameWildcards; + mutable std::unordered_map + _cacheSectionOrder, _cacheExpressionOrder; + llvm::DenseSet _deliveredExprs; + + Expression::SymbolTableTy _symbolTable; +}; + llvm::BumpPtrAllocator &Command::getAllocator() const { return _ctx.getAllocator(); } llvm::BumpPtrAllocator &Expression::getAllocator() const { return _ctx.getAllocator(); } -llvm::BumpPtrAllocator &InputSection::getAllocator() const { - return _ctx.getAllocator(); -} } // end namespace script } // end namespace lld Index: lib/Driver/GnuLdDriver.cpp =================================================================== --- lib/Driver/GnuLdDriver.cpp +++ lib/Driver/GnuLdDriver.cpp @@ -310,7 +310,7 @@ ctx.setOutputPath(output->getOutputFileName()); } // Transfer ownership of the script to the linking context - ctx.addLinkerScript(std::move(parser)); + ctx.linkerScriptSema().addLinkerScript(std::move(parser)); return std::error_code(); } @@ -728,6 +728,9 @@ if (!ctx->validate(diag)) return false; + // Perform linker script semantic actions + ctx->linkerScriptSema().perform(); + context.swap(ctx); return true; } Index: lib/ReaderWriter/ELF/Chunk.h =================================================================== --- lib/ReaderWriter/ELF/Chunk.h +++ lib/ReaderWriter/ELF/Chunk.h @@ -39,7 +39,8 @@ SectionHeader, ///< Section header ELFSegment, ///< Segment ELFSection, ///< Section - AtomSection ///< A section containing atoms. + AtomSection, ///< A section containing atoms. + Expression ///< A linker script expression }; /// \brief the ContentType of the chunk enum ContentType : uint8_t{ Unknown, Header, Code, Data, Note, TLS }; Index: lib/ReaderWriter/ELF/DefaultLayout.h =================================================================== --- lib/ReaderWriter/ELF/DefaultLayout.h +++ lib/ReaderWriter/ELF/DefaultLayout.h @@ -169,7 +169,8 @@ typedef llvm::DenseSet AtomSetT; - DefaultLayout(const ELFLinkingContext &context) : _context(context) {} + DefaultLayout(ELFLinkingContext &context) + : _context(context), _linkerScriptSema(context.linkerScriptSema()) {} /// \brief Return the section order for a input section SectionOrder getSectionOrder(StringRef name, int32_t contentType, @@ -180,13 +181,15 @@ virtual StringRef getInputSectionName(const DefinedAtom *da) const; /// \brief Return the name of the output section from the input section. - virtual StringRef getOutputSectionName(StringRef inputSectionName) const; + virtual StringRef getOutputSectionName(StringRef archivePath, + StringRef memberPath, + StringRef inputSectionName) const; /// \brief Gets or creates a section. AtomSection * getSection(StringRef name, int32_t contentType, DefinedAtom::ContentPermissions contentPermissions, - StringRef path); + const DefinedAtom *da); /// \brief Gets the segment for a output section virtual Layout::SegmentType getSegmentType(Section *section) const; @@ -215,6 +218,18 @@ // Output sections with the same name into a OutputSection void createOutputSections(); + /// \brief Sort the sections by their order as defined by the layout, + /// preparing all sections to be assigned to a segment. + virtual void sortInputSections(); + + /// \brief Add extra chunks to a segment just before including the input + /// section given by . This + /// is used to add linker script expressions before each section. + virtual void addExtraChunksToSegment(Segment *segment, + StringRef archivePath, + StringRef memberPath, + StringRef sectionName); + void assignSectionsToSegments() override; void assignVirtualAddress() override; @@ -323,7 +338,8 @@ std::vector _absoluteAtoms; AtomSetT _referencedDynAtoms; llvm::StringSet<> _copiedDynSymNames; - const ELFLinkingContext &_context; + ELFLinkingContext &_context; + script::Sema &_linkerScriptSema; }; template @@ -415,7 +431,15 @@ /// \brief This maps the input sections to the output section names. template StringRef -DefaultLayout::getOutputSectionName(StringRef inputSectionName) const { +DefaultLayout::getOutputSectionName(StringRef archivePath, + StringRef memberPath, + StringRef inputSectionName) const { + StringRef outputSectionName; + if (_linkerScriptSema.hasLayoutCommands() && + !(outputSectionName = _linkerScriptSema.getOutputSection( + {archivePath, memberPath, inputSectionName})).empty()) + return outputSectionName; + return llvm::StringSwitch(inputSectionName) .StartsWith(".text", ".text") .StartsWith(".ctors", ".ctors") @@ -533,17 +557,20 @@ AtomSection * DefaultLayout::getSection(StringRef sectionName, int32_t contentType, DefinedAtom::ContentPermissions permissions, - StringRef path) { - const SectionKey sectionKey(sectionName, permissions, path); - SectionOrder sectionOrder = - getSectionOrder(sectionName, contentType, permissions); + const DefinedAtom *da) { + const SectionKey sectionKey(sectionName, permissions, da->file().path()); + SectionOrder sectionOrder = getSectionOrder(sectionName, contentType, permissions); auto sec = _sectionMap.find(sectionKey); if (sec != _sectionMap.end()) return sec->second; AtomSection *newSec = createSection(sectionName, contentType, permissions, sectionOrder); - newSec->setOutputSectionName(getOutputSectionName(sectionName)); + + newSec->setOutputSectionName(getOutputSectionName( + da->file().archivePath(), da->file().memberPath(), sectionName)); newSec->setOrder(sectionOrder); + newSec->setArchiveNameOrPath(da->file().archivePath()); + newSec->setMemberNameOrPath(da->file().memberPath()); _sections.push_back(newSec); _sectionMap.insert(std::make_pair(sectionKey, newSec)); return newSec; @@ -563,8 +590,8 @@ const DefinedAtom::ContentType contentType = definedAtom->contentType(); StringRef sectionName = getInputSectionName(definedAtom); - AtomSection *section = getSection( - sectionName, contentType, permissions, definedAtom->file().path()); + AtomSection *section = + getSection(sectionName, contentType, permissions, definedAtom); // Add runtime relocations to the .rela section. for (const auto &reloc : *definedAtom) { @@ -632,10 +659,7 @@ ScopedTask task(getDefaultDomain(), "assignSectionsToSegments"); ELFLinkingContext::OutputMagic outputMagic = _context.getOutputMagic(); // sort the sections by their order as defined by the layout - std::stable_sort(_sections.begin(), _sections.end(), - [](Chunk *A, Chunk *B) { - return A->order() < B->order(); - }); + sortInputSections(); // Create output sections. createOutputSections(); // Set the ordinal after sorting the sections @@ -686,8 +710,8 @@ if (!additionalSegmentInsert.second) { segment = additionalSegmentInsert.first->second; } else { - segment = new (_allocator) Segment(_context, segmentName, - segmentType); + segment = new (_allocator) + Segment(_context, segmentName, segmentType); additionalSegmentInsert.first->second = segment; _segments.push_back(segment); } @@ -713,11 +737,16 @@ if (!segmentInsert.second) { segment = segmentInsert.first->second; } else { - segment = new (_allocator) Segment(_context, "PT_LOAD", - llvm::ELF::PT_LOAD); + segment = new (_allocator) + Segment(_context, "PT_LOAD", llvm::ELF::PT_LOAD); segmentInsert.first->second = segment; _segments.push_back(segment); } + // Insert chunks with linker script expressions that occur at this + // point, just before appending a new input section + addExtraChunksToSegment(segment, section->archivePath(), + section->memberPath(), + section->inputSectionName()); segment->append(section); } } @@ -754,6 +783,7 @@ break; } } + assert(firstLoadSegment != nullptr && "No loadable segment!"); firstLoadSegment->prepend(_programHeader); firstLoadSegment->prepend(_elfHeader); bool newSegmentHeaderAdded = true; @@ -870,6 +900,86 @@ fileoffset += si->fileSize(); } } + +template void DefaultLayout::sortInputSections() { + // First, sort according to default layout's order + std::stable_sort( + _sections.begin(), _sections.end(), + [](Chunk *A, Chunk *B) { return A->order() < B->order(); }); + + if (!_linkerScriptSema.hasLayoutCommands()) + return; + + // Sort the sections by their order as defined by the linker script + std::stable_sort(this->_sections.begin(), this->_sections.end(), + [this](Chunk *A, Chunk *B) { + auto *a = dyn_cast>(A); + auto *b = dyn_cast>(B); + + if (a == nullptr) + return false; + if (b == nullptr) + return true; + + return _linkerScriptSema.less( + {a->archivePath(), a->memberPath(), + a->inputSectionName()}, + {b->archivePath(), b->memberPath(), + b->inputSectionName()}); + }); + // Now try to arrange sections with no mapping rules to sections with + // similar content + auto p = this->_sections.begin(); + // Find first section that has no assigned rule id + while (p != this->_sections.end()) { + auto *sect = dyn_cast>(*p); + if (!sect) + break; + + if (!_linkerScriptSema.hasMapping({sect->archivePath(), + sect->memberPath(), + sect->inputSectionName()})) + break; + + ++p; + } + // For all sections that have no assigned rule id, try to move them near a + // section with similar contents + if (p != this->_sections.begin()) { + for (; p != this->_sections.end(); ++p) { + auto q = p; + --q; + while (q != this->_sections.begin() && + (*q)->getContentType() != (*p)->getContentType()) + --q; + if ((*q)->getContentType() != (*p)->getContentType()) + continue; + ++q; + for (auto i = p; i != q;) { + auto next = i--; + std::iter_swap(i, next); + } + } + } +} + +template +void DefaultLayout::addExtraChunksToSegment(Segment *segment, + StringRef archivePath, + StringRef memberPath, + StringRef sectionName) { + if (!_linkerScriptSema.hasLayoutCommands()) + return; + + std::vector exprs = + _linkerScriptSema.getExprs({archivePath, memberPath, sectionName}); + for (auto expr : exprs) { + auto expChunk = + new (this->_allocator) ExpressionChunk(this->_context, expr); + segment->append(expChunk); + } +} + } // end namespace elf } // end namespace lld Index: lib/ReaderWriter/ELF/ELFLinkingContext.cpp =================================================================== --- lib/ReaderWriter/ELF/ELFLinkingContext.cpp +++ lib/ReaderWriter/ELF/ELFLinkingContext.cpp @@ -47,7 +47,7 @@ _mergeRODataToTextSegment(true), _demangle(true), _stripSymbols(false), _alignSegments(true), _collectStats(false), _outputMagic(OutputMagic::DEFAULT), _initFunction("_init"), - _finiFunction("_fini"), _sysrootPath("") {} + _finiFunction("_fini"), _sysrootPath(""), _linkerScriptSema() {} void ELFLinkingContext::addPasses(PassManager &pm) { pm.add(llvm::make_unique()); Index: lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h =================================================================== --- lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h +++ lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h @@ -29,7 +29,7 @@ ORDER_SDATA = 205 }; - HexagonTargetLayout(const HexagonLinkingContext &hti) + HexagonTargetLayout(HexagonLinkingContext &hti) : TargetLayout(hti), _sdataSection(nullptr), _gotSymAtom(nullptr), _cachedGotSymAtom(false) { _sdataSection = new (_alloc) SDataSection(hti); Index: lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h =================================================================== --- lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h +++ lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h @@ -26,7 +26,7 @@ template class MipsTargetLayout final : public TargetLayout { public: - MipsTargetLayout(const MipsLinkingContext &ctx) + MipsTargetLayout(MipsLinkingContext &ctx) : TargetLayout(ctx), _gotSection(new (_alloc) MipsGOTSection(ctx)), _pltSection(new (_alloc) MipsPLTSection(ctx)) {} Index: lib/ReaderWriter/ELF/SectionChunks.h =================================================================== --- lib/ReaderWriter/ELF/SectionChunks.h +++ lib/ReaderWriter/ELF/SectionChunks.h @@ -123,6 +123,14 @@ _outputSectionName = outputSectionName; } + void setArchiveNameOrPath(StringRef name) { _archivePath = name; } + + void setMemberNameOrPath(StringRef name) { _memberPath = name; } + + StringRef archivePath() { return _archivePath; } + + StringRef memberPath() { return _memberPath; } + protected: /// \brief OutputSection this Section is a member of, or nullptr. OutputSection *_outputSection; @@ -144,6 +152,8 @@ StringRef _inputSectionName; /// \brief Output section name. StringRef _outputSectionName; + StringRef _archivePath; + StringRef _memberPath; }; /// \brief A section containing atoms. Index: lib/ReaderWriter/ELF/SegmentChunks.h =================================================================== --- lib/ReaderWriter/ELF/SegmentChunks.h +++ lib/ReaderWriter/ELF/SegmentChunks.h @@ -119,7 +119,7 @@ /// append a chunk to a segment, this function /// is used by the ProgramHeader segment - virtual void append(Chunk *chunk) {} + virtual void append(Chunk *chunk) { _sections.push_back(chunk); } /// Sort segments depending on the property /// If we have a Program Header segment, it should appear first @@ -273,6 +273,37 @@ llvm::BumpPtrAllocator _segmentAllocate; }; +/// This chunk represents a linker script expression that needs to be calculated +/// at the time the virtual addresses for the parent segment are being assigned. +template class ExpressionChunk : public Chunk { +public: + ExpressionChunk(ELFLinkingContext &ctx, const script::SymbolAssignment *expr) + : Chunk(StringRef(), Chunk::Kind::Expression, ctx), + _expr(expr), _linkerScriptSema(ctx.linkerScriptSema()) { + this->_alignment = 1; + } + + static bool classof(const Chunk *c) { + return c->kind() == Chunk::Kind::Expression; + } + + int getContentType() const override { + return Chunk::ContentType::Unknown; + } + void write(ELFWriter *, TargetLayout &, + llvm::FileOutputBuffer &) override {} + void doPreFlight() override {} + void finalize() override {} + + std::error_code evalExpr(uint64_t &curPos) { + return _linkerScriptSema.evalExpr(_expr, curPos); + } + +private: + const script::SymbolAssignment *_expr; + script::Sema &_linkerScriptSema; +}; + /// \brief A Program Header segment contains a set of chunks instead of sections /// The segment doesn't contain any slice template class ProgramHeaderSegment : public Segment { @@ -394,11 +425,16 @@ bool isDataPageAlignedForNMagic = false; bool alignSegments = this->_context.alignSegments(); uint64_t p_align = this->_context.getPageSize(); + uint64_t lastVirtualAddress = 0; this->setFileOffset(startOffset); for (auto &slice : slices()) { bool isFirstSection = true; for (auto section : slice->sections()) { + // Handle linker script expressions, which may change the offset + if (!isFirstSection) + if (auto expr = dyn_cast>(section)) + fileOffset += expr->virtualAddr() - lastVirtualAddress; // Align fileoffset to the alignment of the section. fileOffset = llvm::RoundUpToAlignment(fileOffset, section->alignment()); // If the linker outputmagic is set to OutputMagic::NMAGIC, align the Data @@ -433,6 +469,7 @@ } section->setFileOffset(fileOffset); fileOffset += section->fileSize(); + lastVirtualAddress = section->virtualAddr() + section->memSize(); } slice->setFileSize(fileOffset - curSliceFileOffset); } @@ -461,7 +498,7 @@ SegmentSlice *slice = nullptr; uint64_t tlsStartAddr = 0; bool alignSegments = this->_context.alignSegments(); - StringRef prevOutputSectionName; + StringRef prevOutputSectionName = StringRef(); for (auto si = _sections.begin(); si != _sections.end(); ++si) { // If this is first section in the segment, page align the section start @@ -485,6 +522,10 @@ } // align the startOffset to the section alignment uint64_t newAddr = llvm::RoundUpToAlignment(startAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast>(*si)) + expr->evalExpr(newAddr); curSliceAddress = newAddr; sliceAlign = (*si)->alignment(); (*si)->setVirtualAddr(curSliceAddress); @@ -517,9 +558,22 @@ isDataPageAlignedForNMagic = true; } uint64_t newAddr = llvm::RoundUpToAlignment(curAddr, (*si)->alignment()); + // Handle linker script expressions, which *may update newAddr* if the + // expression assigns to "." + if (auto expr = dyn_cast>(*si)) + expr->evalExpr(newAddr); Section *sec = dyn_cast>(*si); - StringRef curOutputSectionName = - sec ? sec->outputSectionName() : (*si)->name(); + StringRef curOutputSectionName; + if (sec) + curOutputSectionName = sec->outputSectionName(); + else { + // If this is a linker script expression, propagate the name of the + // previous section instead + if (isa>(*si)) + curOutputSectionName = prevOutputSectionName; + else + curOutputSectionName = (*si)->name(); + } bool autoCreateSlice = true; if (curOutputSectionName == prevOutputSectionName) autoCreateSlice = false; Index: lib/ReaderWriter/ELF/TargetLayout.h =================================================================== --- lib/ReaderWriter/ELF/TargetLayout.h +++ lib/ReaderWriter/ELF/TargetLayout.h @@ -20,8 +20,7 @@ /// be changed in the final layout template class TargetLayout : public DefaultLayout { public: - TargetLayout(const ELFLinkingContext &context) - : DefaultLayout(context) {} + TargetLayout(ELFLinkingContext &context) : DefaultLayout(context) {} }; } // end namespace elf } // end namespace lld Index: lib/ReaderWriter/LinkerScript.cpp =================================================================== --- lib/ReaderWriter/LinkerScript.cpp +++ lib/ReaderWriter/LinkerScript.cpp @@ -823,7 +823,7 @@ os << "KEEP("; int numParen = dumpSortDirectives(os, _fileSortMode); - os << _fileName; + os << _memberName; for (int i = 0; i < numParen; ++i) os << ")"; @@ -1635,7 +1635,7 @@ bool keep = false; WildcardSortMode fileSortMode = WildcardSortMode::NA; WildcardSortMode archiveSortMode = WildcardSortMode::NA; - StringRef fileName; + StringRef memberName; StringRef archiveName; if (_tok._kind == Token::kw_keep) { @@ -1651,7 +1651,7 @@ int numParen = parseSortDirectives(fileSortMode); if (numParen == -1) return nullptr; - fileName = _tok._range; + memberName = _tok._range; consumeToken(); if (numParen) { while (numParen--) @@ -1681,7 +1681,7 @@ if (_tok._kind != Token::l_paren) return new (_alloc) - InputSectionsCmd(*this, fileName, archiveName, keep, fileSortMode, + InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode, archiveSortMode, inputSections); consumeToken(); @@ -1725,7 +1725,7 @@ if (!expectAndConsume(Token::r_paren, "expected )")) return nullptr; return new (_alloc) - InputSectionsCmd(*this, fileName, archiveName, keep, fileSortMode, + InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode, archiveSortMode, inputSections); } @@ -1975,5 +1975,398 @@ return new (_alloc) Sections(*this, sectionsCommands); } -} // end namespace script +// Sema member functions +Sema::Sema() + : _scripts(), _layoutCommands(), _memberToLayoutOrder(), + _memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(), + _deliveredExprs(), _symbolTable() {} + +void Sema::perform() { + for (auto &parser : _scripts) + perform(parser->get()); +} + +bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const { + int a = getLayoutOrder(lhs, true); + int b = getLayoutOrder(rhs, true); + + if (a != b) { + if (a < 0) + return false; + if (b < 0) + return true; + return a < b; + } + + // If both sections are not mapped anywhere, they have the same order + if (a < 0) + return false; + + // If both sections fall into the same layout order, we need to find their + // relative position as written in the (InputSectionsCmd). + return localCompare(a, lhs, rhs); +} + +StringRef Sema::getOutputSection(const SectionKey &key) const { + int layoutOrder = getLayoutOrder(key, true); + if (layoutOrder < 0) + return StringRef(); + + for (int i = layoutOrder - 1; i >= 0; --i) { + if (!isa(_layoutCommands[i])) + continue; + + const OutputSectionDescription *out = + dyn_cast(_layoutCommands[i]); + return out->name(); + } + + return StringRef(); +} + +std::vector +Sema::getExprs(const SectionKey &key) { + int layoutOrder = getLayoutOrder(key, false); + auto ans = std::vector(); + + if (layoutOrder < 0 || _deliveredExprs.count(layoutOrder) > 0) + return ans; + + for (int i = layoutOrder - 1; i >= 0; --i) { + if (isa(_layoutCommands[i])) + break; + if (auto assgn = dyn_cast(_layoutCommands[i])) + ans.push_back(assgn); + } + + // Mark this layout number as delivered + _deliveredExprs.insert(layoutOrder); + return ans; +} + +std::error_code Sema::evalExpr(const SymbolAssignment *assgn, + uint64_t &curPos) { + _symbolTable[StringRef(".")] = curPos; + + auto ans = assgn->expr()->evalExpr(_symbolTable); + if (ans.getError()) + return ans.getError(); + uint64_t result = *ans; + + if (assgn->symbol() == ".") { + curPos = result; + return std::error_code(); + } + + _symbolTable[assgn->symbol()] = result; + return std::error_code(); +} + +void Sema::dump() const { + raw_ostream &os = llvm::outs(); + os << "Linker script semantics dump\n"; + int num = 0; + for (auto &parser : _scripts) { + os << "Dumping script #" << ++num << ":\n"; + parser->get()->dump(os); + os << "\n"; + } + os << "Dumping rule ids:\n"; + for (unsigned i = 0; i < _layoutCommands.size(); ++i) { + os << "LayoutOrder " << i << ":\n"; + _layoutCommands[i]->dump(os); + os << "\n\n"; + } +} + +/// Given a string "pattern" with wildcard characters, return true if it +/// matches "name". This function is useful when checking if a given name +/// pattern written in the linker script, i.e. ".text*", should match +/// ".text.anytext". +static bool wildcardMatch(StringRef pattern, StringRef name) { + auto i = name.begin(); + + // Check if each char in pattern also appears in our input name, handling + // special wildcard characters. + for (auto j = pattern.begin(), e = pattern.end(); j != e; ++j) { + if (i == name.end()) + return false; + + switch (*j) { + case '*': + while (!wildcardMatch(pattern.drop_front(j - pattern.begin() + 1), + name.drop_front(i - name.begin() + 1))) { + if (i == name.end()) + return false; + ++i; + } + break; + case '?': + // Matches any character + break; + case '[': { + // Matches a range of characters specified between brackets + size_t end = pattern.find(']', j - pattern.begin()); + if (end == pattern.size()) + return false; + + StringRef chars = pattern.slice(j - pattern.begin(), end); + if (chars.find(i) == StringRef::npos) + return false; + + j = pattern.begin() + end; + break; + } + case '\\': + ++j; + if (*j != *i) + return false; + break; + default: + // No wildcard character means we must match exactly the same char + if (*j != *i) + return false; + break; + } + ++i; + } + + // If our pattern has't consumed the entire string, it is not a match + return i == name.end(); +} + +int Sema::matchSectionName(int id, const SectionKey &key) const { + const InputSectionsCmd *cmd = dyn_cast(_layoutCommands[id]); + + if (!cmd || !wildcardMatch(cmd->archiveName(), key.archivePath)) + return -1; + + while ((size_t)++id < _layoutCommands.size() && + (isa(_layoutCommands[id]))) { + if (isa(_layoutCommands[id])) + continue; + + const InputSectionName *in = + dyn_cast(_layoutCommands[id]); + if (wildcardMatch(in->name(), key.sectionName)) + return id; + } + return -1; +} + +int Sema::getLayoutOrder(const SectionKey &key, bool coarse) const { + // First check if we already answered this layout question + if (coarse) { + auto entry = _cacheSectionOrder.find(key); + if (entry != _cacheSectionOrder.end()) + return entry->second; + } else { + auto entry = _cacheExpressionOrder.find(key); + if (entry != _cacheExpressionOrder.end()) + return entry->second; + } + + // Try to match exact file name + auto range = _memberToLayoutOrder.equal_range(key.memberPath); + for (auto I = range.first, E = range.second; I != E; ++I) { + int order = I->second; + int exprOrder = -1; + + if ((exprOrder = matchSectionName(order, key)) >= 0) { + if (coarse) { + _cacheSectionOrder.insert(std::make_pair(key, order)); + return order; + } + _cacheExpressionOrder.insert(std::make_pair(key, exprOrder)); + return exprOrder; + } + } + + // If we still couldn't find a rule for this input section, try to match + // wildcards + for (auto I = _memberNameWildcards.begin(), E = _memberNameWildcards.end(); + I != E; ++I) { + if (!wildcardMatch(I->first, key.memberPath)) + continue; + int order = I->second; + int exprOrder = -1; + + if ((exprOrder = matchSectionName(order, key)) >= 0) { + if (coarse) { + _cacheSectionOrder.insert(std::make_pair(key, order)); + return order; + } + _cacheExpressionOrder.insert(std::make_pair(key, exprOrder)); + return exprOrder; + } + } + + _cacheSectionOrder.insert(std::make_pair(key, -1)); + _cacheExpressionOrder.insert(std::make_pair(key, -1)); + return -1; +} + +static bool compareSortedNames(WildcardSortMode sortMode, StringRef lhs, + StringRef rhs) { + switch (sortMode) { + case WildcardSortMode::None: + case WildcardSortMode::NA: + return false; + case WildcardSortMode::ByAlignment: + case WildcardSortMode::ByInitPriority: + case WildcardSortMode::ByAlignmentAndName: + assert(false && "Unimplemented sort order"); + break; + case WildcardSortMode::ByName: + return lhs.compare(rhs) < 0; + case WildcardSortMode::ByNameAndAlignment: + int compare = lhs.compare(rhs); + if (compare != 0) + return compare < 0; + return compareSortedNames(WildcardSortMode::ByAlignment, lhs, rhs); + } + return false; +} + +static bool sortedGroupContains(const InputSectionSortedGroup *cmd, + const Sema::SectionKey &key) { + for (const InputSection *child : *cmd) { + if (auto i = dyn_cast(child)) { + if (wildcardMatch(i->name(), key.sectionName)) + return true; + continue; + } + + auto *sortedGroup = dyn_cast(child); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + if (sortedGroupContains(sortedGroup, key)) + return true; + } + + return false; +} + +bool Sema::localCompare(int order, const SectionKey &lhs, + const SectionKey &rhs) const { + const InputSectionsCmd *cmd = + dyn_cast(_layoutCommands[order]); + + assert(cmd && "Invalid InputSectionsCmd index"); + + if (lhs.archivePath != rhs.archivePath) + return compareSortedNames(cmd->archiveSortMode(), lhs.archivePath, + rhs.archivePath); + + if (lhs.memberPath != rhs.memberPath) + return compareSortedNames(cmd->fileSortMode(), lhs.memberPath, + rhs.memberPath); + + // Both sections come from the same exact same file and rule. Start walking + // through input section names as written in the linker script and the + // first one to match will have higher priority. + for (const InputSection *inputSection : *cmd) { + if (auto i = dyn_cast(inputSection)) { + // If both match, return false (both have equal priority) + // If rhs match, return false (rhs has higher priority) + if (wildcardMatch(i->name(), rhs.sectionName)) + return false; + // If lhs matches first, it has priority over rhs + if (wildcardMatch(i->name(), lhs.sectionName)) + return true; + continue; + } + + // Handle sorted subgroups specially + auto *sortedGroup = dyn_cast(inputSection); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + bool a = sortedGroupContains(sortedGroup, lhs); + bool b = sortedGroupContains(sortedGroup, rhs); + if (a && !b) + return false; + if (b && !a) + return true; + if (!a && !a) + continue; + + return compareSortedNames(sortedGroup->sortMode(), lhs.sectionName, + rhs.sectionName); + } + + llvm_unreachable(""); + return false; +} + +static bool hasWildcard(StringRef name) { + for (auto ch : name) + if (ch == '*' || ch == '?' || ch == '[' || ch == '\\') + return true; + return false; +} + +void Sema::linearizeAST(const InputSection *inputSection) { + if (isa(inputSection)) { + _layoutCommands.push_back(inputSection); + return; + } + + auto *sortedGroup = dyn_cast(inputSection); + assert(sortedGroup && "Expected InputSectionSortedGroup object"); + + for (const InputSection *child : *sortedGroup) { + linearizeAST(child); + } +} + +void Sema::linearizeAST(const InputSectionsCmd *inputSections) { + StringRef memberName = inputSections->memberName(); + // Populate our maps for fast lookup of InputSectionsCmd + if (hasWildcard(memberName)) + _memberNameWildcards.push_back( + std::make_pair(memberName, (int)_layoutCommands.size())); + else if (!memberName.empty()) + _memberToLayoutOrder.insert( + std::make_pair(memberName.str(), (int)_layoutCommands.size())); + + _layoutCommands.push_back(inputSections); + for (const InputSection *inputSection : *inputSections) + linearizeAST(inputSection); +} + +void Sema::linearizeAST(const Sections *sections) { + for (const Command *sectionCommand : *sections) { + if (isa(sectionCommand)) { + _layoutCommands.push_back(sectionCommand); + continue; + } + + if (!isa(sectionCommand)) + continue; + + _layoutCommands.push_back(sectionCommand); + auto *outSection = dyn_cast(sectionCommand); + + for (const Command *outSecCommand : *outSection) { + if (isa(outSecCommand)) { + _layoutCommands.push_back(outSecCommand); + continue; + } + + if (!isa(outSecCommand)) + continue; + + linearizeAST(dyn_cast(outSecCommand)); + } + } +} + +void Sema::perform(const LinkerScript *ls) { + for (const Command *c : ls->_commands) { + if (const Sections *sec = dyn_cast(c)) + linearizeAST(sec); + } +} + +} // End namespace script } // end namespace lld Index: test/elf/linkerscript/Inputs/prog1.o.yaml =================================================================== --- /dev/null +++ test/elf/linkerscript/Inputs/prog1.o.yaml @@ -0,0 +1,88 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E5B000E800000000BF01000000BA0E0000004889C6E80000000031C05DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000007 + Symbol: prog2 + Type: R_X86_64_PC32 + Addend: -4 + - Offset: 0x0000000000000019 + Symbol: write + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E302028687474703A2F2F6C6C766D2E6F72672F6769742F636C616E672E6769742036336134646334616430343938646139623934386330383263623735336430353735323938346638292028687474703A2F2F6C6C766D2E6F72672F6769742F6C6C766D2E67697420623838363135326664656538376564653738613565643965616638663664313839343033616266312900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000002100000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: main + Type: STT_FUNC + Section: .text + Size: 0x0000000000000021 + - Name: prog2 + - Name: write +... Index: test/elf/linkerscript/Inputs/prog2.o.yaml =================================================================== --- /dev/null +++ test/elf/linkerscript/Inputs/prog2.o.yaml @@ -0,0 +1,89 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000010 + Content: 554889E548B800000000000000005DC3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000006 + Symbol: .rodata.str1.1 + Type: R_X86_64_64 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .rodata.str1.1 + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 48656C6C6F2C20776F726C64210A00 + - Name: .comment + Type: SHT_PROGBITS + Flags: [ SHF_MERGE, SHF_STRINGS ] + AddressAlign: 0x0000000000000001 + Content: 00636C616E672076657273696F6E20332E372E302028687474703A2F2F6C6C766D2E6F72672F6769742F636C616E672E6769742036336134646334616430343938646139623934386330383263623735336430353735323938346638292028687474703A2F2F6C6C766D2E6F72672F6769742F6C6C766D2E67697420623838363135326664656538376564653738613565643965616638663664313839343033616266312900 + - Name: .note.GNU-stack + Type: SHT_PROGBITS + AddressAlign: 0x0000000000000001 + Content: '' + - Name: .eh_frame + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC ] + AddressAlign: 0x0000000000000008 + Content: 1400000000000000037A5200017810011B0C0708900100001C0000001C000000000000001000000000410E108602430D0600000000000000 + - Name: .rela.eh_frame + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .eh_frame + Relocations: + - Offset: 0x0000000000000020 + Symbol: .text + Type: R_X86_64_PC32 +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + - Name: .rodata.str1.1 + Type: STT_SECTION + Section: .rodata.str1.1 + - Name: .comment + Type: STT_SECTION + Section: .comment + - Name: .note.GNU-stack + Type: STT_SECTION + Section: .note.GNU-stack + - Name: .eh_frame + Type: STT_SECTION + Section: .eh_frame + Global: + - Name: prog2 + Type: STT_FUNC + Section: .text + Size: 0x0000000000000010 +... Index: test/elf/linkerscript/Inputs/prog3.o.yaml =================================================================== --- /dev/null +++ test/elf/linkerscript/Inputs/prog3.o.yaml @@ -0,0 +1,52 @@ +--- +FileHeader: + Class: ELFCLASS64 + Data: ELFDATA2LSB + OSABI: ELFOSABI_GNU + Type: ET_REL + Machine: EM_X86_64 +Sections: + - Name: .text + Type: SHT_PROGBITS + Flags: [ SHF_ALLOC, SHF_EXECINSTR ] + AddressAlign: 0x0000000000000004 + Content: B8010000000F05C3E800000000B83C0000000F05C3 + - Name: .rela.text + Type: SHT_RELA + Link: .symtab + AddressAlign: 0x0000000000000008 + Info: .text + Relocations: + - Offset: 0x0000000000000009 + Symbol: main + Type: R_X86_64_PC32 + Addend: -4 + - Name: .data + Type: SHT_PROGBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' + - Name: .bss + Type: SHT_NOBITS + Flags: [ SHF_WRITE, SHF_ALLOC ] + AddressAlign: 0x0000000000000004 + Content: '' +Symbols: + Local: + - Name: .text + Type: STT_SECTION + Section: .text + - Name: .data + Type: STT_SECTION + Section: .data + - Name: .bss + Type: STT_SECTION + Section: .bss + Global: + - Name: _start + Section: .text + Value: 0x0000000000000008 + - Name: write + Section: .text + - Name: main +... Index: test/elf/linkerscript/sections-order.test =================================================================== --- /dev/null +++ test/elf/linkerscript/sections-order.test @@ -0,0 +1,97 @@ +/* +Tests a simple linker script that changes the order of output sections and +also changes the address of output sections by using simple expressions. + +This test uses three X86-64 input objects, prog1.o, prog2.o and prog3.o, +which were created with the following C or assembly code: + +*** prog1.o: + +(command line clang -c prog1.c -o prog1.o) + +const char *prog2(); +void write(int, const char *, int); + +int main() { + write(1, prog2(), 14); +} + +*** prog2.o: + +(command line clang -c prog2.c -o prog2.o) + +const char *prog2() { + return "Hello, world!\n"; +} + +*** prog3.o: + +(command line clang -c prog3.S -o prog3.o) + + .globl write +write: + mov $1, %eax + syscall + ret + + .globl _start +_start: + call main + mov $60, %eax + syscall + ret + +We use the following linker script for this test: +*/ + +ENTRY(_start) + +SECTIONS +{ + . = 0x500000; + .text : { prog1.o(.text) } + .mystring : { prog2.o(.rodata.str1.1) } + . = . + 0x6000; + .text.2 : {prog3.o(.text) prog2.o(.text) } +} + +/* +RUN: mkdir -p %T +RUN: yaml2obj -format=elf %p/Inputs/prog1.o.yaml -o=%T/prog1.o +RUN: yaml2obj -format=elf %p/Inputs/prog2.o.yaml -o=%T/prog2.o +RUN: yaml2obj -format=elf %p/Inputs/prog3.o.yaml -o=%T/prog3.o +RUN: cd %T + +RUN: lld -flavor gnu -target x86_64 -T %s prog1.o prog2.o prog3.o \ +RUN: -static -o %t1 +RUN: llvm-readobj -s %t1 | FileCheck -check-prefix CHECKSECTIONS %s + +CHECKSECTIONS: Index: 1 +CHECKSECTIONS: Name: .text +CHECKSECTIONS: Address: 0x500000 +CHECKSECTIONS: Size: 33 + +CHECKSECTIONS: Index: 2 +CHECKSECTIONS: Name: .mystring +CHECKSECTIONS: Address: 0x500021 +CHECKSECTIONS: Size: 15 + +CHECKSECTIONS: Index: 3 +CHECKSECTIONS: Name: .text.2 +CHECKSECTIONS: Address: 0x506030 +CHECKSECTIONS: Size: 48 + +RUN: llvm-readobj -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + +CHECKSYMS: Name: main +CHECKSYMS-NEXT: Value: 0x500000 + +CHECKSYMS: Name: write +CHECKSYMS-NEXT: Value: 0x506030 + +CHECKSYMS: Name: _start +CHECKSYMS-NEXT: Value: 0x506038 + +CHECKSYMS: Name: prog2 +CHECKSYMS-NEXT: Value: 0x506050 +*/ Index: test/elf/linkerscript/sections-with-wildcards.test =================================================================== --- /dev/null +++ test/elf/linkerscript/sections-with-wildcards.test @@ -0,0 +1,84 @@ +/* +Tests a linker script that uses the SECTIONS command with rules containing +wildcards and simple SORT directives. + +This test uses three X86-64 input objects, prog1.o, prog2.o and prog3.o, +which were created with the following C or assembly code: + +*** prog1.o: + +(command line clang -c prog1.c -o prog1.o) + +const char *prog2(); +void write(int, const char *, int); + +int main() { + write(1, prog2(), 14); +} + +*** prog2.o: + +(command line clang -c prog2.c -o prog2.o) + +const char *prog2() { + return "Hello, world!\n"; +} + +*** prog3.o: + +(command line clang -c prog3.S -o prog3.o) + + .globl write +write: + mov $1, %eax + syscall + ret + + .globl _start +_start: + call main + mov $60, %eax + syscall + ret + +We use the following linker script for this test: +*/ + +ENTRY(_start) + +SECTIONS +{ + . = 0x500000; + .foo : { SORT(*)(.text .rodata*) } +} + +/* +RUN: mkdir -p %T +RUN: yaml2obj -format=elf %p/Inputs/prog1.o.yaml -o=%T/p1.o +RUN: yaml2obj -format=elf %p/Inputs/prog2.o.yaml -o=%T/p2.o +RUN: yaml2obj -format=elf %p/Inputs/prog3.o.yaml -o=%T/p3.o +RUN: cd %T + +RUN: lld -flavor gnu -target x86_64 -T %s p1.o p2.o p3.o \ +RUN: -static -o %t1 +RUN: llvm-readobj -s %t1 | FileCheck -check-prefix CHECKSECTIONS %s + +CHECKSECTIONS: Index: 1 +CHECKSECTIONS: Name: .foo +CHECKSECTIONS: Address: 0x500000 +CHECKSECTIONS: Size: 101 + +RUN: llvm-readobj -symbols %t1 | FileCheck -check-prefix CHECKSYMS %s + +CHECKSYMS: Name: main +CHECKSYMS-NEXT: Value: 0x500000 + +CHECKSYMS: Name: prog2 +CHECKSYMS-NEXT: Value: 0x500030 + +CHECKSYMS: Name: write +CHECKSYMS-NEXT: Value: 0x500050 + +CHECKSYMS: Name: _start +CHECKSYMS-NEXT: Value: 0x500058 +*/ Index: unittests/DriverTests/GnuLdDriverTest.cpp =================================================================== --- unittests/DriverTests/GnuLdDriverTest.cpp +++ unittests/DriverTests/GnuLdDriverTest.cpp @@ -246,9 +246,10 @@ parse("SECTIONS { symbol = 0x4000 + 0x40; \n" ". = (symbol >= 0x4040)? (0x5001 * 2 & 0xFFF0) << 1 : 0}"); - EXPECT_EQ((size_t)1, _ctx->scripts().size()); + EXPECT_EQ((size_t)1, _ctx->linkerScriptSema().getLinkerScripts().size()); - script::LinkerScript *ls = _ctx->scripts()[0]->get(); + script::LinkerScript *ls = + _ctx->linkerScriptSema().getLinkerScripts()[0]->get(); EXPECT_EQ((size_t)1, ls->_commands.size()); auto *secs = dyn_cast(*ls->_commands.begin());