[LinkerScript] Implement semantics for simple sections mappings
authorRafael Auler <rafaelauler@gmail.com>
Mon, 16 Mar 2015 19:55:15 +0000 (19:55 +0000)
committerRafael Auler <rafaelauler@gmail.com>
Mon, 16 Mar 2015 19:55:15 +0000 (19:55 +0000)
This commit implements the behaviour of the SECTIONS linker script directive,
used to not only define a custom mapping between input and output sections, but
also order input sections in the output file. To do this, we modify
DefaultLayout with hooks at important places that allow us to re-order input
sections according to a custom order. We also add a hook in SegmentChunk to
allow us to calculate linker script expressions while assigning virtual
addresses to the input sections that live in a segment.

Not all SECTIONS constructs are currently supported, but only the ones that do
not use special sort orders. It adds two LIT test as practical examples of
which sections directives are currently supported.

In terms of high-level changes, it creates a new class "script::Sema" that owns
all linker script ASTs and the logic for linker script semantics as well.
ELFLinkingContext owns a single copy of Sema, which will be used throughout
the object file writing process (to layout sections as proposed by the linker
script).

Other high-level change is that the writer no longer uses a "const" copy of
the linking context. This happens because linker script expressions must be
calculated *while* calculating final virtual addresses, which is a very late
step in object file writing. While calculating these expressions, we need to
update the linker script symbol table (inside the semantics object), and, thus,
we are "modifying our context" as we prepare to write the file.

http://reviews.llvm.org/D8157

llvm-svn: 232402

13 files changed:
lld/include/lld/ReaderWriter/ELFLinkingContext.h
lld/include/lld/ReaderWriter/LinkerScript.h
lld/lib/Driver/GnuLdDriver.cpp
lld/lib/ReaderWriter/ELF/Chunk.h
lld/lib/ReaderWriter/ELF/DefaultLayout.h
lld/lib/ReaderWriter/ELF/ELFLinkingContext.cpp
lld/lib/ReaderWriter/ELF/Hexagon/HexagonTargetHandler.h
lld/lib/ReaderWriter/ELF/Mips/MipsTargetHandler.h
lld/lib/ReaderWriter/ELF/SectionChunks.h
lld/lib/ReaderWriter/ELF/SegmentChunks.h
lld/lib/ReaderWriter/ELF/TargetLayout.h
lld/lib/ReaderWriter/LinkerScript.cpp
lld/unittests/DriverTests/GnuLdDriverTest.cpp

index 45a22fb..c8f85dc 100644 (file)
@@ -298,16 +298,6 @@ public:
   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); }
 
@@ -315,6 +305,8 @@ public:
 
   void setUndefinesResolver(std::unique_ptr<File> resolver);
 
+  script::Sema &linkerScriptSema() { return _linkerScriptSema; }
+
 private:
   ELFLinkingContext() = delete;
 
@@ -358,8 +350,11 @@ protected:
   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
 
index e4a8bf5..6eab12d 100644 (file)
@@ -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 <memory>
 #include <system_error>
+#include <unordered_map>
 #include <vector>
 
 namespace lld {
@@ -159,6 +162,7 @@ public:
     Group,
     Input,
     InputSectionsCmd,
+    InputSectionName,
     Memory,
     Output,
     OutputArch,
@@ -167,6 +171,7 @@ public:
     Overlay,
     SearchDir,
     Sections,
+    SortedGroup,
     SymbolAssignment,
   };
 
@@ -604,23 +609,15 @@ enum class WildcardSortMode {
 ///   .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 {
@@ -631,10 +628,11 @@ public:
 
   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;
@@ -643,6 +641,8 @@ private:
 
 class InputSectionSortedGroup : public InputSection {
 public:
+  typedef llvm::ArrayRef<const InputSection *>::const_iterator const_iterator;
+
   InputSectionSortedGroup(Parser &ctx, WildcardSortMode sort,
                           const SmallVectorImpl<const InputSection *> &sections)
       : InputSection(ctx, Kind::SortedGroup), _sortMode(sort) {
@@ -654,12 +654,15 @@ public:
   }
 
   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;
@@ -677,13 +680,14 @@ private:
 /// }
 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 *> &sections)
-      : 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();
@@ -699,8 +703,15 @@ public:
     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;
@@ -724,6 +735,8 @@ class OutputSectionDescription : public Command {
 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,
@@ -749,6 +762,10 @@ public:
 
   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;
@@ -1174,15 +1191,195 @@ private:
   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
 
index 7a0e633..b9af04d 100644 (file)
@@ -315,7 +315,7 @@ std::error_code GnuLdDriver::evalLinkerScript(ELFLinkingContext &ctx,
     }
   }
   // Transfer ownership of the script to the linking context
-  ctx.addLinkerScript(std::move(parser));
+  ctx.linkerScriptSema().addLinkerScript(std::move(parser));
   return std::error_code();
 }
 
@@ -734,6 +734,9 @@ bool GnuLdDriver::parse(int argc, const char *argv[],
   if (!ctx->validate(diag))
     return false;
 
+  // Perform linker script semantic actions
+  ctx->linkerScriptSema().perform();
+
   context.swap(ctx);
   return true;
 }
index 87688ef..2658d02 100644 (file)
@@ -39,7 +39,8 @@ public:
                        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 49ada59..13d2f5c 100644 (file)
