Files
clang-p2996/lld/lib/ReaderWriter/LinkerScript.cpp
Rafael Auler 9a7e211e8f [LinkerScript] Implement semantics for simple sections mappings
This commit implements the behaviour of the SECTIONS linker script directive,
used to not only define a custom mapping between input and output sections, but
also order input sections in the output file. To do this, we modify
DefaultLayout with hooks at important places that allow us to re-order input
sections according to a custom order. We also add a hook in SegmentChunk to
allow us to calculate linker script expressions while assigning virtual
addresses to the input sections that live in a segment.

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

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

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

http://reviews.llvm.org/D8157

llvm-svn: 232402
2015-03-16 19:55:15 +00:00

2543 lines
67 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_extern)
CASE(kw_group)
CASE(kw_hidden)
CASE(kw_input)
CASE(kw_keep)
CASE(kw_length)
CASE(kw_memory)
CASE(kw_origin)
CASE(kw_provide)
CASE(kw_provide_hidden)
CASE(kw_only_if_ro)
CASE(kw_only_if_rw)
CASE(kw_output)
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 {
// [xX] = hex marker, [hHoO] = type suffix, [MK] = scale suffix.
return strchr("0123456789ABCDEFabcdefxXhHoOMK", c);
}
bool Lexer::canStartName(char c) const {
return strchr(
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$/\\*", c);
}
bool Lexer::canContinueName(char c) const {
return strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789_.$/\\~=+[]*?-:", c);
}
/// 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("EXTERN", Token::kw_extern)
.Case("GROUP", Token::kw_group)
.Case("HIDDEN", Token::kw_hidden)
.Case("INPUT", Token::kw_input)
.Case("KEEP", Token::kw_keep)
.Case("LENGTH", Token::kw_length)
.Case("l", Token::kw_length)
.Case("len", Token::kw_length)
.Case("MEMORY", Token::kw_memory)
.Case("ONLY_IF_RO", Token::kw_only_if_ro)
.Case("ONLY_IF_RW", Token::kw_only_if_rw)
.Case("ORIGIN", Token::kw_origin)
.Case("o", Token::kw_origin)
.Case("org", Token::kw_origin)
.Case("OUTPUT", Token::kw_output)
.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() <= 1 || _buffer[1] != '*')
return;
// 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;
}
break;
default:
return;
}
}
}
// Constant functions
void Constant::dump(raw_ostream &os) const { os << _num; }
ErrorOr<int64_t> Constant::evalExpr(SymbolTableTy &symbolTable) const {
return _num;
}
// Symbol functions
void Symbol::dump(raw_ostream &os) const { os << _name; }
ErrorOr<int64_t> Symbol::evalExpr(SymbolTableTy &symbolTable) const {
auto it = symbolTable.find(_name);
if (it == symbolTable.end())
return LinkerScriptReaderError::unknown_symbol_in_expr;
return it->second;
}
// 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 << ")";
}
ErrorOr<int64_t> FunctionCall::evalExpr(SymbolTableTy &symbolTable) const {
return LinkerScriptReaderError::unrecognized_function_in_expr;
}
// Unary functions
void Unary::dump(raw_ostream &os) const {
os << "(";
if (_op == Unary::Minus)
os << "-";
else
os << "~";
_child->dump(os);
os << ")";
}
ErrorOr<int64_t> Unary::evalExpr(SymbolTableTy &symbolTable) const {
auto child = _child->evalExpr(symbolTable);
if (child.getError())
return child.getError();
int64_t childRes = *child;
switch (_op) {
case Unary::Minus:
return -childRes;
case Unary::Not:
return ~childRes;
}
llvm_unreachable("");
}
// 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 << ")";
}
ErrorOr<int64_t> BinOp::evalExpr(SymbolTableTy &symbolTable) const {
auto lhs = _lhs->evalExpr(symbolTable);
if (lhs.getError())
return lhs.getError();
auto rhs = _rhs->evalExpr(symbolTable);
if (rhs.getError())
return rhs.getError();
int64_t lhsRes = *lhs;
int64_t rhsRes = *rhs;
switch(_op) {
case And: return lhsRes & rhsRes;
case CompareDifferent: return lhsRes != rhsRes;
case CompareEqual: return lhsRes == rhsRes;
case CompareGreater: return lhsRes > rhsRes;
case CompareGreaterEqual: return lhsRes >= rhsRes;
case CompareLess: return lhsRes < rhsRes;
case CompareLessEqual: return lhsRes <= rhsRes;
case Div: return lhsRes / rhsRes;
case Mul: return lhsRes * rhsRes;
case Or: return lhsRes | rhsRes;
case Shl: return lhsRes << rhsRes;
case Shr: return lhsRes >> rhsRes;
case Sub: return lhsRes - rhsRes;
case Sum: return lhsRes + rhsRes;
}
llvm_unreachable("");
}
// TernaryConditional functions
void TernaryConditional::dump(raw_ostream &os) const {
_conditional->dump(os);
os << " ? ";
_trueExpr->dump(os);
os << " : ";
_falseExpr->dump(os);
}
ErrorOr<int64_t>
TernaryConditional::evalExpr(SymbolTableTy &symbolTable) const {
auto conditional = _conditional->evalExpr(symbolTable);
if (conditional.getError())
return conditional.getError();
if (*conditional)
return _trueExpr->evalExpr(symbolTable);
return _falseExpr->evalExpr(symbolTable);
}
// SymbolAssignment functions
void SymbolAssignment::dump(raw_ostream &os) const {
int numParen = 0;
if (_assignmentVisibility != Default) {
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,
llvm::ArrayRef<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 << _memberName;
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";
}
// Memory functions
void MemoryBlock::dump(raw_ostream &os) const {
os << _name;
if (!_attr.empty())
os << " (" << _attr << ")";
os << " : ";
os << "ORIGIN = ";
_origin->dump(os);
os << ", ";
os << "LENGTH = ";
_length->dump(os);
}
void Memory::dump(raw_ostream &os) const {
os << "MEMORY\n{\n";
for (auto &block : _blocks) {
block->dump(os);
os << "\n";
}
os << "}\n";
}
// Extern functions
void Extern::dump(raw_ostream &os) const {
os << "EXTERN(";
for (unsigned i = 0, e = _symbols.size(); i != e; ++i) {
if (i)
os << " ";
os << _symbols[i];
}
os << ")\n";
}
// Parser functions
std::error_code Parser::parse() {
// Get the first token.
_lex.lex(_tok);
// Parse top level commands.
while (true) {
switch (_tok._kind) {
case Token::eof:
return std::error_code();
case Token::semicolon:
consumeToken();
break;
case Token::kw_output: {
auto output = parseOutput();
if (!output)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(output);
break;
}
case Token::kw_output_format: {
auto outputFormat = parseOutputFormat();
if (!outputFormat)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(outputFormat);
break;
}
case Token::kw_output_arch: {
auto outputArch = parseOutputArch();
if (!outputArch)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(outputArch);
break;
}
case Token::kw_input: {
Input *input = parsePathList<Input>();
if (!input)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(input);
break;
}
case Token::kw_group: {
Group *group = parsePathList<Group>();
if (!group)
return LinkerScriptReaderError::parse_error;
_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 LinkerScriptReaderError::parse_error;
case Token::kw_entry: {
Entry *entry = parseEntry();
if (!entry)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(entry);
break;
}
case Token::kw_search_dir: {
SearchDir *searchDir = parseSearchDir();
if (!searchDir)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(searchDir);
break;
}
case Token::kw_sections: {
Sections *sections = parseSections();
if (!sections)
return LinkerScriptReaderError::parse_error;
_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 LinkerScriptReaderError::parse_error;
_script._commands.push_back(cmd);
break;
}
case Token::kw_memory: {
const Command *cmd = parseMemory();
if (!cmd)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(cmd);
break;
}
case Token::kw_extern: {
const Command *cmd = parseExtern();
if (!cmd)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(cmd);
break;
}
default:
// Unexpected.
error(_tok, "expected linker script command");
return LinkerScriptReaderError::parse_error;
}
}
return LinkerScriptReaderError::parse_error;
}
const Expression *Parser::parseFunctionCall() {
assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_align) &&
"expected function call first tokens");
SmallVector<const Expression *, 8> 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(*this, _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(*this, 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(*this, _tok._range);
consumeToken();
return sym;
}
case Token::kw_align:
return parseFunctionCall();
case Token::minus:
consumeToken();
return new (_alloc) Unary(*this, Unary::Minus, parseExprOperand());
case Token::tilde:
consumeToken();
return new (_alloc) Unary(*this, 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(*this, *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(*this, 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(*this, lhs, trueExpr, falseExpr);
}
// Parse OUTPUT(ident)
Output *Parser::parseOutput() {
assert(_tok._kind == Token::kw_output && "Expected OUTPUT");
consumeToken();
if (!expectAndConsume(Token::l_paren, "expected ("))
return nullptr;
if (_tok._kind != Token::identifier) {
error(_tok, "Expected identifier in OUTPUT.");
return nullptr;
}
auto ret = new (_alloc) Output(*this, _tok._range);
consumeToken();
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
return ret;
}
// 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;
}
SmallVector<StringRef, 8> formats;
formats.push_back(_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;
}
formats.push_back(_tok._range);
consumeToken();
} while (isNextToken(Token::comma));
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
return new (_alloc) OutputFormat(*this, formats);
}
// 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(*this, _tok._range);
consumeToken();
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
return ret;
}
// Parse file list for INPUT or GROUP
template<class T> T *Parser::parsePathList() {
consumeToken();
if (!expectAndConsume(Token::l_paren, "expected ("))
return nullptr;
SmallVector<Path, 8> 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.");
}
}
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
return new (_alloc) T(*this, paths);
}
// Parse AS_NEEDED(file ...)
bool Parser::parseAsNeeded(SmallVectorImpl<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(*this, 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(*this, 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::Default;
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(*this, 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(*this, _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;
SmallVector<const InputSection *, 8> inputSections;
while (_tok._kind == Token::identifier) {
inputSections.push_back(new (_alloc)
InputSectionName(*this, _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(*this, 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 memberName;
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;
memberName = _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;
}
}
SmallVector<const InputSection *, 8> inputSections;
if (_tok._kind != Token::l_paren)
return new (_alloc)
InputSectionsCmd(*this, memberName, 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(*this, _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(*this, memberName, 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;
SmallVector<const Command *, 8> 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(
*this, 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;
SmallVector<const Command *, 8> 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(*this, sectionsCommands);
}
Memory *Parser::parseMemory() {
assert(_tok._kind == Token::kw_memory && "Expected MEMORY!");
consumeToken();
if (!expectAndConsume(Token::l_brace, "expected {"))
return nullptr;
SmallVector<const MemoryBlock *, 8> blocks;
bool unrecognizedToken = false;
// Parse zero or more memory block descriptors.
while (!unrecognizedToken) {
if (_tok._kind == Token::identifier) {
StringRef name;
StringRef attrs;
const Expression *origin = nullptr;
const Expression *length = nullptr;
name = _tok._range;
consumeToken();
// Parse optional memory region attributes.
if (_tok._kind == Token::l_paren) {
consumeToken();
if (_tok._kind != Token::identifier) {
error(_tok, "Expected memory attribute string.");
return nullptr;
}
attrs = _tok._range;
consumeToken();
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
}
if (!expectAndConsume(Token::colon, "expected :"))
return nullptr;
// Parse the ORIGIN (base address of memory block).
if (!expectAndConsume(Token::kw_origin, "expected ORIGIN"))
return nullptr;
if (!expectAndConsume(Token::equal, "expected ="))
return nullptr;
origin = parseExpression();
if (!origin)
return nullptr;
if (!expectAndConsume(Token::comma, "expected ,"))
return nullptr;
// Parse the LENGTH (length of memory block).
if (!expectAndConsume(Token::kw_length, "expected LENGTH"))
return nullptr;
if (!expectAndConsume(Token::equal, "expected ="))
return nullptr;
length = parseExpression();
if (!length)
return nullptr;
MemoryBlock *block =
new (_alloc) MemoryBlock(name, attrs, origin, length);
blocks.push_back(block);
} else {
unrecognizedToken = true;
}
}
if (!expectAndConsume(
Token::r_brace,
"expected memory block definition."))
return nullptr;
return new (_alloc) Memory(*this, blocks);
}
Extern *Parser::parseExtern() {
assert(_tok._kind == Token::kw_extern && "Expected EXTERN!");
consumeToken();
if (!expectAndConsume(Token::l_paren, "expected ("))
return nullptr;
// Parse one or more symbols.
SmallVector<StringRef, 8> symbols;
if (_tok._kind != Token::identifier) {
error(_tok, "expected one or more symbols in EXTERN.");
return nullptr;
}
symbols.push_back(_tok._range);
consumeToken();
while (_tok._kind == Token::identifier) {
symbols.push_back(_tok._range);
consumeToken();
}
if (!expectAndConsume(Token::r_paren, "expected symbol in EXTERN."))
return nullptr;
return new (_alloc) Extern(*this, symbols);
}
// Sema member functions
Sema::Sema()
: _scripts(), _layoutCommands(), _memberToLayoutOrder(),
_memberNameWildcards(), _cacheSectionOrder(), _cacheExpressionOrder(),
_deliveredExprs(), _symbolTable() {}
void Sema::perform() {
for (auto &parser : _scripts)
perform(parser->get());
}
bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const {
int a = getLayoutOrder(lhs, true);
int b = getLayoutOrder(rhs, true);
if (a != b) {
if (a < 0)
return false;
if (b < 0)
return true;
return a < b;
}
// If both sections are not mapped anywhere, they have the same order
if (a < 0)
return false;
// If both sections fall into the same layout order, we need to find their
// relative position as written in the (InputSectionsCmd).
return localCompare(a, lhs, rhs);
}
StringRef Sema::getOutputSection(const SectionKey &key) const {
int layoutOrder = getLayoutOrder(key, true);
if (layoutOrder < 0)
return StringRef();
for (int i = layoutOrder - 1; i >= 0; --i) {
if (!isa<OutputSectionDescription>(_layoutCommands[i]))
continue;
const OutputSectionDescription *out =
dyn_cast<OutputSectionDescription>(_layoutCommands[i]);
return out->name();
}
return StringRef();
}
std::vector<const SymbolAssignment *>
Sema::getExprs(const SectionKey &key) {
int layoutOrder = getLayoutOrder(key, false);
auto ans = std::vector<const SymbolAssignment *>();
if (layoutOrder < 0 || _deliveredExprs.count(layoutOrder) > 0)
return ans;
for (int i = layoutOrder - 1; i >= 0; --i) {
if (isa<InputSection>(_layoutCommands[i]))
break;
if (auto assgn = dyn_cast<SymbolAssignment>(_layoutCommands[i]))
ans.push_back(assgn);
}
// Reverse this order so we evaluate the expressions in the original order
// of the linker script
std::reverse(ans.begin(), ans.end());
// Mark this layout number as delivered
_deliveredExprs.insert(layoutOrder);
return ans;
}
std::error_code Sema::evalExpr(const SymbolAssignment *assgn,
uint64_t &curPos) {
_symbolTable[StringRef(".")] = curPos;
auto ans = assgn->expr()->evalExpr(_symbolTable);
if (ans.getError())
return ans.getError();
uint64_t result = *ans;
if (assgn->symbol() == ".") {
curPos = result;
return std::error_code();
}
_symbolTable[assgn->symbol()] = result;
return std::error_code();
}
void Sema::dump() const {
raw_ostream &os = llvm::outs();
os << "Linker script semantics dump\n";
int num = 0;
for (auto &parser : _scripts) {
os << "Dumping script #" << ++num << ":\n";
parser->get()->dump(os);
os << "\n";
}
os << "Dumping rule ids:\n";
for (unsigned i = 0; i < _layoutCommands.size(); ++i) {
os << "LayoutOrder " << i << ":\n";
_layoutCommands[i]->dump(os);
os << "\n\n";
}
}
/// Given a string "pattern" with wildcard characters, return true if it
/// matches "name". This function is useful when checking if a given name
/// pattern written in the linker script, i.e. ".text*", should match
/// ".text.anytext".
static bool wildcardMatch(StringRef pattern, StringRef name) {
auto i = name.begin();
// Check if each char in pattern also appears in our input name, handling
// special wildcard characters.
for (auto j = pattern.begin(), e = pattern.end(); j != e; ++j) {
if (i == name.end())
return false;
switch (*j) {
case '*':
while (!wildcardMatch(pattern.drop_front(j - pattern.begin() + 1),
name.drop_front(i - name.begin() + 1))) {
if (i == name.end())
return false;
++i;
}
break;
case '?':
// Matches any character
break;
case '[': {
// Matches a range of characters specified between brackets
size_t end = pattern.find(']', j - pattern.begin());
if (end == pattern.size())
return false;
StringRef chars = pattern.slice(j - pattern.begin(), end);
if (chars.find(i) == StringRef::npos)
return false;
j = pattern.begin() + end;
break;
}
case '\\':
++j;
if (*j != *i)
return false;
break;
default:
// No wildcard character means we must match exactly the same char
if (*j != *i)
return false;
break;
}
++i;
}
// If our pattern has't consumed the entire string, it is not a match
return i == name.end();
}
int Sema::matchSectionName(int id, const SectionKey &key) const {
const InputSectionsCmd *cmd = dyn_cast<InputSectionsCmd>(_layoutCommands[id]);
if (!cmd || !wildcardMatch(cmd->archiveName(), key.archivePath))
return -1;
while ((size_t)++id < _layoutCommands.size() &&
(isa<InputSection>(_layoutCommands[id]))) {
if (isa<InputSectionSortedGroup>(_layoutCommands[id]))
continue;
const InputSectionName *in =
dyn_cast<InputSectionName>(_layoutCommands[id]);
if (wildcardMatch(in->name(), key.sectionName))
return id;
}
return -1;
}
int Sema::getLayoutOrder(const SectionKey &key, bool coarse) const {
// First check if we already answered this layout question
if (coarse) {
auto entry = _cacheSectionOrder.find(key);
if (entry != _cacheSectionOrder.end())
return entry->second;
} else {
auto entry = _cacheExpressionOrder.find(key);
if (entry != _cacheExpressionOrder.end())
return entry->second;
}
// Try to match exact file name
auto range = _memberToLayoutOrder.equal_range(key.memberPath);
for (auto I = range.first, E = range.second; I != E; ++I) {
int order = I->second;
int exprOrder = -1;
if ((exprOrder = matchSectionName(order, key)) >= 0) {
if (coarse) {
_cacheSectionOrder.insert(std::make_pair(key, order));
return order;
}
_cacheExpressionOrder.insert(std::make_pair(key, exprOrder));
return exprOrder;
}
}
// If we still couldn't find a rule for this input section, try to match
// wildcards
for (auto I = _memberNameWildcards.begin(), E = _memberNameWildcards.end();
I != E; ++I) {
if (!wildcardMatch(I->first, key.memberPath))
continue;
int order = I->second;
int exprOrder = -1;
if ((exprOrder = matchSectionName(order, key)) >= 0) {
if (coarse) {
_cacheSectionOrder.insert(std::make_pair(key, order));
return order;
}
_cacheExpressionOrder.insert(std::make_pair(key, exprOrder));
return exprOrder;
}
}
_cacheSectionOrder.insert(std::make_pair(key, -1));
_cacheExpressionOrder.insert(std::make_pair(key, -1));
return -1;
}
static bool compareSortedNames(WildcardSortMode sortMode, StringRef lhs,
StringRef rhs) {
switch (sortMode) {
case WildcardSortMode::None:
case WildcardSortMode::NA:
return false;
case WildcardSortMode::ByAlignment:
case WildcardSortMode::ByInitPriority:
case WildcardSortMode::ByAlignmentAndName:
assert(false && "Unimplemented sort order");
break;
case WildcardSortMode::ByName:
return lhs.compare(rhs) < 0;
case WildcardSortMode::ByNameAndAlignment:
int compare = lhs.compare(rhs);
if (compare != 0)
return compare < 0;
return compareSortedNames(WildcardSortMode::ByAlignment, lhs, rhs);
}
return false;
}
static bool sortedGroupContains(const InputSectionSortedGroup *cmd,
const Sema::SectionKey &key) {
for (const InputSection *child : *cmd) {
if (auto i = dyn_cast<InputSectionName>(child)) {
if (wildcardMatch(i->name(), key.sectionName))
return true;
continue;
}
auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(child);
assert(sortedGroup && "Expected InputSectionSortedGroup object");
if (sortedGroupContains(sortedGroup, key))
return true;
}
return false;
}
bool Sema::localCompare(int order, const SectionKey &lhs,
const SectionKey &rhs) const {
const InputSectionsCmd *cmd =
dyn_cast<InputSectionsCmd>(_layoutCommands[order]);
assert(cmd && "Invalid InputSectionsCmd index");
if (lhs.archivePath != rhs.archivePath)
return compareSortedNames(cmd->archiveSortMode(), lhs.archivePath,
rhs.archivePath);
if (lhs.memberPath != rhs.memberPath)
return compareSortedNames(cmd->fileSortMode(), lhs.memberPath,
rhs.memberPath);
// Both sections come from the same exact same file and rule. Start walking
// through input section names as written in the linker script and the
// first one to match will have higher priority.
for (const InputSection *inputSection : *cmd) {
if (auto i = dyn_cast<InputSectionName>(inputSection)) {
// If both match, return false (both have equal priority)
// If rhs match, return false (rhs has higher priority)
if (wildcardMatch(i->name(), rhs.sectionName))
return false;
// If lhs matches first, it has priority over rhs
if (wildcardMatch(i->name(), lhs.sectionName))
return true;
continue;
}
// Handle sorted subgroups specially
auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection);
assert(sortedGroup && "Expected InputSectionSortedGroup object");
bool a = sortedGroupContains(sortedGroup, lhs);
bool b = sortedGroupContains(sortedGroup, rhs);
if (a && !b)
return false;
if (b && !a)
return true;
if (!a && !a)
continue;
return compareSortedNames(sortedGroup->sortMode(), lhs.sectionName,
rhs.sectionName);
}
llvm_unreachable("");
return false;
}
static bool hasWildcard(StringRef name) {
for (auto ch : name)
if (ch == '*' || ch == '?' || ch == '[' || ch == '\\')
return true;
return false;
}
void Sema::linearizeAST(const InputSection *inputSection) {
if (isa<InputSectionName>(inputSection)) {
_layoutCommands.push_back(inputSection);
return;
}
auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection);
assert(sortedGroup && "Expected InputSectionSortedGroup object");
for (const InputSection *child : *sortedGroup) {
linearizeAST(child);
}
}
void Sema::linearizeAST(const InputSectionsCmd *inputSections) {
StringRef memberName = inputSections->memberName();
// Populate our maps for fast lookup of InputSectionsCmd
if (hasWildcard(memberName))
_memberNameWildcards.push_back(
std::make_pair(memberName, (int)_layoutCommands.size()));
else if (!memberName.empty())
_memberToLayoutOrder.insert(
std::make_pair(memberName.str(), (int)_layoutCommands.size()));
_layoutCommands.push_back(inputSections);
for (const InputSection *inputSection : *inputSections)
linearizeAST(inputSection);
}
void Sema::linearizeAST(const Sections *sections) {
for (const Command *sectionCommand : *sections) {
if (isa<SymbolAssignment>(sectionCommand)) {
_layoutCommands.push_back(sectionCommand);
continue;
}
if (!isa<OutputSectionDescription>(sectionCommand))
continue;
_layoutCommands.push_back(sectionCommand);
auto *outSection = dyn_cast<OutputSectionDescription>(sectionCommand);
for (const Command *outSecCommand : *outSection) {
if (isa<SymbolAssignment>(outSecCommand)) {
_layoutCommands.push_back(outSecCommand);
continue;
}
if (!isa<InputSectionsCmd>(outSecCommand))
continue;
linearizeAST(dyn_cast<InputSectionsCmd>(outSecCommand));
}
}
}
void Sema::perform(const LinkerScript *ls) {
for (const Command *c : ls->_commands) {
if (const Sections *sec = dyn_cast<Sections>(c))
linearizeAST(sec);
}
}
} // End namespace script
} // end namespace lld