[lld] r221126 - [lld] Teach LLD how to parse most linker scripts

Rafael Auler rafaelauler at gmail.com
Sun Nov 2 20:09:51 PST 2014


Author: rafauler
Date: Sun Nov  2 22:09:51 2014
New Revision: 221126

URL: http://llvm.org/viewvc/llvm-project?rev=221126&view=rev
Log:
[lld] Teach LLD how to parse most linker scripts

This patch does *not* implement any semantic actions, but it is a first step to
teach LLD how to read complete linker scripts. The additional linker scripts
statements whose parsing is now supported are:

* SEARCH_DIR directive
* SECTIONS directive
* Symbol definitions inside SECTIONS including PROVIDE and PROVIDE_HIDDEN
* C-like expressions used in many places in linker scripts
* Input to output sections mapping

The goal of this commit was guided towards completely parsing a default GNU ld
linker script and the linker script used to link the FreeBSD kernel. Thus, it
also adds a test case based on the default linker script used in GNU ld for
x86_64 ELF targets. I tested SPEC userland programs linked by GNU ld, using the
linker script dump'ed by this parser, and everything went fine. I then tested
linking the FreeBSD kernel with a dump'ed linker script, installed the new
kernel and booted it, everything went fine.

Directives that still need to be implemented:
* MEMORY
* PHDRS

Reviewers: silvas, shankarke and ruiu

http://reviews.llvm.org/D5852


Added:
    lld/trunk/test/LinkerScript/expr-precedence.test
    lld/trunk/test/LinkerScript/incomplete-ternary.test
    lld/trunk/test/LinkerScript/missing-entry-symbol.test
    lld/trunk/test/LinkerScript/missing-input-file-name.test
    lld/trunk/test/LinkerScript/missing-input-sections.test
    lld/trunk/test/LinkerScript/missing-operand.test
    lld/trunk/test/LinkerScript/missing-output-section-name.test
    lld/trunk/test/LinkerScript/missing-symbol.test
    lld/trunk/test/LinkerScript/sections.test
Modified:
    lld/trunk/include/lld/ReaderWriter/LinkerScript.h
    lld/trunk/lib/ReaderWriter/LinkerScript.cpp
    lld/trunk/test/LinkerScript/linker-script-outputformat.test
    lld/trunk/test/LinkerScript/linker-script.test

Modified: lld/trunk/include/lld/ReaderWriter/LinkerScript.h
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/include/lld/ReaderWriter/LinkerScript.h?rev=221126&r1=221125&r2=221126&view=diff
==============================================================================
--- lld/trunk/include/lld/ReaderWriter/LinkerScript.h (original)
+++ lld/trunk/include/lld/ReaderWriter/LinkerScript.h Sun Nov  2 22:09:51 2014
@@ -33,16 +33,66 @@ public:
   enum Kind {
     unknown,
     eof,
-    identifier,
-    libname,
-    comma,
+    exclaim,
+    exclaimequal,
+    amp,
+    ampequal,
     l_paren,
     r_paren,
+    star,
+    starequal,
+    plus,
+    plusequal,
+    comma,
+    minus,
+    minusequal,
+    slash,
+    slashequal,
+    number,
+    colon,
+    semicolon,
+    less,
+    lessequal,
+    lessless,
+    lesslessequal,
+    equal,
+    equalequal,
+    greater,
+    greaterequal,
+    greatergreater,
+    greatergreaterequal,
+    question,
+    identifier,
+    libname,
+    kw_align,
+    kw_align_with_input,
+    kw_as_needed,
+    kw_at,
+    kw_discard,
     kw_entry,
+    kw_exclude_file,
     kw_group,
-    kw_output_format,
+    kw_hidden,
+    kw_keep,
+    kw_provide,
+    kw_provide_hidden,
+    kw_only_if_ro,
+    kw_only_if_rw,
     kw_output_arch,
-    kw_as_needed
+    kw_output_format,
+    kw_overlay,
+    kw_search_dir,
+    kw_sections,
+    kw_sort_by_alignment,
+    kw_sort_by_init_priority,
+    kw_sort_by_name,
+    kw_sort_none,
+    kw_subalign,
+    l_brace,
+    pipe,
+    pipeequal,
+    r_brace,
+    tilde
   };
 
   Token() : _kind(unknown) {}
@@ -56,8 +106,7 @@ public:
 
 class Lexer {
 public:
-  explicit Lexer(std::unique_ptr<MemoryBuffer> mb)
-      : _buffer(mb->getBuffer()) {
+  explicit Lexer(std::unique_ptr<MemoryBuffer> mb) : _buffer(mb->getBuffer()) {
     _sourceManager.AddNewSourceBuffer(std::move(mb), llvm::SMLoc());
   }
 
@@ -66,6 +115,8 @@ public:
   const llvm::SourceMgr &getSourceMgr() const { return _sourceManager; }
 
 private:
+  bool canStartNumber(char c) const;
+  bool canContinueNumber(char c) const;
   bool canStartName(char c) const;
   bool canContinueName(char c) const;
   void skipWhitespace();
@@ -77,9 +128,35 @@ private:
   llvm::SourceMgr _sourceManager;
 };
 
+/// All linker scripts commands derive from this class. High-level, sections and
+/// output section commands are all subclasses of this class.
+/// Examples:
+///
+/// OUTPUT_FORMAT("elf64-x86-64") /* A linker script command */
+/// OUTPUT_ARCH(i386:x86-64)      /* Another command */
+/// ENTRY(_start)                 /* Another command */
+///
+/// SECTIONS                      /* Another command */
+/// {
+///   .interp : {                 /* A sections-command */
+///              *(.interp)       /* An output-section-command */
+///              }
+///  }
+///
 class Command {
 public:
-  enum class Kind { Entry, OutputFormat, OutputArch, Group, };
+  enum class Kind {
+    Entry,
+    Group,
+    InputSectionsCmd,
+    OutputArch,
+    OutputFormat,
+    OutputSectionDescription,
+    Overlay,
+    SearchDir,
+    Sections,
+    SymbolAssignment,
+  };
 
   Kind getKind() const { return _kind; }
 
@@ -111,7 +188,7 @@ public:
       if (!first)
         os << ",";
       first = false;
-      os << format;
+      os << "\"" << format << "\"";
     }
     os << ")\n";
   }
@@ -134,7 +211,7 @@ public:
   }
 
   void dump(raw_ostream &os) const override {
-    os << "OUTPUT_arch(" << getArch() << ")\n";
+    os << "OUTPUT_ARCH(" << getArch() << ")\n";
   }
 
   StringRef getArch() const { return _arch; }
@@ -155,8 +232,7 @@ struct Path {
 
 class Group : public Command {
 public:
-  template <class RangeT>
-  explicit Group(RangeT range) : Command(Kind::Group) {
+  template <class RangeT> explicit Group(RangeT range) : Command(Kind::Group) {
     std::copy(std::begin(range), std::end(range), std::back_inserter(_paths));
   }
 
@@ -188,48 +264,495 @@ private:
 
 class Entry : public Command {
 public:
-  explicit Entry(StringRef entryName) :
-      Command(Kind::Entry), _entryName(entryName) { }
+  explicit Entry(StringRef entryName)
+      : Command(Kind::Entry), _entryName(entryName) {}
+
+  static bool classof(const Command *c) { return c->getKind() == Kind::Entry; }
+
+  void dump(raw_ostream &os) const override {
+    os << "ENTRY(" << _entryName << ")\n";
+  }
+
+  StringRef getEntryName() const { return _entryName; }
+
+private:
+  StringRef _entryName;
+};
+
+class SearchDir : public Command {
+public:
+  explicit SearchDir(StringRef searchPath)
+      : Command(Kind::SearchDir), _searchPath(searchPath) {}
 
   static bool classof(const Command *c) {
-    return c->getKind() == Kind::Entry;
+    return c->getKind() == Kind::SearchDir;
   }
 
   void dump(raw_ostream &os) const override {
-    os << "ENTRY(" << _entryName << ")\n";
+    os << "SEARCH_DIR(\"" << _searchPath << "\")\n";
   }
 
-  const StringRef getEntryName() const {
-    return _entryName;
+  StringRef getSearchPath() const { return _searchPath; }
+
+private:
+  StringRef _searchPath;
+};
+
+/// Superclass for expression nodes. Linker scripts accept C-like expressions in
+/// many places, such as when defining the value of a symbol or the address of
+/// an output section.
+/// Example:
+///
+/// SECTIONS {
+///   my_symbol = 1 + 1 * 2;
+///               | |     ^~~~> Constant : Expression
+///               | | ^~~~> Constant : Expression
+///               | |   ^~~~> BinOp : Expression
+///               ^~~~> Constant : Expression
+///                 ^~~~> BinOp : Expression  (the top-level Expression node)
+/// }
+///
+class Expression {
+public:
+  enum class Kind { Constant, Symbol, FunctionCall, Unary, BinOp,
+                    TernaryConditional };
+  Kind getKind() const { return _kind; }
+  virtual void dump(raw_ostream &os) const = 0;
+  virtual ~Expression() {}
+
+protected:
+  explicit Expression(Kind k) : _kind(k) {}
+
+private:
+  Kind _kind;
+};
+
+/// A constant value is stored as unsigned because it represents absolute
+/// values. We represent negative numbers by composing the unary '-' operator
+/// with a constant.
+class Constant : public Expression {
+public:
+  explicit Constant(uint64_t num) : Expression(Kind::Constant), _num(num) {}
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const Expression *c) {
+    return c->getKind() == Kind::Constant;
   }
 
 private:
-  StringRef _entryName;
+  uint64_t _num;
 };
 
+class Symbol : public Expression {
+public:
+  Symbol(StringRef name) : Expression(Kind::Symbol), _name(name) {}
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const Expression *c) {
+    return c->getKind() == Kind::Symbol;
+  }
+
+private:
+  StringRef _name;
+};
+
+class FunctionCall : public Expression {
+public:
+  template <class RangeT>
+  FunctionCall(StringRef name, RangeT range)
+      : Expression(Kind::FunctionCall), _name(name) {
+    std::copy(std::begin(range), std::end(range), std::back_inserter(_args));
+  }
+
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const Expression *c) {
+    return c->getKind() == Kind::FunctionCall;
+  }
+
+private:
+  StringRef _name;
+  std::vector<const Expression *> _args;
+};
+
+class Unary : public Expression {
+public:
+  enum Operation {
+    Minus,
+    Not
+  };
+
+  Unary(Operation op, const Expression *child) : Expression(Kind::Unary),
+    _op(op), _child(child) {}
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const Expression *c) {
+    return c->getKind() == Kind::Unary;
+  }
+
+private:
+  Operation _op;
+  const Expression *_child;
+};
+
+class BinOp : public Expression {
+public:
+  enum Operation {
+    And,
+    CompareDifferent,
+    CompareEqual,
+    CompareGreater,
+    CompareGreaterEqual,
+    CompareLess,
+    CompareLessEqual,
+    Div,
+    Mul,
+    Or,
+    Shl,
+    Shr,
+    Sub,
+    Sum
+  };
+
+  BinOp(const Expression *lhs, Operation op, const Expression *rhs)
+      : Expression(Kind::BinOp), _op(op), _lhs(lhs), _rhs(rhs) {}
+
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const Expression *c) {
+    return c->getKind() == Kind::BinOp;
+  }
+
+private:
+  Operation _op;
+  const Expression *_lhs;
+  const Expression *_rhs;
+};
+
+/// Operands of the ternary operator can be any expression, similar to the other
+/// operations, including another ternary operator. To disambiguate the parse
+/// tree, note that ternary conditionals have precedence 13 and, different from
+/// other operators, associates right-to-left. For example:
+///
+/// i = i > 3 ? i < 5 ? 1 : 2 : 0;
+///
+/// will have the following parse tree:
+///
+/// i = ((i > 3) ? ((i < 5) ? 1 : 2) : 0);
+///
+/// The '>' binds tigher because it has precedence 6. When faced with two "?"
+/// ternary operators back-to-back, the parser prioritized the rightmost one.
+///
+class TernaryConditional : public Expression {
+public:
+  TernaryConditional(const Expression *conditional, const Expression *trueExpr,
+                     const Expression *falseExpr)
+      : Expression(Kind::TernaryConditional), _conditional(conditional),
+        _trueExpr(trueExpr), _falseExpr(falseExpr) {}
+
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const Expression *c) {
+    return c->getKind() == Kind::TernaryConditional;
+  }
+
+private:
+  const Expression *_conditional;
+  const Expression *_trueExpr;
+  const Expression *_falseExpr;
+};
+
+/// Symbol assignments of the form "symbolname = <expression>" may occur either
+/// as sections-commands or as output-section-commands.
+/// Example:
+///
+/// SECTIONS {
+///   mysymbol = .         /* SymbolAssignment as a sections-command */
+///   .data : {
+///     othersymbol = .    /* SymbolAssignment as an output-section-command */
+///   }
+///}
+///
+class SymbolAssignment : public Command {
+public:
+  enum AssignmentKind { Simple, Sum, Sub, Mul, Div, Shl, Shr, And, Or };
+  enum AssignmentVisibility { Normal, Hidden, Provide, ProvideHidden };
+
+  SymbolAssignment(StringRef name, const Expression *expr, AssignmentKind kind,
+                   AssignmentVisibility visibility)
+      : Command(Kind::SymbolAssignment), _expression(expr), _symbol(name),
+        _assignmentKind(Simple), _assignmentVisibility(visibility) {}
+
+  static bool classof(const Command *c) {
+    return c->getKind() == Kind::SymbolAssignment;
+  }
+
+  void dump(raw_ostream &os) const override;
+
+private:
+  const Expression *_expression;
+  StringRef _symbol;
+  AssignmentKind _assignmentKind;
+  AssignmentVisibility _assignmentVisibility;
+};
+
+/// Encodes how to sort file names or section names that are expanded from
+/// wildcard operators. This typically occurs in constructs such as
+/// SECTIONS {  .data : SORT_BY_NAME(*)(*) }}, where the order of the expanded
+/// names is important to determine which sections go first.
+enum class WildcardSortMode {
+  NA,
+  ByAlignment,
+  ByAlignmentAndName,
+  ByInitPriority,
+  ByName,
+  ByNameAndAlignment,
+  None
+};
+
+/// Represents either a single input section name or a group of sorted input
+/// section names. They specify which sections to map to a given output section.
+/// Example:
+///
+/// SECTIONS {
+///   .x: { *(.text) }
+///   /*      ^~~~^         InputSectionName : InputSection  */
+///   .y: { *(SORT(.text*)) }
+///   /*      ^~~~~~~~~~~^  InputSectionSortedGroup : InputSection  */
+/// }
+class InputSection {
+public:
+  enum class Kind { InputSectionName, SortedGroup };
+
+  Kind getKind() const { return _kind; }
+
+  virtual void dump(raw_ostream &os) const = 0;
+
+  virtual ~InputSection() {}
+
+protected:
+  explicit InputSection(Kind k) : _kind(k) {}
+
+private:
+  Kind _kind;
+};
+
+class InputSectionName : public InputSection {
+public:
+  InputSectionName(StringRef name, bool excludeFile)
+      : InputSection(Kind::InputSectionName), _name(name),
+        _excludeFile(excludeFile) {}
+
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const InputSection *c) {
+    return c->getKind() == Kind::InputSectionName;
+  }
+  bool hasExcludeFile() const { return _excludeFile; }
+
+private:
+  StringRef _name;
+  bool _excludeFile;
+};
+
+class InputSectionSortedGroup : public InputSection {
+public:
+  template <class RangeT>
+  InputSectionSortedGroup(WildcardSortMode sort, RangeT range)
+      : InputSection(Kind::SortedGroup), _sortMode(sort) {
+    std::copy(std::begin(range), std::end(range),
+              std::back_inserter(_sections));
+  }
+
+  void dump(raw_ostream &os) const override;
+  WildcardSortMode getSortMode() const { return _sortMode; }
+
+  static bool classof(const InputSection *c) {
+    return c->getKind() == Kind::SortedGroup;
+  }
+
+private:
+  WildcardSortMode _sortMode;
+  std::vector<const InputSection *> _sections;
+};
+
+/// An output-section-command that maps a series of sections inside a given
+/// file-archive pair to an output section.
+/// Example:
+///
+/// SECTIONS {
+///   .x: { *(.text) }
+///   /*    ^~~~~~~^ InputSectionsCmd   */
+///   .y: { w:z(SORT(.text*)) }
+///   /*    ^~~~~~~~~~~~~~~~^  InputSectionsCmd  */
+/// }
+class InputSectionsCmd : public Command {
+public:
+  typedef std::vector<const InputSection *> VectorTy;
+
+  template <class RangeT>
+  InputSectionsCmd(StringRef fileName, StringRef archiveName, bool keep,
+                   WildcardSortMode fileSortMode,
+                   WildcardSortMode archiveSortMode, RangeT range)
+      : Command(Kind::InputSectionsCmd), _fileName(fileName),
+        _archiveName(archiveName), _keep(keep), _fileSortMode(fileSortMode),
+        _archiveSortMode(archiveSortMode) {
+    std::copy(std::begin(range), std::end(range),
+              std::back_inserter(_sections));
+  }
+
+  void dump(raw_ostream &os) const override;
+
+  static bool classof(const Command *c) {
+    return c->getKind() == Kind::InputSectionsCmd;
+  }
+
+private:
+  StringRef _fileName;
+  StringRef _archiveName;
+  bool _keep;
+  WildcardSortMode _fileSortMode;
+  WildcardSortMode _archiveSortMode;
+  VectorTy _sections;
+};
+
+/// A sections-command to specify which input sections and symbols compose a
+/// given output section.
+/// Example:
+///
+/// SECTIONS {
+///   .x: { *(.text) ; symbol = .; }
+/// /*^~~~~~~~~~~~~~~~~~~~~~~~~~~~~^   OutputSectionDescription */
+///   .y: { w:z(SORT(.text*)) }
+/// /*^~~~~~~~~~~~~~~~~~~~~~~~^  OutputSectionDescription  */
+///   .a 0x10000 : ONLY_IF_RW { *(.data*) ; *:libc.a(SORT(*)); }
+/// /*^~~~~~~~~~~~~  OutputSectionDescription ~~~~~~~~~~~~~~~~~^ */
+/// }
+class OutputSectionDescription : public Command {
+public:
+  enum Constraint { C_None, C_OnlyIfRO, C_OnlyIfRW };
+
+  template <class RangeT>
+  OutputSectionDescription(StringRef sectionName, const Expression *address,
+                           const Expression *align, const Expression *subAlign,
+                           const Expression *at, const Expression *fillExpr,
+                           StringRef fillStream,
+                           bool alignWithInput, bool discard,
+                           Constraint constraint, RangeT range)
+      : Command(Kind::OutputSectionDescription), _sectionName(sectionName),
+        _address(address), _align(align), _subAlign(subAlign), _at(at),
+        _fillExpr(fillExpr), _fillStream(fillStream),
+        _alignWithInput(alignWithInput), _discard(discard),
+        _constraint(constraint) {
+    std::copy(std::begin(range), std::end(range),
+              std::back_inserter(_outputSectionCommands));
+  }
+
+  static bool classof(const Command *c) {
+    return c->getKind() == Kind::OutputSectionDescription;
+  }
+
+  void dump(raw_ostream &os) const override;
+
+private:
+  StringRef _sectionName;
+  const Expression *_address;
+  const Expression *_align;
+  const Expression *_subAlign;
+  const Expression *_at;
+  const Expression *_fillExpr;
+  StringRef _fillStream;
+  bool _alignWithInput;
+  bool _discard;
+  Constraint _constraint;
+  std::vector<const Command *> _outputSectionCommands;
+};
+
+/// Represents an Overlay structure as documented in
+/// https://sourceware.org/binutils/docs/ld/Overlay-Description.html#Overlay-Description
+class Overlay : public Command {
+public:
+  Overlay() : Command(Kind::Overlay) {}
+
+  static bool classof(const Command *c) {
+    return c->getKind() == Kind::Overlay;
+  }
+
+  void dump(raw_ostream &os) const override { os << "Overlay description\n"; }
+};
+
+/// Represents all the contents of the SECTIONS {} construct.
+class Sections : public Command {
+public:
+  template <class RangeT> Sections(RangeT range) : Command(Kind::Sections) {
+    std::copy(std::begin(range), std::end(range),
+              std::back_inserter(_sectionsCommands));
+  }
+
+  static bool classof(const Command *c) {
+    return c->getKind() == Kind::Sections;
+  }
+
+  void dump(raw_ostream &os) const override;
+
+private:
+  std::vector<const Command *> _sectionsCommands;
+};
+
+/// Stores the parse tree of a linker script.
 class LinkerScript {
 public:
   void dump(raw_ostream &os) const {
-    for (const Command *c : _commands)
+    for (const Command *c : _commands) {
       c->dump(os);
+      if (isa<SymbolAssignment>(c))
+        os << "\n";
+    }
   }
 
-  std::vector<Command *> _commands;
+  std::vector<const Command *> _commands;
 };
 