@@ -169,7 +169,8 @@ public:
 
   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,
@@ -180,13 +181,15 @@ public:
   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;
@@ -215,6 +218,18 @@ public:
   // 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;
@@ -323,7 +338,8 @@ protected:
   std::vector<lld::AtomLayout *> _absoluteAtoms;
   AtomSetT _referencedDynAtoms;
   llvm::StringSet<> _copiedDynSymNames;
-  const ELFLinkingContext &_context;
+  ELFLinkingContext &_context;
+  script::Sema &_linkerScriptSema;
 };
 
 template <class ELFT>
@@ -415,7 +431,15 @@ DefaultLayout<ELFT>::getInputSectionName(const DefinedAtom *da) const {
 /// \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")
@@ -533,17 +557,20 @@ template <class ELFT>
 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;
@@ -563,8 +590,8 @@ DefaultLayout<ELFT>::addAtom(const Atom *atom) {
     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) {
@@ -632,10 +659,7 @@ template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() {
   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
@@ -686,8 +710,8 @@ template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() {
           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);
           }
@@ -713,11 +737,16 @@ template <class ELFT> void DefaultLayout<ELFT>::assignSectionsToSegments() {
         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);
       }
     }
@@ -754,6 +783,7 @@ DefaultLayout<ELFT>::assignVirtualAddress() {
       break;
     }
   }
+  assert(firstLoadSegment != nullptr && "No loadable segment!");
   firstLoadSegment->prepend(_programHeader);
   firstLoadSegment->prepend(_elfHeader);
   bool newSegmentHeaderAdded = true;
@@ -870,6 +900,86 @@ void DefaultLayout<ELFT>::assignFileOffsetsForMiscSections() {
     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
 
index 29388c2..c7dffda 100644 (file)
@@ -47,7 +47,7 @@ ELFLinkingContext::ELFLinkingContext(
       _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>());
index 7c6a9c5..f4315f7 100644 (file)
@@ -29,7 +29,7 @@ public:
     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);
index 8f3ea0f..023c7a8 100644 (file)
@@ -26,7 +26,7 @@ namespace elf {
 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)) {}
index f21698c..5363669 100644 (file)
@@ -123,6 +123,14 @@ public:
     _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;
@@ -144,6 +152,8 @@ protected:
   StringRef _inputSectionName;
   /// \brief Output section name.
   StringRef _outputSectionName;
+  StringRef _archivePath;
+  StringRef _memberPath;
 };
 
 /// \brief A section containing atoms.
index 75c8b6b..f2a975a 100644 (file)
@@ -269,6 +269,37 @@ protected:
   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> {
@@ -390,11 +421,16 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
   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
@@ -429,6 +465,7 @@ void Segment<ELFT>::assignFileOffsets(uint64_t startOffset) {
       }
       section->setFileOffset(fileOffset);
       fileOffset += section->fileSize();
+      lastVirtualAddress = section->virtualAddr() + section->memSize();
     }
     slice->setFileSize(fileOffset - curSliceFileOffset);
   }
@@ -457,7 +494,7 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
   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
@@ -481,6 +518,10 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
       }
       // 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);
@@ -513,9 +554,22 @@ template <class ELFT> void Segment<ELFT>::assignVirtualAddress(uint64_t addr) {
         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;
index 1329798..ab7a789 100644 (file)
@@ -20,8 +20,7 @@ namespace elf {
 /// 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
index e264c5c..34a7d4a 100644 (file)
@@ -835,7 +835,7 @@ void InputSectionsCmd::dump(raw_ostream &os) const {
     os << "KEEP(";
 
   int numParen = dumpSortDirectives(os, _fileSortMode);
-  os << _fileName;
+  os << _memberName;
   for (int i = 0; i < numParen; ++i)
     os << ")";
 
@@ -1699,7 +1699,7 @@ const InputSectionsCmd *Parser::parseInputSectionsCmd() {
   bool keep = false;
   WildcardSortMode fileSortMode = WildcardSortMode::NA;
   WildcardSortMode archiveSortMode = WildcardSortMode::NA;
-  StringRef fileName;
+  StringRef memberName;
   StringRef archiveName;
 
   if (_tok._kind == Token::kw_keep) {
@@ -1715,7 +1715,7 @@ const InputSectionsCmd *Parser::parseInputSectionsCmd() {
     int numParen = parseSortDirectives(fileSortMode);
     if (numParen == -1)
       return nullptr;
-    fileName = _tok._range;
+    memberName = _tok._range;
     consumeToken();
     if (numParen) {
       while (numParen--)
@@ -1745,7 +1745,7 @@ const InputSectionsCmd *Parser::parseInputSectionsCmd() {
 
   if (_tok._kind != Token::l_paren)
     return new (_alloc)
-        InputSectionsCmd(*this, fileName, archiveName, keep, fileSortMode,
+        InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode,
                          archiveSortMode, inputSections);
   consumeToken();
 
@@ -1789,7 +1789,7 @@ const InputSectionsCmd *Parser::parseInputSectionsCmd() {
     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);
 }
 
@@ -2141,5 +2141,402 @@ Extern *Parser::parseExtern() {
   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
index b0b867b..92eb920 100644 (file)
@@ -246,9 +246,10 @@ TEST_F(LinkerScriptTest, ExprEval) {
   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());