forked from OSchip/llvm-project
1922 lines
50 KiB
C++
1922 lines
50 KiB
C++
//===- ReaderWriter/LinkerScript.cpp --------------------------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// \brief Linker script parser.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lld/ReaderWriter/LinkerScript.h"
|
|
|
|
namespace lld {
|
|
namespace script {
|
|
void Token::dump(raw_ostream &os) const {
|
|
switch (_kind) {
|
|
#define CASE(name) \
|
|
case Token::name: \
|
|
os << #name ": "; \
|
|
break;
|
|
CASE(unknown)
|
|
CASE(eof)
|
|
CASE(exclaim)
|
|
CASE(exclaimequal)
|
|
CASE(amp)
|
|
CASE(ampequal)
|
|
CASE(l_paren)
|
|
CASE(r_paren)
|
|
CASE(star)
|
|
CASE(starequal)
|
|
CASE(plus)
|
|
CASE(plusequal)
|
|
CASE(comma)
|
|
CASE(minus)
|
|
CASE(minusequal)
|
|
CASE(slash)
|
|
CASE(slashequal)
|
|
CASE(number)
|
|
CASE(colon)
|
|
CASE(semicolon)
|
|
CASE(less)
|
|
CASE(lessequal)
|
|
CASE(lessless)
|
|
CASE(lesslessequal)
|
|
CASE(equal)
|
|
CASE(equalequal)
|
|
CASE(greater)
|
|
CASE(greaterequal)
|
|
CASE(greatergreater)
|
|
CASE(greatergreaterequal)
|
|
CASE(question)
|
|
CASE(identifier)
|
|
CASE(libname)
|
|
CASE(kw_align)
|
|
CASE(kw_align_with_input)
|
|
CASE(kw_as_needed)
|
|
CASE(kw_at)
|
|
CASE(kw_discard)
|
|
CASE(kw_entry)
|
|
CASE(kw_exclude_file)
|
|
CASE(kw_group)
|
|
CASE(kw_hidden)
|
|
CASE(kw_keep)
|
|
CASE(kw_provide)
|
|
CASE(kw_provide_hidden)
|
|
CASE(kw_only_if_ro)
|
|
CASE(kw_only_if_rw)
|
|
CASE(kw_output_arch)
|
|
CASE(kw_output_format)
|
|
CASE(kw_overlay)
|
|
CASE(kw_search_dir)
|
|
CASE(kw_sections)
|
|
CASE(kw_sort_by_alignment)
|
|
CASE(kw_sort_by_init_priority)
|
|
CASE(kw_sort_by_name)
|
|
CASE(kw_sort_none)
|
|
CASE(kw_subalign)
|
|
CASE(l_brace)
|
|
CASE(pipe)
|
|
CASE(pipeequal)
|
|
CASE(r_brace)
|
|
CASE(tilde)
|
|
#undef CASE
|
|
}
|
|
os << _range << "\n";
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseDecimal(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res *= 10;
|
|
if (c < '0' || c > '9')
|
|
return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error));
|
|
res += c - '0';
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseOctal(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res <<= 3;
|
|
if (c < '0' || c > '7')
|
|
return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error));
|
|
res += c - '0';
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseBinary(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res <<= 1;
|
|
if (c != '0' && c != '1')
|
|
return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error));
|
|
res += c - '0';
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseHex(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res <<= 4;
|
|
if (c >= '0' && c <= '9')
|
|
res += c - '0';
|
|
else if (c >= 'a' && c <= 'f')
|
|
res += c - 'a' + 10;
|
|
else if (c >= 'A' && c <= 'F')
|
|
res += c - 'A' + 10;
|
|
else
|
|
return llvm::ErrorOr<uint64_t>(std::make_error_code(std::errc::io_error));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static bool parseHexToByteStream(StringRef str, std::string &buf) {
|
|
unsigned char byte = 0;
|
|
bool dumpByte = str.size() % 2;
|
|
for (auto &c : str) {
|
|
byte <<= 4;
|
|
if (c >= '0' && c <= '9')
|
|
byte += c - '0';
|
|
else if (c >= 'a' && c <= 'f')
|
|
byte += c - 'a' + 10;
|
|
else if (c >= 'A' && c <= 'F')
|
|
byte += c - 'A' + 10;
|
|
else
|
|
return false;
|
|
if (!dumpByte) {
|
|
dumpByte = true;
|
|
continue;
|
|
}
|
|
buf.push_back(byte);
|
|
byte = 0;
|
|
dumpByte = false;
|
|
}
|
|
return !dumpByte;
|
|
}
|
|
|
|
static void dumpByteStream(raw_ostream &os, StringRef stream) {
|
|
os << "0x";
|
|
for (auto &c : stream) {
|
|
unsigned char firstNibble = c >> 4 & 0xF;
|
|
if (firstNibble > 9)
|
|
os << (char) ('A' + firstNibble - 10);
|
|
else
|
|
os << (char) ('0' + firstNibble);
|
|
unsigned char secondNibble = c & 0xF;
|
|
if (secondNibble > 9)
|
|
os << (char) ('A' + secondNibble - 10);
|
|
else
|
|
os << (char) ('0' + secondNibble);
|
|
}
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseNum(StringRef str) {
|
|
unsigned multiplier = 1;
|
|
enum NumKind { decimal, hex, octal, binary };
|
|
NumKind kind = llvm::StringSwitch<NumKind>(str)
|
|
.StartsWith("0x", hex)
|
|
.StartsWith("0X", hex)
|
|
.StartsWith("0", octal)
|
|
.Default(decimal);
|
|
|
|
// Parse scale
|
|
if (str.endswith("K")) {
|
|
multiplier = 1 << 10;
|
|
str = str.drop_back();
|
|
} else if (str.endswith("M")) {
|
|
multiplier = 1 << 20;
|
|
str = str.drop_back();
|
|
}
|
|
|
|
// Parse type
|
|
if (str.endswith_lower("o")) {
|
|
kind = octal;
|
|
str = str.drop_back();
|
|
} else if (str.endswith_lower("h")) {
|
|
kind = hex;
|
|
str = str.drop_back();
|
|
} else if (str.endswith_lower("d")) {
|
|
kind = decimal;
|
|
str = str.drop_back();
|
|
} else if (str.endswith_lower("b")) {
|
|
kind = binary;
|
|
str = str.drop_back();
|
|
}
|
|
|
|
llvm::ErrorOr<uint64_t> res(0);
|
|
switch (kind) {
|
|
case hex:
|
|
if (str.startswith_lower("0x"))
|
|
str = str.drop_front(2);
|
|
res = parseHex(str);
|
|
break;
|
|
case octal:
|
|
res = parseOctal(str);
|
|
break;
|
|
case decimal:
|
|
res = parseDecimal(str);
|
|
break;
|
|
case binary:
|
|
res = parseBinary(str);
|
|
break;
|
|
}
|
|
if (res.getError())
|
|
return res;
|
|
|
|
*res = *res * multiplier;
|
|
return res;
|
|
}
|
|
|
|
bool Lexer::canStartNumber(char c) const {
|
|
return '0' <= c && c <= '9';
|
|
}
|
|
|
|
bool Lexer::canContinueNumber(char c) const {
|
|
switch (c) {
|
|
// Digits
|
|
case '0': case '1': case '2': case '3': case '4': case '5': case '6':
|
|
case '7': case '8': case '9':
|
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
|
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
|
// Hex marker
|
|
case 'x': case 'X':
|
|
// Type suffix
|
|
case 'h': case 'H': case 'o': case 'O':
|
|
// Scale suffix
|
|
case 'M': case 'K':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Lexer::canStartName(char c) const {
|
|
switch (c) {
|
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
|
|
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
|
|
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
|
|
case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
|
|
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
|
|
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
|
|
case 'v': case 'w': case 'x': case 'y': case 'z':
|
|
case '_': case '.': case '$': case '/': case '\\':
|
|
case '*':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool Lexer::canContinueName(char c) const {
|
|
switch (c) {
|
|
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G':
|
|
case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N':
|
|
case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U':
|
|
case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g':
|
|
case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n':
|
|
case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u':
|
|
case 'v': case 'w': case 'x': case 'y': case 'z':
|
|
case '0': case '1': case '2': case '3': case '4': case '5': case '6':
|
|
case '7': case '8': case '9':
|
|
case '_': case '.': case '$': case '/': case '\\': case '~': case '=':
|
|
case '+':
|
|
case '[':
|
|
case ']':
|
|
case '*':
|
|
case '?':
|
|
case '-':
|
|
case ':':
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Helper function to split a StringRef in two at the nth character.
|
|
/// The StringRef s is updated, while the function returns the n first
|
|
/// characters.
|
|
static StringRef drop(StringRef &s, int n) {
|
|
StringRef res = s.substr(0, n);
|
|
s = s.drop_front(n);
|
|
return res;
|
|
}
|
|
|
|
void Lexer::lex(Token &tok) {
|
|
skipWhitespace();
|
|
if (_buffer.empty()) {
|
|
tok = Token(_buffer, Token::eof);
|
|
return;
|
|
}
|
|
switch (_buffer[0]) {
|
|
case 0:
|
|
tok = Token(drop(_buffer, 1), Token::eof);
|
|
return;
|
|
case '(':
|
|
tok = Token(drop(_buffer, 1), Token::l_paren);
|
|
return;
|
|
case ')':
|
|
tok = Token(drop(_buffer, 1), Token::r_paren);
|
|
return;
|
|
case '{':
|
|
tok = Token(drop(_buffer, 1), Token::l_brace);
|
|
return;
|
|
case '}':
|
|
tok = Token(drop(_buffer, 1), Token::r_brace);
|
|
return;
|
|
case '=':
|
|
if (_buffer.startswith("==")) {
|
|
tok = Token(drop(_buffer, 2), Token::equalequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::equal);
|
|
return;
|
|
case '!':
|
|
if (_buffer.startswith("!=")) {
|
|
tok = Token(drop(_buffer, 2), Token::exclaimequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::exclaim);
|
|
return;
|
|
case ',':
|
|
tok = Token(drop(_buffer, 1), Token::comma);
|
|
return;
|
|
case ';':
|
|
tok = Token(drop(_buffer, 1), Token::semicolon);
|
|
return;
|
|
case ':':
|
|
tok = Token(drop(_buffer, 1), Token::colon);
|
|
return;
|
|
case '&':
|
|
if (_buffer.startswith("&=")) {
|
|
tok = Token(drop(_buffer, 2), Token::ampequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::amp);
|
|
return;
|
|
case '|':
|
|
if (_buffer.startswith("|=")) {
|
|
tok = Token(drop(_buffer, 2), Token::pipeequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::pipe);
|
|
return;
|
|
case '+':
|
|
if (_buffer.startswith("+=")) {
|
|
tok = Token(drop(_buffer, 2), Token::plusequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::plus);
|
|
return;
|
|
case '-': {
|
|
if (_buffer.startswith("-=")) {
|
|
tok = Token(drop(_buffer, 2), Token::minusequal);
|
|
return;
|
|
}
|
|
if (!_buffer.startswith("-l")) {
|
|
tok = Token(drop(_buffer, 1), Token::minus);
|
|
return;
|
|
}
|
|
// -l<lib name>
|
|
_buffer = _buffer.drop_front(2);
|
|
StringRef::size_type start = 0;
|
|
if (_buffer[start] == ':')
|
|
++start;
|
|
if (!canStartName(_buffer[start]))
|
|
// Create 'unknown' token.
|
|
break;
|
|
auto libNameEnd = std::find_if(_buffer.begin() + start + 1, _buffer.end(),
|
|
[=](char c) { return !canContinueName(c); });
|
|
StringRef::size_type libNameLen =
|
|
std::distance(_buffer.begin(), libNameEnd);
|
|
tok = Token(_buffer.substr(0, libNameLen), Token::libname);
|
|
_buffer = _buffer.drop_front(libNameLen);
|
|
return;
|
|
}
|
|
case '<':
|
|
if (_buffer.startswith("<<=")) {
|
|
tok = Token(drop(_buffer, 3), Token::lesslessequal);
|
|
return;
|
|
}
|
|
if (_buffer.startswith("<<")) {
|
|
tok = Token(drop(_buffer, 2), Token::lessless);
|
|
return;
|
|
}
|
|
if (_buffer.startswith("<=")) {
|
|
tok = Token(drop(_buffer, 2), Token::lessequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::less);
|
|
return;
|
|
case '>':
|
|
if (_buffer.startswith(">>=")) {
|
|
tok = Token(drop(_buffer, 3), Token::greatergreaterequal);
|
|
return;
|
|
}
|
|
if (_buffer.startswith(">>")) {
|
|
tok = Token(drop(_buffer, 2), Token::greatergreater);
|
|
return;
|
|
}
|
|
if (_buffer.startswith(">=")) {
|
|
tok = Token(drop(_buffer, 2), Token::greaterequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::greater);
|
|
return;
|
|
case '~':
|
|
tok = Token(drop(_buffer, 1), Token::tilde);
|
|
return;
|
|
case '\"': case '\'': {
|
|
// Handle quoted strings. They are treated as identifiers for
|
|
// simplicity.
|
|
char c = _buffer[0];
|
|
_buffer = _buffer.drop_front();
|
|
auto quotedStringEnd = _buffer.find(c);
|
|
if (quotedStringEnd == StringRef::npos || quotedStringEnd == 0)
|
|
break;
|
|
StringRef word = _buffer.substr(0, quotedStringEnd);
|
|
tok = Token(word, Token::identifier);
|
|
_buffer = _buffer.drop_front(quotedStringEnd + 1);
|
|
return;
|
|
}
|
|
default:
|
|
// Handle literal numbers
|
|
if (canStartNumber(_buffer[0])) {
|
|
auto endIter = std::find_if(_buffer.begin(), _buffer.end(), [=](char c) {
|
|
return !canContinueNumber(c);
|
|
});
|
|
StringRef::size_type end = endIter == _buffer.end()
|
|
? StringRef::npos
|
|
: std::distance(_buffer.begin(), endIter);
|
|
if (end == StringRef::npos || end == 0)
|
|
break;
|
|
StringRef word = _buffer.substr(0, end);
|
|
tok = Token(word, Token::number);
|
|
_buffer = _buffer.drop_front(end);
|
|
return;
|
|
}
|
|
// Handle slashes '/', which can be either an operator inside an expression
|
|
// or the beginning of an identifier
|
|
if (_buffer.startswith("/=")) {
|
|
tok = Token(drop(_buffer, 2), Token::slashequal);
|
|
return;
|
|
}
|
|
if (_buffer[0] == '/' && _buffer.size() > 1 &&
|
|
!canContinueName(_buffer[1])) {
|
|
tok = Token(drop(_buffer, 1), Token::slash);
|
|
return;
|
|
}
|
|
// Handle stars '*'
|
|
if (_buffer.startswith("*=")) {
|
|
tok = Token(drop(_buffer, 2), Token::starequal);
|
|
return;
|
|
}
|
|
if (_buffer[0] == '*' && _buffer.size() > 1 &&
|
|
!canContinueName(_buffer[1])) {
|
|
tok = Token(drop(_buffer, 1), Token::star);
|
|
return;
|
|
}
|
|
// Handle questions '?'
|
|
if (_buffer[0] == '?' && _buffer.size() > 1 &&
|
|
!canContinueName(_buffer[1])) {
|
|
tok = Token(drop(_buffer, 1), Token::question);
|
|
return;
|
|
}
|
|
// keyword or identifier.
|
|
if (!canStartName(_buffer[0]))
|
|
break;
|
|
auto endIter = std::find_if(_buffer.begin() + 1, _buffer.end(),
|
|
[=](char c) { return !canContinueName(c); });
|
|
StringRef::size_type end = endIter == _buffer.end()
|
|
? StringRef::npos
|
|
: std::distance(_buffer.begin(), endIter);
|
|
if (end == StringRef::npos || end == 0)
|
|
break;
|
|
StringRef word = _buffer.substr(0, end);
|
|
Token::Kind kind =
|
|
llvm::StringSwitch<Token::Kind>(word)
|
|
.Case("ALIGN", Token::kw_align)
|
|
.Case("ALIGN_WITH_INPUT", Token::kw_align_with_input)
|
|
.Case("AS_NEEDED", Token::kw_as_needed)
|
|
.Case("AT", Token::kw_at)
|
|
.Case("ENTRY", Token::kw_entry)
|
|
.Case("EXCLUDE_FILE", Token::kw_exclude_file)
|
|
.Case("GROUP", Token::kw_group)
|
|
.Case("HIDDEN", Token::kw_hidden)
|
|
.Case("KEEP", Token::kw_keep)
|
|
.Case("ONLY_IF_RO", Token::kw_only_if_ro)
|
|
.Case("ONLY_IF_RW", Token::kw_only_if_rw)
|
|
.Case("OUTPUT_ARCH", Token::kw_output_arch)
|
|
.Case("OUTPUT_FORMAT", Token::kw_output_format)
|
|
.Case("OVERLAY", Token::kw_overlay)
|
|
.Case("PROVIDE", Token::kw_provide)
|
|
.Case("PROVIDE_HIDDEN", Token::kw_provide_hidden)
|
|
.Case("SEARCH_DIR", Token::kw_search_dir)
|
|
.Case("SECTIONS", Token::kw_sections)
|
|
.Case("SORT", Token::kw_sort_by_name)
|
|
.Case("SORT_BY_ALIGNMENT", Token::kw_sort_by_alignment)
|
|
.Case("SORT_BY_INIT_PRIORITY", Token::kw_sort_by_init_priority)
|
|
.Case("SORT_BY_NAME", Token::kw_sort_by_name)
|
|
.Case("SORT_NONE", Token::kw_sort_none)
|
|
.Case("SUBALIGN", Token::kw_subalign)
|
|
.Case("/DISCARD/", Token::kw_discard)
|
|
.Default(Token::identifier);
|
|
tok = Token(word, kind);
|
|
_buffer = _buffer.drop_front(end);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::unknown);
|
|
}
|
|
|
|
void Lexer::skipWhitespace() {
|
|
while (true) {
|
|
if (_buffer.empty())
|
|
return;
|
|
switch (_buffer[0]) {
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t':
|
|
_buffer = _buffer.drop_front();
|
|
break;
|
|
// Potential comment.
|
|
case '/':
|
|
if (_buffer.size() >= 2 && _buffer[1] == '*') {
|
|
// Skip starting /*
|
|
_buffer = _buffer.drop_front(2);
|
|
// If the next char is also a /, it's not the end.
|
|
if (!_buffer.empty() && _buffer[0] == '/')
|
|
_buffer = _buffer.drop_front();
|
|
|
|
// Scan for /'s. We're done if it is preceded by a *.
|
|
while (true) {
|
|
if (_buffer.empty())
|
|
break;
|
|
_buffer = _buffer.drop_front();
|
|
if (_buffer.data()[-1] == '/' && _buffer.data()[-2] == '*')
|
|
break;
|
|
}
|
|
} else
|
|
return;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Constant functions
|
|
void Constant::dump(raw_ostream &os) const { os << _num; }
|
|
|
|
// Symbol functions
|
|
void Symbol::dump(raw_ostream &os) const { os << _name; }
|
|
|
|
// FunctionCall functions
|
|
void FunctionCall::dump(raw_ostream &os) const {
|
|
os << _name << "(";
|
|
for (unsigned i = 0, e = _args.size(); i != e; ++i) {
|
|
if (i)
|
|
os << ", ";
|
|
_args[i]->dump(os);
|
|
}
|
|
os << ")";
|
|
}
|
|
|
|
// Unary functions
|
|
void Unary::dump(raw_ostream &os) const {
|
|
os << "(";
|
|
if (_op == Unary::Minus)
|
|
os << "-";
|
|
else
|
|
os << "~";
|
|
_child->dump(os);
|
|
os << ")";
|
|
}
|
|
|
|
// BinOp functions
|
|
void BinOp::dump(raw_ostream &os) const {
|
|
os << "(";
|
|
_lhs->dump(os);
|
|
os << " ";
|
|
switch (_op) {
|
|
case Sum:
|
|
os << "+";
|
|
break;
|
|
case Sub:
|
|
os << "-";
|
|
break;
|
|
case Mul:
|
|
os << "*";
|
|
break;
|
|
case Div:
|
|
os << "/";
|
|
break;
|
|
case Shl:
|
|
os << "<<";
|
|
break;
|
|
case Shr:
|
|
os << ">>";
|
|
break;
|
|
case And:
|
|
os << "&";
|
|
break;
|
|
case Or:
|
|
os << "|";
|
|
break;
|
|
case CompareEqual:
|
|
os << "==";
|
|
break;
|
|
case CompareDifferent:
|
|
os << "!=";
|
|
break;
|
|
case CompareLess:
|
|
os << "<";
|
|
break;
|
|
case CompareGreater:
|
|
os << ">";
|
|
break;
|
|
case CompareLessEqual:
|
|
os << "<=";
|
|
break;
|
|
case CompareGreaterEqual:
|
|
os << ">=";
|
|
break;
|
|
}
|
|
os << " ";
|
|
_rhs->dump(os);
|
|
os << ")";
|
|
}
|
|
|
|
// TernaryConditional functions
|
|
void TernaryConditional::dump(raw_ostream &os) const {
|
|
_conditional->dump(os);
|
|
os << " ? ";
|
|
_trueExpr->dump(os);
|
|
os << " : ";
|
|
_falseExpr->dump(os);
|
|
}
|
|
|
|
// SymbolAssignment functions
|
|
void SymbolAssignment::dump(raw_ostream &os) const {
|
|
int numParen = 0;
|
|
|
|
if (_assignmentVisibility != Normal) {
|
|
switch (_assignmentVisibility) {
|
|
case Hidden:
|
|
os << "HIDDEN(";
|
|
break;
|
|
case Provide:
|
|
os << "PROVIDE(";
|
|
break;
|
|
case ProvideHidden:
|
|
os << "PROVIDE_HIDDEN(";
|
|
break;
|
|
default:
|
|
llvm_unreachable("Unknown visibility");
|
|
}
|
|
++numParen;
|
|
}
|
|
|
|
os << _symbol << " ";
|
|
switch (_assignmentKind) {
|
|
case Simple:
|
|
os << "=";
|
|
break;
|
|
case Sum:
|
|
os << "+=";
|
|
break;
|
|
case Sub:
|
|
os << "-=";
|
|
break;
|
|
case Mul:
|
|
os << "*=";
|
|
break;
|
|
case Div:
|
|
os << "/=";
|
|
break;
|
|
case Shl:
|
|
os << "<<=";
|
|
break;
|
|
case Shr:
|
|
os << ">>=";
|
|
break;
|
|
case And:
|
|
os << "&=";
|
|
break;
|
|
case Or:
|
|
os << "|=";
|
|
break;
|
|
}
|
|
|
|
os << " ";
|
|
_expression->dump(os);
|
|
if (numParen)
|
|
os << ")";
|
|
os << ";";
|
|
}
|
|
|
|
static int dumpSortDirectives(raw_ostream &os, WildcardSortMode sortMode) {
|
|
switch (sortMode) {
|
|
case WildcardSortMode::NA:
|
|
return 0;
|
|
case WildcardSortMode::ByName:
|
|
os << "SORT_BY_NAME(";
|
|
return 1;
|
|
case WildcardSortMode::ByAlignment:
|
|
os << "SORT_BY_ALIGNMENT(";
|
|
return 1;
|
|
case WildcardSortMode::ByInitPriority:
|
|
os << "SORT_BY_INIT_PRIORITY(";
|
|
return 1;
|
|
case WildcardSortMode::ByNameAndAlignment:
|
|
os << "SORT_BY_NAME(SORT_BY_ALIGNMENT(";
|
|
return 2;
|
|
case WildcardSortMode::ByAlignmentAndName:
|
|
os << "SORT_BY_ALIGNMENT(SORT_BY_NAME(";
|
|
return 2;
|
|
case WildcardSortMode::None:
|
|
os << "SORT_NONE(";
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// InputSectionName functions
|
|
void InputSectionName::dump(raw_ostream &os) const {
|
|
os << _name;
|
|
}
|
|
|
|
// InputSectionSortedGroup functions
|
|
static void dumpInputSections(raw_ostream &os,
|
|
const std::vector<const InputSection *> &secs) {
|
|
bool excludeFile = false;
|
|
bool first = true;
|
|
|
|
for (auto &secName : secs) {
|
|
if (!first)
|
|
os << " ";
|
|
first = false;
|
|
// Coalesce multiple input sections marked with EXCLUDE_FILE in the same
|
|
// EXCLUDE_FILE() group
|
|
if (auto inputSec = dyn_cast<InputSectionName>(secName)) {
|
|
if (!excludeFile && inputSec->hasExcludeFile()) {
|
|
excludeFile = true;
|
|
os << "EXCLUDE_FILE(";
|
|
} else if (excludeFile && !inputSec->hasExcludeFile()) {
|
|
excludeFile = false;
|
|
os << ") ";
|
|
}
|
|
}
|
|
secName->dump(os);
|
|
}
|
|
|
|
if (excludeFile)
|
|
os << ")";
|
|
}
|
|
|
|
void InputSectionSortedGroup::dump(raw_ostream &os) const {
|
|
int numParen = dumpSortDirectives(os, _sortMode);
|
|
dumpInputSections(os, _sections);
|
|
for (int i = 0; i < numParen; ++i)
|
|
os << ")";
|
|
}
|
|
|
|
// InputSectionsCmd functions
|
|
void InputSectionsCmd::dump(raw_ostream &os) const {
|
|
if (_keep)
|
|
os << "KEEP(";
|
|
|
|
int numParen = dumpSortDirectives(os, _fileSortMode);
|
|
os << _fileName;
|
|
for (int i = 0; i < numParen; ++i)
|
|
os << ")";
|
|
|
|
if (_archiveName.size() > 0) {
|
|
os << ":";
|
|
numParen = dumpSortDirectives(os, _archiveSortMode);
|
|
os << _archiveName;
|
|
for (int i = 0; i < numParen; ++i)
|
|
os << ")";
|
|
}
|
|
|
|
if (_sections.size() > 0) {
|
|
os << "(";
|
|
dumpInputSections(os, _sections);
|
|
os << ")";
|
|
}
|
|
|
|
if (_keep)
|
|
os << ")";
|
|
}
|
|
|
|
// OutputSectionDescription functions
|
|
void OutputSectionDescription::dump(raw_ostream &os) const {
|
|
if (_discard)
|
|
os << "/DISCARD/";
|
|
else
|
|
os << _sectionName;
|
|
|
|
if (_address) {
|
|
os << " ";
|
|
_address->dump(os);
|
|
}
|
|
os << " :\n";
|
|
|
|
if (_at) {
|
|
os << " AT(";
|
|
_at->dump(os);
|
|
os << ")\n";
|
|
}
|
|
|
|
if (_align) {
|
|
os << " ALIGN(";
|
|
_align->dump(os);
|
|
os << ")\n";
|
|
} else if (_alignWithInput) {
|
|
os << " ALIGN_WITH_INPUT\n";
|
|
}
|
|
|
|
if (_subAlign) {
|
|
os << " SUBALIGN(";
|
|
_subAlign->dump(os);
|
|
os << ")\n";
|
|
}
|
|
|
|
switch (_constraint) {
|
|
case C_None:
|
|
break;
|
|
case C_OnlyIfRO:
|
|
os << "ONLY_IF_RO";
|
|
break;
|
|
case C_OnlyIfRW:
|
|
os << "ONLY_IF_RW";
|
|
break;
|
|
}
|
|
|
|
os << " {\n";
|
|
for (auto &command : _outputSectionCommands) {
|
|
os << " ";
|
|
command->dump(os);
|
|
os << "\n";
|
|
}
|
|
os << " }";
|
|
|
|
if (_fillStream.size() > 0) {
|
|
os << " =";
|
|
dumpByteStream(os, _fillStream);
|
|
} else if (_fillExpr) {
|
|
os << " =";
|
|
_fillExpr->dump(os);
|
|
}
|
|
}
|
|
|
|
// Sections functions
|
|
void Sections::dump(raw_ostream &os) const {
|
|
os << "SECTIONS\n{\n";
|
|
for (auto &command : _sectionsCommands) {
|
|
command->dump(os);
|
|
os << "\n";
|
|
}
|
|
os << "}\n";
|
|
}
|
|
|
|
// Parser functions
|
|
LinkerScript *Parser::parse() {
|
|
// Get the first token.
|
|
_lex.lex(_tok);
|
|
// Parse top level commands.
|
|
while (true) {
|
|
switch (_tok._kind) {
|
|
case Token::eof:
|
|
return &_script;
|
|
case Token::semicolon:
|
|
consumeToken();
|
|
break;
|
|
case Token::kw_output_format: {
|
|
auto outputFormat = parseOutputFormat();
|
|
if (!outputFormat)
|
|
return nullptr;
|
|
_script._commands.push_back(outputFormat);
|
|
break;
|
|
}
|
|
case Token::kw_output_arch: {
|
|
auto outputArch = parseOutputArch();
|
|
if (!outputArch)
|
|
return nullptr;
|
|
_script._commands.push_back(outputArch);
|
|
break;
|
|
}
|
|
case Token::kw_group: {
|
|
auto group = parseGroup();
|
|
if (!group)
|
|
return nullptr;
|
|
_script._commands.push_back(group);
|
|
break;
|
|
}
|
|
case Token::kw_as_needed:
|
|
// Not allowed at top level.
|
|
error(_tok, "AS_NEEDED not allowed at top level.");
|
|
return nullptr;
|
|
case Token::kw_entry: {
|
|
Entry *entry = parseEntry();
|
|
if (!entry)
|
|
return nullptr;
|
|
_script._commands.push_back(entry);
|
|
break;
|
|
}
|
|
case Token::kw_search_dir: {
|
|
SearchDir *searchDir = parseSearchDir();
|
|
if (!searchDir)
|
|
return nullptr;
|
|
_script._commands.push_back(searchDir);
|
|
break;
|
|
}
|
|
case Token::kw_sections: {
|
|
Sections *sections = parseSections();
|
|
if (!sections)
|
|
return nullptr;
|
|
_script._commands.push_back(sections);
|
|
break;
|
|
}
|
|
case Token::identifier:
|
|
case Token::kw_hidden:
|
|
case Token::kw_provide:
|
|
case Token::kw_provide_hidden: {
|
|
const Command *cmd = parseSymbolAssignment();
|
|
if (!cmd)
|
|
return nullptr;
|
|
_script._commands.push_back(cmd);
|
|
break;
|
|
}
|
|
default:
|
|
// Unexpected.
|
|
error(_tok, "expected linker script command");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
const Expression *Parser::parseFunctionCall() {
|
|
assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_align) &&
|
|
"expected function call first tokens");
|
|
std::vector<const Expression *> params;
|
|
StringRef name = _tok._range;
|
|
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
if (_tok._kind == Token::r_paren) {
|
|
consumeToken();
|
|
return new (_alloc) FunctionCall(_tok._range, params);
|
|
}
|
|
|
|
if (const Expression *firstParam = parseExpression())
|
|
params.push_back(firstParam);
|
|
else
|
|
return nullptr;
|
|
|
|
while (_tok._kind == Token::comma) {
|
|
consumeToken();
|
|
if (const Expression *param = parseExpression())
|
|
params.push_back(param);
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc) FunctionCall(name, params);
|
|
}
|
|
|
|
bool Parser::expectExprOperand() {
|
|
if (!(_tok._kind == Token::identifier || _tok._kind == Token::number ||
|
|
_tok._kind == Token::kw_align || _tok._kind == Token::l_paren ||
|
|
_tok._kind == Token::minus || _tok._kind == Token::tilde)) {
|
|
error(_tok, "expected symbol, number, minus, tilde or left parenthesis.");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const Expression *Parser::parseExprOperand() {
|
|
if (!expectExprOperand())
|
|
return nullptr;
|
|
|
|
switch (_tok._kind) {
|
|
case Token::identifier: {
|
|
if (peek()._kind== Token::l_paren)
|
|
return parseFunctionCall();
|
|
Symbol *sym = new (_alloc) Symbol(_tok._range);
|
|
consumeToken();
|
|
return sym;
|
|
}
|
|
case Token::kw_align:
|
|
return parseFunctionCall();
|
|
case Token::minus:
|
|
consumeToken();
|
|
return new (_alloc) Unary(Unary::Minus, parseExprOperand());
|
|
case Token::tilde:
|
|
consumeToken();
|
|
return new (_alloc) Unary(Unary::Not, parseExprOperand());
|
|
case Token::number: {
|
|
auto val = parseNum(_tok._range);
|
|
if (val.getError()) {
|
|
error(_tok, "Unrecognized number constant");
|
|
return nullptr;
|
|
}
|
|
Constant *c = new (_alloc) Constant(*val);
|
|
consumeToken();
|
|
return c;
|
|
}
|
|
case Token::l_paren: {
|
|
consumeToken();
|
|
const Expression *expr = parseExpression();
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return expr;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown token");
|
|
}
|
|
}
|
|
|
|
static bool TokenToBinOp(const Token &tok, BinOp::Operation &op,
|
|
unsigned &precedence) {
|
|
switch (tok._kind) {
|
|
case Token::star:
|
|
op = BinOp::Mul;
|
|
precedence = 3;
|
|
return true;
|
|
case Token::slash:
|
|
op = BinOp::Div;
|
|
precedence = 3;
|
|
return true;
|
|
case Token::plus:
|
|
op = BinOp::Sum;
|
|
precedence = 4;
|
|
return true;
|
|
case Token::minus:
|
|
op = BinOp::Sub;
|
|
precedence = 4;
|
|
return true;
|
|
case Token::lessless:
|
|
op = BinOp::Shl;
|
|
precedence = 5;
|
|
return true;
|
|
case Token::greatergreater:
|
|
op = BinOp::Shr;
|
|
precedence = 5;
|
|
return true;
|
|
case Token::less:
|
|
op = BinOp::CompareLess;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::greater:
|
|
op = BinOp::CompareGreater;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::lessequal:
|
|
op = BinOp::CompareLessEqual;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::greaterequal:
|
|
op = BinOp::CompareGreaterEqual;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::equalequal:
|
|
op = BinOp::CompareEqual;
|
|
precedence = 7;
|
|
return true;
|
|
case Token::exclaimequal:
|
|
op = BinOp::CompareDifferent;
|
|
precedence = 7;
|
|
return true;
|
|
case Token::amp:
|
|
op = BinOp::And;
|
|
precedence = 8;
|
|
return true;
|
|
case Token::pipe:
|
|
op = BinOp::Or;
|
|
precedence = 10;
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool isExpressionOperator(Token tok) {
|
|
switch (tok._kind) {
|
|
case Token::star:
|
|
case Token::slash:
|
|
case Token::plus:
|
|
case Token::minus:
|
|
case Token::lessless:
|
|
case Token::greatergreater:
|
|
case Token::less:
|
|
case Token::greater:
|
|
case Token::lessequal:
|
|
case Token::greaterequal:
|
|
case Token::equalequal:
|
|
case Token::exclaimequal:
|
|
case Token::amp:
|
|
case Token::pipe:
|
|
case Token::question:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const Expression *Parser::parseExpression(unsigned precedence) {
|
|
assert(precedence <= 13 && "Invalid precedence value");
|
|
if (!expectExprOperand())
|
|
return nullptr;
|
|
|
|
const Expression *expr = parseExprOperand();
|
|
if (!expr)
|
|
return nullptr;
|
|
|
|
BinOp::Operation op;
|
|
unsigned binOpPrecedence = 0;
|
|
if (TokenToBinOp(_tok, op, binOpPrecedence)) {
|
|
if (precedence >= binOpPrecedence)
|
|
return parseOperatorOperandLoop(expr, precedence);
|
|
return expr;
|
|
}
|
|
|
|
// Non-binary operators
|
|
if (_tok._kind == Token::question && precedence >= 13)
|
|
return parseOperatorOperandLoop(expr, precedence);
|
|
return expr;
|
|
}
|
|
|
|
const Expression *Parser::parseOperatorOperandLoop(const Expression *lhs,
|
|
unsigned highestPrecedence) {
|
|
assert(highestPrecedence <= 13 && "Invalid precedence value");
|
|
unsigned precedence = 0;
|
|
const Expression *binOp = nullptr;
|
|
|
|
while (1) {
|
|
BinOp::Operation op;
|
|
if (!TokenToBinOp(_tok, op, precedence)) {
|
|
if (_tok._kind == Token::question && highestPrecedence >= 13)
|
|
return parseTernaryCondOp(lhs);
|
|
return binOp;
|
|
}
|
|
|
|
if (precedence > highestPrecedence)
|
|
return binOp;
|
|
|
|
consumeToken();
|
|
const Expression *rhs = parseExpression(precedence - 1);
|
|
if (!rhs)
|
|
return nullptr;
|
|
binOp = new (_alloc) BinOp(lhs, op, rhs);
|
|
lhs = binOp;
|
|
}
|
|
}
|
|
|
|
const Expression *Parser::parseTernaryCondOp(const Expression *lhs) {
|
|
assert(_tok._kind == Token::question && "Expected question mark");
|
|
|
|
consumeToken();
|
|
|
|
// The ternary conditional operator has right-to-left associativity.
|
|
// To implement this, we allow our children to contain ternary conditional
|
|
// operators themselves (precedence 13).
|
|
const Expression *trueExpr = parseExpression(13);
|
|
if (!trueExpr)
|
|
return nullptr;
|
|
|
|
if (!expectAndConsume(Token::colon, "expected :"))
|
|
return nullptr;
|
|
|
|
const Expression *falseExpr = parseExpression(13);
|
|
if (!falseExpr)
|
|
return nullptr;
|
|
|
|
return new (_alloc) TernaryConditional(lhs, trueExpr, falseExpr);
|
|
}
|
|
|
|
// Parse OUTPUT_FORMAT(ident)
|
|
OutputFormat *Parser::parseOutputFormat() {
|
|
assert(_tok._kind == Token::kw_output_format && "Expected OUTPUT_FORMAT!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected identifier in OUTPUT_FORMAT.");
|
|
return nullptr;
|
|
}
|
|
|
|
auto ret = new (_alloc) OutputFormat(_tok._range);
|
|
consumeToken();
|
|
|
|
do {
|
|
if (isNextToken(Token::comma))
|
|
consumeToken();
|
|
else
|
|
break;
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected identifier in OUTPUT_FORMAT.");
|
|
return nullptr;
|
|
}
|
|
ret->addOutputFormat(_tok._range);
|
|
consumeToken();
|
|
} while (isNextToken(Token::comma));
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse OUTPUT_ARCH(ident)
|
|
OutputArch *Parser::parseOutputArch() {
|
|
assert(_tok._kind == Token::kw_output_arch && "Expected OUTPUT_ARCH!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected identifier in OUTPUT_ARCH.");
|
|
return nullptr;
|
|
}
|
|
|
|
auto ret = new (_alloc) OutputArch(_tok._range);
|
|
consumeToken();
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse GROUP(file ...)
|
|
Group *Parser::parseGroup() {
|
|
assert(_tok._kind == Token::kw_group && "Expected GROUP!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
std::vector<Path> paths;
|
|
|
|
while (_tok._kind == Token::identifier || _tok._kind == Token::libname ||
|
|
_tok._kind == Token::kw_as_needed) {
|
|
switch (_tok._kind) {
|
|
case Token::identifier:
|
|
paths.push_back(Path(_tok._range));
|
|
consumeToken();
|
|
break;
|
|
case Token::libname:
|
|
paths.push_back(Path(_tok._range, false, true));
|
|
consumeToken();
|
|
break;
|
|
case Token::kw_as_needed:
|
|
if (!parseAsNeeded(paths))
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
llvm_unreachable("Invalid token.");
|
|
}
|
|
}
|
|
|
|
auto ret = new (_alloc) Group(paths);
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse AS_NEEDED(file ...)
|
|
bool Parser::parseAsNeeded(std::vector<Path> &paths) {
|
|
assert(_tok._kind == Token::kw_as_needed && "Expected AS_NEEDED!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return false;
|
|
|
|
while (_tok._kind == Token::identifier || _tok._kind == Token::libname) {
|
|
switch (_tok._kind) {
|
|
case Token::identifier:
|
|
paths.push_back(Path(_tok._range, true, false));
|
|
consumeToken();
|
|
break;
|
|
case Token::libname:
|
|
paths.push_back(Path(_tok._range, true, true));
|
|
consumeToken();
|
|
break;
|
|
default:
|
|
llvm_unreachable("Invalid token.");
|
|
}
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Parse ENTRY(ident)
|
|
Entry *Parser::parseEntry() {
|
|
assert(_tok._kind == Token::kw_entry && "Expected ENTRY!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "expected identifier in ENTRY");
|
|
return nullptr;
|
|
}
|
|
StringRef entryName(_tok._range);
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc) Entry(entryName);
|
|
}
|
|
|
|
// Parse SEARCH_DIR(ident)
|
|
SearchDir *Parser::parseSearchDir() {
|
|
assert(_tok._kind == Token::kw_search_dir && "Expected SEARCH_DIR!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "expected identifier in SEARCH_DIR");
|
|
return nullptr;
|
|
}
|
|
StringRef searchPath(_tok._range);
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc) SearchDir(searchPath);
|
|
}
|
|
|
|
const SymbolAssignment *Parser::parseSymbolAssignment() {
|
|
assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_hidden ||
|
|
_tok._kind == Token::kw_provide ||
|
|
_tok._kind == Token::kw_provide_hidden) &&
|
|
"Expected identifier!");
|
|
SymbolAssignment::AssignmentVisibility visibility = SymbolAssignment::Normal;
|
|
SymbolAssignment::AssignmentKind kind;
|
|
int numParen = 0;
|
|
|
|
switch (_tok._kind) {
|
|
case Token::kw_hidden:
|
|
visibility = SymbolAssignment::Hidden;
|
|
++numParen;
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
break;
|
|
case Token::kw_provide:
|
|
visibility = SymbolAssignment::Provide;
|
|
++numParen;
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
break;
|
|
case Token::kw_provide_hidden:
|
|
visibility = SymbolAssignment::ProvideHidden;
|
|
++numParen;
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
StringRef name = _tok._range;
|
|
consumeToken();
|
|
|
|
// Parse assignment operator (=, +=, -= etc.)
|
|
switch (_tok._kind) {
|
|
case Token::equal:
|
|
kind = SymbolAssignment::Simple;
|
|
break;
|
|
case Token::plusequal:
|
|
kind = SymbolAssignment::Sum;
|
|
break;
|
|
case Token::minusequal:
|
|
kind = SymbolAssignment::Sub;
|
|
break;
|
|
case Token::starequal:
|
|
kind = SymbolAssignment::Mul;
|
|
break;
|
|
case Token::slashequal:
|
|
kind = SymbolAssignment::Div;
|
|
break;
|
|
case Token::ampequal:
|
|
kind = SymbolAssignment::And;
|
|
break;
|
|
case Token::pipeequal:
|
|
kind = SymbolAssignment::Or;
|
|
break;
|
|
case Token::lesslessequal:
|
|
kind = SymbolAssignment::Shl;
|
|
break;
|
|
case Token::greatergreaterequal:
|
|
kind = SymbolAssignment::Shr;
|
|
break;
|
|
default:
|
|
error(_tok, "unexpected token");
|
|
return nullptr;
|
|
}
|
|
|
|
consumeToken();
|
|
|
|
const Expression *expr = nullptr;
|
|
switch (_tok._kind) {
|
|
case Token::number:
|
|
case Token::kw_align:
|
|
case Token::identifier:
|
|
case Token::l_paren:
|
|
expr = parseExpression();
|
|
if (!expr)
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
error(_tok, "unexpected token while parsing assignment value.");
|
|
return nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < numParen; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return new (_alloc) SymbolAssignment(name, expr, kind, visibility);
|
|
}
|
|
|
|
llvm::ErrorOr<InputSectionsCmd::VectorTy> Parser::parseExcludeFile() {
|
|
assert(_tok._kind == Token::kw_exclude_file && "Expected EXCLUDE_FILE!");
|
|
InputSectionsCmd::VectorTy res;
|
|
consumeToken();
|
|
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return llvm::ErrorOr<InputSectionsCmd::VectorTy>(
|
|
std::make_error_code(std::errc::io_error));
|
|
|
|
while (_tok._kind == Token::identifier) {
|
|
res.push_back(new (_alloc) InputSectionName(_tok._range, true));
|
|
consumeToken();
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return llvm::ErrorOr<InputSectionsCmd::VectorTy>(
|
|
std::make_error_code(std::errc::io_error));
|
|
return llvm::ErrorOr<InputSectionsCmd::VectorTy>(std::move(res));
|
|
}
|
|
|
|
int Parser::parseSortDirectives(WildcardSortMode &sortMode) {
|
|
int numParsedDirectives = 0;
|
|
sortMode = WildcardSortMode::NA;
|
|
|
|
if (_tok._kind == Token::kw_sort_by_name) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
sortMode = WildcardSortMode::ByName;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_sort_by_init_priority) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
sortMode = WildcardSortMode::ByInitPriority;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_sort_by_alignment) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
if (sortMode != WildcardSortMode::ByName)
|
|
sortMode = WildcardSortMode::ByAlignment;
|
|
else
|
|
sortMode = WildcardSortMode::ByNameAndAlignment;
|
|
}
|
|
|
|
if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_name) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
if (sortMode == WildcardSortMode::ByAlignment)
|
|
sortMode = WildcardSortMode::ByAlignmentAndName;
|
|
}
|
|
|
|
if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_alignment) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
}
|
|
|
|
if (numParsedDirectives == 0 && _tok._kind == Token::kw_sort_none) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
sortMode = WildcardSortMode::None;
|
|
}
|
|
|
|
return numParsedDirectives;
|
|
}
|
|
|
|
const InputSection *Parser::parseSortedInputSections() {
|
|
assert((_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) &&
|
|
"Expected SORT directives!");
|
|
|
|
WildcardSortMode sortMode = WildcardSortMode::NA;
|
|
int numParen = parseSortDirectives(sortMode);
|
|
if (numParen == -1)
|
|
return nullptr;
|
|
|
|
std::vector<const InputSection *> inputSections;
|
|
|
|
while (_tok._kind == Token::identifier) {
|
|
inputSections.push_back(new (_alloc) InputSectionName(_tok._range, false));
|
|
consumeToken();
|
|
}
|
|
|
|
// Eat "numParen" rparens
|
|
for (int i = 0, e = numParen; i != e; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return new (_alloc) InputSectionSortedGroup(sortMode, inputSections);
|
|
}
|
|
|
|
const InputSectionsCmd *Parser::parseInputSectionsCmd() {
|
|
assert((_tok._kind == Token::identifier || _tok._kind == Token::colon ||
|
|
_tok._kind == Token::star || _tok._kind == Token::kw_keep ||
|
|
_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) &&
|
|
"Expected input section first tokens!");
|
|
int numParen = 1;
|
|
bool keep = false;
|
|
WildcardSortMode fileSortMode = WildcardSortMode::NA;
|
|
WildcardSortMode archiveSortMode = WildcardSortMode::NA;
|
|
StringRef fileName;
|
|
StringRef archiveName;
|
|
|
|
if (_tok._kind == Token::kw_keep) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
++numParen;
|
|
keep = true;
|
|
}
|
|
|
|
// Input name
|
|
if (_tok._kind != Token::colon) {
|
|
int numParen = parseSortDirectives(fileSortMode);
|
|
if (numParen == -1)
|
|
return nullptr;
|
|
fileName = _tok._range;
|
|
consumeToken();
|
|
if (numParen) {
|
|
while (numParen--)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (_tok._kind == Token::colon) {
|
|
consumeToken();
|
|
if (_tok._kind == Token::identifier ||
|
|
_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) {
|
|
int numParen = parseSortDirectives(archiveSortMode);
|
|
if (numParen == -1)
|
|
return nullptr;
|
|
archiveName = _tok._range;
|
|
consumeToken();
|
|
for (int i = 0; i != numParen; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
std::vector<const InputSection *> inputSections;
|
|
|
|
if (_tok._kind != Token::l_paren)
|
|
return new (_alloc)
|
|
InputSectionsCmd(fileName, archiveName, keep, fileSortMode,
|
|
archiveSortMode, inputSections);
|
|
consumeToken();
|
|
|
|
while (_tok._kind == Token::identifier ||
|
|
_tok._kind == Token::kw_exclude_file ||
|
|
_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) {
|
|
switch (_tok._kind) {
|
|
case Token::kw_exclude_file: {
|
|
auto vec = parseExcludeFile();
|
|
if (vec.getError())
|
|
return nullptr;
|
|
inputSections.insert(inputSections.end(), vec->begin(), vec->end());
|
|
break;
|
|
}
|
|
case Token::star:
|
|
case Token::identifier: {
|
|
inputSections.push_back(new (_alloc)
|
|
InputSectionName(_tok._range, false));
|
|
consumeToken();
|
|
break;
|
|
}
|
|
case Token::kw_sort_by_name:
|
|
case Token::kw_sort_by_alignment:
|
|
case Token::kw_sort_by_init_priority:
|
|
case Token::kw_sort_none: {
|
|
const InputSection *group = parseSortedInputSections();
|
|
if (!group)
|
|
return nullptr;
|
|
inputSections.push_back(group);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown token");
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < numParen; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc)
|
|
InputSectionsCmd(fileName, archiveName, keep, fileSortMode,
|
|
archiveSortMode, inputSections);
|
|
}
|
|
|
|
const OutputSectionDescription *Parser::parseOutputSectionDescription() {
|
|
assert((_tok._kind == Token::kw_discard || _tok._kind == Token::identifier) &&
|
|
"Expected /DISCARD/ or identifier!");
|
|
StringRef sectionName;
|
|
const Expression *address = nullptr;
|
|
const Expression *align = nullptr;
|
|
const Expression *subAlign = nullptr;
|
|
const Expression *at = nullptr;
|
|
const Expression *fillExpr = nullptr;
|
|
StringRef fillStream;
|
|
bool alignWithInput = false;
|
|
bool discard = false;
|
|
OutputSectionDescription::Constraint constraint =
|
|
OutputSectionDescription::C_None;
|
|
std::vector<const Command *> outputSectionCommands;
|
|
|
|
if (_tok._kind == Token::kw_discard)
|
|
discard = true;
|
|
else
|
|
sectionName = _tok._range;
|
|
consumeToken();
|
|
|
|
if (_tok._kind == Token::number || _tok._kind == Token::identifier ||
|
|
_tok._kind == Token::kw_align || _tok._kind == Token::l_paren) {
|
|
address = parseExpression();
|
|
if (!address)
|
|
return nullptr;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::colon, "expected :"))
|
|
return nullptr;
|
|
|
|
if (_tok._kind == Token::kw_at) {
|
|
consumeToken();
|
|
at = parseExpression();
|
|
if (!at)
|
|
return nullptr;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_align) {
|
|
consumeToken();
|
|
align = parseExpression();
|
|
if (!align)
|
|
return nullptr;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_align_with_input) {
|
|
consumeToken();
|
|
alignWithInput = true;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_subalign) {
|
|
consumeToken();
|
|
subAlign = parseExpression();
|
|
if (!subAlign)
|
|
return nullptr;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_only_if_ro) {
|
|
consumeToken();
|
|
constraint = OutputSectionDescription::C_OnlyIfRO;
|
|
} else if (_tok._kind == Token::kw_only_if_rw) {
|
|
consumeToken();
|
|
constraint = OutputSectionDescription::C_OnlyIfRW;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::l_brace, "expected {"))
|
|
return nullptr;
|
|
|
|
// Parse zero or more output-section-commands
|
|
while (_tok._kind != Token::r_brace) {
|
|
switch (_tok._kind) {
|
|
case Token::semicolon:
|
|
consumeToken();
|
|
break;
|
|
case Token::identifier:
|
|
switch (peek()._kind) {
|
|
case Token::equal:
|
|
case Token::plusequal:
|
|
case Token::minusequal:
|
|
case Token::starequal:
|
|
case Token::slashequal:
|
|
case Token::ampequal:
|
|
case Token::pipeequal:
|
|
case Token::lesslessequal:
|
|
case Token::greatergreaterequal:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
if (const Command *cmd = parseInputSectionsCmd())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
}
|
|
break;
|
|
case Token::kw_keep:
|
|
case Token::star:
|
|
case Token::colon:
|
|
case Token::kw_sort_by_name:
|
|
case Token::kw_sort_by_alignment:
|
|
case Token::kw_sort_by_init_priority:
|
|
case Token::kw_sort_none:
|
|
if (const Command *cmd = parseInputSectionsCmd())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
case Token::kw_hidden:
|
|
case Token::kw_provide:
|
|
case Token::kw_provide_hidden:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
error(_tok, "expected symbol assignment or input file name.");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_brace, "expected }"))
|
|
return nullptr;
|
|
|
|
if (_tok._kind == Token::equal) {
|
|
consumeToken();
|
|
if (_tok._kind != Token::number || !_tok._range.startswith_lower("0x")) {
|
|
fillExpr = parseExpression();
|
|
if (!fillExpr)
|
|
return nullptr;
|
|
} else {
|
|
std::string strBuf;
|
|
if (isExpressionOperator(peek()) ||
|
|
!parseHexToByteStream(_tok._range.drop_front(2), strBuf)) {
|
|
fillExpr = parseExpression();
|
|
if(!fillExpr)
|
|
return nullptr;
|
|
} else {
|
|
char *rawBuf = (char *) _alloc.Allocate(strBuf.size(), 1);
|
|
memcpy(rawBuf, strBuf.c_str(), strBuf.size());
|
|
fillStream = StringRef(rawBuf, strBuf.size());
|
|
consumeToken();
|
|
}
|
|
}
|
|
}
|
|
|
|
return new (_alloc) OutputSectionDescription(
|
|
sectionName, address, align, subAlign, at, fillExpr, fillStream,
|
|
alignWithInput, discard, constraint, outputSectionCommands);
|
|
}
|
|
|
|
const Overlay *Parser::parseOverlay() {
|
|
assert(_tok._kind == Token::kw_overlay && "Expected OVERLAY!");
|
|
error(_tok, "Overlay description is not yet supported.");
|
|
return nullptr;
|
|
}
|
|
|
|
Sections *Parser::parseSections() {
|
|
assert(_tok._kind == Token::kw_sections && "Expected SECTIONS!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_brace, "expected {"))
|
|
return nullptr;
|
|
std::vector<const Command *> sectionsCommands;
|
|
|
|
bool unrecognizedToken = false;
|
|
// Parse zero or more sections-commands
|
|
while (!unrecognizedToken) {
|
|
switch (_tok._kind) {
|
|
case Token::semicolon:
|
|
consumeToken();
|
|
break;
|
|
|
|
case Token::identifier:
|
|
switch (peek()._kind) {
|
|
case Token::equal:
|
|
case Token::plusequal:
|
|
case Token::minusequal:
|
|
case Token::starequal:
|
|
case Token::slashequal:
|
|
case Token::ampequal:
|
|
case Token::pipeequal:
|
|
case Token::lesslessequal:
|
|
case Token::greatergreaterequal:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
if (const Command *cmd = parseOutputSectionDescription())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Token::kw_discard:
|
|
case Token::star:
|
|
if (const Command *cmd = parseOutputSectionDescription())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
case Token::kw_entry:
|
|
if (const Command *cmd = parseEntry())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
case Token::kw_hidden:
|
|
case Token::kw_provide:
|
|
case Token::kw_provide_hidden:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
case Token::kw_overlay:
|
|
if (const Command *cmd = parseOverlay())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
default:
|
|
unrecognizedToken = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!expectAndConsume(
|
|
Token::r_brace,
|
|
"expected symbol assignment, entry, overlay or output section name."))
|
|
return nullptr;
|
|
|
|
return new (_alloc) Sections(sectionsCommands);
|
|
}
|
|
|
|
} // end namespace script
|
|
} // end namespace lld
|