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::Parser> script) {
- _scripts.push_back(std::move(script));
- }
-
- const std::vector<std::unique_ptr<script::Parser>> &scripts() const {
- return _scripts;
- }
-
// --wrap option.
void addWrapForSymbol(StringRef sym) { _wrapCalls.insert(sym); }
void setUndefinesResolver(std::unique_ptr<File> resolver);
+ script::Sema &linkerScriptSema() { return _linkerScriptSema; }
+
private:
ELFLinkingContext() = delete;
llvm::StringSet<> _wrapCalls;
std::map<std::string, uint64_t> _absoluteSymbols;
llvm::StringSet<> _dynamicallyExportedSymbols;
- std::vector<std::unique_ptr<script::Parser>> _scripts;
std::unique_ptr<File> _resolver;
+
+ // 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
#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"
#include "llvm/Support/raw_ostream.h"
#include <memory>
#include <system_error>
+#include <unordered_map>
#include <vector>
namespace lld {
Group,
Input,
InputSectionsCmd,
+ InputSectionName,
Memory,
Output,
OutputArch,
Overlay,
SearchDir,
Sections,
+ SortedGroup,
SymbolAssignment,
};
/// .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 {
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;
class InputSectionSortedGroup : public InputSection {
public:
+ typedef llvm::ArrayRef<const InputSection *>::const_iterator const_iterator;
+
InputSectionSortedGroup(Parser &ctx, WildcardSortMode sort,
const SmallVectorImpl<const InputSection *> §ions)
: InputSection(ctx, Kind::SortedGroup), _sortMode(sort) {
}
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<const InputSection *> _sections;
/// }
class InputSectionsCmd : public Command {
public:
+ typedef llvm::ArrayRef<const InputSection *>::const_iterator const_iterator;
typedef std::vector<const InputSection *> VectorTy;
- InputSectionsCmd(Parser &ctx, StringRef fileName, StringRef archiveName,
+ InputSectionsCmd(Parser &ctx, StringRef memberName, StringRef archiveName,
bool keep, WildcardSortMode fileSortMode,
WildcardSortMode archiveSortMode,
const SmallVectorImpl<const InputSection *> §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();
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;
public:
enum Constraint { C_None, C_OnlyIfRO, C_OnlyIfRW };
+ typedef llvm::ArrayRef<const Command *>::const_iterator const_iterator;
+
OutputSectionDescription(
Parser &ctx, StringRef sectionName, const Expression *address,
const Expression *align, const Expression *subAlign, const Expression *at,
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;
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<Parser> script) {
+ _scripts.push_back(std::move(script));
+ }
+
+ const std::vector<std::unique_ptr<Parser>> &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<const SymbolAssignment *> 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
+ /// <archivePath, memberPath, sectionName> 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 : <OutputSectionDescription> (_sectionName = ".text")
+ /// id 1 : <InputSectionsCmd> (_memberName = "*")
+ /// id 2 : <InputSectionName> (_name = ".text)
+ /// id 3 : <OutputSectionDescription> (_sectionName = ".data")
+ /// id 4 : <InputSectionsCmd> (_memberName = "*")
+ /// id 5 : <InputSectionName> (_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<std::unique_ptr<Parser>> _scripts;
+ std::vector<const Command *> _layoutCommands;
+ std::unordered_multimap<std::string, int> _memberToLayoutOrder;
+ std::vector<std::pair<StringRef, int>> _memberNameWildcards;
+ mutable std::unordered_map<SectionKey, int, SectionKeyHash, SectionKeyEq>
+ _cacheSectionOrder, _cacheExpressionOrder;
+ llvm::DenseSet<int> _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
}
}
// Transfer ownership of the script to the linking context
- ctx.addLinkerScript(std::move(parser));
+ ctx.linkerScriptSema().addLinkerScript(std::move(parser));
return std::error_code();
}
if (!ctx->validate(diag))
return false;
+ // Perform linker script semantic actions
+ ctx->linkerScriptSema().perform();
+
context.swap(ctx);
return true;
}
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 };
typedef llvm::DenseSet<const Atom *> 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,
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<ELFT> *
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<ELFT> *section) const;
// 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 <archivePath, memberPath, sectionName>. This
+ /// is used to add linker script expressions before each section.
+ virtual void addExtraChunksToSegment(Segment<ELFT> *segment,
+ StringRef archivePath,
+ StringRef memberPath,
+ StringRef sectionName);
+
void assignSectionsToSegments() override;
void assignVirtualAddress() override;
std::vector<lld::AtomLayout *> _absoluteAtoms;
AtomSetT _referencedDynAtoms;
llvm::StringSet<> _copiedDynSymNames;
- const ELFLinkingContext &_context;
+ ELFLinkingContext &_context;
+ script::Sema &_linkerScriptSema;
};
template <class ELFT>
/// \brief This maps the input sections to the output section names.
template <class ELFT>
StringRef
-DefaultLayout<ELFT>::getOutputSectionName(StringRef inputSectionName) const {
+DefaultLayout<ELFT>::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<StringRef>(inputSectionName)
.StartsWith(".text", ".text")
.StartsWith(".ctors", ".ctors")
AtomSection<ELFT> *
DefaultLayout<ELFT>::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<ELFT> *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;
const DefinedAtom::ContentType contentType = definedAtom->contentType();
StringRef sectionName = getInputSectionName(definedAtom);
- AtomSection<ELFT> *section = getSection(
- sectionName, contentType, permissions, definedAtom->file().path());
+ AtomSection<ELFT> *section =
+ getSection(sectionName, contentType, permissions, definedAtom);
// Add runtime relocations to the .rela section.
for (const auto &reloc : *definedAtom) {
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<ELFT> *A, Chunk<ELFT> *B) {
- return A->order() < B->order();
- });
+ sortInputSections();
// Create output sections.
createOutputSections();
// Set the ordinal after sorting the sections
if (!additionalSegmentInsert.second) {
segment = additionalSegmentInsert.first->second;
} else {
- segment = new (_allocator) Segment<ELFT>(_context, segmentName,
- segmentType);
+ segment = new (_allocator)
+ Segment<ELFT>(_context, segmentName, segmentType);
additionalSegmentInsert.first->second = segment;
_segments.push_back(segment);
}
if (!segmentInsert.second) {
segment = segmentInsert.first->second;
} else {
- segment = new (_allocator) Segment<ELFT>(_context, "PT_LOAD",
- llvm::ELF::PT_LOAD);
+ segment = new (_allocator)
+ Segment<ELFT>(_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);
}
}
break;
}
}
+ assert(firstLoadSegment != nullptr && "No loadable segment!");
firstLoadSegment->prepend(_programHeader);
firstLoadSegment->prepend(_elfHeader);
bool newSegmentHeaderAdded = true;
fileoffset += si->fileSize();
}
}
+
+template <class ELFT> void DefaultLayout<ELFT>::sortInputSections() {
+ // First, sort according to default layout's order
+ std::stable_sort(
+ _sections.begin(), _sections.end(),
+ [](Chunk<ELFT> *A, Chunk<ELFT> *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<ELFT> *A, Chunk<ELFT> *B) {
+ auto *a = dyn_cast<Section<ELFT>>(A);
+ auto *b = dyn_cast<Section<ELFT>>(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<AtomSection<ELFT>>(*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 <class ELFT>
+void DefaultLayout<ELFT>::addExtraChunksToSegment(Segment<ELFT> *segment,
+ StringRef archivePath,
+ StringRef memberPath,
+ StringRef sectionName) {
+ if (!_linkerScriptSema.hasLayoutCommands())
+ return;
+
+ std::vector<const script::SymbolAssignment *> exprs =
+ _linkerScriptSema.getExprs({archivePath, memberPath, sectionName});
+ for (auto expr : exprs) {
+ auto expChunk =
+ new (this->_allocator) ExpressionChunk<ELFT>(this->_context, expr);
+ segment->append(expChunk);
+ }
+}
+
} // end namespace elf
} // end namespace lld
_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<elf::OrderPass>());
ORDER_SDATA = 205
};
- HexagonTargetLayout(const HexagonLinkingContext &hti)
+ HexagonTargetLayout(HexagonLinkingContext &hti)
: TargetLayout<HexagonELFType>(hti), _sdataSection(nullptr),
_gotSymAtom(nullptr), _cachedGotSymAtom(false) {
_sdataSection = new (_alloc) SDataSection<HexagonELFType>(hti);
template <class ELFType>
class MipsTargetLayout final : public TargetLayout<ELFType> {
public:
- MipsTargetLayout(const MipsLinkingContext &ctx)
+ MipsTargetLayout(MipsLinkingContext &ctx)
: TargetLayout<ELFType>(ctx),
_gotSection(new (_alloc) MipsGOTSection<ELFType>(ctx)),
_pltSection(new (_alloc) MipsPLTSection<ELFType>(ctx)) {}
_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<ELFT> *_outputSection;
StringRef _inputSectionName;
/// \brief Output section name.
StringRef _outputSectionName;
+ StringRef _archivePath;
+ StringRef _memberPath;
};
/// \brief A section containing atoms.
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 ELFT> class ExpressionChunk : public Chunk<ELFT> {
+public:
+ ExpressionChunk(ELFLinkingContext &ctx, const script::SymbolAssignment *expr)
+ : Chunk<ELFT>(StringRef(), Chunk<ELFT>::Kind::Expression, ctx),
+ _expr(expr), _linkerScriptSema(ctx.linkerScriptSema()) {
+ this->_alignment = 1;
+ }
+
+ static bool classof(const Chunk<ELFT> *c) {
+ return c->kind() == Chunk<ELFT>::Kind::Expression;
+ }
+
+ int getContentType() const override {
+ return Chunk<ELFT>::ContentType::Unknown;
+ }
+ void write(ELFWriter *, TargetLayout<ELFT> &,
+ 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 ELFT> class ProgramHeaderSegment : public Segment<ELFT> {
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<ExpressionChunk<ELFT>>(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
}
section->setFileOffset(fileOffset);
fileOffset += section->fileSize();
+ lastVirtualAddress = section->virtualAddr() + section->memSize();
}
slice->setFileSize(fileOffset - curSliceFileOffset);
}
SegmentSlice<ELFT> *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
}
// 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<ExpressionChunk<ELFT>>(*si))
+ expr->evalExpr(newAddr);
curSliceAddress = newAddr;
sliceAlign = (*si)->alignment();
(*si)->setVirtualAddr(curSliceAddress);
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<ExpressionChunk<ELFT>>(*si))
+ expr->evalExpr(newAddr);
Section<ELFT> *sec = dyn_cast<Section<ELFT>>(*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<ExpressionChunk<ELFT>>(*si))
+ curOutputSectionName = prevOutputSectionName;
+ else
+ curOutputSectionName = (*si)->name();
+ }
bool autoCreateSlice = true;
if (curOutputSectionName == prevOutputSectionName)
autoCreateSlice = false;
/// be changed in the final layout
template <class ELFT> class TargetLayout : public DefaultLayout<ELFT> {
public:
- TargetLayout(const ELFLinkingContext &context)
- : DefaultLayout<ELFT>(context) {}
+ TargetLayout(ELFLinkingContext &context) : DefaultLayout<ELFT>(context) {}
};
} // end namespace elf
} // end namespace lld
os << "KEEP(";
int numParen = dumpSortDirectives(os, _fileSortMode);
- os << _fileName;
+ os << _memberName;
for (int i = 0; i < numParen; ++i)
os << ")";
bool keep = false;
WildcardSortMode fileSortMode = WildcardSortMode::NA;
WildcardSortMode archiveSortMode = WildcardSortMode::NA;
- StringRef fileName;
+ StringRef memberName;
StringRef archiveName;
if (_tok._kind == Token::kw_keep) {
int numParen = parseSortDirectives(fileSortMode);
if (numParen == -1)
return nullptr;
- fileName = _tok._range;
+ memberName = _tok._range;
consumeToken();
if (numParen) {
while (numParen--)
if (_tok._kind != Token::l_paren)
return new (_alloc)
- InputSectionsCmd(*this, fileName, archiveName, keep, fileSortMode,
+ InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode,
archiveSortMode, inputSections);
consumeToken();
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);
}
return new (_alloc) Extern(*this, symbols);
}
-} // 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<OutputSectionDescription>(_layoutCommands[i]))
+ continue;
+
+ const OutputSectionDescription *out =
+ dyn_cast<OutputSectionDescription>(_layoutCommands[i]);
+ return out->name();
+ }
+
+ return StringRef();
+}
+
+std::vector<const SymbolAssignment *>
+Sema::getExprs(const SectionKey &key) {
+ int layoutOrder = getLayoutOrder(key, false);
+ auto ans = std::vector<const SymbolAssignment *>();
+
+ if (layoutOrder < 0 || _deliveredExprs.count(layoutOrder) > 0)
+ return ans;
+
+ for (int i = layoutOrder - 1; i >= 0; --i) {
+ if (isa<InputSection>(_layoutCommands[i]))
+ break;
+ if (auto assgn = dyn_cast<SymbolAssignment>(_layoutCommands[i]))
+ ans.push_back(assgn);
+ }
+
+ // Reverse this order so we evaluate the expressions in the original order
+ // of the linker script
+ std::reverse(ans.begin(), ans.end());
+
+ // 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<InputSectionsCmd>(_layoutCommands[id]);
+
+ if (!cmd || !wildcardMatch(cmd->archiveName(), key.archivePath))
+ return -1;
+
+ while ((size_t)++id < _layoutCommands.size() &&
+ (isa<InputSection>(_layoutCommands[id]))) {
+ if (isa<InputSectionSortedGroup>(_layoutCommands[id]))
+ continue;
+
+ const InputSectionName *in =
+ dyn_cast<InputSectionName>(_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<InputSectionName>(child)) {
+ if (wildcardMatch(i->name(), key.sectionName))
+ return true;
+ continue;
+ }
+
+ auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(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<InputSectionsCmd>(_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<InputSectionName>(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<InputSectionSortedGroup>(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<InputSectionName>(inputSection)) {
+ _layoutCommands.push_back(inputSection);
+ return;
+ }
+
+ auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(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<SymbolAssignment>(sectionCommand)) {
+ _layoutCommands.push_back(sectionCommand);
+ continue;
+ }
+
+ if (!isa<OutputSectionDescription>(sectionCommand))
+ continue;
+
+ _layoutCommands.push_back(sectionCommand);
+ auto *outSection = dyn_cast<OutputSectionDescription>(sectionCommand);
+
+ for (const Command *outSecCommand : *outSection) {
+ if (isa<SymbolAssignment>(outSecCommand)) {
+ _layoutCommands.push_back(outSecCommand);
+ continue;
+ }
+
+ if (!isa<InputSectionsCmd>(outSecCommand))
+ continue;
+
+ linearizeAST(dyn_cast<InputSectionsCmd>(outSecCommand));
+ }
+ }
+}
+
+void Sema::perform(const LinkerScript *ls) {
+ for (const Command *c : ls->_commands) {
+ if (const Sections *sec = dyn_cast<Sections>(c))
+ linearizeAST(sec);
+ }
+}
+
+} // End namespace script
} // end namespace lld
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<const script::Sections>(*ls->_commands.begin());