+/// Recognizes syntactic constructs of a linker script using a predictive
+/// parser/recursive descent implementation.
+///
+/// Based on the linker script documentation available at
+/// https://sourceware.org/binutils/docs/ld/Scripts.html
 class Parser {
 public:
-  explicit Parser(Lexer &lex) : _lex(lex) {}
+  explicit Parser(Lexer &lex) : _lex(lex), _peekAvailable(false) {}
 
   LinkerScript *parse();
 
 private:
-  void consumeToken() { _lex.lex(_tok); }
+  /// Advances to the next token, either asking the Lexer to lex the next token
+  /// or obtaining it from the look ahead buffer.
+  void consumeToken() {
+    // First check if the look ahead buffer cached the next token
+    if (_peekAvailable) {
+      _tok = _bufferedToken;
+      _peekAvailable = false;
+      return;
+    }
+    _lex.lex(_tok);
+  }
+
+  /// Returns the token that succeeds the current one without consuming the
+  /// current token. This operation will lex an additional token and store it in
+  /// a private buffer.
+  const Token &peek() {
+    if (_peekAvailable)
+      return _bufferedToken;
+
+    _lex.lex(_bufferedToken);
+    _peekAvailable = true;
+    return _bufferedToken;
+  }
 
   void error(const Token &tok, Twine msg) {
-    _lex.getSourceMgr()
-        .PrintMessage(llvm::SMLoc::getFromPointer(tok._range.data()),
-                      llvm::SourceMgr::DK_Error, msg);
+    _lex.getSourceMgr().PrintMessage(
+        llvm::SMLoc::getFromPointer(tok._range.data()),
+        llvm::SourceMgr::DK_Error, msg);
   }
 
   bool expectAndConsume(Token::Kind kind, Twine msg) {
@@ -243,17 +766,203 @@ private:
 
   bool isNextToken(Token::Kind kind) { return (_tok._kind == kind); }
 
+  // Recursive descent parsing member functions
+  // All of these functions consumes tokens and return an AST object,
+  // represented by the Command superclass. However, note that not all AST
+  // objects derive from Command. For nodes of C-like expressions, used in
+  // linker scripts, the superclass is Expression. For nodes that represent
+  // input sections that map to an output section, the superclass is
+  // InputSection.
+  //
+  // Example mapping common constructs to AST nodes:
+  //
+  // SECTIONS {             /* Parsed to Sections class */
+  //   my_symbol = 1 + 1;   /* Parsed to SymbolAssignment class */
+  //   /*          ^~~> Parsed to Expression class         */
+  //   .data : { *(.data) } /* Parsed to OutputSectionDescription class */
+  //   /*          ^~~> Parsed to InputSectionName class   */
+  //   /*        ^~~~~> Parsed to InputSectionsCmd class   */
+  // }
+
+  // ==== Expression parsing member functions ====
+
+  /// Parse "identifier(param [, param]...)"
+  ///
+  /// Example:
+  ///
+  /// SECTIONS {
+  ///   my_symbol = 0x1000 | ALIGN(other_symbol);
+  ///   /*                   ^~~~> parseFunctionCall()
+  /// }
+  const Expression *parseFunctionCall();
+
+  /// Ensures that the current token is an expression operand. If it is not,
+  /// issues an error to the user and returns false.
+  bool expectExprOperand();
+
+  /// Parse operands of an expression, such as function calls, identifiers,
+  /// literal numbers or unary operators.
+  ///
+  /// Example:
+  ///
+  /// SECTIONS {
+  ///   my_symbol = 0x1000 | ALIGN(other_symbol);
+  ///               ^~~~> parseExprTerminal()
+  /// }
+  const Expression *parseExprOperand();
+
+  // As a reference to the precedence of C operators, consult
+  // http://en.cppreference.com/w/c/language/operator_precedence
+
+  /// Parse either a single expression operand and returns or parse an entire
+  /// expression if its top-level node has a lower or equal precedence than the
+  /// indicated.
+  const Expression *parseExpression(unsigned precedence = 13);
+
+  /// Parse an operator and its rhs operand, assuming that the lhs was already
+  /// consumed. Keep parsing subsequent operator-operand pairs that do not
+  /// exceed highestPrecedence.
+  /// * lhs points to the left-hand-side operand of this operator
+  /// * maxPrecedence has the maximum operator precedence level that this parse
+  /// function is allowed to consume.
+  const Expression *parseOperatorOperandLoop(const Expression *lhs,
+                                             unsigned maxPrecedence);
+
+  /// Parse ternary conditionals such as "(condition)? true: false;". This
+  /// operator has precedence level 13 and associates right-to-left.
+  const Expression *parseTernaryCondOp(const Expression *lhs);
+
+  // ==== High-level commands parsing ====
+
+  /// Parse the OUTPUT_FORMAT linker script command.
+  /// Example:
+  ///
+  /// OUTPUT_FORMAT(elf64-x86-64,elf64-x86-64,elf64-x86-64)
+  /// ^~~~> parseOutputFormat()
+  ///
   OutputFormat *parseOutputFormat();
+
+  /// Parse the OUTPUT_ARCH linker script command.
+  /// Example:
+  ///
+  /// OUTPUT_ARCH(i386:x86-64)
+  /// ^~~~> parseOutputArch()
+  ///
   OutputArch *parseOutputArch();
+
+  /// Parse the GROUP linker script command.
+  /// Example:
+  ///
+  /// GROUP ( /lib/x86_64-linux-gnu/libc.so.6
+  ///         /usr/lib/x86_64-linux-gnu/libc_nonshared.a
+  ///         AS_NEEDED ( /lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 )
+  ///         -lm -l:libgcc.a )
+  ///
   Group *parseGroup();
   bool parseAsNeeded(std::vector<Path> &paths);
+
+  /// Parse the ENTRY linker script command.
+  /// Example:
+  ///
+  /// ENTRY(init)
+  /// ^~~~> parseEntry()
+  ///
   Entry *parseEntry();
 
+  /// Parse the SEARCH_DIR linker script command.
+  /// Example:
+  ///
+  /// SEARCH_DIR("/usr/x86_64-linux-gnu/lib64");
+  /// ^~~~> parseSearchDir()
+  ///
+  SearchDir *parseSearchDir();
+
+  /// Parse "symbol = expression" commands that live inside the
+  /// SECTIONS directive.
+  /// Example:
+  ///
+  /// SECTIONS {
+  ///   my_symbol = 1 + 1;
+  ///               ^~~~> parseExpression()
+  ///   ^~~~ parseSymbolAssignment()
+  /// }
+  ///
+  const SymbolAssignment *parseSymbolAssignment();
+
+  /// Parse "EXCLUDE_FILE" used inside the listing of input section names.
+  /// Example:
+  ///
+  /// SECTIONS {
+  ///   .data :  { *(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors) }
+  ///                ^~~~> parseExcludeFile()
+  /// }
+  ///
+  ErrorOr<InputSectionsCmd::VectorTy> parseExcludeFile();
+
+  /// Helper to parse SORT_BY_NAME(, SORT_BY_ALIGNMENT( and SORT_NONE(,
+  /// possibly nested. Returns the number of Token::r_paren tokens that need
+  /// to be consumed, while sortMode is updated with the parsed sort
+  /// criteria.
+  /// Example:
+  ///
+  /// SORT_BY_NAME(SORT_BY_ALIGNMENT(*))
+  /// ^~~~ parseSortDirectives()  ~~^
+  /// Returns 2, finishes with sortMode = WildcardSortMode::ByNameAndAlignment
+  ///
+  int parseSortDirectives(WildcardSortMode &sortMode);
+
+  /// Parse a group of input section names that are sorted via SORT* directives.
+  /// Example:
+  ///   SORT_BY_NAME(SORT_BY_ALIGNMENT(*data *bss))
+  const InputSection *parseSortedInputSections();
+
+  /// Parse input section description statements.
+  /// Example:
+  ///
+  /// SECTIONS {
+  ///   .mysection : crt.o(.data* .bss SORT_BY_NAME(name*))
+  ///                ^~~~ parseInputSectionsCmd()
+  /// }
+  const InputSectionsCmd *parseInputSectionsCmd();
+
+  /// Parse output section description statements.
+  /// Example:
+  ///
+  /// SECTIONS {
+  ///   .data : { crt.o(.data* .bss SORT_BY_NAME(name*)) }
+  ///   ^~~~ parseOutputSectionDescription()
+  /// }
+  const OutputSectionDescription *parseOutputSectionDescription();
+
+  /// Stub for parsing overlay commands. Currently unimplemented.
+  const Overlay *parseOverlay();
+
+  /// Parse the SECTIONS linker script command.
+  /// Example:
+  ///
+  ///   SECTIONS {
+  ///   ^~~~ parseSections()
+  ///     . = 0x100000;
+  ///     .data : { *(.data) }
+  ///   }
+  ///
+  Sections *parseSections();
+
 private:
+  // Owns the entire linker script AST nodes
   llvm::BumpPtrAllocator _alloc;
+
+  // The top-level/entry-point linker script AST node
   LinkerScript _script;
+
   Lexer &_lex;
+
+  // Current token being analyzed
   Token _tok;
+
+  // Annotate whether we buffered the next token to allow peeking
+  bool _peekAvailable;
+  Token _bufferedToken;
 };
 } // end namespace script
 } // end namespace lld

