Files
clang-p2996/lld/lib/ReaderWriter/LinkerScript.cpp
Denis Protivensky 1aaf736d89 [LinkerScript] Add matching of output sections to segments
Add method to query segments for specified output section name.
Return error if the section is assigned to unknown segment.
Check matching of sections to segments during layout on the subject of correctness.
NOTE: no actual functionality of using custom segments is implemented.

Differential Revision: http://reviews.llvm.org/D10359

llvm-svn: 239719
2015-06-15 08:00:51 +00:00

2846 lines
75 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"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/ELF.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_fill)
CASE(kw_group)
CASE(kw_hidden)
CASE(kw_input)
CASE(kw_keep)
CASE(kw_length)
CASE(kw_memory)
CASE(kw_origin)
CASE(kw_phdrs)
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>(make_error_code(llvm::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>(make_error_code(llvm::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>(make_error_code(llvm::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>(make_error_code(llvm::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("FILL", Token::kw_fill)
.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("PHDRS", Token::kw_phdrs)
.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(const SymbolTableTy &symbolTable) const {
return _num;
}
// Symbol functions
void Symbol::dump(raw_ostream &os) const { os << _name; }
ErrorOr<int64_t> Symbol::evalExpr(const 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(const 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(const 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(const 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(const 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 << ")";
}
void FillCmd::dump(raw_ostream &os) const {
os << "FILL(";
dumpByteStream(os, StringRef((const char *)_bytes.begin(), _bytes.size()));
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 << " }";
for (auto && phdr : _phdrs)
os << " : " << phdr;
if (_fillStream.size() > 0) {
os << " =";
dumpByteStream(os, _fillStream);
} else if (_fillExpr) {
os << " =";
_fillExpr->dump(os);
}
}
void PHDR::dump(raw_ostream &os) const {
os << _name << " " << _type;
if (_includeFileHdr)
os << " FILEHDR";
if (_includePHDRs)
os << " PHDRS";
if (_at) {
os << " AT (";
_at->dump(os);
os << ")";
}
if (_flags)
os << " FLAGS (" << _flags << ")";
os << ";\n";
}
static PHDR none("NONE", 0, false, false, NULL, 0);
const PHDR *PHDR::NONE = &none;
void PHDRS::dump(raw_ostream &os) const {
os << "PHDRS\n{\n";
for (auto &&phdr : _phdrs) {
phdr->dump(os);
}
os << "}\n";
}
// 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_phdrs: {
PHDRS *phdrs = parsePHDRS();
if (!phdrs)
return LinkerScriptReaderError::parse_error;
_script._commands.push_back(phdrs);
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>(
make_error_code(llvm::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>(
make_error_code(llvm::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 FillCmd *Parser::parseFillCmd() {
assert(_tok._kind == Token::kw_fill && "Expected FILL!");
consumeToken();
if (!expectAndConsume(Token::l_paren, "expected ("))
return nullptr;
SmallVector<uint8_t, 8> storage;
// If the expression is just a number, it's arbitrary length.
if (_tok._kind == Token::number && peek()._kind == Token::r_paren) {
if (_tok._range.size() > 2 && _tok._range.startswith("0x")) {
StringRef num = _tok._range.substr(2);
for (char c : num) {
unsigned nibble = llvm::hexDigitValue(c);
if (nibble == -1u)
goto not_simple_hex;
storage.push_back(nibble);
}
if (storage.size() % 2 != 0)
storage.insert(storage.begin(), 0);
// Collapse nibbles.
for (std::size_t i = 0, e = storage.size() / 2; i != e; ++i)
storage[i] = (storage[i * 2] << 4) + storage[(i * 2) + 1];
storage.resize(storage.size() / 2);
}
}
not_simple_hex:
const Expression *expr = parseExpression();
if (!expr)
return nullptr;
if (!expectAndConsume(Token::r_paren, "expected )"))
return nullptr;
return new(getAllocator()) FillCmd(*this, storage);
}
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_fill:
if (const Command *cmd = parseFillCmd())
outputSectionCommands.push_back(cmd);
else
return nullptr;
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;
SmallVector<StringRef, 2> phdrs;
while (_tok._kind == Token::colon) {
consumeToken();
if (_tok._kind != Token::identifier) {
error(_tok, "expected program header name");
return nullptr;
}
phdrs.push_back(_tok._range);
consumeToken();
}
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, phdrs);
}
const Overlay *Parser::parseOverlay() {
assert(_tok._kind == Token::kw_overlay && "Expected OVERLAY!");
error(_tok, "Overlay description is not yet supported.");
return nullptr;
}
const PHDR *Parser::parsePHDR() {
assert(_tok._kind == Token::identifier && "Expected identifier!");
StringRef name = _tok._range;
consumeToken();
uint64_t type;
switch (_tok._kind) {
case Token::identifier:
case Token::number:
case Token::l_paren: {
const Expression *expr = parseExpression();
if (!expr)
return nullptr;
Expression::SymbolTableTy PHDRTypes;
#define PHDR_INSERT(x) PHDRTypes.insert(std::make_pair(#x, llvm::ELF::x))
PHDR_INSERT(PT_NULL);
PHDR_INSERT(PT_LOAD);
PHDR_INSERT(PT_DYNAMIC);
PHDR_INSERT(PT_INTERP);
PHDR_INSERT(PT_NOTE);
PHDR_INSERT(PT_SHLIB);
PHDR_INSERT(PT_PHDR);
PHDR_INSERT(PT_TLS);
PHDR_INSERT(PT_LOOS);
PHDR_INSERT(PT_GNU_EH_FRAME);
PHDR_INSERT(PT_GNU_STACK);
PHDR_INSERT(PT_GNU_RELRO);
PHDR_INSERT(PT_SUNW_EH_FRAME);
PHDR_INSERT(PT_SUNW_UNWIND);
PHDR_INSERT(PT_HIOS);
PHDR_INSERT(PT_LOPROC);
PHDR_INSERT(PT_ARM_ARCHEXT);
PHDR_INSERT(PT_ARM_EXIDX);
PHDR_INSERT(PT_ARM_UNWIND);
PHDR_INSERT(PT_MIPS_REGINFO);
PHDR_INSERT(PT_MIPS_RTPROC);
PHDR_INSERT(PT_MIPS_OPTIONS);
PHDR_INSERT(PT_MIPS_ABIFLAGS);
PHDR_INSERT(PT_HIPROC);
#undef PHDR_INSERT
auto t = expr->evalExpr(PHDRTypes);
if (t == LinkerScriptReaderError::unknown_symbol_in_expr) {
error(_tok, "Unknown type");
return nullptr;
}
if (!t)
return nullptr;
type = *t;
break;
}
default:
error(_tok, "expected identifier or expression");
return nullptr;
}
uint64_t flags = 0;
if (_tok._kind == Token::identifier && _tok._range == "FLAGS") {
consumeToken();
if (!expectAndConsume(Token::l_paren, "Expected ("))
return nullptr;
const Expression *flagsExpr = parseExpression();
if (!flagsExpr)
return nullptr;
auto f = flagsExpr->evalExpr();
if (!f)
return nullptr;
flags = *f;
if (!expectAndConsume(Token::r_paren, "Expected )"))
return nullptr;
}
if (!expectAndConsume(Token::semicolon, "Expected ;"))
return nullptr;
return new (getAllocator()) PHDR(name, type, false, false, nullptr, flags);
}
PHDRS *Parser::parsePHDRS() {
assert(_tok._kind == Token::kw_phdrs && "Expected PHDRS!");
consumeToken();
if (!expectAndConsume(Token::l_brace, "expected {"))
return nullptr;
SmallVector<const PHDR *, 8> phdrs;
while (true) {
if (_tok._kind == Token::identifier) {
const PHDR *phdr = parsePHDR();
if (!phdr)
return nullptr;
phdrs.push_back(phdr);
} else
break;
}
if (!expectAndConsume(Token::r_brace, "expected }"))
return nullptr;
return new (getAllocator()) PHDRS(*this, phdrs);
}
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() : _parsedPHDRS(false) {}
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();
}
const llvm::StringSet<> &Sema::getScriptDefinedSymbols() const {
// Do we have cached results?
if (!_definedSymbols.empty())
return _definedSymbols;
// Populate our defined set and return it
for (auto cmd : _layoutCommands)
if (auto sa = dyn_cast<SymbolAssignment>(cmd)) {
StringRef symbol = sa->symbol();
if (!symbol.empty() && symbol != ".")
_definedSymbols.insert(symbol);
}
return _definedSymbols;
}
uint64_t Sema::getLinkerScriptExprValue(StringRef name) const {
auto it = _symbolTable.find(name);
assert (it != _symbolTable.end() && "Invalid symbol name!");
return it->second;
}
std::error_code
Sema::getPHDRsForOutputSection(StringRef name,
std::vector<const PHDR *> &phdrs) const {
// Cache results if not done yet.
if (auto ec = const_cast<Sema *>(this)->buildSectionToPHDR())
return ec;
auto vec = _sectionToPHDR.lookup(name);
std::copy(std::begin(vec), std::end(vec), std::back_inserter(phdrs));
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;
}
std::error_code Sema::buildSectionToPHDR() {
if (_parsedPHDRS)
return std::error_code();
_parsedPHDRS = true;
// No scripts - nothing to do.
if (_scripts.empty() || _layoutCommands.empty())
return std::error_code();
// Collect all header declarations.
llvm::StringMap<const PHDR *> phdrs;
for (auto &parser : _scripts) {
for (auto *cmd : parser->get()->_commands) {
if (auto *ph = dyn_cast<PHDRS>(cmd)) {
for (auto *p : *ph)
phdrs[p->name()] = p;
}
}
}
const bool noPhdrs = phdrs.empty();
// Add NONE header to the map provided there's no user-defined
// header with the same name.
if (!_sectionToPHDR.count(PHDR::NONE->name()))
phdrs[PHDR::NONE->name()] = PHDR::NONE;
// Match output sections to available headers.
llvm::SmallVector<const PHDR *, 2> phdrsCur, phdrsLast { PHDR::NONE };
for (const Command *cmd : _layoutCommands) {
auto osd = dyn_cast<OutputSectionDescription>(cmd);
if (!osd || osd->isDiscarded())
continue;
phdrsCur.clear();
for (StringRef name : osd->PHDRs()) {
auto it = phdrs.find(name);
if (it == phdrs.end()) {
return LinkerScriptReaderError::unknown_phdr_ids;
}
phdrsCur.push_back(it->second);
}
// If no headers and no errors - insert empty headers set.
// If the current set of headers is empty, then use the last non-empty
// set. Otherwise mark the current set to be the last non-empty set for
// successors.
if (noPhdrs)
phdrsCur.clear();
else if (phdrsCur.empty())
phdrsCur = phdrsLast;
else
phdrsLast = phdrsCur;
_sectionToPHDR[osd->name()] = phdrsCur;
}
return std::error_code();
}
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