llvm-project/lld/lib/ReaderWriter/LinkerScript.cpp

1922 lines
50 KiB
C++

//===- ReaderWriter/LinkerScript.cpp --------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
/// \brief Linker script parser.
///
//===----------------------------------------------------------------------===//
#include "lld/ReaderWriter/LinkerScript.h"
namespace lld {
namespace script {
void Token::dump(raw_ostream &os) const {
switch (_kind) {
#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 llvm::ErrorOr<uint64_t>(std::make_error_code(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 llvm::ErrorOr<uint64_t>(std::make_error_code(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 llvm::ErrorOr<uint64_t>(std::make_error_code(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 llvm::ErrorOr<uint64_t>(std::make_error_code(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':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
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 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
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;
}
}
bool Lexer::canContinueName(char c) const {
switch (c) {
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
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 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
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 '0': case '1': case '2': case '3': case '4': case '5': case '6':
case '7': case '8': case '9':
case '_': case '.': case '$': case '/': case '\\': case '~': case '=':
case '+':
case '[':
case ']':
case '*':
case '?':
case '-':
case ':':
return true;
default:
return false;
}
}
/// 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()) {
tok = Token(_buffer, Token::eof);
return;
}
switch (_buffer[0]) {
case 0:
tok = Token(drop(_buffer, 1), Token::eof);
return;
case '(':
tok = Token(drop(_buffer, 1), Token::l_paren);
return;
case ')':
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(drop(_buffer, 1), Token::comma);
return;
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.
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, end);
tok = Token(word, Token::number);
_buffer = _buffer.drop_front(end);
return;
}
// 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 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);
if (end == StringRef::npos || end == 0)
break;
StringRef word = _buffer.substr(0, end);
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(drop(_buffer, 1), Token::unknown);
}
void Lexer::skipWhitespace() {
while (true) {
if (_buffer.empty())
return;
switch (_buffer[0]) {
case ' ':
case '\r':
case '\n':
case '\t':
_buffer = _buffer.drop_front();
break;
// Potential comment.
case '/':
if (_buffer.size() >= 2 && _buffer[1] == '*') {
// Skip starting /*
_buffer = _buffer.drop_front(2);
// If the next char is also a /, it's not the end.
if (!_buffer.empty() && _buffer[0] == '/')
_buffer = _buffer.drop_front();
// Scan for /'s. We're done if it is preceded by a *.
while (true) {
if (_buffer.empty())
break;
_buffer = _buffer.drop_front();
if (_buffer.data()[-1] == '/' && _buffer.data()[-2] == '*')
break;
}
} else
return;
break;
default:
return;
}
}
}
// 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);
// Parse top level commands.
while (true) {
switch (_tok._kind) {
case Token::eof:
return &_script;
case Token::semicolon:
consumeToken();
break;
case Token::kw_output_format: {
auto outputFormat = parseOutputFormat();
if (!outputFormat)
return nullptr;
_script._commands.push_back(outputFormat);
break;
}
case Token::kw_output_arch: {
auto outputArch = parseOutputArch();
if (!outputArch)
return nullptr;
_script._commands.push_back(outputArch);
break;
}
case Token::kw_group: {
auto group = parseGroup();
if (!group)
return nullptr;
_script._commands.push_back(group);
break;
}
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();
if (!entry)
return nullptr;
_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;
}
}
return nullptr;
}
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::r_paren) {
consumeToken();
return new (_alloc) FunctionCall(_tok._range, params);
}
if (const Expression *firstParam = parseExpression())
params.push_back(firstParam);
else
return nullptr;
while (_tok._kind == Token::comma) {
consumeToken();
if (const Expression *param = parseExpression())
params.push_back(param);
else
return nullptr;
}
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
return new (_alloc) FunctionCall(name, params);
}
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;
}
const Expression *Parser::parseExprOperand() {
if (!expectExprOperand())
return nullptr;
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() {
assert(_tok._kind == Token::kw_group && "Expected GROUP!");
consumeToken();
if (!expectAndConsume(Token::l_paren, "expected ("))
return nullptr;
std::vector<Path> paths;
while (_tok._kind == Token::identifier || _tok._kind == Token::libname ||
_tok._kind == Token::kw_as_needed) {
switch (_tok._kind) {
case Token::identifier:
paths.push_back(Path(_tok._range));
consumeToken();
break;
case Token::libname:
paths.push_back(Path(_tok._range, false, true));
consumeToken();
break;
case Token::kw_as_needed:
if (!parseAsNeeded(paths))
return nullptr;
break;
default:
llvm_unreachable("Invalid token.");
}
}
auto ret = new (_alloc) Group(paths);
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
return ret;
}
// Parse AS_NEEDED(file ...)
bool Parser::parseAsNeeded(std::vector<Path> &paths) {
assert(_tok._kind == Token::kw_as_needed && "Expected AS_NEEDED!");
consumeToken();
if (!expectAndConsume(Token::l_paren, "expected ("))
return false;
while (_tok._kind == Token::identifier || _tok._kind == Token::libname) {
switch (_tok._kind) {
case Token::identifier:
paths.push_back(Path(_tok._range, true, false));
consumeToken();
break;
case Token::libname:
paths.push_back(Path(_tok._range, true, true));
consumeToken();
break;
default:
llvm_unreachable("Invalid token.");
}
}
if (!expectAndConsume(Token::r_paren, "expected )"))
return false;
return true;
}
// Parse ENTRY(ident)
Entry *Parser::parseEntry() {
assert(_tok._kind == Token::kw_entry && "Expected ENTRY!");
consumeToken();
if (!expectAndConsume(Token::l_paren, "expected ("))
return nullptr;
if (_tok._kind != Token::identifier) {
error(_tok, "expected identifier in ENTRY");
return nullptr;
}
StringRef entryName(_tok._range);
consumeToken();
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
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