blob: 67822dc48fe60488fe128f276541dae56b5bd2aa [file] [log] [blame]
//===- ReaderWriter/LinkerScript.cpp ----------------------------*- C++ -*-===//
//
// 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_filehdr)
CASE(kw_fill)
CASE(kw_flags)
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("FILEHDR", Token::kw_filehdr)
.Case("FILL", Token::kw_fill)
.Case("FLAGS", Token::kw_flags)
.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);
}
}
// Special header that discards output sections assigned to it.
static const PHDR PHDR_NONE("NONE", 0, false, false, nullptr, 0);
bool PHDR::isNone() const {
return this == &PHDR_NONE;
}
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";
}
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();
auto *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;
}
auto *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;
const Expression *flagsExpr = nullptr;
bool includeFileHdr = false;
bool includePHDRs = false;
while (_tok._kind != Token::semicolon) {
switch (_tok._kind) {
case Token::kw_filehdr:
if (includeFileHdr) {
error(_tok, "Duplicate FILEHDR attribute");
return nullptr;
}
includeFileHdr = true;
consumeToken();
break;
case Token::kw_phdrs:
if (includePHDRs) {
error(_tok, "Duplicate PHDRS attribute");
return nullptr;
}
includePHDRs = true;
consumeToken();
break;
case Token::kw_flags: {
if (flagsExpr) {
error(_tok, "Duplicate FLAGS attribute");
return nullptr;
}
consumeToken();
if (!expectAndConsume(Token::l_paren, "Expected ("))
return nullptr;
flagsExpr = parseExpression();
if (!flagsExpr)
return nullptr;
auto f = flagsExpr->evalExpr();
if (!f)
return nullptr;
flags = *f;
if (!expectAndConsume(Token::r_paren, "Expected )"))
return nullptr;
} break;
default:
error(_tok, "Unexpected token");
return nullptr;
}
}
if (!expectAndConsume(Token::semicolon, "Expected ;"))
return nullptr;
return new (getAllocator())
PHDR(name, type, includeFileHdr, includePHDRs, 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;
auto *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() : _programPHDR(nullptr) {}
std::error_code Sema::perform() {
llvm::StringMap<const PHDR *> phdrs;
for (auto &parser : _scripts) {
for (const Command *c : parser->get()->_commands) {
if (const auto *sec = dyn_cast<Sections>(c)) {
linearizeAST(sec);
} else if (const auto *ph = dyn_cast<PHDRS>(c)) {
if (auto ec = collectPHDRs(ph, phdrs))
return ec;
}
}
}
return buildSectionToPHDR(phdrs);
}
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;
}
bool Sema::hasPHDRs() const { return !_sectionToPHDR.empty(); }
std::vector<const PHDR *> Sema::getPHDRsForOutputSection(StringRef name) const {
auto vec = _sectionToPHDR.lookup(name);
return std::vector<const PHDR *>(std::begin(vec), std::end(vec));
}
const PHDR *Sema::getProgramPHDR() const { return _programPHDR; }
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()))) {
if (i == name.end())
return false;
++i;
}
break;
case '?':
// Matches any character
++i;
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;
++i;
break;
}
case '\\':
++j;
if (*j != *i)
return false;
++i;
break;
default:
// No wildcard character means we must match exactly the same char
if (*j != *i)
return false;
++i;
break;
}
}
// 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 (const auto &I : _memberNameWildcards) {
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::collectPHDRs(const PHDRS *ph,
llvm::StringMap<const PHDR *> &phdrs) {
bool loadFound = false;
for (auto *p : *ph) {
phdrs[p->name()] = p;
switch (p->type()) {
case llvm::ELF::PT_PHDR:
if (_programPHDR != nullptr)
return LinkerScriptReaderError::extra_program_phdr;
if (loadFound)
return LinkerScriptReaderError::misplaced_program_phdr;
if (!p->hasPHDRs())
return LinkerScriptReaderError::program_phdr_wrong_phdrs;
_programPHDR = p;
break;
case llvm::ELF::PT_LOAD:
// Program header, if available, should have program header table
// mapped in the first loadable segment.
if (!loadFound && _programPHDR && !p->hasPHDRs())
return LinkerScriptReaderError::program_phdr_wrong_phdrs;
loadFound = true;
break;
}
}
return std::error_code();
}
std::error_code Sema::buildSectionToPHDR(llvm::StringMap<const PHDR *> &phdrs) {
const bool noPhdrs = phdrs.empty();
// Add NONE header to the map provided there's no user-defined
// header with the same name.
if (!phdrs.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));
}
}
}
} // end namespace script
} // end namespace lld