[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