Modified: lld/trunk/lib/ReaderWriter/LinkerScript.cpp
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/lib/ReaderWriter/LinkerScript.cpp?rev=221126&r1=221125&r2=221126&view=diff
==============================================================================
--- lld/trunk/lib/ReaderWriter/LinkerScript.cpp (original)
+++ lld/trunk/lib/ReaderWriter/LinkerScript.cpp Sun Nov  2 22:09:51 2014
@@ -18,27 +18,246 @@ namespace lld {
 namespace script {
 void Token::dump(raw_ostream &os) const {
   switch (_kind) {
-#define CASE(name)                              \
-  case Token::name:                             \
-    os << #name ": ";                           \
-    break;
-  CASE(eof)
-  CASE(identifier)
-  CASE(libname)
-  CASE(kw_as_needed)
-  CASE(kw_entry)
-  CASE(kw_group)
-  CASE(kw_output_format)
-  CASE(kw_output_arch)
-  CASE(comma)
-  CASE(l_paren)
-  CASE(r_paren)
-  CASE(unknown)
+#define CASE(name)                                                             \
+  case Token::name:                                                            \
+    os << #name ": ";                                                          \
+    break;
+    CASE(unknown)
+    CASE(eof)
+    CASE(exclaim)
+    CASE(exclaimequal)
+    CASE(amp)
+    CASE(ampequal)
+    CASE(l_paren)
+    CASE(r_paren)
+    CASE(star)
+    CASE(starequal)
+    CASE(plus)
+    CASE(plusequal)
+    CASE(comma)
+    CASE(minus)
+    CASE(minusequal)
+    CASE(slash)
+    CASE(slashequal)
+    CASE(number)
+    CASE(colon)
+    CASE(semicolon)
+    CASE(less)
+    CASE(lessequal)
+    CASE(lessless)
+    CASE(lesslessequal)
+    CASE(equal)
+    CASE(equalequal)
+    CASE(greater)
+    CASE(greaterequal)
+    CASE(greatergreater)
+    CASE(greatergreaterequal)
+    CASE(question)
+    CASE(identifier)
+    CASE(libname)
+    CASE(kw_align)
+    CASE(kw_align_with_input)
+    CASE(kw_as_needed)
+    CASE(kw_at)
+    CASE(kw_discard)
+    CASE(kw_entry)
+    CASE(kw_exclude_file)
+    CASE(kw_group)
+    CASE(kw_hidden)
+    CASE(kw_keep)
+    CASE(kw_provide)
+    CASE(kw_provide_hidden)
+    CASE(kw_only_if_ro)
+    CASE(kw_only_if_rw)
+    CASE(kw_output_arch)
+    CASE(kw_output_format)
+    CASE(kw_overlay)
+    CASE(kw_search_dir)
+    CASE(kw_sections)
+    CASE(kw_sort_by_alignment)
+    CASE(kw_sort_by_init_priority)
+    CASE(kw_sort_by_name)
+    CASE(kw_sort_none)
+    CASE(kw_subalign)
+    CASE(l_brace)
+    CASE(pipe)
+    CASE(pipeequal)
+    CASE(r_brace)
+    CASE(tilde)
 #undef CASE
   }
   os << _range << "\n";
 }
 
+static llvm::ErrorOr<uint64_t> parseDecimal(StringRef str) {
+  uint64_t res = 0;
+  for (auto &c : str) {
+    res *= 10;
+    if (c < '0' || c > '9')
+      return std::errc::io_error;
+    res += c - '0';
+  }
+  return res;
+}
+
+static llvm::ErrorOr<uint64_t> parseOctal(StringRef str) {
+  uint64_t res = 0;
+  for (auto &c : str) {
+    res <<= 3;
+    if (c < '0' || c > '7')
+      return std::errc::io_error;
+    res += c - '0';
+  }
+  return res;
+}
+
+static llvm::ErrorOr<uint64_t> parseBinary(StringRef str) {
+  uint64_t res = 0;
+  for (auto &c : str) {
+    res <<= 1;
+    if (c != '0' && c != '1')
+      return std::errc::io_error;
+    res += c - '0';
+  }
+  return res;
+}
+
+static llvm::ErrorOr<uint64_t> parseHex(StringRef str) {
+  uint64_t res = 0;
+  for (auto &c : str) {
+    res <<= 4;
+    if (c >= '0' && c <= '9')
+      res += c - '0';
+    else if (c >= 'a' && c <= 'f')
+      res += c - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+      res += c - 'A' + 10;
+    else
+      return std::errc::io_error;
+  }
+  return res;
+}
+
+static bool parseHexToByteStream(StringRef str, std::string &buf) {
+  unsigned char byte = 0;
+  bool dumpByte = str.size() % 2;
+  for (auto &c : str) {
+    byte <<= 4;
+    if (c >= '0' && c <= '9')
+      byte += c - '0';
+    else if (c >= 'a' && c <= 'f')
+      byte += c - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+      byte += c - 'A' + 10;
+    else
+      return false;
+    if (!dumpByte) {
+      dumpByte = true;
+      continue;
+    }
+    buf.push_back(byte);
+    byte = 0;
+    dumpByte = false;
+  }
+  return !dumpByte;
+}
+
+static void dumpByteStream(raw_ostream &os, StringRef stream) {
+  os << "0x";
+  for (auto &c : stream) {
+    unsigned char firstNibble = c >> 4 & 0xF;
+    if (firstNibble > 9)
+      os << (char) ('A' + firstNibble - 10);
+    else
+      os << (char) ('0' + firstNibble);
+    unsigned char secondNibble = c & 0xF;
+    if (secondNibble > 9)
+      os << (char) ('A' + secondNibble - 10);
+    else
+      os << (char) ('0' + secondNibble);
+  }
+}
+
+static llvm::ErrorOr<uint64_t> parseNum(StringRef str) {
+  unsigned multiplier = 1;
+  enum NumKind { decimal, hex, octal, binary };
+  NumKind kind = llvm::StringSwitch<NumKind>(str)
+                     .StartsWith("0x", hex)
+                     .StartsWith("0X", hex)
+                     .StartsWith("0", octal)
+                     .Default(decimal);
+
+  // Parse scale
+  if (str.endswith("K")) {
+    multiplier = 1 << 10;
+    str = str.drop_back();
+  } else if (str.endswith("M")) {
+    multiplier = 1 << 20;
+    str = str.drop_back();
+  }
+
+  // Parse type
+  if (str.endswith_lower("o")) {
+    kind = octal;
+    str = str.drop_back();
+  } else if (str.endswith_lower("h")) {
+    kind = hex;
+    str = str.drop_back();
+  } else if (str.endswith_lower("d")) {
+    kind = decimal;
+    str = str.drop_back();
+  } else if (str.endswith_lower("b")) {
+    kind = binary;
+    str = str.drop_back();
+  }
+
+  llvm::ErrorOr<uint64_t> res(0);
+  switch (kind) {
+  case hex:
+    if (str.startswith_lower("0x"))
+      str = str.drop_front(2);
+    res = parseHex(str);
+    break;
+  case octal:
+    res = parseOctal(str);
+    break;
+  case decimal:
+    res = parseDecimal(str);
+    break;
+  case binary:
+    res = parseBinary(str);
+    break;
+  }
+  if (res.getError())
+    return res;
+
+  *res = *res * multiplier;
+  return res;
+}
+
+bool Lexer::canStartNumber(char c) const {
+  return '0' <= c && c <= '9';
+}
+
+bool Lexer::canContinueNumber(char c) const {
+  switch (c) {
+  // Digits
+  case '0': case '1': case '2': case '3': case '4': case '5': case '6':
+  case '7': case '8': case '9':
+  case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
+  case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
+  // Hex marker
+  case 'x': case 'X':
+  // Type suffix
+  case 'h': case 'H': case 'o': case 'O':
+  // Scale suffix
+  case 'M': case 'K':
+    return true;
+  default:
+    return false;
+  }
+}
+
 bool Lexer::canStartName(char c) const {
   switch (c) {
   case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
@@ -50,6 +269,7 @@ bool Lexer::canStartName(char c) const {
   case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
   case 'v': case 'w': case 'x': case 'y': case 'z':
   case '_': case '.': case '$': case '/': case '\\':
+  case '*':
     return true;
   default:
     return false;
@@ -82,6 +302,15 @@ bool Lexer::canContinueName(char c) cons
   }
 }
 
+/// Helper function to split a StringRef in two at the nth character.
+/// The StringRef s is updated, while the function returns the n first
+/// characters.
+static StringRef drop(StringRef &s, int n) {
+  StringRef res = s.substr(0, n);
+  s = s.drop_front(n);
+  return res;
+}
+
 void Lexer::lex(Token &tok) {
   skipWhitespace();
   if (_buffer.empty()) {
@@ -90,79 +319,222 @@ void Lexer::lex(Token &tok) {
   }
   switch (_buffer[0]) {
   case 0:
-    tok = Token(_buffer.substr(0, 1), Token::eof);
-    _buffer = _buffer.drop_front();
+    tok = Token(drop(_buffer, 1), Token::eof);
     return;
   case '(':
-    tok = Token(_buffer.substr(0, 1), Token::l_paren);
-    _buffer = _buffer.drop_front();
+    tok = Token(drop(_buffer, 1), Token::l_paren);
     return;
   case ')':
-    tok = Token(_buffer.substr(0, 1), Token::r_paren);
-    _buffer = _buffer.drop_front();
+    tok = Token(drop(_buffer, 1), Token::r_paren);
+    return;
+  case '{':
+    tok = Token(drop(_buffer, 1), Token::l_brace);
+    return;
+  case '}':
+    tok = Token(drop(_buffer, 1), Token::r_brace);
+    return;
+  case '=':
+    if (_buffer.startswith("==")) {
+      tok = Token(drop(_buffer, 2), Token::equalequal);
+      return;
+    }
+    tok = Token(drop(_buffer, 1), Token::equal);
+    return;
+  case '!':
+    if (_buffer.startswith("!=")) {
+      tok = Token(drop(_buffer, 2), Token::exclaimequal);
+      return;
+    }
+    tok = Token(drop(_buffer, 1), Token::exclaim);
     return;
   case ',':
-    tok = Token(_buffer.substr(0, 1), Token::comma);
-    _buffer = _buffer.drop_front();
+    tok = Token(drop(_buffer, 1), Token::comma);
     return;
-  default:
+  case ';':
+    tok = Token(drop(_buffer, 1), Token::semicolon);
+    return;
+  case ':':
+    tok = Token(drop(_buffer, 1), Token::colon);
+    return;
+  case '&':
+    if (_buffer.startswith("&=")) {
+      tok = Token(drop(_buffer, 2), Token::ampequal);
+      return;
+    }
+    tok = Token(drop(_buffer, 1), Token::amp);
+    return;
+  case '|':
+    if (_buffer.startswith("|=")) {
+      tok = Token(drop(_buffer, 2), Token::pipeequal);
+      return;
+    }
+    tok = Token(drop(_buffer, 1), Token::pipe);
+    return;
+  case '+':
+    if (_buffer.startswith("+=")) {
+      tok = Token(drop(_buffer, 2), Token::plusequal);
+      return;
+    }
+    tok = Token(drop(_buffer, 1), Token::plus);
+    return;
+  case '-': {
+    if (_buffer.startswith("-=")) {
+      tok = Token(drop(_buffer, 2), Token::minusequal);
+      return;
+    }
+    if (!_buffer.startswith("-l")) {
+      tok = Token(drop(_buffer, 1), Token::minus);
+      return;
+    }
+    // -l<lib name>
+    _buffer = _buffer.drop_front(2);
+    StringRef::size_type start = 0;
+    if (_buffer[start] == ':')
+      ++start;
+    if (!canStartName(_buffer[start]))
+      // Create 'unknown' token.
+      break;
+    auto libNameEnd = std::find_if(_buffer.begin() + start + 1, _buffer.end(),
+                                   [=](char c) { return !canContinueName(c); });
+    StringRef::size_type libNameLen =
+        std::distance(_buffer.begin(), libNameEnd);
+    tok = Token(_buffer.substr(0, libNameLen), Token::libname);
+    _buffer = _buffer.drop_front(libNameLen);
+    return;
+  }
+  case '<':
+    if (_buffer.startswith("<<=")) {
+      tok = Token(drop(_buffer, 3), Token::lesslessequal);
+      return;
+    }
+    if (_buffer.startswith("<<")) {
+      tok = Token(drop(_buffer, 2), Token::lessless);
+      return;
+    }
+    if (_buffer.startswith("<=")) {
+      tok = Token(drop(_buffer, 2), Token::lessequal);
+      return;
+    }
+    tok = Token(drop(_buffer, 1), Token::less);
+    return;
+  case '>':
+    if (_buffer.startswith(">>=")) {
+      tok = Token(drop(_buffer, 3), Token::greatergreaterequal);
+      return;
+    }
+    if (_buffer.startswith(">>")) {
+      tok = Token(drop(_buffer, 2), Token::greatergreater);
+      return;
+    }
+    if (_buffer.startswith(">=")) {
+      tok = Token(drop(_buffer, 2), Token::greaterequal);
+      return;
+    }
+    tok = Token(drop(_buffer, 1), Token::greater);
+    return;
+  case '~':
+    tok = Token(drop(_buffer, 1), Token::tilde);
+    return;
+  case '\"': case '\'': {
     // Handle quoted strings. They are treated as identifiers for
     // simplicity.
-    if ((_buffer[0] == '\"') || (_buffer[0] == '\'')) {
-      char c = _buffer[0];
-      _buffer = _buffer.drop_front();
-      auto quotedStringEnd = _buffer.find(c);
-      if (quotedStringEnd == StringRef::npos || quotedStringEnd == 0)
+    char c = _buffer[0];
+    _buffer = _buffer.drop_front();
+    auto quotedStringEnd = _buffer.find(c);
+    if (quotedStringEnd == StringRef::npos || quotedStringEnd == 0)
+      break;
+    StringRef word = _buffer.substr(0, quotedStringEnd);
+    tok = Token(word, Token::identifier);
+    _buffer = _buffer.drop_front(quotedStringEnd + 1);
+    return;
+  }
+  default:
+    // Handle literal numbers
+    if (canStartNumber(_buffer[0])) {
+      auto endIter = std::find_if(_buffer.begin(), _buffer.end(), [=](char c) {
+        return !canContinueNumber(c);
+      });
+      StringRef::size_type end = endIter == _buffer.end()
+                                     ? StringRef::npos
+                                     : std::distance(_buffer.begin(), endIter);
+      if (end == StringRef::npos || end == 0)
         break;
-      StringRef word = _buffer.substr(0, quotedStringEnd);
-      tok = Token(word, Token::identifier);
-      _buffer = _buffer.drop_front(quotedStringEnd + 1);
+      StringRef word = _buffer.substr(0, end);
+      tok = Token(word, Token::number);
+      _buffer = _buffer.drop_front(end);
       return;
     }
-    // -l<lib name>
-    if (_buffer.startswith("-l")) {
-      _buffer = _buffer.drop_front(2);
-      StringRef::size_type start = 0;
-      if (_buffer[start] == ':')
-        ++start;
-      if (!canStartName(_buffer[start]))
-        // Create 'unknown' token.
-        break;
-      auto libNameEnd =
-          std::find_if(_buffer.begin() + start + 1, _buffer.end(),
-                       [=](char c) { return !canContinueName(c); });
-      StringRef::size_type libNameLen =
-          std::distance(_buffer.begin(), libNameEnd);
-      tok = Token(_buffer.substr(0, libNameLen), Token::libname);
-      _buffer = _buffer.drop_front(libNameLen);
+    // Handle slashes '/', which can be either an operator inside an expression
+    // or the beginning of an identifier
+    if (_buffer.startswith("/=")) {
+      tok = Token(drop(_buffer, 2), Token::slashequal);
+      return;
+    }
+    if (_buffer[0] == '/' && _buffer.size() > 1 &&
+        !canContinueName(_buffer[1])) {
+      tok = Token(drop(_buffer, 1), Token::slash);
+      return;
+    }
+    // Handle stars '*'
+    if (_buffer.startswith("*=")) {
+      tok = Token(drop(_buffer, 2), Token::starequal);
+      return;
+    }
+    if (_buffer[0] == '*' && _buffer.size() > 1 &&
+        !canContinueName(_buffer[1])) {
+      tok = Token(drop(_buffer, 1), Token::star);
+      return;
+    }
+    // Handle questions '?'
+    if (_buffer[0] == '?' && _buffer.size() > 1 &&
+        !canContinueName(_buffer[1])) {
+      tok = Token(drop(_buffer, 1), Token::question);
       return;
     }
-    /// keyword or identifer.
+    // keyword or identifier.
     if (!canStartName(_buffer[0]))
       break;
-    auto endIter =
-        std::find_if(_buffer.begin() + 1, _buffer.end(), [=](char c) {
-      return !canContinueName(c);
-    });
-    StringRef::size_type end =
-        endIter == _buffer.end() ? StringRef::npos
-                                 : std::distance(_buffer.begin(), endIter);
+    auto endIter = std::find_if(_buffer.begin() + 1, _buffer.end(),
+                                [=](char c) { return !canContinueName(c); });
+    StringRef::size_type end = endIter == _buffer.end()
+                                   ? StringRef::npos
+                                   : std::distance(_buffer.begin(), endIter);
     if (end == StringRef::npos || end == 0)
       break;
     StringRef word = _buffer.substr(0, end);
-    Token::Kind kind = llvm::StringSwitch<Token::Kind>(word)
-                           .Case("OUTPUT_FORMAT", Token::kw_output_format)
-                           .Case("OUTPUT_ARCH", Token::kw_output_arch)
-                           .Case("GROUP", Token::kw_group)
-                           .Case("AS_NEEDED", Token::kw_as_needed)
-                           .Case("ENTRY", Token::kw_entry)
-                           .Default(Token::identifier);
+    Token::Kind kind =
+        llvm::StringSwitch<Token::Kind>(word)
+            .Case("ALIGN", Token::kw_align)
+            .Case("ALIGN_WITH_INPUT", Token::kw_align_with_input)
+            .Case("AS_NEEDED", Token::kw_as_needed)
+            .Case("AT", Token::kw_at)
+            .Case("ENTRY", Token::kw_entry)
+            .Case("EXCLUDE_FILE", Token::kw_exclude_file)
+            .Case("GROUP", Token::kw_group)
+            .Case("HIDDEN", Token::kw_hidden)
+            .Case("KEEP", Token::kw_keep)
+            .Case("ONLY_IF_RO", Token::kw_only_if_ro)
+            .Case("ONLY_IF_RW", Token::kw_only_if_rw)
+            .Case("OUTPUT_ARCH", Token::kw_output_arch)
+            .Case("OUTPUT_FORMAT", Token::kw_output_format)
+            .Case("OVERLAY", Token::kw_overlay)
+            .Case("PROVIDE", Token::kw_provide)
+            .Case("PROVIDE_HIDDEN", Token::kw_provide_hidden)
+            .Case("SEARCH_DIR", Token::kw_search_dir)
+            .Case("SECTIONS", Token::kw_sections)
+            .Case("SORT", Token::kw_sort_by_name)
+            .Case("SORT_BY_ALIGNMENT", Token::kw_sort_by_alignment)
+            .Case("SORT_BY_INIT_PRIORITY", Token::kw_sort_by_init_priority)
+            .Case("SORT_BY_NAME", Token::kw_sort_by_name)
+            .Case("SORT_NONE", Token::kw_sort_none)
+            .Case("SUBALIGN", Token::kw_subalign)
+            .Case("/DISCARD/", Token::kw_discard)
+            .Default(Token::identifier);
     tok = Token(word, kind);
     _buffer = _buffer.drop_front(end);
     return;
   }
-  tok = Token(_buffer.substr(0, 1), Token::unknown);
-  _buffer = _buffer.drop_front();
+  tok = Token(drop(_buffer, 1), Token::unknown);
 }
 
 void Lexer::skipWhitespace() {
@@ -202,6 +574,322 @@ void Lexer::skipWhitespace() {
   }
 }
 
+// Constant functions
+void Constant::dump(raw_ostream &os) const { os << _num; }
+
+// Symbol functions
+void Symbol::dump(raw_ostream &os) const { os << _name; }
+
+// FunctionCall functions
+void FunctionCall::dump(raw_ostream &os) const {
+  os << _name << "(";
+  for (unsigned i = 0, e = _args.size(); i != e; ++i) {
+    if (i)
+      os << ", ";
+    _args[i]->dump(os);
+  }
+  os << ")";
+}
+
+// Unary functions
+void Unary::dump(raw_ostream &os) const {
+  os << "(";
+  if (_op == Unary::Minus)
+    os << "-";
+  else
+    os << "~";
+  _child->dump(os);
+  os << ")";
+}
+
+// BinOp functions
+void BinOp::dump(raw_ostream &os) const {
+  os << "(";
+  _lhs->dump(os);
+  os << " ";
+  switch (_op) {
+  case Sum:
+    os << "+";
+    break;
+  case Sub:
+    os << "-";
+    break;
+  case Mul:
+    os << "*";
+    break;
+  case Div:
+    os << "/";
+    break;
+  case Shl:
+    os << "<<";
+    break;
+  case Shr:
+    os << ">>";
+    break;
+  case And:
+    os << "&";
+    break;
+  case Or:
+    os << "|";
+    break;
+  case CompareEqual:
+    os << "==";
+    break;
+  case CompareDifferent:
+    os << "!=";
+    break;
+  case CompareLess:
+    os << "<";
+    break;
+  case CompareGreater:
+    os << ">";
+    break;
+  case CompareLessEqual:
+    os << "<=";
+    break;
+  case CompareGreaterEqual:
+    os << ">=";
+    break;
+  }
+  os << " ";
+  _rhs->dump(os);
+  os << ")";
+}
+
+// TernaryConditional functions
+void TernaryConditional::dump(raw_ostream &os) const {
+  _conditional->dump(os);
+  os << " ? ";
+  _trueExpr->dump(os);
+  os << " : ";
+  _falseExpr->dump(os);
+}
+
+// SymbolAssignment functions
+void SymbolAssignment::dump(raw_ostream &os) const {
+  int numParen = 0;
+
+  if (_assignmentVisibility != Normal) {
+    switch (_assignmentVisibility) {
+    case Hidden:
+      os << "HIDDEN(";
+      break;
+    case Provide:
+      os << "PROVIDE(";
+      break;
+    case ProvideHidden:
+      os << "PROVIDE_HIDDEN(";
+      break;
+    default:
+      llvm_unreachable("Unknown visibility");
+    }
+    ++numParen;
+  }
+
+  os << _symbol << " ";
+  switch (_assignmentKind) {
+  case Simple:
+    os << "=";
+    break;
+  case Sum:
+    os << "+=";
+    break;
+  case Sub:
+    os << "-=";
+    break;
+  case Mul:
+    os << "*=";
+    break;
+  case Div:
+    os << "/=";
+    break;
+  case Shl:
+    os << "<<=";
+    break;
+  case Shr:
+    os << ">>=";
+    break;
+  case And:
+    os << "&=";
+    break;
+  case Or:
+    os << "|=";
+    break;
+  }
+
+  os << " ";
+  _expression->dump(os);
+  if (numParen)
+    os << ")";
+  os << ";";
+}
+
+static int dumpSortDirectives(raw_ostream &os, WildcardSortMode sortMode) {
+  switch (sortMode) {
+  case WildcardSortMode::NA:
+    return 0;
+  case WildcardSortMode::ByName:
+    os << "SORT_BY_NAME(";
+    return 1;
+  case WildcardSortMode::ByAlignment:
+    os << "SORT_BY_ALIGNMENT(";
+    return 1;
+  case WildcardSortMode::ByInitPriority:
+    os << "SORT_BY_INIT_PRIORITY(";
+    return 1;
+  case WildcardSortMode::ByNameAndAlignment:
+    os << "SORT_BY_NAME(SORT_BY_ALIGNMENT(";
+    return 2;
+  case WildcardSortMode::ByAlignmentAndName:
+    os << "SORT_BY_ALIGNMENT(SORT_BY_NAME(";
+    return 2;
+  case WildcardSortMode::None:
+    os << "SORT_NONE(";
+    return 1;
+  }
+  return 0;
+}
+
+// InputSectionName functions
+void InputSectionName::dump(raw_ostream &os) const {
+  os << _name;
+}
+
+// InputSectionSortedGroup functions
+static void dumpInputSections(raw_ostream &os,
+                              const std::vector<const InputSection *> &secs) {
+  bool excludeFile = false;
+  bool first = true;
+
+  for (auto &secName : secs) {
+    if (!first)
+      os << " ";
+    first = false;
+    // Coalesce multiple input sections marked with EXCLUDE_FILE in the same
+    // EXCLUDE_FILE() group
+    if (auto inputSec = dyn_cast<InputSectionName>(secName)) {
+      if (!excludeFile && inputSec->hasExcludeFile()) {
+        excludeFile = true;
+        os << "EXCLUDE_FILE(";
+      } else if (excludeFile && !inputSec->hasExcludeFile()) {
+        excludeFile = false;
+        os << ") ";
+      }
+    }
+    secName->dump(os);
+  }
+
+  if (excludeFile)
+    os << ")";
+}
+
+void InputSectionSortedGroup::dump(raw_ostream &os) const {
+  int numParen = dumpSortDirectives(os, _sortMode);
+  dumpInputSections(os, _sections);
+  for (int i = 0; i < numParen; ++i)
+    os << ")";
+}
+
+// InputSectionsCmd functions
+void InputSectionsCmd::dump(raw_ostream &os) const {
+  if (_keep)
+    os << "KEEP(";
+
+  int numParen = dumpSortDirectives(os, _fileSortMode);
+  os << _fileName;
+  for (int i = 0; i < numParen; ++i)
+    os << ")";
+
+  if (_archiveName.size() > 0) {
+    os << ":";
+    numParen = dumpSortDirectives(os, _archiveSortMode);
+    os << _archiveName;
+    for (int i = 0; i < numParen; ++i)
+      os << ")";
+  }
+
+  if (_sections.size() > 0) {
+    os << "(";
+    dumpInputSections(os, _sections);
+    os << ")";
+  }
+
+  if (_keep)
+    os << ")";
+}
+
+// OutputSectionDescription functions
+void OutputSectionDescription::dump(raw_ostream &os) const {
+  if (_discard)
+    os << "/DISCARD/";
+  else
+    os << _sectionName;
+
+  if (_address) {
+    os << " ";
+    _address->dump(os);
+  }
+  os << " :\n";
+
+  if (_at) {
+    os << "  AT(";
+    _at->dump(os);
+    os << ")\n";
+  }
+
+  if (_align) {
+    os << "  ALIGN(";
+    _align->dump(os);
+    os << ")\n";
+  } else if (_alignWithInput) {
+    os << " ALIGN_WITH_INPUT\n";
+  }
+
+  if (_subAlign) {
+    os << "  SUBALIGN(";
+    _subAlign->dump(os);
+    os << ")\n";
+  }
+
+  switch (_constraint) {
+  case C_None:
+    break;
+  case C_OnlyIfRO:
+    os << "ONLY_IF_RO";
+    break;
+  case C_OnlyIfRW:
+    os << "ONLY_IF_RW";
+    break;
+  }
+
+  os << "  {\n";
+  for (auto &command : _outputSectionCommands) {
+    os << "    ";
+    command->dump(os);
+    os << "\n";
+  }
+  os << "  }";
+
+  if (_fillStream.size() > 0) {
+    os << " =";
+    dumpByteStream(os, _fillStream);
+  } else if (_fillExpr) {
+    os << " =";
+    _fillExpr->dump(os);
+  }
+}
+
+// Sections functions
+void Sections::dump(raw_ostream &os) const {
+  os << "SECTIONS\n{\n";
+  for (auto &command : _sectionsCommands) {
+    command->dump(os);
+    os << "\n";
+  }
+  os << "}\n";
+}
+
+// Parser functions
 LinkerScript *Parser::parse() {
   // Get the first token.
   _lex.lex(_tok);
@@ -210,6 +898,9 @@ LinkerScript *Parser::parse() {
     switch (_tok._kind) {
     case Token::eof:
       return &_script;
+    case Token::semicolon:
+      consumeToken();
+      break;
     case Token::kw_output_format: {
       auto outputFormat = parseOutputFormat();
       if (!outputFormat)
@@ -233,6 +924,7 @@ LinkerScript *Parser::parse() {
     }
     case Token::kw_as_needed:
       // Not allowed at top level.
+      error(_tok, "AS_NEEDED not allowed at top level.");
       return nullptr;
     case Token::kw_entry: {
       Entry *entry = parseEntry();
@@ -241,8 +933,33 @@ LinkerScript *Parser::parse() {
       _script._commands.push_back(entry);
       break;
     }
+    case Token::kw_search_dir: {
+      SearchDir *searchDir = parseSearchDir();
+      if (!searchDir)
+        return nullptr;
+      _script._commands.push_back(searchDir);
+      break;
+    }
+    case Token::kw_sections: {
+      Sections *sections = parseSections();
+      if (!sections)
+        return nullptr;
+      _script._commands.push_back(sections);
+      break;
+    }
+    case Token::identifier:
+    case Token::kw_hidden:
+    case Token::kw_provide:
+    case Token::kw_provide_hidden: {
+      const Command *cmd = parseSymbolAssignment();
+      if (!cmd)
+        return nullptr;
+      _script._commands.push_back(cmd);
+      break;
+    }
     default:
       // Unexpected.
+      error(_tok, "expected linker script command");
       return nullptr;
     }
   }
@@ -250,60 +967,304 @@ LinkerScript *Parser::parse() {
   return nullptr;
 }
 
-// Parse OUTPUT_FORMAT(ident)
-OutputFormat *Parser::parseOutputFormat() {
-  assert(_tok._kind == Token::kw_output_format && "Expected OUTPUT_FORMAT!");
+const Expression *Parser::parseFunctionCall() {
+  assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_align) &&
+         "expected function call first tokens");
+  std::vector<const Expression *> params;
+  StringRef name = _tok._range;
+
   consumeToken();
   if (!expectAndConsume(Token::l_paren, "expected ("))
     return nullptr;
 
-  if (_tok._kind != Token::identifier) {
-    error(_tok, "Expected identifier in OUTPUT_FORMAT.");
-    return nullptr;
+  if (_tok._kind == Token::r_paren) {
+    consumeToken();
+    return new (_alloc) FunctionCall(_tok._range, params);
   }
 
-  auto ret = new (_alloc) OutputFormat(_tok._range);
-  consumeToken();
+  if (const Expression *firstParam = parseExpression())
+    params.push_back(firstParam);
+  else
+    return nullptr;
 
-  do {
-    if (isNextToken(Token::comma))
-      consumeToken();
+  while (_tok._kind == Token::comma) {
+    consumeToken();
+    if (const Expression *param = parseExpression())
+      params.push_back(param);
     else
-      break;
-    if (_tok._kind != Token::identifier) {
-      error(_tok, "Expected identifier in OUTPUT_FORMAT.");
       return nullptr;
-    }
-    ret->addOutputFormat(_tok._range);
-    consumeToken();
-  } while (isNextToken(Token::comma));
+  }
 
   if (!expectAndConsume(Token::r_paren, "expected )"))
     return nullptr;
-
-  return ret;
+  return new (_alloc) FunctionCall(name, params);
 }
 
-// Parse OUTPUT_ARCH(ident)
-OutputArch *Parser::parseOutputArch() {
-  assert(_tok._kind == Token::kw_output_arch && "Expected OUTPUT_ARCH!");
-  consumeToken();
-  if (!expectAndConsume(Token::l_paren, "expected ("))
-    return nullptr;
-
-  if (_tok._kind != Token::identifier) {
-    error(_tok, "Expected identifier in OUTPUT_ARCH.");
-    return nullptr;
+bool Parser::expectExprOperand() {
+  if (!(_tok._kind == Token::identifier || _tok._kind == Token::number ||
+        _tok._kind == Token::kw_align || _tok._kind == Token::l_paren ||
+        _tok._kind == Token::minus || _tok._kind == Token::tilde)) {
+    error(_tok, "expected symbol, number, minus, tilde or left parenthesis.");
+    return false;
   }
+  return true;
+}
 
-  auto ret = new (_alloc) OutputArch(_tok._range);
-  consumeToken();
-
-  if (!expectAndConsume(Token::r_paren, "expected )"))
+const Expression *Parser::parseExprOperand() {
+  if (!expectExprOperand())
     return nullptr;
 
-  return ret;
-}
+  switch (_tok._kind) {
+  case Token::identifier: {
+    if (peek()._kind== Token::l_paren)
+      return parseFunctionCall();
+    Symbol *sym = new (_alloc) Symbol(_tok._range);
+    consumeToken();
+    return sym;
+  }
+  case Token::kw_align:
+    return parseFunctionCall();
+  case Token::minus:
+    consumeToken();
+    return new (_alloc) Unary(Unary::Minus, parseExprOperand());
+  case Token::tilde:
+    consumeToken();
+    return new (_alloc) Unary(Unary::Not, parseExprOperand());
+  case Token::number: {
+    auto val = parseNum(_tok._range);
+    if (val.getError()) {
+      error(_tok, "Unrecognized number constant");
+      return nullptr;
+    }
+    Constant *c = new (_alloc) Constant(*val);
+    consumeToken();
+    return c;
+  }
+  case Token::l_paren: {
+    consumeToken();
+    const Expression *expr = parseExpression();
+    if (!expectAndConsume(Token::r_paren, "expected )"))
+      return nullptr;
+    return expr;
+  }
+  default:
+    llvm_unreachable("Unknown token");
+  }
+}
+
+static bool TokenToBinOp(const Token &tok, BinOp::Operation &op,
+                         unsigned &precedence) {
+  switch (tok._kind) {
+  case Token::star:
+    op = BinOp::Mul;
+    precedence = 3;
+    return true;
+  case Token::slash:
+    op = BinOp::Div;
+    precedence = 3;
+    return true;
+  case Token::plus:
+    op = BinOp::Sum;
+    precedence = 4;
+    return true;
+  case Token::minus:
+    op = BinOp::Sub;
+    precedence = 4;
+    return true;
+  case Token::lessless:
+    op = BinOp::Shl;
+    precedence = 5;
+    return true;
+  case Token::greatergreater:
+    op = BinOp::Shr;
+    precedence = 5;
+    return true;
+  case Token::less:
+    op = BinOp::CompareLess;
+    precedence = 6;
+    return true;
+  case Token::greater:
+    op = BinOp::CompareGreater;
+    precedence = 6;
+    return true;
+  case Token::lessequal:
+    op = BinOp::CompareLessEqual;
+    precedence = 6;
+    return true;
+  case Token::greaterequal:
+    op = BinOp::CompareGreaterEqual;
+    precedence = 6;
+    return true;
+  case Token::equalequal:
+    op = BinOp::CompareEqual;
+    precedence = 7;
+    return true;
+  case Token::exclaimequal:
+    op = BinOp::CompareDifferent;
+    precedence = 7;
+    return true;
+  case Token::amp:
+    op = BinOp::And;
+    precedence = 8;
+    return true;
+  case Token::pipe:
+    op = BinOp::Or;
+    precedence = 10;
+    return true;
+  default:
+    break;
+  }
+  return false;
+}
+
+static bool isExpressionOperator(Token tok) {
+  switch (tok._kind) {
+  case Token::star:
+  case Token::slash:
+  case Token::plus:
+  case Token::minus:
+  case Token::lessless:
+  case Token::greatergreater:
+  case Token::less:
+  case Token::greater:
+  case Token::lessequal:
+  case Token::greaterequal:
+  case Token::equalequal:
+  case Token::exclaimequal:
+  case Token::amp:
+  case Token::pipe:
+  case Token::question:
+    return true;
+  default:
+    return false;
+  }
+}
+
+const Expression *Parser::parseExpression(unsigned precedence) {
+  assert(precedence <= 13 && "Invalid precedence value");
+  if (!expectExprOperand())
+    return nullptr;
+
+  const Expression *expr = parseExprOperand();
+  if (!expr)
+    return nullptr;
+
+  BinOp::Operation op;
+  unsigned binOpPrecedence = 0;
+  if (TokenToBinOp(_tok, op, binOpPrecedence)) {
+    if (precedence >= binOpPrecedence)
+      return parseOperatorOperandLoop(expr, precedence);
+    return expr;
+  }
+
+  // Non-binary operators
+  if (_tok._kind == Token::question && precedence >= 13)
+    return parseOperatorOperandLoop(expr, precedence);
+  return expr;
+}
+
+const Expression *Parser::parseOperatorOperandLoop(const Expression *lhs,
+                                                   unsigned highestPrecedence) {
+  assert(highestPrecedence <= 13 && "Invalid precedence value");
+  unsigned precedence = 0;
+  const Expression *binOp = nullptr;
+
+  while (1) {
+    BinOp::Operation op;
+    if (!TokenToBinOp(_tok, op, precedence)) {
+      if (_tok._kind == Token::question && highestPrecedence >= 13)
+        return parseTernaryCondOp(lhs);
+      return binOp;
+    }
+
+    if (precedence > highestPrecedence)
+      return binOp;
+
+    consumeToken();
+    const Expression *rhs = parseExpression(precedence - 1);
+    if (!rhs)
+      return nullptr;
+    binOp = new (_alloc) BinOp(lhs, op, rhs);
+    lhs = binOp;
+  }
+}
+
+const Expression *Parser::parseTernaryCondOp(const Expression *lhs) {
+  assert(_tok._kind == Token::question && "Expected question mark");
+
+  consumeToken();
+
+  // The ternary conditional operator has right-to-left associativity.
+  // To implement this, we allow our children to contain ternary conditional
+  // operators themselves (precedence 13).
+  const Expression *trueExpr = parseExpression(13);
+  if (!trueExpr)
+    return nullptr;
+
+  if (!expectAndConsume(Token::colon, "expected :"))
+    return nullptr;
+
+  const Expression *falseExpr = parseExpression(13);
+  if (!falseExpr)
+    return nullptr;
+
+  return new (_alloc) TernaryConditional(lhs, trueExpr, falseExpr);
+}
+
+// Parse OUTPUT_FORMAT(ident)
+OutputFormat *Parser::parseOutputFormat() {
+  assert(_tok._kind == Token::kw_output_format && "Expected OUTPUT_FORMAT!");
+  consumeToken();
+  if (!expectAndConsume(Token::l_paren, "expected ("))
+    return nullptr;
+
+  if (_tok._kind != Token::identifier) {
+    error(_tok, "Expected identifier in OUTPUT_FORMAT.");
+    return nullptr;
+  }
+
+  auto ret = new (_alloc) OutputFormat(_tok._range);
+  consumeToken();
+
+  do {
+    if (isNextToken(Token::comma))
+      consumeToken();
+    else
+      break;
+    if (_tok._kind != Token::identifier) {
+      error(_tok, "Expected identifier in OUTPUT_FORMAT.");
+      return nullptr;
+    }
+    ret->addOutputFormat(_tok._range);
+    consumeToken();
+  } while (isNextToken(Token::comma));
+
+  if (!expectAndConsume(Token::r_paren, "expected )"))
+    return nullptr;
+
+  return ret;
+}
+
+// Parse OUTPUT_ARCH(ident)
+OutputArch *Parser::parseOutputArch() {
+  assert(_tok._kind == Token::kw_output_arch && "Expected OUTPUT_ARCH!");
+  consumeToken();
+  if (!expectAndConsume(Token::l_paren, "expected ("))
+    return nullptr;
+
+  if (_tok._kind != Token::identifier) {
+    error(_tok, "Expected identifier in OUTPUT_ARCH.");
+    return nullptr;
+  }
+
+  auto ret = new (_alloc) OutputArch(_tok._range);
+  consumeToken();
+
+  if (!expectAndConsume(Token::r_paren, "expected )"))
+    return nullptr;
+
+  return ret;
+}
 
 // Parse GROUP(file ...)
 Group *Parser::parseGroup() {
@@ -386,5 +1347,575 @@ Entry *Parser::parseEntry() {
   return new (_alloc) Entry(entryName);
 }
 
+// Parse SEARCH_DIR(ident)
+SearchDir *Parser::parseSearchDir() {
+  assert(_tok._kind == Token::kw_search_dir && "Expected SEARCH_DIR!");
+  consumeToken();
+  if (!expectAndConsume(Token::l_paren, "expected ("))
+    return nullptr;
+  if (_tok._kind != Token::identifier) {
+    error(_tok, "expected identifier in SEARCH_DIR");
+    return nullptr;
+  }
+  StringRef searchPath(_tok._range);
+  consumeToken();
+  if (!expectAndConsume(Token::r_paren, "expected )"))
+    return nullptr;
+  return new (_alloc) SearchDir(searchPath);
+}
+
+const SymbolAssignment *Parser::parseSymbolAssignment() {
+  assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_hidden ||
+          _tok._kind == Token::kw_provide ||
+          _tok._kind == Token::kw_provide_hidden) &&
+         "Expected identifier!");
+  SymbolAssignment::AssignmentVisibility visibility = SymbolAssignment::Normal;
+  SymbolAssignment::AssignmentKind kind;
+  int numParen = 0;
+
+  switch (_tok._kind) {
+  case Token::kw_hidden:
+    visibility = SymbolAssignment::Hidden;
+    ++numParen;
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return nullptr;
+    break;
+  case Token::kw_provide:
+    visibility = SymbolAssignment::Provide;
+    ++numParen;
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return nullptr;
+    break;
+  case Token::kw_provide_hidden:
+    visibility = SymbolAssignment::ProvideHidden;
+    ++numParen;
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return nullptr;
+    break;
+  default:
+    break;
+  }
+
+  StringRef name = _tok._range;
+  consumeToken();
+
+  // Parse assignment operator (=, +=, -= etc.)
+  switch (_tok._kind) {
+  case Token::equal:
+    kind = SymbolAssignment::Simple;
+    break;
+  case Token::plusequal:
+    kind = SymbolAssignment::Sum;
+    break;
+  case Token::minusequal:
+    kind = SymbolAssignment::Sub;
+    break;
+  case Token::starequal:
+    kind = SymbolAssignment::Mul;
+    break;
+  case Token::slashequal:
+    kind = SymbolAssignment::Div;
+    break;
+  case Token::ampequal:
+    kind = SymbolAssignment::And;
+    break;
+  case Token::pipeequal:
+    kind = SymbolAssignment::Or;
+    break;
+  case Token::lesslessequal:
+    kind = SymbolAssignment::Shl;
+    break;
+  case Token::greatergreaterequal:
+    kind = SymbolAssignment::Shr;
+    break;
+  default:
+    error(_tok, "unexpected token");
+    return nullptr;
+  }
+
+  consumeToken();
+
+  const Expression *expr = nullptr;
+  switch (_tok._kind) {
+  case Token::number:
+  case Token::kw_align:
+  case Token::identifier:
+  case Token::l_paren:
+    expr = parseExpression();
+    if (!expr)
+      return nullptr;
+    break;
+  default:
+    error(_tok, "unexpected token while parsing assignment value.");
+    return nullptr;
+  }
+
+  for (int i = 0; i < numParen; ++i)
+    if (!expectAndConsume(Token::r_paren, "expected )"))
+      return nullptr;
+
+  return new (_alloc) SymbolAssignment(name, expr, kind, visibility);
+}
+
+llvm::ErrorOr<InputSectionsCmd::VectorTy> Parser::parseExcludeFile() {
+  assert(_tok._kind == Token::kw_exclude_file && "Expected EXCLUDE_FILE!");
+  InputSectionsCmd::VectorTy res;
+  consumeToken();
+
+  if (!expectAndConsume(Token::l_paren, "expected ("))
+    return llvm::ErrorOr<InputSectionsCmd::VectorTy>(
+        std::make_error_code(std::errc::io_error));
+
+  while (_tok._kind == Token::identifier) {
+    res.push_back(new (_alloc) InputSectionName(_tok._range, true));
+    consumeToken();
+  }
+
+  if (!expectAndConsume(Token::r_paren, "expected )"))
+    return llvm::ErrorOr<InputSectionsCmd::VectorTy>(
+        std::make_error_code(std::errc::io_error));
+  return llvm::ErrorOr<InputSectionsCmd::VectorTy>(std::move(res));
+}
+
+int Parser::parseSortDirectives(WildcardSortMode &sortMode) {
+  int numParsedDirectives = 0;
+  sortMode = WildcardSortMode::NA;
+
+  if (_tok._kind == Token::kw_sort_by_name) {
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return -1;
+    ++numParsedDirectives;
+    sortMode = WildcardSortMode::ByName;
+  }
+
+  if (_tok._kind == Token::kw_sort_by_init_priority) {
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return -1;
+    ++numParsedDirectives;
+    sortMode = WildcardSortMode::ByInitPriority;
+  }
+
+  if (_tok._kind == Token::kw_sort_by_alignment) {
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return -1;
+    ++numParsedDirectives;
+    if (sortMode != WildcardSortMode::ByName)
+      sortMode = WildcardSortMode::ByAlignment;
+    else
+      sortMode = WildcardSortMode::ByNameAndAlignment;
+  }
+
+  if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_name) {
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return -1;
+    ++numParsedDirectives;
+    if (sortMode == WildcardSortMode::ByAlignment)
+      sortMode = WildcardSortMode::ByAlignmentAndName;
+  }
+
+  if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_alignment) {
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return -1;
+    ++numParsedDirectives;
+  }
+
+  if (numParsedDirectives == 0 && _tok._kind == Token::kw_sort_none) {
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return -1;
+    ++numParsedDirectives;
+    sortMode = WildcardSortMode::None;
+  }
+
+  return numParsedDirectives;
+}
+
+const InputSection *Parser::parseSortedInputSections() {
+  assert((_tok._kind == Token::kw_sort_by_name ||
+          _tok._kind == Token::kw_sort_by_alignment ||
+          _tok._kind == Token::kw_sort_by_init_priority ||
+          _tok._kind == Token::kw_sort_none) &&
+         "Expected SORT directives!");
+
+  WildcardSortMode sortMode = WildcardSortMode::NA;
+  int numParen = parseSortDirectives(sortMode);
+  if (numParen == -1)
+    return nullptr;
+
+  std::vector<const InputSection *> inputSections;
+
+  while (_tok._kind == Token::identifier) {
+    inputSections.push_back(new (_alloc) InputSectionName(_tok._range, false));
+    consumeToken();
+  }
+
+  // Eat "numParen" rparens
+  for (int i = 0, e = numParen; i != e; ++i)
+    if (!expectAndConsume(Token::r_paren, "expected )"))
+      return nullptr;
+
+  return new (_alloc) InputSectionSortedGroup(sortMode, inputSections);
+}
+
+const InputSectionsCmd *Parser::parseInputSectionsCmd() {
+  assert((_tok._kind == Token::identifier || _tok._kind == Token::colon ||
+          _tok._kind == Token::star || _tok._kind == Token::kw_keep ||
+          _tok._kind == Token::kw_sort_by_name ||
+          _tok._kind == Token::kw_sort_by_alignment ||
+          _tok._kind == Token::kw_sort_by_init_priority ||
+          _tok._kind == Token::kw_sort_none) &&
+         "Expected input section first tokens!");
+  int numParen = 1;
+  bool keep = false;
+  WildcardSortMode fileSortMode = WildcardSortMode::NA;
+  WildcardSortMode archiveSortMode = WildcardSortMode::NA;
+  StringRef fileName;
+  StringRef archiveName;
+
+  if (_tok._kind == Token::kw_keep) {
+    consumeToken();
+    if (!expectAndConsume(Token::l_paren, "expected ("))
+      return nullptr;
+    ++numParen;
+    keep = true;
+  }
+
+  // Input name
+  if (_tok._kind != Token::colon) {
+    int numParen = parseSortDirectives(fileSortMode);
+    if (numParen == -1)
+      return nullptr;
+    fileName = _tok._range;
+    consumeToken();
+    if (numParen) {
+      while (numParen--)
+        if (!expectAndConsume(Token::r_paren, "expected )"))
+          return nullptr;
+    }
+  }
+  if (_tok._kind == Token::colon) {
+    consumeToken();
+    if (_tok._kind == Token::identifier ||
+        _tok._kind == Token::kw_sort_by_name ||
+        _tok._kind == Token::kw_sort_by_alignment ||
+        _tok._kind == Token::kw_sort_by_init_priority ||
+        _tok._kind == Token::kw_sort_none) {
+      int numParen = parseSortDirectives(archiveSortMode);
+      if (numParen == -1)
+        return nullptr;
+      archiveName = _tok._range;
+      consumeToken();
+      for (int i = 0; i != numParen; ++i)
+	if (!expectAndConsume(Token::r_paren, "expected )"))
+	  return nullptr;
+    }
+  }
+
+  std::vector<const InputSection *> inputSections;
+
+  if (_tok._kind != Token::l_paren)
+    return new (_alloc)
+        InputSectionsCmd(fileName, archiveName, keep, fileSortMode,
+                         archiveSortMode, inputSections);
+  consumeToken();
+
+  while (_tok._kind == Token::identifier ||
+         _tok._kind == Token::kw_exclude_file ||
+         _tok._kind == Token::kw_sort_by_name ||
+         _tok._kind == Token::kw_sort_by_alignment ||
+         _tok._kind == Token::kw_sort_by_init_priority ||
+         _tok._kind == Token::kw_sort_none) {
+    switch (_tok._kind) {
+    case Token::kw_exclude_file: {
+      auto vec = parseExcludeFile();
+      if (vec.getError())
+        return nullptr;
+      inputSections.insert(inputSections.end(), vec->begin(), vec->end());
+      break;
+    }
+    case Token::star:
+    case Token::identifier: {
+      inputSections.push_back(new (_alloc)
+                                  InputSectionName(_tok._range, false));
+      consumeToken();
+      break;
+    }
+    case Token::kw_sort_by_name:
+    case Token::kw_sort_by_alignment:
+    case Token::kw_sort_by_init_priority:
+    case Token::kw_sort_none: {
+      const InputSection *group = parseSortedInputSections();
+      if (!group)
+        return nullptr;
+      inputSections.push_back(group);
+      break;
+    }
+    default:
+      llvm_unreachable("Unknown token");
+    }
+  }
+
+  for (int i = 0; i < numParen; ++i)
+    if (!expectAndConsume(Token::r_paren, "expected )"))
+      return nullptr;
+  return new (_alloc)
+      InputSectionsCmd(fileName, archiveName, keep, fileSortMode,
+                       archiveSortMode, inputSections);
+}
+
+const OutputSectionDescription *Parser::parseOutputSectionDescription() {
+  assert((_tok._kind == Token::kw_discard || _tok._kind == Token::identifier) &&
+         "Expected /DISCARD/ or identifier!");
+  StringRef sectionName;
+  const Expression *address = nullptr;
+  const Expression *align = nullptr;
+  const Expression *subAlign = nullptr;
+  const Expression *at = nullptr;
+  const Expression *fillExpr = nullptr;
+  StringRef fillStream;
+  bool alignWithInput = false;
+  bool discard = false;
+  OutputSectionDescription::Constraint constraint =
+      OutputSectionDescription::C_None;
+  std::vector<const Command *> outputSectionCommands;
+
+  if (_tok._kind == Token::kw_discard)
+    discard = true;
+  else
+    sectionName = _tok._range;
+  consumeToken();
+
+  if (_tok._kind == Token::number || _tok._kind == Token::identifier ||
+      _tok._kind == Token::kw_align || _tok._kind == Token::l_paren) {
+    address = parseExpression();
+    if (!address)
+      return nullptr;
+  }
+
+  if (!expectAndConsume(Token::colon, "expected :"))
+    return nullptr;
+
+  if (_tok._kind == Token::kw_at) {
+    consumeToken();
+    at = parseExpression();
+    if (!at)
+      return nullptr;
+  }
+
+  if (_tok._kind == Token::kw_align) {
+    consumeToken();
+    align = parseExpression();
+    if (!align)
+      return nullptr;
+  }
+
+  if (_tok._kind == Token::kw_align_with_input) {
+    consumeToken();
+    alignWithInput = true;
+  }
+
+  if (_tok._kind == Token::kw_subalign) {
+    consumeToken();
+    subAlign = parseExpression();
+    if (!subAlign)
+      return nullptr;
+  }
+
+  if (_tok._kind == Token::kw_only_if_ro) {
+    consumeToken();
+    constraint = OutputSectionDescription::C_OnlyIfRO;
+  } else if (_tok._kind == Token::kw_only_if_rw) {
+    consumeToken();
+    constraint = OutputSectionDescription::C_OnlyIfRW;
+  }
+
+  if (!expectAndConsume(Token::l_brace, "expected {"))
+    return nullptr;
+
+  // Parse zero or more output-section-commands
+  while (_tok._kind != Token::r_brace) {
+    switch (_tok._kind) {
+    case Token::semicolon:
+      consumeToken();
+      break;
+    case Token::identifier:
+      switch (peek()._kind) {
+      case Token::equal:
+      case Token::plusequal:
+      case Token::minusequal:
+      case Token::starequal:
+      case Token::slashequal:
+      case Token::ampequal:
+      case Token::pipeequal:
+      case Token::lesslessequal:
+      case Token::greatergreaterequal:
+        if (const Command *cmd = parseSymbolAssignment())
+          outputSectionCommands.push_back(cmd);
+        else
+          return nullptr;
+        break;
+      default:
+        if (const Command *cmd = parseInputSectionsCmd())
+          outputSectionCommands.push_back(cmd);
+        else
+          return nullptr;
+        break;
+      }
+      break;
+    case Token::kw_keep:
+    case Token::star:
+    case Token::colon:
+    case Token::kw_sort_by_name:
+    case Token::kw_sort_by_alignment:
+    case Token::kw_sort_by_init_priority:
+    case Token::kw_sort_none:
+      if (const Command *cmd = parseInputSectionsCmd())
+        outputSectionCommands.push_back(cmd);
+      else
+        return nullptr;
+      break;
+    case Token::kw_hidden:
+    case Token::kw_provide:
+    case Token::kw_provide_hidden:
+      if (const Command *cmd = parseSymbolAssignment())
+        outputSectionCommands.push_back(cmd);
+      else
+        return nullptr;
+      break;
+    default:
+      error(_tok, "expected symbol assignment or input file name.");
+      return nullptr;
+    }
+  }
+
+  if (!expectAndConsume(Token::r_brace, "expected }"))
+    return nullptr;
+
+  if (_tok._kind == Token::equal) {
+    consumeToken();
+    if (_tok._kind != Token::number || !_tok._range.startswith_lower("0x")) {
+      fillExpr = parseExpression();
+      if (!fillExpr)
+        return nullptr;
+    } else {
+      std::string strBuf;
+      if (isExpressionOperator(peek()) ||
+          !parseHexToByteStream(_tok._range.drop_front(2), strBuf)) {
+        fillExpr = parseExpression();
+        if(!fillExpr)
+          return nullptr;
+      } else {
+        char *rawBuf = (char *) _alloc.Allocate(strBuf.size(), 1);
+        memcpy(rawBuf, strBuf.c_str(), strBuf.size());
+        fillStream = StringRef(rawBuf, strBuf.size());
+        consumeToken();
+      }
+    }
+  }
+
+  return new (_alloc) OutputSectionDescription(
+      sectionName, address, align, subAlign, at, fillExpr, fillStream,
+      alignWithInput, discard, constraint, outputSectionCommands);
+}
+
+const Overlay *Parser::parseOverlay() {
+  assert(_tok._kind == Token::kw_overlay && "Expected OVERLAY!");
+  error(_tok, "Overlay description is not yet supported.");
+  return nullptr;
+}
+
+Sections *Parser::parseSections() {
+  assert(_tok._kind == Token::kw_sections && "Expected SECTIONS!");
+  consumeToken();
+  if (!expectAndConsume(Token::l_brace, "expected {"))
+    return nullptr;
+  std::vector<const Command *> sectionsCommands;
+
+  bool unrecognizedToken = false;
+  // Parse zero or more sections-commands
+  while (!unrecognizedToken) {
+    switch (_tok._kind) {
+    case Token::semicolon:
+      consumeToken();
+      break;
+
+    case Token::identifier:
+      switch (peek()._kind) {
+      case Token::equal:
+      case Token::plusequal:
+      case Token::minusequal:
+      case Token::starequal:
+      case Token::slashequal:
+      case Token::ampequal:
+      case Token::pipeequal:
+      case Token::lesslessequal:
+      case Token::greatergreaterequal:
+        if (const Command *cmd = parseSymbolAssignment())
+          sectionsCommands.push_back(cmd);
+        else
+          return nullptr;
+        break;
+      default:
+        if (const Command *cmd = parseOutputSectionDescription())
+          sectionsCommands.push_back(cmd);
+        else
+          return nullptr;
+        break;
+      }
+      break;
+
+    case Token::kw_discard:
+    case Token::star:
+      if (const Command *cmd = parseOutputSectionDescription())
+        sectionsCommands.push_back(cmd);
+      else
+        return nullptr;
+      break;
+
+    case Token::kw_entry:
+      if (const Command *cmd = parseEntry())
+        sectionsCommands.push_back(cmd);
+      else
+        return nullptr;
+      break;
+
+    case Token::kw_hidden:
+    case Token::kw_provide:
+    case Token::kw_provide_hidden:
+      if (const Command *cmd = parseSymbolAssignment())
+        sectionsCommands.push_back(cmd);
+      else
+        return nullptr;
+      break;
+
+    case Token::kw_overlay:
+      if (const Command *cmd = parseOverlay())
+        sectionsCommands.push_back(cmd);
+      else
+        return nullptr;
+      break;
+
+    default:
+      unrecognizedToken = true;
+      break;
+    }
+  }
+
+  if (!expectAndConsume(
+          Token::r_brace,
+          "expected symbol assignment, entry, overlay or output section name."))
+    return nullptr;
+
+  return new (_alloc) Sections(sectionsCommands);
+}
+
 } // end namespace script
 } // end namespace lld

Added: lld/trunk/test/LinkerScript/expr-precedence.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/expr-precedence.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/expr-precedence.test (added)
+++ lld/trunk/test/LinkerScript/expr-precedence.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,34 @@
+/*
+  RUN: linker-script-test %s | FileCheck %s
+*/
+SECTIONS {
+    . = foo >= bar + 1 ? bar : 1- - - -1;
+}
+
+/*
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: identifier: foo
+CHECK: greaterequal: >=
+CHECK: identifier: bar
+CHECK: plus: +
+CHECK: number: 1
+CHECK: question: ?
+CHECK: identifier: bar
+CHECK: colon: :
+CHECK: number: 1
+CHECK: minus: -
+CHECK: minus: -
+CHECK: minus: -
+CHECK: minus: -
+CHECK: number: 1
+CHECK: semicolon: ;
+CHECK: r_brace: }
+CHECK: eof:
+CHECK: SECTIONS
+CHECK: {
+CHECK: . = (foo >= (bar + 1)) ? bar : (1 - (-(-(-1))))
+CHECK: }
+*/

Added: lld/trunk/test/LinkerScript/incomplete-ternary.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/incomplete-ternary.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/incomplete-ternary.test (added)
+++ lld/trunk/test/LinkerScript/incomplete-ternary.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,25 @@
+/*
+  RUN: linker-script-test %s 2> %t | FileCheck %s
+  RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
+*/
+SECTIONS {
+    . = foo ? bar;
+/*
+CHECK-ERR: [[@LINE-2]]:18: error: expected :
+CHECK-ERR-NEXT: {{^    \. = foo \? bar;}}
+CHECK-ERR-NEXT: {{^                 \^}}
+*/
+}
+
+/*
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: identifier: foo
+CHECK: question: ?
+CHECK: identifier: bar
+CHECK: semicolon: ;
+CHECK: r_brace: }
+CHECK: eof:
+*/

Modified: lld/trunk/test/LinkerScript/linker-script-outputformat.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/linker-script-outputformat.test?rev=221126&r1=221125&r2=221126&view=diff
==============================================================================
--- lld/trunk/test/LinkerScript/linker-script-outputformat.test (original)
+++ lld/trunk/test/LinkerScript/linker-script-outputformat.test Sun Nov  2 22:09:51 2014
@@ -8,5 +8,5 @@ CHECK: kw_output_format: OUTPUT_FORMAT
 CHECK: l_paren: (
 CHECK: identifier: elf64-x86-64
 CHECK: r_paren: )
-CHECK: OUTPUT_FORMAT(elf64-x86-64)
+CHECK: OUTPUT_FORMAT("elf64-x86-64")
 */

Modified: lld/trunk/test/LinkerScript/linker-script.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/linker-script.test?rev=221126&r1=221125&r2=221126&view=diff
==============================================================================
--- lld/trunk/test/LinkerScript/linker-script.test (original)
+++ lld/trunk/test/LinkerScript/linker-script.test Sun Nov  2 22:09:51 2014
@@ -35,7 +35,7 @@ CHECK: l_paren: (
 CHECK: identifier: init
 CHECK: r_paren: )
 CHECK: eof:
-CHECK: OUTPUT_FORMAT(elf64-x86-64,elf64-x86-64,elf64-x86-64)
+CHECK: OUTPUT_FORMAT("elf64-x86-64","elf64-x86-64","elf64-x86-64")
 CHECK: GROUP(/lib/x86_64-linux-gnu/libc.so.6 /usr/lib/x86_64-linux-gnu/libc_nonshared.a AS_NEEDED(/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2) -lm -l:libgcc.a)
 CHECK: ENTRY(init)
 */

Added: lld/trunk/test/LinkerScript/missing-entry-symbol.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/missing-entry-symbol.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/missing-entry-symbol.test (added)
+++ lld/trunk/test/LinkerScript/missing-entry-symbol.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,21 @@
+/*
+  RUN: linker-script-test %s 2> %t | FileCheck %s
+  RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
+*/
+SECTIONS {
+    ENTRY()
+/*
+CHECK-ERR: [[@LINE-2]]:11: error: expected identifier in ENTRY
+CHECK-ERR-NEXT: {{^    ENTRY()}}
+CHECK-ERR-NEXT: {{^          \^}}
+*/
+}
+
+/*
+CHECK: l_brace: {
+CHECK: kw_entry: ENTRY
+CHECK: l_paren: (
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: eof:
+*/

Added: lld/trunk/test/LinkerScript/missing-input-file-name.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/missing-input-file-name.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/missing-input-file-name.test (added)
+++ lld/trunk/test/LinkerScript/missing-input-file-name.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,25 @@
+/*
+  RUN: linker-script-test %s 2> %t | FileCheck %s
+  RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
+*/
+SECTIONS {
+    .text : { ()}
+/*
+CHECK-ERR: [[@LINE-2]]:15: error: expected symbol assignment or input file name.
+CHECK-ERR-NEXT: {{^    \.text : { \(\)}}}
+CHECK-ERR-NEXT: {{^              \^}}
+*/
+}
+
+/*
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: identifier: .text
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: l_paren: (
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: r_brace: }
+CHECK: eof:
+*/

Added: lld/trunk/test/LinkerScript/missing-input-sections.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/missing-input-sections.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/missing-input-sections.test (added)
+++ lld/trunk/test/LinkerScript/missing-input-sections.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,27 @@
+/*
+  RUN: linker-script-test %s 2> %t | FileCheck %s
+  RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
+*/
+SECTIONS {
+   .text : { *(+)}
+/*
+CHECK-ERR: [[@LINE-2]]:16: error: expected )
+CHECK-ERR-NEXT: {{^   \.text : { \*\(\+\)}}}
+CHECK-ERR-NEXT: {{^                 \^}}
+*/
+}
+
+/*
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: identifier: .text
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: plus: +
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: r_brace: }
+CHECK: eof:
+*/

Added: lld/trunk/test/LinkerScript/missing-operand.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/missing-operand.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/missing-operand.test (added)
+++ lld/trunk/test/LinkerScript/missing-operand.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,24 @@
+/*
+  RUN: linker-script-test %s 2> %t | FileCheck %s
+  RUN: FileCheck -check-prefix=CHECK-ERR -input-file %t %s
+*/
+SECTIONS {
+    . = foo / ;
+/*
+CHECK-ERR: [[@LINE-2]]:15: error: expected symbol, number, minus, tilde or left parenthesis.
+CHECK-ERR-NEXT: {{^ . = foo / ;}}
+CHECK-ERR-NEXT: {{^           \^}}
+*/
+}
+
+/*
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: identifier: foo
+CHECK: slash: /
+CHECK: semicolon: ;
+CHECK: r_brace: }
+CHECK: eof:
+ */

Added: lld/trunk/test/LinkerScript/missing-output-section-name.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/missing-output-section-name.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/missing-output-section-name.test (added)
+++ lld/trunk/test/LinkerScript/missing-output-section-name.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,25 @@
+/*
+  RUN: linker-script-test %s 2> %t | FileCheck %s
+  RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
+*/
+SECTIONS {
+    : { *()}
+/*
+CHECK-ERR: [[@LINE-2]]:5: error: expected symbol assignment, entry, overlay or output section name
+CHECK-ERR-NEXT: {{^    : { \*\(\)}}}
+CHECK-ERR-NEXT: {{^    \^}}
+*/
+}
+
+/*
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: r_brace: }
+CHECK: eof:
+*/

Added: lld/trunk/test/LinkerScript/missing-symbol.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/missing-symbol.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/missing-symbol.test (added)
+++ lld/trunk/test/LinkerScript/missing-symbol.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,24 @@
+/*
+  RUN: linker-script-test %s 2> %t | FileCheck %s
+  RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
+*/
+SECTIONS {
+    = foo + bar;
+/*
+CHECK-ERR: [[@LINE-2]]:5: error: expected symbol assignment, entry, overlay or output section name.
+CHECK-ERR-NEXT: {{^ = foo \+ bar;}}
+CHECK-ERR-NEXT: {{^ \^}}
+*/
+}
+
+/*
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: equal: =
+CHECK: identifier: foo
+CHECK: plus: +
+CHECK: identifier: bar
+CHECK: semicolon: ;
+CHECK: r_brace: }
+CHECK: eof:
+*/

Added: lld/trunk/test/LinkerScript/sections.test
URL: http://llvm.org/viewvc/llvm-project/lld/trunk/test/LinkerScript/sections.test?rev=221126&view=auto
==============================================================================
--- lld/trunk/test/LinkerScript/sections.test (added)
+++ lld/trunk/test/LinkerScript/sections.test Sun Nov  2 22:09:51 2014
@@ -0,0 +1,618 @@
+/*
+  This test exercises parsing typical commands found in GNU ld linker scripts.
+  RUN: linker-script-test %s | FileCheck %s
+*/
+
+SEARCH_DIR("/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu");
+SECTIONS
+{
+  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
+  .interp         : { *(.interp) }
+  .note.gnu.build-id : { *(.note.gnu.build-id) }
+  .hash           : { *(.hash) }
+  .rela.dyn       :
+    {
+      *(.rela.init)
+      *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+      *(.rela.fini)
+      *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+    }
+  .rela.plt       :
+    {
+      *(.rela.plt)
+      PROVIDE_HIDDEN (__rela_iplt_start = .);
+      *(.rela.iplt)
+      PROVIDE_HIDDEN (__rela_iplt_end = .);
+    }
+  .init           :
+  {
+    KEEP (*(SORT_NONE(.init)))
+  } =0x909090909090909090909090
+  PROVIDE (__etext = .);
+  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+  .exception_ranges   : ONLY_IF_RO { *(.exception_ranges
+  .exception_ranges*) }
+  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
+  /* Exception handling  */
+  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+  .ctors          :
+  {
+    KEEP (*crtbegin.o(.ctors))
+    KEEP (*crtbegin?.o(.ctors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+    KEEP (*(SORT(.ctors.*)))
+    KEEP (*(.ctors))
+  }
+  .dtors          :
+  {
+    KEEP (*crtbegin.o(.dtors))
+    KEEP (*crtbegin?.o(.dtors))
+    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+    KEEP (*(SORT(.dtors.*)))
+    KEEP (*(.dtors))
+  }
+  . = DATA_SEGMENT_RELRO_END (SIZEOF (.got.plt) >= 24 ? 24 : 0, .);
+  .got.plt        : { *(.got.plt)  *(.igot.plt) }
+  .lrodata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
+  }
+  .ldata   ALIGN(CONSTANT (MAXPAGESIZE)) + (. & (CONSTANT (MAXPAGESIZE) - 1)) :
+  {
+    *(.ldata .ldata.* .gnu.linkonce.l.*)
+    . = ALIGN(. != 0 ? 64 / 8 : 1);
+  }
+  . = ALIGN(64 / 8);
+  _end = .; PROVIDE (end = .);
+  . = DATA_SEGMENT_END (.);
+  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
+}
+
+/*
+CHECK: kw_search_dir: SEARCH_DIR
+CHECK: l_paren: (
+CHECK: identifier: /usr/x86_64-linux-gnu/lib64
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: kw_search_dir: SEARCH_DIR
+CHECK: l_paren: (
+CHECK: identifier: =/usr/local/lib/x86_64-linux-gnu
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: kw_sections: SECTIONS
+CHECK: l_brace: {
+CHECK: kw_provide: PROVIDE
+CHECK: l_paren: (
+CHECK: identifier: __executable_start
+CHECK: equal: =
+CHECK: identifier: SEGMENT_START
+CHECK: l_paren: (
+CHECK: identifier: text-segment
+CHECK: comma: ,
+CHECK: number: 0x400000
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: identifier: SEGMENT_START
+CHECK: l_paren: (
+CHECK: identifier: text-segment
+CHECK: comma: ,
+CHECK: number: 0x400000
+CHECK: r_paren: )
+CHECK: plus: +
+CHECK: identifier: SIZEOF_HEADERS
+CHECK: semicolon: ;
+CHECK: identifier: .interp
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .interp
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .note.gnu.build-id
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .note.gnu.build-id
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .hash
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .hash
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .rela.dyn
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .rela.init
+CHECK: r_paren: )
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .rela.text
+CHECK: identifier: .rela.text.*
+CHECK: identifier: .rela.gnu.linkonce.t.*
+CHECK: r_paren: )
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .rela.fini
+CHECK: r_paren: )
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .rela.rodata
+CHECK: identifier: .rela.rodata.*
+CHECK: identifier: .rela.gnu.linkonce.r.*
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .rela.plt
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .rela.plt
+CHECK: r_paren: )
+CHECK: kw_provide_hidden: PROVIDE_HIDDEN
+CHECK: l_paren: (
+CHECK: identifier: __rela_iplt_start
+CHECK: equal: =
+CHECK: identifier: .
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .rela.iplt
+CHECK: r_paren: )
+CHECK: kw_provide_hidden: PROVIDE_HIDDEN
+CHECK: l_paren: (
+CHECK: identifier: __rela_iplt_end
+CHECK: equal: =
+CHECK: identifier: .
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: r_brace: }
+CHECK: identifier: .init
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: kw_sort_none: SORT_NONE
+CHECK: l_paren: (
+CHECK: identifier: .init
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: kw_provide: PROVIDE
+CHECK: l_paren: (
+CHECK: identifier: __etext
+CHECK: equal: =
+CHECK: identifier: .
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: identifier: .eh_frame
+CHECK: colon: :
+CHECK: kw_only_if_ro: ONLY_IF_RO
+CHECK: l_brace: {
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .eh_frame
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .exception_ranges
+CHECK: colon: :
+CHECK: kw_only_if_ro: ONLY_IF_RO
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .exception_ranges
+CHECK: identifier: .exception_ranges*
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: kw_align: ALIGN
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: minus: -
+CHECK: l_paren: (
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: minus: -
+CHECK: identifier: .
+CHECK: r_paren: )
+CHECK: amp: &
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: minus: -
+CHECK: number: 1
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: identifier: DATA_SEGMENT_ALIGN
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: comma: ,
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: COMMONPAGESIZE
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: identifier: .eh_frame
+CHECK: colon: :
+CHECK: kw_only_if_rw: ONLY_IF_RW
+CHECK: l_brace: {
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .eh_frame
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .ctors
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: identifier: *crtbegin.o
+CHECK: l_paren: (
+CHECK: identifier: .ctors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: identifier: *crtbegin?.o
+CHECK: l_paren: (
+CHECK: identifier: .ctors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: kw_exclude_file: EXCLUDE_FILE
+CHECK: l_paren: (
+CHECK: identifier: *crtend.o
+CHECK: identifier: *crtend?.o
+CHECK: r_paren: )
+CHECK: identifier: .ctors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: kw_sort_by_name: SORT
+CHECK: l_paren: (
+CHECK: identifier: .ctors.*
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .ctors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .dtors
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: identifier: *crtbegin.o
+CHECK: l_paren: (
+CHECK: identifier: .dtors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: identifier: *crtbegin?.o
+CHECK: l_paren: (
+CHECK: identifier: .dtors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: kw_exclude_file: EXCLUDE_FILE
+CHECK: l_paren: (
+CHECK: identifier: *crtend.o
+CHECK: identifier: *crtend?.o
+CHECK: r_paren: )
+CHECK: identifier: .dtors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: kw_sort_by_name: SORT
+CHECK: l_paren: (
+CHECK: identifier: .dtors.*
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: kw_keep: KEEP
+CHECK: l_paren: (
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .dtors
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: identifier: DATA_SEGMENT_RELRO_END
+CHECK: l_paren: (
+CHECK: identifier: SIZEOF
+CHECK: l_paren: (
+CHECK: identifier: .got.plt
+CHECK: r_paren: )
+CHECK: greaterequal: >=
+CHECK: number: 24
+CHECK: question: ?
+CHECK: number: 24
+CHECK: colon: :
+CHECK: number: 0
+CHECK: comma: ,
+CHECK: identifier: .
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: identifier: .got.plt
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .got.plt
+CHECK: r_paren: )
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .igot.plt
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .lrodata
+CHECK: kw_align: ALIGN
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: plus: +
+CHECK: l_paren: (
+CHECK: identifier: .
+CHECK: amp: &
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: minus: -
+CHECK: number: 1
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .lrodata
+CHECK: identifier: .lrodata.*
+CHECK: identifier: .gnu.linkonce.lr.*
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: identifier: .ldata
+CHECK: kw_align: ALIGN
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: plus: +
+CHECK: l_paren: (
+CHECK: identifier: .
+CHECK: amp: &
+CHECK: l_paren: (
+CHECK: identifier: CONSTANT
+CHECK: l_paren: (
+CHECK: identifier: MAXPAGESIZE
+CHECK: r_paren: )
+CHECK: minus: -
+CHECK: number: 1
+CHECK: r_paren: )
+CHECK: r_paren: )
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .ldata
+CHECK: identifier: .ldata.*
+CHECK: identifier: .gnu.linkonce.l.*
+CHECK: r_paren: )
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: kw_align: ALIGN
+CHECK: l_paren: (
+CHECK: identifier: .
+CHECK: exclaimequal: !=
+CHECK: number: 0
+CHECK: question: ?
+CHECK: number: 64
+CHECK: slash: /
+CHECK: number: 8
+CHECK: colon: :
+CHECK: number: 1
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: r_brace: }
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: kw_align: ALIGN
+CHECK: l_paren: (
+CHECK: number: 64
+CHECK: slash: /
+CHECK: number: 8
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: identifier: _end
+CHECK: equal: =
+CHECK: identifier: .
+CHECK: semicolon: ;
+CHECK: kw_provide: PROVIDE
+CHECK: l_paren: (
+CHECK: identifier: end
+CHECK: equal: =
+CHECK: identifier: .
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: identifier: .
+CHECK: equal: =
+CHECK: identifier: DATA_SEGMENT_END
+CHECK: l_paren: (
+CHECK: identifier: .
+CHECK: r_paren: )
+CHECK: semicolon: ;
+CHECK: kw_discard: /DISCARD/
+CHECK: colon: :
+CHECK: l_brace: {
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .note.GNU-stack
+CHECK: r_paren: )
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .gnu_debuglink
+CHECK: r_paren: )
+CHECK: star: *
+CHECK: l_paren: (
+CHECK: identifier: .gnu.lto_*
+CHECK: r_paren: )
+CHECK: r_brace: }
+CHECK: r_brace: }
+CHECK: eof: 
+CHECK: SEARCH_DIR("/usr/x86_64-linux-gnu/lib64")
+CHECK: SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu")
+CHECK: SECTIONS
+CHECK: {
+CHECK: PROVIDE(__executable_start = SEGMENT_START(text-segment, 4194304))
+CHECK: . = (SEGMENT_START(text-segment, 4194304) + SIZEOF_HEADERS)
+CHECK: .interp :
+CHECK:   { 
+CHECK:     *(.interp)
+CHECK:   }
+CHECK: .note.gnu.build-id :
+CHECK:   { 
+CHECK:     *(.note.gnu.build-id)
+CHECK:   }
+CHECK: .hash :
+CHECK:   { 
+CHECK:     *(.hash)
+CHECK:   }
+CHECK: .rela.dyn :
+CHECK:   { 
+CHECK:     *(.rela.init)
+CHECK:     *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+CHECK:     *(.rela.fini)
+CHECK:     *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+CHECK:   }
+CHECK: .rela.plt :
+CHECK:   { 
+CHECK:     *(.rela.plt)
+CHECK:     PROVIDE_HIDDEN(__rela_iplt_start = .)
+CHECK:     *(.rela.iplt)
+CHECK:     PROVIDE_HIDDEN(__rela_iplt_end = .)
+CHECK:   }
+CHECK: .init :
+CHECK:   { 
+CHECK:     KEEP(*(SORT_NONE(.init)))
+CHECK:   } =0x909090909090909090909090
+CHECK: PROVIDE(__etext = .)
+CHECK: .eh_frame :
+CHECK: ONLY_IF_RO  { 
+CHECK:     KEEP(*(.eh_frame))
+CHECK:   }
+CHECK: .exception_ranges :
+CHECK: ONLY_IF_RO  { 
+CHECK:     *(.exception_ranges .exception_ranges*)
+CHECK:   }
+CHECK: . = (ALIGN(CONSTANT(MAXPAGESIZE)) - ((CONSTANT(MAXPAGESIZE) - .) & (CONSTANT(MAXPAGESIZE) - 1)))
+CHECK: . = DATA_SEGMENT_ALIGN(CONSTANT(MAXPAGESIZE), CONSTANT(COMMONPAGESIZE))
+CHECK: .eh_frame :
+CHECK: ONLY_IF_RW  { 
+CHECK:     KEEP(*(.eh_frame))
+CHECK:   }
+CHECK: .ctors :
+CHECK:   { 
+CHECK:     KEEP(*crtbegin.o(.ctors))
+CHECK:     KEEP(*crtbegin?.o(.ctors))
+CHECK:     KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o ) .ctors))
+CHECK:     KEEP(*(SORT_BY_NAME(.ctors.*)))
+CHECK:     KEEP(*(.ctors))
+CHECK:   }
+CHECK: .dtors :
+CHECK:   { 
+CHECK:     KEEP(*crtbegin.o(.dtors))
+CHECK:     KEEP(*crtbegin?.o(.dtors))
+CHECK:     KEEP(*(EXCLUDE_FILE(*crtend.o *crtend?.o ) .dtors))
+CHECK:     KEEP(*(SORT_BY_NAME(.dtors.*)))
+CHECK:     KEEP(*(.dtors))
+CHECK:   }
+CHECK: . = DATA_SEGMENT_RELRO_END((SIZEOF(.got.plt) >= 24) ? 24 : 0, .)
+CHECK: .got.plt :
+CHECK:   { 
+CHECK:     *(.got.plt)
+CHECK:     *(.igot.plt)
+CHECK:   }
+CHECK: .lrodata (ALIGN(CONSTANT(MAXPAGESIZE)) + (. & (CONSTANT(MAXPAGESIZE) - 1))) :
+CHECK:   { 
+CHECK:     *(.lrodata .lrodata.* .gnu.linkonce.lr.*)
+CHECK:   }
+CHECK: .ldata (ALIGN(CONSTANT(MAXPAGESIZE)) + (. & (CONSTANT(MAXPAGESIZE) - 1))) :
+CHECK:   { 
+CHECK:     *(.ldata .ldata.* .gnu.linkonce.l.*)
+CHECK:     . = ALIGN((. != 0) ? (64 / 8) : 1)
+CHECK:   }
+CHECK: . = ALIGN((64 / 8))
+CHECK: _end = .
+CHECK: PROVIDE(end = .)
+CHECK: . = DATA_SEGMENT_END(.)
+CHECK:  :
+CHECK:   { 
+CHECK:     *(.note.GNU-stack)
+CHECK:     *(.gnu_debuglink)
+CHECK:     *(.gnu.lto_*)
+CHECK:   }
+CHECK: }
+*/





More information about the llvm-commits mailing list